147e07394SAndrew Turner /*- 247e07394SAndrew Turner * SPDX-License-Identifier: BSD-2-Clause 347e07394SAndrew Turner * 447e07394SAndrew Turner * Copyright (C) 2015 Mihai Carabas <mihai.carabas@gmail.com> 547e07394SAndrew Turner * All rights reserved. 647e07394SAndrew Turner * 747e07394SAndrew Turner * Redistribution and use in source and binary forms, with or without 847e07394SAndrew Turner * modification, are permitted provided that the following conditions 947e07394SAndrew Turner * are met: 1047e07394SAndrew Turner * 1. Redistributions of source code must retain the above copyright 1147e07394SAndrew Turner * notice, this list of conditions and the following disclaimer. 1247e07394SAndrew Turner * 2. Redistributions in binary form must reproduce the above copyright 1347e07394SAndrew Turner * notice, this list of conditions and the following disclaimer in the 1447e07394SAndrew Turner * documentation and/or other materials provided with the distribution. 1547e07394SAndrew Turner * 1647e07394SAndrew Turner * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1747e07394SAndrew Turner * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1847e07394SAndrew Turner * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1947e07394SAndrew Turner * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 2047e07394SAndrew Turner * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2147e07394SAndrew Turner * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2247e07394SAndrew Turner * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2347e07394SAndrew Turner * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2447e07394SAndrew Turner * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2547e07394SAndrew Turner * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2647e07394SAndrew Turner * SUCH DAMAGE. 2747e07394SAndrew Turner */ 2847e07394SAndrew Turner 2947e07394SAndrew Turner #include <sys/param.h> 3047e07394SAndrew Turner #include <sys/systm.h> 3147e07394SAndrew Turner #include <sys/cpuset.h> 3247e07394SAndrew Turner #include <sys/kernel.h> 3347e07394SAndrew Turner #include <sys/linker.h> 3447e07394SAndrew Turner #include <sys/lock.h> 3547e07394SAndrew Turner #include <sys/malloc.h> 3647e07394SAndrew Turner #include <sys/module.h> 3747e07394SAndrew Turner #include <sys/mutex.h> 3847e07394SAndrew Turner #include <sys/pcpu.h> 3947e07394SAndrew Turner #include <sys/proc.h> 4047e07394SAndrew Turner #include <sys/queue.h> 4147e07394SAndrew Turner #include <sys/rwlock.h> 4247e07394SAndrew Turner #include <sys/sched.h> 4347e07394SAndrew Turner #include <sys/smp.h> 4447e07394SAndrew Turner #include <sys/sysctl.h> 4547e07394SAndrew Turner 4647e07394SAndrew Turner #include <vm/vm.h> 4747e07394SAndrew Turner #include <vm/vm_object.h> 4847e07394SAndrew Turner #include <vm/vm_page.h> 4947e07394SAndrew Turner #include <vm/pmap.h> 5047e07394SAndrew Turner #include <vm/vm_map.h> 5147e07394SAndrew Turner #include <vm/vm_extern.h> 5247e07394SAndrew Turner #include <vm/vm_param.h> 5347e07394SAndrew Turner 5447e07394SAndrew Turner #include <machine/armreg.h> 5547e07394SAndrew Turner #include <machine/cpu.h> 5647e07394SAndrew Turner #include <machine/fpu.h> 5747e07394SAndrew Turner #include <machine/machdep.h> 5847e07394SAndrew Turner #include <machine/pcb.h> 5947e07394SAndrew Turner #include <machine/smp.h> 6047e07394SAndrew Turner #include <machine/vm.h> 6147e07394SAndrew Turner #include <machine/vmparam.h> 6247e07394SAndrew Turner #include <machine/vmm.h> 6347e07394SAndrew Turner #include <machine/vmm_instruction_emul.h> 6447e07394SAndrew Turner 6547e07394SAndrew Turner #include <dev/pci/pcireg.h> 66b9ef152bSMark Johnston #include <dev/vmm/vmm_dev.h> 673ccb0233SMark Johnston #include <dev/vmm/vmm_ktr.h> 68*c76c2a19SMark Johnston #include <dev/vmm/vmm_mem.h> 6993e81baaSMark Johnston #include <dev/vmm/vmm_stat.h> 7047e07394SAndrew Turner 7147e07394SAndrew Turner #include "arm64.h" 7247e07394SAndrew Turner #include "mmu.h" 7347e07394SAndrew Turner 7447e07394SAndrew Turner #include "io/vgic.h" 7547e07394SAndrew Turner #include "io/vtimer.h" 7647e07394SAndrew Turner 7747e07394SAndrew Turner struct vcpu { 7847e07394SAndrew Turner int flags; 7947e07394SAndrew Turner enum vcpu_state state; 8047e07394SAndrew Turner struct mtx mtx; 8147e07394SAndrew Turner int hostcpu; /* host cpuid this vcpu last ran on */ 8247e07394SAndrew Turner int vcpuid; 8347e07394SAndrew Turner void *stats; 8447e07394SAndrew Turner struct vm_exit exitinfo; 8547e07394SAndrew Turner uint64_t nextpc; /* (x) next instruction to execute */ 8647e07394SAndrew Turner struct vm *vm; /* (o) */ 8747e07394SAndrew Turner void *cookie; /* (i) cpu-specific data */ 8847e07394SAndrew Turner struct vfpstate *guestfpu; /* (a,i) guest fpu state */ 8947e07394SAndrew Turner }; 9047e07394SAndrew Turner 9147e07394SAndrew Turner #define vcpu_lock_initialized(v) mtx_initialized(&((v)->mtx)) 9247e07394SAndrew Turner #define vcpu_lock_init(v) mtx_init(&((v)->mtx), "vcpu lock", 0, MTX_SPIN) 9347e07394SAndrew Turner #define vcpu_lock_destroy(v) mtx_destroy(&((v)->mtx)) 9447e07394SAndrew Turner #define vcpu_lock(v) mtx_lock_spin(&((v)->mtx)) 9547e07394SAndrew Turner #define vcpu_unlock(v) mtx_unlock_spin(&((v)->mtx)) 9647e07394SAndrew Turner #define vcpu_assert_locked(v) mtx_assert(&((v)->mtx), MA_OWNED) 9747e07394SAndrew Turner 9847e07394SAndrew Turner struct vmm_mmio_region { 9947e07394SAndrew Turner uint64_t start; 10047e07394SAndrew Turner uint64_t end; 10147e07394SAndrew Turner mem_region_read_t read; 10247e07394SAndrew Turner mem_region_write_t write; 10347e07394SAndrew Turner }; 10447e07394SAndrew Turner #define VM_MAX_MMIO_REGIONS 4 10547e07394SAndrew Turner 10647e07394SAndrew Turner struct vmm_special_reg { 10747e07394SAndrew Turner uint32_t esr_iss; 10847e07394SAndrew Turner uint32_t esr_mask; 10947e07394SAndrew Turner reg_read_t reg_read; 11047e07394SAndrew Turner reg_write_t reg_write; 11147e07394SAndrew Turner void *arg; 11247e07394SAndrew Turner }; 11347e07394SAndrew Turner #define VM_MAX_SPECIAL_REGS 16 11447e07394SAndrew Turner 11547e07394SAndrew Turner /* 11647e07394SAndrew Turner * Initialization: 11747e07394SAndrew Turner * (o) initialized the first time the VM is created 11847e07394SAndrew Turner * (i) initialized when VM is created and when it is reinitialized 11947e07394SAndrew Turner * (x) initialized before use 12047e07394SAndrew Turner */ 12147e07394SAndrew Turner struct vm { 12247e07394SAndrew Turner void *cookie; /* (i) cpu-specific data */ 12347e07394SAndrew Turner volatile cpuset_t active_cpus; /* (i) active vcpus */ 12447e07394SAndrew Turner volatile cpuset_t debug_cpus; /* (i) vcpus stopped for debug */ 12547e07394SAndrew Turner int suspend; /* (i) stop VM execution */ 126a03354b0SMark Johnston bool dying; /* (o) is dying */ 12747e07394SAndrew Turner volatile cpuset_t suspended_cpus; /* (i) suspended vcpus */ 12847e07394SAndrew Turner volatile cpuset_t halted_cpus; /* (x) cpus in a hard halt */ 12947e07394SAndrew Turner struct vmspace *vmspace; /* (o) guest's address space */ 130*c76c2a19SMark Johnston struct vm_mem mem; /* (i) guest memory */ 13147e07394SAndrew Turner char name[VM_MAX_NAMELEN]; /* (o) virtual machine name */ 13247e07394SAndrew Turner struct vcpu **vcpu; /* (i) guest vcpus */ 13347e07394SAndrew Turner struct vmm_mmio_region mmio_region[VM_MAX_MMIO_REGIONS]; 13447e07394SAndrew Turner /* (o) guest MMIO regions */ 13547e07394SAndrew Turner struct vmm_special_reg special_reg[VM_MAX_SPECIAL_REGS]; 13647e07394SAndrew Turner /* The following describe the vm cpu topology */ 13747e07394SAndrew Turner uint16_t sockets; /* (o) num of sockets */ 13847e07394SAndrew Turner uint16_t cores; /* (o) num of cores/socket */ 13947e07394SAndrew Turner uint16_t threads; /* (o) num of threads/core */ 14047e07394SAndrew Turner uint16_t maxcpus; /* (o) max pluggable cpus */ 14147e07394SAndrew Turner struct sx vcpus_init_lock; /* (o) */ 14247e07394SAndrew Turner }; 14347e07394SAndrew Turner 14447e07394SAndrew Turner static bool vmm_initialized = false; 14547e07394SAndrew Turner 14647e07394SAndrew Turner static int vm_handle_wfi(struct vcpu *vcpu, 14747e07394SAndrew Turner struct vm_exit *vme, bool *retu); 14847e07394SAndrew Turner 14947e07394SAndrew Turner static MALLOC_DEFINE(M_VMM, "vmm", "vmm"); 15047e07394SAndrew Turner 15147e07394SAndrew Turner /* statistics */ 15247e07394SAndrew Turner static VMM_STAT(VCPU_TOTAL_RUNTIME, "vcpu total runtime"); 15347e07394SAndrew Turner 15447e07394SAndrew Turner SYSCTL_NODE(_hw, OID_AUTO, vmm, CTLFLAG_RW, NULL, NULL); 15547e07394SAndrew Turner 15647e07394SAndrew Turner static int vmm_ipinum; 15747e07394SAndrew Turner SYSCTL_INT(_hw_vmm, OID_AUTO, ipinum, CTLFLAG_RD, &vmm_ipinum, 0, 15847e07394SAndrew Turner "IPI vector used for vcpu notifications"); 15947e07394SAndrew Turner 16047e07394SAndrew Turner struct vmm_regs { 16147e07394SAndrew Turner uint64_t id_aa64afr0; 16247e07394SAndrew Turner uint64_t id_aa64afr1; 16347e07394SAndrew Turner uint64_t id_aa64dfr0; 16447e07394SAndrew Turner uint64_t id_aa64dfr1; 16547e07394SAndrew Turner uint64_t id_aa64isar0; 16647e07394SAndrew Turner uint64_t id_aa64isar1; 16747e07394SAndrew Turner uint64_t id_aa64isar2; 16847e07394SAndrew Turner uint64_t id_aa64mmfr0; 16947e07394SAndrew Turner uint64_t id_aa64mmfr1; 17047e07394SAndrew Turner uint64_t id_aa64mmfr2; 17147e07394SAndrew Turner uint64_t id_aa64pfr0; 17247e07394SAndrew Turner uint64_t id_aa64pfr1; 17347e07394SAndrew Turner }; 17447e07394SAndrew Turner 17547e07394SAndrew Turner static const struct vmm_regs vmm_arch_regs_masks = { 17647e07394SAndrew Turner .id_aa64dfr0 = 17747e07394SAndrew Turner ID_AA64DFR0_CTX_CMPs_MASK | 17847e07394SAndrew Turner ID_AA64DFR0_WRPs_MASK | 17947e07394SAndrew Turner ID_AA64DFR0_BRPs_MASK | 18047e07394SAndrew Turner ID_AA64DFR0_PMUVer_3 | 18147e07394SAndrew Turner ID_AA64DFR0_DebugVer_8, 18247e07394SAndrew Turner .id_aa64isar0 = 18347e07394SAndrew Turner ID_AA64ISAR0_TLB_TLBIOSR | 18447e07394SAndrew Turner ID_AA64ISAR0_SHA3_IMPL | 18547e07394SAndrew Turner ID_AA64ISAR0_RDM_IMPL | 18647e07394SAndrew Turner ID_AA64ISAR0_Atomic_IMPL | 18747e07394SAndrew Turner ID_AA64ISAR0_CRC32_BASE | 18847e07394SAndrew Turner ID_AA64ISAR0_SHA2_512 | 18947e07394SAndrew Turner ID_AA64ISAR0_SHA1_BASE | 19047e07394SAndrew Turner ID_AA64ISAR0_AES_PMULL, 19147e07394SAndrew Turner .id_aa64mmfr0 = 19247e07394SAndrew Turner ID_AA64MMFR0_TGran4_IMPL | 19347e07394SAndrew Turner ID_AA64MMFR0_TGran64_IMPL | 19447e07394SAndrew Turner ID_AA64MMFR0_TGran16_IMPL | 19547e07394SAndrew Turner ID_AA64MMFR0_ASIDBits_16 | 19647e07394SAndrew Turner ID_AA64MMFR0_PARange_4P, 19747e07394SAndrew Turner .id_aa64mmfr1 = 19847e07394SAndrew Turner ID_AA64MMFR1_SpecSEI_IMPL | 19947e07394SAndrew Turner ID_AA64MMFR1_PAN_ATS1E1 | 20047e07394SAndrew Turner ID_AA64MMFR1_HAFDBS_AF, 20147e07394SAndrew Turner .id_aa64pfr0 = 20247e07394SAndrew Turner ID_AA64PFR0_GIC_CPUIF_NONE | 20347e07394SAndrew Turner ID_AA64PFR0_AdvSIMD_HP | 20447e07394SAndrew Turner ID_AA64PFR0_FP_HP | 20547e07394SAndrew Turner ID_AA64PFR0_EL3_64 | 20647e07394SAndrew Turner ID_AA64PFR0_EL2_64 | 20747e07394SAndrew Turner ID_AA64PFR0_EL1_64 | 20847e07394SAndrew Turner ID_AA64PFR0_EL0_64, 20947e07394SAndrew Turner }; 21047e07394SAndrew Turner 21147e07394SAndrew Turner /* Host registers masked by vmm_arch_regs_masks. */ 21247e07394SAndrew Turner static struct vmm_regs vmm_arch_regs; 21347e07394SAndrew Turner 21447e07394SAndrew Turner u_int vm_maxcpu; 21547e07394SAndrew Turner SYSCTL_UINT(_hw_vmm, OID_AUTO, maxcpu, CTLFLAG_RDTUN | CTLFLAG_NOFETCH, 21647e07394SAndrew Turner &vm_maxcpu, 0, "Maximum number of vCPUs"); 21747e07394SAndrew Turner 21847e07394SAndrew Turner static void vcpu_notify_event_locked(struct vcpu *vcpu); 21947e07394SAndrew Turner 22093e81baaSMark Johnston /* global statistics */ 22193e81baaSMark Johnston VMM_STAT(VMEXIT_COUNT, "total number of vm exits"); 22293e81baaSMark Johnston VMM_STAT(VMEXIT_UNKNOWN, "number of vmexits for the unknown exception"); 22393e81baaSMark Johnston VMM_STAT(VMEXIT_WFI, "number of times wfi was intercepted"); 22493e81baaSMark Johnston VMM_STAT(VMEXIT_WFE, "number of times wfe was intercepted"); 22593e81baaSMark Johnston VMM_STAT(VMEXIT_HVC, "number of times hvc was intercepted"); 22693e81baaSMark Johnston VMM_STAT(VMEXIT_MSR, "number of times msr/mrs was intercepted"); 22793e81baaSMark Johnston VMM_STAT(VMEXIT_DATA_ABORT, "number of vmexits for a data abort"); 22893e81baaSMark Johnston VMM_STAT(VMEXIT_INSN_ABORT, "number of vmexits for an instruction abort"); 22993e81baaSMark Johnston VMM_STAT(VMEXIT_UNHANDLED_SYNC, "number of vmexits for an unhandled synchronous exception"); 23093e81baaSMark Johnston VMM_STAT(VMEXIT_IRQ, "number of vmexits for an irq"); 23193e81baaSMark Johnston VMM_STAT(VMEXIT_FIQ, "number of vmexits for an interrupt"); 23293e81baaSMark Johnston VMM_STAT(VMEXIT_BRK, "number of vmexits for a breakpoint exception"); 23393e81baaSMark Johnston VMM_STAT(VMEXIT_SS, "number of vmexits for a single-step exception"); 23493e81baaSMark Johnston VMM_STAT(VMEXIT_UNHANDLED_EL2, "number of vmexits for an unhandled EL2 exception"); 23593e81baaSMark Johnston VMM_STAT(VMEXIT_UNHANDLED, "number of vmexits for an unhandled exception"); 23693e81baaSMark Johnston 23747e07394SAndrew Turner /* 23847e07394SAndrew Turner * Upper limit on vm_maxcpu. We could increase this to 28 bits, but this 23947e07394SAndrew Turner * is a safe value for now. 24047e07394SAndrew Turner */ 24147e07394SAndrew Turner #define VM_MAXCPU MIN(0xffff - 1, CPU_SETSIZE) 24247e07394SAndrew Turner 24347e07394SAndrew Turner static int 24447e07394SAndrew Turner vmm_regs_init(struct vmm_regs *regs, const struct vmm_regs *masks) 24547e07394SAndrew Turner { 24647e07394SAndrew Turner #define _FETCH_KERN_REG(reg, field) do { \ 24747e07394SAndrew Turner regs->field = vmm_arch_regs_masks.field; \ 24847e07394SAndrew Turner if (!get_kernel_reg_masked(reg, ®s->field, masks->field)) \ 24947e07394SAndrew Turner regs->field = 0; \ 25047e07394SAndrew Turner } while (0) 25147e07394SAndrew Turner _FETCH_KERN_REG(ID_AA64AFR0_EL1, id_aa64afr0); 25247e07394SAndrew Turner _FETCH_KERN_REG(ID_AA64AFR1_EL1, id_aa64afr1); 25347e07394SAndrew Turner _FETCH_KERN_REG(ID_AA64DFR0_EL1, id_aa64dfr0); 25447e07394SAndrew Turner _FETCH_KERN_REG(ID_AA64DFR1_EL1, id_aa64dfr1); 25547e07394SAndrew Turner _FETCH_KERN_REG(ID_AA64ISAR0_EL1, id_aa64isar0); 25647e07394SAndrew Turner _FETCH_KERN_REG(ID_AA64ISAR1_EL1, id_aa64isar1); 25747e07394SAndrew Turner _FETCH_KERN_REG(ID_AA64ISAR2_EL1, id_aa64isar2); 25847e07394SAndrew Turner _FETCH_KERN_REG(ID_AA64MMFR0_EL1, id_aa64mmfr0); 25947e07394SAndrew Turner _FETCH_KERN_REG(ID_AA64MMFR1_EL1, id_aa64mmfr1); 26047e07394SAndrew Turner _FETCH_KERN_REG(ID_AA64MMFR2_EL1, id_aa64mmfr2); 26147e07394SAndrew Turner _FETCH_KERN_REG(ID_AA64PFR0_EL1, id_aa64pfr0); 26247e07394SAndrew Turner _FETCH_KERN_REG(ID_AA64PFR1_EL1, id_aa64pfr1); 26347e07394SAndrew Turner #undef _FETCH_KERN_REG 26447e07394SAndrew Turner return (0); 26547e07394SAndrew Turner } 26647e07394SAndrew Turner 26747e07394SAndrew Turner static void 26847e07394SAndrew Turner vcpu_cleanup(struct vcpu *vcpu, bool destroy) 26947e07394SAndrew Turner { 27047e07394SAndrew Turner vmmops_vcpu_cleanup(vcpu->cookie); 27147e07394SAndrew Turner vcpu->cookie = NULL; 27247e07394SAndrew Turner if (destroy) { 27347e07394SAndrew Turner vmm_stat_free(vcpu->stats); 27447e07394SAndrew Turner fpu_save_area_free(vcpu->guestfpu); 27547e07394SAndrew Turner vcpu_lock_destroy(vcpu); 27647e07394SAndrew Turner } 27747e07394SAndrew Turner } 27847e07394SAndrew Turner 27947e07394SAndrew Turner static struct vcpu * 28047e07394SAndrew Turner vcpu_alloc(struct vm *vm, int vcpu_id) 28147e07394SAndrew Turner { 28247e07394SAndrew Turner struct vcpu *vcpu; 28347e07394SAndrew Turner 28447e07394SAndrew Turner KASSERT(vcpu_id >= 0 && vcpu_id < vm->maxcpus, 28547e07394SAndrew Turner ("vcpu_alloc: invalid vcpu %d", vcpu_id)); 28647e07394SAndrew Turner 28747e07394SAndrew Turner vcpu = malloc(sizeof(*vcpu), M_VMM, M_WAITOK | M_ZERO); 28847e07394SAndrew Turner vcpu_lock_init(vcpu); 28947e07394SAndrew Turner vcpu->state = VCPU_IDLE; 29047e07394SAndrew Turner vcpu->hostcpu = NOCPU; 29147e07394SAndrew Turner vcpu->vcpuid = vcpu_id; 29247e07394SAndrew Turner vcpu->vm = vm; 29347e07394SAndrew Turner vcpu->guestfpu = fpu_save_area_alloc(); 29447e07394SAndrew Turner vcpu->stats = vmm_stat_alloc(); 29547e07394SAndrew Turner return (vcpu); 29647e07394SAndrew Turner } 29747e07394SAndrew Turner 29847e07394SAndrew Turner static void 29947e07394SAndrew Turner vcpu_init(struct vcpu *vcpu) 30047e07394SAndrew Turner { 30147e07394SAndrew Turner vcpu->cookie = vmmops_vcpu_init(vcpu->vm->cookie, vcpu, vcpu->vcpuid); 30247e07394SAndrew Turner MPASS(vcpu->cookie != NULL); 30347e07394SAndrew Turner fpu_save_area_reset(vcpu->guestfpu); 30447e07394SAndrew Turner vmm_stat_init(vcpu->stats); 30547e07394SAndrew Turner } 30647e07394SAndrew Turner 30747e07394SAndrew Turner struct vm_exit * 30847e07394SAndrew Turner vm_exitinfo(struct vcpu *vcpu) 30947e07394SAndrew Turner { 31047e07394SAndrew Turner return (&vcpu->exitinfo); 31147e07394SAndrew Turner } 31247e07394SAndrew Turner 31347e07394SAndrew Turner static int 31447e07394SAndrew Turner vmm_init(void) 31547e07394SAndrew Turner { 31647e07394SAndrew Turner int error; 31747e07394SAndrew Turner 31847e07394SAndrew Turner vm_maxcpu = mp_ncpus; 31947e07394SAndrew Turner TUNABLE_INT_FETCH("hw.vmm.maxcpu", &vm_maxcpu); 32047e07394SAndrew Turner 32147e07394SAndrew Turner if (vm_maxcpu > VM_MAXCPU) { 32247e07394SAndrew Turner printf("vmm: vm_maxcpu clamped to %u\n", VM_MAXCPU); 32347e07394SAndrew Turner vm_maxcpu = VM_MAXCPU; 32447e07394SAndrew Turner } 32547e07394SAndrew Turner if (vm_maxcpu == 0) 32647e07394SAndrew Turner vm_maxcpu = 1; 32747e07394SAndrew Turner 32847e07394SAndrew Turner error = vmm_regs_init(&vmm_arch_regs, &vmm_arch_regs_masks); 32947e07394SAndrew Turner if (error != 0) 33047e07394SAndrew Turner return (error); 33147e07394SAndrew Turner 33247e07394SAndrew Turner return (vmmops_modinit(0)); 33347e07394SAndrew Turner } 33447e07394SAndrew Turner 33547e07394SAndrew Turner static int 33647e07394SAndrew Turner vmm_handler(module_t mod, int what, void *arg) 33747e07394SAndrew Turner { 33847e07394SAndrew Turner int error; 33947e07394SAndrew Turner 34047e07394SAndrew Turner switch (what) { 34147e07394SAndrew Turner case MOD_LOAD: 342a97f683fSMark Johnston error = vmmdev_init(); 343a97f683fSMark Johnston if (error != 0) 344a97f683fSMark Johnston break; 34547e07394SAndrew Turner error = vmm_init(); 34647e07394SAndrew Turner if (error == 0) 34747e07394SAndrew Turner vmm_initialized = true; 3484a46ece6SMark Johnston else 3494a46ece6SMark Johnston (void)vmmdev_cleanup(); 35047e07394SAndrew Turner break; 35147e07394SAndrew Turner case MOD_UNLOAD: 35247e07394SAndrew Turner error = vmmdev_cleanup(); 35347e07394SAndrew Turner if (error == 0 && vmm_initialized) { 35447e07394SAndrew Turner error = vmmops_modcleanup(); 3554a46ece6SMark Johnston if (error) { 3564a46ece6SMark Johnston /* 3574a46ece6SMark Johnston * Something bad happened - prevent new 3584a46ece6SMark Johnston * VMs from being created 3594a46ece6SMark Johnston */ 36047e07394SAndrew Turner vmm_initialized = false; 36147e07394SAndrew Turner } 3624a46ece6SMark Johnston } 36347e07394SAndrew Turner break; 36447e07394SAndrew Turner default: 36547e07394SAndrew Turner error = 0; 36647e07394SAndrew Turner break; 36747e07394SAndrew Turner } 36847e07394SAndrew Turner return (error); 36947e07394SAndrew Turner } 37047e07394SAndrew Turner 37147e07394SAndrew Turner static moduledata_t vmm_kmod = { 37247e07394SAndrew Turner "vmm", 37347e07394SAndrew Turner vmm_handler, 37447e07394SAndrew Turner NULL 37547e07394SAndrew Turner }; 37647e07394SAndrew Turner 37747e07394SAndrew Turner /* 37847e07394SAndrew Turner * vmm initialization has the following dependencies: 37947e07394SAndrew Turner * 38047e07394SAndrew Turner * - HYP initialization requires smp_rendezvous() and therefore must happen 38147e07394SAndrew Turner * after SMP is fully functional (after SI_SUB_SMP). 382d7023078SMark Johnston * - vmm device initialization requires an initialized devfs. 38347e07394SAndrew Turner */ 384d7023078SMark Johnston DECLARE_MODULE(vmm, vmm_kmod, MAX(SI_SUB_SMP, SI_SUB_DEVFS) + 1, SI_ORDER_ANY); 38547e07394SAndrew Turner MODULE_VERSION(vmm, 1); 38647e07394SAndrew Turner 38747e07394SAndrew Turner static void 38847e07394SAndrew Turner vm_init(struct vm *vm, bool create) 38947e07394SAndrew Turner { 39047e07394SAndrew Turner int i; 39147e07394SAndrew Turner 39247e07394SAndrew Turner vm->cookie = vmmops_init(vm, vmspace_pmap(vm->vmspace)); 39347e07394SAndrew Turner MPASS(vm->cookie != NULL); 39447e07394SAndrew Turner 39547e07394SAndrew Turner CPU_ZERO(&vm->active_cpus); 39647e07394SAndrew Turner CPU_ZERO(&vm->debug_cpus); 39747e07394SAndrew Turner 39847e07394SAndrew Turner vm->suspend = 0; 39947e07394SAndrew Turner CPU_ZERO(&vm->suspended_cpus); 40047e07394SAndrew Turner 40147e07394SAndrew Turner memset(vm->mmio_region, 0, sizeof(vm->mmio_region)); 40247e07394SAndrew Turner memset(vm->special_reg, 0, sizeof(vm->special_reg)); 40347e07394SAndrew Turner 40447e07394SAndrew Turner if (!create) { 40547e07394SAndrew Turner for (i = 0; i < vm->maxcpus; i++) { 40647e07394SAndrew Turner if (vm->vcpu[i] != NULL) 40747e07394SAndrew Turner vcpu_init(vm->vcpu[i]); 40847e07394SAndrew Turner } 40947e07394SAndrew Turner } 41047e07394SAndrew Turner } 41147e07394SAndrew Turner 412a03354b0SMark Johnston void 413a03354b0SMark Johnston vm_disable_vcpu_creation(struct vm *vm) 414a03354b0SMark Johnston { 415a03354b0SMark Johnston sx_xlock(&vm->vcpus_init_lock); 416a03354b0SMark Johnston vm->dying = true; 417a03354b0SMark Johnston sx_xunlock(&vm->vcpus_init_lock); 418a03354b0SMark Johnston } 419a03354b0SMark Johnston 42047e07394SAndrew Turner struct vcpu * 42147e07394SAndrew Turner vm_alloc_vcpu(struct vm *vm, int vcpuid) 42247e07394SAndrew Turner { 42347e07394SAndrew Turner struct vcpu *vcpu; 42447e07394SAndrew Turner 42547e07394SAndrew Turner if (vcpuid < 0 || vcpuid >= vm_get_maxcpus(vm)) 42647e07394SAndrew Turner return (NULL); 42747e07394SAndrew Turner 42847e07394SAndrew Turner /* Some interrupt controllers may have a CPU limit */ 42947e07394SAndrew Turner if (vcpuid >= vgic_max_cpu_count(vm->cookie)) 43047e07394SAndrew Turner return (NULL); 43147e07394SAndrew Turner 43272ae04c7SRuslan Bukin vcpu = (struct vcpu *) 43372ae04c7SRuslan Bukin atomic_load_acq_ptr((uintptr_t *)&vm->vcpu[vcpuid]); 43447e07394SAndrew Turner if (__predict_true(vcpu != NULL)) 43547e07394SAndrew Turner return (vcpu); 43647e07394SAndrew Turner 43747e07394SAndrew Turner sx_xlock(&vm->vcpus_init_lock); 43847e07394SAndrew Turner vcpu = vm->vcpu[vcpuid]; 439a03354b0SMark Johnston if (vcpu == NULL && !vm->dying) { 44047e07394SAndrew Turner vcpu = vcpu_alloc(vm, vcpuid); 44147e07394SAndrew Turner vcpu_init(vcpu); 44247e07394SAndrew Turner 44347e07394SAndrew Turner /* 44447e07394SAndrew Turner * Ensure vCPU is fully created before updating pointer 44547e07394SAndrew Turner * to permit unlocked reads above. 44647e07394SAndrew Turner */ 44747e07394SAndrew Turner atomic_store_rel_ptr((uintptr_t *)&vm->vcpu[vcpuid], 44847e07394SAndrew Turner (uintptr_t)vcpu); 44947e07394SAndrew Turner } 45047e07394SAndrew Turner sx_xunlock(&vm->vcpus_init_lock); 45147e07394SAndrew Turner return (vcpu); 45247e07394SAndrew Turner } 45347e07394SAndrew Turner 45447e07394SAndrew Turner void 45547e07394SAndrew Turner vm_slock_vcpus(struct vm *vm) 45647e07394SAndrew Turner { 45747e07394SAndrew Turner sx_slock(&vm->vcpus_init_lock); 45847e07394SAndrew Turner } 45947e07394SAndrew Turner 46047e07394SAndrew Turner void 46147e07394SAndrew Turner vm_unlock_vcpus(struct vm *vm) 46247e07394SAndrew Turner { 46347e07394SAndrew Turner sx_unlock(&vm->vcpus_init_lock); 46447e07394SAndrew Turner } 46547e07394SAndrew Turner 46647e07394SAndrew Turner int 46747e07394SAndrew Turner vm_create(const char *name, struct vm **retvm) 46847e07394SAndrew Turner { 46947e07394SAndrew Turner struct vm *vm; 47047e07394SAndrew Turner struct vmspace *vmspace; 47147e07394SAndrew Turner 47247e07394SAndrew Turner /* 47347e07394SAndrew Turner * If vmm.ko could not be successfully initialized then don't attempt 47447e07394SAndrew Turner * to create the virtual machine. 47547e07394SAndrew Turner */ 47647e07394SAndrew Turner if (!vmm_initialized) 47747e07394SAndrew Turner return (ENXIO); 47847e07394SAndrew Turner 47947e07394SAndrew Turner if (name == NULL || strlen(name) >= VM_MAX_NAMELEN) 48047e07394SAndrew Turner return (EINVAL); 48147e07394SAndrew Turner 48247e07394SAndrew Turner vmspace = vmmops_vmspace_alloc(0, 1ul << 39); 48347e07394SAndrew Turner if (vmspace == NULL) 48447e07394SAndrew Turner return (ENOMEM); 48547e07394SAndrew Turner 48647e07394SAndrew Turner vm = malloc(sizeof(struct vm), M_VMM, M_WAITOK | M_ZERO); 48747e07394SAndrew Turner strcpy(vm->name, name); 48847e07394SAndrew Turner vm->vmspace = vmspace; 489*c76c2a19SMark Johnston vm_mem_init(&vm->mem); 49047e07394SAndrew Turner sx_init(&vm->vcpus_init_lock, "vm vcpus"); 49147e07394SAndrew Turner 49247e07394SAndrew Turner vm->sockets = 1; 49347e07394SAndrew Turner vm->cores = 1; /* XXX backwards compatibility */ 49447e07394SAndrew Turner vm->threads = 1; /* XXX backwards compatibility */ 49547e07394SAndrew Turner vm->maxcpus = vm_maxcpu; 49647e07394SAndrew Turner 49747e07394SAndrew Turner vm->vcpu = malloc(sizeof(*vm->vcpu) * vm->maxcpus, M_VMM, 49847e07394SAndrew Turner M_WAITOK | M_ZERO); 49947e07394SAndrew Turner 50047e07394SAndrew Turner vm_init(vm, true); 50147e07394SAndrew Turner 50247e07394SAndrew Turner *retvm = vm; 50347e07394SAndrew Turner return (0); 50447e07394SAndrew Turner } 50547e07394SAndrew Turner 50647e07394SAndrew Turner void 50747e07394SAndrew Turner vm_get_topology(struct vm *vm, uint16_t *sockets, uint16_t *cores, 50847e07394SAndrew Turner uint16_t *threads, uint16_t *maxcpus) 50947e07394SAndrew Turner { 51047e07394SAndrew Turner *sockets = vm->sockets; 51147e07394SAndrew Turner *cores = vm->cores; 51247e07394SAndrew Turner *threads = vm->threads; 51347e07394SAndrew Turner *maxcpus = vm->maxcpus; 51447e07394SAndrew Turner } 51547e07394SAndrew Turner 51647e07394SAndrew Turner uint16_t 51747e07394SAndrew Turner vm_get_maxcpus(struct vm *vm) 51847e07394SAndrew Turner { 51947e07394SAndrew Turner return (vm->maxcpus); 52047e07394SAndrew Turner } 52147e07394SAndrew Turner 52247e07394SAndrew Turner int 52347e07394SAndrew Turner vm_set_topology(struct vm *vm, uint16_t sockets, uint16_t cores, 52447e07394SAndrew Turner uint16_t threads, uint16_t maxcpus) 52547e07394SAndrew Turner { 52647e07394SAndrew Turner /* Ignore maxcpus. */ 52747e07394SAndrew Turner if ((sockets * cores * threads) > vm->maxcpus) 52847e07394SAndrew Turner return (EINVAL); 52947e07394SAndrew Turner vm->sockets = sockets; 53047e07394SAndrew Turner vm->cores = cores; 53147e07394SAndrew Turner vm->threads = threads; 53247e07394SAndrew Turner return(0); 53347e07394SAndrew Turner } 53447e07394SAndrew Turner 53547e07394SAndrew Turner static void 53647e07394SAndrew Turner vm_cleanup(struct vm *vm, bool destroy) 53747e07394SAndrew Turner { 53847e07394SAndrew Turner pmap_t pmap __diagused; 53947e07394SAndrew Turner int i; 54047e07394SAndrew Turner 54147e07394SAndrew Turner if (destroy) { 542*c76c2a19SMark Johnston vm_xlock_memsegs(vm); 54347e07394SAndrew Turner pmap = vmspace_pmap(vm->vmspace); 54447e07394SAndrew Turner sched_pin(); 54547e07394SAndrew Turner PCPU_SET(curvmpmap, NULL); 54647e07394SAndrew Turner sched_unpin(); 54747e07394SAndrew Turner CPU_FOREACH(i) { 54847e07394SAndrew Turner MPASS(cpuid_to_pcpu[i]->pc_curvmpmap != pmap); 54947e07394SAndrew Turner } 550*c76c2a19SMark Johnston } else 551*c76c2a19SMark Johnston vm_assert_memseg_xlocked(vm); 552*c76c2a19SMark Johnston 55347e07394SAndrew Turner 55447e07394SAndrew Turner vgic_detach_from_vm(vm->cookie); 55547e07394SAndrew Turner 55647e07394SAndrew Turner for (i = 0; i < vm->maxcpus; i++) { 55747e07394SAndrew Turner if (vm->vcpu[i] != NULL) 55847e07394SAndrew Turner vcpu_cleanup(vm->vcpu[i], destroy); 55947e07394SAndrew Turner } 56047e07394SAndrew Turner 56147e07394SAndrew Turner vmmops_cleanup(vm->cookie); 56247e07394SAndrew Turner 563*c76c2a19SMark Johnston vm_mem_cleanup(vm); 56447e07394SAndrew Turner if (destroy) { 565*c76c2a19SMark Johnston vm_mem_destroy(vm); 56647e07394SAndrew Turner 56747e07394SAndrew Turner vmmops_vmspace_free(vm->vmspace); 56847e07394SAndrew Turner vm->vmspace = NULL; 56947e07394SAndrew Turner 57047e07394SAndrew Turner for (i = 0; i < vm->maxcpus; i++) 57147e07394SAndrew Turner free(vm->vcpu[i], M_VMM); 57247e07394SAndrew Turner free(vm->vcpu, M_VMM); 57347e07394SAndrew Turner sx_destroy(&vm->vcpus_init_lock); 57447e07394SAndrew Turner } 57547e07394SAndrew Turner } 57647e07394SAndrew Turner 57747e07394SAndrew Turner void 57847e07394SAndrew Turner vm_destroy(struct vm *vm) 57947e07394SAndrew Turner { 58047e07394SAndrew Turner vm_cleanup(vm, true); 58147e07394SAndrew Turner free(vm, M_VMM); 58247e07394SAndrew Turner } 58347e07394SAndrew Turner 58447e07394SAndrew Turner int 58547e07394SAndrew Turner vm_reinit(struct vm *vm) 58647e07394SAndrew Turner { 58747e07394SAndrew Turner int error; 58847e07394SAndrew Turner 58947e07394SAndrew Turner /* 59047e07394SAndrew Turner * A virtual machine can be reset only if all vcpus are suspended. 59147e07394SAndrew Turner */ 59247e07394SAndrew Turner if (CPU_CMP(&vm->suspended_cpus, &vm->active_cpus) == 0) { 59347e07394SAndrew Turner vm_cleanup(vm, false); 59447e07394SAndrew Turner vm_init(vm, false); 59547e07394SAndrew Turner error = 0; 59647e07394SAndrew Turner } else { 59747e07394SAndrew Turner error = EBUSY; 59847e07394SAndrew Turner } 59947e07394SAndrew Turner 60047e07394SAndrew Turner return (error); 60147e07394SAndrew Turner } 60247e07394SAndrew Turner 60347e07394SAndrew Turner const char * 60447e07394SAndrew Turner vm_name(struct vm *vm) 60547e07394SAndrew Turner { 60647e07394SAndrew Turner return (vm->name); 60747e07394SAndrew Turner } 60847e07394SAndrew Turner 60947e07394SAndrew Turner int 61047e07394SAndrew Turner vm_gla2gpa_nofault(struct vcpu *vcpu, struct vm_guest_paging *paging, 61147e07394SAndrew Turner uint64_t gla, int prot, uint64_t *gpa, int *is_fault) 61247e07394SAndrew Turner { 613*c76c2a19SMark Johnston return (vmmops_gla2gpa(vcpu->cookie, paging, gla, prot, gpa, is_fault)); 61447e07394SAndrew Turner } 61547e07394SAndrew Turner 61647e07394SAndrew Turner static int 61747e07394SAndrew Turner vmm_reg_raz(struct vcpu *vcpu, uint64_t *rval, void *arg) 61847e07394SAndrew Turner { 61947e07394SAndrew Turner *rval = 0; 62047e07394SAndrew Turner return (0); 62147e07394SAndrew Turner } 62247e07394SAndrew Turner 62347e07394SAndrew Turner static int 62447e07394SAndrew Turner vmm_reg_read_arg(struct vcpu *vcpu, uint64_t *rval, void *arg) 62547e07394SAndrew Turner { 62647e07394SAndrew Turner *rval = *(uint64_t *)arg; 62747e07394SAndrew Turner return (0); 62847e07394SAndrew Turner } 62947e07394SAndrew Turner 63047e07394SAndrew Turner static int 63147e07394SAndrew Turner vmm_reg_wi(struct vcpu *vcpu, uint64_t wval, void *arg) 63247e07394SAndrew Turner { 63347e07394SAndrew Turner return (0); 63447e07394SAndrew Turner } 63547e07394SAndrew Turner 63647e07394SAndrew Turner static const struct vmm_special_reg vmm_special_regs[] = { 63747e07394SAndrew Turner #define SPECIAL_REG(_reg, _read, _write) \ 63847e07394SAndrew Turner { \ 63947e07394SAndrew Turner .esr_iss = ((_reg ## _op0) << ISS_MSR_OP0_SHIFT) | \ 64047e07394SAndrew Turner ((_reg ## _op1) << ISS_MSR_OP1_SHIFT) | \ 64147e07394SAndrew Turner ((_reg ## _CRn) << ISS_MSR_CRn_SHIFT) | \ 64247e07394SAndrew Turner ((_reg ## _CRm) << ISS_MSR_CRm_SHIFT) | \ 64347e07394SAndrew Turner ((_reg ## _op2) << ISS_MSR_OP2_SHIFT), \ 64447e07394SAndrew Turner .esr_mask = ISS_MSR_REG_MASK, \ 64547e07394SAndrew Turner .reg_read = (_read), \ 64647e07394SAndrew Turner .reg_write = (_write), \ 64747e07394SAndrew Turner .arg = NULL, \ 64847e07394SAndrew Turner } 64947e07394SAndrew Turner #define ID_SPECIAL_REG(_reg, _name) \ 65047e07394SAndrew Turner { \ 65147e07394SAndrew Turner .esr_iss = ((_reg ## _op0) << ISS_MSR_OP0_SHIFT) | \ 65247e07394SAndrew Turner ((_reg ## _op1) << ISS_MSR_OP1_SHIFT) | \ 65347e07394SAndrew Turner ((_reg ## _CRn) << ISS_MSR_CRn_SHIFT) | \ 65447e07394SAndrew Turner ((_reg ## _CRm) << ISS_MSR_CRm_SHIFT) | \ 65547e07394SAndrew Turner ((_reg ## _op2) << ISS_MSR_OP2_SHIFT), \ 65647e07394SAndrew Turner .esr_mask = ISS_MSR_REG_MASK, \ 65747e07394SAndrew Turner .reg_read = vmm_reg_read_arg, \ 65847e07394SAndrew Turner .reg_write = vmm_reg_wi, \ 65947e07394SAndrew Turner .arg = &(vmm_arch_regs._name), \ 66047e07394SAndrew Turner } 66147e07394SAndrew Turner 66247e07394SAndrew Turner /* ID registers */ 66347e07394SAndrew Turner ID_SPECIAL_REG(ID_AA64PFR0_EL1, id_aa64pfr0), 66447e07394SAndrew Turner ID_SPECIAL_REG(ID_AA64DFR0_EL1, id_aa64dfr0), 66547e07394SAndrew Turner ID_SPECIAL_REG(ID_AA64ISAR0_EL1, id_aa64isar0), 66647e07394SAndrew Turner ID_SPECIAL_REG(ID_AA64MMFR0_EL1, id_aa64mmfr0), 66747e07394SAndrew Turner ID_SPECIAL_REG(ID_AA64MMFR1_EL1, id_aa64mmfr1), 66847e07394SAndrew Turner 66947e07394SAndrew Turner /* 67047e07394SAndrew Turner * All other ID registers are read as zero. 67147e07394SAndrew Turner * They are all in the op0=3, op1=0, CRn=0, CRm={0..7} space. 67247e07394SAndrew Turner */ 67347e07394SAndrew Turner { 67447e07394SAndrew Turner .esr_iss = (3 << ISS_MSR_OP0_SHIFT) | 67547e07394SAndrew Turner (0 << ISS_MSR_OP1_SHIFT) | 67647e07394SAndrew Turner (0 << ISS_MSR_CRn_SHIFT) | 67747e07394SAndrew Turner (0 << ISS_MSR_CRm_SHIFT), 67847e07394SAndrew Turner .esr_mask = ISS_MSR_OP0_MASK | ISS_MSR_OP1_MASK | 67947e07394SAndrew Turner ISS_MSR_CRn_MASK | (0x8 << ISS_MSR_CRm_SHIFT), 68047e07394SAndrew Turner .reg_read = vmm_reg_raz, 68147e07394SAndrew Turner .reg_write = vmm_reg_wi, 68247e07394SAndrew Turner .arg = NULL, 68347e07394SAndrew Turner }, 68447e07394SAndrew Turner 68547e07394SAndrew Turner /* Counter physical registers */ 68647e07394SAndrew Turner SPECIAL_REG(CNTP_CTL_EL0, vtimer_phys_ctl_read, vtimer_phys_ctl_write), 68747e07394SAndrew Turner SPECIAL_REG(CNTP_CVAL_EL0, vtimer_phys_cval_read, 68847e07394SAndrew Turner vtimer_phys_cval_write), 68947e07394SAndrew Turner SPECIAL_REG(CNTP_TVAL_EL0, vtimer_phys_tval_read, 69047e07394SAndrew Turner vtimer_phys_tval_write), 69147e07394SAndrew Turner SPECIAL_REG(CNTPCT_EL0, vtimer_phys_cnt_read, vtimer_phys_cnt_write), 69247e07394SAndrew Turner #undef SPECIAL_REG 69347e07394SAndrew Turner }; 69447e07394SAndrew Turner 69547e07394SAndrew Turner void 69647e07394SAndrew Turner vm_register_reg_handler(struct vm *vm, uint64_t iss, uint64_t mask, 69747e07394SAndrew Turner reg_read_t reg_read, reg_write_t reg_write, void *arg) 69847e07394SAndrew Turner { 69947e07394SAndrew Turner int i; 70047e07394SAndrew Turner 70147e07394SAndrew Turner for (i = 0; i < nitems(vm->special_reg); i++) { 70247e07394SAndrew Turner if (vm->special_reg[i].esr_iss == 0 && 70347e07394SAndrew Turner vm->special_reg[i].esr_mask == 0) { 70447e07394SAndrew Turner vm->special_reg[i].esr_iss = iss; 70547e07394SAndrew Turner vm->special_reg[i].esr_mask = mask; 70647e07394SAndrew Turner vm->special_reg[i].reg_read = reg_read; 70747e07394SAndrew Turner vm->special_reg[i].reg_write = reg_write; 70847e07394SAndrew Turner vm->special_reg[i].arg = arg; 70947e07394SAndrew Turner return; 71047e07394SAndrew Turner } 71147e07394SAndrew Turner } 71247e07394SAndrew Turner 71347e07394SAndrew Turner panic("%s: No free special register slot", __func__); 71447e07394SAndrew Turner } 71547e07394SAndrew Turner 71647e07394SAndrew Turner void 71747e07394SAndrew Turner vm_deregister_reg_handler(struct vm *vm, uint64_t iss, uint64_t mask) 71847e07394SAndrew Turner { 71947e07394SAndrew Turner int i; 72047e07394SAndrew Turner 72147e07394SAndrew Turner for (i = 0; i < nitems(vm->special_reg); i++) { 72247e07394SAndrew Turner if (vm->special_reg[i].esr_iss == iss && 72347e07394SAndrew Turner vm->special_reg[i].esr_mask == mask) { 72447e07394SAndrew Turner memset(&vm->special_reg[i], 0, 72547e07394SAndrew Turner sizeof(vm->special_reg[i])); 72647e07394SAndrew Turner return; 72747e07394SAndrew Turner } 72847e07394SAndrew Turner } 72947e07394SAndrew Turner 73047e07394SAndrew Turner panic("%s: Invalid special register: iss %lx mask %lx", __func__, iss, 73147e07394SAndrew Turner mask); 73247e07394SAndrew Turner } 73347e07394SAndrew Turner 73447e07394SAndrew Turner static int 73547e07394SAndrew Turner vm_handle_reg_emul(struct vcpu *vcpu, bool *retu) 73647e07394SAndrew Turner { 73747e07394SAndrew Turner struct vm *vm; 73847e07394SAndrew Turner struct vm_exit *vme; 73947e07394SAndrew Turner struct vre *vre; 74047e07394SAndrew Turner int i, rv; 74147e07394SAndrew Turner 74247e07394SAndrew Turner vm = vcpu->vm; 74347e07394SAndrew Turner vme = &vcpu->exitinfo; 74447e07394SAndrew Turner vre = &vme->u.reg_emul.vre; 74547e07394SAndrew Turner 74647e07394SAndrew Turner for (i = 0; i < nitems(vm->special_reg); i++) { 74747e07394SAndrew Turner if (vm->special_reg[i].esr_iss == 0 && 74847e07394SAndrew Turner vm->special_reg[i].esr_mask == 0) 74947e07394SAndrew Turner continue; 75047e07394SAndrew Turner 75147e07394SAndrew Turner if ((vre->inst_syndrome & vm->special_reg[i].esr_mask) == 75247e07394SAndrew Turner vm->special_reg[i].esr_iss) { 75347e07394SAndrew Turner rv = vmm_emulate_register(vcpu, vre, 75447e07394SAndrew Turner vm->special_reg[i].reg_read, 75547e07394SAndrew Turner vm->special_reg[i].reg_write, 75647e07394SAndrew Turner vm->special_reg[i].arg); 75747e07394SAndrew Turner if (rv == 0) { 75847e07394SAndrew Turner *retu = false; 75947e07394SAndrew Turner } 76047e07394SAndrew Turner return (rv); 76147e07394SAndrew Turner } 76247e07394SAndrew Turner } 76347e07394SAndrew Turner for (i = 0; i < nitems(vmm_special_regs); i++) { 76447e07394SAndrew Turner if ((vre->inst_syndrome & vmm_special_regs[i].esr_mask) == 76547e07394SAndrew Turner vmm_special_regs[i].esr_iss) { 76647e07394SAndrew Turner rv = vmm_emulate_register(vcpu, vre, 76747e07394SAndrew Turner vmm_special_regs[i].reg_read, 76847e07394SAndrew Turner vmm_special_regs[i].reg_write, 76947e07394SAndrew Turner vmm_special_regs[i].arg); 77047e07394SAndrew Turner if (rv == 0) { 77147e07394SAndrew Turner *retu = false; 77247e07394SAndrew Turner } 77347e07394SAndrew Turner return (rv); 77447e07394SAndrew Turner } 77547e07394SAndrew Turner } 77647e07394SAndrew Turner 77747e07394SAndrew Turner 77847e07394SAndrew Turner *retu = true; 77947e07394SAndrew Turner return (0); 78047e07394SAndrew Turner } 78147e07394SAndrew Turner 78247e07394SAndrew Turner void 78347e07394SAndrew Turner vm_register_inst_handler(struct vm *vm, uint64_t start, uint64_t size, 78447e07394SAndrew Turner mem_region_read_t mmio_read, mem_region_write_t mmio_write) 78547e07394SAndrew Turner { 78647e07394SAndrew Turner int i; 78747e07394SAndrew Turner 78847e07394SAndrew Turner for (i = 0; i < nitems(vm->mmio_region); i++) { 78947e07394SAndrew Turner if (vm->mmio_region[i].start == 0 && 79047e07394SAndrew Turner vm->mmio_region[i].end == 0) { 79147e07394SAndrew Turner vm->mmio_region[i].start = start; 79247e07394SAndrew Turner vm->mmio_region[i].end = start + size; 79347e07394SAndrew Turner vm->mmio_region[i].read = mmio_read; 79447e07394SAndrew Turner vm->mmio_region[i].write = mmio_write; 79547e07394SAndrew Turner return; 79647e07394SAndrew Turner } 79747e07394SAndrew Turner } 79847e07394SAndrew Turner 79947e07394SAndrew Turner panic("%s: No free MMIO region", __func__); 80047e07394SAndrew Turner } 80147e07394SAndrew Turner 80247e07394SAndrew Turner void 80347e07394SAndrew Turner vm_deregister_inst_handler(struct vm *vm, uint64_t start, uint64_t size) 80447e07394SAndrew Turner { 80547e07394SAndrew Turner int i; 80647e07394SAndrew Turner 80747e07394SAndrew Turner for (i = 0; i < nitems(vm->mmio_region); i++) { 80847e07394SAndrew Turner if (vm->mmio_region[i].start == start && 80947e07394SAndrew Turner vm->mmio_region[i].end == start + size) { 81047e07394SAndrew Turner memset(&vm->mmio_region[i], 0, 81147e07394SAndrew Turner sizeof(vm->mmio_region[i])); 81247e07394SAndrew Turner return; 81347e07394SAndrew Turner } 81447e07394SAndrew Turner } 81547e07394SAndrew Turner 81647e07394SAndrew Turner panic("%s: Invalid MMIO region: %lx - %lx", __func__, start, 81747e07394SAndrew Turner start + size); 81847e07394SAndrew Turner } 81947e07394SAndrew Turner 82047e07394SAndrew Turner static int 82147e07394SAndrew Turner vm_handle_inst_emul(struct vcpu *vcpu, bool *retu) 82247e07394SAndrew Turner { 82347e07394SAndrew Turner struct vm *vm; 82447e07394SAndrew Turner struct vm_exit *vme; 82547e07394SAndrew Turner struct vie *vie; 82647e07394SAndrew Turner struct hyp *hyp; 82747e07394SAndrew Turner uint64_t fault_ipa; 82847e07394SAndrew Turner struct vm_guest_paging *paging; 82947e07394SAndrew Turner struct vmm_mmio_region *vmr; 83047e07394SAndrew Turner int error, i; 83147e07394SAndrew Turner 83247e07394SAndrew Turner vm = vcpu->vm; 83347e07394SAndrew Turner hyp = vm->cookie; 83447e07394SAndrew Turner if (!hyp->vgic_attached) 83547e07394SAndrew Turner goto out_user; 83647e07394SAndrew Turner 83747e07394SAndrew Turner vme = &vcpu->exitinfo; 83847e07394SAndrew Turner vie = &vme->u.inst_emul.vie; 83947e07394SAndrew Turner paging = &vme->u.inst_emul.paging; 84047e07394SAndrew Turner 84147e07394SAndrew Turner fault_ipa = vme->u.inst_emul.gpa; 84247e07394SAndrew Turner 84347e07394SAndrew Turner vmr = NULL; 84447e07394SAndrew Turner for (i = 0; i < nitems(vm->mmio_region); i++) { 84547e07394SAndrew Turner if (vm->mmio_region[i].start <= fault_ipa && 84647e07394SAndrew Turner vm->mmio_region[i].end > fault_ipa) { 84747e07394SAndrew Turner vmr = &vm->mmio_region[i]; 84847e07394SAndrew Turner break; 84947e07394SAndrew Turner } 85047e07394SAndrew Turner } 85147e07394SAndrew Turner if (vmr == NULL) 85247e07394SAndrew Turner goto out_user; 85347e07394SAndrew Turner 85447e07394SAndrew Turner error = vmm_emulate_instruction(vcpu, fault_ipa, vie, paging, 85547e07394SAndrew Turner vmr->read, vmr->write, retu); 85647e07394SAndrew Turner return (error); 85747e07394SAndrew Turner 85847e07394SAndrew Turner out_user: 85947e07394SAndrew Turner *retu = true; 86047e07394SAndrew Turner return (0); 86147e07394SAndrew Turner } 86247e07394SAndrew Turner 86347e07394SAndrew Turner int 86447e07394SAndrew Turner vm_suspend(struct vm *vm, enum vm_suspend_how how) 86547e07394SAndrew Turner { 86647e07394SAndrew Turner int i; 86747e07394SAndrew Turner 86847e07394SAndrew Turner if (how <= VM_SUSPEND_NONE || how >= VM_SUSPEND_LAST) 86947e07394SAndrew Turner return (EINVAL); 87047e07394SAndrew Turner 87147e07394SAndrew Turner if (atomic_cmpset_int(&vm->suspend, 0, how) == 0) { 87247e07394SAndrew Turner VM_CTR2(vm, "virtual machine already suspended %d/%d", 87347e07394SAndrew Turner vm->suspend, how); 87447e07394SAndrew Turner return (EALREADY); 87547e07394SAndrew Turner } 87647e07394SAndrew Turner 87747e07394SAndrew Turner VM_CTR1(vm, "virtual machine successfully suspended %d", how); 87847e07394SAndrew Turner 87947e07394SAndrew Turner /* 88047e07394SAndrew Turner * Notify all active vcpus that they are now suspended. 88147e07394SAndrew Turner */ 88247e07394SAndrew Turner for (i = 0; i < vm->maxcpus; i++) { 88347e07394SAndrew Turner if (CPU_ISSET(i, &vm->active_cpus)) 88447e07394SAndrew Turner vcpu_notify_event(vm_vcpu(vm, i)); 88547e07394SAndrew Turner } 88647e07394SAndrew Turner 88747e07394SAndrew Turner return (0); 88847e07394SAndrew Turner } 88947e07394SAndrew Turner 89047e07394SAndrew Turner void 89147e07394SAndrew Turner vm_exit_suspended(struct vcpu *vcpu, uint64_t pc) 89247e07394SAndrew Turner { 89347e07394SAndrew Turner struct vm *vm = vcpu->vm; 89447e07394SAndrew Turner struct vm_exit *vmexit; 89547e07394SAndrew Turner 89647e07394SAndrew Turner KASSERT(vm->suspend > VM_SUSPEND_NONE && vm->suspend < VM_SUSPEND_LAST, 89747e07394SAndrew Turner ("vm_exit_suspended: invalid suspend type %d", vm->suspend)); 89847e07394SAndrew Turner 89947e07394SAndrew Turner vmexit = vm_exitinfo(vcpu); 90047e07394SAndrew Turner vmexit->pc = pc; 90147e07394SAndrew Turner vmexit->inst_length = 4; 90247e07394SAndrew Turner vmexit->exitcode = VM_EXITCODE_SUSPENDED; 90347e07394SAndrew Turner vmexit->u.suspended.how = vm->suspend; 90447e07394SAndrew Turner } 90547e07394SAndrew Turner 90647e07394SAndrew Turner void 90747e07394SAndrew Turner vm_exit_debug(struct vcpu *vcpu, uint64_t pc) 90847e07394SAndrew Turner { 90947e07394SAndrew Turner struct vm_exit *vmexit; 91047e07394SAndrew Turner 91147e07394SAndrew Turner vmexit = vm_exitinfo(vcpu); 91247e07394SAndrew Turner vmexit->pc = pc; 91347e07394SAndrew Turner vmexit->inst_length = 4; 91447e07394SAndrew Turner vmexit->exitcode = VM_EXITCODE_DEBUG; 91547e07394SAndrew Turner } 91647e07394SAndrew Turner 91747e07394SAndrew Turner int 91847e07394SAndrew Turner vm_activate_cpu(struct vcpu *vcpu) 91947e07394SAndrew Turner { 92047e07394SAndrew Turner struct vm *vm = vcpu->vm; 92147e07394SAndrew Turner 92247e07394SAndrew Turner if (CPU_ISSET(vcpu->vcpuid, &vm->active_cpus)) 92347e07394SAndrew Turner return (EBUSY); 92447e07394SAndrew Turner 92547e07394SAndrew Turner CPU_SET_ATOMIC(vcpu->vcpuid, &vm->active_cpus); 92647e07394SAndrew Turner return (0); 92747e07394SAndrew Turner 92847e07394SAndrew Turner } 92947e07394SAndrew Turner 93047e07394SAndrew Turner int 93147e07394SAndrew Turner vm_suspend_cpu(struct vm *vm, struct vcpu *vcpu) 93247e07394SAndrew Turner { 93347e07394SAndrew Turner if (vcpu == NULL) { 93447e07394SAndrew Turner vm->debug_cpus = vm->active_cpus; 93547e07394SAndrew Turner for (int i = 0; i < vm->maxcpus; i++) { 93647e07394SAndrew Turner if (CPU_ISSET(i, &vm->active_cpus)) 93747e07394SAndrew Turner vcpu_notify_event(vm_vcpu(vm, i)); 93847e07394SAndrew Turner } 93947e07394SAndrew Turner } else { 94047e07394SAndrew Turner if (!CPU_ISSET(vcpu->vcpuid, &vm->active_cpus)) 94147e07394SAndrew Turner return (EINVAL); 94247e07394SAndrew Turner 94347e07394SAndrew Turner CPU_SET_ATOMIC(vcpu->vcpuid, &vm->debug_cpus); 94447e07394SAndrew Turner vcpu_notify_event(vcpu); 94547e07394SAndrew Turner } 94647e07394SAndrew Turner return (0); 94747e07394SAndrew Turner } 94847e07394SAndrew Turner 94947e07394SAndrew Turner int 95047e07394SAndrew Turner vm_resume_cpu(struct vm *vm, struct vcpu *vcpu) 95147e07394SAndrew Turner { 95247e07394SAndrew Turner 95347e07394SAndrew Turner if (vcpu == NULL) { 95447e07394SAndrew Turner CPU_ZERO(&vm->debug_cpus); 95547e07394SAndrew Turner } else { 95647e07394SAndrew Turner if (!CPU_ISSET(vcpu->vcpuid, &vm->debug_cpus)) 95747e07394SAndrew Turner return (EINVAL); 95847e07394SAndrew Turner 95947e07394SAndrew Turner CPU_CLR_ATOMIC(vcpu->vcpuid, &vm->debug_cpus); 96047e07394SAndrew Turner } 96147e07394SAndrew Turner return (0); 96247e07394SAndrew Turner } 96347e07394SAndrew Turner 96447e07394SAndrew Turner int 96547e07394SAndrew Turner vcpu_debugged(struct vcpu *vcpu) 96647e07394SAndrew Turner { 96747e07394SAndrew Turner 96847e07394SAndrew Turner return (CPU_ISSET(vcpu->vcpuid, &vcpu->vm->debug_cpus)); 96947e07394SAndrew Turner } 97047e07394SAndrew Turner 97147e07394SAndrew Turner cpuset_t 97247e07394SAndrew Turner vm_active_cpus(struct vm *vm) 97347e07394SAndrew Turner { 97447e07394SAndrew Turner 97547e07394SAndrew Turner return (vm->active_cpus); 97647e07394SAndrew Turner } 97747e07394SAndrew Turner 97847e07394SAndrew Turner cpuset_t 97947e07394SAndrew Turner vm_debug_cpus(struct vm *vm) 98047e07394SAndrew Turner { 98147e07394SAndrew Turner 98247e07394SAndrew Turner return (vm->debug_cpus); 98347e07394SAndrew Turner } 98447e07394SAndrew Turner 98547e07394SAndrew Turner cpuset_t 98647e07394SAndrew Turner vm_suspended_cpus(struct vm *vm) 98747e07394SAndrew Turner { 98847e07394SAndrew Turner 98947e07394SAndrew Turner return (vm->suspended_cpus); 99047e07394SAndrew Turner } 99147e07394SAndrew Turner 99247e07394SAndrew Turner 99347e07394SAndrew Turner void * 99447e07394SAndrew Turner vcpu_stats(struct vcpu *vcpu) 99547e07394SAndrew Turner { 99647e07394SAndrew Turner 99747e07394SAndrew Turner return (vcpu->stats); 99847e07394SAndrew Turner } 99947e07394SAndrew Turner 100047e07394SAndrew Turner /* 100147e07394SAndrew Turner * This function is called to ensure that a vcpu "sees" a pending event 100247e07394SAndrew Turner * as soon as possible: 100347e07394SAndrew Turner * - If the vcpu thread is sleeping then it is woken up. 100447e07394SAndrew Turner * - If the vcpu is running on a different host_cpu then an IPI will be directed 100547e07394SAndrew Turner * to the host_cpu to cause the vcpu to trap into the hypervisor. 100647e07394SAndrew Turner */ 100747e07394SAndrew Turner static void 100847e07394SAndrew Turner vcpu_notify_event_locked(struct vcpu *vcpu) 100947e07394SAndrew Turner { 101047e07394SAndrew Turner int hostcpu; 101147e07394SAndrew Turner 101247e07394SAndrew Turner hostcpu = vcpu->hostcpu; 101347e07394SAndrew Turner if (vcpu->state == VCPU_RUNNING) { 101447e07394SAndrew Turner KASSERT(hostcpu != NOCPU, ("vcpu running on invalid hostcpu")); 101547e07394SAndrew Turner if (hostcpu != curcpu) { 101647e07394SAndrew Turner ipi_cpu(hostcpu, vmm_ipinum); 101747e07394SAndrew Turner } else { 101847e07394SAndrew Turner /* 101947e07394SAndrew Turner * If the 'vcpu' is running on 'curcpu' then it must 102047e07394SAndrew Turner * be sending a notification to itself (e.g. SELF_IPI). 102147e07394SAndrew Turner * The pending event will be picked up when the vcpu 102247e07394SAndrew Turner * transitions back to guest context. 102347e07394SAndrew Turner */ 102447e07394SAndrew Turner } 102547e07394SAndrew Turner } else { 102647e07394SAndrew Turner KASSERT(hostcpu == NOCPU, ("vcpu state %d not consistent " 102747e07394SAndrew Turner "with hostcpu %d", vcpu->state, hostcpu)); 102847e07394SAndrew Turner if (vcpu->state == VCPU_SLEEPING) 102947e07394SAndrew Turner wakeup_one(vcpu); 103047e07394SAndrew Turner } 103147e07394SAndrew Turner } 103247e07394SAndrew Turner 103347e07394SAndrew Turner void 103447e07394SAndrew Turner vcpu_notify_event(struct vcpu *vcpu) 103547e07394SAndrew Turner { 103647e07394SAndrew Turner vcpu_lock(vcpu); 103747e07394SAndrew Turner vcpu_notify_event_locked(vcpu); 103847e07394SAndrew Turner vcpu_unlock(vcpu); 103947e07394SAndrew Turner } 104047e07394SAndrew Turner 1041*c76c2a19SMark Johnston struct vmspace * 1042*c76c2a19SMark Johnston vm_vmspace(struct vm *vm) 1043*c76c2a19SMark Johnston { 1044*c76c2a19SMark Johnston return (vm->vmspace); 1045*c76c2a19SMark Johnston } 1046*c76c2a19SMark Johnston 1047*c76c2a19SMark Johnston struct vm_mem * 1048*c76c2a19SMark Johnston vm_mem(struct vm *vm) 1049*c76c2a19SMark Johnston { 1050*c76c2a19SMark Johnston return (&vm->mem); 1051*c76c2a19SMark Johnston } 1052*c76c2a19SMark Johnston 105347e07394SAndrew Turner static void 105447e07394SAndrew Turner restore_guest_fpustate(struct vcpu *vcpu) 105547e07394SAndrew Turner { 105647e07394SAndrew Turner 105747e07394SAndrew Turner /* flush host state to the pcb */ 105847e07394SAndrew Turner vfp_save_state(curthread, curthread->td_pcb); 105947e07394SAndrew Turner /* Ensure the VFP state will be re-loaded when exiting the guest */ 106047e07394SAndrew Turner PCPU_SET(fpcurthread, NULL); 106147e07394SAndrew Turner 106247e07394SAndrew Turner /* restore guest FPU state */ 106347e07394SAndrew Turner vfp_enable(); 106447e07394SAndrew Turner vfp_restore(vcpu->guestfpu); 106547e07394SAndrew Turner 106647e07394SAndrew Turner /* 106747e07394SAndrew Turner * The FPU is now "dirty" with the guest's state so turn on emulation 106847e07394SAndrew Turner * to trap any access to the FPU by the host. 106947e07394SAndrew Turner */ 107047e07394SAndrew Turner vfp_disable(); 107147e07394SAndrew Turner } 107247e07394SAndrew Turner 107347e07394SAndrew Turner static void 107447e07394SAndrew Turner save_guest_fpustate(struct vcpu *vcpu) 107547e07394SAndrew Turner { 107647e07394SAndrew Turner if ((READ_SPECIALREG(cpacr_el1) & CPACR_FPEN_MASK) != 107747e07394SAndrew Turner CPACR_FPEN_TRAP_ALL1) 107847e07394SAndrew Turner panic("VFP not enabled in host!"); 107947e07394SAndrew Turner 108047e07394SAndrew Turner /* save guest FPU state */ 108147e07394SAndrew Turner vfp_enable(); 108247e07394SAndrew Turner vfp_store(vcpu->guestfpu); 108347e07394SAndrew Turner vfp_disable(); 108447e07394SAndrew Turner 108547e07394SAndrew Turner KASSERT(PCPU_GET(fpcurthread) == NULL, 108647e07394SAndrew Turner ("%s: fpcurthread set with guest registers", __func__)); 108747e07394SAndrew Turner } 108847e07394SAndrew Turner static int 108947e07394SAndrew Turner vcpu_set_state_locked(struct vcpu *vcpu, enum vcpu_state newstate, 109047e07394SAndrew Turner bool from_idle) 109147e07394SAndrew Turner { 109247e07394SAndrew Turner int error; 109347e07394SAndrew Turner 109447e07394SAndrew Turner vcpu_assert_locked(vcpu); 109547e07394SAndrew Turner 109647e07394SAndrew Turner /* 109747e07394SAndrew Turner * State transitions from the vmmdev_ioctl() must always begin from 109847e07394SAndrew Turner * the VCPU_IDLE state. This guarantees that there is only a single 109947e07394SAndrew Turner * ioctl() operating on a vcpu at any point. 110047e07394SAndrew Turner */ 110147e07394SAndrew Turner if (from_idle) { 110247e07394SAndrew Turner while (vcpu->state != VCPU_IDLE) { 110347e07394SAndrew Turner vcpu_notify_event_locked(vcpu); 110447e07394SAndrew Turner msleep_spin(&vcpu->state, &vcpu->mtx, "vmstat", hz); 110547e07394SAndrew Turner } 110647e07394SAndrew Turner } else { 110747e07394SAndrew Turner KASSERT(vcpu->state != VCPU_IDLE, ("invalid transition from " 110847e07394SAndrew Turner "vcpu idle state")); 110947e07394SAndrew Turner } 111047e07394SAndrew Turner 111147e07394SAndrew Turner if (vcpu->state == VCPU_RUNNING) { 111247e07394SAndrew Turner KASSERT(vcpu->hostcpu == curcpu, ("curcpu %d and hostcpu %d " 111347e07394SAndrew Turner "mismatch for running vcpu", curcpu, vcpu->hostcpu)); 111447e07394SAndrew Turner } else { 111547e07394SAndrew Turner KASSERT(vcpu->hostcpu == NOCPU, ("Invalid hostcpu %d for a " 111647e07394SAndrew Turner "vcpu that is not running", vcpu->hostcpu)); 111747e07394SAndrew Turner } 111847e07394SAndrew Turner 111947e07394SAndrew Turner /* 112047e07394SAndrew Turner * The following state transitions are allowed: 112147e07394SAndrew Turner * IDLE -> FROZEN -> IDLE 112247e07394SAndrew Turner * FROZEN -> RUNNING -> FROZEN 112347e07394SAndrew Turner * FROZEN -> SLEEPING -> FROZEN 112447e07394SAndrew Turner */ 112547e07394SAndrew Turner switch (vcpu->state) { 112647e07394SAndrew Turner case VCPU_IDLE: 112747e07394SAndrew Turner case VCPU_RUNNING: 112847e07394SAndrew Turner case VCPU_SLEEPING: 112947e07394SAndrew Turner error = (newstate != VCPU_FROZEN); 113047e07394SAndrew Turner break; 113147e07394SAndrew Turner case VCPU_FROZEN: 113247e07394SAndrew Turner error = (newstate == VCPU_FROZEN); 113347e07394SAndrew Turner break; 113447e07394SAndrew Turner default: 113547e07394SAndrew Turner error = 1; 113647e07394SAndrew Turner break; 113747e07394SAndrew Turner } 113847e07394SAndrew Turner 113947e07394SAndrew Turner if (error) 114047e07394SAndrew Turner return (EBUSY); 114147e07394SAndrew Turner 114247e07394SAndrew Turner vcpu->state = newstate; 114347e07394SAndrew Turner if (newstate == VCPU_RUNNING) 114447e07394SAndrew Turner vcpu->hostcpu = curcpu; 114547e07394SAndrew Turner else 114647e07394SAndrew Turner vcpu->hostcpu = NOCPU; 114747e07394SAndrew Turner 114847e07394SAndrew Turner if (newstate == VCPU_IDLE) 114947e07394SAndrew Turner wakeup(&vcpu->state); 115047e07394SAndrew Turner 115147e07394SAndrew Turner return (0); 115247e07394SAndrew Turner } 115347e07394SAndrew Turner 115447e07394SAndrew Turner static void 115547e07394SAndrew Turner vcpu_require_state(struct vcpu *vcpu, enum vcpu_state newstate) 115647e07394SAndrew Turner { 115747e07394SAndrew Turner int error; 115847e07394SAndrew Turner 115947e07394SAndrew Turner if ((error = vcpu_set_state(vcpu, newstate, false)) != 0) 116047e07394SAndrew Turner panic("Error %d setting state to %d\n", error, newstate); 116147e07394SAndrew Turner } 116247e07394SAndrew Turner 116347e07394SAndrew Turner static void 116447e07394SAndrew Turner vcpu_require_state_locked(struct vcpu *vcpu, enum vcpu_state newstate) 116547e07394SAndrew Turner { 116647e07394SAndrew Turner int error; 116747e07394SAndrew Turner 116847e07394SAndrew Turner if ((error = vcpu_set_state_locked(vcpu, newstate, false)) != 0) 116947e07394SAndrew Turner panic("Error %d setting state to %d", error, newstate); 117047e07394SAndrew Turner } 117147e07394SAndrew Turner 117247e07394SAndrew Turner int 117347e07394SAndrew Turner vm_get_capability(struct vcpu *vcpu, int type, int *retval) 117447e07394SAndrew Turner { 117547e07394SAndrew Turner if (type < 0 || type >= VM_CAP_MAX) 117647e07394SAndrew Turner return (EINVAL); 117747e07394SAndrew Turner 117847e07394SAndrew Turner return (vmmops_getcap(vcpu->cookie, type, retval)); 117947e07394SAndrew Turner } 118047e07394SAndrew Turner 118147e07394SAndrew Turner int 118247e07394SAndrew Turner vm_set_capability(struct vcpu *vcpu, int type, int val) 118347e07394SAndrew Turner { 118447e07394SAndrew Turner if (type < 0 || type >= VM_CAP_MAX) 118547e07394SAndrew Turner return (EINVAL); 118647e07394SAndrew Turner 118747e07394SAndrew Turner return (vmmops_setcap(vcpu->cookie, type, val)); 118847e07394SAndrew Turner } 118947e07394SAndrew Turner 119047e07394SAndrew Turner struct vm * 119147e07394SAndrew Turner vcpu_vm(struct vcpu *vcpu) 119247e07394SAndrew Turner { 119347e07394SAndrew Turner return (vcpu->vm); 119447e07394SAndrew Turner } 119547e07394SAndrew Turner 119647e07394SAndrew Turner int 119747e07394SAndrew Turner vcpu_vcpuid(struct vcpu *vcpu) 119847e07394SAndrew Turner { 119947e07394SAndrew Turner return (vcpu->vcpuid); 120047e07394SAndrew Turner } 120147e07394SAndrew Turner 120247e07394SAndrew Turner void * 120347e07394SAndrew Turner vcpu_get_cookie(struct vcpu *vcpu) 120447e07394SAndrew Turner { 120547e07394SAndrew Turner return (vcpu->cookie); 120647e07394SAndrew Turner } 120747e07394SAndrew Turner 120847e07394SAndrew Turner struct vcpu * 120947e07394SAndrew Turner vm_vcpu(struct vm *vm, int vcpuid) 121047e07394SAndrew Turner { 121147e07394SAndrew Turner return (vm->vcpu[vcpuid]); 121247e07394SAndrew Turner } 121347e07394SAndrew Turner 121447e07394SAndrew Turner int 121547e07394SAndrew Turner vcpu_set_state(struct vcpu *vcpu, enum vcpu_state newstate, bool from_idle) 121647e07394SAndrew Turner { 121747e07394SAndrew Turner int error; 121847e07394SAndrew Turner 121947e07394SAndrew Turner vcpu_lock(vcpu); 122047e07394SAndrew Turner error = vcpu_set_state_locked(vcpu, newstate, from_idle); 122147e07394SAndrew Turner vcpu_unlock(vcpu); 122247e07394SAndrew Turner 122347e07394SAndrew Turner return (error); 122447e07394SAndrew Turner } 122547e07394SAndrew Turner 122647e07394SAndrew Turner enum vcpu_state 122747e07394SAndrew Turner vcpu_get_state(struct vcpu *vcpu, int *hostcpu) 122847e07394SAndrew Turner { 122947e07394SAndrew Turner enum vcpu_state state; 123047e07394SAndrew Turner 123147e07394SAndrew Turner vcpu_lock(vcpu); 123247e07394SAndrew Turner state = vcpu->state; 123347e07394SAndrew Turner if (hostcpu != NULL) 123447e07394SAndrew Turner *hostcpu = vcpu->hostcpu; 123547e07394SAndrew Turner vcpu_unlock(vcpu); 123647e07394SAndrew Turner 123747e07394SAndrew Turner return (state); 123847e07394SAndrew Turner } 123947e07394SAndrew Turner 124047e07394SAndrew Turner int 124147e07394SAndrew Turner vm_get_register(struct vcpu *vcpu, int reg, uint64_t *retval) 124247e07394SAndrew Turner { 124347e07394SAndrew Turner 124447e07394SAndrew Turner if (reg >= VM_REG_LAST) 124547e07394SAndrew Turner return (EINVAL); 124647e07394SAndrew Turner 124747e07394SAndrew Turner return (vmmops_getreg(vcpu->cookie, reg, retval)); 124847e07394SAndrew Turner } 124947e07394SAndrew Turner 125047e07394SAndrew Turner int 125147e07394SAndrew Turner vm_set_register(struct vcpu *vcpu, int reg, uint64_t val) 125247e07394SAndrew Turner { 125347e07394SAndrew Turner int error; 125447e07394SAndrew Turner 125547e07394SAndrew Turner if (reg >= VM_REG_LAST) 125647e07394SAndrew Turner return (EINVAL); 125747e07394SAndrew Turner error = vmmops_setreg(vcpu->cookie, reg, val); 125847e07394SAndrew Turner if (error || reg != VM_REG_GUEST_PC) 125947e07394SAndrew Turner return (error); 126047e07394SAndrew Turner 126147e07394SAndrew Turner vcpu->nextpc = val; 126247e07394SAndrew Turner 126347e07394SAndrew Turner return (0); 126447e07394SAndrew Turner } 126547e07394SAndrew Turner 126647e07394SAndrew Turner void * 126747e07394SAndrew Turner vm_get_cookie(struct vm *vm) 126847e07394SAndrew Turner { 126947e07394SAndrew Turner return (vm->cookie); 127047e07394SAndrew Turner } 127147e07394SAndrew Turner 127247e07394SAndrew Turner int 127347e07394SAndrew Turner vm_inject_exception(struct vcpu *vcpu, uint64_t esr, uint64_t far) 127447e07394SAndrew Turner { 127547e07394SAndrew Turner return (vmmops_exception(vcpu->cookie, esr, far)); 127647e07394SAndrew Turner } 127747e07394SAndrew Turner 127847e07394SAndrew Turner int 127947e07394SAndrew Turner vm_attach_vgic(struct vm *vm, struct vm_vgic_descr *descr) 128047e07394SAndrew Turner { 128147e07394SAndrew Turner return (vgic_attach_to_vm(vm->cookie, descr)); 128247e07394SAndrew Turner } 128347e07394SAndrew Turner 128447e07394SAndrew Turner int 128547e07394SAndrew Turner vm_assert_irq(struct vm *vm, uint32_t irq) 128647e07394SAndrew Turner { 128747e07394SAndrew Turner return (vgic_inject_irq(vm->cookie, -1, irq, true)); 128847e07394SAndrew Turner } 128947e07394SAndrew Turner 129047e07394SAndrew Turner int 129147e07394SAndrew Turner vm_deassert_irq(struct vm *vm, uint32_t irq) 129247e07394SAndrew Turner { 129347e07394SAndrew Turner return (vgic_inject_irq(vm->cookie, -1, irq, false)); 129447e07394SAndrew Turner } 129547e07394SAndrew Turner 129647e07394SAndrew Turner int 129747e07394SAndrew Turner vm_raise_msi(struct vm *vm, uint64_t msg, uint64_t addr, int bus, int slot, 129847e07394SAndrew Turner int func) 129947e07394SAndrew Turner { 130047e07394SAndrew Turner /* TODO: Should we raise an SError? */ 130147e07394SAndrew Turner return (vgic_inject_msi(vm->cookie, msg, addr)); 130247e07394SAndrew Turner } 130347e07394SAndrew Turner 130447e07394SAndrew Turner static int 130547e07394SAndrew Turner vm_handle_smccc_call(struct vcpu *vcpu, struct vm_exit *vme, bool *retu) 130647e07394SAndrew Turner { 130747e07394SAndrew Turner struct hypctx *hypctx; 130847e07394SAndrew Turner int i; 130947e07394SAndrew Turner 131047e07394SAndrew Turner hypctx = vcpu_get_cookie(vcpu); 131147e07394SAndrew Turner 131247e07394SAndrew Turner if ((hypctx->tf.tf_esr & ESR_ELx_ISS_MASK) != 0) 131347e07394SAndrew Turner return (1); 131447e07394SAndrew Turner 131547e07394SAndrew Turner vme->exitcode = VM_EXITCODE_SMCCC; 131647e07394SAndrew Turner vme->u.smccc_call.func_id = hypctx->tf.tf_x[0]; 131747e07394SAndrew Turner for (i = 0; i < nitems(vme->u.smccc_call.args); i++) 131847e07394SAndrew Turner vme->u.smccc_call.args[i] = hypctx->tf.tf_x[i + 1]; 131947e07394SAndrew Turner 132047e07394SAndrew Turner *retu = true; 132147e07394SAndrew Turner return (0); 132247e07394SAndrew Turner } 132347e07394SAndrew Turner 132447e07394SAndrew Turner static int 132547e07394SAndrew Turner vm_handle_wfi(struct vcpu *vcpu, struct vm_exit *vme, bool *retu) 132647e07394SAndrew Turner { 132747e07394SAndrew Turner vcpu_lock(vcpu); 132847e07394SAndrew Turner while (1) { 132947e07394SAndrew Turner if (vgic_has_pending_irq(vcpu->cookie)) 133047e07394SAndrew Turner break; 133147e07394SAndrew Turner 133247e07394SAndrew Turner if (vcpu_should_yield(vcpu)) 133347e07394SAndrew Turner break; 133447e07394SAndrew Turner 133547e07394SAndrew Turner vcpu_require_state_locked(vcpu, VCPU_SLEEPING); 133647e07394SAndrew Turner /* 133747e07394SAndrew Turner * XXX msleep_spin() cannot be interrupted by signals so 133847e07394SAndrew Turner * wake up periodically to check pending signals. 133947e07394SAndrew Turner */ 134047e07394SAndrew Turner msleep_spin(vcpu, &vcpu->mtx, "vmidle", hz); 134147e07394SAndrew Turner vcpu_require_state_locked(vcpu, VCPU_FROZEN); 134247e07394SAndrew Turner } 134347e07394SAndrew Turner vcpu_unlock(vcpu); 134447e07394SAndrew Turner 134547e07394SAndrew Turner *retu = false; 134647e07394SAndrew Turner return (0); 134747e07394SAndrew Turner } 134847e07394SAndrew Turner 134947e07394SAndrew Turner static int 135047e07394SAndrew Turner vm_handle_paging(struct vcpu *vcpu, bool *retu) 135147e07394SAndrew Turner { 135247e07394SAndrew Turner struct vm *vm = vcpu->vm; 135347e07394SAndrew Turner struct vm_exit *vme; 135447e07394SAndrew Turner struct vm_map *map; 135547e07394SAndrew Turner uint64_t addr, esr; 135647e07394SAndrew Turner pmap_t pmap; 135747e07394SAndrew Turner int ftype, rv; 135847e07394SAndrew Turner 135947e07394SAndrew Turner vme = &vcpu->exitinfo; 136047e07394SAndrew Turner 136147e07394SAndrew Turner pmap = vmspace_pmap(vcpu->vm->vmspace); 136247e07394SAndrew Turner addr = vme->u.paging.gpa; 136347e07394SAndrew Turner esr = vme->u.paging.esr; 136447e07394SAndrew Turner 136547e07394SAndrew Turner /* The page exists, but the page table needs to be updated. */ 136647e07394SAndrew Turner if (pmap_fault(pmap, esr, addr) == KERN_SUCCESS) 136747e07394SAndrew Turner return (0); 136847e07394SAndrew Turner 136947e07394SAndrew Turner switch (ESR_ELx_EXCEPTION(esr)) { 137047e07394SAndrew Turner case EXCP_INSN_ABORT_L: 137147e07394SAndrew Turner case EXCP_DATA_ABORT_L: 137247e07394SAndrew Turner ftype = VM_PROT_EXECUTE | VM_PROT_READ | VM_PROT_WRITE; 137347e07394SAndrew Turner break; 137447e07394SAndrew Turner default: 137547e07394SAndrew Turner panic("%s: Invalid exception (esr = %lx)", __func__, esr); 137647e07394SAndrew Turner } 137747e07394SAndrew Turner 137847e07394SAndrew Turner map = &vm->vmspace->vm_map; 137947e07394SAndrew Turner rv = vm_fault(map, vme->u.paging.gpa, ftype, VM_FAULT_NORMAL, NULL); 138047e07394SAndrew Turner if (rv != KERN_SUCCESS) 138147e07394SAndrew Turner return (EFAULT); 138247e07394SAndrew Turner 138347e07394SAndrew Turner return (0); 138447e07394SAndrew Turner } 138547e07394SAndrew Turner 13861ee7a8faSMark Johnston static int 13871ee7a8faSMark Johnston vm_handle_suspend(struct vcpu *vcpu, bool *retu) 13881ee7a8faSMark Johnston { 13891ee7a8faSMark Johnston struct vm *vm = vcpu->vm; 13901ee7a8faSMark Johnston int error, i; 13911ee7a8faSMark Johnston struct thread *td; 13921ee7a8faSMark Johnston 13931ee7a8faSMark Johnston error = 0; 13941ee7a8faSMark Johnston td = curthread; 13951ee7a8faSMark Johnston 13961ee7a8faSMark Johnston CPU_SET_ATOMIC(vcpu->vcpuid, &vm->suspended_cpus); 13971ee7a8faSMark Johnston 13981ee7a8faSMark Johnston /* 13991ee7a8faSMark Johnston * Wait until all 'active_cpus' have suspended themselves. 14001ee7a8faSMark Johnston * 14011ee7a8faSMark Johnston * Since a VM may be suspended at any time including when one or 14021ee7a8faSMark Johnston * more vcpus are doing a rendezvous we need to call the rendezvous 14031ee7a8faSMark Johnston * handler while we are waiting to prevent a deadlock. 14041ee7a8faSMark Johnston */ 14051ee7a8faSMark Johnston vcpu_lock(vcpu); 14061ee7a8faSMark Johnston while (error == 0) { 14071ee7a8faSMark Johnston if (CPU_CMP(&vm->suspended_cpus, &vm->active_cpus) == 0) 14081ee7a8faSMark Johnston break; 14091ee7a8faSMark Johnston 14101ee7a8faSMark Johnston vcpu_require_state_locked(vcpu, VCPU_SLEEPING); 14111ee7a8faSMark Johnston msleep_spin(vcpu, &vcpu->mtx, "vmsusp", hz); 14121ee7a8faSMark Johnston vcpu_require_state_locked(vcpu, VCPU_FROZEN); 14131ee7a8faSMark Johnston if (td_ast_pending(td, TDA_SUSPEND)) { 14141ee7a8faSMark Johnston vcpu_unlock(vcpu); 14151ee7a8faSMark Johnston error = thread_check_susp(td, false); 14161ee7a8faSMark Johnston vcpu_lock(vcpu); 14171ee7a8faSMark Johnston } 14181ee7a8faSMark Johnston } 14191ee7a8faSMark Johnston vcpu_unlock(vcpu); 14201ee7a8faSMark Johnston 14211ee7a8faSMark Johnston /* 14221ee7a8faSMark Johnston * Wakeup the other sleeping vcpus and return to userspace. 14231ee7a8faSMark Johnston */ 14241ee7a8faSMark Johnston for (i = 0; i < vm->maxcpus; i++) { 14251ee7a8faSMark Johnston if (CPU_ISSET(i, &vm->suspended_cpus)) { 14261ee7a8faSMark Johnston vcpu_notify_event(vm_vcpu(vm, i)); 14271ee7a8faSMark Johnston } 14281ee7a8faSMark Johnston } 14291ee7a8faSMark Johnston 14301ee7a8faSMark Johnston *retu = true; 14311ee7a8faSMark Johnston return (error); 14321ee7a8faSMark Johnston } 14331ee7a8faSMark Johnston 143447e07394SAndrew Turner int 143547e07394SAndrew Turner vm_run(struct vcpu *vcpu) 143647e07394SAndrew Turner { 143747e07394SAndrew Turner struct vm *vm = vcpu->vm; 143847e07394SAndrew Turner struct vm_eventinfo evinfo; 143947e07394SAndrew Turner int error, vcpuid; 144047e07394SAndrew Turner struct vm_exit *vme; 144147e07394SAndrew Turner bool retu; 144247e07394SAndrew Turner pmap_t pmap; 144347e07394SAndrew Turner 144447e07394SAndrew Turner vcpuid = vcpu->vcpuid; 144547e07394SAndrew Turner 144647e07394SAndrew Turner if (!CPU_ISSET(vcpuid, &vm->active_cpus)) 144747e07394SAndrew Turner return (EINVAL); 144847e07394SAndrew Turner 144947e07394SAndrew Turner if (CPU_ISSET(vcpuid, &vm->suspended_cpus)) 145047e07394SAndrew Turner return (EINVAL); 145147e07394SAndrew Turner 145247e07394SAndrew Turner pmap = vmspace_pmap(vm->vmspace); 145347e07394SAndrew Turner vme = &vcpu->exitinfo; 145447e07394SAndrew Turner evinfo.rptr = NULL; 145547e07394SAndrew Turner evinfo.sptr = &vm->suspend; 145647e07394SAndrew Turner evinfo.iptr = NULL; 145747e07394SAndrew Turner restart: 145847e07394SAndrew Turner critical_enter(); 145947e07394SAndrew Turner 146047e07394SAndrew Turner restore_guest_fpustate(vcpu); 146147e07394SAndrew Turner 146247e07394SAndrew Turner vcpu_require_state(vcpu, VCPU_RUNNING); 146347e07394SAndrew Turner error = vmmops_run(vcpu->cookie, vcpu->nextpc, pmap, &evinfo); 146447e07394SAndrew Turner vcpu_require_state(vcpu, VCPU_FROZEN); 146547e07394SAndrew Turner 146647e07394SAndrew Turner save_guest_fpustate(vcpu); 146747e07394SAndrew Turner 146847e07394SAndrew Turner critical_exit(); 146947e07394SAndrew Turner 147047e07394SAndrew Turner if (error == 0) { 147147e07394SAndrew Turner retu = false; 147247e07394SAndrew Turner switch (vme->exitcode) { 147347e07394SAndrew Turner case VM_EXITCODE_INST_EMUL: 147447e07394SAndrew Turner vcpu->nextpc = vme->pc + vme->inst_length; 147547e07394SAndrew Turner error = vm_handle_inst_emul(vcpu, &retu); 147647e07394SAndrew Turner break; 147747e07394SAndrew Turner 147847e07394SAndrew Turner case VM_EXITCODE_REG_EMUL: 147947e07394SAndrew Turner vcpu->nextpc = vme->pc + vme->inst_length; 148047e07394SAndrew Turner error = vm_handle_reg_emul(vcpu, &retu); 148147e07394SAndrew Turner break; 148247e07394SAndrew Turner 148347e07394SAndrew Turner case VM_EXITCODE_HVC: 148447e07394SAndrew Turner /* 148547e07394SAndrew Turner * The HVC instruction saves the address for the 148647e07394SAndrew Turner * next instruction as the return address. 148747e07394SAndrew Turner */ 148847e07394SAndrew Turner vcpu->nextpc = vme->pc; 148947e07394SAndrew Turner /* 149047e07394SAndrew Turner * The PSCI call can change the exit information in the 149147e07394SAndrew Turner * case of suspend/reset/poweroff/cpu off/cpu on. 149247e07394SAndrew Turner */ 149347e07394SAndrew Turner error = vm_handle_smccc_call(vcpu, vme, &retu); 149447e07394SAndrew Turner break; 149547e07394SAndrew Turner 149647e07394SAndrew Turner case VM_EXITCODE_WFI: 149747e07394SAndrew Turner vcpu->nextpc = vme->pc + vme->inst_length; 149847e07394SAndrew Turner error = vm_handle_wfi(vcpu, vme, &retu); 149947e07394SAndrew Turner break; 150047e07394SAndrew Turner 150147e07394SAndrew Turner case VM_EXITCODE_PAGING: 150247e07394SAndrew Turner vcpu->nextpc = vme->pc; 150347e07394SAndrew Turner error = vm_handle_paging(vcpu, &retu); 150447e07394SAndrew Turner break; 150547e07394SAndrew Turner 15061ee7a8faSMark Johnston case VM_EXITCODE_SUSPENDED: 15071ee7a8faSMark Johnston vcpu->nextpc = vme->pc; 15081ee7a8faSMark Johnston error = vm_handle_suspend(vcpu, &retu); 15091ee7a8faSMark Johnston break; 15101ee7a8faSMark Johnston 151147e07394SAndrew Turner default: 151247e07394SAndrew Turner /* Handle in userland */ 151347e07394SAndrew Turner vcpu->nextpc = vme->pc; 151447e07394SAndrew Turner retu = true; 151547e07394SAndrew Turner break; 151647e07394SAndrew Turner } 151747e07394SAndrew Turner } 151847e07394SAndrew Turner 151947e07394SAndrew Turner if (error == 0 && retu == false) 152047e07394SAndrew Turner goto restart; 152147e07394SAndrew Turner 152247e07394SAndrew Turner return (error); 152347e07394SAndrew Turner } 1524