1366f6083SPeter Grehan /*- 2366f6083SPeter Grehan * Copyright (c) 2011 NetApp, Inc. 3366f6083SPeter Grehan * All rights reserved. 4366f6083SPeter Grehan * 5366f6083SPeter Grehan * Redistribution and use in source and binary forms, with or without 6366f6083SPeter Grehan * modification, are permitted provided that the following conditions 7366f6083SPeter Grehan * are met: 8366f6083SPeter Grehan * 1. Redistributions of source code must retain the above copyright 9366f6083SPeter Grehan * notice, this list of conditions and the following disclaimer. 10366f6083SPeter Grehan * 2. Redistributions in binary form must reproduce the above copyright 11366f6083SPeter Grehan * notice, this list of conditions and the following disclaimer in the 12366f6083SPeter Grehan * documentation and/or other materials provided with the distribution. 13366f6083SPeter Grehan * 14366f6083SPeter Grehan * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND 15366f6083SPeter Grehan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16366f6083SPeter Grehan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17366f6083SPeter Grehan * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE 18366f6083SPeter Grehan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19366f6083SPeter Grehan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20366f6083SPeter Grehan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21366f6083SPeter Grehan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22366f6083SPeter Grehan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23366f6083SPeter Grehan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24366f6083SPeter Grehan * SUCH DAMAGE. 25366f6083SPeter Grehan * 26366f6083SPeter Grehan * $FreeBSD$ 27366f6083SPeter Grehan */ 28366f6083SPeter Grehan 29366f6083SPeter Grehan #include <sys/cdefs.h> 30366f6083SPeter Grehan __FBSDID("$FreeBSD$"); 31366f6083SPeter Grehan 32366f6083SPeter Grehan #include <sys/param.h> 3338f1b189SPeter Grehan #include <sys/systm.h> 34366f6083SPeter Grehan #include <sys/kernel.h> 35366f6083SPeter Grehan #include <sys/module.h> 36366f6083SPeter Grehan #include <sys/sysctl.h> 37366f6083SPeter Grehan #include <sys/malloc.h> 38366f6083SPeter Grehan #include <sys/pcpu.h> 39366f6083SPeter Grehan #include <sys/lock.h> 40366f6083SPeter Grehan #include <sys/mutex.h> 41366f6083SPeter Grehan #include <sys/proc.h> 42366f6083SPeter Grehan #include <sys/sched.h> 43366f6083SPeter Grehan #include <sys/smp.h> 44366f6083SPeter Grehan #include <sys/systm.h> 45366f6083SPeter Grehan 46366f6083SPeter Grehan #include <vm/vm.h> 47366f6083SPeter Grehan 48366f6083SPeter Grehan #include <machine/vm.h> 49366f6083SPeter Grehan #include <machine/pcb.h> 5075dd3366SNeel Natu #include <machine/smp.h> 5134a6b2d6SJohn Baldwin #include <x86/apicreg.h> 52366f6083SPeter Grehan 53366f6083SPeter Grehan #include <machine/vmm.h> 54b01c2033SNeel Natu #include "vmm_host.h" 55366f6083SPeter Grehan #include "vmm_mem.h" 56366f6083SPeter Grehan #include "vmm_util.h" 57366f6083SPeter Grehan #include <machine/vmm_dev.h> 58366f6083SPeter Grehan #include "vlapic.h" 59366f6083SPeter Grehan #include "vmm_msr.h" 60366f6083SPeter Grehan #include "vmm_ipi.h" 61366f6083SPeter Grehan #include "vmm_stat.h" 62f76fc5d4SNeel Natu #include "vmm_lapic.h" 63366f6083SPeter Grehan 64366f6083SPeter Grehan #include "io/ppt.h" 65366f6083SPeter Grehan #include "io/iommu.h" 66366f6083SPeter Grehan 67366f6083SPeter Grehan struct vlapic; 68366f6083SPeter Grehan 69366f6083SPeter Grehan struct vcpu { 70366f6083SPeter Grehan int flags; 7175dd3366SNeel Natu enum vcpu_state state; 7275dd3366SNeel Natu struct mtx mtx; 73366f6083SPeter Grehan int hostcpu; /* host cpuid this vcpu last ran on */ 74366f6083SPeter Grehan uint64_t guest_msrs[VMM_MSR_NUM]; 75366f6083SPeter Grehan struct vlapic *vlapic; 76366f6083SPeter Grehan int vcpuid; 7738f1b189SPeter Grehan struct savefpu *guestfpu; /* guest fpu state */ 78366f6083SPeter Grehan void *stats; 7998ed632cSNeel Natu struct vm_exit exitinfo; 80e9027382SNeel Natu enum x2apic_state x2apic_state; 81f352ff0cSNeel Natu int nmi_pending; 82366f6083SPeter Grehan }; 83366f6083SPeter Grehan 84f76fc5d4SNeel Natu #define vcpu_lock_init(v) mtx_init(&((v)->mtx), "vcpu lock", 0, MTX_SPIN) 85f76fc5d4SNeel Natu #define vcpu_lock(v) mtx_lock_spin(&((v)->mtx)) 86f76fc5d4SNeel Natu #define vcpu_unlock(v) mtx_unlock_spin(&((v)->mtx)) 8775dd3366SNeel Natu 88366f6083SPeter Grehan #define VM_MAX_MEMORY_SEGMENTS 2 89366f6083SPeter Grehan 90366f6083SPeter Grehan struct vm { 91366f6083SPeter Grehan void *cookie; /* processor-specific data */ 92366f6083SPeter Grehan void *iommu; /* iommu-specific data */ 93366f6083SPeter Grehan struct vcpu vcpu[VM_MAXCPU]; 94366f6083SPeter Grehan int num_mem_segs; 95366f6083SPeter Grehan struct vm_memory_segment mem_segs[VM_MAX_MEMORY_SEGMENTS]; 96366f6083SPeter Grehan char name[VM_MAX_NAMELEN]; 97366f6083SPeter Grehan 98366f6083SPeter Grehan /* 99a5615c90SPeter Grehan * Set of active vcpus. 100366f6083SPeter Grehan * An active vcpu is one that has been started implicitly (BSP) or 101366f6083SPeter Grehan * explicitly (AP) by sending it a startup ipi. 102366f6083SPeter Grehan */ 103a5615c90SPeter Grehan cpuset_t active_cpus; 104366f6083SPeter Grehan }; 105366f6083SPeter Grehan 106d5408b1dSNeel Natu static int vmm_initialized; 107d5408b1dSNeel Natu 108366f6083SPeter Grehan static struct vmm_ops *ops; 109366f6083SPeter Grehan #define VMM_INIT() (ops != NULL ? (*ops->init)() : 0) 110366f6083SPeter Grehan #define VMM_CLEANUP() (ops != NULL ? (*ops->cleanup)() : 0) 111366f6083SPeter Grehan 112366f6083SPeter Grehan #define VMINIT(vm) (ops != NULL ? (*ops->vminit)(vm): NULL) 11398ed632cSNeel Natu #define VMRUN(vmi, vcpu, rip) \ 11498ed632cSNeel Natu (ops != NULL ? (*ops->vmrun)(vmi, vcpu, rip) : ENXIO) 115366f6083SPeter Grehan #define VMCLEANUP(vmi) (ops != NULL ? (*ops->vmcleanup)(vmi) : NULL) 116bda273f2SNeel Natu #define VMMMAP_SET(vmi, gpa, hpa, len, attr, prot, spm) \ 117bda273f2SNeel Natu (ops != NULL ? \ 118bda273f2SNeel Natu (*ops->vmmmap_set)(vmi, gpa, hpa, len, attr, prot, spm) : \ 119bda273f2SNeel Natu ENXIO) 120bda273f2SNeel Natu #define VMMMAP_GET(vmi, gpa) \ 121bda273f2SNeel Natu (ops != NULL ? (*ops->vmmmap_get)(vmi, gpa) : ENXIO) 122366f6083SPeter Grehan #define VMGETREG(vmi, vcpu, num, retval) \ 123366f6083SPeter Grehan (ops != NULL ? (*ops->vmgetreg)(vmi, vcpu, num, retval) : ENXIO) 124366f6083SPeter Grehan #define VMSETREG(vmi, vcpu, num, val) \ 125366f6083SPeter Grehan (ops != NULL ? (*ops->vmsetreg)(vmi, vcpu, num, val) : ENXIO) 126366f6083SPeter Grehan #define VMGETDESC(vmi, vcpu, num, desc) \ 127366f6083SPeter Grehan (ops != NULL ? (*ops->vmgetdesc)(vmi, vcpu, num, desc) : ENXIO) 128366f6083SPeter Grehan #define VMSETDESC(vmi, vcpu, num, desc) \ 129366f6083SPeter Grehan (ops != NULL ? (*ops->vmsetdesc)(vmi, vcpu, num, desc) : ENXIO) 130366f6083SPeter Grehan #define VMINJECT(vmi, vcpu, type, vec, ec, ecv) \ 131366f6083SPeter Grehan (ops != NULL ? (*ops->vminject)(vmi, vcpu, type, vec, ec, ecv) : ENXIO) 132366f6083SPeter Grehan #define VMGETCAP(vmi, vcpu, num, retval) \ 133366f6083SPeter Grehan (ops != NULL ? (*ops->vmgetcap)(vmi, vcpu, num, retval) : ENXIO) 134366f6083SPeter Grehan #define VMSETCAP(vmi, vcpu, num, val) \ 135366f6083SPeter Grehan (ops != NULL ? (*ops->vmsetcap)(vmi, vcpu, num, val) : ENXIO) 136366f6083SPeter Grehan 137014a52f3SNeel Natu #define fpu_start_emulating() load_cr0(rcr0() | CR0_TS) 138014a52f3SNeel Natu #define fpu_stop_emulating() clts() 139366f6083SPeter Grehan 140366f6083SPeter Grehan static MALLOC_DEFINE(M_VM, "vm", "vm"); 141366f6083SPeter Grehan CTASSERT(VMM_MSR_NUM <= 64); /* msr_mask can keep track of up to 64 msrs */ 142366f6083SPeter Grehan 143366f6083SPeter Grehan /* statistics */ 14461592433SNeel Natu static VMM_STAT(VCPU_TOTAL_RUNTIME, "vcpu total runtime"); 145366f6083SPeter Grehan 146366f6083SPeter Grehan static void 147366f6083SPeter Grehan vcpu_cleanup(struct vcpu *vcpu) 148366f6083SPeter Grehan { 149366f6083SPeter Grehan vlapic_cleanup(vcpu->vlapic); 150366f6083SPeter Grehan vmm_stat_free(vcpu->stats); 15138f1b189SPeter Grehan fpu_save_area_free(vcpu->guestfpu); 152366f6083SPeter Grehan } 153366f6083SPeter Grehan 154366f6083SPeter Grehan static void 155366f6083SPeter Grehan vcpu_init(struct vm *vm, uint32_t vcpu_id) 156366f6083SPeter Grehan { 157366f6083SPeter Grehan struct vcpu *vcpu; 158366f6083SPeter Grehan 159366f6083SPeter Grehan vcpu = &vm->vcpu[vcpu_id]; 160366f6083SPeter Grehan 16175dd3366SNeel Natu vcpu_lock_init(vcpu); 16275dd3366SNeel Natu vcpu->hostcpu = NOCPU; 163366f6083SPeter Grehan vcpu->vcpuid = vcpu_id; 164366f6083SPeter Grehan vcpu->vlapic = vlapic_init(vm, vcpu_id); 16573820fb0SNeel Natu vm_set_x2apic_state(vm, vcpu_id, X2APIC_ENABLED); 16638f1b189SPeter Grehan vcpu->guestfpu = fpu_save_area_alloc(); 16738f1b189SPeter Grehan fpu_save_area_reset(vcpu->guestfpu); 168366f6083SPeter Grehan vcpu->stats = vmm_stat_alloc(); 169366f6083SPeter Grehan } 170366f6083SPeter Grehan 17198ed632cSNeel Natu struct vm_exit * 17298ed632cSNeel Natu vm_exitinfo(struct vm *vm, int cpuid) 17398ed632cSNeel Natu { 17498ed632cSNeel Natu struct vcpu *vcpu; 17598ed632cSNeel Natu 17698ed632cSNeel Natu if (cpuid < 0 || cpuid >= VM_MAXCPU) 17798ed632cSNeel Natu panic("vm_exitinfo: invalid cpuid %d", cpuid); 17898ed632cSNeel Natu 17998ed632cSNeel Natu vcpu = &vm->vcpu[cpuid]; 18098ed632cSNeel Natu 18198ed632cSNeel Natu return (&vcpu->exitinfo); 18298ed632cSNeel Natu } 18398ed632cSNeel Natu 184366f6083SPeter Grehan static int 185366f6083SPeter Grehan vmm_init(void) 186366f6083SPeter Grehan { 187366f6083SPeter Grehan int error; 188366f6083SPeter Grehan 189b01c2033SNeel Natu vmm_host_state_init(); 190366f6083SPeter Grehan vmm_ipi_init(); 191366f6083SPeter Grehan 192366f6083SPeter Grehan error = vmm_mem_init(); 193366f6083SPeter Grehan if (error) 194366f6083SPeter Grehan return (error); 195366f6083SPeter Grehan 196366f6083SPeter Grehan if (vmm_is_intel()) 197366f6083SPeter Grehan ops = &vmm_ops_intel; 198366f6083SPeter Grehan else if (vmm_is_amd()) 199366f6083SPeter Grehan ops = &vmm_ops_amd; 200366f6083SPeter Grehan else 201366f6083SPeter Grehan return (ENXIO); 202366f6083SPeter Grehan 203366f6083SPeter Grehan vmm_msr_init(); 204366f6083SPeter Grehan 205366f6083SPeter Grehan return (VMM_INIT()); 206366f6083SPeter Grehan } 207366f6083SPeter Grehan 208366f6083SPeter Grehan static int 209366f6083SPeter Grehan vmm_handler(module_t mod, int what, void *arg) 210366f6083SPeter Grehan { 211366f6083SPeter Grehan int error; 212366f6083SPeter Grehan 213366f6083SPeter Grehan switch (what) { 214366f6083SPeter Grehan case MOD_LOAD: 215366f6083SPeter Grehan vmmdev_init(); 216366f6083SPeter Grehan iommu_init(); 217366f6083SPeter Grehan error = vmm_init(); 218d5408b1dSNeel Natu if (error == 0) 219d5408b1dSNeel Natu vmm_initialized = 1; 220366f6083SPeter Grehan break; 221366f6083SPeter Grehan case MOD_UNLOAD: 222cdc5b9e7SNeel Natu error = vmmdev_cleanup(); 223cdc5b9e7SNeel Natu if (error == 0) { 224366f6083SPeter Grehan iommu_cleanup(); 225366f6083SPeter Grehan vmm_ipi_cleanup(); 226366f6083SPeter Grehan error = VMM_CLEANUP(); 227cdc5b9e7SNeel Natu } 228d5408b1dSNeel Natu vmm_initialized = 0; 229366f6083SPeter Grehan break; 230366f6083SPeter Grehan default: 231366f6083SPeter Grehan error = 0; 232366f6083SPeter Grehan break; 233366f6083SPeter Grehan } 234366f6083SPeter Grehan return (error); 235366f6083SPeter Grehan } 236366f6083SPeter Grehan 237366f6083SPeter Grehan static moduledata_t vmm_kmod = { 238366f6083SPeter Grehan "vmm", 239366f6083SPeter Grehan vmm_handler, 240366f6083SPeter Grehan NULL 241366f6083SPeter Grehan }; 242366f6083SPeter Grehan 243366f6083SPeter Grehan /* 244e3f0800bSNeel Natu * vmm initialization has the following dependencies: 245e3f0800bSNeel Natu * 246e3f0800bSNeel Natu * - iommu initialization must happen after the pci passthru driver has had 247e3f0800bSNeel Natu * a chance to attach to any passthru devices (after SI_SUB_CONFIGURE). 248e3f0800bSNeel Natu * 249e3f0800bSNeel Natu * - VT-x initialization requires smp_rendezvous() and therefore must happen 250e3f0800bSNeel Natu * after SMP is fully functional (after SI_SUB_SMP). 251366f6083SPeter Grehan */ 252e3f0800bSNeel Natu DECLARE_MODULE(vmm, vmm_kmod, SI_SUB_SMP + 1, SI_ORDER_ANY); 253366f6083SPeter Grehan MODULE_VERSION(vmm, 1); 254366f6083SPeter Grehan 255366f6083SPeter Grehan SYSCTL_NODE(_hw, OID_AUTO, vmm, CTLFLAG_RW, NULL, NULL); 256366f6083SPeter Grehan 257d5408b1dSNeel Natu int 258d5408b1dSNeel Natu vm_create(const char *name, struct vm **retvm) 259366f6083SPeter Grehan { 260366f6083SPeter Grehan int i; 261366f6083SPeter Grehan struct vm *vm; 262366f6083SPeter Grehan vm_paddr_t maxaddr; 263366f6083SPeter Grehan 264366f6083SPeter Grehan const int BSP = 0; 265366f6083SPeter Grehan 266d5408b1dSNeel Natu /* 267d5408b1dSNeel Natu * If vmm.ko could not be successfully initialized then don't attempt 268d5408b1dSNeel Natu * to create the virtual machine. 269d5408b1dSNeel Natu */ 270d5408b1dSNeel Natu if (!vmm_initialized) 271d5408b1dSNeel Natu return (ENXIO); 272d5408b1dSNeel Natu 273366f6083SPeter Grehan if (name == NULL || strlen(name) >= VM_MAX_NAMELEN) 274d5408b1dSNeel Natu return (EINVAL); 275366f6083SPeter Grehan 276366f6083SPeter Grehan vm = malloc(sizeof(struct vm), M_VM, M_WAITOK | M_ZERO); 277366f6083SPeter Grehan strcpy(vm->name, name); 278366f6083SPeter Grehan vm->cookie = VMINIT(vm); 279366f6083SPeter Grehan 280366f6083SPeter Grehan for (i = 0; i < VM_MAXCPU; i++) { 281366f6083SPeter Grehan vcpu_init(vm, i); 282366f6083SPeter Grehan guest_msrs_init(vm, i); 283366f6083SPeter Grehan } 284366f6083SPeter Grehan 285366f6083SPeter Grehan maxaddr = vmm_mem_maxaddr(); 286366f6083SPeter Grehan vm->iommu = iommu_create_domain(maxaddr); 287366f6083SPeter Grehan vm_activate_cpu(vm, BSP); 288366f6083SPeter Grehan 289d5408b1dSNeel Natu *retvm = vm; 290d5408b1dSNeel Natu return (0); 291366f6083SPeter Grehan } 292366f6083SPeter Grehan 293f7d51510SNeel Natu static void 294f7d51510SNeel Natu vm_free_mem_seg(struct vm *vm, struct vm_memory_segment *seg) 295f7d51510SNeel Natu { 296f7d51510SNeel Natu size_t len; 297f7d51510SNeel Natu vm_paddr_t hpa; 2987ce04d0aSNeel Natu void *host_domain; 2997ce04d0aSNeel Natu 3007ce04d0aSNeel Natu host_domain = iommu_host_domain(); 301f7d51510SNeel Natu 302f7d51510SNeel Natu len = 0; 303f7d51510SNeel Natu while (len < seg->len) { 304f7d51510SNeel Natu hpa = vm_gpa2hpa(vm, seg->gpa + len, PAGE_SIZE); 305f7d51510SNeel Natu if (hpa == (vm_paddr_t)-1) { 306f7d51510SNeel Natu panic("vm_free_mem_segs: cannot free hpa " 307f7d51510SNeel Natu "associated with gpa 0x%016lx", seg->gpa + len); 308f7d51510SNeel Natu } 309f7d51510SNeel Natu 3107ce04d0aSNeel Natu /* 3117ce04d0aSNeel Natu * Remove the 'gpa' to 'hpa' mapping in VMs domain. 3127ce04d0aSNeel Natu * And resurrect the 1:1 mapping for 'hpa' in 'host_domain'. 3137ce04d0aSNeel Natu */ 3147ce04d0aSNeel Natu iommu_remove_mapping(vm->iommu, seg->gpa + len, PAGE_SIZE); 3157ce04d0aSNeel Natu iommu_create_mapping(host_domain, hpa, hpa, PAGE_SIZE); 3167ce04d0aSNeel Natu 317f7d51510SNeel Natu vmm_mem_free(hpa, PAGE_SIZE); 318f7d51510SNeel Natu 319f7d51510SNeel Natu len += PAGE_SIZE; 320f7d51510SNeel Natu } 321f7d51510SNeel Natu 3227ce04d0aSNeel Natu /* 3237ce04d0aSNeel Natu * Invalidate cached translations associated with 'vm->iommu' since 3247ce04d0aSNeel Natu * we have now moved some pages from it. 3257ce04d0aSNeel Natu */ 3267ce04d0aSNeel Natu iommu_invalidate_tlb(vm->iommu); 3277ce04d0aSNeel Natu 328f7d51510SNeel Natu bzero(seg, sizeof(struct vm_memory_segment)); 329f7d51510SNeel Natu } 330f7d51510SNeel Natu 331366f6083SPeter Grehan void 332366f6083SPeter Grehan vm_destroy(struct vm *vm) 333366f6083SPeter Grehan { 334366f6083SPeter Grehan int i; 335366f6083SPeter Grehan 336366f6083SPeter Grehan ppt_unassign_all(vm); 337366f6083SPeter Grehan 338366f6083SPeter Grehan for (i = 0; i < vm->num_mem_segs; i++) 339f7d51510SNeel Natu vm_free_mem_seg(vm, &vm->mem_segs[i]); 340f7d51510SNeel Natu 341f7d51510SNeel Natu vm->num_mem_segs = 0; 342366f6083SPeter Grehan 343366f6083SPeter Grehan for (i = 0; i < VM_MAXCPU; i++) 344366f6083SPeter Grehan vcpu_cleanup(&vm->vcpu[i]); 345366f6083SPeter Grehan 346366f6083SPeter Grehan iommu_destroy_domain(vm->iommu); 347366f6083SPeter Grehan 348366f6083SPeter Grehan VMCLEANUP(vm->cookie); 349366f6083SPeter Grehan 350366f6083SPeter Grehan free(vm, M_VM); 351366f6083SPeter Grehan } 352366f6083SPeter Grehan 353366f6083SPeter Grehan const char * 354366f6083SPeter Grehan vm_name(struct vm *vm) 355366f6083SPeter Grehan { 356366f6083SPeter Grehan return (vm->name); 357366f6083SPeter Grehan } 358366f6083SPeter Grehan 359366f6083SPeter Grehan int 360366f6083SPeter Grehan vm_map_mmio(struct vm *vm, vm_paddr_t gpa, size_t len, vm_paddr_t hpa) 361366f6083SPeter Grehan { 362366f6083SPeter Grehan const boolean_t spok = TRUE; /* superpage mappings are ok */ 363366f6083SPeter Grehan 364bda273f2SNeel Natu return (VMMMAP_SET(vm->cookie, gpa, hpa, len, VM_MEMATTR_UNCACHEABLE, 365366f6083SPeter Grehan VM_PROT_RW, spok)); 366366f6083SPeter Grehan } 367366f6083SPeter Grehan 368366f6083SPeter Grehan int 369366f6083SPeter Grehan vm_unmap_mmio(struct vm *vm, vm_paddr_t gpa, size_t len) 370366f6083SPeter Grehan { 371366f6083SPeter Grehan const boolean_t spok = TRUE; /* superpage mappings are ok */ 372366f6083SPeter Grehan 373bda273f2SNeel Natu return (VMMMAP_SET(vm->cookie, gpa, 0, len, 0, 374366f6083SPeter Grehan VM_PROT_NONE, spok)); 375366f6083SPeter Grehan } 376366f6083SPeter Grehan 377341f19c9SNeel Natu /* 378341f19c9SNeel Natu * Returns TRUE if 'gpa' is available for allocation and FALSE otherwise 379341f19c9SNeel Natu */ 380341f19c9SNeel Natu static boolean_t 381341f19c9SNeel Natu vm_gpa_available(struct vm *vm, vm_paddr_t gpa) 382366f6083SPeter Grehan { 383341f19c9SNeel Natu int i; 384341f19c9SNeel Natu vm_paddr_t gpabase, gpalimit; 385341f19c9SNeel Natu 386341f19c9SNeel Natu if (gpa & PAGE_MASK) 387341f19c9SNeel Natu panic("vm_gpa_available: gpa (0x%016lx) not page aligned", gpa); 388341f19c9SNeel Natu 389341f19c9SNeel Natu for (i = 0; i < vm->num_mem_segs; i++) { 390341f19c9SNeel Natu gpabase = vm->mem_segs[i].gpa; 391341f19c9SNeel Natu gpalimit = gpabase + vm->mem_segs[i].len; 392341f19c9SNeel Natu if (gpa >= gpabase && gpa < gpalimit) 393341f19c9SNeel Natu return (FALSE); 394341f19c9SNeel Natu } 395341f19c9SNeel Natu 396341f19c9SNeel Natu return (TRUE); 397341f19c9SNeel Natu } 398341f19c9SNeel Natu 399341f19c9SNeel Natu int 400341f19c9SNeel Natu vm_malloc(struct vm *vm, vm_paddr_t gpa, size_t len) 401341f19c9SNeel Natu { 402341f19c9SNeel Natu int error, available, allocated; 403f7d51510SNeel Natu struct vm_memory_segment *seg; 404341f19c9SNeel Natu vm_paddr_t g, hpa; 4057ce04d0aSNeel Natu void *host_domain; 406366f6083SPeter Grehan 407366f6083SPeter Grehan const boolean_t spok = TRUE; /* superpage mappings are ok */ 408366f6083SPeter Grehan 409341f19c9SNeel Natu if ((gpa & PAGE_MASK) || (len & PAGE_MASK) || len == 0) 410341f19c9SNeel Natu return (EINVAL); 411341f19c9SNeel Natu 412341f19c9SNeel Natu available = allocated = 0; 413341f19c9SNeel Natu g = gpa; 414341f19c9SNeel Natu while (g < gpa + len) { 415341f19c9SNeel Natu if (vm_gpa_available(vm, g)) 416341f19c9SNeel Natu available++; 417341f19c9SNeel Natu else 418341f19c9SNeel Natu allocated++; 419341f19c9SNeel Natu 420341f19c9SNeel Natu g += PAGE_SIZE; 421341f19c9SNeel Natu } 422341f19c9SNeel Natu 423366f6083SPeter Grehan /* 424341f19c9SNeel Natu * If there are some allocated and some available pages in the address 425341f19c9SNeel Natu * range then it is an error. 426366f6083SPeter Grehan */ 427341f19c9SNeel Natu if (allocated && available) 428341f19c9SNeel Natu return (EINVAL); 429341f19c9SNeel Natu 430341f19c9SNeel Natu /* 431341f19c9SNeel Natu * If the entire address range being requested has already been 432341f19c9SNeel Natu * allocated then there isn't anything more to do. 433341f19c9SNeel Natu */ 434341f19c9SNeel Natu if (allocated && available == 0) 435341f19c9SNeel Natu return (0); 436366f6083SPeter Grehan 437366f6083SPeter Grehan if (vm->num_mem_segs >= VM_MAX_MEMORY_SEGMENTS) 438366f6083SPeter Grehan return (E2BIG); 439366f6083SPeter Grehan 4407ce04d0aSNeel Natu host_domain = iommu_host_domain(); 4417ce04d0aSNeel Natu 442f7d51510SNeel Natu seg = &vm->mem_segs[vm->num_mem_segs]; 443366f6083SPeter Grehan 4447ce04d0aSNeel Natu error = 0; 445f7d51510SNeel Natu seg->gpa = gpa; 446f7d51510SNeel Natu seg->len = 0; 447f7d51510SNeel Natu while (seg->len < len) { 448f7d51510SNeel Natu hpa = vmm_mem_alloc(PAGE_SIZE); 449f7d51510SNeel Natu if (hpa == 0) { 450f7d51510SNeel Natu error = ENOMEM; 451f7d51510SNeel Natu break; 452f7d51510SNeel Natu } 453f7d51510SNeel Natu 454f7d51510SNeel Natu error = VMMMAP_SET(vm->cookie, gpa + seg->len, hpa, PAGE_SIZE, 455f7d51510SNeel Natu VM_MEMATTR_WRITE_BACK, VM_PROT_ALL, spok); 456f7d51510SNeel Natu if (error) 457f7d51510SNeel Natu break; 458f7d51510SNeel Natu 4597ce04d0aSNeel Natu /* 4607ce04d0aSNeel Natu * Remove the 1:1 mapping for 'hpa' from the 'host_domain'. 4617ce04d0aSNeel Natu * Add mapping for 'gpa + seg->len' to 'hpa' in the VMs domain. 4627ce04d0aSNeel Natu */ 4637ce04d0aSNeel Natu iommu_remove_mapping(host_domain, hpa, PAGE_SIZE); 464f7d51510SNeel Natu iommu_create_mapping(vm->iommu, gpa + seg->len, hpa, PAGE_SIZE); 465f7d51510SNeel Natu 466f7d51510SNeel Natu seg->len += PAGE_SIZE; 467f7d51510SNeel Natu } 468f7d51510SNeel Natu 4697ce04d0aSNeel Natu if (error) { 470f7d51510SNeel Natu vm_free_mem_seg(vm, seg); 471366f6083SPeter Grehan return (error); 472366f6083SPeter Grehan } 473366f6083SPeter Grehan 4747ce04d0aSNeel Natu /* 4757ce04d0aSNeel Natu * Invalidate cached translations associated with 'host_domain' since 4767ce04d0aSNeel Natu * we have now moved some pages from it. 4777ce04d0aSNeel Natu */ 4787ce04d0aSNeel Natu iommu_invalidate_tlb(host_domain); 4797ce04d0aSNeel Natu 480366f6083SPeter Grehan vm->num_mem_segs++; 481341f19c9SNeel Natu 482366f6083SPeter Grehan return (0); 483366f6083SPeter Grehan } 484366f6083SPeter Grehan 485366f6083SPeter Grehan vm_paddr_t 486366f6083SPeter Grehan vm_gpa2hpa(struct vm *vm, vm_paddr_t gpa, size_t len) 487366f6083SPeter Grehan { 4884db4fb2cSNeel Natu vm_paddr_t nextpage; 4894db4fb2cSNeel Natu 4904db4fb2cSNeel Natu nextpage = rounddown(gpa + PAGE_SIZE, PAGE_SIZE); 4914db4fb2cSNeel Natu if (len > nextpage - gpa) 4924db4fb2cSNeel Natu panic("vm_gpa2hpa: invalid gpa/len: 0x%016lx/%lu", gpa, len); 493366f6083SPeter Grehan 494bda273f2SNeel Natu return (VMMMAP_GET(vm->cookie, gpa)); 495366f6083SPeter Grehan } 496366f6083SPeter Grehan 497366f6083SPeter Grehan int 498366f6083SPeter Grehan vm_gpabase2memseg(struct vm *vm, vm_paddr_t gpabase, 499366f6083SPeter Grehan struct vm_memory_segment *seg) 500366f6083SPeter Grehan { 501366f6083SPeter Grehan int i; 502366f6083SPeter Grehan 503366f6083SPeter Grehan for (i = 0; i < vm->num_mem_segs; i++) { 504366f6083SPeter Grehan if (gpabase == vm->mem_segs[i].gpa) { 505366f6083SPeter Grehan *seg = vm->mem_segs[i]; 506366f6083SPeter Grehan return (0); 507366f6083SPeter Grehan } 508366f6083SPeter Grehan } 509366f6083SPeter Grehan return (-1); 510366f6083SPeter Grehan } 511366f6083SPeter Grehan 512366f6083SPeter Grehan int 513366f6083SPeter Grehan vm_get_register(struct vm *vm, int vcpu, int reg, uint64_t *retval) 514366f6083SPeter Grehan { 515366f6083SPeter Grehan 516366f6083SPeter Grehan if (vcpu < 0 || vcpu >= VM_MAXCPU) 517366f6083SPeter Grehan return (EINVAL); 518366f6083SPeter Grehan 519366f6083SPeter Grehan if (reg >= VM_REG_LAST) 520366f6083SPeter Grehan return (EINVAL); 521366f6083SPeter Grehan 522366f6083SPeter Grehan return (VMGETREG(vm->cookie, vcpu, reg, retval)); 523366f6083SPeter Grehan } 524366f6083SPeter Grehan 525366f6083SPeter Grehan int 526366f6083SPeter Grehan vm_set_register(struct vm *vm, int vcpu, int reg, uint64_t val) 527366f6083SPeter Grehan { 528366f6083SPeter Grehan 529366f6083SPeter Grehan if (vcpu < 0 || vcpu >= VM_MAXCPU) 530366f6083SPeter Grehan return (EINVAL); 531366f6083SPeter Grehan 532366f6083SPeter Grehan if (reg >= VM_REG_LAST) 533366f6083SPeter Grehan return (EINVAL); 534366f6083SPeter Grehan 535366f6083SPeter Grehan return (VMSETREG(vm->cookie, vcpu, reg, val)); 536366f6083SPeter Grehan } 537366f6083SPeter Grehan 538366f6083SPeter Grehan static boolean_t 539366f6083SPeter Grehan is_descriptor_table(int reg) 540366f6083SPeter Grehan { 541366f6083SPeter Grehan 542366f6083SPeter Grehan switch (reg) { 543366f6083SPeter Grehan case VM_REG_GUEST_IDTR: 544366f6083SPeter Grehan case VM_REG_GUEST_GDTR: 545366f6083SPeter Grehan return (TRUE); 546366f6083SPeter Grehan default: 547366f6083SPeter Grehan return (FALSE); 548366f6083SPeter Grehan } 549366f6083SPeter Grehan } 550366f6083SPeter Grehan 551366f6083SPeter Grehan static boolean_t 552366f6083SPeter Grehan is_segment_register(int reg) 553366f6083SPeter Grehan { 554366f6083SPeter Grehan 555366f6083SPeter Grehan switch (reg) { 556366f6083SPeter Grehan case VM_REG_GUEST_ES: 557366f6083SPeter Grehan case VM_REG_GUEST_CS: 558366f6083SPeter Grehan case VM_REG_GUEST_SS: 559366f6083SPeter Grehan case VM_REG_GUEST_DS: 560366f6083SPeter Grehan case VM_REG_GUEST_FS: 561366f6083SPeter Grehan case VM_REG_GUEST_GS: 562366f6083SPeter Grehan case VM_REG_GUEST_TR: 563366f6083SPeter Grehan case VM_REG_GUEST_LDTR: 564366f6083SPeter Grehan return (TRUE); 565366f6083SPeter Grehan default: 566366f6083SPeter Grehan return (FALSE); 567366f6083SPeter Grehan } 568366f6083SPeter Grehan } 569366f6083SPeter Grehan 570366f6083SPeter Grehan int 571366f6083SPeter Grehan vm_get_seg_desc(struct vm *vm, int vcpu, int reg, 572366f6083SPeter Grehan struct seg_desc *desc) 573366f6083SPeter Grehan { 574366f6083SPeter Grehan 575366f6083SPeter Grehan if (vcpu < 0 || vcpu >= VM_MAXCPU) 576366f6083SPeter Grehan return (EINVAL); 577366f6083SPeter Grehan 578366f6083SPeter Grehan if (!is_segment_register(reg) && !is_descriptor_table(reg)) 579366f6083SPeter Grehan return (EINVAL); 580366f6083SPeter Grehan 581366f6083SPeter Grehan return (VMGETDESC(vm->cookie, vcpu, reg, desc)); 582366f6083SPeter Grehan } 583366f6083SPeter Grehan 584366f6083SPeter Grehan int 585366f6083SPeter Grehan vm_set_seg_desc(struct vm *vm, int vcpu, int reg, 586366f6083SPeter Grehan struct seg_desc *desc) 587366f6083SPeter Grehan { 588366f6083SPeter Grehan if (vcpu < 0 || vcpu >= VM_MAXCPU) 589366f6083SPeter Grehan return (EINVAL); 590366f6083SPeter Grehan 591366f6083SPeter Grehan if (!is_segment_register(reg) && !is_descriptor_table(reg)) 592366f6083SPeter Grehan return (EINVAL); 593366f6083SPeter Grehan 594366f6083SPeter Grehan return (VMSETDESC(vm->cookie, vcpu, reg, desc)); 595366f6083SPeter Grehan } 596366f6083SPeter Grehan 597366f6083SPeter Grehan static void 598366f6083SPeter Grehan restore_guest_fpustate(struct vcpu *vcpu) 599366f6083SPeter Grehan { 600366f6083SPeter Grehan 60138f1b189SPeter Grehan /* flush host state to the pcb */ 60238f1b189SPeter Grehan fpuexit(curthread); 603bd8572e0SNeel Natu 604bd8572e0SNeel Natu /* restore guest FPU state */ 605366f6083SPeter Grehan fpu_stop_emulating(); 60638f1b189SPeter Grehan fpurestore(vcpu->guestfpu); 607bd8572e0SNeel Natu 608bd8572e0SNeel Natu /* 609bd8572e0SNeel Natu * The FPU is now "dirty" with the guest's state so turn on emulation 610bd8572e0SNeel Natu * to trap any access to the FPU by the host. 611bd8572e0SNeel Natu */ 612bd8572e0SNeel Natu fpu_start_emulating(); 613366f6083SPeter Grehan } 614366f6083SPeter Grehan 615366f6083SPeter Grehan static void 616366f6083SPeter Grehan save_guest_fpustate(struct vcpu *vcpu) 617366f6083SPeter Grehan { 618366f6083SPeter Grehan 619bd8572e0SNeel Natu if ((rcr0() & CR0_TS) == 0) 620bd8572e0SNeel Natu panic("fpu emulation not enabled in host!"); 621bd8572e0SNeel Natu 622bd8572e0SNeel Natu /* save guest FPU state */ 623bd8572e0SNeel Natu fpu_stop_emulating(); 62438f1b189SPeter Grehan fpusave(vcpu->guestfpu); 625366f6083SPeter Grehan fpu_start_emulating(); 626366f6083SPeter Grehan } 627366f6083SPeter Grehan 62861592433SNeel Natu static VMM_STAT(VCPU_IDLE_TICKS, "number of ticks vcpu was idle"); 629f76fc5d4SNeel Natu 630366f6083SPeter Grehan int 631366f6083SPeter Grehan vm_run(struct vm *vm, struct vm_run *vmrun) 632366f6083SPeter Grehan { 633f76fc5d4SNeel Natu int error, vcpuid, sleepticks, t; 634366f6083SPeter Grehan struct vcpu *vcpu; 635366f6083SPeter Grehan struct pcb *pcb; 636f76fc5d4SNeel Natu uint64_t tscval, rip; 637f76fc5d4SNeel Natu struct vm_exit *vme; 638366f6083SPeter Grehan 639366f6083SPeter Grehan vcpuid = vmrun->cpuid; 640366f6083SPeter Grehan 641366f6083SPeter Grehan if (vcpuid < 0 || vcpuid >= VM_MAXCPU) 642366f6083SPeter Grehan return (EINVAL); 643366f6083SPeter Grehan 644366f6083SPeter Grehan vcpu = &vm->vcpu[vcpuid]; 645f76fc5d4SNeel Natu vme = &vmrun->vm_exit; 646f76fc5d4SNeel Natu rip = vmrun->rip; 647f76fc5d4SNeel Natu restart: 648366f6083SPeter Grehan critical_enter(); 649366f6083SPeter Grehan 650366f6083SPeter Grehan tscval = rdtsc(); 651366f6083SPeter Grehan 652366f6083SPeter Grehan pcb = PCPU_GET(curpcb); 65334a6b2d6SJohn Baldwin set_pcb_flags(pcb, PCB_FULL_IRET); 654366f6083SPeter Grehan 655366f6083SPeter Grehan restore_guest_msrs(vm, vcpuid); 656366f6083SPeter Grehan restore_guest_fpustate(vcpu); 65775dd3366SNeel Natu 65875dd3366SNeel Natu vcpu->hostcpu = curcpu; 659f76fc5d4SNeel Natu error = VMRUN(vm->cookie, vcpuid, rip); 66075dd3366SNeel Natu vcpu->hostcpu = NOCPU; 66175dd3366SNeel Natu 662366f6083SPeter Grehan save_guest_fpustate(vcpu); 663366f6083SPeter Grehan restore_host_msrs(vm, vcpuid); 664366f6083SPeter Grehan 665366f6083SPeter Grehan vmm_stat_incr(vm, vcpuid, VCPU_TOTAL_RUNTIME, rdtsc() - tscval); 666366f6083SPeter Grehan 66798ed632cSNeel Natu /* copy the exit information */ 668f76fc5d4SNeel Natu bcopy(&vcpu->exitinfo, vme, sizeof(struct vm_exit)); 66998ed632cSNeel Natu 670366f6083SPeter Grehan critical_exit(); 671366f6083SPeter Grehan 672f76fc5d4SNeel Natu /* 673f76fc5d4SNeel Natu * Oblige the guest's desire to 'hlt' by sleeping until the vcpu 674f76fc5d4SNeel Natu * is ready to run. 675f76fc5d4SNeel Natu */ 676f76fc5d4SNeel Natu if (error == 0 && vme->exitcode == VM_EXITCODE_HLT) { 677f76fc5d4SNeel Natu vcpu_lock(vcpu); 678f76fc5d4SNeel Natu 679f76fc5d4SNeel Natu /* 680f76fc5d4SNeel Natu * Figure out the number of host ticks until the next apic 681f76fc5d4SNeel Natu * timer interrupt in the guest. 682f76fc5d4SNeel Natu */ 683f76fc5d4SNeel Natu sleepticks = lapic_timer_tick(vm, vcpuid); 684f76fc5d4SNeel Natu 685f76fc5d4SNeel Natu /* 686f76fc5d4SNeel Natu * If the guest local apic timer is disabled then sleep for 687f76fc5d4SNeel Natu * a long time but not forever. 688f76fc5d4SNeel Natu */ 689f76fc5d4SNeel Natu if (sleepticks < 0) 690f76fc5d4SNeel Natu sleepticks = hz; 691f76fc5d4SNeel Natu 692f76fc5d4SNeel Natu /* 693f76fc5d4SNeel Natu * Do a final check for pending NMI or interrupts before 694f76fc5d4SNeel Natu * really putting this thread to sleep. 695f76fc5d4SNeel Natu * 696f76fc5d4SNeel Natu * These interrupts could have happened any time after we 697f76fc5d4SNeel Natu * returned from VMRUN() and before we grabbed the vcpu lock. 698f76fc5d4SNeel Natu */ 699f76fc5d4SNeel Natu if (!vm_nmi_pending(vm, vcpuid) && 700f76fc5d4SNeel Natu lapic_pending_intr(vm, vcpuid) < 0) { 701f76fc5d4SNeel Natu if (sleepticks <= 0) 702f76fc5d4SNeel Natu panic("invalid sleepticks %d", sleepticks); 703f76fc5d4SNeel Natu t = ticks; 704f76fc5d4SNeel Natu msleep_spin(vcpu, &vcpu->mtx, "vmidle", sleepticks); 705f76fc5d4SNeel Natu vmm_stat_incr(vm, vcpuid, VCPU_IDLE_TICKS, ticks - t); 706f76fc5d4SNeel Natu } 707f76fc5d4SNeel Natu 708f76fc5d4SNeel Natu vcpu_unlock(vcpu); 709f76fc5d4SNeel Natu 710f76fc5d4SNeel Natu rip = vme->rip + vme->inst_length; 711f76fc5d4SNeel Natu goto restart; 712f76fc5d4SNeel Natu } 713f76fc5d4SNeel Natu 714366f6083SPeter Grehan return (error); 715366f6083SPeter Grehan } 716366f6083SPeter Grehan 717366f6083SPeter Grehan int 718366f6083SPeter Grehan vm_inject_event(struct vm *vm, int vcpuid, int type, 719366f6083SPeter Grehan int vector, uint32_t code, int code_valid) 720366f6083SPeter Grehan { 721366f6083SPeter Grehan if (vcpuid < 0 || vcpuid >= VM_MAXCPU) 722366f6083SPeter Grehan return (EINVAL); 723366f6083SPeter Grehan 724366f6083SPeter Grehan if ((type > VM_EVENT_NONE && type < VM_EVENT_MAX) == 0) 725366f6083SPeter Grehan return (EINVAL); 726366f6083SPeter Grehan 727366f6083SPeter Grehan if (vector < 0 || vector > 255) 728366f6083SPeter Grehan return (EINVAL); 729366f6083SPeter Grehan 730366f6083SPeter Grehan return (VMINJECT(vm->cookie, vcpuid, type, vector, code, code_valid)); 731366f6083SPeter Grehan } 732366f6083SPeter Grehan 73361592433SNeel Natu static VMM_STAT(VCPU_NMI_COUNT, "number of NMIs delivered to vcpu"); 734366f6083SPeter Grehan 735f352ff0cSNeel Natu int 736f352ff0cSNeel Natu vm_inject_nmi(struct vm *vm, int vcpuid) 737f352ff0cSNeel Natu { 738f352ff0cSNeel Natu struct vcpu *vcpu; 739f352ff0cSNeel Natu 740f352ff0cSNeel Natu if (vcpuid < 0 || vcpuid >= VM_MAXCPU) 741366f6083SPeter Grehan return (EINVAL); 742366f6083SPeter Grehan 743f352ff0cSNeel Natu vcpu = &vm->vcpu[vcpuid]; 744f352ff0cSNeel Natu 745f352ff0cSNeel Natu vcpu->nmi_pending = 1; 746f352ff0cSNeel Natu vm_interrupt_hostcpu(vm, vcpuid); 747f352ff0cSNeel Natu return (0); 748f352ff0cSNeel Natu } 749f352ff0cSNeel Natu 750f352ff0cSNeel Natu int 751f352ff0cSNeel Natu vm_nmi_pending(struct vm *vm, int vcpuid) 752f352ff0cSNeel Natu { 753f352ff0cSNeel Natu struct vcpu *vcpu; 754f352ff0cSNeel Natu 755f352ff0cSNeel Natu if (vcpuid < 0 || vcpuid >= VM_MAXCPU) 756f352ff0cSNeel Natu panic("vm_nmi_pending: invalid vcpuid %d", vcpuid); 757f352ff0cSNeel Natu 758f352ff0cSNeel Natu vcpu = &vm->vcpu[vcpuid]; 759f352ff0cSNeel Natu 760f352ff0cSNeel Natu return (vcpu->nmi_pending); 761f352ff0cSNeel Natu } 762f352ff0cSNeel Natu 763f352ff0cSNeel Natu void 764f352ff0cSNeel Natu vm_nmi_clear(struct vm *vm, int vcpuid) 765f352ff0cSNeel Natu { 766f352ff0cSNeel Natu struct vcpu *vcpu; 767f352ff0cSNeel Natu 768f352ff0cSNeel Natu if (vcpuid < 0 || vcpuid >= VM_MAXCPU) 769f352ff0cSNeel Natu panic("vm_nmi_pending: invalid vcpuid %d", vcpuid); 770f352ff0cSNeel Natu 771f352ff0cSNeel Natu vcpu = &vm->vcpu[vcpuid]; 772f352ff0cSNeel Natu 773f352ff0cSNeel Natu if (vcpu->nmi_pending == 0) 774f352ff0cSNeel Natu panic("vm_nmi_clear: inconsistent nmi_pending state"); 775f352ff0cSNeel Natu 776f352ff0cSNeel Natu vcpu->nmi_pending = 0; 777f352ff0cSNeel Natu vmm_stat_incr(vm, vcpuid, VCPU_NMI_COUNT, 1); 778366f6083SPeter Grehan } 779366f6083SPeter Grehan 780366f6083SPeter Grehan int 781366f6083SPeter Grehan vm_get_capability(struct vm *vm, int vcpu, int type, int *retval) 782366f6083SPeter Grehan { 783366f6083SPeter Grehan if (vcpu < 0 || vcpu >= VM_MAXCPU) 784366f6083SPeter Grehan return (EINVAL); 785366f6083SPeter Grehan 786366f6083SPeter Grehan if (type < 0 || type >= VM_CAP_MAX) 787366f6083SPeter Grehan return (EINVAL); 788366f6083SPeter Grehan 789366f6083SPeter Grehan return (VMGETCAP(vm->cookie, vcpu, type, retval)); 790366f6083SPeter Grehan } 791366f6083SPeter Grehan 792366f6083SPeter Grehan int 793366f6083SPeter Grehan vm_set_capability(struct vm *vm, int vcpu, int type, int val) 794366f6083SPeter Grehan { 795366f6083SPeter Grehan if (vcpu < 0 || vcpu >= VM_MAXCPU) 796366f6083SPeter Grehan return (EINVAL); 797366f6083SPeter Grehan 798366f6083SPeter Grehan if (type < 0 || type >= VM_CAP_MAX) 799366f6083SPeter Grehan return (EINVAL); 800366f6083SPeter Grehan 801366f6083SPeter Grehan return (VMSETCAP(vm->cookie, vcpu, type, val)); 802366f6083SPeter Grehan } 803366f6083SPeter Grehan 804366f6083SPeter Grehan uint64_t * 805366f6083SPeter Grehan vm_guest_msrs(struct vm *vm, int cpu) 806366f6083SPeter Grehan { 807366f6083SPeter Grehan return (vm->vcpu[cpu].guest_msrs); 808366f6083SPeter Grehan } 809366f6083SPeter Grehan 810366f6083SPeter Grehan struct vlapic * 811366f6083SPeter Grehan vm_lapic(struct vm *vm, int cpu) 812366f6083SPeter Grehan { 813366f6083SPeter Grehan return (vm->vcpu[cpu].vlapic); 814366f6083SPeter Grehan } 815366f6083SPeter Grehan 816366f6083SPeter Grehan boolean_t 817366f6083SPeter Grehan vmm_is_pptdev(int bus, int slot, int func) 818366f6083SPeter Grehan { 81907044a96SNeel Natu int found, i, n; 82007044a96SNeel Natu int b, s, f; 821366f6083SPeter Grehan char *val, *cp, *cp2; 822366f6083SPeter Grehan 823366f6083SPeter Grehan /* 82407044a96SNeel Natu * XXX 82507044a96SNeel Natu * The length of an environment variable is limited to 128 bytes which 82607044a96SNeel Natu * puts an upper limit on the number of passthru devices that may be 82707044a96SNeel Natu * specified using a single environment variable. 82807044a96SNeel Natu * 82907044a96SNeel Natu * Work around this by scanning multiple environment variable 83007044a96SNeel Natu * names instead of a single one - yuck! 831366f6083SPeter Grehan */ 83207044a96SNeel Natu const char *names[] = { "pptdevs", "pptdevs2", "pptdevs3", NULL }; 83307044a96SNeel Natu 83407044a96SNeel Natu /* set pptdevs="1/2/3 4/5/6 7/8/9 10/11/12" */ 835366f6083SPeter Grehan found = 0; 83607044a96SNeel Natu for (i = 0; names[i] != NULL && !found; i++) { 83707044a96SNeel Natu cp = val = getenv(names[i]); 838366f6083SPeter Grehan while (cp != NULL && *cp != '\0') { 839366f6083SPeter Grehan if ((cp2 = strchr(cp, ' ')) != NULL) 840366f6083SPeter Grehan *cp2 = '\0'; 841366f6083SPeter Grehan 842366f6083SPeter Grehan n = sscanf(cp, "%d/%d/%d", &b, &s, &f); 843366f6083SPeter Grehan if (n == 3 && bus == b && slot == s && func == f) { 844366f6083SPeter Grehan found = 1; 845366f6083SPeter Grehan break; 846366f6083SPeter Grehan } 847366f6083SPeter Grehan 848366f6083SPeter Grehan if (cp2 != NULL) 849366f6083SPeter Grehan *cp2++ = ' '; 850366f6083SPeter Grehan 851366f6083SPeter Grehan cp = cp2; 852366f6083SPeter Grehan } 853366f6083SPeter Grehan freeenv(val); 85407044a96SNeel Natu } 855366f6083SPeter Grehan return (found); 856366f6083SPeter Grehan } 857366f6083SPeter Grehan 858366f6083SPeter Grehan void * 859366f6083SPeter Grehan vm_iommu_domain(struct vm *vm) 860366f6083SPeter Grehan { 861366f6083SPeter Grehan 862366f6083SPeter Grehan return (vm->iommu); 863366f6083SPeter Grehan } 864366f6083SPeter Grehan 86575dd3366SNeel Natu int 86675dd3366SNeel Natu vcpu_set_state(struct vm *vm, int vcpuid, enum vcpu_state state) 867366f6083SPeter Grehan { 86875dd3366SNeel Natu int error; 869366f6083SPeter Grehan struct vcpu *vcpu; 870366f6083SPeter Grehan 871366f6083SPeter Grehan if (vcpuid < 0 || vcpuid >= VM_MAXCPU) 872366f6083SPeter Grehan panic("vm_set_run_state: invalid vcpuid %d", vcpuid); 873366f6083SPeter Grehan 874366f6083SPeter Grehan vcpu = &vm->vcpu[vcpuid]; 875366f6083SPeter Grehan 87675dd3366SNeel Natu vcpu_lock(vcpu); 87775dd3366SNeel Natu 87875dd3366SNeel Natu /* 87975dd3366SNeel Natu * The following state transitions are allowed: 88075dd3366SNeel Natu * IDLE -> RUNNING -> IDLE 88175dd3366SNeel Natu * IDLE -> CANNOT_RUN -> IDLE 88275dd3366SNeel Natu */ 88375dd3366SNeel Natu if ((vcpu->state == VCPU_IDLE && state != VCPU_IDLE) || 88475dd3366SNeel Natu (vcpu->state != VCPU_IDLE && state == VCPU_IDLE)) { 88575dd3366SNeel Natu error = 0; 88675dd3366SNeel Natu vcpu->state = state; 887366f6083SPeter Grehan } else { 88875dd3366SNeel Natu error = EBUSY; 889366f6083SPeter Grehan } 890366f6083SPeter Grehan 89175dd3366SNeel Natu vcpu_unlock(vcpu); 89275dd3366SNeel Natu 89375dd3366SNeel Natu return (error); 89475dd3366SNeel Natu } 89575dd3366SNeel Natu 89675dd3366SNeel Natu enum vcpu_state 897*d3c11f40SPeter Grehan vcpu_get_state(struct vm *vm, int vcpuid, int *hostcpu) 898366f6083SPeter Grehan { 899366f6083SPeter Grehan struct vcpu *vcpu; 90075dd3366SNeel Natu enum vcpu_state state; 901366f6083SPeter Grehan 902366f6083SPeter Grehan if (vcpuid < 0 || vcpuid >= VM_MAXCPU) 903366f6083SPeter Grehan panic("vm_get_run_state: invalid vcpuid %d", vcpuid); 904366f6083SPeter Grehan 905366f6083SPeter Grehan vcpu = &vm->vcpu[vcpuid]; 906366f6083SPeter Grehan 90775dd3366SNeel Natu vcpu_lock(vcpu); 90875dd3366SNeel Natu state = vcpu->state; 909*d3c11f40SPeter Grehan if (hostcpu != NULL) 910*d3c11f40SPeter Grehan *hostcpu = vcpu->hostcpu; 91175dd3366SNeel Natu vcpu_unlock(vcpu); 912366f6083SPeter Grehan 91375dd3366SNeel Natu return (state); 914366f6083SPeter Grehan } 915366f6083SPeter Grehan 916366f6083SPeter Grehan void 917366f6083SPeter Grehan vm_activate_cpu(struct vm *vm, int vcpuid) 918366f6083SPeter Grehan { 919366f6083SPeter Grehan 920366f6083SPeter Grehan if (vcpuid >= 0 && vcpuid < VM_MAXCPU) 921a5615c90SPeter Grehan CPU_SET(vcpuid, &vm->active_cpus); 922366f6083SPeter Grehan } 923366f6083SPeter Grehan 924a5615c90SPeter Grehan cpuset_t 925366f6083SPeter Grehan vm_active_cpus(struct vm *vm) 926366f6083SPeter Grehan { 927366f6083SPeter Grehan 928366f6083SPeter Grehan return (vm->active_cpus); 929366f6083SPeter Grehan } 930366f6083SPeter Grehan 931366f6083SPeter Grehan void * 932366f6083SPeter Grehan vcpu_stats(struct vm *vm, int vcpuid) 933366f6083SPeter Grehan { 934366f6083SPeter Grehan 935366f6083SPeter Grehan return (vm->vcpu[vcpuid].stats); 936366f6083SPeter Grehan } 937e9027382SNeel Natu 938e9027382SNeel Natu int 939e9027382SNeel Natu vm_get_x2apic_state(struct vm *vm, int vcpuid, enum x2apic_state *state) 940e9027382SNeel Natu { 941e9027382SNeel Natu if (vcpuid < 0 || vcpuid >= VM_MAXCPU) 942e9027382SNeel Natu return (EINVAL); 943e9027382SNeel Natu 944e9027382SNeel Natu *state = vm->vcpu[vcpuid].x2apic_state; 945e9027382SNeel Natu 946e9027382SNeel Natu return (0); 947e9027382SNeel Natu } 948e9027382SNeel Natu 949e9027382SNeel Natu int 950e9027382SNeel Natu vm_set_x2apic_state(struct vm *vm, int vcpuid, enum x2apic_state state) 951e9027382SNeel Natu { 952e9027382SNeel Natu if (vcpuid < 0 || vcpuid >= VM_MAXCPU) 953e9027382SNeel Natu return (EINVAL); 954e9027382SNeel Natu 9553f23d3caSNeel Natu if (state >= X2APIC_STATE_LAST) 956e9027382SNeel Natu return (EINVAL); 957e9027382SNeel Natu 958e9027382SNeel Natu vm->vcpu[vcpuid].x2apic_state = state; 959e9027382SNeel Natu 96073820fb0SNeel Natu vlapic_set_x2apic_state(vm, vcpuid, state); 96173820fb0SNeel Natu 962e9027382SNeel Natu return (0); 963e9027382SNeel Natu } 96475dd3366SNeel Natu 96575dd3366SNeel Natu void 96675dd3366SNeel Natu vm_interrupt_hostcpu(struct vm *vm, int vcpuid) 96775dd3366SNeel Natu { 96875dd3366SNeel Natu int hostcpu; 96975dd3366SNeel Natu struct vcpu *vcpu; 97075dd3366SNeel Natu 97175dd3366SNeel Natu vcpu = &vm->vcpu[vcpuid]; 97275dd3366SNeel Natu 973f76fc5d4SNeel Natu vcpu_lock(vcpu); 97475dd3366SNeel Natu hostcpu = vcpu->hostcpu; 975f76fc5d4SNeel Natu if (hostcpu == NOCPU) { 976f76fc5d4SNeel Natu /* 977f76fc5d4SNeel Natu * If the vcpu is 'RUNNING' but without a valid 'hostcpu' then 978f76fc5d4SNeel Natu * the host thread must be sleeping waiting for an event to 979f76fc5d4SNeel Natu * kick the vcpu out of 'hlt'. 980f76fc5d4SNeel Natu * 981f76fc5d4SNeel Natu * XXX this is racy because the condition exists right before 982f76fc5d4SNeel Natu * and after calling VMRUN() in vm_run(). The wakeup() is 983f76fc5d4SNeel Natu * benign in this case. 984f76fc5d4SNeel Natu */ 985f76fc5d4SNeel Natu if (vcpu->state == VCPU_RUNNING) 986f76fc5d4SNeel Natu wakeup_one(vcpu); 987f76fc5d4SNeel Natu } else { 988f76fc5d4SNeel Natu if (vcpu->state != VCPU_RUNNING) 989f76fc5d4SNeel Natu panic("invalid vcpu state %d", vcpu->state); 990f76fc5d4SNeel Natu if (hostcpu != curcpu) 99175dd3366SNeel Natu ipi_cpu(hostcpu, vmm_ipinum); 99275dd3366SNeel Natu } 993f76fc5d4SNeel Natu vcpu_unlock(vcpu); 994f76fc5d4SNeel Natu } 995