direct-io.hg

changeset 14995:f3cf882c9578

[HVM] Save/restore: save pending interrupts/events on HVM vcpus
along with the rest of the cpu state. This stops us from dropping
interrupts after the vlapic has recorded them in the ISR but before the
guest has actually taken them.
Signed-off-by: Tim Deegan <Tim.Deegan@xensource.com>
author Tim Deegan <Tim.Deegan@xensource.com>
date Wed May 02 14:41:51 2007 +0100 (2007-05-02)
parents c1fafdcdb19a
children 1c3abf6d144d
files xen/arch/x86/hvm/svm/svm.c xen/arch/x86/hvm/vmx/vmx.c xen/include/public/hvm/save.h
line diff
     1.1 --- a/xen/arch/x86/hvm/svm/svm.c	Tue May 01 22:24:01 2007 +0100
     1.2 +++ b/xen/arch/x86/hvm/svm/svm.c	Wed May 02 14:41:51 2007 +0100
     1.3 @@ -307,6 +307,41 @@ int svm_vmcb_save(struct vcpu *v, struct
     1.4      c->sysenter_esp = vmcb->sysenter_esp;
     1.5      c->sysenter_eip = vmcb->sysenter_eip;
     1.6  
     1.7 +    /* Save any event/interrupt that was being injected when we last
     1.8 +     * exited.  Although there are three(!) VMCB fields that can contain
     1.9 +     * active events, we only need to save at most one: because the
    1.10 +     * intr_assist logic never delivers an IRQ when any other event is
    1.11 +     * active, we know that the only possible collision is if we inject
    1.12 +     * a fault while exitintinfo contains a valid event (the delivery of
    1.13 +     * which caused the last exit).  In that case replaying just the
    1.14 +     * first event should cause the same behaviour when we restore. */
    1.15 +    if ( vmcb->vintr.fields.irq 
    1.16 +         && /* Check it's not a fake interrupt (see svm_intr_assist()) */
    1.17 +         !(vmcb->general1_intercepts & GENERAL1_INTERCEPT_VINTR) )
    1.18 +    {
    1.19 +        c->pending_vector = vmcb->vintr.fields.vector;
    1.20 +        c->pending_type = 0; /* External interrupt */
    1.21 +        c->pending_error_valid = 0;
    1.22 +        c->pending_reserved = 0;
    1.23 +        c->pending_valid = 1;
    1.24 +        c->error_code = 0;
    1.25 +    }
    1.26 +    else if ( vmcb->exitintinfo.fields.v )
    1.27 +    {
    1.28 +        c->pending_event = vmcb->exitintinfo.bytes & 0xffffffff;
    1.29 +        c->error_code = vmcb->exitintinfo.fields.errorcode;
    1.30 +    }
    1.31 +    else if ( vmcb->eventinj.fields.v ) 
    1.32 +    {
    1.33 +        c->pending_event = vmcb->eventinj.bytes & 0xffffffff;
    1.34 +        c->error_code = vmcb->eventinj.fields.errorcode;
    1.35 +    }
    1.36 +    else 
    1.37 +    {
    1.38 +        c->pending_event = 0;
    1.39 +        c->error_code = 0;
    1.40 +    }
    1.41 +
    1.42      return 1;
    1.43  }
    1.44  
    1.45 @@ -335,7 +370,7 @@ int svm_vmcb_restore(struct vcpu *v, str
    1.46  
    1.47      if ( !svm_paging_enabled(v) ) 
    1.48      {
    1.49 -        printk("%s: paging not enabled.", __func__);
    1.50 +        printk("%s: paging not enabled.\n", __func__);
    1.51          goto skip_cr3;
    1.52      }
    1.53  
    1.54 @@ -436,11 +471,33 @@ int svm_vmcb_restore(struct vcpu *v, str
    1.55      vmcb->dr6 = c->dr6;
    1.56      vmcb->dr7 = c->dr7;
    1.57  
    1.58 +    if ( c->pending_valid ) 
    1.59 +    {
    1.60 +        gdprintk(XENLOG_INFO, "Re-injecting 0x%"PRIx32", 0x%"PRIx32"\n",
    1.61 +                 c->pending_event, c->error_code);
    1.62 +
    1.63 +        /* VMX uses a different type for #OF and #BP; fold into "Exception"  */
    1.64 +        if ( c->pending_type == 6 ) 
    1.65 +            c->pending_type = 3;
    1.66 +        /* Sanity check */
    1.67 +        if ( c->pending_type == 1 || c->pending_type > 4 
    1.68 +             || c->pending_reserved != 0 )
    1.69 +        {
    1.70 +            gdprintk(XENLOG_ERR, "Invalid pending event 0x%"PRIx32"\n", 
    1.71 +                     c->pending_event);
    1.72 +            return -EINVAL;
    1.73 +        }
    1.74 +        /* Put this pending event in exitintinfo and svm_intr_assist()
    1.75 +         * will reinject it when we return to the guest. */
    1.76 +        vmcb->exitintinfo.bytes = c->pending_event;
    1.77 +        vmcb->exitintinfo.fields.errorcode = c->error_code;
    1.78 +    }
    1.79 +
    1.80      paging_update_paging_modes(v);
    1.81      return 0;
    1.82   
    1.83   bad_cr3:
    1.84 -    gdprintk(XENLOG_ERR, "Invalid CR3 value=0x%"PRIx64"", c->cr3);
    1.85 +    gdprintk(XENLOG_ERR, "Invalid CR3 value=0x%"PRIx64"\n", c->cr3);
    1.86      return -EINVAL;
    1.87  }
    1.88  
     2.1 --- a/xen/arch/x86/hvm/vmx/vmx.c	Tue May 01 22:24:01 2007 +0100
     2.2 +++ b/xen/arch/x86/hvm/vmx/vmx.c	Wed May 02 14:41:51 2007 +0100
     2.3 @@ -370,6 +370,8 @@ static inline void __restore_debug_regis
     2.4  
     2.5  int vmx_vmcs_save(struct vcpu *v, struct hvm_hw_cpu *c)
     2.6  {    
     2.7 +    uint32_t ev;
     2.8 +
     2.9      c->rip = __vmread(GUEST_RIP);
    2.10      c->rsp = __vmread(GUEST_RSP);
    2.11      c->rflags = __vmread(GUEST_RFLAGS);
    2.12 @@ -436,6 +438,28 @@ int vmx_vmcs_save(struct vcpu *v, struct
    2.13      c->sysenter_esp = __vmread(GUEST_SYSENTER_ESP);
    2.14      c->sysenter_eip = __vmread(GUEST_SYSENTER_EIP);
    2.15  
    2.16 +    /* Save any event/interrupt that was being injected when we last
    2.17 +     * exited.  IDT_VECTORING_INFO_FIELD has priority, as anything in
    2.18 +     * VM_ENTRY_INTR_INFO_FIELD is either a fault caused by the first
    2.19 +     * event, which will happen the next time, or an interrupt, which we
    2.20 +     * never inject when IDT_VECTORING_INFO_FIELD is valid.*/
    2.21 +    if ( (ev = __vmread(IDT_VECTORING_INFO_FIELD)) & INTR_INFO_VALID_MASK ) 
    2.22 +    {
    2.23 +        c->pending_event = ev;
    2.24 +        c->error_code = __vmread(IDT_VECTORING_ERROR_CODE);
    2.25 +    }
    2.26 +    else if ( (ev = __vmread(VM_ENTRY_INTR_INFO_FIELD)) 
    2.27 +              & INTR_INFO_VALID_MASK ) 
    2.28 +    {
    2.29 +        c->pending_event = ev;
    2.30 +        c->error_code = __vmread(VM_ENTRY_EXCEPTION_ERROR_CODE);
    2.31 +    }
    2.32 +    else 
    2.33 +    {
    2.34 +        c->pending_event = 0;
    2.35 +        c->error_code = 0;
    2.36 +    }
    2.37 +
    2.38      return 1;
    2.39  }
    2.40  
    2.41 @@ -563,6 +587,48 @@ int vmx_vmcs_restore(struct vcpu *v, str
    2.42      vmx_vmcs_exit(v);
    2.43  
    2.44      paging_update_paging_modes(v);
    2.45 +
    2.46 +    if ( c->pending_valid ) 
    2.47 +    {
    2.48 +        vmx_vmcs_enter(v);
    2.49 +        gdprintk(XENLOG_INFO, "Re-injecting 0x%"PRIx32", 0x%"PRIx32"\n",
    2.50 +                 c->pending_event, c->error_code);
    2.51 +
    2.52 +        /* SVM uses type 3 ("Exception") for #OF and #BP; VMX uses type 6 */
    2.53 +        if ( c->pending_type == 3 
    2.54 +             && (c->pending_vector == 3 || c->pending_vector == 4) ) 
    2.55 +            c->pending_type = 6;
    2.56 +
    2.57 +        /* For software exceptions, we need to tell the hardware the 
    2.58 +         * instruction length as well (hmmm). */
    2.59 +        if ( c->pending_type > 4 ) 
    2.60 +        {
    2.61 +            int addrbytes, ilen; 
    2.62 +            if ( (c->cs_arbytes & (1u<<13)) && (c->msr_efer & EFER_LMA) ) 
    2.63 +                addrbytes = 8;
    2.64 +            else if ( (c->cs_arbytes & (1u<<14)) ) 
    2.65 +                addrbytes = 4;
    2.66 +            else 
    2.67 +                addrbytes = 2;
    2.68 +            ilen = hvm_instruction_length(c->rip, hvm_guest_x86_mode(v));
    2.69 +            __vmwrite(VM_ENTRY_INSTRUCTION_LEN, ilen);
    2.70 +        }
    2.71 +
    2.72 +        /* Sanity check */
    2.73 +        if ( c->pending_type == 1 || c->pending_type > 6
    2.74 +             || c->pending_reserved != 0 )
    2.75 +        {
    2.76 +            gdprintk(XENLOG_ERR, "Invalid pending event 0x%"PRIx32"\n", 
    2.77 +                     c->pending_event);
    2.78 +            return -EINVAL;
    2.79 +        }
    2.80 +        /* Re-inject the exception */
    2.81 +        __vmwrite(VM_ENTRY_INTR_INFO_FIELD, c->pending_event);
    2.82 +        __vmwrite(VM_ENTRY_EXCEPTION_ERROR_CODE, c->error_code);
    2.83 +        v->arch.hvm_vmx.vector_injected = 1;
    2.84 +        vmx_vmcs_exit(v);
    2.85 +    }
    2.86 +
    2.87      return 0;
    2.88  
    2.89   bad_cr3:
     3.1 --- a/xen/include/public/hvm/save.h	Tue May 01 22:24:01 2007 +0100
     3.2 +++ b/xen/include/public/hvm/save.h	Wed May 02 14:41:51 2007 +0100
     3.3 @@ -182,6 +182,20 @@ struct hvm_hw_cpu {
     3.4  
     3.5      /* guest's idea of what rdtsc() would return */
     3.6      uint64_t tsc;
     3.7 +
     3.8 +    /* pending event, if any */
     3.9 +    union {
    3.10 +        uint32_t pending_event;
    3.11 +        struct {
    3.12 +            uint8_t  pending_vector:8;
    3.13 +            uint8_t  pending_type:3;
    3.14 +            uint8_t  pending_error_valid:1;
    3.15 +            uint32_t pending_reserved:19;
    3.16 +            uint8_t  pending_valid:1;
    3.17 +        };
    3.18 +    };
    3.19 +    /* error code for pending event */
    3.20 +    uint32_t error_code;
    3.21  };
    3.22  
    3.23  DECLARE_HVM_SAVE_TYPE(CPU, 2, struct hvm_hw_cpu);