Changeset View
Changeset View
Standalone View
Standalone View
sys/amd64/vmm/intel/vmx.c
Show First 20 Lines • Show All 50 Lines • ▼ Show 20 Lines | |||||
#include <machine/segments.h> | #include <machine/segments.h> | ||||
#include <machine/smp.h> | #include <machine/smp.h> | ||||
#include <machine/specialreg.h> | #include <machine/specialreg.h> | ||||
#include <machine/vmparam.h> | #include <machine/vmparam.h> | ||||
#include <machine/vmm.h> | #include <machine/vmm.h> | ||||
#include <machine/vmm_dev.h> | #include <machine/vmm_dev.h> | ||||
#include <machine/vmm_instruction_emul.h> | #include <machine/vmm_instruction_emul.h> | ||||
#include <machine/vmm_snapshot.h> | |||||
#include "vmm_lapic.h" | #include "vmm_lapic.h" | ||||
#include "vmm_host.h" | #include "vmm_host.h" | ||||
#include "vmm_ioport.h" | #include "vmm_ioport.h" | ||||
#include "vmm_ktr.h" | #include "vmm_ktr.h" | ||||
#include "vmm_stat.h" | #include "vmm_stat.h" | ||||
#include "vatpic.h" | #include "vatpic.h" | ||||
#include "vlapic.h" | #include "vlapic.h" | ||||
#include "vlapic_priv.h" | #include "vlapic_priv.h" | ||||
▲ Show 20 Lines • Show All 216 Lines • ▼ Show 20 Lines | |||||
* 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 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); | ||||
static int vmx_restore_tsc(void *arg, int vcpu, uint64_t now); | |||||
#ifdef KTR | #ifdef KTR | ||||
static const char * | static const char * | ||||
exit_reason_to_str(int reason) | exit_reason_to_str(int reason) | ||||
{ | { | ||||
static char reasonbuf[32]; | static char reasonbuf[32]; | ||||
switch (reason) { | switch (reason) { | ||||
▲ Show 20 Lines • Show All 975 Lines • ▼ Show 20 Lines | vmx_set_tsc_offset(struct vmx *vmx, int vcpu, uint64_t offset) | ||||
if ((vmx->cap[vcpu].proc_ctls & PROCBASED_TSC_OFFSET) == 0) { | if ((vmx->cap[vcpu].proc_ctls & PROCBASED_TSC_OFFSET) == 0) { | ||||
vmx->cap[vcpu].proc_ctls |= PROCBASED_TSC_OFFSET; | vmx->cap[vcpu].proc_ctls |= PROCBASED_TSC_OFFSET; | ||||
vmcs_write(VMCS_PRI_PROC_BASED_CTLS, vmx->cap[vcpu].proc_ctls); | vmcs_write(VMCS_PRI_PROC_BASED_CTLS, vmx->cap[vcpu].proc_ctls); | ||||
VCPU_CTR0(vmx->vm, vcpu, "Enabling TSC offsetting"); | VCPU_CTR0(vmx->vm, vcpu, "Enabling TSC offsetting"); | ||||
} | } | ||||
error = vmwrite(VMCS_TSC_OFFSET, offset); | error = vmwrite(VMCS_TSC_OFFSET, offset); | ||||
if (error != 0) | |||||
goto done; | |||||
error = vm_set_tsc_offset(vmx->vm, vcpu, offset); | |||||
done: | |||||
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) | ||||
▲ Show 20 Lines • Show All 2,489 Lines • ▼ Show 20 Lines | |||||
static void | static void | ||||
vmx_vlapic_cleanup(void *arg, struct vlapic *vlapic) | vmx_vlapic_cleanup(void *arg, struct vlapic *vlapic) | ||||
{ | { | ||||
vlapic_cleanup(vlapic); | vlapic_cleanup(vlapic); | ||||
free(vlapic, M_VLAPIC); | free(vlapic, M_VLAPIC); | ||||
} | } | ||||
static int | |||||
vmx_snapshot_vmi(void *arg, struct vm_snapshot_meta *meta) | |||||
{ | |||||
struct vmx *vmx; | |||||
struct vmxctx *vmxctx; | |||||
struct pmap *new_pmap; | |||||
int i; | |||||
int ret; | |||||
vmx = arg; | |||||
KASSERT(vmx != NULL, ("%s: arg was NULL", __func__)); | |||||
for (i = 0; i < VM_MAXCPU; i++) { | |||||
SNAPSHOT_BUF_OR_LEAVE(vmx->guest_msrs[i], | |||||
sizeof(vmx->guest_msrs[i]), meta, ret, | |||||
done); | |||||
vmxctx = &vmx->ctx[i]; | |||||
new_pmap = vmxctx->pmap; | |||||
SNAPSHOT_BUF_OR_LEAVE(vmxctx, sizeof(*vmxctx), meta, ret, done); | |||||
vmxctx->pmap = new_pmap; | |||||
vmx->eptgen[i] = new_pmap->pm_eptgen - 1; | |||||
} | |||||
done: | |||||
return (0); | |||||
} | |||||
static int | |||||
vmx_snapshot_vmcx(void *arg, struct vm_snapshot_meta *meta, int vcpu) | |||||
{ | |||||
struct vmcs *vmcs; | |||||
struct vmx *vmx; | |||||
int err, run, hostcpu; | |||||
vmx = (struct vmx *)arg; | |||||
err = 0; | |||||
KASSERT(arg != NULL, ("%s: arg was NULL", __func__)); | |||||
vmcs = &vmx->vmcs[vcpu]; | |||||
run = vcpu_is_running(vmx->vm, vcpu, &hostcpu); | |||||
if (run && hostcpu != curcpu) { | |||||
printf("%s: %s%d is running", __func__, vm_name(vmx->vm), vcpu); | |||||
return (EINVAL); | |||||
} | |||||
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_CR4, meta); | |||||
err += vmcs_snapshot_reg(vmcs, run, VM_REG_GUEST_DR7, meta); | |||||
err += vmcs_snapshot_reg(vmcs, run, VM_REG_GUEST_RSP, meta); | |||||
err += vmcs_snapshot_reg(vmcs, run, VM_REG_GUEST_RIP, meta); | |||||
err += vmcs_snapshot_reg(vmcs, run, VM_REG_GUEST_RFLAGS, meta); | |||||
/* Guest segments */ | |||||
err += vmcs_snapshot_reg(vmcs, run, VM_REG_GUEST_ES, meta); | |||||
err += vmcs_snapshot_desc(vmcs, run, VM_REG_GUEST_ES, meta); | |||||
err += vmcs_snapshot_reg(vmcs, run, VM_REG_GUEST_CS, meta); | |||||
err += vmcs_snapshot_desc(vmcs, run, VM_REG_GUEST_CS, meta); | |||||
err += vmcs_snapshot_reg(vmcs, run, VM_REG_GUEST_SS, meta); | |||||
err += vmcs_snapshot_desc(vmcs, run, VM_REG_GUEST_SS, meta); | |||||
err += vmcs_snapshot_reg(vmcs, run, VM_REG_GUEST_DS, meta); | |||||
err += vmcs_snapshot_desc(vmcs, run, VM_REG_GUEST_DS, meta); | |||||
err += vmcs_snapshot_reg(vmcs, run, VM_REG_GUEST_FS, meta); | |||||
err += vmcs_snapshot_desc(vmcs, run, VM_REG_GUEST_FS, meta); | |||||
err += vmcs_snapshot_reg(vmcs, run, VM_REG_GUEST_GS, meta); | |||||
err += vmcs_snapshot_desc(vmcs, run, VM_REG_GUEST_GS, meta); | |||||
pmooney_pfmooney.com: With descriptors being common across the two CPU types, why not use a general mechanism for… | |||||
Not Done Inline ActionsSimply meant to keep code for all data from vmcs bundled in one function - may be useful to move to a generic function to avoid duplication. darius.mihaim_gmail.com: Simply meant to keep code for all data from vmcs bundled in one function - may be useful to… | |||||
err += vmcs_snapshot_reg(vmcs, run, VM_REG_GUEST_TR, meta); | |||||
err += vmcs_snapshot_desc(vmcs, run, VM_REG_GUEST_TR, meta); | |||||
err += vmcs_snapshot_reg(vmcs, run, VM_REG_GUEST_LDTR, meta); | |||||
err += vmcs_snapshot_desc(vmcs, run, VM_REG_GUEST_LDTR, meta); | |||||
err += vmcs_snapshot_reg(vmcs, run, VM_REG_GUEST_EFER, meta); | |||||
err += vmcs_snapshot_desc(vmcs, run, VM_REG_GUEST_IDTR, meta); | |||||
err += vmcs_snapshot_desc(vmcs, run, VM_REG_GUEST_GDTR, meta); | |||||
/* Guest page tables */ | |||||
err += vmcs_snapshot_reg(vmcs, run, VM_REG_GUEST_PDPTE0, meta); | |||||
err += vmcs_snapshot_reg(vmcs, run, VM_REG_GUEST_PDPTE1, meta); | |||||
err += vmcs_snapshot_reg(vmcs, run, VM_REG_GUEST_PDPTE2, meta); | |||||
err += vmcs_snapshot_reg(vmcs, run, VM_REG_GUEST_PDPTE3, meta); | |||||
/* Other guest state */ | |||||
err += vmcs_snapshot_any(vmcs, run, VMCS_GUEST_IA32_SYSENTER_CS, meta); | |||||
err += vmcs_snapshot_any(vmcs, run, VMCS_GUEST_IA32_SYSENTER_ESP, meta); | |||||
err += vmcs_snapshot_any(vmcs, run, VMCS_GUEST_IA32_SYSENTER_EIP, meta); | |||||
Not Done Inline ActionsWhy not save these MSRs in a general fasion? (Same goes for EFER too) pmooney_pfmooney.com: Why not save these MSRs in a general fasion? (Same goes for EFER too) | |||||
Not Done Inline ActionsSame comment as the one for other registers. darius.mihaim_gmail.com: Same comment as the one for other registers. | |||||
err += vmcs_snapshot_any(vmcs, run, VMCS_GUEST_INTERRUPTIBILITY, meta); | |||||
err += vmcs_snapshot_any(vmcs, run, VMCS_GUEST_ACTIVITY, meta); | |||||
err += vmcs_snapshot_any(vmcs, run, VMCS_ENTRY_CTLS, meta); | |||||
err += vmcs_snapshot_any(vmcs, run, VMCS_EXIT_CTLS, meta); | |||||
Not Done Inline ActionsSaving/restoring control state like this with no validation seems dangerous. It's probably safest to allow the host to reconstruct that from present (and requested) features? pmooney_pfmooney.com: Saving/restoring control state like this with no validation seems dangerous. It's probably… | |||||
return (err); | |||||
} | |||||
static int | |||||
vmx_restore_tsc(void *arg, int vcpu, uint64_t offset) | |||||
{ | |||||
struct vmcs *vmcs; | |||||
struct vmx *vmx = (struct vmx *)arg; | |||||
int error, running, hostcpu; | |||||
KASSERT(arg != NULL, ("%s: arg was NULL", __func__)); | |||||
vmcs = &vmx->vmcs[vcpu]; | |||||
running = vcpu_is_running(vmx->vm, vcpu, &hostcpu); | |||||
if (running && hostcpu != curcpu) { | |||||
printf("%s: %s%d is running", __func__, vm_name(vmx->vm), vcpu); | |||||
return (EINVAL); | |||||
} | |||||
if (!running) | |||||
VMPTRLD(vmcs); | |||||
error = vmx_set_tsc_offset(vmx, vcpu, offset); | |||||
if (!running) | |||||
VMCLEAR(vmcs); | |||||
return (error); | |||||
} | |||||
struct vmm_ops vmm_ops_intel = { | struct vmm_ops vmm_ops_intel = { | ||||
Not Done Inline ActionsIn SmartOS bhyve, we added a CPU-agnostic method for setting the tsc offset in vmm.c, which then VMX/SVM consume in order to do the CPU-specific settings. It might make sense to do something like that here. pmooney_pfmooney.com: In SmartOS bhyve, we added a CPU-agnostic method for setting the tsc offset in vmm.c, which… | |||||
Done Inline ActionsWe will look into it, but it's not a priority for now. Thanks for pointing it out. darius.mihaim_gmail.com: We will look into it, but it's not a priority for now. Thanks for pointing it out. | |||||
vmx_init, | vmx_init, | ||||
vmx_cleanup, | vmx_cleanup, | ||||
vmx_restore, | vmx_restore, | ||||
vmx_vminit, | vmx_vminit, | ||||
vmx_run, | vmx_run, | ||||
vmx_vmcleanup, | vmx_vmcleanup, | ||||
vmx_getreg, | vmx_getreg, | ||||
vmx_setreg, | vmx_setreg, | ||||
vmx_getdesc, | vmx_getdesc, | ||||
vmx_setdesc, | vmx_setdesc, | ||||
vmx_getcap, | vmx_getcap, | ||||
vmx_setcap, | vmx_setcap, | ||||
ept_vmspace_alloc, | ept_vmspace_alloc, | ||||
ept_vmspace_free, | ept_vmspace_free, | ||||
vmx_vlapic_init, | vmx_vlapic_init, | ||||
vmx_vlapic_cleanup, | vmx_vlapic_cleanup, | ||||
vmx_snapshot_vmi, | |||||
vmx_snapshot_vmcx, | |||||
vmx_restore_tsc, | |||||
}; | }; |
With descriptors being common across the two CPU types, why not use a general mechanism for saving them?