Pass NMIs to DOM0 via a dedicated callback, core Xen support.
authorIan.Campbell@xensource.com <Ian.Campbell@xensource.com>
Wed, 11 Jan 2006 15:51:56 +0000 (15:51 +0000)
committerIan.Campbell@xensource.com <Ian.Campbell@xensource.com>
Wed, 11 Jan 2006 15:51:56 +0000 (15:51 +0000)
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>
xen/arch/x86/traps.c
xen/common/kernel.c
xen/include/asm-x86/nmi.h
xen/include/public/arch-x86_32.h
xen/include/public/arch-x86_64.h
xen/include/public/nmi.h [new file with mode: 0644]
xen/include/public/xen.h
xen/include/xen/sched.h
xen/include/xen/softirq.h

index d7590d36bdd034e627571578632d4ede53933cd2..4458b70636523062ded2c9c18109c3da2a873894 100644 (file)
@@ -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);
 }
 
 
index 9975ee639d880d8ecb1a95498dba096f30201a73..f2bb308652d07c853711b15df57378c7e822da60 100644 (file)
@@ -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);
index 1529bbb8c7809978bba4221e9072d079d565f36c..d79b823ee25990b7dad74eeefe4e8443453ea802 100644 (file)
@@ -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);
index b4372da204322f9bd9d5f82b646aafabd9115455..534acb6b781625e5c77627576df2890fa4a4a55f 100644 (file)
@@ -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 {
index ea8af05c6e4264595354cce776e5c3eb7a7ea23d..03bc3ec08cd06c80e93a5a7cdc7c08e7e135a961 100644 (file)
@@ -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 (file)
index 0000000..0c0c67b
--- /dev/null
@@ -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:
+ */
index ddc901bc9aeb25d42b2828d18965eb144f0ace7b..354985359c2c94e7b5ab3cb1b0eee82a58dcbf73 100644 (file)
@@ -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
 #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
 
 /*
index 8492acf97a75a13b8ab33d635c35f70856ebca94..7d9dee679b3903ff1e8e19861725272e086868fc 100644 (file)
@@ -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).
index 5f1903695b6f31619090dba0a253cbc2271fb10a..87d6c807eee3f0be30cf77ec2ceb77a3aaafba9f 100644 (file)
@@ -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