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> 6893e81baaSMark Johnston #include <dev/vmm/vmm_stat.h> 6947e07394SAndrew Turner 7047e07394SAndrew Turner #include "arm64.h" 7147e07394SAndrew Turner #include "mmu.h" 7247e07394SAndrew Turner 7347e07394SAndrew Turner #include "io/vgic.h" 7447e07394SAndrew Turner #include "io/vtimer.h" 7547e07394SAndrew Turner 7647e07394SAndrew Turner struct vcpu { 7747e07394SAndrew Turner int flags; 7847e07394SAndrew Turner enum vcpu_state state; 7947e07394SAndrew Turner struct mtx mtx; 8047e07394SAndrew Turner int hostcpu; /* host cpuid this vcpu last ran on */ 8147e07394SAndrew Turner int vcpuid; 8247e07394SAndrew Turner void *stats; 8347e07394SAndrew Turner struct vm_exit exitinfo; 8447e07394SAndrew Turner uint64_t nextpc; /* (x) next instruction to execute */ 8547e07394SAndrew Turner struct vm *vm; /* (o) */ 8647e07394SAndrew Turner void *cookie; /* (i) cpu-specific data */ 8747e07394SAndrew Turner struct vfpstate *guestfpu; /* (a,i) guest fpu state */ 8847e07394SAndrew Turner }; 8947e07394SAndrew Turner 9047e07394SAndrew Turner #define vcpu_lock_initialized(v) mtx_initialized(&((v)->mtx)) 9147e07394SAndrew Turner #define vcpu_lock_init(v) mtx_init(&((v)->mtx), "vcpu lock", 0, MTX_SPIN) 9247e07394SAndrew Turner #define vcpu_lock_destroy(v) mtx_destroy(&((v)->mtx)) 9347e07394SAndrew Turner #define vcpu_lock(v) mtx_lock_spin(&((v)->mtx)) 9447e07394SAndrew Turner #define vcpu_unlock(v) mtx_unlock_spin(&((v)->mtx)) 9547e07394SAndrew Turner #define vcpu_assert_locked(v) mtx_assert(&((v)->mtx), MA_OWNED) 9647e07394SAndrew Turner 9747e07394SAndrew Turner struct mem_seg { 9847e07394SAndrew Turner uint64_t gpa; 9947e07394SAndrew Turner size_t len; 10047e07394SAndrew Turner bool wired; 10147e07394SAndrew Turner bool sysmem; 10247e07394SAndrew Turner vm_object_t object; 10347e07394SAndrew Turner }; 10447e07394SAndrew Turner #define VM_MAX_MEMSEGS 3 10547e07394SAndrew Turner 10647e07394SAndrew Turner struct mem_map { 10747e07394SAndrew Turner vm_paddr_t gpa; 10847e07394SAndrew Turner size_t len; 10947e07394SAndrew Turner vm_ooffset_t segoff; 11047e07394SAndrew Turner int segid; 11147e07394SAndrew Turner int prot; 11247e07394SAndrew Turner int flags; 11347e07394SAndrew Turner }; 11447e07394SAndrew Turner #define VM_MAX_MEMMAPS 4 11547e07394SAndrew Turner 11647e07394SAndrew Turner struct vmm_mmio_region { 11747e07394SAndrew Turner uint64_t start; 11847e07394SAndrew Turner uint64_t end; 11947e07394SAndrew Turner mem_region_read_t read; 12047e07394SAndrew Turner mem_region_write_t write; 12147e07394SAndrew Turner }; 12247e07394SAndrew Turner #define VM_MAX_MMIO_REGIONS 4 12347e07394SAndrew Turner 12447e07394SAndrew Turner struct vmm_special_reg { 12547e07394SAndrew Turner uint32_t esr_iss; 12647e07394SAndrew Turner uint32_t esr_mask; 12747e07394SAndrew Turner reg_read_t reg_read; 12847e07394SAndrew Turner reg_write_t reg_write; 12947e07394SAndrew Turner void *arg; 13047e07394SAndrew Turner }; 13147e07394SAndrew Turner #define VM_MAX_SPECIAL_REGS 16 13247e07394SAndrew Turner 13347e07394SAndrew Turner /* 13447e07394SAndrew Turner * Initialization: 13547e07394SAndrew Turner * (o) initialized the first time the VM is created 13647e07394SAndrew Turner * (i) initialized when VM is created and when it is reinitialized 13747e07394SAndrew Turner * (x) initialized before use 13847e07394SAndrew Turner */ 13947e07394SAndrew Turner struct vm { 14047e07394SAndrew Turner void *cookie; /* (i) cpu-specific data */ 14147e07394SAndrew Turner volatile cpuset_t active_cpus; /* (i) active vcpus */ 14247e07394SAndrew Turner volatile cpuset_t debug_cpus; /* (i) vcpus stopped for debug */ 14347e07394SAndrew Turner int suspend; /* (i) stop VM execution */ 144a03354b0SMark Johnston bool dying; /* (o) is dying */ 14547e07394SAndrew Turner volatile cpuset_t suspended_cpus; /* (i) suspended vcpus */ 14647e07394SAndrew Turner volatile cpuset_t halted_cpus; /* (x) cpus in a hard halt */ 14747e07394SAndrew Turner struct mem_map mem_maps[VM_MAX_MEMMAPS]; /* (i) guest address space */ 14847e07394SAndrew Turner struct mem_seg mem_segs[VM_MAX_MEMSEGS]; /* (o) guest memory regions */ 14947e07394SAndrew Turner struct vmspace *vmspace; /* (o) guest's address space */ 15047e07394SAndrew Turner char name[VM_MAX_NAMELEN]; /* (o) virtual machine name */ 15147e07394SAndrew Turner struct vcpu **vcpu; /* (i) guest vcpus */ 15247e07394SAndrew Turner struct vmm_mmio_region mmio_region[VM_MAX_MMIO_REGIONS]; 15347e07394SAndrew Turner /* (o) guest MMIO regions */ 15447e07394SAndrew Turner struct vmm_special_reg special_reg[VM_MAX_SPECIAL_REGS]; 15547e07394SAndrew Turner /* The following describe the vm cpu topology */ 15647e07394SAndrew Turner uint16_t sockets; /* (o) num of sockets */ 15747e07394SAndrew Turner uint16_t cores; /* (o) num of cores/socket */ 15847e07394SAndrew Turner uint16_t threads; /* (o) num of threads/core */ 15947e07394SAndrew Turner uint16_t maxcpus; /* (o) max pluggable cpus */ 16047e07394SAndrew Turner struct sx mem_segs_lock; /* (o) */ 16147e07394SAndrew Turner struct sx vcpus_init_lock; /* (o) */ 16247e07394SAndrew Turner }; 16347e07394SAndrew Turner 16447e07394SAndrew Turner static bool vmm_initialized = false; 16547e07394SAndrew Turner 16647e07394SAndrew Turner static int vm_handle_wfi(struct vcpu *vcpu, 16747e07394SAndrew Turner struct vm_exit *vme, bool *retu); 16847e07394SAndrew Turner 16947e07394SAndrew Turner static MALLOC_DEFINE(M_VMM, "vmm", "vmm"); 17047e07394SAndrew Turner 17147e07394SAndrew Turner /* statistics */ 17247e07394SAndrew Turner static VMM_STAT(VCPU_TOTAL_RUNTIME, "vcpu total runtime"); 17347e07394SAndrew Turner 17447e07394SAndrew Turner SYSCTL_NODE(_hw, OID_AUTO, vmm, CTLFLAG_RW, NULL, NULL); 17547e07394SAndrew Turner 17647e07394SAndrew Turner static int vmm_ipinum; 17747e07394SAndrew Turner SYSCTL_INT(_hw_vmm, OID_AUTO, ipinum, CTLFLAG_RD, &vmm_ipinum, 0, 17847e07394SAndrew Turner "IPI vector used for vcpu notifications"); 17947e07394SAndrew Turner 18047e07394SAndrew Turner struct vmm_regs { 18147e07394SAndrew Turner uint64_t id_aa64afr0; 18247e07394SAndrew Turner uint64_t id_aa64afr1; 18347e07394SAndrew Turner uint64_t id_aa64dfr0; 18447e07394SAndrew Turner uint64_t id_aa64dfr1; 18547e07394SAndrew Turner uint64_t id_aa64isar0; 18647e07394SAndrew Turner uint64_t id_aa64isar1; 18747e07394SAndrew Turner uint64_t id_aa64isar2; 18847e07394SAndrew Turner uint64_t id_aa64mmfr0; 18947e07394SAndrew Turner uint64_t id_aa64mmfr1; 19047e07394SAndrew Turner uint64_t id_aa64mmfr2; 19147e07394SAndrew Turner uint64_t id_aa64pfr0; 19247e07394SAndrew Turner uint64_t id_aa64pfr1; 19347e07394SAndrew Turner }; 19447e07394SAndrew Turner 19547e07394SAndrew Turner static const struct vmm_regs vmm_arch_regs_masks = { 19647e07394SAndrew Turner .id_aa64dfr0 = 19747e07394SAndrew Turner ID_AA64DFR0_CTX_CMPs_MASK | 19847e07394SAndrew Turner ID_AA64DFR0_WRPs_MASK | 19947e07394SAndrew Turner ID_AA64DFR0_BRPs_MASK | 20047e07394SAndrew Turner ID_AA64DFR0_PMUVer_3 | 20147e07394SAndrew Turner ID_AA64DFR0_DebugVer_8, 20247e07394SAndrew Turner .id_aa64isar0 = 20347e07394SAndrew Turner ID_AA64ISAR0_TLB_TLBIOSR | 20447e07394SAndrew Turner ID_AA64ISAR0_SHA3_IMPL | 20547e07394SAndrew Turner ID_AA64ISAR0_RDM_IMPL | 20647e07394SAndrew Turner ID_AA64ISAR0_Atomic_IMPL | 20747e07394SAndrew Turner ID_AA64ISAR0_CRC32_BASE | 20847e07394SAndrew Turner ID_AA64ISAR0_SHA2_512 | 20947e07394SAndrew Turner ID_AA64ISAR0_SHA1_BASE | 21047e07394SAndrew Turner ID_AA64ISAR0_AES_PMULL, 21147e07394SAndrew Turner .id_aa64mmfr0 = 21247e07394SAndrew Turner ID_AA64MMFR0_TGran4_IMPL | 21347e07394SAndrew Turner ID_AA64MMFR0_TGran64_IMPL | 21447e07394SAndrew Turner ID_AA64MMFR0_TGran16_IMPL | 21547e07394SAndrew Turner ID_AA64MMFR0_ASIDBits_16 | 21647e07394SAndrew Turner ID_AA64MMFR0_PARange_4P, 21747e07394SAndrew Turner .id_aa64mmfr1 = 21847e07394SAndrew Turner ID_AA64MMFR1_SpecSEI_IMPL | 21947e07394SAndrew Turner ID_AA64MMFR1_PAN_ATS1E1 | 22047e07394SAndrew Turner ID_AA64MMFR1_HAFDBS_AF, 22147e07394SAndrew Turner .id_aa64pfr0 = 22247e07394SAndrew Turner ID_AA64PFR0_GIC_CPUIF_NONE | 22347e07394SAndrew Turner ID_AA64PFR0_AdvSIMD_HP | 22447e07394SAndrew Turner ID_AA64PFR0_FP_HP | 22547e07394SAndrew Turner ID_AA64PFR0_EL3_64 | 22647e07394SAndrew Turner ID_AA64PFR0_EL2_64 | 22747e07394SAndrew Turner ID_AA64PFR0_EL1_64 | 22847e07394SAndrew Turner ID_AA64PFR0_EL0_64, 22947e07394SAndrew Turner }; 23047e07394SAndrew Turner 23147e07394SAndrew Turner /* Host registers masked by vmm_arch_regs_masks. */ 23247e07394SAndrew Turner static struct vmm_regs vmm_arch_regs; 23347e07394SAndrew Turner 23447e07394SAndrew Turner u_int vm_maxcpu; 23547e07394SAndrew Turner SYSCTL_UINT(_hw_vmm, OID_AUTO, maxcpu, CTLFLAG_RDTUN | CTLFLAG_NOFETCH, 23647e07394SAndrew Turner &vm_maxcpu, 0, "Maximum number of vCPUs"); 23747e07394SAndrew Turner 23847e07394SAndrew Turner static void vm_free_memmap(struct vm *vm, int ident); 23947e07394SAndrew Turner static bool sysmem_mapping(struct vm *vm, struct mem_map *mm); 24047e07394SAndrew Turner static void vcpu_notify_event_locked(struct vcpu *vcpu); 24147e07394SAndrew Turner 24293e81baaSMark Johnston /* global statistics */ 24393e81baaSMark Johnston VMM_STAT(VMEXIT_COUNT, "total number of vm exits"); 24493e81baaSMark Johnston VMM_STAT(VMEXIT_UNKNOWN, "number of vmexits for the unknown exception"); 24593e81baaSMark Johnston VMM_STAT(VMEXIT_WFI, "number of times wfi was intercepted"); 24693e81baaSMark Johnston VMM_STAT(VMEXIT_WFE, "number of times wfe was intercepted"); 24793e81baaSMark Johnston VMM_STAT(VMEXIT_HVC, "number of times hvc was intercepted"); 24893e81baaSMark Johnston VMM_STAT(VMEXIT_MSR, "number of times msr/mrs was intercepted"); 24993e81baaSMark Johnston VMM_STAT(VMEXIT_DATA_ABORT, "number of vmexits for a data abort"); 25093e81baaSMark Johnston VMM_STAT(VMEXIT_INSN_ABORT, "number of vmexits for an instruction abort"); 25193e81baaSMark Johnston VMM_STAT(VMEXIT_UNHANDLED_SYNC, "number of vmexits for an unhandled synchronous exception"); 25293e81baaSMark Johnston VMM_STAT(VMEXIT_IRQ, "number of vmexits for an irq"); 25393e81baaSMark Johnston VMM_STAT(VMEXIT_FIQ, "number of vmexits for an interrupt"); 25493e81baaSMark Johnston VMM_STAT(VMEXIT_BRK, "number of vmexits for a breakpoint exception"); 25593e81baaSMark Johnston VMM_STAT(VMEXIT_SS, "number of vmexits for a single-step exception"); 25693e81baaSMark Johnston VMM_STAT(VMEXIT_UNHANDLED_EL2, "number of vmexits for an unhandled EL2 exception"); 25793e81baaSMark Johnston VMM_STAT(VMEXIT_UNHANDLED, "number of vmexits for an unhandled exception"); 25893e81baaSMark Johnston 25947e07394SAndrew Turner /* 26047e07394SAndrew Turner * Upper limit on vm_maxcpu. We could increase this to 28 bits, but this 26147e07394SAndrew Turner * is a safe value for now. 26247e07394SAndrew Turner */ 26347e07394SAndrew Turner #define VM_MAXCPU MIN(0xffff - 1, CPU_SETSIZE) 26447e07394SAndrew Turner 26547e07394SAndrew Turner static int 26647e07394SAndrew Turner vmm_regs_init(struct vmm_regs *regs, const struct vmm_regs *masks) 26747e07394SAndrew Turner { 26847e07394SAndrew Turner #define _FETCH_KERN_REG(reg, field) do { \ 26947e07394SAndrew Turner regs->field = vmm_arch_regs_masks.field; \ 27047e07394SAndrew Turner if (!get_kernel_reg_masked(reg, ®s->field, masks->field)) \ 27147e07394SAndrew Turner regs->field = 0; \ 27247e07394SAndrew Turner } while (0) 27347e07394SAndrew Turner _FETCH_KERN_REG(ID_AA64AFR0_EL1, id_aa64afr0); 27447e07394SAndrew Turner _FETCH_KERN_REG(ID_AA64AFR1_EL1, id_aa64afr1); 27547e07394SAndrew Turner _FETCH_KERN_REG(ID_AA64DFR0_EL1, id_aa64dfr0); 27647e07394SAndrew Turner _FETCH_KERN_REG(ID_AA64DFR1_EL1, id_aa64dfr1); 27747e07394SAndrew Turner _FETCH_KERN_REG(ID_AA64ISAR0_EL1, id_aa64isar0); 27847e07394SAndrew Turner _FETCH_KERN_REG(ID_AA64ISAR1_EL1, id_aa64isar1); 27947e07394SAndrew Turner _FETCH_KERN_REG(ID_AA64ISAR2_EL1, id_aa64isar2); 28047e07394SAndrew Turner _FETCH_KERN_REG(ID_AA64MMFR0_EL1, id_aa64mmfr0); 28147e07394SAndrew Turner _FETCH_KERN_REG(ID_AA64MMFR1_EL1, id_aa64mmfr1); 28247e07394SAndrew Turner _FETCH_KERN_REG(ID_AA64MMFR2_EL1, id_aa64mmfr2); 28347e07394SAndrew Turner _FETCH_KERN_REG(ID_AA64PFR0_EL1, id_aa64pfr0); 28447e07394SAndrew Turner _FETCH_KERN_REG(ID_AA64PFR1_EL1, id_aa64pfr1); 28547e07394SAndrew Turner #undef _FETCH_KERN_REG 28647e07394SAndrew Turner return (0); 28747e07394SAndrew Turner } 28847e07394SAndrew Turner 28947e07394SAndrew Turner static void 29047e07394SAndrew Turner vcpu_cleanup(struct vcpu *vcpu, bool destroy) 29147e07394SAndrew Turner { 29247e07394SAndrew Turner vmmops_vcpu_cleanup(vcpu->cookie); 29347e07394SAndrew Turner vcpu->cookie = NULL; 29447e07394SAndrew Turner if (destroy) { 29547e07394SAndrew Turner vmm_stat_free(vcpu->stats); 29647e07394SAndrew Turner fpu_save_area_free(vcpu->guestfpu); 29747e07394SAndrew Turner vcpu_lock_destroy(vcpu); 29847e07394SAndrew Turner } 29947e07394SAndrew Turner } 30047e07394SAndrew Turner 30147e07394SAndrew Turner static struct vcpu * 30247e07394SAndrew Turner vcpu_alloc(struct vm *vm, int vcpu_id) 30347e07394SAndrew Turner { 30447e07394SAndrew Turner struct vcpu *vcpu; 30547e07394SAndrew Turner 30647e07394SAndrew Turner KASSERT(vcpu_id >= 0 && vcpu_id < vm->maxcpus, 30747e07394SAndrew Turner ("vcpu_alloc: invalid vcpu %d", vcpu_id)); 30847e07394SAndrew Turner 30947e07394SAndrew Turner vcpu = malloc(sizeof(*vcpu), M_VMM, M_WAITOK | M_ZERO); 31047e07394SAndrew Turner vcpu_lock_init(vcpu); 31147e07394SAndrew Turner vcpu->state = VCPU_IDLE; 31247e07394SAndrew Turner vcpu->hostcpu = NOCPU; 31347e07394SAndrew Turner vcpu->vcpuid = vcpu_id; 31447e07394SAndrew Turner vcpu->vm = vm; 31547e07394SAndrew Turner vcpu->guestfpu = fpu_save_area_alloc(); 31647e07394SAndrew Turner vcpu->stats = vmm_stat_alloc(); 31747e07394SAndrew Turner return (vcpu); 31847e07394SAndrew Turner } 31947e07394SAndrew Turner 32047e07394SAndrew Turner static void 32147e07394SAndrew Turner vcpu_init(struct vcpu *vcpu) 32247e07394SAndrew Turner { 32347e07394SAndrew Turner vcpu->cookie = vmmops_vcpu_init(vcpu->vm->cookie, vcpu, vcpu->vcpuid); 32447e07394SAndrew Turner MPASS(vcpu->cookie != NULL); 32547e07394SAndrew Turner fpu_save_area_reset(vcpu->guestfpu); 32647e07394SAndrew Turner vmm_stat_init(vcpu->stats); 32747e07394SAndrew Turner } 32847e07394SAndrew Turner 32947e07394SAndrew Turner struct vm_exit * 33047e07394SAndrew Turner vm_exitinfo(struct vcpu *vcpu) 33147e07394SAndrew Turner { 33247e07394SAndrew Turner return (&vcpu->exitinfo); 33347e07394SAndrew Turner } 33447e07394SAndrew Turner 33547e07394SAndrew Turner static int 33647e07394SAndrew Turner vmm_init(void) 33747e07394SAndrew Turner { 33847e07394SAndrew Turner int error; 33947e07394SAndrew Turner 34047e07394SAndrew Turner vm_maxcpu = mp_ncpus; 34147e07394SAndrew Turner TUNABLE_INT_FETCH("hw.vmm.maxcpu", &vm_maxcpu); 34247e07394SAndrew Turner 34347e07394SAndrew Turner if (vm_maxcpu > VM_MAXCPU) { 34447e07394SAndrew Turner printf("vmm: vm_maxcpu clamped to %u\n", VM_MAXCPU); 34547e07394SAndrew Turner vm_maxcpu = VM_MAXCPU; 34647e07394SAndrew Turner } 34747e07394SAndrew Turner if (vm_maxcpu == 0) 34847e07394SAndrew Turner vm_maxcpu = 1; 34947e07394SAndrew Turner 35047e07394SAndrew Turner error = vmm_regs_init(&vmm_arch_regs, &vmm_arch_regs_masks); 35147e07394SAndrew Turner if (error != 0) 35247e07394SAndrew Turner return (error); 35347e07394SAndrew Turner 35447e07394SAndrew Turner return (vmmops_modinit(0)); 35547e07394SAndrew Turner } 35647e07394SAndrew Turner 35747e07394SAndrew Turner static int 35847e07394SAndrew Turner vmm_handler(module_t mod, int what, void *arg) 35947e07394SAndrew Turner { 36047e07394SAndrew Turner int error; 36147e07394SAndrew Turner 36247e07394SAndrew Turner switch (what) { 36347e07394SAndrew Turner case MOD_LOAD: 36447e07394SAndrew Turner /* TODO: if (vmm_is_hw_supported()) { */ 365*a97f683fSMark Johnston error = vmmdev_init(); 366*a97f683fSMark Johnston if (error != 0) 367*a97f683fSMark Johnston break; 36847e07394SAndrew Turner error = vmm_init(); 36947e07394SAndrew Turner if (error == 0) 37047e07394SAndrew Turner vmm_initialized = true; 37147e07394SAndrew Turner break; 37247e07394SAndrew Turner case MOD_UNLOAD: 37347e07394SAndrew Turner /* TODO: if (vmm_is_hw_supported()) { */ 37447e07394SAndrew Turner error = vmmdev_cleanup(); 37547e07394SAndrew Turner if (error == 0 && vmm_initialized) { 37647e07394SAndrew Turner error = vmmops_modcleanup(); 37747e07394SAndrew Turner if (error) 37847e07394SAndrew Turner vmm_initialized = false; 37947e07394SAndrew Turner } 38047e07394SAndrew Turner break; 38147e07394SAndrew Turner default: 38247e07394SAndrew Turner error = 0; 38347e07394SAndrew Turner break; 38447e07394SAndrew Turner } 38547e07394SAndrew Turner return (error); 38647e07394SAndrew Turner } 38747e07394SAndrew Turner 38847e07394SAndrew Turner static moduledata_t vmm_kmod = { 38947e07394SAndrew Turner "vmm", 39047e07394SAndrew Turner vmm_handler, 39147e07394SAndrew Turner NULL 39247e07394SAndrew Turner }; 39347e07394SAndrew Turner 39447e07394SAndrew Turner /* 39547e07394SAndrew Turner * vmm initialization has the following dependencies: 39647e07394SAndrew Turner * 39747e07394SAndrew Turner * - HYP initialization requires smp_rendezvous() and therefore must happen 39847e07394SAndrew Turner * after SMP is fully functional (after SI_SUB_SMP). 39947e07394SAndrew Turner */ 40047e07394SAndrew Turner DECLARE_MODULE(vmm, vmm_kmod, SI_SUB_SMP + 1, SI_ORDER_ANY); 40147e07394SAndrew Turner MODULE_VERSION(vmm, 1); 40247e07394SAndrew Turner 40347e07394SAndrew Turner static void 40447e07394SAndrew Turner vm_init(struct vm *vm, bool create) 40547e07394SAndrew Turner { 40647e07394SAndrew Turner int i; 40747e07394SAndrew Turner 40847e07394SAndrew Turner vm->cookie = vmmops_init(vm, vmspace_pmap(vm->vmspace)); 40947e07394SAndrew Turner MPASS(vm->cookie != NULL); 41047e07394SAndrew Turner 41147e07394SAndrew Turner CPU_ZERO(&vm->active_cpus); 41247e07394SAndrew Turner CPU_ZERO(&vm->debug_cpus); 41347e07394SAndrew Turner 41447e07394SAndrew Turner vm->suspend = 0; 41547e07394SAndrew Turner CPU_ZERO(&vm->suspended_cpus); 41647e07394SAndrew Turner 41747e07394SAndrew Turner memset(vm->mmio_region, 0, sizeof(vm->mmio_region)); 41847e07394SAndrew Turner memset(vm->special_reg, 0, sizeof(vm->special_reg)); 41947e07394SAndrew Turner 42047e07394SAndrew Turner if (!create) { 42147e07394SAndrew Turner for (i = 0; i < vm->maxcpus; i++) { 42247e07394SAndrew Turner if (vm->vcpu[i] != NULL) 42347e07394SAndrew Turner vcpu_init(vm->vcpu[i]); 42447e07394SAndrew Turner } 42547e07394SAndrew Turner } 42647e07394SAndrew Turner } 42747e07394SAndrew Turner 428a03354b0SMark Johnston void 429a03354b0SMark Johnston vm_disable_vcpu_creation(struct vm *vm) 430a03354b0SMark Johnston { 431a03354b0SMark Johnston sx_xlock(&vm->vcpus_init_lock); 432a03354b0SMark Johnston vm->dying = true; 433a03354b0SMark Johnston sx_xunlock(&vm->vcpus_init_lock); 434a03354b0SMark Johnston } 435a03354b0SMark Johnston 43647e07394SAndrew Turner struct vcpu * 43747e07394SAndrew Turner vm_alloc_vcpu(struct vm *vm, int vcpuid) 43847e07394SAndrew Turner { 43947e07394SAndrew Turner struct vcpu *vcpu; 44047e07394SAndrew Turner 44147e07394SAndrew Turner if (vcpuid < 0 || vcpuid >= vm_get_maxcpus(vm)) 44247e07394SAndrew Turner return (NULL); 44347e07394SAndrew Turner 44447e07394SAndrew Turner /* Some interrupt controllers may have a CPU limit */ 44547e07394SAndrew Turner if (vcpuid >= vgic_max_cpu_count(vm->cookie)) 44647e07394SAndrew Turner return (NULL); 44747e07394SAndrew Turner 44872ae04c7SRuslan Bukin vcpu = (struct vcpu *) 44972ae04c7SRuslan Bukin atomic_load_acq_ptr((uintptr_t *)&vm->vcpu[vcpuid]); 45047e07394SAndrew Turner if (__predict_true(vcpu != NULL)) 45147e07394SAndrew Turner return (vcpu); 45247e07394SAndrew Turner 45347e07394SAndrew Turner sx_xlock(&vm->vcpus_init_lock); 45447e07394SAndrew Turner vcpu = vm->vcpu[vcpuid]; 455a03354b0SMark Johnston if (vcpu == NULL && !vm->dying) { 45647e07394SAndrew Turner vcpu = vcpu_alloc(vm, vcpuid); 45747e07394SAndrew Turner vcpu_init(vcpu); 45847e07394SAndrew Turner 45947e07394SAndrew Turner /* 46047e07394SAndrew Turner * Ensure vCPU is fully created before updating pointer 46147e07394SAndrew Turner * to permit unlocked reads above. 46247e07394SAndrew Turner */ 46347e07394SAndrew Turner atomic_store_rel_ptr((uintptr_t *)&vm->vcpu[vcpuid], 46447e07394SAndrew Turner (uintptr_t)vcpu); 46547e07394SAndrew Turner } 46647e07394SAndrew Turner sx_xunlock(&vm->vcpus_init_lock); 46747e07394SAndrew Turner return (vcpu); 46847e07394SAndrew Turner } 46947e07394SAndrew Turner 47047e07394SAndrew Turner void 47147e07394SAndrew Turner vm_slock_vcpus(struct vm *vm) 47247e07394SAndrew Turner { 47347e07394SAndrew Turner sx_slock(&vm->vcpus_init_lock); 47447e07394SAndrew Turner } 47547e07394SAndrew Turner 47647e07394SAndrew Turner void 47747e07394SAndrew Turner vm_unlock_vcpus(struct vm *vm) 47847e07394SAndrew Turner { 47947e07394SAndrew Turner sx_unlock(&vm->vcpus_init_lock); 48047e07394SAndrew Turner } 48147e07394SAndrew Turner 48247e07394SAndrew Turner int 48347e07394SAndrew Turner vm_create(const char *name, struct vm **retvm) 48447e07394SAndrew Turner { 48547e07394SAndrew Turner struct vm *vm; 48647e07394SAndrew Turner struct vmspace *vmspace; 48747e07394SAndrew Turner 48847e07394SAndrew Turner /* 48947e07394SAndrew Turner * If vmm.ko could not be successfully initialized then don't attempt 49047e07394SAndrew Turner * to create the virtual machine. 49147e07394SAndrew Turner */ 49247e07394SAndrew Turner if (!vmm_initialized) 49347e07394SAndrew Turner return (ENXIO); 49447e07394SAndrew Turner 49547e07394SAndrew Turner if (name == NULL || strlen(name) >= VM_MAX_NAMELEN) 49647e07394SAndrew Turner return (EINVAL); 49747e07394SAndrew Turner 49847e07394SAndrew Turner vmspace = vmmops_vmspace_alloc(0, 1ul << 39); 49947e07394SAndrew Turner if (vmspace == NULL) 50047e07394SAndrew Turner return (ENOMEM); 50147e07394SAndrew Turner 50247e07394SAndrew Turner vm = malloc(sizeof(struct vm), M_VMM, M_WAITOK | M_ZERO); 50347e07394SAndrew Turner strcpy(vm->name, name); 50447e07394SAndrew Turner vm->vmspace = vmspace; 50547e07394SAndrew Turner sx_init(&vm->mem_segs_lock, "vm mem_segs"); 50647e07394SAndrew Turner sx_init(&vm->vcpus_init_lock, "vm vcpus"); 50747e07394SAndrew Turner 50847e07394SAndrew Turner vm->sockets = 1; 50947e07394SAndrew Turner vm->cores = 1; /* XXX backwards compatibility */ 51047e07394SAndrew Turner vm->threads = 1; /* XXX backwards compatibility */ 51147e07394SAndrew Turner vm->maxcpus = vm_maxcpu; 51247e07394SAndrew Turner 51347e07394SAndrew Turner vm->vcpu = malloc(sizeof(*vm->vcpu) * vm->maxcpus, M_VMM, 51447e07394SAndrew Turner M_WAITOK | M_ZERO); 51547e07394SAndrew Turner 51647e07394SAndrew Turner vm_init(vm, true); 51747e07394SAndrew Turner 51847e07394SAndrew Turner *retvm = vm; 51947e07394SAndrew Turner return (0); 52047e07394SAndrew Turner } 52147e07394SAndrew Turner 52247e07394SAndrew Turner void 52347e07394SAndrew Turner vm_get_topology(struct vm *vm, uint16_t *sockets, uint16_t *cores, 52447e07394SAndrew Turner uint16_t *threads, uint16_t *maxcpus) 52547e07394SAndrew Turner { 52647e07394SAndrew Turner *sockets = vm->sockets; 52747e07394SAndrew Turner *cores = vm->cores; 52847e07394SAndrew Turner *threads = vm->threads; 52947e07394SAndrew Turner *maxcpus = vm->maxcpus; 53047e07394SAndrew Turner } 53147e07394SAndrew Turner 53247e07394SAndrew Turner uint16_t 53347e07394SAndrew Turner vm_get_maxcpus(struct vm *vm) 53447e07394SAndrew Turner { 53547e07394SAndrew Turner return (vm->maxcpus); 53647e07394SAndrew Turner } 53747e07394SAndrew Turner 53847e07394SAndrew Turner int 53947e07394SAndrew Turner vm_set_topology(struct vm *vm, uint16_t sockets, uint16_t cores, 54047e07394SAndrew Turner uint16_t threads, uint16_t maxcpus) 54147e07394SAndrew Turner { 54247e07394SAndrew Turner /* Ignore maxcpus. */ 54347e07394SAndrew Turner if ((sockets * cores * threads) > vm->maxcpus) 54447e07394SAndrew Turner return (EINVAL); 54547e07394SAndrew Turner vm->sockets = sockets; 54647e07394SAndrew Turner vm->cores = cores; 54747e07394SAndrew Turner vm->threads = threads; 54847e07394SAndrew Turner return(0); 54947e07394SAndrew Turner } 55047e07394SAndrew Turner 55147e07394SAndrew Turner static void 55247e07394SAndrew Turner vm_cleanup(struct vm *vm, bool destroy) 55347e07394SAndrew Turner { 55447e07394SAndrew Turner struct mem_map *mm; 55547e07394SAndrew Turner pmap_t pmap __diagused; 55647e07394SAndrew Turner int i; 55747e07394SAndrew Turner 55847e07394SAndrew Turner if (destroy) { 55947e07394SAndrew Turner pmap = vmspace_pmap(vm->vmspace); 56047e07394SAndrew Turner sched_pin(); 56147e07394SAndrew Turner PCPU_SET(curvmpmap, NULL); 56247e07394SAndrew Turner sched_unpin(); 56347e07394SAndrew Turner CPU_FOREACH(i) { 56447e07394SAndrew Turner MPASS(cpuid_to_pcpu[i]->pc_curvmpmap != pmap); 56547e07394SAndrew Turner } 56647e07394SAndrew Turner } 56747e07394SAndrew Turner 56847e07394SAndrew Turner vgic_detach_from_vm(vm->cookie); 56947e07394SAndrew Turner 57047e07394SAndrew Turner for (i = 0; i < vm->maxcpus; i++) { 57147e07394SAndrew Turner if (vm->vcpu[i] != NULL) 57247e07394SAndrew Turner vcpu_cleanup(vm->vcpu[i], destroy); 57347e07394SAndrew Turner } 57447e07394SAndrew Turner 57547e07394SAndrew Turner vmmops_cleanup(vm->cookie); 57647e07394SAndrew Turner 57747e07394SAndrew Turner /* 57847e07394SAndrew Turner * System memory is removed from the guest address space only when 57947e07394SAndrew Turner * the VM is destroyed. This is because the mapping remains the same 58047e07394SAndrew Turner * across VM reset. 58147e07394SAndrew Turner * 58247e07394SAndrew Turner * Device memory can be relocated by the guest (e.g. using PCI BARs) 58347e07394SAndrew Turner * so those mappings are removed on a VM reset. 58447e07394SAndrew Turner */ 58547e07394SAndrew Turner if (!destroy) { 58647e07394SAndrew Turner for (i = 0; i < VM_MAX_MEMMAPS; i++) { 58747e07394SAndrew Turner mm = &vm->mem_maps[i]; 58847e07394SAndrew Turner if (destroy || !sysmem_mapping(vm, mm)) 58947e07394SAndrew Turner vm_free_memmap(vm, i); 59047e07394SAndrew Turner } 59147e07394SAndrew Turner } 59247e07394SAndrew Turner 59347e07394SAndrew Turner if (destroy) { 59447e07394SAndrew Turner for (i = 0; i < VM_MAX_MEMSEGS; i++) 59547e07394SAndrew Turner vm_free_memseg(vm, i); 59647e07394SAndrew Turner 59747e07394SAndrew Turner vmmops_vmspace_free(vm->vmspace); 59847e07394SAndrew Turner vm->vmspace = NULL; 59947e07394SAndrew Turner 60047e07394SAndrew Turner for (i = 0; i < vm->maxcpus; i++) 60147e07394SAndrew Turner free(vm->vcpu[i], M_VMM); 60247e07394SAndrew Turner free(vm->vcpu, M_VMM); 60347e07394SAndrew Turner sx_destroy(&vm->vcpus_init_lock); 60447e07394SAndrew Turner sx_destroy(&vm->mem_segs_lock); 60547e07394SAndrew Turner } 60647e07394SAndrew Turner } 60747e07394SAndrew Turner 60847e07394SAndrew Turner void 60947e07394SAndrew Turner vm_destroy(struct vm *vm) 61047e07394SAndrew Turner { 61147e07394SAndrew Turner vm_cleanup(vm, true); 61247e07394SAndrew Turner free(vm, M_VMM); 61347e07394SAndrew Turner } 61447e07394SAndrew Turner 61547e07394SAndrew Turner int 61647e07394SAndrew Turner vm_reinit(struct vm *vm) 61747e07394SAndrew Turner { 61847e07394SAndrew Turner int error; 61947e07394SAndrew Turner 62047e07394SAndrew Turner /* 62147e07394SAndrew Turner * A virtual machine can be reset only if all vcpus are suspended. 62247e07394SAndrew Turner */ 62347e07394SAndrew Turner if (CPU_CMP(&vm->suspended_cpus, &vm->active_cpus) == 0) { 62447e07394SAndrew Turner vm_cleanup(vm, false); 62547e07394SAndrew Turner vm_init(vm, false); 62647e07394SAndrew Turner error = 0; 62747e07394SAndrew Turner } else { 62847e07394SAndrew Turner error = EBUSY; 62947e07394SAndrew Turner } 63047e07394SAndrew Turner 63147e07394SAndrew Turner return (error); 63247e07394SAndrew Turner } 63347e07394SAndrew Turner 63447e07394SAndrew Turner const char * 63547e07394SAndrew Turner vm_name(struct vm *vm) 63647e07394SAndrew Turner { 63747e07394SAndrew Turner return (vm->name); 63847e07394SAndrew Turner } 63947e07394SAndrew Turner 64047e07394SAndrew Turner void 64147e07394SAndrew Turner vm_slock_memsegs(struct vm *vm) 64247e07394SAndrew Turner { 64347e07394SAndrew Turner sx_slock(&vm->mem_segs_lock); 64447e07394SAndrew Turner } 64547e07394SAndrew Turner 64647e07394SAndrew Turner void 64747e07394SAndrew Turner vm_xlock_memsegs(struct vm *vm) 64847e07394SAndrew Turner { 64947e07394SAndrew Turner sx_xlock(&vm->mem_segs_lock); 65047e07394SAndrew Turner } 65147e07394SAndrew Turner 65247e07394SAndrew Turner void 65347e07394SAndrew Turner vm_unlock_memsegs(struct vm *vm) 65447e07394SAndrew Turner { 65547e07394SAndrew Turner sx_unlock(&vm->mem_segs_lock); 65647e07394SAndrew Turner } 65747e07394SAndrew Turner 65847e07394SAndrew Turner /* 65947e07394SAndrew Turner * Return 'true' if 'gpa' is allocated in the guest address space. 66047e07394SAndrew Turner * 66147e07394SAndrew Turner * This function is called in the context of a running vcpu which acts as 66247e07394SAndrew Turner * an implicit lock on 'vm->mem_maps[]'. 66347e07394SAndrew Turner */ 66447e07394SAndrew Turner bool 66547e07394SAndrew Turner vm_mem_allocated(struct vcpu *vcpu, vm_paddr_t gpa) 66647e07394SAndrew Turner { 66747e07394SAndrew Turner struct vm *vm = vcpu->vm; 66847e07394SAndrew Turner struct mem_map *mm; 66947e07394SAndrew Turner int i; 67047e07394SAndrew Turner 67147e07394SAndrew Turner #ifdef INVARIANTS 67247e07394SAndrew Turner int hostcpu, state; 67347e07394SAndrew Turner state = vcpu_get_state(vcpu, &hostcpu); 67447e07394SAndrew Turner KASSERT(state == VCPU_RUNNING && hostcpu == curcpu, 67547e07394SAndrew Turner ("%s: invalid vcpu state %d/%d", __func__, state, hostcpu)); 67647e07394SAndrew Turner #endif 67747e07394SAndrew Turner 67847e07394SAndrew Turner for (i = 0; i < VM_MAX_MEMMAPS; i++) { 67947e07394SAndrew Turner mm = &vm->mem_maps[i]; 68047e07394SAndrew Turner if (mm->len != 0 && gpa >= mm->gpa && gpa < mm->gpa + mm->len) 68147e07394SAndrew Turner return (true); /* 'gpa' is sysmem or devmem */ 68247e07394SAndrew Turner } 68347e07394SAndrew Turner 68447e07394SAndrew Turner return (false); 68547e07394SAndrew Turner } 68647e07394SAndrew Turner 68747e07394SAndrew Turner int 68847e07394SAndrew Turner vm_alloc_memseg(struct vm *vm, int ident, size_t len, bool sysmem) 68947e07394SAndrew Turner { 69047e07394SAndrew Turner struct mem_seg *seg; 69147e07394SAndrew Turner vm_object_t obj; 69247e07394SAndrew Turner 69347e07394SAndrew Turner sx_assert(&vm->mem_segs_lock, SX_XLOCKED); 69447e07394SAndrew Turner 69547e07394SAndrew Turner if (ident < 0 || ident >= VM_MAX_MEMSEGS) 69647e07394SAndrew Turner return (EINVAL); 69747e07394SAndrew Turner 69847e07394SAndrew Turner if (len == 0 || (len & PAGE_MASK)) 69947e07394SAndrew Turner return (EINVAL); 70047e07394SAndrew Turner 70147e07394SAndrew Turner seg = &vm->mem_segs[ident]; 70247e07394SAndrew Turner if (seg->object != NULL) { 70347e07394SAndrew Turner if (seg->len == len && seg->sysmem == sysmem) 70447e07394SAndrew Turner return (EEXIST); 70547e07394SAndrew Turner else 70647e07394SAndrew Turner return (EINVAL); 70747e07394SAndrew Turner } 70847e07394SAndrew Turner 70947e07394SAndrew Turner obj = vm_object_allocate(OBJT_DEFAULT, len >> PAGE_SHIFT); 71047e07394SAndrew Turner if (obj == NULL) 71147e07394SAndrew Turner return (ENOMEM); 71247e07394SAndrew Turner 71347e07394SAndrew Turner seg->len = len; 71447e07394SAndrew Turner seg->object = obj; 71547e07394SAndrew Turner seg->sysmem = sysmem; 71647e07394SAndrew Turner return (0); 71747e07394SAndrew Turner } 71847e07394SAndrew Turner 71947e07394SAndrew Turner int 72047e07394SAndrew Turner vm_get_memseg(struct vm *vm, int ident, size_t *len, bool *sysmem, 72147e07394SAndrew Turner vm_object_t *objptr) 72247e07394SAndrew Turner { 72347e07394SAndrew Turner struct mem_seg *seg; 72447e07394SAndrew Turner 72547e07394SAndrew Turner sx_assert(&vm->mem_segs_lock, SX_LOCKED); 72647e07394SAndrew Turner 72747e07394SAndrew Turner if (ident < 0 || ident >= VM_MAX_MEMSEGS) 72847e07394SAndrew Turner return (EINVAL); 72947e07394SAndrew Turner 73047e07394SAndrew Turner seg = &vm->mem_segs[ident]; 73147e07394SAndrew Turner if (len) 73247e07394SAndrew Turner *len = seg->len; 73347e07394SAndrew Turner if (sysmem) 73447e07394SAndrew Turner *sysmem = seg->sysmem; 73547e07394SAndrew Turner if (objptr) 73647e07394SAndrew Turner *objptr = seg->object; 73747e07394SAndrew Turner return (0); 73847e07394SAndrew Turner } 73947e07394SAndrew Turner 74047e07394SAndrew Turner void 74147e07394SAndrew Turner vm_free_memseg(struct vm *vm, int ident) 74247e07394SAndrew Turner { 74347e07394SAndrew Turner struct mem_seg *seg; 74447e07394SAndrew Turner 74547e07394SAndrew Turner KASSERT(ident >= 0 && ident < VM_MAX_MEMSEGS, 74647e07394SAndrew Turner ("%s: invalid memseg ident %d", __func__, ident)); 74747e07394SAndrew Turner 74847e07394SAndrew Turner seg = &vm->mem_segs[ident]; 74947e07394SAndrew Turner if (seg->object != NULL) { 75047e07394SAndrew Turner vm_object_deallocate(seg->object); 75147e07394SAndrew Turner bzero(seg, sizeof(struct mem_seg)); 75247e07394SAndrew Turner } 75347e07394SAndrew Turner } 75447e07394SAndrew Turner 75547e07394SAndrew Turner int 75647e07394SAndrew Turner vm_mmap_memseg(struct vm *vm, vm_paddr_t gpa, int segid, vm_ooffset_t first, 75747e07394SAndrew Turner size_t len, int prot, int flags) 75847e07394SAndrew Turner { 75947e07394SAndrew Turner struct mem_seg *seg; 76047e07394SAndrew Turner struct mem_map *m, *map; 76147e07394SAndrew Turner vm_ooffset_t last; 76247e07394SAndrew Turner int i, error; 76347e07394SAndrew Turner 76447e07394SAndrew Turner if (prot == 0 || (prot & ~(VM_PROT_ALL)) != 0) 76547e07394SAndrew Turner return (EINVAL); 76647e07394SAndrew Turner 76747e07394SAndrew Turner if (flags & ~VM_MEMMAP_F_WIRED) 76847e07394SAndrew Turner return (EINVAL); 76947e07394SAndrew Turner 77047e07394SAndrew Turner if (segid < 0 || segid >= VM_MAX_MEMSEGS) 77147e07394SAndrew Turner return (EINVAL); 77247e07394SAndrew Turner 77347e07394SAndrew Turner seg = &vm->mem_segs[segid]; 77447e07394SAndrew Turner if (seg->object == NULL) 77547e07394SAndrew Turner return (EINVAL); 77647e07394SAndrew Turner 77747e07394SAndrew Turner last = first + len; 77847e07394SAndrew Turner if (first < 0 || first >= last || last > seg->len) 77947e07394SAndrew Turner return (EINVAL); 78047e07394SAndrew Turner 78147e07394SAndrew Turner if ((gpa | first | last) & PAGE_MASK) 78247e07394SAndrew Turner return (EINVAL); 78347e07394SAndrew Turner 78447e07394SAndrew Turner map = NULL; 78547e07394SAndrew Turner for (i = 0; i < VM_MAX_MEMMAPS; i++) { 78647e07394SAndrew Turner m = &vm->mem_maps[i]; 78747e07394SAndrew Turner if (m->len == 0) { 78847e07394SAndrew Turner map = m; 78947e07394SAndrew Turner break; 79047e07394SAndrew Turner } 79147e07394SAndrew Turner } 79247e07394SAndrew Turner 79347e07394SAndrew Turner if (map == NULL) 79447e07394SAndrew Turner return (ENOSPC); 79547e07394SAndrew Turner 79647e07394SAndrew Turner error = vm_map_find(&vm->vmspace->vm_map, seg->object, first, &gpa, 79747e07394SAndrew Turner len, 0, VMFS_NO_SPACE, prot, prot, 0); 79847e07394SAndrew Turner if (error != KERN_SUCCESS) 79947e07394SAndrew Turner return (EFAULT); 80047e07394SAndrew Turner 80147e07394SAndrew Turner vm_object_reference(seg->object); 80247e07394SAndrew Turner 80347e07394SAndrew Turner if (flags & VM_MEMMAP_F_WIRED) { 80447e07394SAndrew Turner error = vm_map_wire(&vm->vmspace->vm_map, gpa, gpa + len, 80547e07394SAndrew Turner VM_MAP_WIRE_USER | VM_MAP_WIRE_NOHOLES); 80647e07394SAndrew Turner if (error != KERN_SUCCESS) { 80747e07394SAndrew Turner vm_map_remove(&vm->vmspace->vm_map, gpa, gpa + len); 80847e07394SAndrew Turner return (error == KERN_RESOURCE_SHORTAGE ? ENOMEM : 80947e07394SAndrew Turner EFAULT); 81047e07394SAndrew Turner } 81147e07394SAndrew Turner } 81247e07394SAndrew Turner 81347e07394SAndrew Turner map->gpa = gpa; 81447e07394SAndrew Turner map->len = len; 81547e07394SAndrew Turner map->segoff = first; 81647e07394SAndrew Turner map->segid = segid; 81747e07394SAndrew Turner map->prot = prot; 81847e07394SAndrew Turner map->flags = flags; 81947e07394SAndrew Turner return (0); 82047e07394SAndrew Turner } 82147e07394SAndrew Turner 82247e07394SAndrew Turner int 82347e07394SAndrew Turner vm_munmap_memseg(struct vm *vm, vm_paddr_t gpa, size_t len) 82447e07394SAndrew Turner { 82547e07394SAndrew Turner struct mem_map *m; 82647e07394SAndrew Turner int i; 82747e07394SAndrew Turner 82847e07394SAndrew Turner for (i = 0; i < VM_MAX_MEMMAPS; i++) { 82947e07394SAndrew Turner m = &vm->mem_maps[i]; 83047e07394SAndrew Turner if (m->gpa == gpa && m->len == len) { 83147e07394SAndrew Turner vm_free_memmap(vm, i); 83247e07394SAndrew Turner return (0); 83347e07394SAndrew Turner } 83447e07394SAndrew Turner } 83547e07394SAndrew Turner 83647e07394SAndrew Turner return (EINVAL); 83747e07394SAndrew Turner } 83847e07394SAndrew Turner 83947e07394SAndrew Turner int 84047e07394SAndrew Turner vm_mmap_getnext(struct vm *vm, vm_paddr_t *gpa, int *segid, 84147e07394SAndrew Turner vm_ooffset_t *segoff, size_t *len, int *prot, int *flags) 84247e07394SAndrew Turner { 84347e07394SAndrew Turner struct mem_map *mm, *mmnext; 84447e07394SAndrew Turner int i; 84547e07394SAndrew Turner 84647e07394SAndrew Turner mmnext = NULL; 84747e07394SAndrew Turner for (i = 0; i < VM_MAX_MEMMAPS; i++) { 84847e07394SAndrew Turner mm = &vm->mem_maps[i]; 84947e07394SAndrew Turner if (mm->len == 0 || mm->gpa < *gpa) 85047e07394SAndrew Turner continue; 85147e07394SAndrew Turner if (mmnext == NULL || mm->gpa < mmnext->gpa) 85247e07394SAndrew Turner mmnext = mm; 85347e07394SAndrew Turner } 85447e07394SAndrew Turner 85547e07394SAndrew Turner if (mmnext != NULL) { 85647e07394SAndrew Turner *gpa = mmnext->gpa; 85747e07394SAndrew Turner if (segid) 85847e07394SAndrew Turner *segid = mmnext->segid; 85947e07394SAndrew Turner if (segoff) 86047e07394SAndrew Turner *segoff = mmnext->segoff; 86147e07394SAndrew Turner if (len) 86247e07394SAndrew Turner *len = mmnext->len; 86347e07394SAndrew Turner if (prot) 86447e07394SAndrew Turner *prot = mmnext->prot; 86547e07394SAndrew Turner if (flags) 86647e07394SAndrew Turner *flags = mmnext->flags; 86747e07394SAndrew Turner return (0); 86847e07394SAndrew Turner } else { 86947e07394SAndrew Turner return (ENOENT); 87047e07394SAndrew Turner } 87147e07394SAndrew Turner } 87247e07394SAndrew Turner 87347e07394SAndrew Turner static void 87447e07394SAndrew Turner vm_free_memmap(struct vm *vm, int ident) 87547e07394SAndrew Turner { 87647e07394SAndrew Turner struct mem_map *mm; 87747e07394SAndrew Turner int error __diagused; 87847e07394SAndrew Turner 87947e07394SAndrew Turner mm = &vm->mem_maps[ident]; 88047e07394SAndrew Turner if (mm->len) { 88147e07394SAndrew Turner error = vm_map_remove(&vm->vmspace->vm_map, mm->gpa, 88247e07394SAndrew Turner mm->gpa + mm->len); 88347e07394SAndrew Turner KASSERT(error == KERN_SUCCESS, ("%s: vm_map_remove error %d", 88447e07394SAndrew Turner __func__, error)); 88547e07394SAndrew Turner bzero(mm, sizeof(struct mem_map)); 88647e07394SAndrew Turner } 88747e07394SAndrew Turner } 88847e07394SAndrew Turner 88947e07394SAndrew Turner static __inline bool 89047e07394SAndrew Turner sysmem_mapping(struct vm *vm, struct mem_map *mm) 89147e07394SAndrew Turner { 89247e07394SAndrew Turner 89347e07394SAndrew Turner if (mm->len != 0 && vm->mem_segs[mm->segid].sysmem) 89447e07394SAndrew Turner return (true); 89547e07394SAndrew Turner else 89647e07394SAndrew Turner return (false); 89747e07394SAndrew Turner } 89847e07394SAndrew Turner 89947e07394SAndrew Turner vm_paddr_t 90047e07394SAndrew Turner vmm_sysmem_maxaddr(struct vm *vm) 90147e07394SAndrew Turner { 90247e07394SAndrew Turner struct mem_map *mm; 90347e07394SAndrew Turner vm_paddr_t maxaddr; 90447e07394SAndrew Turner int i; 90547e07394SAndrew Turner 90647e07394SAndrew Turner maxaddr = 0; 90747e07394SAndrew Turner for (i = 0; i < VM_MAX_MEMMAPS; i++) { 90847e07394SAndrew Turner mm = &vm->mem_maps[i]; 90947e07394SAndrew Turner if (sysmem_mapping(vm, mm)) { 91047e07394SAndrew Turner if (maxaddr < mm->gpa + mm->len) 91147e07394SAndrew Turner maxaddr = mm->gpa + mm->len; 91247e07394SAndrew Turner } 91347e07394SAndrew Turner } 91447e07394SAndrew Turner return (maxaddr); 91547e07394SAndrew Turner } 91647e07394SAndrew Turner 91747e07394SAndrew Turner int 91847e07394SAndrew Turner vm_gla2gpa_nofault(struct vcpu *vcpu, struct vm_guest_paging *paging, 91947e07394SAndrew Turner uint64_t gla, int prot, uint64_t *gpa, int *is_fault) 92047e07394SAndrew Turner { 92147e07394SAndrew Turner 92247e07394SAndrew Turner vmmops_gla2gpa(vcpu->cookie, paging, gla, prot, gpa, is_fault); 92347e07394SAndrew Turner return (0); 92447e07394SAndrew Turner } 92547e07394SAndrew Turner 92647e07394SAndrew Turner static int 92747e07394SAndrew Turner vmm_reg_raz(struct vcpu *vcpu, uint64_t *rval, void *arg) 92847e07394SAndrew Turner { 92947e07394SAndrew Turner *rval = 0; 93047e07394SAndrew Turner return (0); 93147e07394SAndrew Turner } 93247e07394SAndrew Turner 93347e07394SAndrew Turner static int 93447e07394SAndrew Turner vmm_reg_read_arg(struct vcpu *vcpu, uint64_t *rval, void *arg) 93547e07394SAndrew Turner { 93647e07394SAndrew Turner *rval = *(uint64_t *)arg; 93747e07394SAndrew Turner return (0); 93847e07394SAndrew Turner } 93947e07394SAndrew Turner 94047e07394SAndrew Turner static int 94147e07394SAndrew Turner vmm_reg_wi(struct vcpu *vcpu, uint64_t wval, void *arg) 94247e07394SAndrew Turner { 94347e07394SAndrew Turner return (0); 94447e07394SAndrew Turner } 94547e07394SAndrew Turner 94647e07394SAndrew Turner static const struct vmm_special_reg vmm_special_regs[] = { 94747e07394SAndrew Turner #define SPECIAL_REG(_reg, _read, _write) \ 94847e07394SAndrew Turner { \ 94947e07394SAndrew Turner .esr_iss = ((_reg ## _op0) << ISS_MSR_OP0_SHIFT) | \ 95047e07394SAndrew Turner ((_reg ## _op1) << ISS_MSR_OP1_SHIFT) | \ 95147e07394SAndrew Turner ((_reg ## _CRn) << ISS_MSR_CRn_SHIFT) | \ 95247e07394SAndrew Turner ((_reg ## _CRm) << ISS_MSR_CRm_SHIFT) | \ 95347e07394SAndrew Turner ((_reg ## _op2) << ISS_MSR_OP2_SHIFT), \ 95447e07394SAndrew Turner .esr_mask = ISS_MSR_REG_MASK, \ 95547e07394SAndrew Turner .reg_read = (_read), \ 95647e07394SAndrew Turner .reg_write = (_write), \ 95747e07394SAndrew Turner .arg = NULL, \ 95847e07394SAndrew Turner } 95947e07394SAndrew Turner #define ID_SPECIAL_REG(_reg, _name) \ 96047e07394SAndrew Turner { \ 96147e07394SAndrew Turner .esr_iss = ((_reg ## _op0) << ISS_MSR_OP0_SHIFT) | \ 96247e07394SAndrew Turner ((_reg ## _op1) << ISS_MSR_OP1_SHIFT) | \ 96347e07394SAndrew Turner ((_reg ## _CRn) << ISS_MSR_CRn_SHIFT) | \ 96447e07394SAndrew Turner ((_reg ## _CRm) << ISS_MSR_CRm_SHIFT) | \ 96547e07394SAndrew Turner ((_reg ## _op2) << ISS_MSR_OP2_SHIFT), \ 96647e07394SAndrew Turner .esr_mask = ISS_MSR_REG_MASK, \ 96747e07394SAndrew Turner .reg_read = vmm_reg_read_arg, \ 96847e07394SAndrew Turner .reg_write = vmm_reg_wi, \ 96947e07394SAndrew Turner .arg = &(vmm_arch_regs._name), \ 97047e07394SAndrew Turner } 97147e07394SAndrew Turner 97247e07394SAndrew Turner /* ID registers */ 97347e07394SAndrew Turner ID_SPECIAL_REG(ID_AA64PFR0_EL1, id_aa64pfr0), 97447e07394SAndrew Turner ID_SPECIAL_REG(ID_AA64DFR0_EL1, id_aa64dfr0), 97547e07394SAndrew Turner ID_SPECIAL_REG(ID_AA64ISAR0_EL1, id_aa64isar0), 97647e07394SAndrew Turner ID_SPECIAL_REG(ID_AA64MMFR0_EL1, id_aa64mmfr0), 97747e07394SAndrew Turner ID_SPECIAL_REG(ID_AA64MMFR1_EL1, id_aa64mmfr1), 97847e07394SAndrew Turner 97947e07394SAndrew Turner /* 98047e07394SAndrew Turner * All other ID registers are read as zero. 98147e07394SAndrew Turner * They are all in the op0=3, op1=0, CRn=0, CRm={0..7} space. 98247e07394SAndrew Turner */ 98347e07394SAndrew Turner { 98447e07394SAndrew Turner .esr_iss = (3 << ISS_MSR_OP0_SHIFT) | 98547e07394SAndrew Turner (0 << ISS_MSR_OP1_SHIFT) | 98647e07394SAndrew Turner (0 << ISS_MSR_CRn_SHIFT) | 98747e07394SAndrew Turner (0 << ISS_MSR_CRm_SHIFT), 98847e07394SAndrew Turner .esr_mask = ISS_MSR_OP0_MASK | ISS_MSR_OP1_MASK | 98947e07394SAndrew Turner ISS_MSR_CRn_MASK | (0x8 << ISS_MSR_CRm_SHIFT), 99047e07394SAndrew Turner .reg_read = vmm_reg_raz, 99147e07394SAndrew Turner .reg_write = vmm_reg_wi, 99247e07394SAndrew Turner .arg = NULL, 99347e07394SAndrew Turner }, 99447e07394SAndrew Turner 99547e07394SAndrew Turner /* Counter physical registers */ 99647e07394SAndrew Turner SPECIAL_REG(CNTP_CTL_EL0, vtimer_phys_ctl_read, vtimer_phys_ctl_write), 99747e07394SAndrew Turner SPECIAL_REG(CNTP_CVAL_EL0, vtimer_phys_cval_read, 99847e07394SAndrew Turner vtimer_phys_cval_write), 99947e07394SAndrew Turner SPECIAL_REG(CNTP_TVAL_EL0, vtimer_phys_tval_read, 100047e07394SAndrew Turner vtimer_phys_tval_write), 100147e07394SAndrew Turner SPECIAL_REG(CNTPCT_EL0, vtimer_phys_cnt_read, vtimer_phys_cnt_write), 100247e07394SAndrew Turner #undef SPECIAL_REG 100347e07394SAndrew Turner }; 100447e07394SAndrew Turner 100547e07394SAndrew Turner void 100647e07394SAndrew Turner vm_register_reg_handler(struct vm *vm, uint64_t iss, uint64_t mask, 100747e07394SAndrew Turner reg_read_t reg_read, reg_write_t reg_write, void *arg) 100847e07394SAndrew Turner { 100947e07394SAndrew Turner int i; 101047e07394SAndrew Turner 101147e07394SAndrew Turner for (i = 0; i < nitems(vm->special_reg); i++) { 101247e07394SAndrew Turner if (vm->special_reg[i].esr_iss == 0 && 101347e07394SAndrew Turner vm->special_reg[i].esr_mask == 0) { 101447e07394SAndrew Turner vm->special_reg[i].esr_iss = iss; 101547e07394SAndrew Turner vm->special_reg[i].esr_mask = mask; 101647e07394SAndrew Turner vm->special_reg[i].reg_read = reg_read; 101747e07394SAndrew Turner vm->special_reg[i].reg_write = reg_write; 101847e07394SAndrew Turner vm->special_reg[i].arg = arg; 101947e07394SAndrew Turner return; 102047e07394SAndrew Turner } 102147e07394SAndrew Turner } 102247e07394SAndrew Turner 102347e07394SAndrew Turner panic("%s: No free special register slot", __func__); 102447e07394SAndrew Turner } 102547e07394SAndrew Turner 102647e07394SAndrew Turner void 102747e07394SAndrew Turner vm_deregister_reg_handler(struct vm *vm, uint64_t iss, uint64_t mask) 102847e07394SAndrew Turner { 102947e07394SAndrew Turner int i; 103047e07394SAndrew Turner 103147e07394SAndrew Turner for (i = 0; i < nitems(vm->special_reg); i++) { 103247e07394SAndrew Turner if (vm->special_reg[i].esr_iss == iss && 103347e07394SAndrew Turner vm->special_reg[i].esr_mask == mask) { 103447e07394SAndrew Turner memset(&vm->special_reg[i], 0, 103547e07394SAndrew Turner sizeof(vm->special_reg[i])); 103647e07394SAndrew Turner return; 103747e07394SAndrew Turner } 103847e07394SAndrew Turner } 103947e07394SAndrew Turner 104047e07394SAndrew Turner panic("%s: Invalid special register: iss %lx mask %lx", __func__, iss, 104147e07394SAndrew Turner mask); 104247e07394SAndrew Turner } 104347e07394SAndrew Turner 104447e07394SAndrew Turner static int 104547e07394SAndrew Turner vm_handle_reg_emul(struct vcpu *vcpu, bool *retu) 104647e07394SAndrew Turner { 104747e07394SAndrew Turner struct vm *vm; 104847e07394SAndrew Turner struct vm_exit *vme; 104947e07394SAndrew Turner struct vre *vre; 105047e07394SAndrew Turner int i, rv; 105147e07394SAndrew Turner 105247e07394SAndrew Turner vm = vcpu->vm; 105347e07394SAndrew Turner vme = &vcpu->exitinfo; 105447e07394SAndrew Turner vre = &vme->u.reg_emul.vre; 105547e07394SAndrew Turner 105647e07394SAndrew Turner for (i = 0; i < nitems(vm->special_reg); i++) { 105747e07394SAndrew Turner if (vm->special_reg[i].esr_iss == 0 && 105847e07394SAndrew Turner vm->special_reg[i].esr_mask == 0) 105947e07394SAndrew Turner continue; 106047e07394SAndrew Turner 106147e07394SAndrew Turner if ((vre->inst_syndrome & vm->special_reg[i].esr_mask) == 106247e07394SAndrew Turner vm->special_reg[i].esr_iss) { 106347e07394SAndrew Turner rv = vmm_emulate_register(vcpu, vre, 106447e07394SAndrew Turner vm->special_reg[i].reg_read, 106547e07394SAndrew Turner vm->special_reg[i].reg_write, 106647e07394SAndrew Turner vm->special_reg[i].arg); 106747e07394SAndrew Turner if (rv == 0) { 106847e07394SAndrew Turner *retu = false; 106947e07394SAndrew Turner } 107047e07394SAndrew Turner return (rv); 107147e07394SAndrew Turner } 107247e07394SAndrew Turner } 107347e07394SAndrew Turner for (i = 0; i < nitems(vmm_special_regs); i++) { 107447e07394SAndrew Turner if ((vre->inst_syndrome & vmm_special_regs[i].esr_mask) == 107547e07394SAndrew Turner vmm_special_regs[i].esr_iss) { 107647e07394SAndrew Turner rv = vmm_emulate_register(vcpu, vre, 107747e07394SAndrew Turner vmm_special_regs[i].reg_read, 107847e07394SAndrew Turner vmm_special_regs[i].reg_write, 107947e07394SAndrew Turner vmm_special_regs[i].arg); 108047e07394SAndrew Turner if (rv == 0) { 108147e07394SAndrew Turner *retu = false; 108247e07394SAndrew Turner } 108347e07394SAndrew Turner return (rv); 108447e07394SAndrew Turner } 108547e07394SAndrew Turner } 108647e07394SAndrew Turner 108747e07394SAndrew Turner 108847e07394SAndrew Turner *retu = true; 108947e07394SAndrew Turner return (0); 109047e07394SAndrew Turner } 109147e07394SAndrew Turner 109247e07394SAndrew Turner void 109347e07394SAndrew Turner vm_register_inst_handler(struct vm *vm, uint64_t start, uint64_t size, 109447e07394SAndrew Turner mem_region_read_t mmio_read, mem_region_write_t mmio_write) 109547e07394SAndrew Turner { 109647e07394SAndrew Turner int i; 109747e07394SAndrew Turner 109847e07394SAndrew Turner for (i = 0; i < nitems(vm->mmio_region); i++) { 109947e07394SAndrew Turner if (vm->mmio_region[i].start == 0 && 110047e07394SAndrew Turner vm->mmio_region[i].end == 0) { 110147e07394SAndrew Turner vm->mmio_region[i].start = start; 110247e07394SAndrew Turner vm->mmio_region[i].end = start + size; 110347e07394SAndrew Turner vm->mmio_region[i].read = mmio_read; 110447e07394SAndrew Turner vm->mmio_region[i].write = mmio_write; 110547e07394SAndrew Turner return; 110647e07394SAndrew Turner } 110747e07394SAndrew Turner } 110847e07394SAndrew Turner 110947e07394SAndrew Turner panic("%s: No free MMIO region", __func__); 111047e07394SAndrew Turner } 111147e07394SAndrew Turner 111247e07394SAndrew Turner void 111347e07394SAndrew Turner vm_deregister_inst_handler(struct vm *vm, uint64_t start, uint64_t size) 111447e07394SAndrew Turner { 111547e07394SAndrew Turner int i; 111647e07394SAndrew Turner 111747e07394SAndrew Turner for (i = 0; i < nitems(vm->mmio_region); i++) { 111847e07394SAndrew Turner if (vm->mmio_region[i].start == start && 111947e07394SAndrew Turner vm->mmio_region[i].end == start + size) { 112047e07394SAndrew Turner memset(&vm->mmio_region[i], 0, 112147e07394SAndrew Turner sizeof(vm->mmio_region[i])); 112247e07394SAndrew Turner return; 112347e07394SAndrew Turner } 112447e07394SAndrew Turner } 112547e07394SAndrew Turner 112647e07394SAndrew Turner panic("%s: Invalid MMIO region: %lx - %lx", __func__, start, 112747e07394SAndrew Turner start + size); 112847e07394SAndrew Turner } 112947e07394SAndrew Turner 113047e07394SAndrew Turner static int 113147e07394SAndrew Turner vm_handle_inst_emul(struct vcpu *vcpu, bool *retu) 113247e07394SAndrew Turner { 113347e07394SAndrew Turner struct vm *vm; 113447e07394SAndrew Turner struct vm_exit *vme; 113547e07394SAndrew Turner struct vie *vie; 113647e07394SAndrew Turner struct hyp *hyp; 113747e07394SAndrew Turner uint64_t fault_ipa; 113847e07394SAndrew Turner struct vm_guest_paging *paging; 113947e07394SAndrew Turner struct vmm_mmio_region *vmr; 114047e07394SAndrew Turner int error, i; 114147e07394SAndrew Turner 114247e07394SAndrew Turner vm = vcpu->vm; 114347e07394SAndrew Turner hyp = vm->cookie; 114447e07394SAndrew Turner if (!hyp->vgic_attached) 114547e07394SAndrew Turner goto out_user; 114647e07394SAndrew Turner 114747e07394SAndrew Turner vme = &vcpu->exitinfo; 114847e07394SAndrew Turner vie = &vme->u.inst_emul.vie; 114947e07394SAndrew Turner paging = &vme->u.inst_emul.paging; 115047e07394SAndrew Turner 115147e07394SAndrew Turner fault_ipa = vme->u.inst_emul.gpa; 115247e07394SAndrew Turner 115347e07394SAndrew Turner vmr = NULL; 115447e07394SAndrew Turner for (i = 0; i < nitems(vm->mmio_region); i++) { 115547e07394SAndrew Turner if (vm->mmio_region[i].start <= fault_ipa && 115647e07394SAndrew Turner vm->mmio_region[i].end > fault_ipa) { 115747e07394SAndrew Turner vmr = &vm->mmio_region[i]; 115847e07394SAndrew Turner break; 115947e07394SAndrew Turner } 116047e07394SAndrew Turner } 116147e07394SAndrew Turner if (vmr == NULL) 116247e07394SAndrew Turner goto out_user; 116347e07394SAndrew Turner 116447e07394SAndrew Turner error = vmm_emulate_instruction(vcpu, fault_ipa, vie, paging, 116547e07394SAndrew Turner vmr->read, vmr->write, retu); 116647e07394SAndrew Turner return (error); 116747e07394SAndrew Turner 116847e07394SAndrew Turner out_user: 116947e07394SAndrew Turner *retu = true; 117047e07394SAndrew Turner return (0); 117147e07394SAndrew Turner } 117247e07394SAndrew Turner 117347e07394SAndrew Turner int 117447e07394SAndrew Turner vm_suspend(struct vm *vm, enum vm_suspend_how how) 117547e07394SAndrew Turner { 117647e07394SAndrew Turner int i; 117747e07394SAndrew Turner 117847e07394SAndrew Turner if (how <= VM_SUSPEND_NONE || how >= VM_SUSPEND_LAST) 117947e07394SAndrew Turner return (EINVAL); 118047e07394SAndrew Turner 118147e07394SAndrew Turner if (atomic_cmpset_int(&vm->suspend, 0, how) == 0) { 118247e07394SAndrew Turner VM_CTR2(vm, "virtual machine already suspended %d/%d", 118347e07394SAndrew Turner vm->suspend, how); 118447e07394SAndrew Turner return (EALREADY); 118547e07394SAndrew Turner } 118647e07394SAndrew Turner 118747e07394SAndrew Turner VM_CTR1(vm, "virtual machine successfully suspended %d", how); 118847e07394SAndrew Turner 118947e07394SAndrew Turner /* 119047e07394SAndrew Turner * Notify all active vcpus that they are now suspended. 119147e07394SAndrew Turner */ 119247e07394SAndrew Turner for (i = 0; i < vm->maxcpus; i++) { 119347e07394SAndrew Turner if (CPU_ISSET(i, &vm->active_cpus)) 119447e07394SAndrew Turner vcpu_notify_event(vm_vcpu(vm, i)); 119547e07394SAndrew Turner } 119647e07394SAndrew Turner 119747e07394SAndrew Turner return (0); 119847e07394SAndrew Turner } 119947e07394SAndrew Turner 120047e07394SAndrew Turner void 120147e07394SAndrew Turner vm_exit_suspended(struct vcpu *vcpu, uint64_t pc) 120247e07394SAndrew Turner { 120347e07394SAndrew Turner struct vm *vm = vcpu->vm; 120447e07394SAndrew Turner struct vm_exit *vmexit; 120547e07394SAndrew Turner 120647e07394SAndrew Turner KASSERT(vm->suspend > VM_SUSPEND_NONE && vm->suspend < VM_SUSPEND_LAST, 120747e07394SAndrew Turner ("vm_exit_suspended: invalid suspend type %d", vm->suspend)); 120847e07394SAndrew Turner 120947e07394SAndrew Turner vmexit = vm_exitinfo(vcpu); 121047e07394SAndrew Turner vmexit->pc = pc; 121147e07394SAndrew Turner vmexit->inst_length = 4; 121247e07394SAndrew Turner vmexit->exitcode = VM_EXITCODE_SUSPENDED; 121347e07394SAndrew Turner vmexit->u.suspended.how = vm->suspend; 121447e07394SAndrew Turner } 121547e07394SAndrew Turner 121647e07394SAndrew Turner void 121747e07394SAndrew Turner vm_exit_debug(struct vcpu *vcpu, uint64_t pc) 121847e07394SAndrew Turner { 121947e07394SAndrew Turner struct vm_exit *vmexit; 122047e07394SAndrew Turner 122147e07394SAndrew Turner vmexit = vm_exitinfo(vcpu); 122247e07394SAndrew Turner vmexit->pc = pc; 122347e07394SAndrew Turner vmexit->inst_length = 4; 122447e07394SAndrew Turner vmexit->exitcode = VM_EXITCODE_DEBUG; 122547e07394SAndrew Turner } 122647e07394SAndrew Turner 122747e07394SAndrew Turner int 122847e07394SAndrew Turner vm_activate_cpu(struct vcpu *vcpu) 122947e07394SAndrew Turner { 123047e07394SAndrew Turner struct vm *vm = vcpu->vm; 123147e07394SAndrew Turner 123247e07394SAndrew Turner if (CPU_ISSET(vcpu->vcpuid, &vm->active_cpus)) 123347e07394SAndrew Turner return (EBUSY); 123447e07394SAndrew Turner 123547e07394SAndrew Turner CPU_SET_ATOMIC(vcpu->vcpuid, &vm->active_cpus); 123647e07394SAndrew Turner return (0); 123747e07394SAndrew Turner 123847e07394SAndrew Turner } 123947e07394SAndrew Turner 124047e07394SAndrew Turner int 124147e07394SAndrew Turner vm_suspend_cpu(struct vm *vm, struct vcpu *vcpu) 124247e07394SAndrew Turner { 124347e07394SAndrew Turner if (vcpu == NULL) { 124447e07394SAndrew Turner vm->debug_cpus = vm->active_cpus; 124547e07394SAndrew Turner for (int i = 0; i < vm->maxcpus; i++) { 124647e07394SAndrew Turner if (CPU_ISSET(i, &vm->active_cpus)) 124747e07394SAndrew Turner vcpu_notify_event(vm_vcpu(vm, i)); 124847e07394SAndrew Turner } 124947e07394SAndrew Turner } else { 125047e07394SAndrew Turner if (!CPU_ISSET(vcpu->vcpuid, &vm->active_cpus)) 125147e07394SAndrew Turner return (EINVAL); 125247e07394SAndrew Turner 125347e07394SAndrew Turner CPU_SET_ATOMIC(vcpu->vcpuid, &vm->debug_cpus); 125447e07394SAndrew Turner vcpu_notify_event(vcpu); 125547e07394SAndrew Turner } 125647e07394SAndrew Turner return (0); 125747e07394SAndrew Turner } 125847e07394SAndrew Turner 125947e07394SAndrew Turner int 126047e07394SAndrew Turner vm_resume_cpu(struct vm *vm, struct vcpu *vcpu) 126147e07394SAndrew Turner { 126247e07394SAndrew Turner 126347e07394SAndrew Turner if (vcpu == NULL) { 126447e07394SAndrew Turner CPU_ZERO(&vm->debug_cpus); 126547e07394SAndrew Turner } else { 126647e07394SAndrew Turner if (!CPU_ISSET(vcpu->vcpuid, &vm->debug_cpus)) 126747e07394SAndrew Turner return (EINVAL); 126847e07394SAndrew Turner 126947e07394SAndrew Turner CPU_CLR_ATOMIC(vcpu->vcpuid, &vm->debug_cpus); 127047e07394SAndrew Turner } 127147e07394SAndrew Turner return (0); 127247e07394SAndrew Turner } 127347e07394SAndrew Turner 127447e07394SAndrew Turner int 127547e07394SAndrew Turner vcpu_debugged(struct vcpu *vcpu) 127647e07394SAndrew Turner { 127747e07394SAndrew Turner 127847e07394SAndrew Turner return (CPU_ISSET(vcpu->vcpuid, &vcpu->vm->debug_cpus)); 127947e07394SAndrew Turner } 128047e07394SAndrew Turner 128147e07394SAndrew Turner cpuset_t 128247e07394SAndrew Turner vm_active_cpus(struct vm *vm) 128347e07394SAndrew Turner { 128447e07394SAndrew Turner 128547e07394SAndrew Turner return (vm->active_cpus); 128647e07394SAndrew Turner } 128747e07394SAndrew Turner 128847e07394SAndrew Turner cpuset_t 128947e07394SAndrew Turner vm_debug_cpus(struct vm *vm) 129047e07394SAndrew Turner { 129147e07394SAndrew Turner 129247e07394SAndrew Turner return (vm->debug_cpus); 129347e07394SAndrew Turner } 129447e07394SAndrew Turner 129547e07394SAndrew Turner cpuset_t 129647e07394SAndrew Turner vm_suspended_cpus(struct vm *vm) 129747e07394SAndrew Turner { 129847e07394SAndrew Turner 129947e07394SAndrew Turner return (vm->suspended_cpus); 130047e07394SAndrew Turner } 130147e07394SAndrew Turner 130247e07394SAndrew Turner 130347e07394SAndrew Turner void * 130447e07394SAndrew Turner vcpu_stats(struct vcpu *vcpu) 130547e07394SAndrew Turner { 130647e07394SAndrew Turner 130747e07394SAndrew Turner return (vcpu->stats); 130847e07394SAndrew Turner } 130947e07394SAndrew Turner 131047e07394SAndrew Turner /* 131147e07394SAndrew Turner * This function is called to ensure that a vcpu "sees" a pending event 131247e07394SAndrew Turner * as soon as possible: 131347e07394SAndrew Turner * - If the vcpu thread is sleeping then it is woken up. 131447e07394SAndrew Turner * - If the vcpu is running on a different host_cpu then an IPI will be directed 131547e07394SAndrew Turner * to the host_cpu to cause the vcpu to trap into the hypervisor. 131647e07394SAndrew Turner */ 131747e07394SAndrew Turner static void 131847e07394SAndrew Turner vcpu_notify_event_locked(struct vcpu *vcpu) 131947e07394SAndrew Turner { 132047e07394SAndrew Turner int hostcpu; 132147e07394SAndrew Turner 132247e07394SAndrew Turner hostcpu = vcpu->hostcpu; 132347e07394SAndrew Turner if (vcpu->state == VCPU_RUNNING) { 132447e07394SAndrew Turner KASSERT(hostcpu != NOCPU, ("vcpu running on invalid hostcpu")); 132547e07394SAndrew Turner if (hostcpu != curcpu) { 132647e07394SAndrew Turner ipi_cpu(hostcpu, vmm_ipinum); 132747e07394SAndrew Turner } else { 132847e07394SAndrew Turner /* 132947e07394SAndrew Turner * If the 'vcpu' is running on 'curcpu' then it must 133047e07394SAndrew Turner * be sending a notification to itself (e.g. SELF_IPI). 133147e07394SAndrew Turner * The pending event will be picked up when the vcpu 133247e07394SAndrew Turner * transitions back to guest context. 133347e07394SAndrew Turner */ 133447e07394SAndrew Turner } 133547e07394SAndrew Turner } else { 133647e07394SAndrew Turner KASSERT(hostcpu == NOCPU, ("vcpu state %d not consistent " 133747e07394SAndrew Turner "with hostcpu %d", vcpu->state, hostcpu)); 133847e07394SAndrew Turner if (vcpu->state == VCPU_SLEEPING) 133947e07394SAndrew Turner wakeup_one(vcpu); 134047e07394SAndrew Turner } 134147e07394SAndrew Turner } 134247e07394SAndrew Turner 134347e07394SAndrew Turner void 134447e07394SAndrew Turner vcpu_notify_event(struct vcpu *vcpu) 134547e07394SAndrew Turner { 134647e07394SAndrew Turner vcpu_lock(vcpu); 134747e07394SAndrew Turner vcpu_notify_event_locked(vcpu); 134847e07394SAndrew Turner vcpu_unlock(vcpu); 134947e07394SAndrew Turner } 135047e07394SAndrew Turner 135147e07394SAndrew Turner static void 135247e07394SAndrew Turner restore_guest_fpustate(struct vcpu *vcpu) 135347e07394SAndrew Turner { 135447e07394SAndrew Turner 135547e07394SAndrew Turner /* flush host state to the pcb */ 135647e07394SAndrew Turner vfp_save_state(curthread, curthread->td_pcb); 135747e07394SAndrew Turner /* Ensure the VFP state will be re-loaded when exiting the guest */ 135847e07394SAndrew Turner PCPU_SET(fpcurthread, NULL); 135947e07394SAndrew Turner 136047e07394SAndrew Turner /* restore guest FPU state */ 136147e07394SAndrew Turner vfp_enable(); 136247e07394SAndrew Turner vfp_restore(vcpu->guestfpu); 136347e07394SAndrew Turner 136447e07394SAndrew Turner /* 136547e07394SAndrew Turner * The FPU is now "dirty" with the guest's state so turn on emulation 136647e07394SAndrew Turner * to trap any access to the FPU by the host. 136747e07394SAndrew Turner */ 136847e07394SAndrew Turner vfp_disable(); 136947e07394SAndrew Turner } 137047e07394SAndrew Turner 137147e07394SAndrew Turner static void 137247e07394SAndrew Turner save_guest_fpustate(struct vcpu *vcpu) 137347e07394SAndrew Turner { 137447e07394SAndrew Turner if ((READ_SPECIALREG(cpacr_el1) & CPACR_FPEN_MASK) != 137547e07394SAndrew Turner CPACR_FPEN_TRAP_ALL1) 137647e07394SAndrew Turner panic("VFP not enabled in host!"); 137747e07394SAndrew Turner 137847e07394SAndrew Turner /* save guest FPU state */ 137947e07394SAndrew Turner vfp_enable(); 138047e07394SAndrew Turner vfp_store(vcpu->guestfpu); 138147e07394SAndrew Turner vfp_disable(); 138247e07394SAndrew Turner 138347e07394SAndrew Turner KASSERT(PCPU_GET(fpcurthread) == NULL, 138447e07394SAndrew Turner ("%s: fpcurthread set with guest registers", __func__)); 138547e07394SAndrew Turner } 138647e07394SAndrew Turner static int 138747e07394SAndrew Turner vcpu_set_state_locked(struct vcpu *vcpu, enum vcpu_state newstate, 138847e07394SAndrew Turner bool from_idle) 138947e07394SAndrew Turner { 139047e07394SAndrew Turner int error; 139147e07394SAndrew Turner 139247e07394SAndrew Turner vcpu_assert_locked(vcpu); 139347e07394SAndrew Turner 139447e07394SAndrew Turner /* 139547e07394SAndrew Turner * State transitions from the vmmdev_ioctl() must always begin from 139647e07394SAndrew Turner * the VCPU_IDLE state. This guarantees that there is only a single 139747e07394SAndrew Turner * ioctl() operating on a vcpu at any point. 139847e07394SAndrew Turner */ 139947e07394SAndrew Turner if (from_idle) { 140047e07394SAndrew Turner while (vcpu->state != VCPU_IDLE) { 140147e07394SAndrew Turner vcpu_notify_event_locked(vcpu); 140247e07394SAndrew Turner msleep_spin(&vcpu->state, &vcpu->mtx, "vmstat", hz); 140347e07394SAndrew Turner } 140447e07394SAndrew Turner } else { 140547e07394SAndrew Turner KASSERT(vcpu->state != VCPU_IDLE, ("invalid transition from " 140647e07394SAndrew Turner "vcpu idle state")); 140747e07394SAndrew Turner } 140847e07394SAndrew Turner 140947e07394SAndrew Turner if (vcpu->state == VCPU_RUNNING) { 141047e07394SAndrew Turner KASSERT(vcpu->hostcpu == curcpu, ("curcpu %d and hostcpu %d " 141147e07394SAndrew Turner "mismatch for running vcpu", curcpu, vcpu->hostcpu)); 141247e07394SAndrew Turner } else { 141347e07394SAndrew Turner KASSERT(vcpu->hostcpu == NOCPU, ("Invalid hostcpu %d for a " 141447e07394SAndrew Turner "vcpu that is not running", vcpu->hostcpu)); 141547e07394SAndrew Turner } 141647e07394SAndrew Turner 141747e07394SAndrew Turner /* 141847e07394SAndrew Turner * The following state transitions are allowed: 141947e07394SAndrew Turner * IDLE -> FROZEN -> IDLE 142047e07394SAndrew Turner * FROZEN -> RUNNING -> FROZEN 142147e07394SAndrew Turner * FROZEN -> SLEEPING -> FROZEN 142247e07394SAndrew Turner */ 142347e07394SAndrew Turner switch (vcpu->state) { 142447e07394SAndrew Turner case VCPU_IDLE: 142547e07394SAndrew Turner case VCPU_RUNNING: 142647e07394SAndrew Turner case VCPU_SLEEPING: 142747e07394SAndrew Turner error = (newstate != VCPU_FROZEN); 142847e07394SAndrew Turner break; 142947e07394SAndrew Turner case VCPU_FROZEN: 143047e07394SAndrew Turner error = (newstate == VCPU_FROZEN); 143147e07394SAndrew Turner break; 143247e07394SAndrew Turner default: 143347e07394SAndrew Turner error = 1; 143447e07394SAndrew Turner break; 143547e07394SAndrew Turner } 143647e07394SAndrew Turner 143747e07394SAndrew Turner if (error) 143847e07394SAndrew Turner return (EBUSY); 143947e07394SAndrew Turner 144047e07394SAndrew Turner vcpu->state = newstate; 144147e07394SAndrew Turner if (newstate == VCPU_RUNNING) 144247e07394SAndrew Turner vcpu->hostcpu = curcpu; 144347e07394SAndrew Turner else 144447e07394SAndrew Turner vcpu->hostcpu = NOCPU; 144547e07394SAndrew Turner 144647e07394SAndrew Turner if (newstate == VCPU_IDLE) 144747e07394SAndrew Turner wakeup(&vcpu->state); 144847e07394SAndrew Turner 144947e07394SAndrew Turner return (0); 145047e07394SAndrew Turner } 145147e07394SAndrew Turner 145247e07394SAndrew Turner static void 145347e07394SAndrew Turner vcpu_require_state(struct vcpu *vcpu, enum vcpu_state newstate) 145447e07394SAndrew Turner { 145547e07394SAndrew Turner int error; 145647e07394SAndrew Turner 145747e07394SAndrew Turner if ((error = vcpu_set_state(vcpu, newstate, false)) != 0) 145847e07394SAndrew Turner panic("Error %d setting state to %d\n", error, newstate); 145947e07394SAndrew Turner } 146047e07394SAndrew Turner 146147e07394SAndrew Turner static void 146247e07394SAndrew Turner vcpu_require_state_locked(struct vcpu *vcpu, enum vcpu_state newstate) 146347e07394SAndrew Turner { 146447e07394SAndrew Turner int error; 146547e07394SAndrew Turner 146647e07394SAndrew Turner if ((error = vcpu_set_state_locked(vcpu, newstate, false)) != 0) 146747e07394SAndrew Turner panic("Error %d setting state to %d", error, newstate); 146847e07394SAndrew Turner } 146947e07394SAndrew Turner 147047e07394SAndrew Turner int 147147e07394SAndrew Turner vm_get_capability(struct vcpu *vcpu, int type, int *retval) 147247e07394SAndrew Turner { 147347e07394SAndrew Turner if (type < 0 || type >= VM_CAP_MAX) 147447e07394SAndrew Turner return (EINVAL); 147547e07394SAndrew Turner 147647e07394SAndrew Turner return (vmmops_getcap(vcpu->cookie, type, retval)); 147747e07394SAndrew Turner } 147847e07394SAndrew Turner 147947e07394SAndrew Turner int 148047e07394SAndrew Turner vm_set_capability(struct vcpu *vcpu, int type, int val) 148147e07394SAndrew Turner { 148247e07394SAndrew Turner if (type < 0 || type >= VM_CAP_MAX) 148347e07394SAndrew Turner return (EINVAL); 148447e07394SAndrew Turner 148547e07394SAndrew Turner return (vmmops_setcap(vcpu->cookie, type, val)); 148647e07394SAndrew Turner } 148747e07394SAndrew Turner 148847e07394SAndrew Turner struct vm * 148947e07394SAndrew Turner vcpu_vm(struct vcpu *vcpu) 149047e07394SAndrew Turner { 149147e07394SAndrew Turner return (vcpu->vm); 149247e07394SAndrew Turner } 149347e07394SAndrew Turner 149447e07394SAndrew Turner int 149547e07394SAndrew Turner vcpu_vcpuid(struct vcpu *vcpu) 149647e07394SAndrew Turner { 149747e07394SAndrew Turner return (vcpu->vcpuid); 149847e07394SAndrew Turner } 149947e07394SAndrew Turner 150047e07394SAndrew Turner void * 150147e07394SAndrew Turner vcpu_get_cookie(struct vcpu *vcpu) 150247e07394SAndrew Turner { 150347e07394SAndrew Turner return (vcpu->cookie); 150447e07394SAndrew Turner } 150547e07394SAndrew Turner 150647e07394SAndrew Turner struct vcpu * 150747e07394SAndrew Turner vm_vcpu(struct vm *vm, int vcpuid) 150847e07394SAndrew Turner { 150947e07394SAndrew Turner return (vm->vcpu[vcpuid]); 151047e07394SAndrew Turner } 151147e07394SAndrew Turner 151247e07394SAndrew Turner int 151347e07394SAndrew Turner vcpu_set_state(struct vcpu *vcpu, enum vcpu_state newstate, bool from_idle) 151447e07394SAndrew Turner { 151547e07394SAndrew Turner int error; 151647e07394SAndrew Turner 151747e07394SAndrew Turner vcpu_lock(vcpu); 151847e07394SAndrew Turner error = vcpu_set_state_locked(vcpu, newstate, from_idle); 151947e07394SAndrew Turner vcpu_unlock(vcpu); 152047e07394SAndrew Turner 152147e07394SAndrew Turner return (error); 152247e07394SAndrew Turner } 152347e07394SAndrew Turner 152447e07394SAndrew Turner enum vcpu_state 152547e07394SAndrew Turner vcpu_get_state(struct vcpu *vcpu, int *hostcpu) 152647e07394SAndrew Turner { 152747e07394SAndrew Turner enum vcpu_state state; 152847e07394SAndrew Turner 152947e07394SAndrew Turner vcpu_lock(vcpu); 153047e07394SAndrew Turner state = vcpu->state; 153147e07394SAndrew Turner if (hostcpu != NULL) 153247e07394SAndrew Turner *hostcpu = vcpu->hostcpu; 153347e07394SAndrew Turner vcpu_unlock(vcpu); 153447e07394SAndrew Turner 153547e07394SAndrew Turner return (state); 153647e07394SAndrew Turner } 153747e07394SAndrew Turner 153847e07394SAndrew Turner static void * 153947e07394SAndrew Turner _vm_gpa_hold(struct vm *vm, vm_paddr_t gpa, size_t len, int reqprot, 154047e07394SAndrew Turner void **cookie) 154147e07394SAndrew Turner { 154247e07394SAndrew Turner int i, count, pageoff; 154347e07394SAndrew Turner struct mem_map *mm; 154447e07394SAndrew Turner vm_page_t m; 154547e07394SAndrew Turner 154647e07394SAndrew Turner pageoff = gpa & PAGE_MASK; 154747e07394SAndrew Turner if (len > PAGE_SIZE - pageoff) 154847e07394SAndrew Turner panic("vm_gpa_hold: invalid gpa/len: 0x%016lx/%lu", gpa, len); 154947e07394SAndrew Turner 155047e07394SAndrew Turner count = 0; 155147e07394SAndrew Turner for (i = 0; i < VM_MAX_MEMMAPS; i++) { 155247e07394SAndrew Turner mm = &vm->mem_maps[i]; 155347e07394SAndrew Turner if (sysmem_mapping(vm, mm) && gpa >= mm->gpa && 155447e07394SAndrew Turner gpa < mm->gpa + mm->len) { 155547e07394SAndrew Turner count = vm_fault_quick_hold_pages(&vm->vmspace->vm_map, 155647e07394SAndrew Turner trunc_page(gpa), PAGE_SIZE, reqprot, &m, 1); 155747e07394SAndrew Turner break; 155847e07394SAndrew Turner } 155947e07394SAndrew Turner } 156047e07394SAndrew Turner 156147e07394SAndrew Turner if (count == 1) { 156247e07394SAndrew Turner *cookie = m; 156347e07394SAndrew Turner return ((void *)(PHYS_TO_DMAP(VM_PAGE_TO_PHYS(m)) + pageoff)); 156447e07394SAndrew Turner } else { 156547e07394SAndrew Turner *cookie = NULL; 156647e07394SAndrew Turner return (NULL); 156747e07394SAndrew Turner } 156847e07394SAndrew Turner } 156947e07394SAndrew Turner 157047e07394SAndrew Turner void * 157147e07394SAndrew Turner vm_gpa_hold(struct vcpu *vcpu, vm_paddr_t gpa, size_t len, int reqprot, 157247e07394SAndrew Turner void **cookie) 157347e07394SAndrew Turner { 157447e07394SAndrew Turner #ifdef INVARIANTS 157547e07394SAndrew Turner /* 157647e07394SAndrew Turner * The current vcpu should be frozen to ensure 'vm_memmap[]' 157747e07394SAndrew Turner * stability. 157847e07394SAndrew Turner */ 157947e07394SAndrew Turner int state = vcpu_get_state(vcpu, NULL); 158047e07394SAndrew Turner KASSERT(state == VCPU_FROZEN, ("%s: invalid vcpu state %d", 158147e07394SAndrew Turner __func__, state)); 158247e07394SAndrew Turner #endif 158347e07394SAndrew Turner return (_vm_gpa_hold(vcpu->vm, gpa, len, reqprot, cookie)); 158447e07394SAndrew Turner } 158547e07394SAndrew Turner 158647e07394SAndrew Turner void * 158747e07394SAndrew Turner vm_gpa_hold_global(struct vm *vm, vm_paddr_t gpa, size_t len, int reqprot, 158847e07394SAndrew Turner void **cookie) 158947e07394SAndrew Turner { 159047e07394SAndrew Turner sx_assert(&vm->mem_segs_lock, SX_LOCKED); 159147e07394SAndrew Turner return (_vm_gpa_hold(vm, gpa, len, reqprot, cookie)); 159247e07394SAndrew Turner } 159347e07394SAndrew Turner 159447e07394SAndrew Turner void 159547e07394SAndrew Turner vm_gpa_release(void *cookie) 159647e07394SAndrew Turner { 159747e07394SAndrew Turner vm_page_t m = cookie; 159847e07394SAndrew Turner 159947e07394SAndrew Turner vm_page_unwire(m, PQ_ACTIVE); 160047e07394SAndrew Turner } 160147e07394SAndrew Turner 160247e07394SAndrew Turner int 160347e07394SAndrew Turner vm_get_register(struct vcpu *vcpu, int reg, uint64_t *retval) 160447e07394SAndrew Turner { 160547e07394SAndrew Turner 160647e07394SAndrew Turner if (reg >= VM_REG_LAST) 160747e07394SAndrew Turner return (EINVAL); 160847e07394SAndrew Turner 160947e07394SAndrew Turner return (vmmops_getreg(vcpu->cookie, reg, retval)); 161047e07394SAndrew Turner } 161147e07394SAndrew Turner 161247e07394SAndrew Turner int 161347e07394SAndrew Turner vm_set_register(struct vcpu *vcpu, int reg, uint64_t val) 161447e07394SAndrew Turner { 161547e07394SAndrew Turner int error; 161647e07394SAndrew Turner 161747e07394SAndrew Turner if (reg >= VM_REG_LAST) 161847e07394SAndrew Turner return (EINVAL); 161947e07394SAndrew Turner error = vmmops_setreg(vcpu->cookie, reg, val); 162047e07394SAndrew Turner if (error || reg != VM_REG_GUEST_PC) 162147e07394SAndrew Turner return (error); 162247e07394SAndrew Turner 162347e07394SAndrew Turner vcpu->nextpc = val; 162447e07394SAndrew Turner 162547e07394SAndrew Turner return (0); 162647e07394SAndrew Turner } 162747e07394SAndrew Turner 162847e07394SAndrew Turner void * 162947e07394SAndrew Turner vm_get_cookie(struct vm *vm) 163047e07394SAndrew Turner { 163147e07394SAndrew Turner return (vm->cookie); 163247e07394SAndrew Turner } 163347e07394SAndrew Turner 163447e07394SAndrew Turner int 163547e07394SAndrew Turner vm_inject_exception(struct vcpu *vcpu, uint64_t esr, uint64_t far) 163647e07394SAndrew Turner { 163747e07394SAndrew Turner return (vmmops_exception(vcpu->cookie, esr, far)); 163847e07394SAndrew Turner } 163947e07394SAndrew Turner 164047e07394SAndrew Turner int 164147e07394SAndrew Turner vm_attach_vgic(struct vm *vm, struct vm_vgic_descr *descr) 164247e07394SAndrew Turner { 164347e07394SAndrew Turner return (vgic_attach_to_vm(vm->cookie, descr)); 164447e07394SAndrew Turner } 164547e07394SAndrew Turner 164647e07394SAndrew Turner int 164747e07394SAndrew Turner vm_assert_irq(struct vm *vm, uint32_t irq) 164847e07394SAndrew Turner { 164947e07394SAndrew Turner return (vgic_inject_irq(vm->cookie, -1, irq, true)); 165047e07394SAndrew Turner } 165147e07394SAndrew Turner 165247e07394SAndrew Turner int 165347e07394SAndrew Turner vm_deassert_irq(struct vm *vm, uint32_t irq) 165447e07394SAndrew Turner { 165547e07394SAndrew Turner return (vgic_inject_irq(vm->cookie, -1, irq, false)); 165647e07394SAndrew Turner } 165747e07394SAndrew Turner 165847e07394SAndrew Turner int 165947e07394SAndrew Turner vm_raise_msi(struct vm *vm, uint64_t msg, uint64_t addr, int bus, int slot, 166047e07394SAndrew Turner int func) 166147e07394SAndrew Turner { 166247e07394SAndrew Turner /* TODO: Should we raise an SError? */ 166347e07394SAndrew Turner return (vgic_inject_msi(vm->cookie, msg, addr)); 166447e07394SAndrew Turner } 166547e07394SAndrew Turner 166647e07394SAndrew Turner static int 166747e07394SAndrew Turner vm_handle_smccc_call(struct vcpu *vcpu, struct vm_exit *vme, bool *retu) 166847e07394SAndrew Turner { 166947e07394SAndrew Turner struct hypctx *hypctx; 167047e07394SAndrew Turner int i; 167147e07394SAndrew Turner 167247e07394SAndrew Turner hypctx = vcpu_get_cookie(vcpu); 167347e07394SAndrew Turner 167447e07394SAndrew Turner if ((hypctx->tf.tf_esr & ESR_ELx_ISS_MASK) != 0) 167547e07394SAndrew Turner return (1); 167647e07394SAndrew Turner 167747e07394SAndrew Turner vme->exitcode = VM_EXITCODE_SMCCC; 167847e07394SAndrew Turner vme->u.smccc_call.func_id = hypctx->tf.tf_x[0]; 167947e07394SAndrew Turner for (i = 0; i < nitems(vme->u.smccc_call.args); i++) 168047e07394SAndrew Turner vme->u.smccc_call.args[i] = hypctx->tf.tf_x[i + 1]; 168147e07394SAndrew Turner 168247e07394SAndrew Turner *retu = true; 168347e07394SAndrew Turner return (0); 168447e07394SAndrew Turner } 168547e07394SAndrew Turner 168647e07394SAndrew Turner static int 168747e07394SAndrew Turner vm_handle_wfi(struct vcpu *vcpu, struct vm_exit *vme, bool *retu) 168847e07394SAndrew Turner { 168947e07394SAndrew Turner vcpu_lock(vcpu); 169047e07394SAndrew Turner while (1) { 169147e07394SAndrew Turner if (vgic_has_pending_irq(vcpu->cookie)) 169247e07394SAndrew Turner break; 169347e07394SAndrew Turner 169447e07394SAndrew Turner if (vcpu_should_yield(vcpu)) 169547e07394SAndrew Turner break; 169647e07394SAndrew Turner 169747e07394SAndrew Turner vcpu_require_state_locked(vcpu, VCPU_SLEEPING); 169847e07394SAndrew Turner /* 169947e07394SAndrew Turner * XXX msleep_spin() cannot be interrupted by signals so 170047e07394SAndrew Turner * wake up periodically to check pending signals. 170147e07394SAndrew Turner */ 170247e07394SAndrew Turner msleep_spin(vcpu, &vcpu->mtx, "vmidle", hz); 170347e07394SAndrew Turner vcpu_require_state_locked(vcpu, VCPU_FROZEN); 170447e07394SAndrew Turner } 170547e07394SAndrew Turner vcpu_unlock(vcpu); 170647e07394SAndrew Turner 170747e07394SAndrew Turner *retu = false; 170847e07394SAndrew Turner return (0); 170947e07394SAndrew Turner } 171047e07394SAndrew Turner 171147e07394SAndrew Turner static int 171247e07394SAndrew Turner vm_handle_paging(struct vcpu *vcpu, bool *retu) 171347e07394SAndrew Turner { 171447e07394SAndrew Turner struct vm *vm = vcpu->vm; 171547e07394SAndrew Turner struct vm_exit *vme; 171647e07394SAndrew Turner struct vm_map *map; 171747e07394SAndrew Turner uint64_t addr, esr; 171847e07394SAndrew Turner pmap_t pmap; 171947e07394SAndrew Turner int ftype, rv; 172047e07394SAndrew Turner 172147e07394SAndrew Turner vme = &vcpu->exitinfo; 172247e07394SAndrew Turner 172347e07394SAndrew Turner pmap = vmspace_pmap(vcpu->vm->vmspace); 172447e07394SAndrew Turner addr = vme->u.paging.gpa; 172547e07394SAndrew Turner esr = vme->u.paging.esr; 172647e07394SAndrew Turner 172747e07394SAndrew Turner /* The page exists, but the page table needs to be updated. */ 172847e07394SAndrew Turner if (pmap_fault(pmap, esr, addr) == KERN_SUCCESS) 172947e07394SAndrew Turner return (0); 173047e07394SAndrew Turner 173147e07394SAndrew Turner switch (ESR_ELx_EXCEPTION(esr)) { 173247e07394SAndrew Turner case EXCP_INSN_ABORT_L: 173347e07394SAndrew Turner case EXCP_DATA_ABORT_L: 173447e07394SAndrew Turner ftype = VM_PROT_EXECUTE | VM_PROT_READ | VM_PROT_WRITE; 173547e07394SAndrew Turner break; 173647e07394SAndrew Turner default: 173747e07394SAndrew Turner panic("%s: Invalid exception (esr = %lx)", __func__, esr); 173847e07394SAndrew Turner } 173947e07394SAndrew Turner 174047e07394SAndrew Turner map = &vm->vmspace->vm_map; 174147e07394SAndrew Turner rv = vm_fault(map, vme->u.paging.gpa, ftype, VM_FAULT_NORMAL, NULL); 174247e07394SAndrew Turner if (rv != KERN_SUCCESS) 174347e07394SAndrew Turner return (EFAULT); 174447e07394SAndrew Turner 174547e07394SAndrew Turner return (0); 174647e07394SAndrew Turner } 174747e07394SAndrew Turner 17481ee7a8faSMark Johnston static int 17491ee7a8faSMark Johnston vm_handle_suspend(struct vcpu *vcpu, bool *retu) 17501ee7a8faSMark Johnston { 17511ee7a8faSMark Johnston struct vm *vm = vcpu->vm; 17521ee7a8faSMark Johnston int error, i; 17531ee7a8faSMark Johnston struct thread *td; 17541ee7a8faSMark Johnston 17551ee7a8faSMark Johnston error = 0; 17561ee7a8faSMark Johnston td = curthread; 17571ee7a8faSMark Johnston 17581ee7a8faSMark Johnston CPU_SET_ATOMIC(vcpu->vcpuid, &vm->suspended_cpus); 17591ee7a8faSMark Johnston 17601ee7a8faSMark Johnston /* 17611ee7a8faSMark Johnston * Wait until all 'active_cpus' have suspended themselves. 17621ee7a8faSMark Johnston * 17631ee7a8faSMark Johnston * Since a VM may be suspended at any time including when one or 17641ee7a8faSMark Johnston * more vcpus are doing a rendezvous we need to call the rendezvous 17651ee7a8faSMark Johnston * handler while we are waiting to prevent a deadlock. 17661ee7a8faSMark Johnston */ 17671ee7a8faSMark Johnston vcpu_lock(vcpu); 17681ee7a8faSMark Johnston while (error == 0) { 17691ee7a8faSMark Johnston if (CPU_CMP(&vm->suspended_cpus, &vm->active_cpus) == 0) 17701ee7a8faSMark Johnston break; 17711ee7a8faSMark Johnston 17721ee7a8faSMark Johnston vcpu_require_state_locked(vcpu, VCPU_SLEEPING); 17731ee7a8faSMark Johnston msleep_spin(vcpu, &vcpu->mtx, "vmsusp", hz); 17741ee7a8faSMark Johnston vcpu_require_state_locked(vcpu, VCPU_FROZEN); 17751ee7a8faSMark Johnston if (td_ast_pending(td, TDA_SUSPEND)) { 17761ee7a8faSMark Johnston vcpu_unlock(vcpu); 17771ee7a8faSMark Johnston error = thread_check_susp(td, false); 17781ee7a8faSMark Johnston vcpu_lock(vcpu); 17791ee7a8faSMark Johnston } 17801ee7a8faSMark Johnston } 17811ee7a8faSMark Johnston vcpu_unlock(vcpu); 17821ee7a8faSMark Johnston 17831ee7a8faSMark Johnston /* 17841ee7a8faSMark Johnston * Wakeup the other sleeping vcpus and return to userspace. 17851ee7a8faSMark Johnston */ 17861ee7a8faSMark Johnston for (i = 0; i < vm->maxcpus; i++) { 17871ee7a8faSMark Johnston if (CPU_ISSET(i, &vm->suspended_cpus)) { 17881ee7a8faSMark Johnston vcpu_notify_event(vm_vcpu(vm, i)); 17891ee7a8faSMark Johnston } 17901ee7a8faSMark Johnston } 17911ee7a8faSMark Johnston 17921ee7a8faSMark Johnston *retu = true; 17931ee7a8faSMark Johnston return (error); 17941ee7a8faSMark Johnston } 17951ee7a8faSMark Johnston 179647e07394SAndrew Turner int 179747e07394SAndrew Turner vm_run(struct vcpu *vcpu) 179847e07394SAndrew Turner { 179947e07394SAndrew Turner struct vm *vm = vcpu->vm; 180047e07394SAndrew Turner struct vm_eventinfo evinfo; 180147e07394SAndrew Turner int error, vcpuid; 180247e07394SAndrew Turner struct vm_exit *vme; 180347e07394SAndrew Turner bool retu; 180447e07394SAndrew Turner pmap_t pmap; 180547e07394SAndrew Turner 180647e07394SAndrew Turner vcpuid = vcpu->vcpuid; 180747e07394SAndrew Turner 180847e07394SAndrew Turner if (!CPU_ISSET(vcpuid, &vm->active_cpus)) 180947e07394SAndrew Turner return (EINVAL); 181047e07394SAndrew Turner 181147e07394SAndrew Turner if (CPU_ISSET(vcpuid, &vm->suspended_cpus)) 181247e07394SAndrew Turner return (EINVAL); 181347e07394SAndrew Turner 181447e07394SAndrew Turner pmap = vmspace_pmap(vm->vmspace); 181547e07394SAndrew Turner vme = &vcpu->exitinfo; 181647e07394SAndrew Turner evinfo.rptr = NULL; 181747e07394SAndrew Turner evinfo.sptr = &vm->suspend; 181847e07394SAndrew Turner evinfo.iptr = NULL; 181947e07394SAndrew Turner restart: 182047e07394SAndrew Turner critical_enter(); 182147e07394SAndrew Turner 182247e07394SAndrew Turner restore_guest_fpustate(vcpu); 182347e07394SAndrew Turner 182447e07394SAndrew Turner vcpu_require_state(vcpu, VCPU_RUNNING); 182547e07394SAndrew Turner error = vmmops_run(vcpu->cookie, vcpu->nextpc, pmap, &evinfo); 182647e07394SAndrew Turner vcpu_require_state(vcpu, VCPU_FROZEN); 182747e07394SAndrew Turner 182847e07394SAndrew Turner save_guest_fpustate(vcpu); 182947e07394SAndrew Turner 183047e07394SAndrew Turner critical_exit(); 183147e07394SAndrew Turner 183247e07394SAndrew Turner if (error == 0) { 183347e07394SAndrew Turner retu = false; 183447e07394SAndrew Turner switch (vme->exitcode) { 183547e07394SAndrew Turner case VM_EXITCODE_INST_EMUL: 183647e07394SAndrew Turner vcpu->nextpc = vme->pc + vme->inst_length; 183747e07394SAndrew Turner error = vm_handle_inst_emul(vcpu, &retu); 183847e07394SAndrew Turner break; 183947e07394SAndrew Turner 184047e07394SAndrew Turner case VM_EXITCODE_REG_EMUL: 184147e07394SAndrew Turner vcpu->nextpc = vme->pc + vme->inst_length; 184247e07394SAndrew Turner error = vm_handle_reg_emul(vcpu, &retu); 184347e07394SAndrew Turner break; 184447e07394SAndrew Turner 184547e07394SAndrew Turner case VM_EXITCODE_HVC: 184647e07394SAndrew Turner /* 184747e07394SAndrew Turner * The HVC instruction saves the address for the 184847e07394SAndrew Turner * next instruction as the return address. 184947e07394SAndrew Turner */ 185047e07394SAndrew Turner vcpu->nextpc = vme->pc; 185147e07394SAndrew Turner /* 185247e07394SAndrew Turner * The PSCI call can change the exit information in the 185347e07394SAndrew Turner * case of suspend/reset/poweroff/cpu off/cpu on. 185447e07394SAndrew Turner */ 185547e07394SAndrew Turner error = vm_handle_smccc_call(vcpu, vme, &retu); 185647e07394SAndrew Turner break; 185747e07394SAndrew Turner 185847e07394SAndrew Turner case VM_EXITCODE_WFI: 185947e07394SAndrew Turner vcpu->nextpc = vme->pc + vme->inst_length; 186047e07394SAndrew Turner error = vm_handle_wfi(vcpu, vme, &retu); 186147e07394SAndrew Turner break; 186247e07394SAndrew Turner 186347e07394SAndrew Turner case VM_EXITCODE_PAGING: 186447e07394SAndrew Turner vcpu->nextpc = vme->pc; 186547e07394SAndrew Turner error = vm_handle_paging(vcpu, &retu); 186647e07394SAndrew Turner break; 186747e07394SAndrew Turner 18681ee7a8faSMark Johnston case VM_EXITCODE_SUSPENDED: 18691ee7a8faSMark Johnston vcpu->nextpc = vme->pc; 18701ee7a8faSMark Johnston error = vm_handle_suspend(vcpu, &retu); 18711ee7a8faSMark Johnston break; 18721ee7a8faSMark Johnston 187347e07394SAndrew Turner default: 187447e07394SAndrew Turner /* Handle in userland */ 187547e07394SAndrew Turner vcpu->nextpc = vme->pc; 187647e07394SAndrew Turner retu = true; 187747e07394SAndrew Turner break; 187847e07394SAndrew Turner } 187947e07394SAndrew Turner } 188047e07394SAndrew Turner 188147e07394SAndrew Turner if (error == 0 && retu == false) 188247e07394SAndrew Turner goto restart; 188347e07394SAndrew Turner 188447e07394SAndrew Turner return (error); 188547e07394SAndrew Turner } 1886