Changeset View
Changeset View
Standalone View
Standalone View
sys/amd64/vmm/amd/svm.c
Show First 20 Lines • Show All 128 Lines • ▼ Show 20 Lines | |||||
static uint8_t hsave[MAXCPU][PAGE_SIZE] __aligned(PAGE_SIZE); | static uint8_t hsave[MAXCPU][PAGE_SIZE] __aligned(PAGE_SIZE); | ||||
static VMM_STAT_AMD(VCPU_EXITINTINFO, "VM exits during event delivery"); | static VMM_STAT_AMD(VCPU_EXITINTINFO, "VM exits during event delivery"); | ||||
static VMM_STAT_AMD(VCPU_INTINFO_INJECTED, "Events pending at VM entry"); | static VMM_STAT_AMD(VCPU_INTINFO_INJECTED, "Events pending at VM entry"); | ||||
static VMM_STAT_AMD(VMEXIT_VINTR, "VM exits due to interrupt window"); | static VMM_STAT_AMD(VMEXIT_VINTR, "VM exits due to interrupt window"); | ||||
static int svm_getdesc(void *arg, int vcpu, int reg, struct seg_desc *desc); | static int svm_getdesc(void *arg, int vcpu, int reg, struct seg_desc *desc); | ||||
static int svm_setreg(void *arg, int vcpu, int ident, uint64_t val); | static int svm_setreg(void *arg, int vcpu, int ident, uint64_t val); | ||||
static int svm_getreg(void *arg, int vcpu, int ident, uint64_t *val); | |||||
static __inline int | static __inline int | ||||
flush_by_asid(void) | flush_by_asid(void) | ||||
{ | { | ||||
return (svm_feature & AMD_CPUID_SVM_FLUSH_BY_ASID); | return (svm_feature & AMD_CPUID_SVM_FLUSH_BY_ASID); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 1,169 Lines • ▼ Show 20 Lines | nrip_valid(uint64_t exitcode) | ||||
case 0x65 ... 0x7C: /* VMEXIT_CR0_SEL_WRITE ... VMEXIT_MSR */ | case 0x65 ... 0x7C: /* VMEXIT_CR0_SEL_WRITE ... VMEXIT_MSR */ | ||||
case 0x80 ... 0x8D: /* VMEXIT_VMRUN ... VMEXIT_XSETBV */ | case 0x80 ... 0x8D: /* VMEXIT_VMRUN ... VMEXIT_XSETBV */ | ||||
return (1); | return (1); | ||||
default: | default: | ||||
return (0); | return (0); | ||||
} | } | ||||
} | } | ||||
static __inline int | |||||
mov_dr_gpr_num_to_reg(int gpr) | |||||
{ | |||||
switch (gpr) { | |||||
case 0 ... 3: | |||||
return VM_REG_GUEST_RAX + gpr; | |||||
case 4: | |||||
return VM_REG_GUEST_RDI; | |||||
case 5: | |||||
return VM_REG_GUEST_RSI; | |||||
case 6: | |||||
return VM_REG_GUEST_RBP; | |||||
case 7: | |||||
return VM_REG_GUEST_RSP; | |||||
case 8 ... 15: | |||||
return VM_REG_GUEST_R8 + (gpr - 8); | |||||
default: | |||||
break; | |||||
}; | |||||
return -1; | |||||
} | |||||
static int | static int | ||||
emulate_mov_dr(struct svm_softc *svm_sc, struct vm_exit *vmexit, int vcpu, | |||||
uint64_t code, uint64_t info1) | |||||
{ | |||||
int write, error; | |||||
int src, dst; | |||||
int dbreg_num, dbreg; | |||||
int gpr = mov_dr_gpr_num_to_reg(VMCB_DR_INTCTP_GPR_NUM(info1)); | |||||
uint64_t new_dst_val; | |||||
KASSERT(gpr >= 0, ("%s: invalid GPR num %d\r\n", __func__, gpr)); | |||||
if (code >= 0x20 && code <= 0x27) { | |||||
dbreg_num = code - 0x20; | |||||
write = 0; | |||||
} else if (code >= 0x30 && code <= 0x37) { | |||||
dbreg_num = code - 0x30; | |||||
write = 1; | |||||
} else { | |||||
// should not happen | |||||
return -1; | |||||
} | |||||
/* | |||||
* 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; | |||||
/* | |||||
* Emulate MOV DR. | |||||
* No checks are needed since all other | |||||
* exceptions take precedence over the intercept. | |||||
* (AMD APM v2, page 498) | |||||
*/ | |||||
if (dbreg_num == 7) { | |||||
dbreg = VM_REG_GUEST_DR7; | |||||
} else { | |||||
dbreg = VM_REG_GUEST_DR0 + dbreg_num; | |||||
} | |||||
if (write) { | |||||
src = gpr; | |||||
dst = dbreg; | |||||
} else { | |||||
vmexit->u.dbg.gpr = gpr; | |||||
src = dbreg; | |||||
dst = gpr; | |||||
} | |||||
error = svm_getreg(svm_sc, vcpu, src, &new_dst_val); | |||||
KASSERT(error == 0, | |||||
("%s: error %d fetching reg %d\r\n", __func__, error, src)); | |||||
if (write && dbreg_num == 7) { | |||||
vmexit->u.dbg.watchpoints = (int)new_dst_val; | |||||
} | |||||
error = svm_setreg(svm_sc, vcpu, dst, new_dst_val); | |||||
KASSERT(error == 0, | |||||
("%s: error %d updating reg %d\r\n", __func__, error, dst)); | |||||
return error; | |||||
} | |||||
static int | |||||
svm_vmexit(struct svm_softc *svm_sc, int vcpu, struct vm_exit *vmexit) | svm_vmexit(struct svm_softc *svm_sc, int vcpu, struct vm_exit *vmexit) | ||||
{ | { | ||||
struct vmcb *vmcb; | struct vmcb *vmcb; | ||||
struct vmcb_state *state; | struct vmcb_state *state; | ||||
struct vmcb_ctrl *ctrl; | struct vmcb_ctrl *ctrl; | ||||
struct svm_regctx *ctx; | struct svm_regctx *ctx; | ||||
uint64_t code, info1, info2, val; | uint64_t code, info1, info2, val; | ||||
uint32_t eax, ecx, edx; | uint32_t eax, ecx, edx; | ||||
▲ Show 20 Lines • Show All 51 Lines • ▼ Show 20 Lines | case VMCB_EXIT_VINTR: /* interrupt window exiting */ | ||||
break; | break; | ||||
case VMCB_EXIT_INTR: /* external interrupt */ | case VMCB_EXIT_INTR: /* external interrupt */ | ||||
vmm_stat_incr(svm_sc->vm, vcpu, VMEXIT_EXTINT, 1); | vmm_stat_incr(svm_sc->vm, vcpu, VMEXIT_EXTINT, 1); | ||||
handled = 1; | handled = 1; | ||||
break; | break; | ||||
case VMCB_EXIT_NMI: /* external NMI */ | case VMCB_EXIT_NMI: /* external NMI */ | ||||
handled = 1; | handled = 1; | ||||
break; | break; | ||||
case 0x20 ... 0x23: /* DR{0-3,7} read */ | |||||
case 0x27: | |||||
case 0x30 ... 0x33: /* DR{0-3,7} write */ | |||||
case 0x37: | |||||
error = emulate_mov_dr(svm_sc, vmexit, vcpu, code, info1); | |||||
KASSERT(error == 0, | |||||
("%s: error %d emulating MOV DR", __func__, error)); | |||||
handled = 0; | |||||
break; | |||||
case 0x40 ... 0x5F: | case 0x40 ... 0x5F: | ||||
vmm_stat_incr(svm_sc->vm, vcpu, VMEXIT_EXCEPTION, 1); | vmm_stat_incr(svm_sc->vm, vcpu, VMEXIT_EXCEPTION, 1); | ||||
reflect = 1; | reflect = 1; | ||||
handled = 1; | |||||
idtvec = code - 0x40; | idtvec = code - 0x40; | ||||
switch (idtvec) { | switch (idtvec) { | ||||
case IDT_MC: | case IDT_MC: | ||||
/* | /* | ||||
* Call the machine check handler by hand. Also don't | * Call the machine check handler by hand. Also don't | ||||
* reflect the machine check back into the guest. | * reflect the machine check back into the guest. | ||||
*/ | */ | ||||
reflect = 0; | reflect = 0; | ||||
VCPU_CTR0(svm_sc->vm, vcpu, "Vectoring to MCE handler"); | VCPU_CTR0(svm_sc->vm, vcpu, "Vectoring to MCE handler"); | ||||
__asm __volatile("int $18"); | __asm __volatile("int $18"); | ||||
handled = 1; | |||||
break; | break; | ||||
case IDT_PF: | case IDT_PF: | ||||
error = svm_setreg(svm_sc, vcpu, VM_REG_GUEST_CR2, | error = svm_setreg(svm_sc, vcpu, VM_REG_GUEST_CR2, | ||||
info2); | info2); | ||||
KASSERT(error == 0, ("%s: error %d updating cr2", | KASSERT(error == 0, ("%s: error %d updating cr2", | ||||
__func__, error)); | __func__, error)); | ||||
/* fallthru */ | /* fallthru */ | ||||
case IDT_NP: | case IDT_NP: | ||||
case IDT_SS: | case IDT_SS: | ||||
case IDT_GP: | case IDT_GP: | ||||
case IDT_AC: | case IDT_AC: | ||||
case IDT_TS: | case IDT_TS: | ||||
errcode_valid = 1; | errcode_valid = 1; | ||||
break; | break; | ||||
case IDT_DF: | case IDT_DF: | ||||
errcode_valid = 1; | errcode_valid = 1; | ||||
info1 = 0; | info1 = 0; | ||||
break; | break; | ||||
case IDT_DB: { | |||||
/* | |||||
* Check if we are being stepped (RFLAGS.TF) | |||||
* or if a gdb-related watchpoint has been triggered | |||||
* and bounce vmexit to userland. | |||||
*/ | |||||
struct svm_vcpu *s_vcpu = svm_get_vcpu(svm_sc, vcpu); | |||||
uint64_t dr6 = 0; | |||||
bool stepped = 0; | |||||
uint64_t watch_mask = 0; | |||||
errcode_valid = 0; | |||||
info1 = 0; | |||||
vmcb_read(svm_sc, vcpu, VM_REG_GUEST_DR6, &dr6); | |||||
stepped = !!(dr6 & DBREG_DR6_BS); | |||||
watch_mask = (dr6 & DBREG_DR6_BMASK); | |||||
if (stepped && | |||||
(s_vcpu->caps & (1 << VM_CAP_RFLAGS_SSTEP))) { | |||||
vmexit->exitcode = VM_EXITCODE_DB; | |||||
vmexit->u.dbg.trace_trap = 1; | |||||
vmexit->u.dbg.pushf_intercept = 0; | |||||
vmexit->u.dbg.drx_access = -1; | |||||
vmexit->u.dbg.gpr = -1; | |||||
vmexit->u.dbg.watchpoints = 0; | |||||
if (s_vcpu->db_info.popf_next) { | |||||
/* DB exit was caused by stepping over | |||||
* popf */ | |||||
uint64_t rflags; | |||||
s_vcpu->db_info.popf_next = 0; | |||||
/* | |||||
* Update shadowed TF bit so the next | |||||
* setcap(..., RFLAGS_SSTEP, 0) restores | |||||
* the correct value | |||||
*/ | |||||
vmcb_read(svm_sc, vcpu, | |||||
VM_REG_GUEST_RFLAGS, &rflags); | |||||
s_vcpu->db_info.shadow_rflags_tf = | |||||
rflags & PSL_T; | |||||
} else if (s_vcpu->db_info.pushf_next) { | |||||
/* DB exit was caused by stepping over | |||||
* pushf */ | |||||
/* | |||||
* Adjusting the pushed rflags after a | |||||
* restarted pushf instruction must be | |||||
* handled outside of svm.c due to the | |||||
* critical_enter() lock being held. | |||||
*/ | |||||
vmexit->u.dbg.pushf_intercept = 1; | |||||
vmexit->u.dbg.tf_shadow_val = | |||||
s_vcpu->db_info.shadow_rflags_tf; | |||||
svm_paging_info( | |||||
svm_get_vmcb(svm_sc, vcpu), | |||||
&vmexit->u.dbg.paging); | |||||
s_vcpu->db_info.pushf_next = 0; | |||||
} | |||||
reflect = 0; | |||||
handled = 0; | |||||
} else if ((watch_mask != 0) && | |||||
(s_vcpu->caps & (1 << VM_CAP_DB_EXIT))) { | |||||
/* A hw watchpoint was triggered - bounce to | |||||
* userland */ | |||||
vmexit->exitcode = VM_EXITCODE_DB; | |||||
vmexit->u.dbg.trace_trap = 0; | |||||
vmexit->u.dbg.pushf_intercept = 0; | |||||
vmexit->u.dbg.drx_access = -1; | |||||
vmexit->u.dbg.gpr = -1; | |||||
vmexit->u.dbg.watchpoints = (int)watch_mask; | |||||
dr6 &= ~DBREG_DR6_BS; | |||||
error = vmcb_write( | |||||
svm_sc, vcpu, VM_REG_GUEST_DR6, dr6); | |||||
KASSERT(error == 0, | |||||
("%s: error %d updating DR6\r\n", __func__, | |||||
error)); | |||||
reflect = 0; | |||||
handled = 0; | |||||
} | |||||
break; | |||||
} | |||||
case IDT_BP: | case IDT_BP: | ||||
if (svm_get_intercept(svm_sc, vcpu, VMCB_EXC_INTCPT, | |||||
BIT(IDT_BP)) == 1) { | |||||
vmexit->exitcode = VM_EXITCODE_BPT; | |||||
vmexit->u.bpt.inst_length = vmexit->inst_length; | |||||
vmexit->inst_length = 0; | |||||
reflect = 0; | |||||
handled = 0; | |||||
break; | |||||
} | |||||
case IDT_OF: | case IDT_OF: | ||||
case IDT_BR: | case IDT_BR: | ||||
/* | /* | ||||
* The 'nrip' field is populated for INT3, INTO and | * The 'nrip' field is populated for INT3, INTO and | ||||
* BOUND exceptions and this also implies that | * BOUND exceptions and this also implies that | ||||
* 'inst_length' is non-zero. | * 'inst_length' is non-zero. | ||||
* | * | ||||
* Reset 'inst_length' to zero so the guest %rip at | * Reset 'inst_length' to zero so the guest %rip at | ||||
* event injection is identical to what it was when | * event injection is identical to what it was when | ||||
* the exception originally happened. | * the exception originally happened. | ||||
*/ | */ | ||||
VCPU_CTR2(svm_sc->vm, vcpu, "Reset inst_length from %d " | VCPU_CTR2(svm_sc->vm, vcpu, "Reset inst_length from %d " | ||||
"to zero before injecting exception %d", | "to zero before injecting exception %d", | ||||
vmexit->inst_length, idtvec); | vmexit->inst_length, idtvec); | ||||
vmexit->inst_length = 0; | vmexit->inst_length = 0; | ||||
/* fallthru */ | /* fallthru */ | ||||
default: | default: | ||||
errcode_valid = 0; | errcode_valid = 0; | ||||
info1 = 0; | info1 = 0; | ||||
break; | break; | ||||
} | } | ||||
KASSERT(vmexit->inst_length == 0, ("invalid inst_length (%d) " | |||||
if (reflect) { | |||||
KASSERT(vmexit->inst_length == 0, | |||||
("invalid inst_length (%d) " | |||||
"when reflecting exception %d into guest", | "when reflecting exception %d into guest", | ||||
vmexit->inst_length, idtvec)); | vmexit->inst_length, idtvec)); | ||||
if (reflect) { | |||||
/* Reflect the exception back into the guest */ | /* Reflect the exception back into the guest */ | ||||
VCPU_CTR2(svm_sc->vm, vcpu, "Reflecting exception " | VCPU_CTR2(svm_sc->vm, vcpu, "Reflecting exception " | ||||
"%d/%#x into the guest", idtvec, (int)info1); | "%d/%#x into the guest", idtvec, (int)info1); | ||||
error = vm_inject_exception(svm_sc->vm, vcpu, idtvec, | error = vm_inject_exception(svm_sc->vm, vcpu, idtvec, | ||||
errcode_valid, info1, 0); | errcode_valid, info1, 0); | ||||
KASSERT(error == 0, ("%s: vm_inject_exception error %d", | KASSERT(error == 0, ("%s: vm_inject_exception error %d", | ||||
__func__, error)); | __func__, error)); | ||||
} | } | ||||
handled = 1; | |||||
break; | break; | ||||
case VMCB_EXIT_MSR: /* MSR access. */ | case VMCB_EXIT_MSR: /* MSR access. */ | ||||
eax = state->rax; | eax = state->rax; | ||||
ecx = ctx->sctx_rcx; | ecx = ctx->sctx_rcx; | ||||
edx = ctx->sctx_rdx; | edx = ctx->sctx_rdx; | ||||
retu = false; | retu = false; | ||||
if (info1) { | if (info1) { | ||||
vmm_stat_incr(svm_sc->vm, vcpu, VMEXIT_WRMSR, 1); | vmm_stat_incr(svm_sc->vm, vcpu, VMEXIT_WRMSR, 1); | ||||
val = (uint64_t)edx << 32 | eax; | val = (uint64_t)edx << 32 | eax; | ||||
VCPU_CTR2(svm_sc->vm, vcpu, "wrmsr %#x val %#lx", | VCPU_CTR2(svm_sc->vm, vcpu, "wrmsr %#x val %#lx", | ||||
ecx, val); | ecx, val); | ||||
if (emulate_wrmsr(svm_sc, vcpu, ecx, val, &retu)) { | if (emulate_wrmsr(svm_sc, vcpu, ecx, val, &retu)) { | ||||
vmexit->exitcode = VM_EXITCODE_WRMSR; | vmexit->exitcode = VM_EXITCODE_WRMSR; | ||||
▲ Show 20 Lines • Show All 60 Lines • ▼ Show 20 Lines | case VMCB_EXIT_NPF: | ||||
} | } | ||||
break; | break; | ||||
case VMCB_EXIT_MONITOR: | case VMCB_EXIT_MONITOR: | ||||
vmexit->exitcode = VM_EXITCODE_MONITOR; | vmexit->exitcode = VM_EXITCODE_MONITOR; | ||||
break; | break; | ||||
case VMCB_EXIT_MWAIT: | case VMCB_EXIT_MWAIT: | ||||
vmexit->exitcode = VM_EXITCODE_MWAIT; | vmexit->exitcode = VM_EXITCODE_MWAIT; | ||||
break; | break; | ||||
case VMCB_EXIT_PUSHF: { | |||||
uint64_t rflags; | |||||
struct svm_vcpu *s_vcpu = svm_get_vcpu(svm_sc, vcpu); | |||||
svm_getreg(svm_sc, vcpu, VM_REG_GUEST_RFLAGS, &rflags); | |||||
/* Update shadow TF to guard against unrelated intercepts */ | |||||
s_vcpu->db_info.shadow_rflags_tf = rflags & PSL_T; | |||||
/* Restart this instruction */ | |||||
vmexit->rip -= vmexit->inst_length; | |||||
/* Disable PUSHF intercepts - avoid a loop*/ | |||||
svm_set_intercept( | |||||
svm_sc, vcpu, VMCB_CTRL1_INTCPT, VMCB_INTCPT_PUSHF, 0); | |||||
/* Trace restarted instruction */ | |||||
vmcb_write(svm_sc, vcpu, VM_REG_GUEST_RFLAGS, (rflags | PSL_T)); | |||||
s_vcpu->db_info.pushf_next = 1; | |||||
handled = 1; | |||||
break; | |||||
} | |||||
case VMCB_EXIT_POPF: { | |||||
uint64_t rflags; | |||||
svm_getreg(svm_sc, vcpu, VM_REG_GUEST_RFLAGS, &rflags); | |||||
/* Restart this instruction */ | |||||
vmexit->rip -= vmexit->inst_length; | |||||
/* Disable POPF intercepts - avoid a loop*/ | |||||
svm_set_intercept( | |||||
svm_sc, vcpu, VMCB_CTRL1_INTCPT, VMCB_INTCPT_POPF, 0); | |||||
/* Trace restarted instruction */ | |||||
vmcb_write(svm_sc, vcpu, VM_REG_GUEST_RFLAGS, (rflags | PSL_T)); | |||||
svm_get_vcpu(svm_sc, vcpu)->db_info.popf_next = 1; | |||||
handled = 1; | |||||
break; | |||||
} | |||||
case VMCB_EXIT_SHUTDOWN: | case VMCB_EXIT_SHUTDOWN: | ||||
case VMCB_EXIT_VMRUN: | case VMCB_EXIT_VMRUN: | ||||
case VMCB_EXIT_VMMCALL: | case VMCB_EXIT_VMMCALL: | ||||
case VMCB_EXIT_VMLOAD: | case VMCB_EXIT_VMLOAD: | ||||
case VMCB_EXIT_VMSAVE: | case VMCB_EXIT_VMSAVE: | ||||
case VMCB_EXIT_STGI: | case VMCB_EXIT_STGI: | ||||
case VMCB_EXIT_CLGI: | case VMCB_EXIT_CLGI: | ||||
case VMCB_EXIT_SKINIT: | case VMCB_EXIT_SKINIT: | ||||
▲ Show 20 Lines • Show All 771 Lines • ▼ Show 20 Lines | case VM_CAP_PAUSE_EXIT: | ||||
svm_set_intercept(sc, vcpu, VMCB_CTRL1_INTCPT, | svm_set_intercept(sc, vcpu, VMCB_CTRL1_INTCPT, | ||||
VMCB_INTCPT_PAUSE, val); | VMCB_INTCPT_PAUSE, val); | ||||
break; | break; | ||||
case VM_CAP_UNRESTRICTED_GUEST: | case VM_CAP_UNRESTRICTED_GUEST: | ||||
/* Unrestricted guest execution cannot be disabled in SVM */ | /* Unrestricted guest execution cannot be disabled in SVM */ | ||||
if (val == 0) | if (val == 0) | ||||
error = EINVAL; | error = EINVAL; | ||||
break; | break; | ||||
case VM_CAP_BPT_EXIT: | |||||
svm_set_intercept(sc, vcpu, VMCB_EXC_INTCPT, BIT(IDT_BP), val); | |||||
break; | |||||
case VM_CAP_RFLAGS_SSTEP: { | |||||
uint64_t rflags; | |||||
int db_inctpt_val = val; | |||||
struct svm_vcpu *s_vcpu; | |||||
if (svm_getreg(sc, vcpu, VM_REG_GUEST_RFLAGS, &rflags)) { | |||||
error = (EINVAL); | |||||
break; | |||||
} | |||||
s_vcpu = svm_get_vcpu(sc, vcpu); | |||||
if (val) { | |||||
/* Save current TF bit */ | |||||
s_vcpu->db_info.shadow_rflags_tf = rflags & PSL_T; | |||||
/* Trace next instruction */ | |||||
if (vmcb_write(sc, vcpu, VM_REG_GUEST_RFLAGS, | |||||
(rflags | PSL_T))) { | |||||
error = (EINVAL); | |||||
break; | |||||
} | |||||
s_vcpu->caps |= (1 << VM_CAP_RFLAGS_SSTEP); | |||||
} else { | |||||
/* | |||||
* Restore shadowed RFLAGS.TF only if vCPU was being | |||||
* stepped | |||||
*/ | |||||
if (s_vcpu->caps & (1 << VM_CAP_RFLAGS_SSTEP)) { | |||||
rflags |= s_vcpu->db_info.shadow_rflags_tf; | |||||
s_vcpu->db_info.shadow_rflags_tf = 0; | |||||
if (vmcb_write(sc, vcpu, VM_REG_GUEST_RFLAGS, | |||||
rflags)) { | |||||
error = (EINVAL); | |||||
break; | |||||
} | |||||
s_vcpu->caps &= ~(1 << VM_CAP_RFLAGS_SSTEP); | |||||
} | |||||
/* Dont disable intercept if VM_CAP_DB_EXIT is active */ | |||||
db_inctpt_val = (s_vcpu->caps & (1 << VM_CAP_DB_EXIT)); | |||||
} | |||||
svm_set_intercept( | |||||
sc, vcpu, VMCB_EXC_INTCPT, BIT(IDT_DB), db_inctpt_val); | |||||
svm_set_intercept( | |||||
sc, vcpu, VMCB_CTRL1_INTCPT, VMCB_INTCPT_POPF, val); | |||||
svm_set_intercept( | |||||
sc, vcpu, VMCB_CTRL1_INTCPT, VMCB_INTCPT_PUSHF, val); | |||||
break; | |||||
} | |||||
case VM_CAP_DB_EXIT: { | |||||
struct svm_vcpu *s_vcpu = svm_get_vcpu(sc, vcpu); | |||||
if (val) { | |||||
/* Require decode assist support for now */ | |||||
if (!decode_assist()) { | |||||
error = (ENOTSUP); | |||||
break; | |||||
} | |||||
s_vcpu->caps |= (1 << VM_CAP_DB_EXIT); | |||||
} else { | |||||
s_vcpu->caps &= ~(1 << VM_CAP_DB_EXIT); | |||||
/* Dont disable intercept if VM_CAP_RFLAGS_SSTEP is | |||||
* active */ | |||||
val = (s_vcpu->caps & (1 << VM_CAP_RFLAGS_SSTEP)); | |||||
} | |||||
svm_set_intercept(sc, vcpu, VMCB_EXC_INTCPT, BIT(IDT_DB), val); | |||||
break; | |||||
} | |||||
case VM_CAP_DR_MOV_EXIT: { | |||||
struct svm_vcpu *s_vcpu = svm_get_vcpu(sc, vcpu); | |||||
if (val) { | |||||
s_vcpu->caps |= (1 << VM_CAP_DR_MOV_EXIT); | |||||
} else { | |||||
s_vcpu->caps &= ~(1 << VM_CAP_DR_MOV_EXIT); | |||||
} | |||||
/* Intercept DR0-3,7 writes */ | |||||
svm_set_intercept( | |||||
sc, vcpu, VMCB_DR_INTCPT, VMCB_INTCPT_DR_WRITE(0), val); | |||||
svm_set_intercept( | |||||
sc, vcpu, VMCB_DR_INTCPT, VMCB_INTCPT_DR_WRITE(1), val); | |||||
svm_set_intercept( | |||||
sc, vcpu, VMCB_DR_INTCPT, VMCB_INTCPT_DR_WRITE(2), val); | |||||
svm_set_intercept( | |||||
sc, vcpu, VMCB_DR_INTCPT, VMCB_INTCPT_DR_WRITE(3), val); | |||||
svm_set_intercept( | |||||
sc, vcpu, VMCB_DR_INTCPT, VMCB_INTCPT_DR_WRITE(7), val); | |||||
/* Intercept DR0-3,7 reads */ | |||||
svm_set_intercept( | |||||
sc, vcpu, VMCB_DR_INTCPT, VMCB_INTCPT_DR_READ(0), val); | |||||
svm_set_intercept( | |||||
sc, vcpu, VMCB_DR_INTCPT, VMCB_INTCPT_DR_READ(1), val); | |||||
svm_set_intercept( | |||||
sc, vcpu, VMCB_DR_INTCPT, VMCB_INTCPT_DR_READ(2), val); | |||||
svm_set_intercept( | |||||
sc, vcpu, VMCB_DR_INTCPT, VMCB_INTCPT_DR_READ(3), val); | |||||
svm_set_intercept( | |||||
sc, vcpu, VMCB_DR_INTCPT, VMCB_INTCPT_DR_READ(7), val); | |||||
break; | |||||
} | |||||
default: | default: | ||||
error = ENOENT; | error = ENOENT; | ||||
break; | break; | ||||
} | } | ||||
return (error); | return (error); | ||||
} | } | ||||
static int | static int | ||||
Show All 11 Lines | *retval = svm_get_intercept(sc, vcpu, VMCB_CTRL1_INTCPT, | ||||
VMCB_INTCPT_HLT); | VMCB_INTCPT_HLT); | ||||
break; | break; | ||||
case VM_CAP_PAUSE_EXIT: | case VM_CAP_PAUSE_EXIT: | ||||
*retval = svm_get_intercept(sc, vcpu, VMCB_CTRL1_INTCPT, | *retval = svm_get_intercept(sc, vcpu, VMCB_CTRL1_INTCPT, | ||||
VMCB_INTCPT_PAUSE); | VMCB_INTCPT_PAUSE); | ||||
break; | break; | ||||
case VM_CAP_UNRESTRICTED_GUEST: | case VM_CAP_UNRESTRICTED_GUEST: | ||||
*retval = 1; /* unrestricted guest is always enabled */ | *retval = 1; /* unrestricted guest is always enabled */ | ||||
break; | |||||
case VM_CAP_DB_EXIT: | |||||
*retval = !!( | |||||
svm_get_vcpu(sc, vcpu)->caps & (1 << VM_CAP_DB_EXIT)); | |||||
break; | |||||
case VM_CAP_BPT_EXIT: | |||||
*retval = svm_get_intercept( | |||||
sc, vcpu, VMCB_EXC_INTCPT, BIT(IDT_BP)); | |||||
break; | |||||
case VM_CAP_RFLAGS_SSTEP: | |||||
*retval = !!( | |||||
svm_get_vcpu(sc, vcpu)->caps & (1 << VM_CAP_RFLAGS_SSTEP)); | |||||
break; | |||||
case VM_CAP_DR_MOV_EXIT: | |||||
*retval = !!( | |||||
svm_get_vcpu(sc, vcpu)->caps & (1 << VM_CAP_DR_MOV_EXIT)); | |||||
break; | break; | ||||
default: | default: | ||||
error = ENOENT; | error = ENOENT; | ||||
break; | break; | ||||
} | } | ||||
return (error); | return (error); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 358 Lines • Show Last 20 Lines |