Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

xen: Support 64-bit PV guest receiving NMIs

This is based on a patch that Zhenzhong Duan had sent - which
was missing some of the remaining pieces. The kernel has the
logic to handle Xen-type-exceptions using the paravirt interface
in the assembler code (see PARAVIRT_ADJUST_EXCEPTION_FRAME -
pv_irq_ops.adjust_exception_frame and and INTERRUPT_RETURN -
pv_cpu_ops.iret).

That means the nmi handler (and other exception handlers) use
the hypervisor iret.

The other changes that would be neccessary for this would
be to translate the NMI_VECTOR to one of the entries on the
ipi_vector and make xen_send_IPI_mask_allbutself use different
events.

Fortunately for us commit 1db01b4903639fcfaec213701a494fe3fb2c490b
(xen: Clean up apic ipi interface) implemented this and we piggyback
on the cleanup such that the apic IPI interface will pass the right
vector value for NMI.

With this patch we can trigger NMIs within a PV guest (only tested
x86_64).

For this to work with normal PV guests (not initial domain)
we need the domain to be able to use the APIC ops - they are
already implemented to use the Xen event channels. For that
to be turned on in a PV domU we need to remove the masking
of X86_FEATURE_APIC.

Incidentally that means kgdb will also now work within
a PV guest without using the 'nokgdbroundup' workaround.

Note that the 32-bit version is different and this patch
does not enable that.

CC: Lisa Nguyen <lisa@xenapiadmin.com>
CC: Ben Guthro <benjamin.guthro@citrix.com>
CC: Zhenzhong Duan <zhenzhong.duan@oracle.com>
Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
[v1: Fixed up per David Vrabel comments]
Reviewed-by: Ben Guthro <benjamin.guthro@citrix.com>
Reviewed-by: David Vrabel <david.vrabel@citrix.com>

authored by

Konrad Rzeszutek Wilk and committed by
Konrad Rzeszutek Wilk
6efa20e4 c095ba72

+38 -8
+1
arch/x86/include/asm/xen/events.h
··· 7 7 XEN_CALL_FUNCTION_SINGLE_VECTOR, 8 8 XEN_SPIN_UNLOCK_VECTOR, 9 9 XEN_IRQ_WORK_VECTOR, 10 + XEN_NMI_VECTOR, 10 11 11 12 XEN_NR_IPIS, 12 13 };
+8 -5
arch/x86/xen/enlighten.c
··· 427 427 428 428 if (!xen_initial_domain()) 429 429 cpuid_leaf1_edx_mask &= 430 - ~((1 << X86_FEATURE_APIC) | /* disable local APIC */ 431 - (1 << X86_FEATURE_ACPI)); /* disable ACPI */ 430 + ~((1 << X86_FEATURE_ACPI)); /* disable ACPI */ 432 431 433 432 cpuid_leaf1_ecx_mask &= ~(1 << (X86_FEATURE_X2APIC % 32)); 434 433 ··· 734 735 addr = (unsigned long)xen_int3; 735 736 else if (addr == (unsigned long)stack_segment) 736 737 addr = (unsigned long)xen_stack_segment; 737 - else if (addr == (unsigned long)double_fault || 738 - addr == (unsigned long)nmi) { 738 + else if (addr == (unsigned long)double_fault) { 739 739 /* Don't need to handle these */ 740 740 return 0; 741 741 #ifdef CONFIG_X86_MCE ··· 745 747 */ 746 748 ; 747 749 #endif 748 - } else { 750 + } else if (addr == (unsigned long)nmi) 751 + /* 752 + * Use the native version as well. 753 + */ 754 + ; 755 + else { 749 756 /* Some other trap using IST? */ 750 757 if (WARN_ON(val->ist != 0)) 751 758 return 0;
+11 -2
arch/x86/xen/setup.c
··· 33 33 /* These are code, but not functions. Defined in entry.S */ 34 34 extern const char xen_hypervisor_callback[]; 35 35 extern const char xen_failsafe_callback[]; 36 + #ifdef CONFIG_X86_64 37 + extern const char nmi[]; 38 + #endif 36 39 extern void xen_sysenter_target(void); 37 40 extern void xen_syscall_target(void); 38 41 extern void xen_syscall32_target(void); ··· 528 525 } 529 526 #endif /* CONFIG_X86_64 */ 530 527 } 531 - 528 + void __cpuinit xen_enable_nmi(void) 529 + { 530 + #ifdef CONFIG_X86_64 531 + if (register_callback(CALLBACKTYPE_nmi, nmi)) 532 + BUG(); 533 + #endif 534 + } 532 535 void __init xen_arch_setup(void) 533 536 { 534 537 xen_panic_handler_init(); ··· 552 543 553 544 xen_enable_sysenter(); 554 545 xen_enable_syscall(); 555 - 546 + xen_enable_nmi(); 556 547 #ifdef CONFIG_ACPI 557 548 if (!(xen_start_info->flags & SIF_INITDOMAIN)) { 558 549 printk(KERN_INFO "ACPI in unprivileged domain disabled\n");
+6
arch/x86/xen/smp.c
··· 572 572 case IRQ_WORK_VECTOR: 573 573 xen_vector = XEN_IRQ_WORK_VECTOR; 574 574 break; 575 + #ifdef CONFIG_X86_64 576 + case NMI_VECTOR: 577 + case APIC_DM_NMI: /* Some use that instead of NMI_VECTOR */ 578 + xen_vector = XEN_NMI_VECTOR; 579 + break; 580 + #endif 575 581 default: 576 582 xen_vector = -1; 577 583 printk(KERN_ERR "xen: vector 0x%x is not implemented\n",
+10 -1
drivers/xen/events.c
··· 56 56 #include <xen/interface/hvm/params.h> 57 57 #include <xen/interface/physdev.h> 58 58 #include <xen/interface/sched.h> 59 + #include <xen/interface/vcpu.h> 59 60 #include <asm/hw_irq.h> 60 61 61 62 /* ··· 1213 1212 1214 1213 void xen_send_IPI_one(unsigned int cpu, enum ipi_vector vector) 1215 1214 { 1216 - int irq = per_cpu(ipi_to_irq, cpu)[vector]; 1215 + int irq; 1216 + 1217 + if (unlikely(vector == XEN_NMI_VECTOR)) { 1218 + int rc = HYPERVISOR_vcpu_op(VCPUOP_send_nmi, cpu, NULL); 1219 + if (rc < 0) 1220 + printk(KERN_WARNING "Sending nmi to CPU%d failed (rc:%d)\n", cpu, rc); 1221 + return; 1222 + } 1223 + irq = per_cpu(ipi_to_irq, cpu)[vector]; 1217 1224 BUG_ON(irq < 0); 1218 1225 notify_remote_via_irq(irq); 1219 1226 }
+2
include/xen/interface/vcpu.h
··· 170 170 }; 171 171 DEFINE_GUEST_HANDLE_STRUCT(vcpu_register_vcpu_info); 172 172 173 + /* Send an NMI to the specified VCPU. @extra_arg == NULL. */ 174 + #define VCPUOP_send_nmi 11 173 175 #endif /* __XEN_PUBLIC_VCPU_H__ */