Changeset View
Changeset View
Standalone View
Standalone View
sys/amd64/vmm/vmm.c
Show First 20 Lines • Show All 61 Lines • ▼ Show 20 Lines | |||||
#include <vm/uma.h> | #include <vm/uma.h> | ||||
#include <machine/cpu.h> | #include <machine/cpu.h> | ||||
#include <machine/pcb.h> | #include <machine/pcb.h> | ||||
#include <machine/smp.h> | #include <machine/smp.h> | ||||
#include <machine/md_var.h> | #include <machine/md_var.h> | ||||
#include <x86/psl.h> | #include <x86/psl.h> | ||||
#include <x86/apicreg.h> | #include <x86/apicreg.h> | ||||
#include <x86/ifunc.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 <machine/vmm_snapshot.h> | ||||
#include "vmm_ioport.h" | #include "vmm_ioport.h" | ||||
#include "vmm_ktr.h" | #include "vmm_ktr.h" | ||||
▲ Show 20 Lines • Show All 101 Lines • ▼ Show 20 Lines | struct vm { | ||||
uint16_t sockets; /* (o) num of sockets */ | uint16_t sockets; /* (o) num of sockets */ | ||||
uint16_t cores; /* (o) num of cores/socket */ | uint16_t cores; /* (o) num of cores/socket */ | ||||
uint16_t threads; /* (o) num of threads/core */ | uint16_t threads; /* (o) num of threads/core */ | ||||
uint16_t maxcpus; /* (o) max pluggable cpus */ | uint16_t maxcpus; /* (o) max pluggable cpus */ | ||||
}; | }; | ||||
static int vmm_initialized; | static int vmm_initialized; | ||||
static struct vmm_ops *ops; | static void vmmops_panic(void); | ||||
#define VMM_INIT(num) (ops != NULL ? (*ops->init)(num) : 0) | |||||
#define VMM_CLEANUP() (ops != NULL ? (*ops->cleanup)() : 0) | |||||
#define VMM_RESUME() (ops != NULL ? (*ops->resume)() : 0) | |||||
#define VMINIT(vm, pmap) (ops != NULL ? (*ops->vminit)(vm, pmap): NULL) | static void | ||||
#define VMRUN(vmi, vcpu, rip, pmap, evinfo) \ | vmmops_panic(void) | ||||
(ops != NULL ? (*ops->vmrun)(vmi, vcpu, rip, pmap, evinfo) : ENXIO) | { | ||||
#define VMCLEANUP(vmi) (ops != NULL ? (*ops->vmcleanup)(vmi) : NULL) | panic("vmm_ops func called when !vmm_is_intel() && !vmm_is_svm()"); | ||||
#define VMSPACE_ALLOC(min, max) \ | } | ||||
(ops != NULL ? (*ops->vmspace_alloc)(min, max) : NULL) | |||||
#define VMSPACE_FREE(vmspace) \ | #define DEFINE_VMMOPS_IFUNC(ret_type, opname, args) \ | ||||
(ops != NULL ? (*ops->vmspace_free)(vmspace) : ENXIO) | DEFINE_IFUNC(static, ret_type, vmmops_##opname, args) \ | ||||
#define VMGETREG(vmi, vcpu, num, retval) \ | { \ | ||||
(ops != NULL ? (*ops->vmgetreg)(vmi, vcpu, num, retval) : ENXIO) | if (vmm_is_intel()) \ | ||||
#define VMSETREG(vmi, vcpu, num, val) \ | return (vmm_ops_intel.opname); \ | ||||
(ops != NULL ? (*ops->vmsetreg)(vmi, vcpu, num, val) : ENXIO) | else if (vmm_is_svm()) \ | ||||
#define VMGETDESC(vmi, vcpu, num, desc) \ | return (vmm_ops_amd.opname); \ | ||||
(ops != NULL ? (*ops->vmgetdesc)(vmi, vcpu, num, desc) : ENXIO) | else \ | ||||
#define VMSETDESC(vmi, vcpu, num, desc) \ | return ((ret_type (*)args)vmmops_panic); \ | ||||
(ops != NULL ? (*ops->vmsetdesc)(vmi, vcpu, num, desc) : ENXIO) | } | ||||
#define VMGETCAP(vmi, vcpu, num, retval) \ | |||||
(ops != NULL ? (*ops->vmgetcap)(vmi, vcpu, num, retval) : ENXIO) | DEFINE_VMMOPS_IFUNC(int, modinit, (int ipinum)) | ||||
#define VMSETCAP(vmi, vcpu, num, val) \ | DEFINE_VMMOPS_IFUNC(int, modcleanup, (void)) | ||||
(ops != NULL ? (*ops->vmsetcap)(vmi, vcpu, num, val) : ENXIO) | DEFINE_VMMOPS_IFUNC(void, modresume, (void)) | ||||
#define VLAPIC_INIT(vmi, vcpu) \ | DEFINE_VMMOPS_IFUNC(void *, init, (struct vm *vm, struct pmap *pmap)) | ||||
(ops != NULL ? (*ops->vlapic_init)(vmi, vcpu) : NULL) | DEFINE_VMMOPS_IFUNC(int, run, (void *vmi, int vcpu, register_t rip, | ||||
#define VLAPIC_CLEANUP(vmi, vlapic) \ | struct pmap *pmap, struct vm_eventinfo *info)) | ||||
(ops != NULL ? (*ops->vlapic_cleanup)(vmi, vlapic) : NULL) | DEFINE_VMMOPS_IFUNC(void, cleanup, (void *vmi)) | ||||
DEFINE_VMMOPS_IFUNC(int, getreg, (void *vmi, int vcpu, int num, | |||||
uint64_t *retval)) | |||||
DEFINE_VMMOPS_IFUNC(int, setreg, (void *vmi, int vcpu, int num, | |||||
uint64_t val)) | |||||
DEFINE_VMMOPS_IFUNC(int, getdesc, (void *vmi, int vcpu, int num, | |||||
struct seg_desc *desc)) | |||||
DEFINE_VMMOPS_IFUNC(int, setdesc, (void *vmi, int vcpu, int num, | |||||
struct seg_desc *desc)) | |||||
DEFINE_VMMOPS_IFUNC(int, getcap, (void *vmi, int vcpu, int num, int *retval)) | |||||
DEFINE_VMMOPS_IFUNC(int, setcap, (void *vmi, int vcpu, int num, int val)) | |||||
DEFINE_VMMOPS_IFUNC(struct vmspace *, vmspace_alloc, (vm_offset_t min, | |||||
vm_offset_t max)) | |||||
DEFINE_VMMOPS_IFUNC(void, vmspace_free, (struct vmspace *vmspace)) | |||||
DEFINE_VMMOPS_IFUNC(struct vlapic *, vlapic_init, (void *vmi, int vcpu)) | |||||
DEFINE_VMMOPS_IFUNC(void, vlapic_cleanup, (void *vmi, struct vlapic *vlapic)) | |||||
#ifdef BHYVE_SNAPSHOT | #ifdef BHYVE_SNAPSHOT | ||||
#define VM_SNAPSHOT_VMI(vmi, meta) \ | DEFINE_VMMOPS_IFUNC(int, snapshot, (void *vmi, struct vm_snapshot_meta | ||||
(ops != NULL ? (*ops->vmsnapshot)(vmi, meta) : ENXIO) | *meta)) | ||||
#define VM_SNAPSHOT_VMCX(vmi, meta, vcpuid) \ | DEFINE_VMMOPS_IFUNC(int, vmcx_snapshot, (void *vmi, struct vm_snapshot_meta | ||||
(ops != NULL ? (*ops->vmcx_snapshot)(vmi, meta, vcpuid) : ENXIO) | *meta, int vcpu)) | ||||
#define VM_RESTORE_TSC(vmi, vcpuid, offset) \ | DEFINE_VMMOPS_IFUNC(int, restore_tsc, (void *vmi, int vcpuid, uint64_t now)) | ||||
(ops != NULL ? (*ops->vm_restore_tsc)(vmi, vcpuid, offset) : ENXIO) | |||||
#endif | #endif | ||||
#define fpu_start_emulating() load_cr0(rcr0() | CR0_TS) | #define fpu_start_emulating() load_cr0(rcr0() | CR0_TS) | ||||
#define fpu_stop_emulating() clts() | #define fpu_stop_emulating() clts() | ||||
SDT_PROVIDER_DEFINE(vmm); | SDT_PROVIDER_DEFINE(vmm); | ||||
static MALLOC_DEFINE(M_VM, "vm", "vm"); | static MALLOC_DEFINE(M_VM, "vm", "vm"); | ||||
▲ Show 20 Lines • Show All 46 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
#endif | #endif | ||||
static void | static void | ||||
vcpu_cleanup(struct vm *vm, int i, bool destroy) | vcpu_cleanup(struct vm *vm, int i, bool destroy) | ||||
{ | { | ||||
struct vcpu *vcpu = &vm->vcpu[i]; | struct vcpu *vcpu = &vm->vcpu[i]; | ||||
VLAPIC_CLEANUP(vm->cookie, vcpu->vlapic); | vmmops_vlapic_cleanup(vm->cookie, vcpu->vlapic); | ||||
if (destroy) { | if (destroy) { | ||||
vmm_stat_free(vcpu->stats); | vmm_stat_free(vcpu->stats); | ||||
fpu_save_area_free(vcpu->guestfpu); | fpu_save_area_free(vcpu->guestfpu); | ||||
} | } | ||||
} | } | ||||
static void | static void | ||||
vcpu_init(struct vm *vm, int vcpu_id, bool create) | vcpu_init(struct vm *vm, int vcpu_id, bool create) | ||||
Show All 11 Lines | if (create) { | ||||
vcpu_lock_init(vcpu); | vcpu_lock_init(vcpu); | ||||
vcpu->state = VCPU_IDLE; | vcpu->state = VCPU_IDLE; | ||||
vcpu->hostcpu = NOCPU; | vcpu->hostcpu = NOCPU; | ||||
vcpu->guestfpu = fpu_save_area_alloc(); | vcpu->guestfpu = fpu_save_area_alloc(); | ||||
vcpu->stats = vmm_stat_alloc(); | vcpu->stats = vmm_stat_alloc(); | ||||
vcpu->tsc_offset = 0; | vcpu->tsc_offset = 0; | ||||
} | } | ||||
vcpu->vlapic = VLAPIC_INIT(vm->cookie, vcpu_id); | vcpu->vlapic = vmmops_vlapic_init(vm->cookie, vcpu_id); | ||||
vm_set_x2apic_state(vm, vcpu_id, X2APIC_DISABLED); | vm_set_x2apic_state(vm, vcpu_id, X2APIC_DISABLED); | ||||
vcpu->reqidle = 0; | vcpu->reqidle = 0; | ||||
vcpu->exitintinfo = 0; | vcpu->exitintinfo = 0; | ||||
vcpu->nmi_pending = 0; | vcpu->nmi_pending = 0; | ||||
vcpu->extint_pending = 0; | vcpu->extint_pending = 0; | ||||
vcpu->exception_pending = 0; | vcpu->exception_pending = 0; | ||||
vcpu->guest_xcr0 = XFEATURE_ENABLED_X87; | vcpu->guest_xcr0 = XFEATURE_ENABLED_X87; | ||||
fpu_save_area_reset(vcpu->guestfpu); | fpu_save_area_reset(vcpu->guestfpu); | ||||
Show All 15 Lines | vm_exitinfo(struct vm *vm, int cpuid) | ||||
if (cpuid < 0 || cpuid >= vm->maxcpus) | if (cpuid < 0 || cpuid >= vm->maxcpus) | ||||
panic("vm_exitinfo: invalid cpuid %d", cpuid); | panic("vm_exitinfo: invalid cpuid %d", cpuid); | ||||
vcpu = &vm->vcpu[cpuid]; | vcpu = &vm->vcpu[cpuid]; | ||||
return (&vcpu->exitinfo); | return (&vcpu->exitinfo); | ||||
} | } | ||||
static void | |||||
vmm_resume(void) | |||||
{ | |||||
VMM_RESUME(); | |||||
} | |||||
static int | static int | ||||
vmm_init(void) | vmm_init(void) | ||||
{ | { | ||||
int error; | int error; | ||||
if (!vmm_is_hw_supported()) | |||||
return (ENXIO); | |||||
vmm_host_state_init(); | vmm_host_state_init(); | ||||
vmm_ipinum = lapic_ipi_alloc(pti ? &IDTVEC(justreturn1_pti) : | vmm_ipinum = lapic_ipi_alloc(pti ? &IDTVEC(justreturn1_pti) : | ||||
&IDTVEC(justreturn)); | &IDTVEC(justreturn)); | ||||
if (vmm_ipinum < 0) | if (vmm_ipinum < 0) | ||||
vmm_ipinum = IPI_AST; | vmm_ipinum = IPI_AST; | ||||
error = vmm_mem_init(); | error = vmm_mem_init(); | ||||
if (error) | if (error) | ||||
return (error); | return (error); | ||||
if (vmm_is_intel()) | vmm_resume_p = vmmops_modresume; | ||||
ops = &vmm_ops_intel; | |||||
else if (vmm_is_svm()) | |||||
ops = &vmm_ops_amd; | |||||
else | |||||
return (ENXIO); | |||||
vmm_resume_p = vmm_resume; | return (vmmops_modinit(vmm_ipinum)); | ||||
return (VMM_INIT(vmm_ipinum)); | |||||
} | } | ||||
static int | static int | ||||
vmm_handler(module_t mod, int what, void *arg) | vmm_handler(module_t mod, int what, void *arg) | ||||
{ | { | ||||
int error; | int error; | ||||
switch (what) { | switch (what) { | ||||
case MOD_LOAD: | case MOD_LOAD: | ||||
if (vmm_is_hw_supported()) { | |||||
vmmdev_init(); | vmmdev_init(); | ||||
error = vmm_init(); | error = vmm_init(); | ||||
if (error == 0) | if (error == 0) | ||||
vmm_initialized = 1; | vmm_initialized = 1; | ||||
} else { | |||||
error = ENXIO; | |||||
} | |||||
break; | break; | ||||
case MOD_UNLOAD: | case MOD_UNLOAD: | ||||
if (vmm_is_hw_supported()) { | |||||
error = vmmdev_cleanup(); | error = vmmdev_cleanup(); | ||||
if (error == 0) { | if (error == 0) { | ||||
vmm_resume_p = NULL; | vmm_resume_p = NULL; | ||||
iommu_cleanup(); | iommu_cleanup(); | ||||
if (vmm_ipinum != IPI_AST) | if (vmm_ipinum != IPI_AST) | ||||
lapic_ipi_free(vmm_ipinum); | lapic_ipi_free(vmm_ipinum); | ||||
error = VMM_CLEANUP(); | error = vmmops_modcleanup(); | ||||
/* | /* | ||||
* Something bad happened - prevent new | * Something bad happened - prevent new | ||||
* VMs from being created | * VMs from being created | ||||
*/ | */ | ||||
if (error) | if (error) | ||||
vmm_initialized = 0; | vmm_initialized = 0; | ||||
} | } | ||||
} else { | |||||
error = 0; | |||||
} | |||||
break; | break; | ||||
default: | default: | ||||
error = 0; | error = 0; | ||||
break; | break; | ||||
} | } | ||||
return (error); | return (error); | ||||
} | } | ||||
Show All 12 Lines | |||||
DECLARE_MODULE(vmm, vmm_kmod, SI_SUB_SMP + 1, SI_ORDER_ANY); | DECLARE_MODULE(vmm, vmm_kmod, SI_SUB_SMP + 1, SI_ORDER_ANY); | ||||
MODULE_VERSION(vmm, 1); | MODULE_VERSION(vmm, 1); | ||||
static void | static void | ||||
vm_init(struct vm *vm, bool create) | vm_init(struct vm *vm, bool create) | ||||
{ | { | ||||
int i; | int i; | ||||
vm->cookie = VMINIT(vm, vmspace_pmap(vm->vmspace)); | vm->cookie = vmmops_init(vm, vmspace_pmap(vm->vmspace)); | ||||
vm->iommu = NULL; | vm->iommu = NULL; | ||||
vm->vioapic = vioapic_init(vm); | vm->vioapic = vioapic_init(vm); | ||||
vm->vhpet = vhpet_init(vm); | vm->vhpet = vhpet_init(vm); | ||||
vm->vatpic = vatpic_init(vm); | vm->vatpic = vatpic_init(vm); | ||||
vm->vatpit = vatpit_init(vm); | vm->vatpit = vatpit_init(vm); | ||||
vm->vpmtmr = vpmtmr_init(vm); | vm->vpmtmr = vpmtmr_init(vm); | ||||
if (create) | if (create) | ||||
vm->vrtc = vrtc_init(vm); | vm->vrtc = vrtc_init(vm); | ||||
Show All 25 Lines | vm_create(const char *name, struct vm **retvm) | ||||
* to create the virtual machine. | * to create the virtual machine. | ||||
*/ | */ | ||||
if (!vmm_initialized) | if (!vmm_initialized) | ||||
return (ENXIO); | return (ENXIO); | ||||
if (name == NULL || strlen(name) >= VM_MAX_NAMELEN) | if (name == NULL || strlen(name) >= VM_MAX_NAMELEN) | ||||
return (EINVAL); | return (EINVAL); | ||||
vmspace = VMSPACE_ALLOC(0, VM_MAXUSER_ADDRESS); | vmspace = vmmops_vmspace_alloc(0, VM_MAXUSER_ADDRESS); | ||||
if (vmspace == NULL) | if (vmspace == NULL) | ||||
return (ENOMEM); | return (ENOMEM); | ||||
vm = malloc(sizeof(struct vm), M_VM, M_WAITOK | M_ZERO); | vm = malloc(sizeof(struct vm), M_VM, M_WAITOK | M_ZERO); | ||||
strcpy(vm->name, name); | strcpy(vm->name, name); | ||||
vm->vmspace = vmspace; | vm->vmspace = vmspace; | ||||
mtx_init(&vm->rendezvous_mtx, "vm rendezvous lock", 0, MTX_DEF); | mtx_init(&vm->rendezvous_mtx, "vm rendezvous lock", 0, MTX_DEF); | ||||
▲ Show 20 Lines • Show All 59 Lines • ▼ Show 20 Lines | vm_cleanup(struct vm *vm, bool destroy) | ||||
vatpit_cleanup(vm->vatpit); | vatpit_cleanup(vm->vatpit); | ||||
vhpet_cleanup(vm->vhpet); | vhpet_cleanup(vm->vhpet); | ||||
vatpic_cleanup(vm->vatpic); | vatpic_cleanup(vm->vatpic); | ||||
vioapic_cleanup(vm->vioapic); | vioapic_cleanup(vm->vioapic); | ||||
for (i = 0; i < vm->maxcpus; i++) | for (i = 0; i < vm->maxcpus; i++) | ||||
vcpu_cleanup(vm, i, destroy); | vcpu_cleanup(vm, i, destroy); | ||||
VMCLEANUP(vm->cookie); | vmmops_cleanup(vm->cookie); | ||||
/* | /* | ||||
* System memory is removed from the guest address space only when | * System memory is removed from the guest address space only when | ||||
* the VM is destroyed. This is because the mapping remains the same | * the VM is destroyed. This is because the mapping remains the same | ||||
* across VM reset. | * across VM reset. | ||||
* | * | ||||
* Device memory can be relocated by the guest (e.g. using PCI BARs) | * Device memory can be relocated by the guest (e.g. using PCI BARs) | ||||
* so those mappings are removed on a VM reset. | * so those mappings are removed on a VM reset. | ||||
*/ | */ | ||||
for (i = 0; i < VM_MAX_MEMMAPS; i++) { | for (i = 0; i < VM_MAX_MEMMAPS; i++) { | ||||
mm = &vm->mem_maps[i]; | mm = &vm->mem_maps[i]; | ||||
if (destroy || !sysmem_mapping(vm, mm)) | if (destroy || !sysmem_mapping(vm, mm)) | ||||
vm_free_memmap(vm, i); | vm_free_memmap(vm, i); | ||||
} | } | ||||
if (destroy) { | if (destroy) { | ||||
for (i = 0; i < VM_MAX_MEMSEGS; i++) | for (i = 0; i < VM_MAX_MEMSEGS; i++) | ||||
vm_free_memseg(vm, i); | vm_free_memseg(vm, i); | ||||
VMSPACE_FREE(vm->vmspace); | vmmops_vmspace_free(vm->vmspace); | ||||
vm->vmspace = NULL; | vm->vmspace = NULL; | ||||
} | } | ||||
} | } | ||||
void | void | ||||
vm_destroy(struct vm *vm) | vm_destroy(struct vm *vm) | ||||
{ | { | ||||
vm_cleanup(vm, true); | vm_cleanup(vm, true); | ||||
▲ Show 20 Lines • Show All 447 Lines • ▼ Show 20 Lines | |||||
{ | { | ||||
if (vcpu < 0 || vcpu >= vm->maxcpus) | if (vcpu < 0 || vcpu >= vm->maxcpus) | ||||
return (EINVAL); | return (EINVAL); | ||||
if (reg >= VM_REG_LAST) | if (reg >= VM_REG_LAST) | ||||
return (EINVAL); | return (EINVAL); | ||||
return (VMGETREG(vm->cookie, vcpu, reg, retval)); | return (vmmops_getreg(vm->cookie, vcpu, reg, retval)); | ||||
} | } | ||||
int | int | ||||
vm_set_register(struct vm *vm, int vcpuid, int reg, uint64_t val) | vm_set_register(struct vm *vm, int vcpuid, int reg, uint64_t val) | ||||
{ | { | ||||
struct vcpu *vcpu; | struct vcpu *vcpu; | ||||
int error; | int error; | ||||
if (vcpuid < 0 || vcpuid >= vm->maxcpus) | if (vcpuid < 0 || vcpuid >= vm->maxcpus) | ||||
return (EINVAL); | return (EINVAL); | ||||
if (reg >= VM_REG_LAST) | if (reg >= VM_REG_LAST) | ||||
return (EINVAL); | return (EINVAL); | ||||
error = VMSETREG(vm->cookie, vcpuid, reg, val); | error = vmmops_setreg(vm->cookie, vcpuid, reg, val); | ||||
if (error || reg != VM_REG_GUEST_RIP) | if (error || reg != VM_REG_GUEST_RIP) | ||||
return (error); | return (error); | ||||
/* Set 'nextrip' to match the value of %rip */ | /* Set 'nextrip' to match the value of %rip */ | ||||
VCPU_CTR1(vm, vcpuid, "Setting nextrip to %#lx", val); | VCPU_CTR1(vm, vcpuid, "Setting nextrip to %#lx", val); | ||||
vcpu = &vm->vcpu[vcpuid]; | vcpu = &vm->vcpu[vcpuid]; | ||||
vcpu->nextrip = val; | vcpu->nextrip = val; | ||||
return (0); | return (0); | ||||
Show All 37 Lines | |||||
{ | { | ||||
if (vcpu < 0 || vcpu >= vm->maxcpus) | if (vcpu < 0 || vcpu >= vm->maxcpus) | ||||
return (EINVAL); | return (EINVAL); | ||||
if (!is_segment_register(reg) && !is_descriptor_table(reg)) | if (!is_segment_register(reg) && !is_descriptor_table(reg)) | ||||
return (EINVAL); | return (EINVAL); | ||||
return (VMGETDESC(vm->cookie, vcpu, reg, desc)); | return (vmmops_getdesc(vm->cookie, vcpu, reg, desc)); | ||||
} | } | ||||
int | int | ||||
vm_set_seg_desc(struct vm *vm, int vcpu, int reg, | vm_set_seg_desc(struct vm *vm, int vcpu, int reg, | ||||
struct seg_desc *desc) | struct seg_desc *desc) | ||||
{ | { | ||||
if (vcpu < 0 || vcpu >= vm->maxcpus) | if (vcpu < 0 || vcpu >= vm->maxcpus) | ||||
return (EINVAL); | return (EINVAL); | ||||
if (!is_segment_register(reg) && !is_descriptor_table(reg)) | if (!is_segment_register(reg) && !is_descriptor_table(reg)) | ||||
return (EINVAL); | return (EINVAL); | ||||
return (VMSETDESC(vm->cookie, vcpu, reg, desc)); | return (vmmops_setdesc(vm->cookie, vcpu, reg, desc)); | ||||
} | } | ||||
static void | static void | ||||
restore_guest_fpustate(struct vcpu *vcpu) | restore_guest_fpustate(struct vcpu *vcpu) | ||||
{ | { | ||||
/* flush host state to the pcb */ | /* flush host state to the pcb */ | ||||
fpuexit(curthread); | fpuexit(curthread); | ||||
▲ Show 20 Lines • Show All 201 Lines • ▼ Show 20 Lines | vm_handle_hlt(struct vm *vm, int vcpuid, bool intr_disabled, bool *retu) | ||||
vcpu_lock(vcpu); | vcpu_lock(vcpu); | ||||
while (1) { | while (1) { | ||||
/* | /* | ||||
* Do a final check for pending NMI or interrupts before | * Do a final check for pending NMI or interrupts before | ||||
* really putting this thread to sleep. Also check for | * really putting this thread to sleep. Also check for | ||||
* software events that would cause this vcpu to wakeup. | * software events that would cause this vcpu to wakeup. | ||||
* | * | ||||
* These interrupts/events could have happened after the | * These interrupts/events could have happened after the | ||||
* vcpu returned from VMRUN() and before it acquired the | * vcpu returned from vmmops_run() and before it acquired the | ||||
* vcpu lock above. | * vcpu lock above. | ||||
*/ | */ | ||||
if (vm->rendezvous_func != NULL || vm->suspend || vcpu->reqidle) | if (vm->rendezvous_func != NULL || vm->suspend || vcpu->reqidle) | ||||
break; | break; | ||||
if (vm_nmi_pending(vm, vcpuid)) | if (vm_nmi_pending(vm, vcpuid)) | ||||
break; | break; | ||||
if (!intr_disabled) { | if (!intr_disabled) { | ||||
if (vm_extint_pending(vm, vcpuid) || | if (vm_extint_pending(vm, vcpuid) || | ||||
▲ Show 20 Lines • Show All 380 Lines • ▼ Show 20 Lines | restart: | ||||
tscval = rdtsc(); | tscval = rdtsc(); | ||||
pcb = PCPU_GET(curpcb); | pcb = PCPU_GET(curpcb); | ||||
set_pcb_flags(pcb, PCB_FULL_IRET); | set_pcb_flags(pcb, PCB_FULL_IRET); | ||||
restore_guest_fpustate(vcpu); | restore_guest_fpustate(vcpu); | ||||
vcpu_require_state(vm, vcpuid, VCPU_RUNNING); | vcpu_require_state(vm, vcpuid, VCPU_RUNNING); | ||||
error = VMRUN(vm->cookie, vcpuid, vcpu->nextrip, pmap, &evinfo); | error = vmmops_run(vm->cookie, vcpuid, vcpu->nextrip, pmap, &evinfo); | ||||
vcpu_require_state(vm, vcpuid, VCPU_FROZEN); | vcpu_require_state(vm, vcpuid, VCPU_FROZEN); | ||||
save_guest_fpustate(vcpu); | save_guest_fpustate(vcpu); | ||||
vmm_stat_incr(vm, vcpuid, VCPU_TOTAL_RUNTIME, rdtsc() - tscval); | vmm_stat_incr(vm, vcpuid, VCPU_TOTAL_RUNTIME, rdtsc() - tscval); | ||||
critical_exit(); | critical_exit(); | ||||
▲ Show 20 Lines • Show All 72 Lines • ▼ Show 20 Lines | if (state == VCPU_RUNNING) { | ||||
* instruction to be restarted. | * instruction to be restarted. | ||||
*/ | */ | ||||
vcpu->exitinfo.inst_length = 0; | vcpu->exitinfo.inst_length = 0; | ||||
VCPU_CTR1(vm, vcpuid, "restarting instruction at %#lx by " | VCPU_CTR1(vm, vcpuid, "restarting instruction at %#lx by " | ||||
"setting inst_length to zero", vcpu->exitinfo.rip); | "setting inst_length to zero", vcpu->exitinfo.rip); | ||||
} else if (state == VCPU_FROZEN) { | } else if (state == VCPU_FROZEN) { | ||||
/* | /* | ||||
* When a vcpu is "frozen" it is outside the critical section | * When a vcpu is "frozen" it is outside the critical section | ||||
* around VMRUN() and 'nextrip' points to the next instruction. | * around vmmops_run() and 'nextrip' points to the next | ||||
* Thus instruction restart is achieved by setting 'nextrip' | * instruction. Thus instruction restart is achieved by setting | ||||
* to the vcpu's %rip. | * 'nextrip' to the vcpu's %rip. | ||||
*/ | */ | ||||
error = vm_get_register(vm, vcpuid, VM_REG_GUEST_RIP, &rip); | error = vm_get_register(vm, vcpuid, VM_REG_GUEST_RIP, &rip); | ||||
KASSERT(!error, ("%s: error %d getting rip", __func__, error)); | KASSERT(!error, ("%s: error %d getting rip", __func__, error)); | ||||
VCPU_CTR2(vm, vcpuid, "restarting instruction by updating " | VCPU_CTR2(vm, vcpuid, "restarting instruction by updating " | ||||
"nextrip from %#lx to %#lx", vcpu->nextrip, rip); | "nextrip from %#lx to %#lx", vcpu->nextrip, rip); | ||||
vcpu->nextrip = rip; | vcpu->nextrip = rip; | ||||
} else { | } else { | ||||
panic("%s: invalid state %d", __func__, state); | panic("%s: invalid state %d", __func__, state); | ||||
▲ Show 20 Lines • Show All 388 Lines • ▼ Show 20 Lines | |||||
vm_get_capability(struct vm *vm, int vcpu, int type, int *retval) | vm_get_capability(struct vm *vm, int vcpu, int type, int *retval) | ||||
{ | { | ||||
if (vcpu < 0 || vcpu >= vm->maxcpus) | if (vcpu < 0 || vcpu >= vm->maxcpus) | ||||
return (EINVAL); | return (EINVAL); | ||||
if (type < 0 || type >= VM_CAP_MAX) | if (type < 0 || type >= VM_CAP_MAX) | ||||
return (EINVAL); | return (EINVAL); | ||||
return (VMGETCAP(vm->cookie, vcpu, type, retval)); | return (vmmops_getcap(vm->cookie, vcpu, type, retval)); | ||||
} | } | ||||
int | int | ||||
vm_set_capability(struct vm *vm, int vcpu, int type, int val) | vm_set_capability(struct vm *vm, int vcpu, int type, int val) | ||||
{ | { | ||||
if (vcpu < 0 || vcpu >= vm->maxcpus) | if (vcpu < 0 || vcpu >= vm->maxcpus) | ||||
return (EINVAL); | return (EINVAL); | ||||
if (type < 0 || type >= VM_CAP_MAX) | if (type < 0 || type >= VM_CAP_MAX) | ||||
return (EINVAL); | return (EINVAL); | ||||
return (VMSETCAP(vm->cookie, vcpu, type, val)); | return (vmmops_setcap(vm->cookie, vcpu, type, val)); | ||||
} | } | ||||
struct vlapic * | struct vlapic * | ||||
vm_lapic(struct vm *vm, int cpu) | vm_lapic(struct vm *vm, int cpu) | ||||
{ | { | ||||
return (vm->vcpu[cpu].vlapic); | return (vm->vcpu[cpu].vlapic); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 569 Lines • ▼ Show 20 Lines | |||||
static int | static int | ||||
vm_snapshot_vmcx(struct vm *vm, struct vm_snapshot_meta *meta) | vm_snapshot_vmcx(struct vm *vm, struct vm_snapshot_meta *meta) | ||||
{ | { | ||||
int i, error; | int i, error; | ||||
error = 0; | error = 0; | ||||
for (i = 0; i < VM_MAXCPU; i++) { | for (i = 0; i < VM_MAXCPU; i++) { | ||||
error = VM_SNAPSHOT_VMCX(vm->cookie, meta, i); | error = vmmops_vmcx_snapshot(vm->cookie, meta, i); | ||||
if (error != 0) { | if (error != 0) { | ||||
printf("%s: failed to snapshot vmcs/vmcb data for " | printf("%s: failed to snapshot vmcs/vmcb data for " | ||||
"vCPU: %d; error: %d\n", __func__, i, error); | "vCPU: %d; error: %d\n", __func__, i, error); | ||||
goto done; | goto done; | ||||
} | } | ||||
} | } | ||||
done: | done: | ||||
return (error); | return (error); | ||||
} | } | ||||
/* | /* | ||||
* Save kernel-side structures to user-space for snapshotting. | * Save kernel-side structures to user-space for snapshotting. | ||||
*/ | */ | ||||
int | int | ||||
vm_snapshot_req(struct vm *vm, struct vm_snapshot_meta *meta) | vm_snapshot_req(struct vm *vm, struct vm_snapshot_meta *meta) | ||||
{ | { | ||||
int ret = 0; | int ret = 0; | ||||
switch (meta->dev_req) { | switch (meta->dev_req) { | ||||
case STRUCT_VMX: | case STRUCT_VMX: | ||||
ret = VM_SNAPSHOT_VMI(vm->cookie, meta); | ret = vmmops_snapshot(vm->cookie, meta); | ||||
break; | break; | ||||
case STRUCT_VMCX: | case STRUCT_VMCX: | ||||
ret = vm_snapshot_vmcx(vm, meta); | ret = vm_snapshot_vmcx(vm, meta); | ||||
break; | break; | ||||
case STRUCT_VM: | case STRUCT_VM: | ||||
ret = vm_snapshot_vm(vm, meta); | ret = vm_snapshot_vm(vm, meta); | ||||
break; | break; | ||||
case STRUCT_VIOAPIC: | case STRUCT_VIOAPIC: | ||||
▲ Show 20 Lines • Show All 50 Lines • ▼ Show 20 Lines | vm_restore_time(struct vm *vm) | ||||
error = vhpet_restore_time(vm_hpet(vm)); | error = vhpet_restore_time(vm_hpet(vm)); | ||||
if (error) | if (error) | ||||
return (error); | return (error); | ||||
for (i = 0; i < nitems(vm->vcpu); i++) { | for (i = 0; i < nitems(vm->vcpu); i++) { | ||||
vcpu = &vm->vcpu[i]; | vcpu = &vm->vcpu[i]; | ||||
error = VM_RESTORE_TSC(vm->cookie, i, vcpu->tsc_offset - now); | error = vmmops_restore_tsc(vm->cookie, i, vcpu->tsc_offset - | ||||
now); | |||||
if (error) | if (error) | ||||
return (error); | return (error); | ||||
} | } | ||||
return (0); | return (0); | ||||
} | } | ||||
#endif | #endif |