mirror of
https://github.com/xen-project/xen.git
synced 2026-10-29 18:50:05 +01:00
Allow unknown NMIs to be propagated to domain0 via new
VIRQ_NMI. Also simplify x86_32 NMI handling -- there's no need to greedily consume IO/parity errors in assembly code as they can be deferred without causing an interrupt storm (the NMI pin is always edge-triggered even though the sources are level-asserted). Signed-off-by: Keir Fraser <keir@xensource.com>
This commit is contained in:
1 parent
4cbd6b3272
commit
34c31a402c
7 files changed
+75
-111
No files matched your search
+59
-36
@@ -1074,29 +1074,35 @@ asmlinkage int do_general_protection(struct cpu_user_regs *regs)
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned long nmi_softirq_reason;
|
||||
static void nmi_softirq(void)
|
||||
|
||||
/* 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)
|
||||
{
|
||||
if ( dom0 == NULL )
|
||||
return;
|
||||
|
||||
if ( test_and_clear_bit(0, &nmi_softirq_reason) )
|
||||
if ( test_and_clear_bit(NMI_DOM0_PARITY_ERR, &nmi_dom0_softirq_reason) )
|
||||
send_guest_virq(dom0->vcpu[0], VIRQ_PARITY_ERR);
|
||||
|
||||
if ( test_and_clear_bit(1, &nmi_softirq_reason) )
|
||||
if ( test_and_clear_bit(NMI_DOM0_IO_ERR, &nmi_dom0_softirq_reason) )
|
||||
send_guest_virq(dom0->vcpu[0], VIRQ_IO_ERR);
|
||||
|
||||
if ( test_and_clear_bit(NMI_DOM0_UNKNOWN, &nmi_dom0_softirq_reason) )
|
||||
send_guest_virq(dom0->vcpu[0], VIRQ_NMI);
|
||||
}
|
||||
|
||||
asmlinkage void mem_parity_error(struct cpu_user_regs *regs)
|
||||
{
|
||||
/* Clear and disable the parity-error line. */
|
||||
outb((inb(0x61)&15)|4,0x61);
|
||||
|
||||
switch ( opt_nmi[0] )
|
||||
{
|
||||
case 'd': /* 'dom0' */
|
||||
set_bit(0, &nmi_softirq_reason);
|
||||
raise_softirq(NMI_SOFTIRQ);
|
||||
set_bit(NMI_DOM0_PARITY_ERR, &nmi_dom0_softirq_reason);
|
||||
raise_softirq(NMI_DOM0_SOFTIRQ);
|
||||
case 'i': /* 'ignore' */
|
||||
break;
|
||||
default: /* 'fatal' */
|
||||
@@ -1104,18 +1110,19 @@ asmlinkage void mem_parity_error(struct cpu_user_regs *regs)
|
||||
printk("\n\nNMI - MEMORY ERROR\n");
|
||||
fatal_trap(TRAP_nmi, regs);
|
||||
}
|
||||
|
||||
outb((inb(0x61) & 0x0f) | 0x04, 0x61); /* clear-and-disable parity check */
|
||||
mdelay(1);
|
||||
outb((inb(0x61) & 0x0b) | 0x00, 0x61); /* enable parity check */
|
||||
}
|
||||
|
||||
asmlinkage void io_check_error(struct cpu_user_regs *regs)
|
||||
{
|
||||
/* Clear and disable the I/O-error line. */
|
||||
outb((inb(0x61)&15)|8,0x61);
|
||||
|
||||
switch ( opt_nmi[0] )
|
||||
{
|
||||
case 'd': /* 'dom0' */
|
||||
set_bit(0, &nmi_softirq_reason);
|
||||
raise_softirq(NMI_SOFTIRQ);
|
||||
set_bit(NMI_DOM0_IO_ERR, &nmi_dom0_softirq_reason);
|
||||
raise_softirq(NMI_DOM0_SOFTIRQ);
|
||||
case 'i': /* 'ignore' */
|
||||
break;
|
||||
default: /* 'fatal' */
|
||||
@@ -1123,43 +1130,59 @@ asmlinkage void io_check_error(struct cpu_user_regs *regs)
|
||||
printk("\n\nNMI - I/O ERROR\n");
|
||||
fatal_trap(TRAP_nmi, regs);
|
||||
}
|
||||
|
||||
outb((inb(0x61) & 0x0f) | 0x08, 0x61); /* clear-and-disable IOCK */
|
||||
mdelay(1);
|
||||
outb((inb(0x61) & 0x07) | 0x00, 0x61); /* enable IOCK */
|
||||
}
|
||||
|
||||
static void unknown_nmi_error(unsigned char reason)
|
||||
{
|
||||
printk("Uhhuh. NMI received for unknown reason %02x.\n", reason);
|
||||
printk("Dazed and confused, but trying to continue\n");
|
||||
printk("Do you have a strange power saving mode enabled?\n");
|
||||
}
|
||||
|
||||
static void default_do_nmi(struct cpu_user_regs *regs, unsigned long reason)
|
||||
{
|
||||
if ( nmi_watchdog )
|
||||
nmi_watchdog_tick(regs);
|
||||
|
||||
if ( reason & 0x80 )
|
||||
mem_parity_error(regs);
|
||||
else if ( reason & 0x40 )
|
||||
io_check_error(regs);
|
||||
else if ( !nmi_watchdog )
|
||||
unknown_nmi_error((unsigned char)(reason&0xff));
|
||||
switch ( opt_nmi[0] )
|
||||
{
|
||||
case 'd': /* 'dom0' */
|
||||
set_bit(NMI_DOM0_UNKNOWN, &nmi_dom0_softirq_reason);
|
||||
raise_softirq(NMI_DOM0_SOFTIRQ);
|
||||
case 'i': /* 'ignore' */
|
||||
break;
|
||||
default: /* 'fatal' */
|
||||
printk("Uhhuh. NMI received for unknown reason %02x.\n", reason);
|
||||
printk("Dazed and confused, but trying to continue\n");
|
||||
printk("Do you have a strange power saving mode enabled?\n");
|
||||
}
|
||||
}
|
||||
|
||||
static int dummy_nmi_callback(struct cpu_user_regs *regs, int cpu)
|
||||
{
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static nmi_callback_t nmi_callback = dummy_nmi_callback;
|
||||
|
||||
asmlinkage void do_nmi(struct cpu_user_regs *regs, unsigned long reason)
|
||||
asmlinkage void do_nmi(struct cpu_user_regs *regs)
|
||||
{
|
||||
unsigned int cpu = smp_processor_id();
|
||||
unsigned char reason;
|
||||
|
||||
++nmi_count(cpu);
|
||||
|
||||
if ( !nmi_callback(regs, cpu) )
|
||||
default_do_nmi(regs, reason);
|
||||
if ( nmi_callback(regs, cpu) )
|
||||
return;
|
||||
|
||||
if ( nmi_watchdog )
|
||||
nmi_watchdog_tick(regs);
|
||||
|
||||
/* Only the BSP gets external NMIs from the system. */
|
||||
if ( cpu == 0 )
|
||||
{
|
||||
reason = inb(0x61);
|
||||
if ( reason & 0x80 )
|
||||
mem_parity_error(regs);
|
||||
else if ( reason & 0x40 )
|
||||
io_check_error(regs);
|
||||
else if ( !nmi_watchdog )
|
||||
unknown_nmi_error((unsigned char)(reason&0xff));
|
||||
}
|
||||
}
|
||||
|
||||
void set_nmi_callback(nmi_callback_t callback)
|
||||
@@ -1169,7 +1192,7 @@ void set_nmi_callback(nmi_callback_t callback)
|
||||
|
||||
void unset_nmi_callback(void)
|
||||
{
|
||||
nmi_callback = dummy_nmi_callback;
|
||||
nmi_callback = dummy_nmi_callback;
|
||||
}
|
||||
|
||||
asmlinkage int math_state_restore(struct cpu_user_regs *regs)
|
||||
@@ -1318,7 +1341,7 @@ void __init trap_init(void)
|
||||
|
||||
cpu_init();
|
||||
|
||||
open_softirq(NMI_SOFTIRQ, nmi_softirq);
|
||||
open_softirq(NMI_DOM0_SOFTIRQ, nmi_dom0_softirq);
|
||||
}
|
||||
|
||||
|
||||
|
||||
+2
-2
@@ -335,7 +335,7 @@ static inline int long_mode_do_msr_write(struct cpu_user_regs *regs){
|
||||
|
||||
extern long evtchn_send(int lport);
|
||||
extern long do_block(void);
|
||||
void do_nmi(struct cpu_user_regs *, unsigned long);
|
||||
void do_nmi(struct cpu_user_regs *);
|
||||
|
||||
static int check_vmx_controls(ctrls, msr)
|
||||
{
|
||||
@@ -1850,7 +1850,7 @@ asmlinkage void vmx_vmexit_handler(struct cpu_user_regs regs)
|
||||
break;
|
||||
}
|
||||
case TRAP_nmi:
|
||||
do_nmi(®s, 0);
|
||||
do_nmi(®s);
|
||||
break;
|
||||
default:
|
||||
vmx_reflect_exception(v);
|
||||
|
||||
@@ -601,15 +601,7 @@ ENTRY(nmi)
|
||||
pushl %eax
|
||||
SAVE_ALL_NOSEGREGS(a)
|
||||
|
||||
# Check for hardware problems.
|
||||
inb $0x61,%al
|
||||
testb $0x80,%al
|
||||
jne nmi_parity_err
|
||||
testb $0x40,%al
|
||||
jne nmi_io_err
|
||||
movl %eax,%ebx
|
||||
|
||||
# Okay, its almost a normal NMI tick. We can only process it if:
|
||||
# We can only process the NMI if:
|
||||
# A. We are the outermost Xen activation (in which case we have
|
||||
# the selectors safely saved on our stack)
|
||||
# B. DS and ES contain sane Xen values.
|
||||
@@ -619,7 +611,7 @@ ENTRY(nmi)
|
||||
movl UREGS_eflags(%esp),%eax
|
||||
movb UREGS_cs(%esp),%al
|
||||
testl $(3|X86_EFLAGS_VM),%eax
|
||||
jnz do_watchdog_tick
|
||||
jnz continue_nmi
|
||||
movl %ds,%eax
|
||||
cmpw $(__HYPERVISOR_DS),%ax
|
||||
jne defer_nmi
|
||||
@@ -627,15 +619,14 @@ ENTRY(nmi)
|
||||
cmpw $(__HYPERVISOR_DS),%ax
|
||||
jne defer_nmi
|
||||
|
||||
do_watchdog_tick:
|
||||
continue_nmi:
|
||||
movl $(__HYPERVISOR_DS),%edx
|
||||
movl %edx,%ds
|
||||
movl %edx,%es
|
||||
movl %esp,%edx
|
||||
pushl %ebx # reason
|
||||
pushl %edx # regs
|
||||
pushl %edx
|
||||
call do_nmi
|
||||
addl $8,%esp
|
||||
addl $4,%esp
|
||||
jmp ret_from_intr
|
||||
|
||||
defer_nmi:
|
||||
@@ -649,55 +640,6 @@ defer_nmi:
|
||||
TRAP_deferred_nmi),%ss:APIC_ICR(%eax)
|
||||
jmp restore_all_xen
|
||||
|
||||
nmi_parity_err:
|
||||
# Clear and disable the parity-error line
|
||||
andb $0xf,%al
|
||||
orb $0x4,%al
|
||||
outb %al,$0x61
|
||||
cmpb $'i',%ss:opt_nmi # nmi=ignore
|
||||
je nmi_out
|
||||
bts $0,%ss:nmi_softirq_reason
|
||||
bts $NMI_SOFTIRQ,%ss:irq_stat
|
||||
cmpb $'d',%ss:opt_nmi # nmi=dom0
|
||||
je nmi_out
|
||||
movl $(__HYPERVISOR_DS),%edx # nmi=fatal
|
||||
movl %edx,%ds
|
||||
movl %edx,%es
|
||||
movl %esp,%edx
|
||||
push %edx
|
||||
call mem_parity_error
|
||||
addl $4,%esp
|
||||
nmi_out:movl %ss:UREGS_eflags(%esp),%eax
|
||||
movb %ss:UREGS_cs(%esp),%al
|
||||
testl $(3|X86_EFLAGS_VM),%eax
|
||||
jz restore_all_xen
|
||||
movl $(__HYPERVISOR_DS),%edx
|
||||
movl %edx,%ds
|
||||
movl %edx,%es
|
||||
GET_CURRENT(%ebx)
|
||||
jmp test_all_events
|
||||
|
||||
nmi_io_err:
|
||||
# Clear and disable the I/O-error line
|
||||
andb $0xf,%al
|
||||
orb $0x8,%al
|
||||
outb %al,$0x61
|
||||
cmpb $'i',%ss:opt_nmi # nmi=ignore
|
||||
je nmi_out
|
||||
bts $1,%ss:nmi_softirq_reason
|
||||
bts $NMI_SOFTIRQ,%ss:irq_stat
|
||||
cmpb $'d',%ss:opt_nmi # nmi=dom0
|
||||
je nmi_out
|
||||
movl $(__HYPERVISOR_DS),%edx # nmi=fatal
|
||||
movl %edx,%ds
|
||||
movl %edx,%es
|
||||
movl %esp,%edx
|
||||
push %edx
|
||||
call io_check_error
|
||||
addl $4,%esp
|
||||
jmp nmi_out
|
||||
|
||||
|
||||
ENTRY(setup_vm86_frame)
|
||||
# Copies the entire stack frame forwards by 16 bytes.
|
||||
.macro copy_vm86_words count=18
|
||||
|
||||
@@ -160,9 +160,9 @@ asmlinkage void do_double_fault(void)
|
||||
BUILD_SMP_INTERRUPT(deferred_nmi, TRAP_deferred_nmi)
|
||||
asmlinkage void smp_deferred_nmi(struct cpu_user_regs regs)
|
||||
{
|
||||
asmlinkage void do_nmi(struct cpu_user_regs *, unsigned long);
|
||||
asmlinkage void do_nmi(struct cpu_user_regs *);
|
||||
ack_APIC_irq();
|
||||
do_nmi(®s, 0);
|
||||
do_nmi(®s);
|
||||
}
|
||||
|
||||
void __init percpu_traps_init(void)
|
||||
|
||||
@@ -567,9 +567,7 @@ ENTRY(double_fault)
|
||||
ENTRY(nmi)
|
||||
pushq $0
|
||||
SAVE_ALL
|
||||
inb $0x61,%al
|
||||
movl %eax,%esi # reason
|
||||
movq %rsp,%rdi # regs
|
||||
movq %rsp,%rdi
|
||||
call do_nmi
|
||||
jmp restore_all_xen
|
||||
|
||||
|
||||
@@ -67,12 +67,13 @@
|
||||
*/
|
||||
#define VIRQ_TIMER 0 /* Timebase update, and/or requested timeout. */
|
||||
#define VIRQ_DEBUG 1 /* Request guest to dump debug info. */
|
||||
#define VIRQ_CONSOLE 2 /* (DOM0) bytes received on emergency console. */
|
||||
#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. */
|
||||
#define VIRQ_IO_ERR 5 /* (DOM0) NMI I/O error. */
|
||||
#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 NR_VIRQS 7
|
||||
#define VIRQ_NMI 7 /* (DOM0) Unknown NMI (not from ISA port 0x61).*/
|
||||
#define NR_VIRQS 8
|
||||
|
||||
/*
|
||||
* MMU-UPDATE REQUESTS
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#define SCHEDULE_SOFTIRQ 1
|
||||
#define NEW_TLBFLUSH_CLOCK_PERIOD_SOFTIRQ 2
|
||||
#define KEYPRESS_SOFTIRQ 3
|
||||
#define NMI_SOFTIRQ 4
|
||||
#define NMI_DOM0_SOFTIRQ 4
|
||||
#define PAGE_SCRUB_SOFTIRQ 5
|
||||
#define DOMAIN_SHUTDOWN_FINALISE_SOFTIRQ 6
|
||||
#define NR_SOFTIRQS 7
|
||||
|
||||