Changeset View
Changeset View
Standalone View
Standalone View
sys/amd64/vmm/intel/vmx.c
Show First 20 Lines • Show All 300 Lines • ▼ Show 20 Lines | |||||
* Use the last page below 4GB as the APIC access address. This address is | * Use the last page below 4GB as the APIC access address. This address is | ||||
* occupied by the boot firmware so it is guaranteed that it will not conflict | * occupied by the boot firmware so it is guaranteed that it will not conflict | ||||
* with a page in system memory. | * with a page in system memory. | ||||
*/ | */ | ||||
#define APIC_ACCESS_ADDRESS 0xFFFFF000 | #define APIC_ACCESS_ADDRESS 0xFFFFF000 | ||||
static int vmx_getdesc(void *arg, int vcpu, int reg, struct seg_desc *desc); | static int vmx_getdesc(void *arg, int vcpu, int reg, struct seg_desc *desc); | ||||
static int vmx_getreg(void *arg, int vcpu, int reg, uint64_t *retval); | static int vmx_getreg(void *arg, int vcpu, int reg, uint64_t *retval); | ||||
static int vmx_setreg(void *arg, int vcpu, int reg, uint64_t val); | |||||
static int vmxctx_setreg(struct vmxctx *vmxctx, int reg, uint64_t val); | static int vmxctx_setreg(struct vmxctx *vmxctx, int reg, uint64_t val); | ||||
static void vmx_inject_pir(struct vlapic *vlapic); | static void vmx_inject_pir(struct vlapic *vlapic); | ||||
#ifdef BHYVE_SNAPSHOT | #ifdef BHYVE_SNAPSHOT | ||||
static int vmx_restore_tsc(void *arg, int vcpu, uint64_t now); | static int vmx_restore_tsc(void *arg, int vcpu, uint64_t now); | ||||
#endif | #endif | ||||
static inline bool | static inline bool | ||||
host_has_rdpid(void) | host_has_rdpid(void) | ||||
▲ Show 20 Lines • Show All 2,002 Lines • ▼ Show 20 Lines | if (error == 0) { | ||||
edx = result >> 32; | edx = result >> 32; | ||||
error = vmxctx_setreg(vmxctx, VM_REG_GUEST_RDX, edx); | error = vmxctx_setreg(vmxctx, VM_REG_GUEST_RDX, edx); | ||||
KASSERT(error == 0, ("vmxctx_setreg(rdx) error %d", error)); | KASSERT(error == 0, ("vmxctx_setreg(rdx) error %d", error)); | ||||
} | } | ||||
return (error); | return (error); | ||||
} | } | ||||
static __inline int | |||||
mov_dr_gpr_num_to_reg(int gpr) | |||||
{ | |||||
switch (gpr) { | |||||
case 0: | |||||
return VM_REG_GUEST_RAX; | |||||
case 1: | |||||
return VM_REG_GUEST_RCX; | |||||
case 2: | |||||
return VM_REG_GUEST_RDX; | |||||
case 3: | |||||
return VM_REG_GUEST_RBX; | |||||
case 4: | |||||
return VM_REG_GUEST_RSP; | |||||
case 5: | |||||
return VM_REG_GUEST_RBP; | |||||
case 6: | |||||
return VM_REG_GUEST_RSI; | |||||
case 7: | |||||
return VM_REG_GUEST_RDI; | |||||
case 8 ... 15: | |||||
return VM_REG_GUEST_R8 + (gpr - 8); | |||||
default: | |||||
break; | |||||
}; | |||||
return -1; | |||||
} | |||||
/* | |||||
* Emulates MOV DR according to Intel SDM Vol. 2B 4-43. | |||||
*/ | |||||
static int | static int | ||||
emulate_mov_dr(struct vmx *vmx, struct vm_exit *vmexit, int vcpu, uint64_t qual) | |||||
{ | |||||
int error; | |||||
int cpl, src, dst; | |||||
int dbreg; | |||||
uint64_t regval; | |||||
int dbreg_num = EXIT_QUAL_MOV_DR_REG(qual); | |||||
int gpr = mov_dr_gpr_num_to_reg(EXIT_QUAL_MOV_DR_GPR(qual)); | |||||
int write = (EXIT_QUAL_MOV_DR_RW(qual) == 0); | |||||
cpl = vmx_cpl(); | |||||
if (cpl != 0) { | |||||
vm_inject_gp(vmx->vm, vcpu); | |||||
return 1; | |||||
} | |||||
error = vmx_getreg(vmx, vcpu, VM_REG_GUEST_CR4, ®val); | |||||
KASSERT( | |||||
error == 0, ("%s: error %d fetching GPR %d", __func__, error, gpr)); | |||||
if ((regval & CR4_DE) && (dbreg_num == 4 || dbreg_num == 5)) { | |||||
vm_inject_ud(vmx->vm, vcpu); | |||||
return 1; | |||||
} | |||||
switch (dbreg_num) { | |||||
/* TODO: figure out how to handle DR{4,5} */ | |||||
case 0 ... 3: | |||||
dbreg = VM_REG_GUEST_DR0 + dbreg_num; | |||||
break; | |||||
case 6: | |||||
dbreg = VM_REG_GUEST_DR6; | |||||
break; | |||||
case 7: | |||||
dbreg = VM_REG_GUEST_DR7; | |||||
break; | |||||
default: | |||||
return -1; | |||||
break; | |||||
} | |||||
/* | |||||
* Bounce exit to userland - allow the | |||||
* gdb stub to adjust its watchpoint metadata | |||||
*/ | |||||
vmexit->exitcode = VM_EXITCODE_DB; | |||||
vmexit->u.dbg.trace_trap = 0; | |||||
vmexit->u.dbg.pushf_intercept = 0; | |||||
vmexit->u.dbg.drx_access = dbreg_num; | |||||
vmexit->u.dbg.gpr = -1; | |||||
if (write) { | |||||
dst = dbreg; | |||||
src = gpr; | |||||
} else { | |||||
dst = gpr; | |||||
src = dbreg; | |||||
vmexit->u.dbg.gpr = gpr; | |||||
} | |||||
error = vmx_getreg(vmx, vcpu, src, ®val); | |||||
KASSERT(error == 0, | |||||
("%s: error %d fetching register %d", __func__, error, src)); | |||||
if (write && dbreg_num == 7) { | |||||
vmexit->u.dbg.watchpoints = (int)(regval); | |||||
} | |||||
error = vmx_setreg(vmx, vcpu, dst, regval); | |||||
KASSERT(error == 0, | |||||
("%s: error %d updating register %d", __func__, error, dst)); | |||||
return error; | |||||
} | |||||
static int | |||||
vmx_exit_process(struct vmx *vmx, int vcpu, struct vm_exit *vmexit) | vmx_exit_process(struct vmx *vmx, int vcpu, struct vm_exit *vmexit) | ||||
{ | { | ||||
int error, errcode, errcode_valid, handled, in; | int error, errcode, errcode_valid, handled, in; | ||||
struct vmxctx *vmxctx; | struct vmxctx *vmxctx; | ||||
struct vlapic *vlapic; | struct vlapic *vlapic; | ||||
struct vm_inout_str *vis; | struct vm_inout_str *vis; | ||||
struct vm_task_switch *ts; | struct vm_task_switch *ts; | ||||
uint32_t eax, ecx, edx, idtvec_info, idtvec_err, intr_info, inst_info; | uint32_t eax, ecx, edx, idtvec_info, idtvec_err, intr_info, inst_info; | ||||
▲ Show 20 Lines • Show All 131 Lines • ▼ Show 20 Lines | case EXIT_REASON_CR_ACCESS: | ||||
case 4: | case 4: | ||||
handled = vmx_emulate_cr4_access(vmx, vcpu, qual); | handled = vmx_emulate_cr4_access(vmx, vcpu, qual); | ||||
break; | break; | ||||
case 8: | case 8: | ||||
handled = vmx_emulate_cr8_access(vmx, vcpu, qual); | handled = vmx_emulate_cr8_access(vmx, vcpu, qual); | ||||
break; | break; | ||||
} | } | ||||
break; | break; | ||||
case EXIT_REASON_DR_ACCESS: | |||||
handled = 0; | |||||
error = emulate_mov_dr(vmx, vmexit, vcpu, qual); | |||||
KASSERT( | |||||
error >= 0, ("%s: emulate_mov_dr returned -1", __func__)); | |||||
if (error == 1) { | |||||
/* Fault was injected into guest */ | |||||
vmexit->exitcode = VM_EXITCODE_BOGUS; | |||||
handled = 1; | |||||
} | |||||
break; | |||||
case EXIT_REASON_RDMSR: | case EXIT_REASON_RDMSR: | ||||
vmm_stat_incr(vmx->vm, vcpu, VMEXIT_RDMSR, 1); | vmm_stat_incr(vmx->vm, vcpu, VMEXIT_RDMSR, 1); | ||||
retu = false; | retu = false; | ||||
ecx = vmxctx->guest_rcx; | ecx = vmxctx->guest_rcx; | ||||
VCPU_CTR1(vmx->vm, vcpu, "rdmsr 0x%08x", ecx); | VCPU_CTR1(vmx->vm, vcpu, "rdmsr 0x%08x", ecx); | ||||
SDT_PROBE4(vmm, vmx, exit, rdmsr, vmx, vcpu, vmexit, ecx); | SDT_PROBE4(vmm, vmx, exit, rdmsr, vmx, vcpu, vmexit, ecx); | ||||
error = emulate_rdmsr(vmx, vcpu, ecx, &retu); | error = emulate_rdmsr(vmx, vcpu, ecx, &retu); | ||||
if (error) { | if (error) { | ||||
▲ Show 20 Lines • Show All 170 Lines • ▼ Show 20 Lines | case EXIT_REASON_EXCEPTION: | ||||
*/ | */ | ||||
if (intr_type == VMCS_INTR_T_SWEXCEPTION && intr_vec == IDT_BP && | if (intr_type == VMCS_INTR_T_SWEXCEPTION && intr_vec == IDT_BP && | ||||
(vmx->cap[vcpu].set & (1 << VM_CAP_BPT_EXIT))) { | (vmx->cap[vcpu].set & (1 << VM_CAP_BPT_EXIT))) { | ||||
vmexit->exitcode = VM_EXITCODE_BPT; | vmexit->exitcode = VM_EXITCODE_BPT; | ||||
vmexit->u.bpt.inst_length = vmexit->inst_length; | vmexit->u.bpt.inst_length = vmexit->inst_length; | ||||
vmexit->inst_length = 0; | vmexit->inst_length = 0; | ||||
break; | break; | ||||
} | } | ||||
if (intr_type == VMCS_INTR_T_HWEXCEPTION && | |||||
intr_vec == IDT_DB && | |||||
(vmx->cap[vcpu].set & (1 << VM_CAP_DB_EXIT))) { | |||||
int reflect = 0; | |||||
/* | |||||
* A debug exception VMEXIT does not update the DR{6,7} | |||||
* registers (SDM Vol. 3C 27-1). It is therefore | |||||
* necessary to emulate these writes here. | |||||
* | |||||
* We reflect everything except watchpoint hits. Since | |||||
* it is up to the userland to reinject a debug | |||||
* exception when a guest watchpoint is hit, the | |||||
* register must be updated here so that the guest may | |||||
* properly register the watchpoint hit. | |||||
*/ | |||||
int trace_trap = !!(qual & EXIT_QUAL_DBG_BS); | |||||
int debug_detect = !!(qual & EXIT_QUAL_DBG_BD); | |||||
int watch_mask = qual & EXIT_QUAL_DBG_B_MASK; | |||||
uint64_t dr6; | |||||
error = vmx_getreg(vmx, vcpu, VM_REG_GUEST_DR6, &dr6); | |||||
KASSERT(error == 0, | |||||
("%s: error %d fetching DR6", __func__, error)); | |||||
uint64_t regval; | |||||
error = vmx_getreg( | |||||
vmx, vcpu, VM_REG_GUEST_RFLAGS, ®val); | |||||
KASSERT(error == 0, | |||||
("%s: error %d fetching DR6", __func__, error)); | |||||
dr6 &= DBREG_DR6_RESERVED1; | |||||
/* | |||||
* Clear the RTM flag (0 indicates a hit, | |||||
* Intel SDM Vol. 3B 17-3 ). | |||||
*/ | |||||
dr6 |= (1 << 16); | |||||
if (watch_mask) { | |||||
vmexit->exitcode = VM_EXITCODE_DB; | |||||
vmexit->u.dbg.pushf_intercept = 0; | |||||
vmexit->u.dbg.trace_trap = 0; | |||||
vmexit->u.dbg.drx_access = -1; | |||||
vmexit->u.dbg.watchpoints = watch_mask; | |||||
vmexit->u.dbg.drx_access = -1; | |||||
vmexit->u.dbg.watchpoints = watch_mask; | |||||
dr6 |= watch_mask; | |||||
/* Bounce to userland */ | |||||
reflect = 0; | |||||
} else { | |||||
dr6 |= debug_detect ? DBREG_DR6_BD : 0; | |||||
dr6 |= (trace_trap) ? DBREG_DR6_BS : 0; | |||||
regval &= ~(PSL_T); | |||||
/* Reflect back into guest */ | |||||
reflect = 1; | |||||
} | |||||
error = vmx_setreg(vmx, vcpu, VM_REG_GUEST_DR6, dr6); | |||||
KASSERT(error == 0, | |||||
("%s: error %d updating DR6", __func__, error)); | |||||
error = vmx_setreg( | |||||
vmx, vcpu, VM_REG_GUEST_RFLAGS, regval); | |||||
KASSERT(error == 0, | |||||
("%s: error %d fetching DR6", __func__, error)); | |||||
if (!reflect) { | |||||
break; | |||||
} | |||||
} | |||||
if (intr_vec == IDT_PF) { | if (intr_vec == IDT_PF) { | ||||
error = vmxctx_setreg(vmxctx, VM_REG_GUEST_CR2, qual); | error = vmxctx_setreg(vmxctx, VM_REG_GUEST_CR2, qual); | ||||
KASSERT(error == 0, ("%s: vmxctx_setreg(cr2) error %d", | KASSERT(error == 0, ("%s: vmxctx_setreg(cr2) error %d", | ||||
__func__, error)); | __func__, error)); | ||||
} | } | ||||
/* | /* | ||||
* Software exceptions exhibit trap-like behavior. This in | * Software exceptions exhibit trap-like behavior. This in | ||||
▲ Show 20 Lines • Show All 813 Lines • ▼ Show 20 Lines | case VM_CAP_UNRESTRICTED_GUEST: | ||||
if (cap_unrestricted_guest) | if (cap_unrestricted_guest) | ||||
ret = 0; | ret = 0; | ||||
break; | break; | ||||
case VM_CAP_ENABLE_INVPCID: | case VM_CAP_ENABLE_INVPCID: | ||||
if (cap_invpcid) | if (cap_invpcid) | ||||
ret = 0; | ret = 0; | ||||
break; | break; | ||||
case VM_CAP_BPT_EXIT: | case VM_CAP_BPT_EXIT: | ||||
case VM_CAP_DB_EXIT: | |||||
case VM_CAP_DR_MOV_EXIT: | |||||
ret = 0; | ret = 0; | ||||
break; | break; | ||||
default: | default: | ||||
break; | break; | ||||
} | } | ||||
if (ret == 0) | if (ret == 0) | ||||
*retval = (vcap & (1 << type)) ? 1 : 0; | *retval = (vcap & (1 << type)) ? 1 : 0; | ||||
▲ Show 20 Lines • Show All 78 Lines • ▼ Show 20 Lines | case VM_CAP_BPT_EXIT: | ||||
/* Don't change the bitmap if we are tracing all exceptions. */ | /* Don't change the bitmap if we are tracing all exceptions. */ | ||||
if (vmx->cap[vcpu].exc_bitmap != 0xffffffff) { | if (vmx->cap[vcpu].exc_bitmap != 0xffffffff) { | ||||
pptr = &vmx->cap[vcpu].exc_bitmap; | pptr = &vmx->cap[vcpu].exc_bitmap; | ||||
baseval = *pptr; | baseval = *pptr; | ||||
flag = (1 << IDT_BP); | flag = (1 << IDT_BP); | ||||
reg = VMCS_EXCEPTION_BITMAP; | reg = VMCS_EXCEPTION_BITMAP; | ||||
} | } | ||||
break; | |||||
case VM_CAP_DB_EXIT: | |||||
retval = 0; | |||||
/* Don't change the bitmap if we are tracing all exceptions. */ | |||||
if (vmx->cap[vcpu].exc_bitmap != 0xffffffff) { | |||||
pptr = &vmx->cap[vcpu].exc_bitmap; | |||||
baseval = *pptr; | |||||
flag = (1 << IDT_DB); | |||||
reg = VMCS_EXCEPTION_BITMAP; | |||||
} | |||||
break; | |||||
case VM_CAP_DR_MOV_EXIT: | |||||
retval = 0; | |||||
pptr = &vmx->cap[vcpu].proc_ctls; | |||||
baseval = *pptr; | |||||
flag = PROCBASED_MOV_DR_EXITING; | |||||
reg = VMCS_PRI_PROC_BASED_CTLS; | |||||
break; | break; | ||||
default: | default: | ||||
break; | break; | ||||
} | } | ||||
if (retval) | if (retval) | ||||
return (retval); | return (retval); | ||||
▲ Show 20 Lines • Show All 624 Lines • Show Last 20 Lines |