'struct vm_exception' was intended to be used only as the collateral for the
VM_INJECT_EXCEPTION ioctl. However it morphed into other uses like keeping track pending exceptions for a vcpu. This in turn causes confusion because some fields in 'struct vm_exception' like 'vcpuid' make sense only in the ioctl context. It also makes it harder to add or remove structure fields. Fix this by using 'struct vm_exception' only to communicate information from userspace to vmm.ko when injecting an exception. Also, add a field 'restart_instruction' to 'struct vm_exception'. This field is set to '1' for exceptions where the faulting instruction is restarted after the exception is handled. MFC after: 1 week
This commit is contained in:
parent
df2eab9144
commit
5c965bc583
@ -289,7 +289,7 @@ struct vpmtmr *vm_pmtmr(struct vm *vm);
|
||||
struct vrtc *vm_rtc(struct vm *vm);
|
||||
|
||||
/*
|
||||
* Inject exception 'vme' into the guest vcpu. This function returns 0 on
|
||||
* Inject exception 'vector' into the guest vcpu. This function returns 0 on
|
||||
* success and non-zero on failure.
|
||||
*
|
||||
* Wrapper functions like 'vm_inject_gp()' should be preferred to calling
|
||||
@ -299,7 +299,8 @@ struct vrtc *vm_rtc(struct vm *vm);
|
||||
* This function should only be called in the context of the thread that is
|
||||
* executing this vcpu.
|
||||
*/
|
||||
int vm_inject_exception(struct vm *vm, int vcpuid, struct vm_exception *vme);
|
||||
int vm_inject_exception(struct vm *vm, int vcpuid, int vector, int err_valid,
|
||||
uint32_t errcode, int restart_instruction);
|
||||
|
||||
/*
|
||||
* This function is called after a VM-exit that occurred during exception or
|
||||
@ -628,4 +629,6 @@ vm_inject_ss(void *vm, int vcpuid, int errcode)
|
||||
|
||||
void vm_inject_pf(void *vm, int vcpuid, int error_code, uint64_t cr2);
|
||||
|
||||
int vm_restart_instruction(void *vm, int vcpuid);
|
||||
|
||||
#endif /* _VMM_H_ */
|
||||
|
@ -63,6 +63,7 @@ struct vm_exception {
|
||||
int vector;
|
||||
uint32_t error_code;
|
||||
int error_code_valid;
|
||||
int restart_instruction;
|
||||
};
|
||||
|
||||
struct vm_lapic_msi {
|
||||
|
@ -1201,7 +1201,6 @@ svm_vmexit(struct svm_softc *svm_sc, int vcpu, struct vm_exit *vmexit)
|
||||
struct vmcb_state *state;
|
||||
struct vmcb_ctrl *ctrl;
|
||||
struct svm_regctx *ctx;
|
||||
struct vm_exception exception;
|
||||
uint64_t code, info1, info2, val;
|
||||
uint32_t eax, ecx, edx;
|
||||
int error, errcode_valid, handled, idtvec, reflect;
|
||||
@ -1315,6 +1314,7 @@ svm_vmexit(struct svm_softc *svm_sc, int vcpu, struct vm_exit *vmexit)
|
||||
/* fallthru */
|
||||
default:
|
||||
errcode_valid = 0;
|
||||
info1 = 0;
|
||||
break;
|
||||
}
|
||||
KASSERT(vmexit->inst_length == 0, ("invalid inst_length (%d) "
|
||||
@ -1323,17 +1323,10 @@ svm_vmexit(struct svm_softc *svm_sc, int vcpu, struct vm_exit *vmexit)
|
||||
|
||||
if (reflect) {
|
||||
/* Reflect the exception back into the guest */
|
||||
bzero(&exception, sizeof(struct vm_exception));
|
||||
exception.vector = idtvec;
|
||||
if (errcode_valid) {
|
||||
exception.error_code = info1;
|
||||
exception.error_code_valid = 1;
|
||||
}
|
||||
VCPU_CTR2(svm_sc->vm, vcpu, "Reflecting exception "
|
||||
"%d/%#x into the guest", exception.vector,
|
||||
exception.error_code);
|
||||
error = vm_inject_exception(svm_sc->vm, vcpu,
|
||||
&exception);
|
||||
"%d/%#x into the guest", idtvec, (int)info1);
|
||||
error = vm_inject_exception(svm_sc->vm, vcpu, idtvec,
|
||||
errcode_valid, info1, 0);
|
||||
KASSERT(error == 0, ("%s: vm_inject_exception error %d",
|
||||
__func__, error));
|
||||
}
|
||||
|
@ -1784,7 +1784,7 @@ vmexit_inst_emul(struct vm_exit *vmexit, uint64_t gpa, uint64_t gla)
|
||||
{
|
||||
struct vm_guest_paging *paging;
|
||||
uint32_t csar;
|
||||
|
||||
|
||||
paging = &vmexit->u.inst_emul.paging;
|
||||
|
||||
vmexit->exitcode = VM_EXITCODE_INST_EMUL;
|
||||
@ -2073,12 +2073,11 @@ emulate_rdmsr(struct vmx *vmx, int vcpuid, u_int num, bool *retu)
|
||||
static int
|
||||
vmx_exit_process(struct vmx *vmx, int vcpu, struct vm_exit *vmexit)
|
||||
{
|
||||
int error, handled, in;
|
||||
int error, errcode, errcode_valid, handled, in;
|
||||
struct vmxctx *vmxctx;
|
||||
struct vlapic *vlapic;
|
||||
struct vm_inout_str *vis;
|
||||
struct vm_task_switch *ts;
|
||||
struct vm_exception vmexc;
|
||||
uint32_t eax, ecx, edx, idtvec_info, idtvec_err, intr_info, inst_info;
|
||||
uint32_t intr_type, intr_vec, reason;
|
||||
uint64_t exitintinfo, qual, gpa;
|
||||
@ -2263,6 +2262,7 @@ vmx_exit_process(struct vmx *vmx, int vcpu, struct vm_exit *vmexit)
|
||||
case EXIT_REASON_MTF:
|
||||
vmm_stat_incr(vmx->vm, vcpu, VMEXIT_MTRAP, 1);
|
||||
vmexit->exitcode = VM_EXITCODE_MTRAP;
|
||||
vmexit->inst_length = 0;
|
||||
break;
|
||||
case EXIT_REASON_PAUSE:
|
||||
vmm_stat_incr(vmx->vm, vcpu, VMEXIT_PAUSE, 1);
|
||||
@ -2389,15 +2389,15 @@ vmx_exit_process(struct vmx *vmx, int vcpu, struct vm_exit *vmexit)
|
||||
vmcs_write(VMCS_ENTRY_INST_LENGTH, vmexit->inst_length);
|
||||
|
||||
/* Reflect all other exceptions back into the guest */
|
||||
bzero(&vmexc, sizeof(struct vm_exception));
|
||||
vmexc.vector = intr_vec;
|
||||
errcode_valid = errcode = 0;
|
||||
if (intr_info & VMCS_INTR_DEL_ERRCODE) {
|
||||
vmexc.error_code_valid = 1;
|
||||
vmexc.error_code = vmcs_read(VMCS_EXIT_INTR_ERRCODE);
|
||||
errcode_valid = 1;
|
||||
errcode = vmcs_read(VMCS_EXIT_INTR_ERRCODE);
|
||||
}
|
||||
VCPU_CTR2(vmx->vm, vcpu, "Reflecting exception %d/%#x into "
|
||||
"the guest", vmexc.vector, vmexc.error_code);
|
||||
error = vm_inject_exception(vmx->vm, vcpu, &vmexc);
|
||||
"the guest", intr_vec, errcode);
|
||||
error = vm_inject_exception(vmx->vm, vcpu, intr_vec,
|
||||
errcode_valid, errcode, 0);
|
||||
KASSERT(error == 0, ("%s: vm_inject_exception error %d",
|
||||
__func__, error));
|
||||
return (1);
|
||||
|
@ -101,8 +101,10 @@ struct vcpu {
|
||||
uint64_t exitintinfo; /* (i) events pending at VM exit */
|
||||
int nmi_pending; /* (i) NMI pending */
|
||||
int extint_pending; /* (i) INTR pending */
|
||||
struct vm_exception exception; /* (x) exception collateral */
|
||||
int exception_pending; /* (i) exception pending */
|
||||
int exc_vector; /* (x) exception collateral */
|
||||
int exc_errcode_valid;
|
||||
uint32_t exc_errcode;
|
||||
struct savefpu *guestfpu; /* (a,i) guest fpu state */
|
||||
uint64_t guest_xcr0; /* (i) guest %xcr0 register */
|
||||
void *stats; /* (a,i) statistics */
|
||||
@ -1223,7 +1225,7 @@ vm_handle_paging(struct vm *vm, int vcpuid, bool *retu)
|
||||
return (EFAULT);
|
||||
done:
|
||||
/* restart execution at the faulting instruction */
|
||||
vme->inst_length = 0;
|
||||
vm_restart_instruction(vm, vcpuid);
|
||||
|
||||
return (0);
|
||||
}
|
||||
@ -1525,6 +1527,20 @@ vm_run(struct vm *vm, struct vm_run *vmrun)
|
||||
return (error);
|
||||
}
|
||||
|
||||
int
|
||||
vm_restart_instruction(void *arg, int vcpuid)
|
||||
{
|
||||
struct vcpu *vcpu;
|
||||
struct vm *vm = arg;
|
||||
|
||||
if (vcpuid < 0 || vcpuid >= VM_MAXCPU)
|
||||
return (EINVAL);
|
||||
|
||||
vcpu = &vm->vcpu[vcpuid];
|
||||
vcpu->exitinfo.inst_length = 0;
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
vm_exit_intinfo(struct vm *vm, int vcpuid, uint64_t info)
|
||||
{
|
||||
@ -1655,11 +1671,11 @@ vcpu_exception_intinfo(struct vcpu *vcpu)
|
||||
uint64_t info = 0;
|
||||
|
||||
if (vcpu->exception_pending) {
|
||||
info = vcpu->exception.vector & 0xff;
|
||||
info = vcpu->exc_vector & 0xff;
|
||||
info |= VM_INTINFO_VALID | VM_INTINFO_HWEXCEPTION;
|
||||
if (vcpu->exception.error_code_valid) {
|
||||
if (vcpu->exc_errcode_valid) {
|
||||
info |= VM_INTINFO_DEL_ERRCODE;
|
||||
info |= (uint64_t)vcpu->exception.error_code << 32;
|
||||
info |= (uint64_t)vcpu->exc_errcode << 32;
|
||||
}
|
||||
}
|
||||
return (info);
|
||||
@ -1684,7 +1700,7 @@ vm_entry_intinfo(struct vm *vm, int vcpuid, uint64_t *retinfo)
|
||||
info2 = vcpu_exception_intinfo(vcpu);
|
||||
vcpu->exception_pending = 0;
|
||||
VCPU_CTR2(vm, vcpuid, "Exception %d delivered: %#lx",
|
||||
vcpu->exception.vector, info2);
|
||||
vcpu->exc_vector, info2);
|
||||
}
|
||||
|
||||
if ((info1 & VM_INTINFO_VALID) && (info2 & VM_INTINFO_VALID)) {
|
||||
@ -1722,7 +1738,8 @@ vm_get_intinfo(struct vm *vm, int vcpuid, uint64_t *info1, uint64_t *info2)
|
||||
}
|
||||
|
||||
int
|
||||
vm_inject_exception(struct vm *vm, int vcpuid, struct vm_exception *exception)
|
||||
vm_inject_exception(struct vm *vm, int vcpuid, int vector, int errcode_valid,
|
||||
uint32_t errcode, int restart_instruction)
|
||||
{
|
||||
struct vcpu *vcpu;
|
||||
int error;
|
||||
@ -1730,7 +1747,7 @@ vm_inject_exception(struct vm *vm, int vcpuid, struct vm_exception *exception)
|
||||
if (vcpuid < 0 || vcpuid >= VM_MAXCPU)
|
||||
return (EINVAL);
|
||||
|
||||
if (exception->vector < 0 || exception->vector >= 32)
|
||||
if (vector < 0 || vector >= 32)
|
||||
return (EINVAL);
|
||||
|
||||
/*
|
||||
@ -1738,15 +1755,14 @@ vm_inject_exception(struct vm *vm, int vcpuid, struct vm_exception *exception)
|
||||
* the guest. It is a derived exception that results from specific
|
||||
* combinations of nested faults.
|
||||
*/
|
||||
if (exception->vector == IDT_DF)
|
||||
if (vector == IDT_DF)
|
||||
return (EINVAL);
|
||||
|
||||
vcpu = &vm->vcpu[vcpuid];
|
||||
|
||||
if (vcpu->exception_pending) {
|
||||
VCPU_CTR2(vm, vcpuid, "Unable to inject exception %d due to "
|
||||
"pending exception %d", exception->vector,
|
||||
vcpu->exception.vector);
|
||||
"pending exception %d", vector, vcpu->exc_vector);
|
||||
return (EBUSY);
|
||||
}
|
||||
|
||||
@ -1760,9 +1776,14 @@ vm_inject_exception(struct vm *vm, int vcpuid, struct vm_exception *exception)
|
||||
KASSERT(error == 0, ("%s: error %d clearing interrupt shadow",
|
||||
__func__, error));
|
||||
|
||||
if (restart_instruction)
|
||||
vm_restart_instruction(vm, vcpuid);
|
||||
|
||||
vcpu->exception_pending = 1;
|
||||
vcpu->exception = *exception;
|
||||
VCPU_CTR1(vm, vcpuid, "Exception %d pending", exception->vector);
|
||||
vcpu->exc_vector = vector;
|
||||
vcpu->exc_errcode = errcode;
|
||||
vcpu->exc_errcode_valid = errcode_valid;
|
||||
VCPU_CTR1(vm, vcpuid, "Exception %d pending", vector);
|
||||
return (0);
|
||||
}
|
||||
|
||||
@ -1770,28 +1791,15 @@ void
|
||||
vm_inject_fault(void *vmarg, int vcpuid, int vector, int errcode_valid,
|
||||
int errcode)
|
||||
{
|
||||
struct vm_exception exception;
|
||||
struct vm_exit *vmexit;
|
||||
struct vm *vm;
|
||||
int error;
|
||||
int error, restart_instruction;
|
||||
|
||||
vm = vmarg;
|
||||
restart_instruction = 1;
|
||||
|
||||
exception.vector = vector;
|
||||
exception.error_code = errcode;
|
||||
exception.error_code_valid = errcode_valid;
|
||||
error = vm_inject_exception(vm, vcpuid, &exception);
|
||||
error = vm_inject_exception(vm, vcpuid, vector, errcode_valid,
|
||||
errcode, restart_instruction);
|
||||
KASSERT(error == 0, ("vm_inject_exception error %d", error));
|
||||
|
||||
/*
|
||||
* A fault-like exception allows the instruction to be restarted
|
||||
* after the exception handler returns.
|
||||
*
|
||||
* By setting the inst_length to 0 we ensure that the instruction
|
||||
* pointer remains at the faulting instruction.
|
||||
*/
|
||||
vmexit = vm_exitinfo(vm, vcpuid);
|
||||
vmexit->inst_length = 0;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -310,7 +310,9 @@ vmmdev_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag,
|
||||
break;
|
||||
case VM_INJECT_EXCEPTION:
|
||||
vmexc = (struct vm_exception *)data;
|
||||
error = vm_inject_exception(sc->vm, vmexc->cpuid, vmexc);
|
||||
error = vm_inject_exception(sc->vm, vmexc->cpuid,
|
||||
vmexc->vector, vmexc->error_code_valid, vmexc->error_code,
|
||||
vmexc->restart_instruction);
|
||||
break;
|
||||
case VM_INJECT_NMI:
|
||||
vmnmi = (struct vm_nmi *)data;
|
||||
|
Loading…
Reference in New Issue
Block a user