Pass NMIs to DOM0 via a dedicated callback, Xen/Linux x86_64 support.
authorIan.Campbell@xensource.com <Ian.Campbell@xensource.com>
Wed, 11 Jan 2006 15:53:59 +0000 (15:53 +0000)
committerIan.Campbell@xensource.com <Ian.Campbell@xensource.com>
Wed, 11 Jan 2006 15:53:59 +0000 (15:53 +0000)
Register our NMI handler with Xen. Do an iret via the hypervisor
whenever the return CS is > RING0.

Add include/asm-xen/asm-x86_64/nmi.h as a Xen modified copy of
include/asm-x86_64/nmi.h in order that we can implement
get_nmi_reason() in a manner suitable for Xen.

Signed-off-by: Ian Campbell <Ian.Campbell@XenSource.com>
linux-2.6-xen-sparse/arch/xen/x86_64/kernel/entry.S
linux-2.6-xen-sparse/arch/xen/x86_64/kernel/setup.c
linux-2.6-xen-sparse/arch/xen/x86_64/kernel/traps.c
linux-2.6-xen-sparse/include/asm-xen/asm-x86_64/hypercall.h
linux-2.6-xen-sparse/include/asm-xen/asm-x86_64/mach-xen/setup_arch_post.h
linux-2.6-xen-sparse/include/asm-xen/asm-x86_64/nmi.h [new file with mode: 0644]

index c8fa7871d857d10a9c6dfafa6580b22fff2b0ccf..9ecc36e5ca9f9e960004cf7fddcefad9af90b385 100644 (file)
        .endm
 
         /*
-         * Must be consistent with the definition in arch_x86_64.h:    
-         *     struct switch_to_user {
+         * Must be consistent with the definition in arch-x86_64.h:    
+         *     struct iret_context {
          *        u64 rax, r11, rcx, flags, rip, cs, rflags, rsp, ss;
          *     };
          * #define VGCF_IN_SYSCALL (1<<8) 
          */
-        .macro SWITCH_TO_USER flag
+        .macro HYPERVISOR_IRET flag
         subq $8*4,%rsp                   # reuse rip, cs, rflags, rsp, ss in the stack
         movq %rax,(%rsp)
         movq %r11,1*8(%rsp)
         movq %rcx,2*8(%rsp)              # we saved %rcx upon exceptions
         movq $\flag,3*8(%rsp)
-        movq $__HYPERVISOR_switch_to_user,%rax
+        movq $__HYPERVISOR_iret,%rax
         syscall
         .endm
 
@@ -225,7 +225,7 @@ sysret_check:
        jnz  sysret_careful 
         XEN_UNBLOCK_EVENTS(%rsi)                
        RESTORE_ARGS 0,8,0
-        SWITCH_TO_USER VGCF_IN_SYSCALL
+        HYPERVISOR_IRET VGCF_IN_SYSCALL
 
        /* Handle reschedules */
        /* edx: work, edi: workmask */  
@@ -478,7 +478,7 @@ kernel_mode:
         orb   $3,1*8(%rsp)
        iretq
 user_mode:
-       SWITCH_TO_USER 0                        
+       HYPERVISOR_IRET 0
        
        /* edi: workmask, edx: work */  
 retint_careful:
@@ -719,6 +719,16 @@ ENTRY(do_hypervisor_callback)   # do_hyperviosr_callback(struct *pt_regs)
        call evtchn_do_upcall
         jmp  error_exit
 
+ENTRY(nmi)
+       zeroentry do_nmi_callback
+ENTRY(do_nmi_callback)
+        addq $8, %rsp
+        call do_nmi
+        RESTORE_REST
+        XEN_BLOCK_EVENTS(%rsi)
+        GET_THREAD_INFO(%rcx)
+        jmp  retint_restore_args
+
         ALIGN
 restore_all_enable_events:  
        XEN_UNBLOCK_EVENTS(%rsi)        # %rsi is already set up...
@@ -733,7 +743,7 @@ scrit:      /**** START OF CRITICAL REGION ****/
         orb   $3,1*8(%rsp)
         iretq
 crit_user_mode:
-        SWITCH_TO_USER 0
+        HYPERVISOR_IRET 0
         
 14:    XEN_LOCKED_BLOCK_EVENTS(%rsi)
        XEN_PUT_VCPU_INFO(%rsi)
index f82f8aac84e3767ac04dc1b3966e886e2644fb17..ef7deb8938b1b523dffb992e3fcc1072500bd83d 100644 (file)
@@ -62,6 +62,7 @@
 #include <asm-xen/xen-public/physdev.h>
 #include "setup_arch_pre.h"
 #include <asm/hypervisor.h>
+#include <asm-xen/xen-public/nmi.h>
 #define PFN_UP(x)       (((x) + PAGE_SIZE-1) >> PAGE_SHIFT)
 #define PFN_PHYS(x)     ((x) << PAGE_SHIFT)
 #define end_pfn_map end_pfn
index 227042ba63a750194f5fe81edd3b0deb7092e06c..6229915b11943710e561435831487b0241affea0 100644 (file)
@@ -559,9 +559,11 @@ static void mem_parity_error(unsigned char reason, struct pt_regs * regs)
        printk("Uhhuh. NMI received. Dazed and confused, but trying to continue\n");
        printk("You probably have a hardware problem with your RAM chips\n");
 
+#if 0 /* XEN */
        /* Clear and disable the memory parity error line. */
        reason = (reason & 0xf) | 4;
        outb(reason, 0x61);
+#endif /* XEN */
 }
 
 static void io_check_error(unsigned char reason, struct pt_regs * regs)
@@ -569,12 +571,14 @@ static void io_check_error(unsigned char reason, struct pt_regs * regs)
        printk("NMI: IOCK error (debug interrupt?)\n");
        show_registers(regs);
 
+#if 0 /* XEN */
        /* Re-enable the IOCK line, wait for a few seconds */
        reason = (reason & 0xf) | 8;
        outb(reason, 0x61);
        mdelay(2000);
        reason &= ~8;
        outb(reason, 0x61);
+#endif /* XEN */
 }
 
 static void unknown_nmi_error(unsigned char reason, struct pt_regs * regs)
index bb338772d08c08fbaa4be5dd8734565c671d1aaf..521f004c000e510522acb9c11950bbbb83dcf205 100644 (file)
@@ -287,9 +287,9 @@ HYPERVISOR_vcpu_op(
 }
 
 static inline int
-HYPERVISOR_switch_to_user(void)
+HYPERVISOR_iret(void)
 {
-       return _hypercall0(int, switch_to_user);
+       return _hypercall0(int, iret);
 }
 
 static inline int
@@ -307,6 +307,14 @@ HYPERVISOR_suspend(
                           SHUTDOWN_suspend, srec);
 }
 
+static inline int
+HYPERVISOR_nmi_op(
+       unsigned long op,
+       unsigned long arg)
+{
+       return _hypercall2(int, nmi_op, op, arg);
+}
+
 #endif /* __HYPERCALL_H__ */
 
 /*
index 7be26e86600b14ff9688f965b0468860c2ac0a66..bad118e44cbe3ac929b3fcd0e47ab16dd921cc2d 100644 (file)
@@ -35,6 +35,7 @@ void __init machine_specific_modify_cpu_capabilities(struct cpuinfo_x86 *c)
 
 extern void hypervisor_callback(void);
 extern void failsafe_callback(void);
+extern void nmi(void);
 
 static void __init machine_specific_arch_setup(void)
 {
@@ -43,5 +44,7 @@ static void __init machine_specific_arch_setup(void)
                 (unsigned long) failsafe_callback,
                 (unsigned long) system_call);
 
+       HYPERVISOR_nmi_op(XENNMI_register_callback, (unsigned long)&nmi);
+
        machine_specific_modify_cpu_capabilities(&boot_cpu_data);
 }
diff --git a/linux-2.6-xen-sparse/include/asm-xen/asm-x86_64/nmi.h b/linux-2.6-xen-sparse/include/asm-xen/asm-x86_64/nmi.h
new file mode 100644 (file)
index 0000000..1c5d28d
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ *  linux/include/asm-i386/nmi.h
+ */
+#ifndef ASM_NMI_H
+#define ASM_NMI_H
+
+#include <linux/pm.h>
+
+#include <asm-xen/xen-public/nmi.h>
+
+struct pt_regs;
+typedef int (*nmi_callback_t)(struct pt_regs * regs, int cpu);
+/** 
+ * set_nmi_callback
+ *
+ * Set a handler for an NMI. Only one handler may be
+ * set. Return 1 if the NMI was handled.
+ */
+void set_nmi_callback(nmi_callback_t callback);
+/** 
+ * unset_nmi_callback
+ *
+ * Remove the handler previously set.
+ */
+void unset_nmi_callback(void);
+#ifdef CONFIG_PM
+/** Replace the PM callback routine for NMI. */
+struct pm_dev * set_nmi_pm_callback(pm_callback callback);
+
+/** Unset the PM callback routine back to the default. */
+void unset_nmi_pm_callback(struct pm_dev * dev);
+
+#else
+
+static inline struct pm_dev * set_nmi_pm_callback(pm_callback callback)
+{
+       return 0;
+} 
+static inline void unset_nmi_pm_callback(struct pm_dev * dev)
+{
+}
+
+#endif /* CONFIG_PM */
+extern void default_do_nmi(struct pt_regs *);
+extern void die_nmi(char *str, struct pt_regs *regs);
+
+static inline unsigned char get_nmi_reason(void)
+{
+        shared_info_t *s = HYPERVISOR_shared_info;
+        unsigned char reason = 0;
+
+        /* construct a value which looks like it came from
+         * port 0x61.
+         */
+        if (test_bit(_XEN_NMIREASON_io_error, &s->arch.nmi_reason))
+                reason |= 0x40;
+        if (test_bit(_XEN_NMIREASON_parity_error, &s->arch.nmi_reason))
+                reason |= 0x80;
+
+        return reason;
+}
+
+extern int panic_on_timeout;
+extern int unknown_nmi_panic;
+
+extern int check_nmi_watchdog(void);
+#endif /* ASM_NMI_H */