direct-io.hg

changeset 15276:4d8381679606

hvm vmx: Various cleanups and clarifications around event injection.
Signed-off-by: Keir Fraser <keir@xensource.com>
author Keir Fraser <keir@xensource.com>
date Wed Jun 13 22:24:28 2007 +0100 (2007-06-13)
parents b643179d7452
children 912f7e312ec2
files xen/arch/x86/hvm/vmx/intr.c xen/arch/x86/hvm/vmx/vmcs.c xen/arch/x86/hvm/vmx/vmx.c xen/include/asm-x86/hvm/vmx/vmcs.h xen/include/asm-x86/hvm/vmx/vmx.h
line diff
     1.1 --- a/xen/arch/x86/hvm/vmx/intr.c	Wed Jun 13 11:28:13 2007 +0100
     1.2 +++ b/xen/arch/x86/hvm/vmx/intr.c	Wed Jun 13 22:24:28 2007 +0100
     1.3 @@ -1,6 +1,7 @@
     1.4  /*
     1.5 - * io.c: handling I/O, interrupts related VMX entry/exit
     1.6 + * intr.c: handling I/O, interrupts related VMX entry/exit
     1.7   * Copyright (c) 2004, Intel Corporation.
     1.8 + * Copyright (c) 2004-2007, XenSource Inc.
     1.9   *
    1.10   * This program is free software; you can redistribute it and/or modify it
    1.11   * under the terms and conditions of the GNU General Public License,
    1.12 @@ -14,7 +15,6 @@
    1.13   * You should have received a copy of the GNU General Public License along with
    1.14   * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
    1.15   * Place - Suite 330, Boston, MA 02111-1307 USA.
    1.16 - *
    1.17   */
    1.18  
    1.19  #include <xen/config.h>
    1.20 @@ -24,7 +24,6 @@
    1.21  #include <xen/errno.h>
    1.22  #include <xen/trace.h>
    1.23  #include <xen/event.h>
    1.24 -
    1.25  #include <asm/current.h>
    1.26  #include <asm/cpufeature.h>
    1.27  #include <asm/processor.h>
    1.28 @@ -39,34 +38,50 @@
    1.29  #include <public/hvm/ioreq.h>
    1.30  #include <asm/hvm/trace.h>
    1.31  
    1.32 +/*
    1.33 + * A few notes on virtual NMI and INTR delivery, and interactions with
    1.34 + * interruptibility states:
    1.35 + * 
    1.36 + * We can only inject an ExtInt if EFLAGS.IF = 1 and no blocking by
    1.37 + * STI nor MOV SS. Otherwise the VM entry fails. The 'virtual interrupt
    1.38 + * pending' control causes a VM exit when all these checks succeed. It will
    1.39 + * exit immediately after VM entry if the checks succeed at that point.
    1.40 + * 
    1.41 + * We can only inject an NMI if no blocking by MOV SS (also, depending on
    1.42 + * implementation, if no blocking by STI). If pin-based 'virtual NMIs'
    1.43 + * control is specified then the NMI-blocking interruptibility flag is
    1.44 + * also checked. The 'virtual NMI pending' control (available only in
    1.45 + * conjunction with 'virtual NMIs') causes a VM exit when all these checks
    1.46 + * succeed. It will exit immediately after VM entry if the checks succeed
    1.47 + * at that point.
    1.48 + * 
    1.49 + * Because a processor may or may not check blocking-by-STI when injecting
    1.50 + * a virtual NMI, it will be necessary to convert that to block-by-MOV-SS
    1.51 + * before specifying the 'virtual NMI pending' control. Otherwise we could
    1.52 + * enter an infinite loop where we check blocking-by-STI in software and
    1.53 + * thus delay delivery of a virtual NMI, but the processor causes immediate
    1.54 + * VM exit because it does not check blocking-by-STI.
    1.55 + * 
    1.56 + * Injecting a virtual NMI sets the NMI-blocking interruptibility flag only
    1.57 + * if the 'virtual NMIs' control is set. Injecting *any* kind of event clears
    1.58 + * the STI- and MOV-SS-blocking interruptibility-state flags.
    1.59 + * 
    1.60 + * If MOV/POP SS is executed while MOV-SS-blocking is in effect, the effect
    1.61 + * is cleared. If STI is executed while MOV-SS- or STI-blocking is in effect,
    1.62 + * the effect is cleared. (i.e., MOV-SS-blocking 'dominates' STI-blocking).
    1.63 + */
    1.64  
    1.65 -static inline void
    1.66 -enable_irq_window(struct vcpu *v)
    1.67 +static void enable_irq_window(struct vcpu *v)
    1.68  {
    1.69      u32  *cpu_exec_control = &v->arch.hvm_vcpu.u.vmx.exec_control;
    1.70      
    1.71 -    if (!(*cpu_exec_control & CPU_BASED_VIRTUAL_INTR_PENDING)) {
    1.72 +    if ( !(*cpu_exec_control & CPU_BASED_VIRTUAL_INTR_PENDING) )
    1.73 +    {
    1.74          *cpu_exec_control |= CPU_BASED_VIRTUAL_INTR_PENDING;
    1.75          __vmwrite(CPU_BASED_VM_EXEC_CONTROL, *cpu_exec_control);
    1.76      }
    1.77  }
    1.78  
    1.79 -static inline void
    1.80 -disable_irq_window(struct vcpu *v)
    1.81 -{
    1.82 -    u32  *cpu_exec_control = &v->arch.hvm_vcpu.u.vmx.exec_control;
    1.83 -    
    1.84 -    if ( *cpu_exec_control & CPU_BASED_VIRTUAL_INTR_PENDING ) {
    1.85 -        *cpu_exec_control &= ~CPU_BASED_VIRTUAL_INTR_PENDING;
    1.86 -        __vmwrite(CPU_BASED_VM_EXEC_CONTROL, *cpu_exec_control);
    1.87 -    }
    1.88 -}
    1.89 -
    1.90 -static inline int is_interruptibility_state(void)
    1.91 -{
    1.92 -    return __vmread(GUEST_INTERRUPTIBILITY_INFO);
    1.93 -}
    1.94 -
    1.95  static void update_tpr_threshold(struct vlapic *vlapic)
    1.96  {
    1.97      int max_irr, tpr;
    1.98 @@ -87,13 +102,11 @@ static void update_tpr_threshold(struct 
    1.99  
   1.100  asmlinkage void vmx_intr_assist(void)
   1.101  {
   1.102 -    int intr_type = 0;
   1.103 -    int intr_vector;
   1.104 -    unsigned long eflags;
   1.105 +    int has_ext_irq, intr_vector, intr_type = 0;
   1.106 +    unsigned long eflags, intr_shadow;
   1.107      struct vcpu *v = current;
   1.108      unsigned int idtv_info_field;
   1.109      unsigned long inst_len;
   1.110 -    int    has_ext_irq;
   1.111  
   1.112      pt_update_irq(v);
   1.113  
   1.114 @@ -125,10 +138,10 @@ asmlinkage void vmx_intr_assist(void)
   1.115          inst_len = __vmread(VM_EXIT_INSTRUCTION_LEN); /* Safe */
   1.116          __vmwrite(VM_ENTRY_INSTRUCTION_LEN, inst_len);
   1.117  
   1.118 -        if (unlikely(idtv_info_field & 0x800)) /* valid error code */
   1.119 +        if ( unlikely(idtv_info_field & 0x800) ) /* valid error code */
   1.120              __vmwrite(VM_ENTRY_EXCEPTION_ERROR_CODE,
   1.121                        __vmread(IDT_VECTORING_ERROR_CODE));
   1.122 -        if (unlikely(has_ext_irq))
   1.123 +        if ( unlikely(has_ext_irq) )
   1.124              enable_irq_window(v);
   1.125  
   1.126          HVM_DBG_LOG(DBG_LEVEL_1, "idtv_info_field=%x", idtv_info_field);
   1.127 @@ -138,9 +151,9 @@ asmlinkage void vmx_intr_assist(void)
   1.128      if ( likely(!has_ext_irq) )
   1.129          return;
   1.130  
   1.131 -    if ( unlikely(is_interruptibility_state()) )
   1.132 +    intr_shadow = __vmread(GUEST_INTERRUPTIBILITY_INFO);
   1.133 +    if ( unlikely(intr_shadow & (VMX_INTR_SHADOW_STI|VMX_INTR_SHADOW_MOV_SS)) )
   1.134      {
   1.135 -        /* pre-cleared for emulated instruction */
   1.136          enable_irq_window(v);
   1.137          HVM_DBG_LOG(DBG_LEVEL_1, "interruptibility");
   1.138          return;
     2.1 --- a/xen/arch/x86/hvm/vmx/vmcs.c	Wed Jun 13 11:28:13 2007 +0100
     2.2 +++ b/xen/arch/x86/hvm/vmx/vmcs.c	Wed Jun 13 22:24:28 2007 +0100
     2.3 @@ -70,8 +70,9 @@ void vmx_init_vmcs_config(void)
     2.4      u32 _vmx_vmexit_control;
     2.5      u32 _vmx_vmentry_control;
     2.6  
     2.7 -    min = PIN_BASED_EXT_INTR_MASK | PIN_BASED_NMI_EXITING;
     2.8 -    opt = 0;
     2.9 +    min = (PIN_BASED_EXT_INTR_MASK |
    2.10 +           PIN_BASED_NMI_EXITING);
    2.11 +    opt = 0; /*PIN_BASED_VIRTUAL_NMIS*/
    2.12      _vmx_pin_based_exec_control = adjust_vmx_controls(
    2.13          min, opt, MSR_IA32_VMX_PINBASED_CTLS);
    2.14  
     3.1 --- a/xen/arch/x86/hvm/vmx/vmx.c	Wed Jun 13 11:28:13 2007 +0100
     3.2 +++ b/xen/arch/x86/hvm/vmx/vmx.c	Wed Jun 13 22:24:28 2007 +0100
     3.3 @@ -1300,11 +1300,17 @@ static int __get_instruction_length(void
     3.4  
     3.5  static void inline __update_guest_eip(unsigned long inst_len)
     3.6  {
     3.7 -    unsigned long current_eip;
     3.8 +    unsigned long current_eip, intr_shadow;
     3.9  
    3.10      current_eip = __vmread(GUEST_RIP);
    3.11      __vmwrite(GUEST_RIP, current_eip + inst_len);
    3.12 -    __vmwrite(GUEST_INTERRUPTIBILITY_INFO, 0);
    3.13 +
    3.14 +    intr_shadow = __vmread(GUEST_INTERRUPTIBILITY_INFO);
    3.15 +    if ( intr_shadow & (VMX_INTR_SHADOW_STI | VMX_INTR_SHADOW_MOV_SS) )
    3.16 +    {
    3.17 +        intr_shadow &= ~(VMX_INTR_SHADOW_STI | VMX_INTR_SHADOW_MOV_SS);
    3.18 +        __vmwrite(GUEST_INTERRUPTIBILITY_INFO, intr_shadow);
    3.19 +    }
    3.20  }
    3.21  
    3.22  static void vmx_do_no_device_fault(void)
    3.23 @@ -2902,12 +2908,18 @@ asmlinkage void vmx_vmexit_handler(struc
    3.24      case EXIT_REASON_TRIPLE_FAULT:
    3.25          hvm_triple_fault();
    3.26          break;
    3.27 -    case EXIT_REASON_PENDING_INTERRUPT:
    3.28 +    case EXIT_REASON_PENDING_VIRT_INTR:
    3.29          /* Disable the interrupt window. */
    3.30          v->arch.hvm_vcpu.u.vmx.exec_control &= ~CPU_BASED_VIRTUAL_INTR_PENDING;
    3.31          __vmwrite(CPU_BASED_VM_EXEC_CONTROL,
    3.32                    v->arch.hvm_vcpu.u.vmx.exec_control);
    3.33          break;
    3.34 +    case EXIT_REASON_PENDING_VIRT_NMI:
    3.35 +        /* Disable the NMI window. */
    3.36 +        v->arch.hvm_vcpu.u.vmx.exec_control &= ~CPU_BASED_VIRTUAL_NMI_PENDING;
    3.37 +        __vmwrite(CPU_BASED_VM_EXEC_CONTROL,
    3.38 +                  v->arch.hvm_vcpu.u.vmx.exec_control);
    3.39 +        break;
    3.40      case EXIT_REASON_TASK_SWITCH:
    3.41          goto exit_and_crash;
    3.42      case EXIT_REASON_CPUID:
     4.1 --- a/xen/include/asm-x86/hvm/vmx/vmcs.h	Wed Jun 13 11:28:13 2007 +0100
     4.2 +++ b/xen/include/asm-x86/hvm/vmx/vmcs.h	Wed Jun 13 22:24:28 2007 +0100
     4.3 @@ -104,6 +104,7 @@ void vmx_vmcs_exit(struct vcpu *v);
     4.4  #define CPU_BASED_CR8_LOAD_EXITING            0x00080000
     4.5  #define CPU_BASED_CR8_STORE_EXITING           0x00100000
     4.6  #define CPU_BASED_TPR_SHADOW                  0x00200000
     4.7 +#define CPU_BASED_VIRTUAL_NMI_PENDING         0x00400000
     4.8  #define CPU_BASED_MOV_DR_EXITING              0x00800000
     4.9  #define CPU_BASED_UNCOND_IO_EXITING           0x01000000
    4.10  #define CPU_BASED_ACTIVATE_IO_BITMAP          0x02000000
    4.11 @@ -115,6 +116,7 @@ extern u32 vmx_cpu_based_exec_control;
    4.12  
    4.13  #define PIN_BASED_EXT_INTR_MASK         0x00000001
    4.14  #define PIN_BASED_NMI_EXITING           0x00000008
    4.15 +#define PIN_BASED_VIRTUAL_NMIS          0x00000020
    4.16  extern u32 vmx_pin_based_exec_control;
    4.17  
    4.18  #define VM_EXIT_IA32E_MODE              0x00000200
    4.19 @@ -137,7 +139,13 @@ extern u32 vmx_secondary_exec_control;
    4.20      (vmx_cpu_based_exec_control & CPU_BASED_ACTIVATE_MSR_BITMAP)
    4.21  extern char *vmx_msr_bitmap;
    4.22  
    4.23 -/* VMCS Encordings */
    4.24 +/* GUEST_INTERRUPTIBILITY_INFO flags. */
    4.25 +#define VMX_INTR_SHADOW_STI             0x00000001
    4.26 +#define VMX_INTR_SHADOW_MOV_SS          0x00000002
    4.27 +#define VMX_INTR_SHADOW_SMI             0x00000004
    4.28 +#define VMX_INTR_SHADOW_NMI             0x00000008
    4.29 +
    4.30 +/* VMCS field encodings. */
    4.31  enum vmcs_field {
    4.32      GUEST_ES_SELECTOR               = 0x00000800,
    4.33      GUEST_CS_SELECTOR               = 0x00000802,
     5.1 --- a/xen/include/asm-x86/hvm/vmx/vmx.h	Wed Jun 13 11:28:13 2007 +0100
     5.2 +++ b/xen/include/asm-x86/hvm/vmx/vmx.h	Wed Jun 13 22:24:28 2007 +0100
     5.3 @@ -46,8 +46,8 @@ void vmx_vlapic_msr_changed(struct vcpu 
     5.4  #define EXIT_REASON_SIPI                4
     5.5  #define EXIT_REASON_IO_SMI              5
     5.6  #define EXIT_REASON_OTHER_SMI           6
     5.7 -#define EXIT_REASON_PENDING_INTERRUPT   7
     5.8 -
     5.9 +#define EXIT_REASON_PENDING_VIRT_INTR   7
    5.10 +#define EXIT_REASON_PENDING_VIRT_NMI    8
    5.11  #define EXIT_REASON_TASK_SWITCH         9
    5.12  #define EXIT_REASON_CPUID               10
    5.13  #define EXIT_REASON_HLT                 12
    5.14 @@ -295,7 +295,14 @@ static inline void __vmx_inject_exceptio
    5.15  {
    5.16      unsigned long intr_fields;
    5.17  
    5.18 -    /* Reflect it back into the guest */
    5.19 +    /*
    5.20 +     * NB. Callers do not need to worry about clearing STI/MOV-SS blocking:
    5.21 +     *  "If the VM entry is injecting, there is no blocking by STI or by
    5.22 +     *   MOV SS following the VM entry, regardless of the contents of the
    5.23 +     *   interruptibility-state field [in the guest-state area before the
    5.24 +     *   VM entry]", PRM Vol. 3, 22.6.1 (Interruptibility State).
    5.25 +     */
    5.26 +
    5.27      intr_fields = (INTR_INFO_VALID_MASK | type | trap);
    5.28      if ( error_code != VMX_DELIVER_NO_ERROR_CODE ) {
    5.29          __vmwrite(VM_ENTRY_EXCEPTION_ERROR_CODE, error_code);
    5.30 @@ -332,7 +339,6 @@ static inline void vmx_inject_sw_excepti
    5.31  static inline void vmx_inject_extint(struct vcpu *v, int trap, int error_code)
    5.32  {
    5.33      __vmx_inject_exception(v, trap, INTR_TYPE_EXT_INTR, error_code, 0);
    5.34 -    __vmwrite(GUEST_INTERRUPTIBILITY_INFO, 0);
    5.35  }
    5.36  
    5.37  #endif /* __ASM_X86_HVM_VMX_VMX_H__ */