Changeset View
Changeset View
Standalone View
Standalone View
sys/amd64/vmm/intel/vmx.c
Show First 20 Lines • Show All 1,137 Lines • ▼ Show 20 Lines | vmx_vcpu_init(void *vmi, struct vcpu *vcpu1, int vcpuid) | ||||
KASSERT(error == 0, ("vmcs_init error %d", error)); | KASSERT(error == 0, ("vmcs_init error %d", error)); | ||||
VMPTRLD(vmcs); | VMPTRLD(vmcs); | ||||
error = 0; | error = 0; | ||||
error += vmwrite(VMCS_HOST_RSP, (u_long)&vcpu->ctx); | error += vmwrite(VMCS_HOST_RSP, (u_long)&vcpu->ctx); | ||||
error += vmwrite(VMCS_EPTP, vmx->eptp); | error += vmwrite(VMCS_EPTP, vmx->eptp); | ||||
error += vmwrite(VMCS_PIN_BASED_CTLS, pinbased_ctls); | error += vmwrite(VMCS_PIN_BASED_CTLS, pinbased_ctls); | ||||
error += vmwrite(VMCS_PRI_PROC_BASED_CTLS, procbased_ctls); | error += vmwrite(VMCS_PRI_PROC_BASED_CTLS, procbased_ctls); | ||||
if (vcpu_trap_wbinvd(vmx->vm, vcpuid)) { | if (vcpu_trap_wbinvd(vcpu->vcpu)) { | ||||
KASSERT(cap_wbinvd_exit, ("WBINVD trap not available")); | KASSERT(cap_wbinvd_exit, ("WBINVD trap not available")); | ||||
procbased_ctls2 |= PROCBASED2_WBINVD_EXITING; | procbased_ctls2 |= PROCBASED2_WBINVD_EXITING; | ||||
} | } | ||||
error += vmwrite(VMCS_SEC_PROC_BASED_CTLS, procbased_ctls2); | error += vmwrite(VMCS_SEC_PROC_BASED_CTLS, procbased_ctls2); | ||||
error += vmwrite(VMCS_EXIT_CTLS, exit_ctls); | error += vmwrite(VMCS_EXIT_CTLS, exit_ctls); | ||||
error += vmwrite(VMCS_ENTRY_CTLS, entry_ctls); | error += vmwrite(VMCS_ENTRY_CTLS, entry_ctls); | ||||
error += vmwrite(VMCS_MSR_BITMAP, vtophys(vmx->msr_bitmap)); | error += vmwrite(VMCS_MSR_BITMAP, vtophys(vmx->msr_bitmap)); | ||||
error += vmwrite(VMCS_VPID, vmx->vpids[vcpuid]); | error += vmwrite(VMCS_VPID, vmx->vpids[vcpuid]); | ||||
if (guest_l1d_flush && !guest_l1d_flush_sw) { | if (guest_l1d_flush && !guest_l1d_flush_sw) { | ||||
vmcs_write(VMCS_ENTRY_MSR_LOAD, pmap_kextract( | vmcs_write(VMCS_ENTRY_MSR_LOAD, pmap_kextract( | ||||
(vm_offset_t)&msr_load_list[0])); | (vm_offset_t)&msr_load_list[0])); | ||||
vmcs_write(VMCS_ENTRY_MSR_LOAD_COUNT, | vmcs_write(VMCS_ENTRY_MSR_LOAD_COUNT, | ||||
nitems(msr_load_list)); | nitems(msr_load_list)); | ||||
vmcs_write(VMCS_EXIT_MSR_STORE, 0); | vmcs_write(VMCS_EXIT_MSR_STORE, 0); | ||||
vmcs_write(VMCS_EXIT_MSR_STORE_COUNT, 0); | vmcs_write(VMCS_EXIT_MSR_STORE_COUNT, 0); | ||||
} | } | ||||
/* exception bitmap */ | /* exception bitmap */ | ||||
if (vcpu_trace_exceptions(vmx->vm, vcpuid)) | if (vcpu_trace_exceptions(vcpu->vcpu)) | ||||
exc_bitmap = 0xffffffff; | exc_bitmap = 0xffffffff; | ||||
else | else | ||||
exc_bitmap = 1 << IDT_MC; | exc_bitmap = 1 << IDT_MC; | ||||
error += vmwrite(VMCS_EXCEPTION_BITMAP, exc_bitmap); | error += vmwrite(VMCS_EXCEPTION_BITMAP, exc_bitmap); | ||||
vcpu->ctx.guest_dr6 = DBREG_DR6_RESERVED1; | vcpu->ctx.guest_dr6 = DBREG_DR6_RESERVED1; | ||||
error += vmwrite(VMCS_GUEST_DR7, DBREG_DR7_RESERVED1); | error += vmwrite(VMCS_GUEST_DR7, DBREG_DR7_RESERVED1); | ||||
▲ Show 20 Lines • Show All 41 Lines • ▼ Show 20 Lines | if (error != 0) | ||||
panic("vmx_setup_cr4_shadow %d", error); | panic("vmx_setup_cr4_shadow %d", error); | ||||
vcpu->ctx.pmap = vmx->pmap; | vcpu->ctx.pmap = vmx->pmap; | ||||
return (vcpu); | return (vcpu); | ||||
} | } | ||||
static int | static int | ||||
vmx_handle_cpuid(struct vm *vm, int vcpu, struct vmxctx *vmxctx) | vmx_handle_cpuid(struct vmx_vcpu *vcpu, struct vmxctx *vmxctx) | ||||
{ | { | ||||
int handled; | int handled; | ||||
handled = x86_emulate_cpuid(vm, vcpu, (uint64_t *)&vmxctx->guest_rax, | handled = x86_emulate_cpuid(vcpu->vcpu, (uint64_t *)&vmxctx->guest_rax, | ||||
(uint64_t *)&vmxctx->guest_rbx, (uint64_t *)&vmxctx->guest_rcx, | (uint64_t *)&vmxctx->guest_rbx, (uint64_t *)&vmxctx->guest_rcx, | ||||
(uint64_t *)&vmxctx->guest_rdx); | (uint64_t *)&vmxctx->guest_rdx); | ||||
return (handled); | return (handled); | ||||
} | } | ||||
static __inline void | static __inline void | ||||
vmx_run_trace(struct vmx_vcpu *vcpu) | vmx_run_trace(struct vmx_vcpu *vcpu) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 148 Lines • ▼ Show 20 Lines | vmx_clear_nmi_window_exiting(struct vmx_vcpu *vcpu) | ||||
KASSERT((vcpu->cap.proc_ctls & PROCBASED_NMI_WINDOW_EXITING) != 0, | KASSERT((vcpu->cap.proc_ctls & PROCBASED_NMI_WINDOW_EXITING) != 0, | ||||
("nmi_window_exiting not set %#x", vcpu->cap.proc_ctls)); | ("nmi_window_exiting not set %#x", vcpu->cap.proc_ctls)); | ||||
vcpu->cap.proc_ctls &= ~PROCBASED_NMI_WINDOW_EXITING; | vcpu->cap.proc_ctls &= ~PROCBASED_NMI_WINDOW_EXITING; | ||||
vmcs_write(VMCS_PRI_PROC_BASED_CTLS, vcpu->cap.proc_ctls); | vmcs_write(VMCS_PRI_PROC_BASED_CTLS, vcpu->cap.proc_ctls); | ||||
VMX_CTR0(vcpu, "Disabling NMI window exiting"); | VMX_CTR0(vcpu, "Disabling NMI window exiting"); | ||||
} | } | ||||
int | int | ||||
vmx_set_tsc_offset(struct vmx *vmx, struct vmx_vcpu *vcpu, uint64_t offset) | vmx_set_tsc_offset(struct vmx_vcpu *vcpu, uint64_t offset) | ||||
{ | { | ||||
int error; | int error; | ||||
if ((vcpu->cap.proc_ctls & PROCBASED_TSC_OFFSET) == 0) { | if ((vcpu->cap.proc_ctls & PROCBASED_TSC_OFFSET) == 0) { | ||||
vcpu->cap.proc_ctls |= PROCBASED_TSC_OFFSET; | vcpu->cap.proc_ctls |= PROCBASED_TSC_OFFSET; | ||||
vmcs_write(VMCS_PRI_PROC_BASED_CTLS, vcpu->cap.proc_ctls); | vmcs_write(VMCS_PRI_PROC_BASED_CTLS, vcpu->cap.proc_ctls); | ||||
VMX_CTR0(vcpu, "Enabling TSC offsetting"); | VMX_CTR0(vcpu, "Enabling TSC offsetting"); | ||||
} | } | ||||
error = vmwrite(VMCS_TSC_OFFSET, offset); | error = vmwrite(VMCS_TSC_OFFSET, offset); | ||||
#ifdef BHYVE_SNAPSHOT | #ifdef BHYVE_SNAPSHOT | ||||
if (error == 0) | if (error == 0) | ||||
error = vm_set_tsc_offset(vmx->vm, vcpu->vcpuid, offset); | vm_set_tsc_offset(vcpu->vcpu, offset); | ||||
#endif | #endif | ||||
return (error); | return (error); | ||||
} | } | ||||
#define NMI_BLOCKING (VMCS_INTERRUPTIBILITY_NMI_BLOCKING | \ | #define NMI_BLOCKING (VMCS_INTERRUPTIBILITY_NMI_BLOCKING | \ | ||||
VMCS_INTERRUPTIBILITY_MOVSS_BLOCKING) | VMCS_INTERRUPTIBILITY_MOVSS_BLOCKING) | ||||
#define HWINTR_BLOCKING (VMCS_INTERRUPTIBILITY_STI_BLOCKING | \ | #define HWINTR_BLOCKING (VMCS_INTERRUPTIBILITY_STI_BLOCKING | \ | ||||
VMCS_INTERRUPTIBILITY_MOVSS_BLOCKING) | VMCS_INTERRUPTIBILITY_MOVSS_BLOCKING) | ||||
static void | static void | ||||
vmx_inject_nmi(struct vmx *vmx, struct vmx_vcpu *vcpu) | vmx_inject_nmi(struct vmx_vcpu *vcpu) | ||||
{ | { | ||||
uint32_t gi __diagused, info; | uint32_t gi __diagused, info; | ||||
gi = vmcs_read(VMCS_GUEST_INTERRUPTIBILITY); | gi = vmcs_read(VMCS_GUEST_INTERRUPTIBILITY); | ||||
KASSERT((gi & NMI_BLOCKING) == 0, ("vmx_inject_nmi: invalid guest " | KASSERT((gi & NMI_BLOCKING) == 0, ("vmx_inject_nmi: invalid guest " | ||||
"interruptibility-state %#x", gi)); | "interruptibility-state %#x", gi)); | ||||
info = vmcs_read(VMCS_ENTRY_INTR_INFO); | info = vmcs_read(VMCS_ENTRY_INTR_INFO); | ||||
KASSERT((info & VMCS_INTR_VALID) == 0, ("vmx_inject_nmi: invalid " | KASSERT((info & VMCS_INTR_VALID) == 0, ("vmx_inject_nmi: invalid " | ||||
"VM-entry interruption information %#x", info)); | "VM-entry interruption information %#x", info)); | ||||
/* | /* | ||||
* Inject the virtual NMI. The vector must be the NMI IDT entry | * Inject the virtual NMI. The vector must be the NMI IDT entry | ||||
* or the VMCS entry check will fail. | * or the VMCS entry check will fail. | ||||
*/ | */ | ||||
info = IDT_NMI | VMCS_INTR_T_NMI | VMCS_INTR_VALID; | info = IDT_NMI | VMCS_INTR_T_NMI | VMCS_INTR_VALID; | ||||
vmcs_write(VMCS_ENTRY_INTR_INFO, info); | vmcs_write(VMCS_ENTRY_INTR_INFO, info); | ||||
VMX_CTR0(vcpu, "Injecting vNMI"); | VMX_CTR0(vcpu, "Injecting vNMI"); | ||||
/* Clear the request */ | /* Clear the request */ | ||||
vm_nmi_clear(vmx->vm, vcpu->vcpuid); | vm_nmi_clear(vcpu->vcpu); | ||||
} | } | ||||
static void | static void | ||||
vmx_inject_interrupts(struct vmx *vmx, struct vmx_vcpu *vcpu, | vmx_inject_interrupts(struct vmx_vcpu *vcpu, struct vlapic *vlapic, | ||||
struct vlapic *vlapic, uint64_t guestrip) | uint64_t guestrip) | ||||
{ | { | ||||
int vector, need_nmi_exiting, extint_pending; | int vector, need_nmi_exiting, extint_pending; | ||||
uint64_t rflags, entryinfo; | uint64_t rflags, entryinfo; | ||||
uint32_t gi, info; | uint32_t gi, info; | ||||
if (vcpu->state.nextrip != guestrip) { | if (vcpu->state.nextrip != guestrip) { | ||||
gi = vmcs_read(VMCS_GUEST_INTERRUPTIBILITY); | gi = vmcs_read(VMCS_GUEST_INTERRUPTIBILITY); | ||||
if (gi & HWINTR_BLOCKING) { | if (gi & HWINTR_BLOCKING) { | ||||
VMX_CTR2(vcpu, "Guest interrupt blocking " | VMX_CTR2(vcpu, "Guest interrupt blocking " | ||||
"cleared due to rip change: %#lx/%#lx", | "cleared due to rip change: %#lx/%#lx", | ||||
vcpu->state.nextrip, guestrip); | vcpu->state.nextrip, guestrip); | ||||
gi &= ~HWINTR_BLOCKING; | gi &= ~HWINTR_BLOCKING; | ||||
vmcs_write(VMCS_GUEST_INTERRUPTIBILITY, gi); | vmcs_write(VMCS_GUEST_INTERRUPTIBILITY, gi); | ||||
} | } | ||||
} | } | ||||
if (vm_entry_intinfo(vmx->vm, vcpu->vcpuid, &entryinfo)) { | if (vm_entry_intinfo(vcpu->vcpu, &entryinfo)) { | ||||
KASSERT((entryinfo & VMCS_INTR_VALID) != 0, ("%s: entry " | KASSERT((entryinfo & VMCS_INTR_VALID) != 0, ("%s: entry " | ||||
"intinfo is not valid: %#lx", __func__, entryinfo)); | "intinfo is not valid: %#lx", __func__, entryinfo)); | ||||
info = vmcs_read(VMCS_ENTRY_INTR_INFO); | info = vmcs_read(VMCS_ENTRY_INTR_INFO); | ||||
KASSERT((info & VMCS_INTR_VALID) == 0, ("%s: cannot inject " | KASSERT((info & VMCS_INTR_VALID) == 0, ("%s: cannot inject " | ||||
"pending exception: %#lx/%#x", __func__, entryinfo, info)); | "pending exception: %#lx/%#x", __func__, entryinfo, info)); | ||||
info = entryinfo; | info = entryinfo; | ||||
vector = info & 0xff; | vector = info & 0xff; | ||||
if (vector == IDT_BP || vector == IDT_OF) { | if (vector == IDT_BP || vector == IDT_OF) { | ||||
/* | /* | ||||
* VT-x requires #BP and #OF to be injected as software | * VT-x requires #BP and #OF to be injected as software | ||||
* exceptions. | * exceptions. | ||||
*/ | */ | ||||
info &= ~VMCS_INTR_T_MASK; | info &= ~VMCS_INTR_T_MASK; | ||||
info |= VMCS_INTR_T_SWEXCEPTION; | info |= VMCS_INTR_T_SWEXCEPTION; | ||||
} | } | ||||
if (info & VMCS_INTR_DEL_ERRCODE) | if (info & VMCS_INTR_DEL_ERRCODE) | ||||
vmcs_write(VMCS_ENTRY_EXCEPTION_ERROR, entryinfo >> 32); | vmcs_write(VMCS_ENTRY_EXCEPTION_ERROR, entryinfo >> 32); | ||||
vmcs_write(VMCS_ENTRY_INTR_INFO, info); | vmcs_write(VMCS_ENTRY_INTR_INFO, info); | ||||
} | } | ||||
if (vm_nmi_pending(vmx->vm, vcpu->vcpuid)) { | if (vm_nmi_pending(vcpu->vcpu)) { | ||||
/* | /* | ||||
* If there are no conditions blocking NMI injection then | * If there are no conditions blocking NMI injection then | ||||
* inject it directly here otherwise enable "NMI window | * inject it directly here otherwise enable "NMI window | ||||
* exiting" to inject it as soon as we can. | * exiting" to inject it as soon as we can. | ||||
* | * | ||||
* We also check for STI_BLOCKING because some implementations | * We also check for STI_BLOCKING because some implementations | ||||
* don't allow NMI injection in this case. If we are running | * don't allow NMI injection in this case. If we are running | ||||
* on a processor that doesn't have this restriction it will | * on a processor that doesn't have this restriction it will | ||||
* immediately exit and the NMI will be injected in the | * immediately exit and the NMI will be injected in the | ||||
* "NMI window exiting" handler. | * "NMI window exiting" handler. | ||||
*/ | */ | ||||
need_nmi_exiting = 1; | need_nmi_exiting = 1; | ||||
gi = vmcs_read(VMCS_GUEST_INTERRUPTIBILITY); | gi = vmcs_read(VMCS_GUEST_INTERRUPTIBILITY); | ||||
if ((gi & (HWINTR_BLOCKING | NMI_BLOCKING)) == 0) { | if ((gi & (HWINTR_BLOCKING | NMI_BLOCKING)) == 0) { | ||||
info = vmcs_read(VMCS_ENTRY_INTR_INFO); | info = vmcs_read(VMCS_ENTRY_INTR_INFO); | ||||
if ((info & VMCS_INTR_VALID) == 0) { | if ((info & VMCS_INTR_VALID) == 0) { | ||||
vmx_inject_nmi(vmx, vcpu); | vmx_inject_nmi(vcpu); | ||||
need_nmi_exiting = 0; | need_nmi_exiting = 0; | ||||
} else { | } else { | ||||
VMX_CTR1(vcpu, "Cannot inject NMI " | VMX_CTR1(vcpu, "Cannot inject NMI " | ||||
"due to VM-entry intr info %#x", info); | "due to VM-entry intr info %#x", info); | ||||
} | } | ||||
} else { | } else { | ||||
VMX_CTR1(vcpu, "Cannot inject NMI due to " | VMX_CTR1(vcpu, "Cannot inject NMI due to " | ||||
"Guest Interruptibility-state %#x", gi); | "Guest Interruptibility-state %#x", gi); | ||||
} | } | ||||
if (need_nmi_exiting) | if (need_nmi_exiting) | ||||
vmx_set_nmi_window_exiting(vcpu); | vmx_set_nmi_window_exiting(vcpu); | ||||
} | } | ||||
extint_pending = vm_extint_pending(vmx->vm, vcpu->vcpuid); | extint_pending = vm_extint_pending(vcpu->vcpu); | ||||
if (!extint_pending && virtual_interrupt_delivery) { | if (!extint_pending && virtual_interrupt_delivery) { | ||||
vmx_inject_pir(vlapic); | vmx_inject_pir(vlapic); | ||||
return; | return; | ||||
} | } | ||||
/* | /* | ||||
* If interrupt-window exiting is already in effect then don't bother | * If interrupt-window exiting is already in effect then don't bother | ||||
Show All 16 Lines | if (!extint_pending) { | ||||
* Hardware Interrupts": | * Hardware Interrupts": | ||||
* - maskable interrupt vectors [16,255] can be delivered | * - maskable interrupt vectors [16,255] can be delivered | ||||
* through the local APIC. | * through the local APIC. | ||||
*/ | */ | ||||
KASSERT(vector >= 16 && vector <= 255, | KASSERT(vector >= 16 && vector <= 255, | ||||
("invalid vector %d from local APIC", vector)); | ("invalid vector %d from local APIC", vector)); | ||||
} else { | } else { | ||||
/* Ask the legacy pic for a vector to inject */ | /* Ask the legacy pic for a vector to inject */ | ||||
vatpic_pending_intr(vmx->vm, &vector); | vatpic_pending_intr(vcpu->vmx->vm, &vector); | ||||
/* | /* | ||||
* From the Intel SDM, Volume 3, Section "Maskable | * From the Intel SDM, Volume 3, Section "Maskable | ||||
* Hardware Interrupts": | * Hardware Interrupts": | ||||
* - maskable interrupt vectors [0,255] can be delivered | * - maskable interrupt vectors [0,255] can be delivered | ||||
* through the INTR pin. | * through the INTR pin. | ||||
*/ | */ | ||||
KASSERT(vector >= 0 && vector <= 255, | KASSERT(vector >= 0 && vector <= 255, | ||||
Show All 33 Lines | vmx_inject_interrupts(struct vmx_vcpu *vcpu, struct vlapic *vlapic, | ||||
info = VMCS_INTR_T_HWINTR | VMCS_INTR_VALID; | info = VMCS_INTR_T_HWINTR | VMCS_INTR_VALID; | ||||
info |= vector; | info |= vector; | ||||
vmcs_write(VMCS_ENTRY_INTR_INFO, info); | vmcs_write(VMCS_ENTRY_INTR_INFO, info); | ||||
if (!extint_pending) { | if (!extint_pending) { | ||||
/* Update the Local APIC ISR */ | /* Update the Local APIC ISR */ | ||||
vlapic_intr_accepted(vlapic, vector); | vlapic_intr_accepted(vlapic, vector); | ||||
} else { | } else { | ||||
vm_extint_clear(vmx->vm, vcpu->vcpuid); | vm_extint_clear(vcpu->vcpu); | ||||
vatpic_intr_accepted(vmx->vm, vector); | vatpic_intr_accepted(vcpu->vmx->vm, vector); | ||||
/* | /* | ||||
* After we accepted the current ExtINT the PIC may | * After we accepted the current ExtINT the PIC may | ||||
* have posted another one. If that is the case, set | * have posted another one. If that is the case, set | ||||
* the Interrupt Window Exiting execution control so | * the Interrupt Window Exiting execution control so | ||||
* we can inject that one too. | * we can inject that one too. | ||||
* | * | ||||
* Also, interrupt window exiting allows us to inject any | * Also, interrupt window exiting allows us to inject any | ||||
▲ Show 20 Lines • Show All 698 Lines • ▼ Show 20 Lines | vmx_task_switch_reason(uint64_t qual) | ||||
case 3: | case 3: | ||||
return (TSR_IDT_GATE); | return (TSR_IDT_GATE); | ||||
default: | default: | ||||
panic("%s: invalid reason %d", __func__, reason); | panic("%s: invalid reason %d", __func__, reason); | ||||
} | } | ||||
} | } | ||||
static int | static int | ||||
emulate_wrmsr(struct vmx *vmx, struct vmx_vcpu *vcpu, u_int num, uint64_t val, | emulate_wrmsr(struct vmx_vcpu *vcpu, u_int num, uint64_t val, bool *retu) | ||||
bool *retu) | |||||
{ | { | ||||
int error; | int error; | ||||
if (lapic_msr(num)) | if (lapic_msr(num)) | ||||
error = lapic_wrmsr(vmx->vm, vcpu->vcpuid, num, val, retu); | error = lapic_wrmsr(vcpu->vcpu, num, val, retu); | ||||
else | else | ||||
error = vmx_wrmsr(vmx, vcpu, num, val, retu); | error = vmx_wrmsr(vcpu, num, val, retu); | ||||
return (error); | return (error); | ||||
} | } | ||||
static int | static int | ||||
emulate_rdmsr(struct vmx *vmx, struct vmx_vcpu *vcpu, u_int num, bool *retu) | emulate_rdmsr(struct vmx_vcpu *vcpu, u_int num, bool *retu) | ||||
{ | { | ||||
struct vmxctx *vmxctx; | struct vmxctx *vmxctx; | ||||
uint64_t result; | uint64_t result; | ||||
uint32_t eax, edx; | uint32_t eax, edx; | ||||
int error; | int error; | ||||
if (lapic_msr(num)) | if (lapic_msr(num)) | ||||
error = lapic_rdmsr(vmx->vm, vcpu->vcpuid, num, &result, retu); | error = lapic_rdmsr(vcpu->vcpu, num, &result, retu); | ||||
else | else | ||||
error = vmx_rdmsr(vmx, vcpu, num, &result, retu); | error = vmx_rdmsr(vcpu, num, &result, retu); | ||||
if (error == 0) { | if (error == 0) { | ||||
eax = result; | eax = result; | ||||
vmxctx = &vcpu->ctx; | vmxctx = &vcpu->ctx; | ||||
error = vmxctx_setreg(vmxctx, VM_REG_GUEST_RAX, eax); | error = vmxctx_setreg(vmxctx, VM_REG_GUEST_RAX, eax); | ||||
KASSERT(error == 0, ("vmxctx_setreg(rax) error %d", error)); | KASSERT(error == 0, ("vmxctx_setreg(rax) error %d", error)); | ||||
edx = result >> 32; | edx = result >> 32; | ||||
▲ Show 20 Lines • Show All 55 Lines • ▼ Show 20 Lines | vmx_exit_process(struct vmx *vmx, struct vmx_vcpu *vcpu, struct vm_exit *vmexit) | ||||
idtvec_info = vmcs_idt_vectoring_info(); | idtvec_info = vmcs_idt_vectoring_info(); | ||||
if (idtvec_info & VMCS_IDT_VEC_VALID) { | if (idtvec_info & VMCS_IDT_VEC_VALID) { | ||||
idtvec_info &= ~(1 << 12); /* clear undefined bit */ | idtvec_info &= ~(1 << 12); /* clear undefined bit */ | ||||
exitintinfo = idtvec_info; | exitintinfo = idtvec_info; | ||||
if (idtvec_info & VMCS_IDT_VEC_ERRCODE_VALID) { | if (idtvec_info & VMCS_IDT_VEC_ERRCODE_VALID) { | ||||
idtvec_err = vmcs_idt_vectoring_err(); | idtvec_err = vmcs_idt_vectoring_err(); | ||||
exitintinfo |= (uint64_t)idtvec_err << 32; | exitintinfo |= (uint64_t)idtvec_err << 32; | ||||
} | } | ||||
error = vm_exit_intinfo(vmx->vm, vcpuid, exitintinfo); | error = vm_exit_intinfo(vcpu->vcpu, exitintinfo); | ||||
KASSERT(error == 0, ("%s: vm_set_intinfo error %d", | KASSERT(error == 0, ("%s: vm_set_intinfo error %d", | ||||
__func__, error)); | __func__, error)); | ||||
/* | /* | ||||
* If 'virtual NMIs' are being used and the VM-exit | * If 'virtual NMIs' are being used and the VM-exit | ||||
* happened while injecting an NMI during the previous | * happened while injecting an NMI during the previous | ||||
* VM-entry, then clear "blocking by NMI" in the | * VM-entry, then clear "blocking by NMI" in the | ||||
* Guest Interruptibility-State so the NMI can be | * Guest Interruptibility-State so the NMI can be | ||||
▲ Show 20 Lines • Show All 83 Lines • ▼ Show 20 Lines | case EXIT_REASON_CR_ACCESS: | ||||
} | } | ||||
break; | break; | ||||
case EXIT_REASON_RDMSR: | case EXIT_REASON_RDMSR: | ||||
vmm_stat_incr(vcpu->vcpu, VMEXIT_RDMSR, 1); | vmm_stat_incr(vcpu->vcpu, VMEXIT_RDMSR, 1); | ||||
retu = false; | retu = false; | ||||
ecx = vmxctx->guest_rcx; | ecx = vmxctx->guest_rcx; | ||||
VMX_CTR1(vcpu, "rdmsr 0x%08x", ecx); | VMX_CTR1(vcpu, "rdmsr 0x%08x", ecx); | ||||
SDT_PROBE4(vmm, vmx, exit, rdmsr, vmx, vcpuid, vmexit, ecx); | SDT_PROBE4(vmm, vmx, exit, rdmsr, vmx, vcpuid, vmexit, ecx); | ||||
error = emulate_rdmsr(vmx, vcpu, ecx, &retu); | error = emulate_rdmsr(vcpu, ecx, &retu); | ||||
if (error) { | if (error) { | ||||
vmexit->exitcode = VM_EXITCODE_RDMSR; | vmexit->exitcode = VM_EXITCODE_RDMSR; | ||||
vmexit->u.msr.code = ecx; | vmexit->u.msr.code = ecx; | ||||
} else if (!retu) { | } else if (!retu) { | ||||
handled = HANDLED; | handled = HANDLED; | ||||
} else { | } else { | ||||
/* Return to userspace with a valid exitcode */ | /* Return to userspace with a valid exitcode */ | ||||
KASSERT(vmexit->exitcode != VM_EXITCODE_BOGUS, | KASSERT(vmexit->exitcode != VM_EXITCODE_BOGUS, | ||||
("emulate_rdmsr retu with bogus exitcode")); | ("emulate_rdmsr retu with bogus exitcode")); | ||||
} | } | ||||
break; | break; | ||||
case EXIT_REASON_WRMSR: | case EXIT_REASON_WRMSR: | ||||
vmm_stat_incr(vcpu->vcpu, VMEXIT_WRMSR, 1); | vmm_stat_incr(vcpu->vcpu, VMEXIT_WRMSR, 1); | ||||
retu = false; | retu = false; | ||||
eax = vmxctx->guest_rax; | eax = vmxctx->guest_rax; | ||||
ecx = vmxctx->guest_rcx; | ecx = vmxctx->guest_rcx; | ||||
edx = vmxctx->guest_rdx; | edx = vmxctx->guest_rdx; | ||||
VMX_CTR2(vcpu, "wrmsr 0x%08x value 0x%016lx", | VMX_CTR2(vcpu, "wrmsr 0x%08x value 0x%016lx", | ||||
ecx, (uint64_t)edx << 32 | eax); | ecx, (uint64_t)edx << 32 | eax); | ||||
SDT_PROBE5(vmm, vmx, exit, wrmsr, vmx, vmexit, vcpuid, ecx, | SDT_PROBE5(vmm, vmx, exit, wrmsr, vmx, vmexit, vcpuid, ecx, | ||||
(uint64_t)edx << 32 | eax); | (uint64_t)edx << 32 | eax); | ||||
error = emulate_wrmsr(vmx, vcpu, ecx, | error = emulate_wrmsr(vcpu, ecx, (uint64_t)edx << 32 | eax, | ||||
(uint64_t)edx << 32 | eax, &retu); | &retu); | ||||
if (error) { | if (error) { | ||||
vmexit->exitcode = VM_EXITCODE_WRMSR; | vmexit->exitcode = VM_EXITCODE_WRMSR; | ||||
vmexit->u.msr.code = ecx; | vmexit->u.msr.code = ecx; | ||||
vmexit->u.msr.wval = (uint64_t)edx << 32 | eax; | vmexit->u.msr.wval = (uint64_t)edx << 32 | eax; | ||||
} else if (!retu) { | } else if (!retu) { | ||||
handled = HANDLED; | handled = HANDLED; | ||||
} else { | } else { | ||||
/* Return to userspace with a valid exitcode */ | /* Return to userspace with a valid exitcode */ | ||||
▲ Show 20 Lines • Show All 57 Lines • ▼ Show 20 Lines | case EXIT_REASON_EXT_INTR: | ||||
* This is special. We want to treat this as an 'handled' | * This is special. We want to treat this as an 'handled' | ||||
* VM-exit but not increment the instruction pointer. | * VM-exit but not increment the instruction pointer. | ||||
*/ | */ | ||||
vmm_stat_incr(vcpu->vcpu, VMEXIT_EXTINT, 1); | vmm_stat_incr(vcpu->vcpu, VMEXIT_EXTINT, 1); | ||||
return (1); | return (1); | ||||
case EXIT_REASON_NMI_WINDOW: | case EXIT_REASON_NMI_WINDOW: | ||||
SDT_PROBE3(vmm, vmx, exit, nmiwindow, vmx, vcpuid, vmexit); | SDT_PROBE3(vmm, vmx, exit, nmiwindow, vmx, vcpuid, vmexit); | ||||
/* Exit to allow the pending virtual NMI to be injected */ | /* Exit to allow the pending virtual NMI to be injected */ | ||||
if (vm_nmi_pending(vmx->vm, vcpuid)) | if (vm_nmi_pending(vcpu->vcpu)) | ||||
vmx_inject_nmi(vmx, vcpu); | vmx_inject_nmi(vcpu); | ||||
vmx_clear_nmi_window_exiting(vcpu); | vmx_clear_nmi_window_exiting(vcpu); | ||||
vmm_stat_incr(vcpu->vcpu, VMEXIT_NMI_WINDOW, 1); | vmm_stat_incr(vcpu->vcpu, VMEXIT_NMI_WINDOW, 1); | ||||
return (1); | return (1); | ||||
case EXIT_REASON_INOUT: | case EXIT_REASON_INOUT: | ||||
vmm_stat_incr(vcpu->vcpu, VMEXIT_INOUT, 1); | vmm_stat_incr(vcpu->vcpu, VMEXIT_INOUT, 1); | ||||
vmexit->exitcode = VM_EXITCODE_INOUT; | vmexit->exitcode = VM_EXITCODE_INOUT; | ||||
vmexit->u.inout.bytes = (qual & 0x7) + 1; | vmexit->u.inout.bytes = (qual & 0x7) + 1; | ||||
vmexit->u.inout.in = in = (qual & 0x8) ? 1 : 0; | vmexit->u.inout.in = in = (qual & 0x8) ? 1 : 0; | ||||
Show All 13 Lines | if (vmexit->u.inout.string) { | ||||
vis->addrsize = inout_str_addrsize(inst_info); | vis->addrsize = inout_str_addrsize(inst_info); | ||||
inout_str_seginfo(vcpu, inst_info, in, vis); | inout_str_seginfo(vcpu, inst_info, in, vis); | ||||
} | } | ||||
SDT_PROBE3(vmm, vmx, exit, inout, vmx, vcpuid, vmexit); | SDT_PROBE3(vmm, vmx, exit, inout, vmx, vcpuid, vmexit); | ||||
break; | break; | ||||
case EXIT_REASON_CPUID: | case EXIT_REASON_CPUID: | ||||
vmm_stat_incr(vcpu->vcpu, VMEXIT_CPUID, 1); | vmm_stat_incr(vcpu->vcpu, VMEXIT_CPUID, 1); | ||||
SDT_PROBE3(vmm, vmx, exit, cpuid, vmx, vcpuid, vmexit); | SDT_PROBE3(vmm, vmx, exit, cpuid, vmx, vcpuid, vmexit); | ||||
handled = vmx_handle_cpuid(vmx->vm, vcpuid, vmxctx); | handled = vmx_handle_cpuid(vcpu, vmxctx); | ||||
break; | break; | ||||
case EXIT_REASON_EXCEPTION: | case EXIT_REASON_EXCEPTION: | ||||
vmm_stat_incr(vcpu->vcpu, VMEXIT_EXCEPTION, 1); | vmm_stat_incr(vcpu->vcpu, VMEXIT_EXCEPTION, 1); | ||||
intr_info = vmcs_read(VMCS_EXIT_INTR_INFO); | intr_info = vmcs_read(VMCS_EXIT_INTR_INFO); | ||||
KASSERT((intr_info & VMCS_INTR_VALID) != 0, | KASSERT((intr_info & VMCS_INTR_VALID) != 0, | ||||
("VM exit interruption info invalid: %#x", intr_info)); | ("VM exit interruption info invalid: %#x", intr_info)); | ||||
intr_vec = intr_info & 0xff; | intr_vec = intr_info & 0xff; | ||||
▲ Show 20 Lines • Show All 74 Lines • ▼ Show 20 Lines | vmx_exit_process(struct vmx *vmx, struct vmx_vcpu *vcpu, struct vm_exit *vmexit) | ||||
case EXIT_REASON_EPT_FAULT: | case EXIT_REASON_EPT_FAULT: | ||||
/* | /* | ||||
* If 'gpa' lies within the address space allocated to | * If 'gpa' lies within the address space allocated to | ||||
* memory then this must be a nested page fault otherwise | * memory then this must be a nested page fault otherwise | ||||
* this must be an instruction that accesses MMIO space. | * this must be an instruction that accesses MMIO space. | ||||
*/ | */ | ||||
gpa = vmcs_gpa(); | gpa = vmcs_gpa(); | ||||
if (vm_mem_allocated(vmx->vm, vcpuid, gpa) || | if (vm_mem_allocated(vcpu->vcpu, gpa) || | ||||
apic_access_fault(vcpu, gpa)) { | apic_access_fault(vcpu, gpa)) { | ||||
vmexit->exitcode = VM_EXITCODE_PAGING; | vmexit->exitcode = VM_EXITCODE_PAGING; | ||||
vmexit->inst_length = 0; | vmexit->inst_length = 0; | ||||
vmexit->u.paging.gpa = gpa; | vmexit->u.paging.gpa = gpa; | ||||
vmexit->u.paging.fault_type = ept_fault_type(qual); | vmexit->u.paging.fault_type = ept_fault_type(qual); | ||||
vmm_stat_incr(vcpu->vcpu, VMEXIT_NESTED_FAULT, 1); | vmm_stat_incr(vcpu->vcpu, VMEXIT_NESTED_FAULT, 1); | ||||
SDT_PROBE5(vmm, vmx, exit, nestedfault, | SDT_PROBE5(vmm, vmx, exit, nestedfault, | ||||
vmx, vcpuid, vmexit, gpa, qual); | vmx, vcpuid, vmexit, gpa, qual); | ||||
▲ Show 20 Lines • Show All 261 Lines • ▼ Show 20 Lines | |||||
{ | { | ||||
smr_exit(pmap->pm_eptsmr); | smr_exit(pmap->pm_eptsmr); | ||||
CPU_CLR_ATOMIC(curcpu, &pmap->pm_active); | CPU_CLR_ATOMIC(curcpu, &pmap->pm_active); | ||||
} | } | ||||
static int | static int | ||||
vmx_run(void *vcpui, register_t rip, pmap_t pmap, struct vm_eventinfo *evinfo) | vmx_run(void *vcpui, register_t rip, pmap_t pmap, struct vm_eventinfo *evinfo) | ||||
{ | { | ||||
int rc, handled, launched, vcpuid; | int rc, handled, launched; | ||||
struct vmx *vmx; | struct vmx *vmx; | ||||
struct vmx_vcpu *vcpu; | struct vmx_vcpu *vcpu; | ||||
struct vm *vm; | |||||
struct vmxctx *vmxctx; | struct vmxctx *vmxctx; | ||||
struct vmcs *vmcs; | struct vmcs *vmcs; | ||||
struct vm_exit *vmexit; | struct vm_exit *vmexit; | ||||
struct vlapic *vlapic; | struct vlapic *vlapic; | ||||
uint32_t exit_reason; | uint32_t exit_reason; | ||||
struct region_descriptor gdtr, idtr; | struct region_descriptor gdtr, idtr; | ||||
uint16_t ldt_sel; | uint16_t ldt_sel; | ||||
vcpu = vcpui; | vcpu = vcpui; | ||||
vmx = vcpu->vmx; | vmx = vcpu->vmx; | ||||
vm = vmx->vm; | |||||
vcpuid = vcpu->vcpuid; | |||||
vmcs = vcpu->vmcs; | vmcs = vcpu->vmcs; | ||||
vmxctx = &vcpu->ctx; | vmxctx = &vcpu->ctx; | ||||
vlapic = vm_lapic(vcpu->vcpu); | vlapic = vm_lapic(vcpu->vcpu); | ||||
vmexit = vm_exitinfo(vm, vcpuid); | vmexit = vm_exitinfo(vcpu->vcpu); | ||||
launched = 0; | launched = 0; | ||||
KASSERT(vmxctx->pmap == pmap, | KASSERT(vmxctx->pmap == pmap, | ||||
("pmap %p different than ctx pmap %p", pmap, vmxctx->pmap)); | ("pmap %p different than ctx pmap %p", pmap, vmxctx->pmap)); | ||||
vmx_msr_guest_enter(vmx, vcpu); | vmx_msr_guest_enter(vcpu); | ||||
VMPTRLD(vmcs); | VMPTRLD(vmcs); | ||||
/* | /* | ||||
* XXX | * XXX | ||||
* We do this every time because we may setup the virtual machine | * We do this every time because we may setup the virtual machine | ||||
* from a different process than the one that actually runs it. | * from a different process than the one that actually runs it. | ||||
* | * | ||||
Show All 23 Lines | do { | ||||
* not be "lost" because it will be held pending in the host | * not be "lost" because it will be held pending in the host | ||||
* APIC because interrupts are disabled. The pending interrupt | * APIC because interrupts are disabled. The pending interrupt | ||||
* will be recognized as soon as the guest state is loaded. | * will be recognized as soon as the guest state is loaded. | ||||
* | * | ||||
* The same reasoning applies to the IPI generated by | * The same reasoning applies to the IPI generated by | ||||
* pmap_invalidate_ept(). | * pmap_invalidate_ept(). | ||||
*/ | */ | ||||
disable_intr(); | disable_intr(); | ||||
vmx_inject_interrupts(vmx, vcpu, vlapic, rip); | vmx_inject_interrupts(vcpu, vlapic, rip); | ||||
/* | /* | ||||
* Check for vcpu suspension after injecting events because | * Check for vcpu suspension after injecting events because | ||||
* vmx_inject_interrupts() can suspend the vcpu due to a | * vmx_inject_interrupts() can suspend the vcpu due to a | ||||
* triple fault. | * triple fault. | ||||
*/ | */ | ||||
if (vcpu_suspended(evinfo)) { | if (vcpu_suspended(evinfo)) { | ||||
enable_intr(); | enable_intr(); | ||||
vm_exit_suspended(vmx->vm, vcpuid, rip); | vm_exit_suspended(vcpu->vcpu, rip); | ||||
break; | break; | ||||
} | } | ||||
if (vcpu_rendezvous_pending(evinfo)) { | if (vcpu_rendezvous_pending(evinfo)) { | ||||
enable_intr(); | enable_intr(); | ||||
vm_exit_rendezvous(vmx->vm, vcpuid, rip); | vm_exit_rendezvous(vcpu->vcpu, rip); | ||||
break; | break; | ||||
} | } | ||||
if (vcpu_reqidle(evinfo)) { | if (vcpu_reqidle(evinfo)) { | ||||
enable_intr(); | enable_intr(); | ||||
vm_exit_reqidle(vmx->vm, vcpuid, rip); | vm_exit_reqidle(vcpu->vcpu, rip); | ||||
break; | break; | ||||
} | } | ||||
if (vcpu_should_yield(vm, vcpuid)) { | if (vcpu_should_yield(vcpu->vcpu)) { | ||||
enable_intr(); | enable_intr(); | ||||
vm_exit_astpending(vmx->vm, vcpuid, rip); | vm_exit_astpending(vcpu->vcpu, rip); | ||||
vmx_astpending_trace(vcpu, rip); | vmx_astpending_trace(vcpu, rip); | ||||
handled = HANDLED; | handled = HANDLED; | ||||
break; | break; | ||||
} | } | ||||
if (vcpu_debugged(vm, vcpuid)) { | if (vcpu_debugged(vcpu->vcpu)) { | ||||
enable_intr(); | enable_intr(); | ||||
vm_exit_debug(vmx->vm, vcpuid, rip); | vm_exit_debug(vcpu->vcpu, rip); | ||||
break; | break; | ||||
} | } | ||||
/* | /* | ||||
* If TPR Shadowing is enabled, the TPR Threshold | * If TPR Shadowing is enabled, the TPR Threshold | ||||
* must be updated right before entering the guest. | * must be updated right before entering the guest. | ||||
*/ | */ | ||||
if (tpr_shadowing && !virtual_interrupt_delivery) { | if (tpr_shadowing && !virtual_interrupt_delivery) { | ||||
▲ Show 20 Lines • Show All 85 Lines • ▼ Show 20 Lines | if ((handled && vmexit->exitcode != VM_EXITCODE_BOGUS) || | ||||
panic("Mismatch between handled (%d) and exitcode (%d)", | panic("Mismatch between handled (%d) and exitcode (%d)", | ||||
handled, vmexit->exitcode); | handled, vmexit->exitcode); | ||||
} | } | ||||
VMX_CTR1(vcpu, "returning from vmx_run: exitcode %d", | VMX_CTR1(vcpu, "returning from vmx_run: exitcode %d", | ||||
vmexit->exitcode); | vmexit->exitcode); | ||||
VMCLEAR(vmcs); | VMCLEAR(vmcs); | ||||
vmx_msr_guest_exit(vmx, vcpu); | vmx_msr_guest_exit(vcpu); | ||||
return (0); | return (0); | ||||
} | } | ||||
static void | static void | ||||
vmx_vcpu_cleanup(void *vcpui) | vmx_vcpu_cleanup(void *vcpui) | ||||
{ | { | ||||
struct vmx_vcpu *vcpu = vcpui; | struct vmx_vcpu *vcpu = vcpui; | ||||
▲ Show 20 Lines • Show All 159 Lines • ▼ Show 20 Lines | |||||
static int | static int | ||||
vmx_getreg(void *vcpui, int reg, uint64_t *retval) | vmx_getreg(void *vcpui, int reg, uint64_t *retval) | ||||
{ | { | ||||
int running, hostcpu; | int running, hostcpu; | ||||
struct vmx_vcpu *vcpu = vcpui; | struct vmx_vcpu *vcpu = vcpui; | ||||
struct vmx *vmx = vcpu->vmx; | struct vmx *vmx = vcpu->vmx; | ||||
running = vcpu_is_running(vmx->vm, vcpu->vcpuid, &hostcpu); | running = vcpu_is_running(vcpu->vcpu, &hostcpu); | ||||
if (running && hostcpu != curcpu) | if (running && hostcpu != curcpu) | ||||
panic("vmx_getreg: %s%d is running", vm_name(vmx->vm), | panic("vmx_getreg: %s%d is running", vm_name(vmx->vm), | ||||
vcpu->vcpuid); | vcpu->vcpuid); | ||||
if (reg == VM_REG_GUEST_INTR_SHADOW) | if (reg == VM_REG_GUEST_INTR_SHADOW) | ||||
return (vmx_get_intr_shadow(vcpu, running, retval)); | return (vmx_get_intr_shadow(vcpu, running, retval)); | ||||
if (vmxctx_getreg(&vcpu->ctx, reg, retval) == 0) | if (vmxctx_getreg(&vcpu->ctx, reg, retval) == 0) | ||||
return (0); | return (0); | ||||
return (vmcs_getreg(vcpu->vmcs, running, reg, retval)); | return (vmcs_getreg(vcpu->vmcs, running, reg, retval)); | ||||
} | } | ||||
static int | static int | ||||
vmx_setreg(void *vcpui, int reg, uint64_t val) | vmx_setreg(void *vcpui, int reg, uint64_t val) | ||||
{ | { | ||||
int error, hostcpu, running, shadow; | int error, hostcpu, running, shadow; | ||||
uint64_t ctls; | uint64_t ctls; | ||||
pmap_t pmap; | pmap_t pmap; | ||||
struct vmx_vcpu *vcpu = vcpui; | struct vmx_vcpu *vcpu = vcpui; | ||||
struct vmx *vmx = vcpu->vmx; | struct vmx *vmx = vcpu->vmx; | ||||
running = vcpu_is_running(vmx->vm, vcpu->vcpuid, &hostcpu); | running = vcpu_is_running(vcpu->vcpu, &hostcpu); | ||||
if (running && hostcpu != curcpu) | if (running && hostcpu != curcpu) | ||||
panic("vmx_setreg: %s%d is running", vm_name(vmx->vm), | panic("vmx_setreg: %s%d is running", vm_name(vmx->vm), | ||||
vcpu->vcpuid); | vcpu->vcpuid); | ||||
if (reg == VM_REG_GUEST_INTR_SHADOW) | if (reg == VM_REG_GUEST_INTR_SHADOW) | ||||
return (vmx_modify_intr_shadow(vcpu, running, val)); | return (vmx_modify_intr_shadow(vcpu, running, val)); | ||||
if (vmxctx_setreg(&vcpu->ctx, reg, val) == 0) | if (vmxctx_setreg(&vcpu->ctx, reg, val) == 0) | ||||
▲ Show 20 Lines • Show All 50 Lines • ▼ Show 20 Lines | |||||
static int | static int | ||||
vmx_getdesc(void *vcpui, int reg, struct seg_desc *desc) | vmx_getdesc(void *vcpui, int reg, struct seg_desc *desc) | ||||
{ | { | ||||
int hostcpu, running; | int hostcpu, running; | ||||
struct vmx_vcpu *vcpu = vcpui; | struct vmx_vcpu *vcpu = vcpui; | ||||
struct vmx *vmx = vcpu->vmx; | struct vmx *vmx = vcpu->vmx; | ||||
running = vcpu_is_running(vmx->vm, vcpu->vcpuid, &hostcpu); | running = vcpu_is_running(vcpu->vcpu, &hostcpu); | ||||
if (running && hostcpu != curcpu) | if (running && hostcpu != curcpu) | ||||
panic("vmx_getdesc: %s%d is running", vm_name(vmx->vm), | panic("vmx_getdesc: %s%d is running", vm_name(vmx->vm), | ||||
vcpu->vcpuid); | vcpu->vcpuid); | ||||
return (vmcs_getdesc(vcpu->vmcs, running, reg, desc)); | return (vmcs_getdesc(vcpu->vmcs, running, reg, desc)); | ||||
} | } | ||||
static int | static int | ||||
vmx_setdesc(void *vcpui, int reg, struct seg_desc *desc) | vmx_setdesc(void *vcpui, int reg, struct seg_desc *desc) | ||||
{ | { | ||||
int hostcpu, running; | int hostcpu, running; | ||||
struct vmx_vcpu *vcpu = vcpui; | struct vmx_vcpu *vcpu = vcpui; | ||||
struct vmx *vmx = vcpu->vmx; | struct vmx *vmx = vcpu->vmx; | ||||
running = vcpu_is_running(vmx->vm, vcpu->vcpuid, &hostcpu); | running = vcpu_is_running(vcpu->vcpu, &hostcpu); | ||||
if (running && hostcpu != curcpu) | if (running && hostcpu != curcpu) | ||||
panic("vmx_setdesc: %s%d is running", vm_name(vmx->vm), | panic("vmx_setdesc: %s%d is running", vm_name(vmx->vm), | ||||
vcpu->vcpuid); | vcpu->vcpuid); | ||||
return (vmcs_setdesc(vcpu->vmcs, running, reg, desc)); | return (vmcs_setdesc(vcpu->vmcs, running, reg, desc)); | ||||
} | } | ||||
static int | static int | ||||
▲ Show 20 Lines • Show All 294 Lines • ▼ Show 20 Lines | if (!pending) { | ||||
* processed the actual delivery maybe pending the | * processed the actual delivery maybe pending the | ||||
* interruptibility of the guest. Recognize a pending | * interruptibility of the guest. Recognize a pending | ||||
* interrupt by reevaluating virtual interrupts | * interrupt by reevaluating virtual interrupts | ||||
* following Section 29.2.1 in the Intel SDM Volume 3. | * following Section 29.2.1 in the Intel SDM Volume 3. | ||||
*/ | */ | ||||
struct vm_exit *vmexit; | struct vm_exit *vmexit; | ||||
uint8_t rvi, ppr; | uint8_t rvi, ppr; | ||||
vmexit = vm_exitinfo(vlapic->vm, vlapic->vcpuid); | vmexit = vm_exitinfo(vlapic->vcpu); | ||||
KASSERT(vmexit->exitcode == VM_EXITCODE_HLT, | KASSERT(vmexit->exitcode == VM_EXITCODE_HLT, | ||||
("vmx_pending_intr: exitcode not 'HLT'")); | ("vmx_pending_intr: exitcode not 'HLT'")); | ||||
rvi = vmexit->u.hlt.intr_status & APIC_TPR_INT; | rvi = vmexit->u.hlt.intr_status & APIC_TPR_INT; | ||||
lapic = vlapic->apic_page; | lapic = vlapic->apic_page; | ||||
ppr = lapic->ppr & APIC_TPR_INT; | ppr = lapic->ppr & APIC_TPR_INT; | ||||
if (rvi > ppr) { | if (rvi > ppr) { | ||||
return (1); | return (1); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 52 Lines • ▼ Show 20 Lines | |||||
static void | static void | ||||
vmx_set_tmr(struct vlapic *vlapic, int vector, bool level) | vmx_set_tmr(struct vlapic *vlapic, int vector, bool level) | ||||
{ | { | ||||
struct vlapic_vtx *vlapic_vtx; | struct vlapic_vtx *vlapic_vtx; | ||||
struct vmcs *vmcs; | struct vmcs *vmcs; | ||||
uint64_t mask, val; | uint64_t mask, val; | ||||
KASSERT(vector >= 0 && vector <= 255, ("invalid vector %d", vector)); | KASSERT(vector >= 0 && vector <= 255, ("invalid vector %d", vector)); | ||||
KASSERT(!vcpu_is_running(vlapic->vm, vlapic->vcpuid, NULL), | KASSERT(!vcpu_is_running(vlapic->vcpu, NULL), | ||||
("vmx_set_tmr: vcpu cannot be running")); | ("vmx_set_tmr: vcpu cannot be running")); | ||||
vlapic_vtx = (struct vlapic_vtx *)vlapic; | vlapic_vtx = (struct vlapic_vtx *)vlapic; | ||||
vmcs = vlapic_vtx->vcpu->vmcs; | vmcs = vlapic_vtx->vcpu->vmcs; | ||||
mask = 1UL << (vector % 64); | mask = 1UL << (vector % 64); | ||||
VMPTRLD(vmcs); | VMPTRLD(vmcs); | ||||
val = vmcs_read(VMCS_EOI_EXIT(vector)); | val = vmcs_read(VMCS_EOI_EXIT(vector)); | ||||
▲ Show 20 Lines • Show All 240 Lines • ▼ Show 20 Lines | vmx_vcpu_snapshot(void *vcpui, struct vm_snapshot_meta *meta) | ||||
struct vmxctx *vmxctx; | struct vmxctx *vmxctx; | ||||
int err, run, hostcpu; | int err, run, hostcpu; | ||||
err = 0; | err = 0; | ||||
vcpu = vcpui; | vcpu = vcpui; | ||||
vmx = vcpu->vmx; | vmx = vcpu->vmx; | ||||
vmcs = vcpu->vmcs; | vmcs = vcpu->vmcs; | ||||
run = vcpu_is_running(vmx->vm, vcpu->vcpuid, &hostcpu); | run = vcpu_is_running(vcpu->vcpu, &hostcpu); | ||||
if (run && hostcpu != curcpu) { | if (run && hostcpu != curcpu) { | ||||
printf("%s: %s%d is running", __func__, vm_name(vmx->vm), | printf("%s: %s%d is running", __func__, vm_name(vmx->vm), | ||||
vcpu->vcpuid); | vcpu->vcpuid); | ||||
return (EINVAL); | return (EINVAL); | ||||
} | } | ||||
err += vmcs_snapshot_reg(vmcs, run, VM_REG_GUEST_CR0, meta); | err += vmcs_snapshot_reg(vmcs, run, VM_REG_GUEST_CR0, meta); | ||||
err += vmcs_snapshot_reg(vmcs, run, VM_REG_GUEST_CR3, meta); | err += vmcs_snapshot_reg(vmcs, run, VM_REG_GUEST_CR3, meta); | ||||
▲ Show 20 Lines • Show All 86 Lines • ▼ Show 20 Lines | vmx_restore_tsc(void *vcpui, uint64_t offset) | ||||
struct vmx_vcpu *vcpu = vcpui; | struct vmx_vcpu *vcpu = vcpui; | ||||
struct vmcs *vmcs; | struct vmcs *vmcs; | ||||
struct vmx *vmx; | struct vmx *vmx; | ||||
int error, running, hostcpu; | int error, running, hostcpu; | ||||
vmx = vcpu->vmx; | vmx = vcpu->vmx; | ||||
vmcs = vcpu->vmcs; | vmcs = vcpu->vmcs; | ||||
running = vcpu_is_running(vmx->vm, vcpu->vcpuid, &hostcpu); | running = vcpu_is_running(vcpu->vcpu, &hostcpu); | ||||
if (running && hostcpu != curcpu) { | if (running && hostcpu != curcpu) { | ||||
printf("%s: %s%d is running", __func__, vm_name(vmx->vm), | printf("%s: %s%d is running", __func__, vm_name(vmx->vm), | ||||
vcpu->vcpuid); | vcpu->vcpuid); | ||||
return (EINVAL); | return (EINVAL); | ||||
} | } | ||||
if (!running) | if (!running) | ||||
VMPTRLD(vmcs); | VMPTRLD(vmcs); | ||||
error = vmx_set_tsc_offset(vmx, vcpu, offset); | error = vmx_set_tsc_offset(vcpu, offset); | ||||
if (!running) | if (!running) | ||||
VMCLEAR(vmcs); | VMCLEAR(vmcs); | ||||
return (error); | return (error); | ||||
} | } | ||||
#endif | #endif | ||||
const struct vmm_ops vmm_ops_intel = { | const struct vmm_ops vmm_ops_intel = { | ||||
Show All 24 Lines |