Download raw body.
vmm/vmd: introduce exception injection api
As part of rolling out some diffs for repeat and string-based io
emulation (and mmio emulation) we first need the ability for userland
(vmd) to inject exceptions (e.g. page faults) into the vcpu.
This diff adds the api and keeps the functionality the same to keep it
reviewable. Should be no functional change. After this lands, I have a
diff for cleaning up exception injection in vmm (it's incomplete at the
moment) before sharing the actual [REP] INS/OUTS emulation which needs
the ability to inject various exceptions and error codes.
ok?
diffstat refs/heads/master refs/heads/vmm-inject-struct
M sys/arch/amd64/amd64/vmm_machdep.c | 30+ 25-
M sys/arch/amd64/include/vmmvar.h | 16+ 3-
M usr.sbin/vmd/vm.c | 3+ 4-
3 files changed, 49 insertions(+), 32 deletions(-)
diff refs/heads/master refs/heads/vmm-inject-struct
commit - ca6dee1d942da1dc0763e0bb438cef6ceb77eb68
commit + 763193b91e9cf5d7b9dc962ca121d8f1f1316641
blob - 7cc3759171c9502aa4022e8d8aa7be02aaafef04
blob + 338295f1dd89a037e7099419da4c73d19293c47d
--- sys/arch/amd64/amd64/vmm_machdep.c
+++ sys/arch/amd64/amd64/vmm_machdep.c
@@ -3686,6 +3686,10 @@ vm_run(struct vm_run_params *vrp)
}
}
+ vcpu->vc_inject.vie_type = vrp->vrp_inject.vie_type;
+ vcpu->vc_inject.vie_vector = vrp->vrp_inject.vie_vector;
+ vcpu->vc_inject.vie_errorcode = vrp->vrp_inject.vie_errorcode;
+
WRITE_ONCE(vcpu->vc_curcpu, curcpu());
/* Run the VCPU specified in vrp */
if (vcpu->vc_virt_mode == VMM_MODE_EPT) {
@@ -3958,7 +3962,7 @@ vcpu_run_vmx(struct vcpu *vcpu, struct vm_run_params *
struct schedstate_percpu *spc;
struct vmx_invvpid_descriptor vid;
uint64_t eii, procbased, int_st;
- uint16_t irq, ldt_sel;
+ uint16_t ldt_sel;
u_long s;
struct region_descriptor idtr;
@@ -3975,8 +3979,6 @@ vcpu_run_vmx(struct vcpu *vcpu, struct vm_run_params *
* needs to be fixed up depends on what vmd populated in the
* exit data structure.
*/
- irq = vrp->vrp_irq;
-
if (vrp->vrp_intr_pending)
vcpu->vc_intr = 1;
else
@@ -4054,7 +4056,7 @@ vcpu_run_vmx(struct vcpu *vcpu, struct vm_run_params *
/* Handle vmd(8) injected interrupts */
/* Is there an interrupt pending injection? */
- if (irq != 0xFFFF) {
+ if (vcpu->vc_inject.vie_type == VCPU_INJECT_INTR) {
if (vmread(VMCS_GUEST_INTERRUPTIBILITY_ST, &int_st)) {
printf("%s: can't get interruptibility state\n",
__func__);
@@ -4063,7 +4065,7 @@ vcpu_run_vmx(struct vcpu *vcpu, struct vm_run_params *
/* Interruptibility state 0x3 covers NMIs and STI */
if (!(int_st & 0x3) && vcpu->vc_irqready) {
- eii = (irq & 0xFF);
+ eii = vcpu->vc_inject.vie_vector;
eii |= (1ULL << 31); /* Valid */
eii |= (0ULL << 8); /* Hardware Interrupt */
if (vmwrite(VMCS_ENTRY_INTERRUPTION_INFO, eii)) {
@@ -4072,7 +4074,7 @@ vcpu_run_vmx(struct vcpu *vcpu, struct vm_run_params *
return (EINVAL);
}
- irq = 0xFFFF;
+ vcpu->vc_inject.vie_type = VCPU_INJECT_NONE;
}
} else if (!vcpu->vc_intr) {
/*
@@ -4130,12 +4132,12 @@ vcpu_run_vmx(struct vcpu *vcpu, struct vm_run_params *
}
/* Inject event if present */
- if (vcpu->vc_event != 0) {
- eii = (vcpu->vc_event & 0xFF);
+ if (vcpu->vc_inject.vie_type == VCPU_INJECT_EX) {
+ eii = vcpu->vc_inject.vie_vector;
eii |= (1ULL << 31); /* Valid */
/* Set the "Send error code" flag for certain vectors */
- switch (vcpu->vc_event & 0xFF) {
+ switch (vcpu->vc_inject.vie_vector) {
case VMM_EX_DF:
case VMM_EX_TS:
case VMM_EX_NP:
@@ -4161,7 +4163,7 @@ vcpu_run_vmx(struct vcpu *vcpu, struct vm_run_params *
break;
}
- vcpu->vc_event = 0;
+ vcpu->vc_inject.vie_type = VCPU_INJECT_NONE;
}
if (vcpu->vc_vmx_vpid_enabled) {
@@ -4739,7 +4741,8 @@ vmm_inject_gp(struct vcpu *vcpu)
{
DPRINTF("%s: injecting #GP at guest %%rip 0x%llx\n", __func__,
vcpu->vc_gueststate.vg_rip);
- vcpu->vc_event = VMM_EX_GP;
+ vcpu->vc_inject.vie_vector = VMM_EX_GP;
+ vcpu->vc_inject.vie_type = VCPU_INJECT_EX;
return (0);
}
@@ -4760,7 +4763,8 @@ vmm_inject_ud(struct vcpu *vcpu)
{
DPRINTF("%s: injecting #UD at guest %%rip 0x%llx\n", __func__,
vcpu->vc_gueststate.vg_rip);
- vcpu->vc_event = VMM_EX_UD;
+ vcpu->vc_inject.vie_vector = VMM_EX_UD;
+ vcpu->vc_inject.vie_type = VCPU_INJECT_EX;
return (0);
}
@@ -4781,7 +4785,8 @@ vmm_inject_db(struct vcpu *vcpu)
{
DPRINTF("%s: injecting #DB at guest %%rip 0x%llx\n", __func__,
vcpu->vc_gueststate.vg_rip);
- vcpu->vc_event = VMM_EX_DB;
+ vcpu->vc_inject.vie_vector = VMM_EX_DB;
+ vcpu->vc_inject.vie_type = VCPU_INJECT_EX;
return (0);
}
@@ -6394,11 +6399,8 @@ vcpu_run_svm(struct vcpu *vcpu, struct vm_run_params *
struct cpu_info *ci = NULL;
uint64_t exit_reason;
struct schedstate_percpu *spc;
- uint16_t irq;
struct vmcb *vmcb = (struct vmcb *)vcpu->vc_control_va;
- irq = vrp->vrp_irq;
-
if (vrp->vrp_intr_pending)
vcpu->vc_intr = 1;
else
@@ -6472,18 +6474,21 @@ vcpu_run_svm(struct vcpu *vcpu, struct vm_run_params *
/* Handle vmd(8) injected interrupts */
/* Is there an interrupt pending injection? */
- if (irq != 0xFFFF && vcpu->vc_irqready) {
- vmcb->v_eventinj = (irq & 0xFF) | (1U << 31);
- irq = 0xFFFF;
+ if (vcpu->vc_inject.vie_type == VCPU_INJECT_INTR &&
+ vcpu->vc_irqready) {
+ vmcb->v_eventinj = vcpu->vc_inject.vie_vector |
+ (1U << 31);
+ vcpu->vc_inject.vie_type = VCPU_INJECT_NONE;
}
/* Inject event if present */
- if (vcpu->vc_event != 0) {
+ if (vcpu->vc_inject.vie_type == VCPU_INJECT_EX) {
DPRINTF("%s: inject event %d\n", __func__,
- vcpu->vc_event);
- vmcb->v_eventinj = 0;
+ vcpu->vc_inject.vie_vector);
+ vmcb->v_eventinj = vcpu->vc_inject.vie_vector;
+
/* Set the "Event Valid" flag for certain vectors */
- switch (vcpu->vc_event & 0xFF) {
+ switch (vcpu->vc_inject.vie_vector) {
case VMM_EX_DF:
case VMM_EX_TS:
case VMM_EX_NP:
@@ -6493,9 +6498,9 @@ vcpu_run_svm(struct vcpu *vcpu, struct vm_run_params *
case VMM_EX_AC:
vmcb->v_eventinj |= (1ULL << 11);
}
- vmcb->v_eventinj |= (vcpu->vc_event) | (1U << 31);
+ vmcb->v_eventinj |= (1U << 31);
vmcb->v_eventinj |= (3ULL << 8); /* Exception */
- vcpu->vc_event = 0;
+ vcpu->vc_inject.vie_type = VCPU_INJECT_NONE;
}
TRACEPOINT(vmm, guest_enter, vcpu, vrp);
blob - e6a35211b0f7aced0d580d5ae3ff8c32aa03cad4
blob + 2c30b8995746c66cab3994cbfa4fdde5cdf5755c
--- sys/arch/amd64/include/vmmvar.h
+++ sys/arch/amd64/include/vmmvar.h
@@ -340,6 +340,7 @@ struct vm_exit_inout {
uint32_t vei_data; /* data */
uint8_t vei_insn_len; /* Count of instruction bytes */
};
+
/*
* vm_exit_eptviolation : describes an EPT VIOLATION exit
*/
@@ -353,6 +354,19 @@ struct vm_exit_eptviolation {
};
/*
+ * struct vcpu_inject_event : describes an exception or interrupt to inject.
+ */
+struct vcpu_inject_event {
+ uint8_t vie_vector; /* Exception or interrupt vector. */
+ uint32_t vie_errorcode; /* Optional error code. */
+ uint8_t vie_type;
+#define VCPU_INJECT_NONE 0
+#define VCPU_INJECT_INTR 1 /* External hardware interrupt. */
+#define VCPU_INJECT_EX 2 /* HW or SW Exception */
+#define VCPU_INJECT_NMI 3 /* Non-maskable Interrupt */
+};
+
+/*
* struct vcpu_segment_info
*
* Describes a segment + selector set, used in constructing the initial vcpu
@@ -456,7 +470,7 @@ struct vm_run_params {
uint32_t vrp_vm_id;
uint32_t vrp_vcpu_id;
uint8_t vrp_continue; /* Continuing from an exit */
- uint16_t vrp_irq; /* IRQ to inject */
+ struct vcpu_inject_event vrp_inject;
uint8_t vrp_intr_pending; /* Additional intrs pending? */
/* Input/output parameter to VMM_IOC_RUN */
@@ -866,9 +880,8 @@ struct vcpu {
uint64_t vc_h_xcr0; /* [v] */
struct vcpu_gueststate vc_gueststate; /* [v] */
+ struct vcpu_inject_event vc_inject; /* [v] */
- uint8_t vc_event;
-
uint32_t vc_pvclock_version; /* [v] */
paddr_t vc_pvclock_system_gpa; /* [v] */
uint32_t vc_pvclock_system_tsc_mul; /* [v] */
blob - 772d044604d742116ff31e8e7e9ae4db059f6bf4
blob + 5add77a6427dbe5a1500e72c196a167d3f87ff81
--- usr.sbin/vmd/vm.c
+++ usr.sbin/vmd/vm.c
@@ -1536,7 +1536,6 @@ vcpu_run_loop(void *arg)
{
struct vm_run_params *vrp = (struct vm_run_params *)arg;
intptr_t ret = 0;
- int irq;
uint32_t n;
vrp->vrp_continue = 0;
@@ -1611,10 +1610,10 @@ vcpu_run_loop(void *arg)
}
if (vrp->vrp_irqready && i8259_is_pending()) {
- irq = i8259_ack();
- vrp->vrp_irq = irq;
+ vrp->vrp_inject.vie_vector = i8259_ack();
+ vrp->vrp_inject.vie_type = VCPU_INJECT_INTR;
} else
- vrp->vrp_irq = 0xFFFF;
+ vrp->vrp_inject.vie_type = VCPU_INJECT_NONE;
/* Still more interrupts pending? */
vrp->vrp_intr_pending = i8259_is_pending();
vmm/vmd: introduce exception injection api