WARNING - OLD ARCHIVES

This is an archived copy of the Xen.org mailing list, which we have preserved to ensure that existing links to archives are not broken. The live archive, which contains the latest emails, can be found at http://lists.xen.org/
   
 
 
Xen 
 
Home Products Support Community News
 
   
 

xen-devel

[Xen-devel] [PATCH 09/17] vmx: nest: interrupt

To: xen-devel@xxxxxxxxxxxxxxxxxxx
Subject: [Xen-devel] [PATCH 09/17] vmx: nest: interrupt
From: Qing He <qing.he@xxxxxxxxx>
Date: Thu, 22 Apr 2010 17:41:21 +0800
Cc: Qing He <qing.he@xxxxxxxxx>
Delivery-date: Thu, 22 Apr 2010 02:49:44 -0700
Envelope-to: www-data@xxxxxxxxxxxxxxxxxxx
In-reply-to: <1271929289-18572-1-git-send-email-qing.he@xxxxxxxxx>
List-help: <mailto:xen-devel-request@lists.xensource.com?subject=help>
List-id: Xen developer discussion <xen-devel.lists.xensource.com>
List-post: <mailto:xen-devel@lists.xensource.com>
List-subscribe: <http://lists.xensource.com/mailman/listinfo/xen-devel>, <mailto:xen-devel-request@lists.xensource.com?subject=subscribe>
List-unsubscribe: <http://lists.xensource.com/mailman/listinfo/xen-devel>, <mailto:xen-devel-request@lists.xensource.com?subject=unsubscribe>
References: <1271929289-18572-1-git-send-email-qing.he@xxxxxxxxx>
Sender: xen-devel-bounces@xxxxxxxxxxxxxxxxxxx
This patch adds interrupt handling of nested, mainly includes:
  - L1 interrupt causes L2 to exit,
  - idtv handling in L2.
  - interrupt blocking handling in l2

Signed-off-by: Qing He <qing.he@xxxxxxxxx>

---
 arch/x86/hvm/vmx/intr.c        |   91 +++++++++++++++++++++++++++++++++++++++++
 arch/x86/hvm/vmx/nest.c        |   66 +++++++++++++++++++++++++++++
 arch/x86/hvm/vmx/vmx.c         |   78 +++++++++++++++++++++++------------
 include/asm-x86/hvm/vmx/nest.h |    5 ++
 4 files changed, 214 insertions(+), 26 deletions(-)

diff -r c7e763bbea63 -r a7de30ed250d xen/arch/x86/hvm/vmx/intr.c
--- a/xen/arch/x86/hvm/vmx/intr.c       Thu Apr 22 22:30:09 2010 +0800
+++ b/xen/arch/x86/hvm/vmx/intr.c       Thu Apr 22 22:30:09 2010 +0800
@@ -33,6 +33,7 @@
 #include <asm/hvm/support.h>
 #include <asm/hvm/vmx/vmx.h>
 #include <asm/hvm/vmx/vmcs.h>
+#include <asm/hvm/vmx/vvmcs.h>
 #include <asm/hvm/vpic.h>
 #include <asm/hvm/vlapic.h>
 #include <public/hvm/ioreq.h>
@@ -110,6 +111,93 @@
     }
 }
 
+/*
+ * Nested virtualization interrupt handling:
+ *
+ *   When vcpu runs in nested context (L2), the event delivery from
+ *   L0 to L1 may be blocked by several reasons:
+ *       - virtual VMExit
+ *       - virtual VMEntry
+ *       - IDT vectoring reinjection
+ *
+ *   However, when in nesting, the blocked interrupt should not be
+ *   blocked by normal reasons like RFLAGS.IF (generating a VMExit
+ *   instead), so simply intr window may delay the interrupt from
+ *   delivery in time.
+ *
+ *   To solve this, the algorithm below is used.
+ *   v->arch.hvm_vmx.exec_control.VIRTUAL_INTR_PENDING now denotes
+ *   only L0 control, physical control may be different from it.
+ *       - if in L1, it behaves normally, intr window is written
+ *         to physical control as it is
+ *       - if in L2, replace it to MTF (or NMI window) if possible
+ *       - if MTF/NMI window is not used, intr window can still be
+ *         used but may have negative impact on interrupt performance.
+ */
+static int nest_intr_blocked(struct vcpu *v, struct hvm_intack intack)
+{
+    int r = 0;
+
+    if ( !v->arch.hvm_vcpu.in_nesting &&
+         v->arch.hvm_vmx.nest.vmresume_pending )
+        r = 1;
+
+    if ( v->arch.hvm_vcpu.in_nesting )
+    {
+        if ( v->arch.hvm_vmx.nest.vmexit_pending ||
+             v->arch.hvm_vmx.nest.vmresume_in_progress ||
+             (__vmread(VM_ENTRY_INTR_INFO) & INTR_INFO_VALID_MASK) )
+            r = 1;
+    }
+
+    return r;
+}
+
+static int vmx_nest_intr_intercept(struct vcpu *v, struct hvm_intack intack)
+{
+    u32 exit_ctrl;
+
+    /*
+     * TODO:
+     *   - if L1 intr-window exiting == 0
+     *   - vNMI
+     */
+
+    if ( nest_intr_blocked(v, intack) )
+    {
+        enable_intr_window(v, intack);
+        return 1;
+    }
+
+    if ( v->arch.hvm_vcpu.in_nesting )
+    {
+        if ( intack.source == hvm_intsrc_pic ||
+                 intack.source == hvm_intsrc_lapic )
+        {
+            vmx_inject_extint(intack.vector);
+
+            exit_ctrl = __get_vvmcs(v->arch.hvm_vmx.nest.vvmcs,
+                            VM_EXIT_CONTROLS);
+            if ( exit_ctrl & VM_EXIT_ACK_INTR_ON_EXIT )
+            {
+                /* for now, duplicate the ack path in vmx_intr_assist */
+                hvm_vcpu_ack_pending_irq(v, intack);
+                pt_intr_post(v, intack);
+
+                intack = hvm_vcpu_has_pending_irq(v);
+                if ( unlikely(intack.source != hvm_intsrc_none) )
+                    enable_intr_window(v, intack);
+            }
+            else
+                enable_intr_window(v, intack);
+
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
 asmlinkage void vmx_intr_assist(void)
 {
     struct hvm_intack intack;
@@ -133,6 +221,9 @@
         if ( likely(intack.source == hvm_intsrc_none) )
             goto out;
 
+        if ( unlikely(vmx_nest_intr_intercept(v, intack)) )
+            goto out;
+
         intblk = hvm_interrupt_blocked(v, intack);
         if ( intblk == hvm_intblk_tpr )
         {
diff -r c7e763bbea63 -r a7de30ed250d xen/arch/x86/hvm/vmx/nest.c
--- a/xen/arch/x86/hvm/vmx/nest.c       Thu Apr 22 22:30:09 2010 +0800
+++ b/xen/arch/x86/hvm/vmx/nest.c       Thu Apr 22 22:30:09 2010 +0800
@@ -642,6 +642,7 @@
 {
     struct vmx_nest_struct *nest = &v->arch.hvm_vmx.nest;
 
+    /* TODO: change L0 intr window to MTF or NMI window */
     set_shadow_control(nest, CPU_BASED_VM_EXEC_CONTROL, value);
 }
 
@@ -839,6 +840,33 @@
     __set_vvmcs(nest->vvmcs, VM_ENTRY_INTR_INFO, 0);
 }
 
+static void vmx_nest_intr_exit(struct vmx_nest_struct *nest)
+{
+    if ( !(nest->intr_info & INTR_INFO_VALID_MASK) )
+        return;
+
+    switch ( nest->intr_info & INTR_INFO_INTR_TYPE_MASK )
+    {
+    case X86_EVENTTYPE_EXT_INTR:
+        /* rename exit_reason to EXTERNAL_INTERRUPT */
+        __set_vvmcs(nest->vvmcs, VM_EXIT_REASON, 
EXIT_REASON_EXTERNAL_INTERRUPT);
+        __set_vvmcs(nest->vvmcs, EXIT_QUALIFICATION, 0);
+        __set_vvmcs(nest->vvmcs, VM_EXIT_INTR_INFO, nest->intr_info);
+        break;
+
+    case X86_EVENTTYPE_HW_EXCEPTION:
+    case X86_EVENTTYPE_SW_INTERRUPT:
+    case X86_EVENTTYPE_SW_EXCEPTION:
+        /* throw to L1 */
+        __set_vvmcs(nest->vvmcs, VM_EXIT_INTR_INFO, nest->intr_info);
+        __set_vvmcs(nest->vvmcs, VM_EXIT_INTR_ERROR_CODE, nest->error_code);
+        break;
+    case X86_EVENTTYPE_NMI:
+    default:
+        break;
+    }
+}
+
 static void virtual_vmexit(struct cpu_user_regs *regs)
 {
     struct vcpu *v = current;
@@ -848,6 +876,8 @@
 #endif
 
     sync_vvmcs_ro(nest);
+    vmx_nest_intr_exit(nest);
+
     sync_vvmcs_guest_state(nest);
 
     vmx_vmcs_switch_current(v, v->arch.hvm_vmx.vmcs, nest->hvmcs);
@@ -910,3 +940,39 @@
         virtual_vmentry(regs);
     }
 }
+
+void vmx_nest_idtv_handling(void)
+{
+    struct vcpu *v = current;
+    struct vmx_nest_struct *nest = &v->arch.hvm_vmx.nest;
+    unsigned int idtv_info = __vmread(IDT_VECTORING_INFO);
+
+    if ( likely(!(idtv_info & INTR_INFO_VALID_MASK)) )
+        return;
+
+    /*
+     * If L0 can solve the fault that causes idt vectoring, it should
+     * be reinjected, otherwise, pass to L1.
+     */
+    if ( (__vmread(VM_EXIT_REASON) != EXIT_REASON_EPT_VIOLATION &&
+          !(nest->intr_info & INTR_INFO_VALID_MASK)) ||
+         (__vmread(VM_EXIT_REASON) == EXIT_REASON_EPT_VIOLATION &&
+          !nest->vmexit_pending) )
+    {
+        __vmwrite(VM_ENTRY_INTR_INFO, idtv_info & ~INTR_INFO_RESVD_BITS_MASK);
+        if ( idtv_info & INTR_INFO_DELIVER_CODE_MASK )
+            __vmwrite(VM_ENTRY_EXCEPTION_ERROR_CODE,
+                        __vmread(IDT_VECTORING_ERROR_CODE));
+        /*
+         * SDM 23.2.4, if L1 tries to inject a software interrupt
+         * and the delivery fails, VM_EXIT_INSTRUCTION_LEN receives
+         * the value of previous VM_ENTRY_INSTRUCTION_LEN.
+         *
+         * This means EXIT_INSTRUCTION_LEN is always valid here, for
+         * software interrupts both injected by L1, and generated in L2.
+         */
+        __vmwrite(VM_ENTRY_INSTRUCTION_LEN, __vmread(VM_EXIT_INSTRUCTION_LEN));
+    }
+
+    /* TODO: NMI */
+}
diff -r c7e763bbea63 -r a7de30ed250d xen/arch/x86/hvm/vmx/vmx.c
--- a/xen/arch/x86/hvm/vmx/vmx.c        Thu Apr 22 22:30:09 2010 +0800
+++ b/xen/arch/x86/hvm/vmx/vmx.c        Thu Apr 22 22:30:09 2010 +0800
@@ -1274,6 +1274,7 @@
 {
     unsigned long intr_fields;
     struct vcpu *curr = current;
+    struct vmx_nest_struct *nest = &curr->arch.hvm_vmx.nest;
 
     /*
      * NB. Callers do not need to worry about clearing STI/MOV-SS blocking:
@@ -1285,11 +1286,21 @@
 
     intr_fields = (INTR_INFO_VALID_MASK | (type<<8) | trap);
     if ( error_code != HVM_DELIVER_NO_ERROR_CODE ) {
-        __vmwrite(VM_ENTRY_EXCEPTION_ERROR_CODE, error_code);
         intr_fields |= INTR_INFO_DELIVER_CODE_MASK;
+        if ( curr->arch.hvm_vcpu.in_nesting )
+            nest->error_code = error_code;
+        else
+            __vmwrite(VM_ENTRY_EXCEPTION_ERROR_CODE, error_code);
     }
 
-    __vmwrite(VM_ENTRY_INTR_INFO, intr_fields);
+    if ( curr->arch.hvm_vcpu.in_nesting )
+    {
+        nest->intr_info = intr_fields;
+        nest->vmexit_pending = 1;
+        return;
+    }
+    else
+        __vmwrite(VM_ENTRY_INTR_INFO, intr_fields);
 
     /* Can't inject exceptions in virtual 8086 mode because they would 
      * use the protected-mode IDT.  Emulate at the next vmenter instead. */
@@ -1299,9 +1310,14 @@
 
 void vmx_inject_hw_exception(int trap, int error_code)
 {
-    unsigned long intr_info = __vmread(VM_ENTRY_INTR_INFO);
+    unsigned long intr_info;
     struct vcpu *curr = current;
 
+    if ( curr->arch.hvm_vcpu.in_nesting )
+        intr_info = curr->arch.hvm_vmx.nest.intr_info;
+    else
+        intr_info = __vmread(VM_ENTRY_INTR_INFO);
+
     switch ( trap )
     {
     case TRAP_debug:
@@ -2314,9 +2330,31 @@
     return -1;
 }
 
+static void vmx_idtv_reinject(unsigned long idtv_info)
+{
+    if ( hvm_event_needs_reinjection((idtv_info>>8)&7, idtv_info&0xff) )
+    {
+        /* See SDM 3B 25.7.1.1 and .2 for info about masking resvd bits. */
+        __vmwrite(VM_ENTRY_INTR_INFO,
+                  idtv_info & ~INTR_INFO_RESVD_BITS_MASK);
+        if ( idtv_info & INTR_INFO_DELIVER_CODE_MASK )
+            __vmwrite(VM_ENTRY_EXCEPTION_ERROR_CODE,
+                      __vmread(IDT_VECTORING_ERROR_CODE));
+    }
+
+    /*
+     * 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 & INTR_INFO_INTR_TYPE_MASK) == (X86_EVENTTYPE_NMI<<8) )
+        __vmwrite(GUEST_INTERRUPTIBILITY_INFO,
+                  __vmread(GUEST_INTERRUPTIBILITY_INFO) &
+                  ~VMX_INTR_SHADOW_NMI);
+}
+
 asmlinkage void vmx_vmexit_handler(struct cpu_user_regs *regs)
 {
-    unsigned int exit_reason, idtv_info, intr_info = 0, vector = 0;
+    unsigned int exit_reason, idtv_info = 0, intr_info = 0, vector = 0;
     unsigned long exit_qualification, inst_len = 0;
     struct vcpu *v = current;
 
@@ -2398,29 +2436,14 @@
 
     hvm_maybe_deassert_evtchn_irq();
 
-    /* Event delivery caused this intercept? Queue for redelivery. */
-    idtv_info = __vmread(IDT_VECTORING_INFO);
-    if ( unlikely(idtv_info & INTR_INFO_VALID_MASK) &&
-         (exit_reason != EXIT_REASON_TASK_SWITCH) )
+    /* TODO: consolidate nested idtv handling with ordinary one */
+    if ( !v->arch.hvm_vcpu.in_nesting )
     {
-        if ( hvm_event_needs_reinjection((idtv_info>>8)&7, idtv_info&0xff) )
-        {
-            /* See SDM 3B 25.7.1.1 and .2 for info about masking resvd bits. */
-            __vmwrite(VM_ENTRY_INTR_INFO,
-                      idtv_info & ~INTR_INFO_RESVD_BITS_MASK);
-            if ( idtv_info & INTR_INFO_DELIVER_CODE_MASK )
-                __vmwrite(VM_ENTRY_EXCEPTION_ERROR_CODE,
-                          __vmread(IDT_VECTORING_ERROR_CODE));
-        }
-
-        /*
-         * 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 & INTR_INFO_INTR_TYPE_MASK) == (X86_EVENTTYPE_NMI<<8) )
-            __vmwrite(GUEST_INTERRUPTIBILITY_INFO,
-                      __vmread(GUEST_INTERRUPTIBILITY_INFO) &
-                      ~VMX_INTR_SHADOW_NMI);
+        /* Event delivery caused this intercept? Queue for redelivery. */
+        idtv_info = __vmread(IDT_VECTORING_INFO);
+        if ( unlikely(idtv_info & INTR_INFO_VALID_MASK) &&
+             (exit_reason != EXIT_REASON_TASK_SWITCH) )
+            vmx_idtv_reinject(idtv_info);
     }
 
     switch ( exit_reason )
@@ -2736,6 +2759,9 @@
         domain_crash(v->domain);
         break;
     }
+
+    if ( v->arch.hvm_vcpu.in_nesting )
+        vmx_nest_idtv_handling();
 }
 
 asmlinkage void vmx_vmenter_helper(void)
diff -r c7e763bbea63 -r a7de30ed250d xen/include/asm-x86/hvm/vmx/nest.h
--- a/xen/include/asm-x86/hvm/vmx/nest.h        Thu Apr 22 22:30:09 2010 +0800
+++ b/xen/include/asm-x86/hvm/vmx/nest.h        Thu Apr 22 22:30:09 2010 +0800
@@ -44,6 +44,9 @@
     int                  vmexit_pending;
     int                  vmresume_pending;
     int                  vmresume_in_progress;
+
+    unsigned long        intr_info;
+    unsigned long        error_code;
 };
 
 asmlinkage void vmx_nest_switch_mode(void);
@@ -66,4 +69,6 @@
                                             unsigned long value);
 void vmx_nest_update_exception_bitmap(struct vcpu *v, unsigned long value);
 
+void vmx_nest_idtv_handling(void);
+
 #endif /* __ASM_X86_HVM_NEST_H__ */

_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel