aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIan.Campbell@xensource.com <Ian.Campbell@xensource.com>2006-01-11 15:51:56 +0000
committerIan.Campbell@xensource.com <Ian.Campbell@xensource.com>2006-01-11 15:51:56 +0000
commitf071016a5c42f407493ffd5172cdbc9ffa177db0 (patch)
treee6ee9cdd64f6162f406dd4b6e1fc1ee4ed2bbcb1
parent1582d1175001a99133810c158622c53d1e3dc9ee (diff)
downloadxen-f071016a5c42f407493ffd5172cdbc9ffa177db0.tar.gz
xen-f071016a5c42f407493ffd5172cdbc9ffa177db0.tar.bz2
xen-f071016a5c42f407493ffd5172cdbc9ffa177db0.zip
Pass NMIs to DOM0 via a dedicated callback, core Xen support.
This patch adds core and generic x86 support code to enable Xen to pass NMI's to a dedicated NMI callback in DOM0 instead of raising a VIRQ. Introduces the nmi_op hypercall to allow DOM0 to (un)register the NMI handler. Signed-off-by: Ian Campbell <Ian.Campbell@XenSource.com>
-rw-r--r--xen/arch/x86/traps.c38
-rw-r--r--xen/common/kernel.c25
-rw-r--r--xen/include/asm-x86/nmi.h2
-rw-r--r--xen/include/public/arch-x86_32.h1
-rw-r--r--xen/include/public/arch-x86_64.h1
-rw-r--r--xen/include/public/nmi.h54
-rw-r--r--xen/include/public/xen.h4
-rw-r--r--xen/include/xen/sched.h8
-rw-r--r--xen/include/xen/softirq.h2
9 files changed, 109 insertions, 26 deletions
diff --git a/xen/arch/x86/traps.c b/xen/arch/x86/traps.c
index d7590d36bd..4458b70636 100644
--- a/xen/arch/x86/traps.c
+++ b/xen/arch/x86/traps.c
@@ -1080,26 +1080,23 @@ asmlinkage int do_general_protection(struct cpu_user_regs *regs)
return 0;
}
+static void nmi_softirq(void)
+{
+ /* Only used to defer wakeup of dom0,vcpu0 to a safe (non-NMI) context. */
+ evtchn_notify(dom0->vcpu[0]);
+}
-/* Defer dom0 notification to softirq context (unsafe in NMI context). */
-static unsigned long nmi_dom0_softirq_reason;
-#define NMI_DOM0_PARITY_ERR 0
-#define NMI_DOM0_IO_ERR 1
-#define NMI_DOM0_UNKNOWN 2
-
-static void nmi_dom0_softirq(void)
+static void nmi_dom0_report(unsigned int reason_idx)
{
- if ( dom0 == NULL )
- return;
+ struct domain *d;
- if ( test_and_clear_bit(NMI_DOM0_PARITY_ERR, &nmi_dom0_softirq_reason) )
- send_guest_virq(dom0->vcpu[0], VIRQ_PARITY_ERR);
+ if ( (d = dom0) == NULL )
+ return;
- if ( test_and_clear_bit(NMI_DOM0_IO_ERR, &nmi_dom0_softirq_reason) )
- send_guest_virq(dom0->vcpu[0], VIRQ_IO_ERR);
+ set_bit(reason_idx, &d->shared_info->arch.nmi_reason);
- if ( test_and_clear_bit(NMI_DOM0_UNKNOWN, &nmi_dom0_softirq_reason) )
- send_guest_virq(dom0->vcpu[0], VIRQ_NMI);
+ if ( test_and_set_bit(_VCPUF_nmi_pending, &d->vcpu[0]->vcpu_flags) )
+ raise_softirq(NMI_SOFTIRQ); /* not safe to wake up a vcpu here */
}
asmlinkage void mem_parity_error(struct cpu_user_regs *regs)
@@ -1107,8 +1104,7 @@ asmlinkage void mem_parity_error(struct cpu_user_regs *regs)
switch ( opt_nmi[0] )
{
case 'd': /* 'dom0' */
- set_bit(NMI_DOM0_PARITY_ERR, &nmi_dom0_softirq_reason);
- raise_softirq(NMI_DOM0_SOFTIRQ);
+ nmi_dom0_report(_XEN_NMIREASON_parity_error);
case 'i': /* 'ignore' */
break;
default: /* 'fatal' */
@@ -1127,8 +1123,7 @@ asmlinkage void io_check_error(struct cpu_user_regs *regs)
switch ( opt_nmi[0] )
{
case 'd': /* 'dom0' */
- set_bit(NMI_DOM0_IO_ERR, &nmi_dom0_softirq_reason);
- raise_softirq(NMI_DOM0_SOFTIRQ);
+ nmi_dom0_report(_XEN_NMIREASON_io_error);
case 'i': /* 'ignore' */
break;
default: /* 'fatal' */
@@ -1147,8 +1142,7 @@ static void unknown_nmi_error(unsigned char reason)
switch ( opt_nmi[0] )
{
case 'd': /* 'dom0' */
- set_bit(NMI_DOM0_UNKNOWN, &nmi_dom0_softirq_reason);
- raise_softirq(NMI_DOM0_SOFTIRQ);
+ nmi_dom0_report(_XEN_NMIREASON_unknown);
case 'i': /* 'ignore' */
break;
default: /* 'fatal' */
@@ -1347,7 +1341,7 @@ void __init trap_init(void)
cpu_init();
- open_softirq(NMI_DOM0_SOFTIRQ, nmi_dom0_softirq);
+ open_softirq(NMI_SOFTIRQ, nmi_softirq);
}
diff --git a/xen/common/kernel.c b/xen/common/kernel.c
index 9975ee639d..f2bb308652 100644
--- a/xen/common/kernel.c
+++ b/xen/common/kernel.c
@@ -11,6 +11,7 @@
#include <xen/compile.h>
#include <xen/sched.h>
#include <asm/current.h>
+#include <public/nmi.h>
#include <public/version.h>
void cmdline_parse(char *cmdline)
@@ -148,6 +149,30 @@ long do_xen_version(int cmd, void *arg)
return -ENOSYS;
}
+long do_nmi_op(unsigned int cmd, void *arg)
+{
+ long rc = 0;
+
+ switch ( cmd )
+ {
+ case XENNMI_register_callback:
+ if ( (current->domain->domain_id != 0) || (current->vcpu_id != 0) )
+ rc = -EINVAL;
+ else
+ current->nmi_addr = (unsigned long)arg;
+ printk("***** NMI handler at 0x%lx\n", current->nmi_addr);
+ break;
+ case XENNMI_unregister_callback:
+ current->nmi_addr = 0;
+ break;
+ default:
+ rc = -ENOSYS;
+ break;
+ }
+
+ return rc;
+}
+
long do_vm_assist(unsigned int cmd, unsigned int type)
{
return vm_assist(current->domain, cmd, type);
diff --git a/xen/include/asm-x86/nmi.h b/xen/include/asm-x86/nmi.h
index 1529bbb8c7..d79b823ee2 100644
--- a/xen/include/asm-x86/nmi.h
+++ b/xen/include/asm-x86/nmi.h
@@ -2,6 +2,8 @@
#ifndef ASM_NMI_H
#define ASM_NMI_H
+#include <public/nmi.h>
+
struct cpu_user_regs;
typedef int (*nmi_callback_t)(struct cpu_user_regs *regs, int cpu);
diff --git a/xen/include/public/arch-x86_32.h b/xen/include/public/arch-x86_32.h
index b4372da204..534acb6b78 100644
--- a/xen/include/public/arch-x86_32.h
+++ b/xen/include/public/arch-x86_32.h
@@ -135,6 +135,7 @@ typedef struct arch_shared_info {
unsigned long max_pfn; /* max pfn that appears in table */
/* Frame containing list of mfns containing list of mfns containing p2m. */
unsigned long pfn_to_mfn_frame_list_list;
+ unsigned long nmi_reason;
} arch_shared_info_t;
typedef struct {
diff --git a/xen/include/public/arch-x86_64.h b/xen/include/public/arch-x86_64.h
index ea8af05c6e..03bc3ec08c 100644
--- a/xen/include/public/arch-x86_64.h
+++ b/xen/include/public/arch-x86_64.h
@@ -202,6 +202,7 @@ typedef struct arch_shared_info {
unsigned long max_pfn; /* max pfn that appears in table */
/* Frame containing list of mfns containing list of mfns containing p2m. */
unsigned long pfn_to_mfn_frame_list_list;
+ unsigned long nmi_reason;
} arch_shared_info_t;
typedef struct {
diff --git a/xen/include/public/nmi.h b/xen/include/public/nmi.h
new file mode 100644
index 0000000000..0c0c67b920
--- /dev/null
+++ b/xen/include/public/nmi.h
@@ -0,0 +1,54 @@
+/******************************************************************************
+ * nmi.h
+ *
+ * NMI callback registration and reason codes.
+ *
+ * Copyright (c) 2005, Keir Fraser <keir@xensource.com>
+ */
+
+#ifndef __XEN_PUBLIC_NMI_H__
+#define __XEN_PUBLIC_NMI_H__
+
+/*
+ * NMI reason codes:
+ * Currently these are x86-specific, stored in arch_shared_info.nmi_reason.
+ */
+ /* I/O-check error reported via ISA port 0x61, bit 6. */
+#define _XEN_NMIREASON_io_error 0
+#define XEN_NMIREASON_io_error (1UL << _XEN_NMIREASON_io_error)
+ /* Parity error reported via ISA port 0x61, bit 7. */
+#define _XEN_NMIREASON_parity_error 1
+#define XEN_NMIREASON_parity_error (1UL << _XEN_NMIREASON_parity_error)
+ /* Unknown hardware-generated NMI. */
+#define _XEN_NMIREASON_unknown 2
+#define XEN_NMIREASON_unknown (1UL << _XEN_NMIREASON_unknown)
+
+/*
+ * long nmi_op(unsigned int cmd, void *arg)
+ * NB. All ops return zero on success, else a negative error code.
+ */
+
+/*
+ * Register NMI callback for this (calling) VCPU. Currently this only makes
+ * sense for domain 0, vcpu 0. All other callers will be returned EINVAL.
+ * arg == address of callback function.
+ */
+#define XENNMI_register_callback 0
+
+/*
+ * Deregister NMI callback for this (calling) VCPU.
+ * arg == NULL.
+ */
+#define XENNMI_unregister_callback 1
+
+#endif /* __XEN_PUBLIC_NMI_H__ */
+
+/*
+ * Local variables:
+ * mode: C
+ * c-set-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/xen/include/public/xen.h b/xen/include/public/xen.h
index ddc901bc9a..354985359c 100644
--- a/xen/include/public/xen.h
+++ b/xen/include/public/xen.h
@@ -59,6 +59,7 @@
#define __HYPERVISOR_set_segment_base 25 /* x86/64 only */
#define __HYPERVISOR_mmuext_op 26
#define __HYPERVISOR_acm_op 27
+#define __HYPERVISOR_nmi_op 28
/*
* VIRTUAL INTERRUPTS
@@ -69,10 +70,7 @@
#define VIRQ_DEBUG 1 /* Request guest to dump debug info. */
#define VIRQ_CONSOLE 2 /* (DOM0) Bytes received on emergency console. */
#define VIRQ_DOM_EXC 3 /* (DOM0) Exceptional event for some domain. */
-#define VIRQ_PARITY_ERR 4 /* (DOM0) NMI parity error (port 0x61, bit 7). */
-#define VIRQ_IO_ERR 5 /* (DOM0) NMI I/O error (port 0x61, bit 6). */
#define VIRQ_DEBUGGER 6 /* (DOM0) A domain has paused for debugging. */
-#define VIRQ_NMI 7 /* (DOM0) Unknown NMI (not from ISA port 0x61).*/
#define NR_VIRQS 8
/*
diff --git a/xen/include/xen/sched.h b/xen/include/xen/sched.h
index 8492acf97a..7d9dee679b 100644
--- a/xen/include/xen/sched.h
+++ b/xen/include/xen/sched.h
@@ -81,6 +81,8 @@ struct vcpu
/* Bitmask of CPUs on which this VCPU may run. */
cpumask_t cpu_affinity;
+ unsigned long nmi_addr; /* NMI callback address. */
+
/* Bitmask of CPUs which are holding onto this VCPU's state. */
cpumask_t vcpu_dirty_cpumask;
@@ -361,6 +363,12 @@ extern struct domain *domain_list;
/* VCPU is not-runnable */
#define _VCPUF_down 5
#define VCPUF_down (1UL<<_VCPUF_down)
+ /* NMI callback pending for this VCPU? */
+#define _VCPUF_nmi_pending 8
+#define VCPUF_nmi_pending (1UL<<_VCPUF_nmi_pending)
+ /* Avoid NMI reentry by allowing NMIs to be masked for short periods. */
+#define _VCPUF_nmi_masked 9
+#define VCPUF_nmi_masked (1UL<<_VCPUF_nmi_masked)
/*
* Per-domain flags (domain_flags).
diff --git a/xen/include/xen/softirq.h b/xen/include/xen/softirq.h
index 5f1903695b..87d6c807ee 100644
--- a/xen/include/xen/softirq.h
+++ b/xen/include/xen/softirq.h
@@ -6,7 +6,7 @@
#define SCHEDULE_SOFTIRQ 1
#define NEW_TLBFLUSH_CLOCK_PERIOD_SOFTIRQ 2
#define KEYPRESS_SOFTIRQ 3
-#define NMI_DOM0_SOFTIRQ 4
+#define NMI_SOFTIRQ 4
#define PAGE_SCRUB_SOFTIRQ 5
#define DOMAIN_SHUTDOWN_FINALISE_SOFTIRQ 6
#define NR_SOFTIRQS 7