[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [PATCH] x86: allow NMI injection



NetWare's internal debugger needs the ability to send NMI IPIs, and
there is no reason to not allow domUs or dom0's vCPUs other than vCPU 0
to handle NMIs (they just will never see hardware generated ones).

While currently not having a frontend, the added hypercall also allows
for being used to inject NMIs into foreign VMs.

Along the lines, this fixes a potential race condition caused by
previously accessing the VCPU flags field non-atomically in entry.S.

Signed-off-by: Jan Beulich <jbeulich@xxxxxxxxxx>

Index: 2007-02-27/xen/arch/x86/physdev.c
===================================================================
--- 2007-02-27.orig/xen/arch/x86/physdev.c      2007-02-28 12:14:26.000000000 
+0100
+++ 2007-02-27/xen/arch/x86/physdev.c   2007-02-28 12:14:58.000000000 +0100
@@ -143,6 +143,57 @@ ret_t do_physdev_op(int cmd, XEN_GUEST_H
         break;
     }
 
+    case PHYSDEVOP_send_nmi: {
+        struct physdev_send_nmi send_nmi;
+        struct domain *d;
+
+        ret = -EFAULT;
+        if ( copy_from_guest(&send_nmi, arg, 1) != 0 )
+            break;
+
+        ret = -EPERM;
+        if ( send_nmi.domain == DOMID_SELF )
+            d = current->domain;
+        else if ( !IS_PRIV(current->domain) )
+            break;
+        else
+            d = get_domain_by_id(send_nmi.domain);
+        ret = -ESRCH;
+        if ( d == NULL )
+            break;
+
+        switch ( send_nmi.vcpu )
+        {
+            struct vcpu *v;
+
+        case XEN_SEND_NMI_ALL:
+        case XEN_SEND_NMI_ALL_BUT_SELF:
+            for_each_vcpu(d, v)
+            {
+                if ( (send_nmi.vcpu == XEN_SEND_NMI_ALL || v != current) &&
+                     !test_and_set_bit(_VCPUF_nmi_pending, &v->vcpu_flags) )
+                    vcpu_kick(v);
+            }
+            ret = 0;
+            break;
+        case 0 ... MAX_VIRT_CPUS - 1:
+            if ( (v = d->vcpu[send_nmi.vcpu]) != NULL )
+            {
+                if ( !test_and_set_bit(_VCPUF_nmi_pending, &v->vcpu_flags) )
+                    vcpu_kick(v);
+                ret = 0;
+            }
+            break;
+        default:
+            ret = EINVAL;
+            break;
+        }
+
+        if ( send_nmi.domain != DOMID_SELF )
+            put_domain(d);
+        break;
+    }
+
     default:
         ret = -ENOSYS;
         break;
Index: 2007-02-27/xen/arch/x86/traps.c
===================================================================
--- 2007-02-27.orig/xen/arch/x86/traps.c        2007-02-28 12:14:26.000000000 
+0100
+++ 2007-02-27/xen/arch/x86/traps.c     2007-02-28 12:08:44.000000000 +0100
@@ -2123,6 +2123,12 @@ long do_set_trap_table(XEN_GUEST_HANDLE(
         if ( cur.address == 0 )
             break;
 
+        if ( cur.vector == 2 && !TI_GET_IF(&cur) )
+        {
+            rc = -EINVAL;
+            break;
+        }
+
         fixup_guest_code_selector(current->domain, cur.cs);
 
         memcpy(&dst[cur.vector], &cur, sizeof(cur));
Index: 2007-02-27/xen/arch/x86/x86_32/asm-offsets.c
===================================================================
--- 2007-02-27.orig/xen/arch/x86/x86_32/asm-offsets.c   2007-02-28 
12:14:26.000000000 +0100
+++ 2007-02-27/xen/arch/x86/x86_32/asm-offsets.c        2007-02-28 
12:08:44.000000000 +0100
@@ -68,6 +68,7 @@ void __dummy__(void)
     OFFSET(VCPU_arch_guest_fpu_ctxt, struct vcpu, arch.guest_context.fpu_ctxt);
     OFFSET(VCPU_flags, struct vcpu, vcpu_flags);
     OFFSET(VCPU_nmi_addr, struct vcpu, nmi_addr);
+    OFFSET(VCPU_nmi_cs, struct vcpu, arch.guest_context.trap_ctxt[2].cs);
     DEFINE(_VCPUF_nmi_pending, _VCPUF_nmi_pending);
     DEFINE(_VCPUF_nmi_masked, _VCPUF_nmi_masked);
     DEFINE(_VGCF_failsafe_disables_events, _VGCF_failsafe_disables_events);
Index: 2007-02-27/xen/arch/x86/x86_32/entry.S
===================================================================
--- 2007-02-27.orig/xen/arch/x86/x86_32/entry.S 2007-02-28 12:14:26.000000000 
+0100
+++ 2007-02-27/xen/arch/x86/x86_32/entry.S      2007-02-28 12:08:44.000000000 
+0100
@@ -232,7 +232,7 @@ test_all_events:
         shl  $IRQSTAT_shift,%eax
         test %ecx,irq_stat(%eax,1)
         jnz  process_softirqs
-        btr  $_VCPUF_nmi_pending,VCPU_flags(%ebx)
+        lock btrl $_VCPUF_nmi_pending,VCPU_flags(%ebx)
         jc   process_nmi
 test_guest_events:
         movl VCPU_vcpu_info(%ebx),%eax
@@ -259,19 +259,20 @@ process_softirqs:
 
         ALIGN
 process_nmi:
-        movl VCPU_nmi_addr(%ebx),%eax
+        movzwl VCPU_nmi_cs(%ebx),%eax
+        movl VCPU_nmi_addr(%ebx),%ecx
         test %eax,%eax
         jz   test_all_events
-        bts  $_VCPUF_nmi_masked,VCPU_flags(%ebx)
+        lock btsl $_VCPUF_nmi_masked,VCPU_flags(%ebx)
         jc   1f
         sti
         leal VCPU_trap_bounce(%ebx),%edx
-        movl %eax,TRAPBOUNCE_eip(%edx)
-        movw $FLAT_KERNEL_CS,TRAPBOUNCE_cs(%edx)
+        movl %ecx,TRAPBOUNCE_eip(%edx)
+        movw %ax,TRAPBOUNCE_cs(%edx)
         movw $TBF_INTERRUPT,TRAPBOUNCE_flags(%edx)
         call create_bounce_frame
         jmp  test_all_events
-1:      bts  $_VCPUF_nmi_pending,VCPU_flags(%ebx)
+1:      lock btsl $_VCPUF_nmi_pending,VCPU_flags(%ebx)
         jmp  test_guest_events
 
 bad_hypercall:
Index: 2007-02-27/xen/arch/x86/x86_64/asm-offsets.c
===================================================================
--- 2007-02-27.orig/xen/arch/x86/x86_64/asm-offsets.c   2007-02-28 
12:14:26.000000000 +0100
+++ 2007-02-27/xen/arch/x86/x86_64/asm-offsets.c        2007-02-28 
12:08:44.000000000 +0100
@@ -76,6 +76,7 @@ void __dummy__(void)
     OFFSET(VCPU_arch_guest_fpu_ctxt, struct vcpu, arch.guest_context.fpu_ctxt);
     OFFSET(VCPU_flags, struct vcpu, vcpu_flags);
     OFFSET(VCPU_nmi_addr, struct vcpu, nmi_addr);
+    OFFSET(VCPU_nmi_cs, struct vcpu, arch.guest_context.trap_ctxt[2].cs);
     DEFINE(_VCPUF_nmi_pending, _VCPUF_nmi_pending);
     DEFINE(_VCPUF_nmi_masked, _VCPUF_nmi_masked);
     DEFINE(_VGCF_failsafe_disables_events, _VGCF_failsafe_disables_events);
Index: 2007-02-27/xen/arch/x86/x86_64/compat/entry.S
===================================================================
--- 2007-02-27.orig/xen/arch/x86/x86_64/compat/entry.S  2007-02-28 
12:14:26.000000000 +0100
+++ 2007-02-27/xen/arch/x86/x86_64/compat/entry.S       2007-02-28 
12:08:44.000000000 +0100
@@ -87,7 +87,7 @@ ENTRY(compat_test_all_events)
         leaq  irq_stat(%rip),%rcx
         testl $~0,(%rcx,%rax,1)
         jnz   compat_process_softirqs
-        btrq  $_VCPUF_nmi_pending,VCPU_flags(%rbx)
+        lock btrl $_VCPUF_nmi_pending,VCPU_flags(%rbx)
         jc    compat_process_nmi
 compat_test_guest_events:
         movq  VCPU_vcpu_info(%rbx),%rax
@@ -101,7 +101,7 @@ compat_test_guest_events:
         movl  VCPU_event_addr(%rbx),%eax
         movl  %eax,TRAPBOUNCE_eip(%rdx)
         movl  VCPU_event_sel(%rbx),%eax
-        movl  %eax,TRAPBOUNCE_cs(%rdx)
+        movw  %ax,TRAPBOUNCE_cs(%rdx)
         movw  $TBF_INTERRUPT,TRAPBOUNCE_flags(%rdx)
         call  compat_create_bounce_frame
         jmp   compat_test_all_events
@@ -116,20 +116,21 @@ compat_process_softirqs:
        ALIGN
 /* %rbx: struct vcpu */
 compat_process_nmi:
-        movl  VCPU_nmi_addr(%rbx),%eax
+        movzwl VCPU_nmi_cs(%rbx),%eax
+        movl  VCPU_nmi_addr(%rbx),%ecx
         testl %eax,%eax
         jz    compat_test_all_events
-        btsq  $_VCPUF_nmi_masked,VCPU_flags(%rbx)
+        lock btsl $_VCPUF_nmi_masked,VCPU_flags(%rbx)
         jc    1f
         sti
         leaq  VCPU_trap_bounce(%rbx),%rdx
-        movl  %eax,TRAPBOUNCE_eip(%rdx)
-        movl  $FLAT_COMPAT_KERNEL_CS,TRAPBOUNCE_cs(%rdx)
+        movl  %ecx,TRAPBOUNCE_eip(%rdx)
+        movw  %ax,TRAPBOUNCE_cs(%rdx)
         movw  $TBF_INTERRUPT,TRAPBOUNCE_flags(%rdx)
         call  compat_create_bounce_frame
         jmp   compat_test_all_events
 1:
-        btsq  $_VCPUF_nmi_pending,VCPU_flags(%rbx)
+        lock btsl $_VCPUF_nmi_pending,VCPU_flags(%rbx)
         jmp   compat_test_guest_events
 
 compat_bad_hypercall:
@@ -164,7 +165,7 @@ compat_failsafe_callback:
         movl  VCPU_failsafe_addr(%rbx),%eax
         movl  %eax,TRAPBOUNCE_eip(%rdx)
         movl  VCPU_failsafe_sel(%rbx),%eax
-        movl  %eax,TRAPBOUNCE_cs(%rdx)
+        movw  %ax,TRAPBOUNCE_cs(%rdx)
         movw  $TBF_FAILSAFE,TRAPBOUNCE_flags(%rdx)
         btq   $_VGCF_failsafe_disables_events,VCPU_guest_context_flags(%rbx)
         jnc   1f
Index: 2007-02-27/xen/arch/x86/x86_64/compat/traps.c
===================================================================
--- 2007-02-27.orig/xen/arch/x86/x86_64/compat/traps.c  2007-02-28 
12:14:26.000000000 +0100
+++ 2007-02-27/xen/arch/x86/x86_64/compat/traps.c       2007-02-28 
12:08:44.000000000 +0100
@@ -287,6 +287,12 @@ int compat_set_trap_table(XEN_GUEST_HAND
         if ( cur.address == 0 )
             break;
 
+        if ( cur.vector == 2 && !TI_GET_IF(&cur) )
+        {
+            rc = -EINVAL;
+            break;
+        }
+
         fixup_guest_code_selector(current->domain, cur.cs);
 
         XLAT_trap_info(dst + cur.vector, &cur);
Index: 2007-02-27/xen/arch/x86/x86_64/entry.S
===================================================================
--- 2007-02-27.orig/xen/arch/x86/x86_64/entry.S 2007-02-28 12:14:26.000000000 
+0100
+++ 2007-02-27/xen/arch/x86/x86_64/entry.S      2007-02-28 12:08:44.000000000 
+0100
@@ -177,7 +177,7 @@ test_all_events:
         leaq  irq_stat(%rip),%rcx
         testl $~0,(%rcx,%rax,1)
         jnz   process_softirqs
-        btr   $_VCPUF_nmi_pending,VCPU_flags(%rbx)
+        lock btrl $_VCPUF_nmi_pending,VCPU_flags(%rbx)
         jc    process_nmi
 test_guest_events:
         movq  VCPU_vcpu_info(%rbx),%rax
@@ -207,7 +207,7 @@ process_nmi:
         movq VCPU_nmi_addr(%rbx),%rax
         test %rax,%rax
         jz   test_all_events
-        bts  $_VCPUF_nmi_masked,VCPU_flags(%rbx)
+        lock btsl $_VCPUF_nmi_masked,VCPU_flags(%rbx)
         jc   1f
         sti
         leaq VCPU_trap_bounce(%rbx),%rdx
@@ -215,7 +215,7 @@ process_nmi:
         movw $TBF_INTERRUPT,TRAPBOUNCE_flags(%rdx)
         call create_bounce_frame
         jmp  test_all_events
-1:      bts  $_VCPUF_nmi_pending,VCPU_flags(%rbx)
+1:      lock btsl $_VCPUF_nmi_pending,VCPU_flags(%rbx)
         jmp  test_guest_events
 
 bad_hypercall:
Index: 2007-02-27/xen/arch/x86/x86_64/physdev.c
===================================================================
--- 2007-02-27.orig/xen/arch/x86/x86_64/physdev.c       2007-02-28 
12:14:26.000000000 +0100
+++ 2007-02-27/xen/arch/x86/x86_64/physdev.c    2007-02-28 12:08:44.000000000 
+0100
@@ -30,6 +30,10 @@
 #define physdev_irq_status_query   compat_physdev_irq_status_query
 #define physdev_irq_status_query_t physdev_irq_status_query_compat_t
 
+#define xen_physdev_send_nmi       physdev_send_nmi
+CHECK_physdev_send_nmi;
+#undef xen_physdev_send_nmi
+
 #define COMPAT
 #undef guest_handle_okay
 #define guest_handle_okay          compat_handle_okay
Index: 2007-02-27/xen/common/kernel.c
===================================================================
--- 2007-02-27.orig/xen/common/kernel.c 2007-02-28 12:14:26.000000000 +0100
+++ 2007-02-27/xen/common/kernel.c      2007-02-28 12:08:44.000000000 +0100
@@ -246,16 +246,20 @@ long register_guest_nmi_callback(unsigne
     struct vcpu *v = current;
     struct domain *d = current->domain;
 
-    if ( (d->domain_id != 0) || (v->vcpu_id != 0) )
-        return -EINVAL;
-
     v->nmi_addr = address;
 #ifdef CONFIG_X86
+    v->arch.guest_context.trap_ctxt[2].vector = 2;
+    v->arch.guest_context.trap_ctxt[2].flags = 0;
+    TI_SET_IF(v->arch.guest_context.trap_ctxt + 2, 1);
+    v->arch.guest_context.trap_ctxt[2].cs =
+        !IS_COMPAT(d) ? FLAT_KERNEL_CS : FLAT_COMPAT_KERNEL_CS;
+
     /*
      * If no handler was registered we can 'lose the NMI edge'. Re-assert it
      * now.
      */
-    if ( arch_get_nmi_reason(d) != 0 )
+    if ( d->domain_id == 0 && v->vcpu_id == 0 &&
+         arch_get_nmi_reason(d) != 0 )
         set_bit(_VCPUF_nmi_pending, &v->vcpu_flags);
 #endif
 
@@ -266,6 +270,11 @@ long unregister_guest_nmi_callback(void)
 {
     struct vcpu *v = current;
 
+#ifdef CONFIG_X86
+    v->arch.guest_context.trap_ctxt[2].cs = 0;
+    v->arch.guest_context.trap_ctxt[2].vector = 0;
+    v->arch.guest_context.trap_ctxt[2].flags = 0;
+#endif
     v->nmi_addr = 0;
 
     return 0;
Index: 2007-02-27/xen/include/public/physdev.h
===================================================================
--- 2007-02-27.orig/xen/include/public/physdev.h        2007-02-28 
12:14:26.000000000 +0100
+++ 2007-02-27/xen/include/public/physdev.h     2007-02-28 12:08:44.000000000 
+0100
@@ -119,6 +119,22 @@ typedef struct physdev_irq physdev_irq_t
 DEFINE_XEN_GUEST_HANDLE(physdev_irq_t);
 
 /*
+ * Allocate or free a physical upcall vector for the specified IRQ line.
+ * @arg == pointer to physdev_irq structure.
+ */
+#define PHYSDEVOP_send_nmi              13
+struct physdev_send_nmi {
+    /* IN */
+    domid_t domain;
+    uint32_t vcpu;
+};
+typedef struct physdev_send_nmi physdev_send_nmi_t;
+DEFINE_XEN_GUEST_HANDLE(physdev_send_nmi_t);
+
+#define XEN_SEND_NMI_ALL          (~(uint32_t)0)
+#define XEN_SEND_NMI_ALL_BUT_SELF (~(uint32_t)1)
+
+/*
  * Argument to physdev_op_compat() hypercall. Superceded by new physdev_op()
  * hypercall since 0x00030202.
  */
Index: 2007-02-27/xen/include/xen/sched.h
===================================================================
--- 2007-02-27.orig/xen/include/xen/sched.h     2007-02-28 12:14:26.000000000 
+0100
+++ 2007-02-27/xen/include/xen/sched.h  2007-02-28 12:08:44.000000000 +0100
@@ -108,7 +108,11 @@ struct vcpu 
     /* Bitmask of CPUs on which this VCPU may run. */
     cpumask_t        cpu_affinity;
 
+#ifndef CONFIG_X86
     unsigned long    nmi_addr;      /* NMI callback address. */
+#else
+# define nmi_addr arch.guest_context.trap_ctxt[2].address
+#endif
 
     /* Bitmask of CPUs which are holding onto this VCPU's state. */
     cpumask_t        vcpu_dirty_cpumask;
Index: 2007-02-27/xen/include/xlat.lst
===================================================================
--- 2007-02-27.orig/xen/include/xlat.lst        2007-02-28 12:14:26.000000000 
+0100
+++ 2007-02-27/xen/include/xlat.lst     2007-02-28 12:08:44.000000000 +0100
@@ -36,6 +36,7 @@
 !      memory_map                      memory.h
 !      memory_reservation              memory.h
 !      translate_gpfn_list             memory.h
+?      physdev_send_nmi                physdev.h
 !      sched_poll                      sched.h
 ?      sched_remote_shutdown           sched.h
 ?      sched_shutdown                  sched.h



_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.