# HG changeset patch
# User Tim Deegan <Tim.Deegan@xxxxxxxxxxxxx>
# Date 1178113311 -3600
# Node ID f3cf882c9578f4c283ed8e17423e1da36aa12947
# Parent c1fafdcdb19a781a603988346e49deae07054a54
[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@xxxxxxxxxxxxx>
---
xen/arch/x86/hvm/svm/svm.c | 61 +++++++++++++++++++++++++++++++++++++-
xen/arch/x86/hvm/vmx/vmx.c | 66 ++++++++++++++++++++++++++++++++++++++++++
xen/include/public/hvm/save.h | 14 ++++++++
3 files changed, 139 insertions(+), 2 deletions(-)
diff -r c1fafdcdb19a -r f3cf882c9578 xen/arch/x86/hvm/svm/svm.c
--- a/xen/arch/x86/hvm/svm/svm.c Tue May 01 22:24:01 2007 +0100
+++ b/xen/arch/x86/hvm/svm/svm.c Wed May 02 14:41:51 2007 +0100
@@ -307,6 +307,41 @@ int svm_vmcb_save(struct vcpu *v, struct
c->sysenter_esp = vmcb->sysenter_esp;
c->sysenter_eip = vmcb->sysenter_eip;
+ /* Save any event/interrupt that was being injected when we last
+ * exited. Although there are three(!) VMCB fields that can contain
+ * active events, we only need to save at most one: because the
+ * intr_assist logic never delivers an IRQ when any other event is
+ * active, we know that the only possible collision is if we inject
+ * a fault while exitintinfo contains a valid event (the delivery of
+ * which caused the last exit). In that case replaying just the
+ * first event should cause the same behaviour when we restore. */
+ if ( vmcb->vintr.fields.irq
+ && /* Check it's not a fake interrupt (see svm_intr_assist()) */
+ !(vmcb->general1_intercepts & GENERAL1_INTERCEPT_VINTR) )
+ {
+ c->pending_vector = vmcb->vintr.fields.vector;
+ c->pending_type = 0; /* External interrupt */
+ c->pending_error_valid = 0;
+ c->pending_reserved = 0;
+ c->pending_valid = 1;
+ c->error_code = 0;
+ }
+ else if ( vmcb->exitintinfo.fields.v )
+ {
+ c->pending_event = vmcb->exitintinfo.bytes & 0xffffffff;
+ c->error_code = vmcb->exitintinfo.fields.errorcode;
+ }
+ else if ( vmcb->eventinj.fields.v )
+ {
+ c->pending_event = vmcb->eventinj.bytes & 0xffffffff;
+ c->error_code = vmcb->eventinj.fields.errorcode;
+ }
+ else
+ {
+ c->pending_event = 0;
+ c->error_code = 0;
+ }
+
return 1;
}
@@ -335,7 +370,7 @@ int svm_vmcb_restore(struct vcpu *v, str
if ( !svm_paging_enabled(v) )
{
- printk("%s: paging not enabled.", __func__);
+ printk("%s: paging not enabled.\n", __func__);
goto skip_cr3;
}
@@ -436,11 +471,33 @@ int svm_vmcb_restore(struct vcpu *v, str
vmcb->dr6 = c->dr6;
vmcb->dr7 = c->dr7;
+ if ( c->pending_valid )
+ {
+ gdprintk(XENLOG_INFO, "Re-injecting 0x%"PRIx32", 0x%"PRIx32"\n",
+ c->pending_event, c->error_code);
+
+ /* VMX uses a different type for #OF and #BP; fold into "Exception" */
+ if ( c->pending_type == 6 )
+ c->pending_type = 3;
+ /* Sanity check */
+ if ( c->pending_type == 1 || c->pending_type > 4
+ || c->pending_reserved != 0 )
+ {
+ gdprintk(XENLOG_ERR, "Invalid pending event 0x%"PRIx32"\n",
+ c->pending_event);
+ return -EINVAL;
+ }
+ /* Put this pending event in exitintinfo and svm_intr_assist()
+ * will reinject it when we return to the guest. */
+ vmcb->exitintinfo.bytes = c->pending_event;
+ vmcb->exitintinfo.fields.errorcode = c->error_code;
+ }
+
paging_update_paging_modes(v);
return 0;
bad_cr3:
- gdprintk(XENLOG_ERR, "Invalid CR3 value=0x%"PRIx64"", c->cr3);
+ gdprintk(XENLOG_ERR, "Invalid CR3 value=0x%"PRIx64"\n", c->cr3);
return -EINVAL;
}
diff -r c1fafdcdb19a -r f3cf882c9578 xen/arch/x86/hvm/vmx/vmx.c
--- a/xen/arch/x86/hvm/vmx/vmx.c Tue May 01 22:24:01 2007 +0100
+++ b/xen/arch/x86/hvm/vmx/vmx.c Wed May 02 14:41:51 2007 +0100
@@ -370,6 +370,8 @@ static inline void __restore_debug_regis
int vmx_vmcs_save(struct vcpu *v, struct hvm_hw_cpu *c)
{
+ uint32_t ev;
+
c->rip = __vmread(GUEST_RIP);
c->rsp = __vmread(GUEST_RSP);
c->rflags = __vmread(GUEST_RFLAGS);
@@ -435,6 +437,28 @@ int vmx_vmcs_save(struct vcpu *v, struct
c->sysenter_cs = __vmread(GUEST_SYSENTER_CS);
c->sysenter_esp = __vmread(GUEST_SYSENTER_ESP);
c->sysenter_eip = __vmread(GUEST_SYSENTER_EIP);
+
+ /* Save any event/interrupt that was being injected when we last
+ * exited. IDT_VECTORING_INFO_FIELD has priority, as anything in
+ * VM_ENTRY_INTR_INFO_FIELD is either a fault caused by the first
+ * event, which will happen the next time, or an interrupt, which we
+ * never inject when IDT_VECTORING_INFO_FIELD is valid.*/
+ if ( (ev = __vmread(IDT_VECTORING_INFO_FIELD)) & INTR_INFO_VALID_MASK )
+ {
+ c->pending_event = ev;
+ c->error_code = __vmread(IDT_VECTORING_ERROR_CODE);
+ }
+ else if ( (ev = __vmread(VM_ENTRY_INTR_INFO_FIELD))
+ & INTR_INFO_VALID_MASK )
+ {
+ c->pending_event = ev;
+ c->error_code = __vmread(VM_ENTRY_EXCEPTION_ERROR_CODE);
+ }
+ else
+ {
+ c->pending_event = 0;
+ c->error_code = 0;
+ }
return 1;
}
@@ -563,6 +587,48 @@ int vmx_vmcs_restore(struct vcpu *v, str
vmx_vmcs_exit(v);
paging_update_paging_modes(v);
+
+ if ( c->pending_valid )
+ {
+ vmx_vmcs_enter(v);
+ gdprintk(XENLOG_INFO, "Re-injecting 0x%"PRIx32", 0x%"PRIx32"\n",
+ c->pending_event, c->error_code);
+
+ /* SVM uses type 3 ("Exception") for #OF and #BP; VMX uses type 6 */
+ if ( c->pending_type == 3
+ && (c->pending_vector == 3 || c->pending_vector == 4) )
+ c->pending_type = 6;
+
+ /* For software exceptions, we need to tell the hardware the
+ * instruction length as well (hmmm). */
+ if ( c->pending_type > 4 )
+ {
+ int addrbytes, ilen;
+ if ( (c->cs_arbytes & (1u<<13)) && (c->msr_efer & EFER_LMA) )
+ addrbytes = 8;
+ else if ( (c->cs_arbytes & (1u<<14)) )
+ addrbytes = 4;
+ else
+ addrbytes = 2;
+ ilen = hvm_instruction_length(c->rip, hvm_guest_x86_mode(v));
+ __vmwrite(VM_ENTRY_INSTRUCTION_LEN, ilen);
+ }
+
+ /* Sanity check */
+ if ( c->pending_type == 1 || c->pending_type > 6
+ || c->pending_reserved != 0 )
+ {
+ gdprintk(XENLOG_ERR, "Invalid pending event 0x%"PRIx32"\n",
+ c->pending_event);
+ return -EINVAL;
+ }
+ /* Re-inject the exception */
+ __vmwrite(VM_ENTRY_INTR_INFO_FIELD, c->pending_event);
+ __vmwrite(VM_ENTRY_EXCEPTION_ERROR_CODE, c->error_code);
+ v->arch.hvm_vmx.vector_injected = 1;
+ vmx_vmcs_exit(v);
+ }
+
return 0;
bad_cr3:
diff -r c1fafdcdb19a -r f3cf882c9578 xen/include/public/hvm/save.h
--- a/xen/include/public/hvm/save.h Tue May 01 22:24:01 2007 +0100
+++ b/xen/include/public/hvm/save.h Wed May 02 14:41:51 2007 +0100
@@ -182,6 +182,20 @@ struct hvm_hw_cpu {
/* guest's idea of what rdtsc() would return */
uint64_t tsc;
+
+ /* pending event, if any */
+ union {
+ uint32_t pending_event;
+ struct {
+ uint8_t pending_vector:8;
+ uint8_t pending_type:3;
+ uint8_t pending_error_valid:1;
+ uint32_t pending_reserved:19;
+ uint8_t pending_valid:1;
+ };
+ };
+ /* error code for pending event */
+ uint32_t error_code;
};
DECLARE_HVM_SAVE_TYPE(CPU, 2, struct hvm_hw_cpu);
_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog
|