# HG changeset patch
# User kfraser@xxxxxxxxxxxxxxxxxxxxx
# Date 1183484760 -3600
# Node ID e6d5e4709466b66146d2538574df9704ecb9a5e1
# Parent 9fa9346e1c700d0ea81a99318c564c4b9bccaa8a
hvm vmx: Support 'virtual NMI' feature of VMX.
Signed-off-by: Haitao Shan <haitao.shan@xxxxxxxxx>
Signed-off-by: Eddie Dong <eddie.dong@xxxxxxxxx>
Signed-off-by: Dexuan Cui <dexuan.cui@xxxxxxxxx>
Signed-off-by: Keir Fraser <keir@xxxxxxxxxxxxx>
---
xen/arch/x86/hvm/vmx/intr.c | 65 ++++++++++++++++++++++++++-----------
xen/arch/x86/hvm/vmx/vmcs.c | 2 -
xen/arch/x86/hvm/vmx/vmx.c | 21 +++++++++--
xen/include/asm-x86/hvm/vmx/vmcs.h | 2 +
xen/include/asm-x86/hvm/vmx/vmx.h | 2 +
5 files changed, 69 insertions(+), 23 deletions(-)
diff -r 9fa9346e1c70 -r e6d5e4709466 xen/arch/x86/hvm/vmx/intr.c
--- a/xen/arch/x86/hvm/vmx/intr.c Tue Jul 03 17:22:17 2007 +0100
+++ b/xen/arch/x86/hvm/vmx/intr.c Tue Jul 03 18:46:00 2007 +0100
@@ -71,13 +71,38 @@
* the effect is cleared. (i.e., MOV-SS-blocking 'dominates' STI-blocking).
*/
-static void enable_irq_window(struct vcpu *v)
-{
- u32 *cpu_exec_control = &v->arch.hvm_vcpu.u.vmx.exec_control;
-
- if ( !(*cpu_exec_control & CPU_BASED_VIRTUAL_INTR_PENDING) )
- {
- *cpu_exec_control |= CPU_BASED_VIRTUAL_INTR_PENDING;
+static void enable_intr_window(struct vcpu *v, enum hvm_intack intr_source)
+{
+ u32 *cpu_exec_control = &v->arch.hvm_vcpu.u.vmx.exec_control;
+ u32 ctl = CPU_BASED_VIRTUAL_INTR_PENDING;
+
+ if ( unlikely(intr_source == hvm_intack_none) )
+ return;
+
+ if ( unlikely(intr_source == hvm_intack_nmi) && cpu_has_vmx_vnmi )
+ {
+ /*
+ * We set MOV-SS blocking in lieu of STI blocking when delivering an
+ * NMI. This is because it is processor-specific whether STI-blocking
+ * blocks NMIs. Hence we *must* check for STI-blocking on NMI delivery
+ * (otherwise vmentry will fail on processors that check for STI-
+ * blocking) but if the processor does not check for STI-blocking then
+ * we may immediately vmexit and hance make no progress!
+ * (see SDM 3B 21.3, "Other Causes of VM Exits").
+ */
+ u32 intr_shadow = __vmread(GUEST_INTERRUPTIBILITY_INFO);
+ if ( intr_shadow & VMX_INTR_SHADOW_STI )
+ {
+ /* Having both STI-blocking and MOV-SS-blocking fails vmentry. */
+ intr_shadow &= ~VMX_INTR_SHADOW_STI;
+ intr_shadow |= VMX_INTR_SHADOW_MOV_SS;
+ }
+ ctl = CPU_BASED_VIRTUAL_NMI_PENDING;
+ }
+
+ if ( !(*cpu_exec_control & ctl) )
+ {
+ *cpu_exec_control |= ctl;
__vmwrite(CPU_BASED_VM_EXEC_CONTROL, *cpu_exec_control);
}
}
@@ -120,8 +145,7 @@ asmlinkage void vmx_intr_assist(void)
if ( unlikely(v->arch.hvm_vmx.vector_injected) )
{
v->arch.hvm_vmx.vector_injected = 0;
- if ( unlikely(intr_source != hvm_intack_none) )
- enable_irq_window(v);
+ enable_intr_window(v, intr_source);
return;
}
@@ -129,7 +153,9 @@ asmlinkage void vmx_intr_assist(void)
idtv_info_field = __vmread(IDT_VECTORING_INFO_FIELD);
if ( unlikely(idtv_info_field & INTR_INFO_VALID_MASK) )
{
- __vmwrite(VM_ENTRY_INTR_INFO_FIELD, idtv_info_field);
+ /* See SDM 3B 25.7.1.1 and .2 for info about masking resvd bits. */
+ __vmwrite(VM_ENTRY_INTR_INFO_FIELD,
+ idtv_info_field & ~INTR_INFO_RESVD_BITS_MASK);
/*
* Safe: the length will only be interpreted for software
@@ -143,8 +169,16 @@ asmlinkage void vmx_intr_assist(void)
if ( unlikely(idtv_info_field & 0x800) ) /* valid error code */
__vmwrite(VM_ENTRY_EXCEPTION_ERROR_CODE,
__vmread(IDT_VECTORING_ERROR_CODE));
- if ( unlikely(intr_source != hvm_intack_none) )
- enable_irq_window(v);
+ enable_intr_window(v, intr_source);
+
+ /*
+ * Clear NMI-blocking interruptibility info if an NMI delivery
+ * faulted. Re-delivery will re-set it (see SDM 3B 25.7.1.2).
+ */
+ if ( (idtv_info_field&INTR_INFO_INTR_TYPE_MASK) == INTR_TYPE_NMI )
+ __vmwrite(GUEST_INTERRUPTIBILITY_INFO,
+ __vmread(GUEST_INTERRUPTIBILITY_INFO) &
+ ~VMX_INTR_SHADOW_NMI);
HVM_DBG_LOG(DBG_LEVEL_1, "idtv_info_field=%x", idtv_info_field);
return;
@@ -153,14 +187,9 @@ asmlinkage void vmx_intr_assist(void)
if ( likely(intr_source == hvm_intack_none) )
return;
- /*
- * TODO: Better NMI handling. Shouldn't wait for EFLAGS.IF==1, but
- * should wait for exit from 'NMI blocking' window (NMI injection to
- * next IRET). This requires us to use the new 'virtual NMI' support.
- */
if ( !hvm_interrupts_enabled(v, intr_source) )
{
- enable_irq_window(v);
+ enable_intr_window(v, intr_source);
return;
}
} while ( !hvm_vcpu_ack_pending_irq(v, intr_source, &intr_vector) );
diff -r 9fa9346e1c70 -r e6d5e4709466 xen/arch/x86/hvm/vmx/vmcs.c
--- a/xen/arch/x86/hvm/vmx/vmcs.c Tue Jul 03 17:22:17 2007 +0100
+++ b/xen/arch/x86/hvm/vmx/vmcs.c Tue Jul 03 18:46:00 2007 +0100
@@ -75,7 +75,7 @@ void vmx_init_vmcs_config(void)
min = (PIN_BASED_EXT_INTR_MASK |
PIN_BASED_NMI_EXITING);
- opt = 0; /*PIN_BASED_VIRTUAL_NMIS*/
+ opt = PIN_BASED_VIRTUAL_NMIS;
_vmx_pin_based_exec_control = adjust_vmx_controls(
min, opt, MSR_IA32_VMX_PINBASED_CTLS);
diff -r 9fa9346e1c70 -r e6d5e4709466 xen/arch/x86/hvm/vmx/vmx.c
--- a/xen/arch/x86/hvm/vmx/vmx.c Tue Jul 03 17:22:17 2007 +0100
+++ b/xen/arch/x86/hvm/vmx/vmx.c Tue Jul 03 18:46:00 2007 +0100
@@ -1106,15 +1106,17 @@ static int vmx_interrupts_enabled(struct
ASSERT(v == current);
- intr_shadow = __vmread(GUEST_INTERRUPTIBILITY_INFO);
- intr_shadow &= VMX_INTR_SHADOW_STI|VMX_INTR_SHADOW_MOV_SS;
+ intr_shadow = __vmread(GUEST_INTERRUPTIBILITY_INFO);
if ( type == hvm_intack_nmi )
- return !intr_shadow;
+ return !(intr_shadow & (VMX_INTR_SHADOW_STI|
+ VMX_INTR_SHADOW_MOV_SS|
+ VMX_INTR_SHADOW_NMI));
ASSERT((type == hvm_intack_pic) || (type == hvm_intack_lapic));
eflags = __vmread(GUEST_RFLAGS);
- return !irq_masked(eflags) && !intr_shadow;
+ return (!irq_masked(eflags) &&
+ !(intr_shadow & (VMX_INTR_SHADOW_STI|VMX_INTR_SHADOW_MOV_SS)));
}
static void vmx_update_host_cr3(struct vcpu *v)
@@ -2911,6 +2913,17 @@ asmlinkage void vmx_vmexit_handler(struc
vector = intr_info & INTR_INFO_VECTOR_MASK;
+ /*
+ * Re-set the NMI shadow if vmexit caused by a guest IRET fault (see 3B
+ * 25.7.1.2, "Resuming Guest Software after Handling an Exception").
+ * (NB. If we emulate this IRET for any reason, we should re-clear!)
+ */
+ if ( unlikely(intr_info & INTR_INFO_NMI_UNBLOCKED_BY_IRET) &&
+ !(__vmread(IDT_VECTORING_INFO_FIELD) & INTR_INFO_VALID_MASK) &&
+ (vector != TRAP_double_fault) )
+ __vmwrite(GUEST_INTERRUPTIBILITY_INFO,
+ __vmread(GUEST_INTERRUPTIBILITY_INFO)|VMX_INTR_SHADOW_NMI);
+
perfc_incra(cause_vector, vector);
switch ( vector )
diff -r 9fa9346e1c70 -r e6d5e4709466 xen/include/asm-x86/hvm/vmx/vmcs.h
--- a/xen/include/asm-x86/hvm/vmx/vmcs.h Tue Jul 03 17:22:17 2007 +0100
+++ b/xen/include/asm-x86/hvm/vmx/vmcs.h Tue Jul 03 18:46:00 2007 +0100
@@ -137,6 +137,8 @@ extern bool_t cpu_has_vmx_ins_outs_instr
(vmx_secondary_exec_control & SECONDARY_EXEC_VIRTUALIZE_APIC_ACCESSES)
#define cpu_has_vmx_tpr_shadow \
(vmx_cpu_based_exec_control & CPU_BASED_TPR_SHADOW)
+#define cpu_has_vmx_vnmi \
+ (vmx_pin_based_exec_control & PIN_BASED_VIRTUAL_NMIS)
#define cpu_has_vmx_msr_bitmap \
(vmx_cpu_based_exec_control & CPU_BASED_ACTIVATE_MSR_BITMAP)
extern char *vmx_msr_bitmap;
diff -r 9fa9346e1c70 -r e6d5e4709466 xen/include/asm-x86/hvm/vmx/vmx.h
--- a/xen/include/asm-x86/hvm/vmx/vmx.h Tue Jul 03 17:22:17 2007 +0100
+++ b/xen/include/asm-x86/hvm/vmx/vmx.h Tue Jul 03 18:46:00 2007 +0100
@@ -90,7 +90,9 @@ void vmx_vlapic_msr_changed(struct vcpu
#define INTR_INFO_VECTOR_MASK 0xff /* 7:0 */
#define INTR_INFO_INTR_TYPE_MASK 0x700 /* 10:8 */
#define INTR_INFO_DELIVER_CODE_MASK 0x800 /* 11 */
+#define INTR_INFO_NMI_UNBLOCKED_BY_IRET 0x1000 /* 12 */
#define INTR_INFO_VALID_MASK 0x80000000 /* 31 */
+#define INTR_INFO_RESVD_BITS_MASK 0x7ffff000
#define INTR_TYPE_EXT_INTR (0 << 8) /* external interrupt */
#define INTR_TYPE_NMI (2 << 8) /* NMI */
_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog
|