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