From: Keir Fraser Date: Mon, 19 May 2008 09:03:26 +0000 (+0100) Subject: svm: Better handling of NMI injection -- avoid nested NMIs. X-Git-Tag: archive/raspbian/4.8.0-1+rpi1~1^2~14212^2 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=8ef19bcf73c05ba49a1d606d994e67baad2a7c37;p=xen.git svm: Better handling of NMI injection -- avoid nested NMIs. We do this by emulating the NMI mask which blocks NMI delivery until next IRET on native hardware. Signed-off-by: Gianluca Guida Signed-off-by: Keir Fraser --- diff --git a/xen/arch/x86/hvm/svm/intr.c b/xen/arch/x86/hvm/svm/intr.c index 3463ac42a6..64b02f703c 100644 --- a/xen/arch/x86/hvm/svm/intr.c +++ b/xen/arch/x86/hvm/svm/intr.c @@ -51,6 +51,12 @@ static void svm_inject_nmi(struct vcpu *v) ASSERT(vmcb->eventinj.fields.v == 0); vmcb->eventinj = event; + + /* + * SVM does not virtualise the NMI mask, so we emulate it by intercepting + * the next IRET and blocking NMI injection until the intercept triggers. + */ + vmcb->general1_intercepts |= GENERAL1_INTERCEPT_IRET; } static void svm_inject_extint(struct vcpu *v, int vector) diff --git a/xen/arch/x86/hvm/svm/svm.c b/xen/arch/x86/hvm/svm/svm.c index d1ebd2a220..3468770bcb 100644 --- a/xen/arch/x86/hvm/svm/svm.c +++ b/xen/arch/x86/hvm/svm/svm.c @@ -367,15 +367,27 @@ static void svm_fpu_leave(struct vcpu *v) static unsigned int svm_get_interrupt_shadow(struct vcpu *v) { struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb; - return (vmcb->interrupt_shadow ? - (HVM_INTR_SHADOW_MOV_SS|HVM_INTR_SHADOW_STI) : 0); + unsigned int intr_shadow = 0; + + if ( vmcb->interrupt_shadow ) + intr_shadow |= HVM_INTR_SHADOW_MOV_SS | HVM_INTR_SHADOW_STI; + + if ( vmcb->general1_intercepts & GENERAL1_INTERCEPT_IRET ) + intr_shadow |= HVM_INTR_SHADOW_NMI; + + return intr_shadow; } static void svm_set_interrupt_shadow(struct vcpu *v, unsigned int intr_shadow) { struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb; + vmcb->interrupt_shadow = !!(intr_shadow & (HVM_INTR_SHADOW_MOV_SS|HVM_INTR_SHADOW_STI)); + + vmcb->general1_intercepts &= ~GENERAL1_INTERCEPT_IRET; + if ( intr_shadow & HVM_INTR_SHADOW_NMI ) + vmcb->general1_intercepts |= GENERAL1_INTERCEPT_IRET; } static int svm_guest_x86_mode(struct vcpu *v) @@ -1266,6 +1278,15 @@ asmlinkage void svm_vmexit_handler(struct cpu_user_regs *regs) reason = TSW_call_or_int; if ( (vmcb->exitinfo2 >> 44) & 1 ) errcode = (uint32_t)vmcb->exitinfo2; + + /* + * Some processors set the EXITINTINFO field when the task switch + * is caused by a task gate in the IDT. In this case we will be + * emulating the event injection, so we do not want the processor + * to re-inject the original event! + */ + vmcb->eventinj.bytes = 0; + hvm_task_switch((uint16_t)vmcb->exitinfo1, reason, errcode); break; } @@ -1331,6 +1352,19 @@ asmlinkage void svm_vmexit_handler(struct cpu_user_regs *regs) svm_do_nested_pgfault(vmcb->exitinfo2, regs); break; + case VMEXIT_IRET: + /* + * IRET clears the NMI mask. However because we clear the mask + * /before/ executing IRET, we set the interrupt shadow to prevent + * a pending NMI from being injected immediately. This will work + * perfectly unless the IRET instruction faults: in that case we + * may inject an NMI before the NMI handler's IRET instruction is + * retired. + */ + vmcb->general1_intercepts &= ~GENERAL1_INTERCEPT_IRET; + vmcb->interrupt_shadow = 1; + break; + default: exit_and_crash: gdprintk(XENLOG_ERR, "unexpected VMEXIT: exit reason = 0x%x, "