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/cdefs.h> 3047e07394SAndrew Turner #include <sys/param.h> 3147e07394SAndrew Turner #include <sys/systm.h> 3247e07394SAndrew Turner #include <sys/smp.h> 3347e07394SAndrew Turner #include <sys/kernel.h> 3447e07394SAndrew Turner #include <sys/malloc.h> 3547e07394SAndrew Turner #include <sys/mman.h> 3647e07394SAndrew Turner #include <sys/pcpu.h> 3747e07394SAndrew Turner #include <sys/proc.h> 3847e07394SAndrew Turner #include <sys/sysctl.h> 3947e07394SAndrew Turner #include <sys/lock.h> 4047e07394SAndrew Turner #include <sys/mutex.h> 4147e07394SAndrew Turner #include <sys/vmem.h> 4247e07394SAndrew Turner 4347e07394SAndrew Turner #include <vm/vm.h> 4447e07394SAndrew Turner #include <vm/pmap.h> 4547e07394SAndrew Turner #include <vm/vm_extern.h> 4647e07394SAndrew Turner #include <vm/vm_map.h> 4747e07394SAndrew Turner #include <vm/vm_page.h> 4847e07394SAndrew Turner #include <vm/vm_param.h> 4947e07394SAndrew Turner 5047e07394SAndrew Turner #include <machine/armreg.h> 5147e07394SAndrew Turner #include <machine/vm.h> 5247e07394SAndrew Turner #include <machine/cpufunc.h> 5347e07394SAndrew Turner #include <machine/cpu.h> 5447e07394SAndrew Turner #include <machine/machdep.h> 5547e07394SAndrew Turner #include <machine/vmm.h> 5647e07394SAndrew Turner #include <machine/vmm_dev.h> 5747e07394SAndrew Turner #include <machine/atomic.h> 5847e07394SAndrew Turner #include <machine/hypervisor.h> 5947e07394SAndrew Turner #include <machine/pmap.h> 6047e07394SAndrew Turner 6147e07394SAndrew Turner #include "mmu.h" 6247e07394SAndrew Turner #include "arm64.h" 6347e07394SAndrew Turner #include "hyp.h" 6447e07394SAndrew Turner #include "reset.h" 6547e07394SAndrew Turner #include "io/vgic.h" 6647e07394SAndrew Turner #include "io/vgic_v3.h" 6747e07394SAndrew Turner #include "io/vtimer.h" 68*55aa3148SAndrew Turner #include "vmm_handlers.h" 6947e07394SAndrew Turner #include "vmm_stat.h" 7047e07394SAndrew Turner 7147e07394SAndrew Turner #define HANDLED 1 7247e07394SAndrew Turner #define UNHANDLED 0 7347e07394SAndrew Turner 7447e07394SAndrew Turner /* Number of bits in an EL2 virtual address */ 7547e07394SAndrew Turner #define EL2_VIRT_BITS 48 7647e07394SAndrew Turner CTASSERT((1ul << EL2_VIRT_BITS) >= HYP_VM_MAX_ADDRESS); 7747e07394SAndrew Turner 7847e07394SAndrew Turner /* TODO: Move the host hypctx off the stack */ 7947e07394SAndrew Turner #define VMM_STACK_PAGES 4 8047e07394SAndrew Turner #define VMM_STACK_SIZE (VMM_STACK_PAGES * PAGE_SIZE) 8147e07394SAndrew Turner 8247e07394SAndrew Turner static int vmm_pmap_levels, vmm_virt_bits, vmm_max_ipa_bits; 8347e07394SAndrew Turner 8447e07394SAndrew Turner /* Register values passed to arm_setup_vectors to set in the hypervisor */ 8547e07394SAndrew Turner struct vmm_init_regs { 8647e07394SAndrew Turner uint64_t tcr_el2; 8747e07394SAndrew Turner uint64_t vtcr_el2; 8847e07394SAndrew Turner }; 8947e07394SAndrew Turner 9047e07394SAndrew Turner MALLOC_DEFINE(M_HYP, "ARM VMM HYP", "ARM VMM HYP"); 9147e07394SAndrew Turner 9247e07394SAndrew Turner extern char hyp_init_vectors[]; 9347e07394SAndrew Turner extern char hyp_vectors[]; 9447e07394SAndrew Turner extern char hyp_stub_vectors[]; 9547e07394SAndrew Turner 9647e07394SAndrew Turner static vm_paddr_t hyp_code_base; 9747e07394SAndrew Turner static size_t hyp_code_len; 9847e07394SAndrew Turner 9947e07394SAndrew Turner static char *stack[MAXCPU]; 10047e07394SAndrew Turner static vm_offset_t stack_hyp_va[MAXCPU]; 10147e07394SAndrew Turner 10247e07394SAndrew Turner static vmem_t *el2_mem_alloc; 10347e07394SAndrew Turner 10447e07394SAndrew Turner static void arm_setup_vectors(void *arg); 10547e07394SAndrew Turner 10647e07394SAndrew Turner DPCPU_DEFINE_STATIC(struct hypctx *, vcpu); 10747e07394SAndrew Turner 10847e07394SAndrew Turner static inline void 10947e07394SAndrew Turner arm64_set_active_vcpu(struct hypctx *hypctx) 11047e07394SAndrew Turner { 11147e07394SAndrew Turner DPCPU_SET(vcpu, hypctx); 11247e07394SAndrew Turner } 11347e07394SAndrew Turner 11447e07394SAndrew Turner struct hypctx * 11547e07394SAndrew Turner arm64_get_active_vcpu(void) 11647e07394SAndrew Turner { 11747e07394SAndrew Turner return (DPCPU_GET(vcpu)); 11847e07394SAndrew Turner } 11947e07394SAndrew Turner 12047e07394SAndrew Turner static void 12147e07394SAndrew Turner arm_setup_vectors(void *arg) 12247e07394SAndrew Turner { 12347e07394SAndrew Turner struct vmm_init_regs *el2_regs; 12447e07394SAndrew Turner uintptr_t stack_top; 12547e07394SAndrew Turner uint32_t sctlr_el2; 12647e07394SAndrew Turner register_t daif; 12747e07394SAndrew Turner 12847e07394SAndrew Turner el2_regs = arg; 12947e07394SAndrew Turner arm64_set_active_vcpu(NULL); 13047e07394SAndrew Turner 13147e07394SAndrew Turner daif = intr_disable(); 13247e07394SAndrew Turner 13347e07394SAndrew Turner /* 13447e07394SAndrew Turner * Install the temporary vectors which will be responsible for 13547e07394SAndrew Turner * initializing the VMM when we next trap into EL2. 13647e07394SAndrew Turner * 13747e07394SAndrew Turner * x0: the exception vector table responsible for hypervisor 13847e07394SAndrew Turner * initialization on the next call. 13947e07394SAndrew Turner */ 14047e07394SAndrew Turner vmm_call_hyp(vtophys(&vmm_hyp_code)); 14147e07394SAndrew Turner 14247e07394SAndrew Turner /* Create and map the hypervisor stack */ 14347e07394SAndrew Turner stack_top = stack_hyp_va[PCPU_GET(cpuid)] + VMM_STACK_SIZE; 14447e07394SAndrew Turner 14547e07394SAndrew Turner /* 14647e07394SAndrew Turner * Configure the system control register for EL2: 14747e07394SAndrew Turner * 14847e07394SAndrew Turner * SCTLR_EL2_M: MMU on 14947e07394SAndrew Turner * SCTLR_EL2_C: Data cacheability not affected 15047e07394SAndrew Turner * SCTLR_EL2_I: Instruction cacheability not affected 15147e07394SAndrew Turner * SCTLR_EL2_A: Instruction alignment check 15247e07394SAndrew Turner * SCTLR_EL2_SA: Stack pointer alignment check 15347e07394SAndrew Turner * SCTLR_EL2_WXN: Treat writable memory as execute never 15447e07394SAndrew Turner * ~SCTLR_EL2_EE: Data accesses are little-endian 15547e07394SAndrew Turner */ 15647e07394SAndrew Turner sctlr_el2 = SCTLR_EL2_RES1; 15747e07394SAndrew Turner sctlr_el2 |= SCTLR_EL2_M | SCTLR_EL2_C | SCTLR_EL2_I; 15847e07394SAndrew Turner sctlr_el2 |= SCTLR_EL2_A | SCTLR_EL2_SA; 15947e07394SAndrew Turner sctlr_el2 |= SCTLR_EL2_WXN; 16047e07394SAndrew Turner sctlr_el2 &= ~SCTLR_EL2_EE; 16147e07394SAndrew Turner 16247e07394SAndrew Turner /* Special call to initialize EL2 */ 16347e07394SAndrew Turner vmm_call_hyp(vmmpmap_to_ttbr0(), stack_top, el2_regs->tcr_el2, 16447e07394SAndrew Turner sctlr_el2, el2_regs->vtcr_el2); 16547e07394SAndrew Turner 16647e07394SAndrew Turner intr_restore(daif); 16747e07394SAndrew Turner } 16847e07394SAndrew Turner 16947e07394SAndrew Turner static void 17047e07394SAndrew Turner arm_teardown_vectors(void *arg) 17147e07394SAndrew Turner { 17247e07394SAndrew Turner register_t daif; 17347e07394SAndrew Turner 17447e07394SAndrew Turner /* 17547e07394SAndrew Turner * vmm_cleanup() will disable the MMU. For the next few instructions, 17647e07394SAndrew Turner * before the hardware disables the MMU, one of the following is 17747e07394SAndrew Turner * possible: 17847e07394SAndrew Turner * 17947e07394SAndrew Turner * a. The instruction addresses are fetched with the MMU disabled, 18047e07394SAndrew Turner * and they must represent the actual physical addresses. This will work 18147e07394SAndrew Turner * because we call the vmm_cleanup() function by its physical address. 18247e07394SAndrew Turner * 18347e07394SAndrew Turner * b. The instruction addresses are fetched using the old translation 18447e07394SAndrew Turner * tables. This will work because we have an identity mapping in place 18547e07394SAndrew Turner * in the translation tables and vmm_cleanup() is called by its physical 18647e07394SAndrew Turner * address. 18747e07394SAndrew Turner */ 18847e07394SAndrew Turner daif = intr_disable(); 18947e07394SAndrew Turner /* TODO: Invalidate the cache */ 19047e07394SAndrew Turner vmm_call_hyp(HYP_CLEANUP, vtophys(hyp_stub_vectors)); 19147e07394SAndrew Turner intr_restore(daif); 19247e07394SAndrew Turner 19347e07394SAndrew Turner arm64_set_active_vcpu(NULL); 19447e07394SAndrew Turner } 19547e07394SAndrew Turner 19647e07394SAndrew Turner static uint64_t 19747e07394SAndrew Turner vmm_vtcr_el2_sl(u_int levels) 19847e07394SAndrew Turner { 19947e07394SAndrew Turner #if PAGE_SIZE == PAGE_SIZE_4K 20047e07394SAndrew Turner switch (levels) { 20147e07394SAndrew Turner case 2: 20247e07394SAndrew Turner return (VTCR_EL2_SL0_4K_LVL2); 20347e07394SAndrew Turner case 3: 20447e07394SAndrew Turner return (VTCR_EL2_SL0_4K_LVL1); 20547e07394SAndrew Turner case 4: 20647e07394SAndrew Turner return (VTCR_EL2_SL0_4K_LVL0); 20747e07394SAndrew Turner default: 20847e07394SAndrew Turner panic("%s: Invalid number of page table levels %u", __func__, 20947e07394SAndrew Turner levels); 21047e07394SAndrew Turner } 21147e07394SAndrew Turner #elif PAGE_SIZE == PAGE_SIZE_16K 21247e07394SAndrew Turner switch (levels) { 21347e07394SAndrew Turner case 2: 21447e07394SAndrew Turner return (VTCR_EL2_SL0_16K_LVL2); 21547e07394SAndrew Turner case 3: 21647e07394SAndrew Turner return (VTCR_EL2_SL0_16K_LVL1); 21747e07394SAndrew Turner case 4: 21847e07394SAndrew Turner return (VTCR_EL2_SL0_16K_LVL0); 21947e07394SAndrew Turner default: 22047e07394SAndrew Turner panic("%s: Invalid number of page table levels %u", __func__, 22147e07394SAndrew Turner levels); 22247e07394SAndrew Turner } 22347e07394SAndrew Turner #else 22447e07394SAndrew Turner #error Unsupported page size 22547e07394SAndrew Turner #endif 22647e07394SAndrew Turner } 22747e07394SAndrew Turner 22847e07394SAndrew Turner int 22947e07394SAndrew Turner vmmops_modinit(int ipinum) 23047e07394SAndrew Turner { 23147e07394SAndrew Turner struct vmm_init_regs el2_regs; 23247e07394SAndrew Turner vm_offset_t next_hyp_va; 23347e07394SAndrew Turner vm_paddr_t vmm_base; 23447e07394SAndrew Turner uint64_t id_aa64mmfr0_el1, pa_range_bits, pa_range_field; 23547e07394SAndrew Turner uint64_t cnthctl_el2; 23647e07394SAndrew Turner int cpu, i; 23747e07394SAndrew Turner bool rv __diagused; 23847e07394SAndrew Turner 239d730cdeaSMark Johnston if (!has_hyp()) { 24047e07394SAndrew Turner printf( 24147e07394SAndrew Turner "vmm: Processor doesn't have support for virtualization\n"); 24247e07394SAndrew Turner return (ENXIO); 24347e07394SAndrew Turner } 24447e07394SAndrew Turner 24547e07394SAndrew Turner /* TODO: Support VHE */ 24647e07394SAndrew Turner if (in_vhe()) { 24747e07394SAndrew Turner printf("vmm: VHE is unsupported\n"); 24847e07394SAndrew Turner return (ENXIO); 24947e07394SAndrew Turner } 25047e07394SAndrew Turner 25147e07394SAndrew Turner if (!vgic_present()) { 25247e07394SAndrew Turner printf("vmm: No vgic found\n"); 25347e07394SAndrew Turner return (ENODEV); 25447e07394SAndrew Turner } 25547e07394SAndrew Turner 25647e07394SAndrew Turner if (!get_kernel_reg(ID_AA64MMFR0_EL1, &id_aa64mmfr0_el1)) { 25747e07394SAndrew Turner printf("vmm: Unable to read ID_AA64MMFR0_EL1\n"); 25847e07394SAndrew Turner return (ENXIO); 25947e07394SAndrew Turner } 26047e07394SAndrew Turner pa_range_field = ID_AA64MMFR0_PARange_VAL(id_aa64mmfr0_el1); 26147e07394SAndrew Turner /* 26247e07394SAndrew Turner * Use 3 levels to give us up to 39 bits with 4k pages, or 26347e07394SAndrew Turner * 47 bits with 16k pages. 26447e07394SAndrew Turner */ 26547e07394SAndrew Turner /* TODO: Check the number of levels for 64k pages */ 26647e07394SAndrew Turner vmm_pmap_levels = 3; 26747e07394SAndrew Turner switch (pa_range_field) { 26847e07394SAndrew Turner case ID_AA64MMFR0_PARange_4G: 26947e07394SAndrew Turner printf("vmm: Not enough physical address bits\n"); 27047e07394SAndrew Turner return (ENXIO); 27147e07394SAndrew Turner case ID_AA64MMFR0_PARange_64G: 27247e07394SAndrew Turner vmm_virt_bits = 36; 27347e07394SAndrew Turner #if PAGE_SIZE == PAGE_SIZE_16K 27447e07394SAndrew Turner vmm_pmap_levels = 2; 27547e07394SAndrew Turner #endif 27647e07394SAndrew Turner break; 27747e07394SAndrew Turner default: 27847e07394SAndrew Turner vmm_virt_bits = 39; 27947e07394SAndrew Turner break; 28047e07394SAndrew Turner } 28147e07394SAndrew Turner pa_range_bits = pa_range_field >> ID_AA64MMFR0_PARange_SHIFT; 28247e07394SAndrew Turner 28347e07394SAndrew Turner /* Initialise the EL2 MMU */ 28447e07394SAndrew Turner if (!vmmpmap_init()) { 28547e07394SAndrew Turner printf("vmm: Failed to init the EL2 MMU\n"); 28647e07394SAndrew Turner return (ENOMEM); 28747e07394SAndrew Turner } 28847e07394SAndrew Turner 28947e07394SAndrew Turner /* Set up the stage 2 pmap callbacks */ 29047e07394SAndrew Turner MPASS(pmap_clean_stage2_tlbi == NULL); 291*55aa3148SAndrew Turner pmap_clean_stage2_tlbi = vmm_clean_s2_tlbi; 292*55aa3148SAndrew Turner pmap_stage2_invalidate_range = vmm_s2_tlbi_range; 293*55aa3148SAndrew Turner pmap_stage2_invalidate_all = vmm_s2_tlbi_all; 29447e07394SAndrew Turner 29547e07394SAndrew Turner /* 29647e07394SAndrew Turner * Create an allocator for the virtual address space used by EL2. 29747e07394SAndrew Turner * EL2 code is identity-mapped; the allocator is used to find space for 29847e07394SAndrew Turner * VM structures. 29947e07394SAndrew Turner */ 30047e07394SAndrew Turner el2_mem_alloc = vmem_create("VMM EL2", 0, 0, PAGE_SIZE, 0, M_WAITOK); 30147e07394SAndrew Turner 30247e07394SAndrew Turner /* Create the mappings for the hypervisor translation table. */ 30347e07394SAndrew Turner hyp_code_len = round_page(&vmm_hyp_code_end - &vmm_hyp_code); 30447e07394SAndrew Turner 30547e07394SAndrew Turner /* We need an physical identity mapping for when we activate the MMU */ 30647e07394SAndrew Turner hyp_code_base = vmm_base = vtophys(&vmm_hyp_code); 30747e07394SAndrew Turner rv = vmmpmap_enter(vmm_base, hyp_code_len, vmm_base, 30847e07394SAndrew Turner VM_PROT_READ | VM_PROT_EXECUTE); 30947e07394SAndrew Turner MPASS(rv); 31047e07394SAndrew Turner 31147e07394SAndrew Turner next_hyp_va = roundup2(vmm_base + hyp_code_len, L2_SIZE); 31247e07394SAndrew Turner 31347e07394SAndrew Turner /* Create a per-CPU hypervisor stack */ 31447e07394SAndrew Turner CPU_FOREACH(cpu) { 31547e07394SAndrew Turner stack[cpu] = malloc(VMM_STACK_SIZE, M_HYP, M_WAITOK | M_ZERO); 31647e07394SAndrew Turner stack_hyp_va[cpu] = next_hyp_va; 31747e07394SAndrew Turner 31847e07394SAndrew Turner for (i = 0; i < VMM_STACK_PAGES; i++) { 31947e07394SAndrew Turner rv = vmmpmap_enter(stack_hyp_va[cpu] + ptoa(i), 32047e07394SAndrew Turner PAGE_SIZE, vtophys(stack[cpu] + ptoa(i)), 32147e07394SAndrew Turner VM_PROT_READ | VM_PROT_WRITE); 32247e07394SAndrew Turner MPASS(rv); 32347e07394SAndrew Turner } 32447e07394SAndrew Turner next_hyp_va += L2_SIZE; 32547e07394SAndrew Turner } 32647e07394SAndrew Turner 32747e07394SAndrew Turner el2_regs.tcr_el2 = TCR_EL2_RES1; 32847e07394SAndrew Turner el2_regs.tcr_el2 |= min(pa_range_bits << TCR_EL2_PS_SHIFT, 32947e07394SAndrew Turner TCR_EL2_PS_52BITS); 33047e07394SAndrew Turner el2_regs.tcr_el2 |= TCR_EL2_T0SZ(64 - EL2_VIRT_BITS); 33147e07394SAndrew Turner el2_regs.tcr_el2 |= TCR_EL2_IRGN0_WBWA | TCR_EL2_ORGN0_WBWA; 33247e07394SAndrew Turner #if PAGE_SIZE == PAGE_SIZE_4K 33347e07394SAndrew Turner el2_regs.tcr_el2 |= TCR_EL2_TG0_4K; 33447e07394SAndrew Turner #elif PAGE_SIZE == PAGE_SIZE_16K 33547e07394SAndrew Turner el2_regs.tcr_el2 |= TCR_EL2_TG0_16K; 33647e07394SAndrew Turner #else 33747e07394SAndrew Turner #error Unsupported page size 33847e07394SAndrew Turner #endif 33947e07394SAndrew Turner #ifdef SMP 34047e07394SAndrew Turner el2_regs.tcr_el2 |= TCR_EL2_SH0_IS; 34147e07394SAndrew Turner #endif 34247e07394SAndrew Turner 34347e07394SAndrew Turner switch (el2_regs.tcr_el2 & TCR_EL2_PS_MASK) { 34447e07394SAndrew Turner case TCR_EL2_PS_32BITS: 34547e07394SAndrew Turner vmm_max_ipa_bits = 32; 34647e07394SAndrew Turner break; 34747e07394SAndrew Turner case TCR_EL2_PS_36BITS: 34847e07394SAndrew Turner vmm_max_ipa_bits = 36; 34947e07394SAndrew Turner break; 35047e07394SAndrew Turner case TCR_EL2_PS_40BITS: 35147e07394SAndrew Turner vmm_max_ipa_bits = 40; 35247e07394SAndrew Turner break; 35347e07394SAndrew Turner case TCR_EL2_PS_42BITS: 35447e07394SAndrew Turner vmm_max_ipa_bits = 42; 35547e07394SAndrew Turner break; 35647e07394SAndrew Turner case TCR_EL2_PS_44BITS: 35747e07394SAndrew Turner vmm_max_ipa_bits = 44; 35847e07394SAndrew Turner break; 35947e07394SAndrew Turner case TCR_EL2_PS_48BITS: 36047e07394SAndrew Turner vmm_max_ipa_bits = 48; 36147e07394SAndrew Turner break; 36247e07394SAndrew Turner case TCR_EL2_PS_52BITS: 36347e07394SAndrew Turner default: 36447e07394SAndrew Turner vmm_max_ipa_bits = 52; 36547e07394SAndrew Turner break; 36647e07394SAndrew Turner } 36747e07394SAndrew Turner 36847e07394SAndrew Turner /* 36947e07394SAndrew Turner * Configure the Stage 2 translation control register: 37047e07394SAndrew Turner * 37147e07394SAndrew Turner * VTCR_IRGN0_WBWA: Translation table walks access inner cacheable 37247e07394SAndrew Turner * normal memory 37347e07394SAndrew Turner * VTCR_ORGN0_WBWA: Translation table walks access outer cacheable 37447e07394SAndrew Turner * normal memory 37547e07394SAndrew Turner * VTCR_EL2_TG0_4K/16K: Stage 2 uses the same page size as the kernel 37647e07394SAndrew Turner * VTCR_EL2_SL0_4K_LVL1: Stage 2 uses concatenated level 1 tables 37747e07394SAndrew Turner * VTCR_EL2_SH0_IS: Memory associated with Stage 2 walks is inner 37847e07394SAndrew Turner * shareable 37947e07394SAndrew Turner */ 38047e07394SAndrew Turner el2_regs.vtcr_el2 = VTCR_EL2_RES1; 38147e07394SAndrew Turner el2_regs.vtcr_el2 |= 38247e07394SAndrew Turner min(pa_range_bits << VTCR_EL2_PS_SHIFT, VTCR_EL2_PS_48BIT); 38347e07394SAndrew Turner el2_regs.vtcr_el2 |= VTCR_EL2_IRGN0_WBWA | VTCR_EL2_ORGN0_WBWA; 38447e07394SAndrew Turner el2_regs.vtcr_el2 |= VTCR_EL2_T0SZ(64 - vmm_virt_bits); 38547e07394SAndrew Turner el2_regs.vtcr_el2 |= vmm_vtcr_el2_sl(vmm_pmap_levels); 38647e07394SAndrew Turner #if PAGE_SIZE == PAGE_SIZE_4K 38747e07394SAndrew Turner el2_regs.vtcr_el2 |= VTCR_EL2_TG0_4K; 38847e07394SAndrew Turner #elif PAGE_SIZE == PAGE_SIZE_16K 38947e07394SAndrew Turner el2_regs.vtcr_el2 |= VTCR_EL2_TG0_16K; 39047e07394SAndrew Turner #else 39147e07394SAndrew Turner #error Unsupported page size 39247e07394SAndrew Turner #endif 39347e07394SAndrew Turner #ifdef SMP 39447e07394SAndrew Turner el2_regs.vtcr_el2 |= VTCR_EL2_SH0_IS; 39547e07394SAndrew Turner #endif 39647e07394SAndrew Turner 39747e07394SAndrew Turner smp_rendezvous(NULL, arm_setup_vectors, NULL, &el2_regs); 39847e07394SAndrew Turner 39947e07394SAndrew Turner /* Add memory to the vmem allocator (checking there is space) */ 40047e07394SAndrew Turner if (vmm_base > (L2_SIZE + PAGE_SIZE)) { 40147e07394SAndrew Turner /* 40247e07394SAndrew Turner * Ensure there is an L2 block before the vmm code to check 40347e07394SAndrew Turner * for buffer overflows on earlier data. Include the PAGE_SIZE 40447e07394SAndrew Turner * of the minimum we can allocate. 40547e07394SAndrew Turner */ 40647e07394SAndrew Turner vmm_base -= L2_SIZE + PAGE_SIZE; 40747e07394SAndrew Turner vmm_base = rounddown2(vmm_base, L2_SIZE); 40847e07394SAndrew Turner 40947e07394SAndrew Turner /* 41047e07394SAndrew Turner * Check there is memory before the vmm code to add. 41147e07394SAndrew Turner * 41247e07394SAndrew Turner * Reserve the L2 block at address 0 so NULL dereference will 41347e07394SAndrew Turner * raise an exception. 41447e07394SAndrew Turner */ 41547e07394SAndrew Turner if (vmm_base > L2_SIZE) 41647e07394SAndrew Turner vmem_add(el2_mem_alloc, L2_SIZE, vmm_base - L2_SIZE, 41747e07394SAndrew Turner M_WAITOK); 41847e07394SAndrew Turner } 41947e07394SAndrew Turner 42047e07394SAndrew Turner /* 42147e07394SAndrew Turner * Add the memory after the stacks. There is most of an L2 block 42247e07394SAndrew Turner * between the last stack and the first allocation so this should 42347e07394SAndrew Turner * be safe without adding more padding. 42447e07394SAndrew Turner */ 42547e07394SAndrew Turner if (next_hyp_va < HYP_VM_MAX_ADDRESS - PAGE_SIZE) 42647e07394SAndrew Turner vmem_add(el2_mem_alloc, next_hyp_va, 42747e07394SAndrew Turner HYP_VM_MAX_ADDRESS - next_hyp_va, M_WAITOK); 42847e07394SAndrew Turner 429*55aa3148SAndrew Turner cnthctl_el2 = vmm_read_reg(HYP_REG_CNTHCTL); 43047e07394SAndrew Turner 43147e07394SAndrew Turner vgic_init(); 43247e07394SAndrew Turner vtimer_init(cnthctl_el2); 43347e07394SAndrew Turner 43447e07394SAndrew Turner return (0); 43547e07394SAndrew Turner } 43647e07394SAndrew Turner 43747e07394SAndrew Turner int 43847e07394SAndrew Turner vmmops_modcleanup(void) 43947e07394SAndrew Turner { 44047e07394SAndrew Turner int cpu; 44147e07394SAndrew Turner 44247e07394SAndrew Turner smp_rendezvous(NULL, arm_teardown_vectors, NULL, NULL); 44347e07394SAndrew Turner 44447e07394SAndrew Turner CPU_FOREACH(cpu) { 44547e07394SAndrew Turner vmmpmap_remove(stack_hyp_va[cpu], VMM_STACK_PAGES * PAGE_SIZE, 44647e07394SAndrew Turner false); 44747e07394SAndrew Turner } 44847e07394SAndrew Turner 44947e07394SAndrew Turner vmmpmap_remove(hyp_code_base, hyp_code_len, false); 45047e07394SAndrew Turner 45147e07394SAndrew Turner vtimer_cleanup(); 45247e07394SAndrew Turner 45347e07394SAndrew Turner vmmpmap_fini(); 45447e07394SAndrew Turner 45547e07394SAndrew Turner CPU_FOREACH(cpu) 45647e07394SAndrew Turner free(stack[cpu], M_HYP); 45747e07394SAndrew Turner 45847e07394SAndrew Turner pmap_clean_stage2_tlbi = NULL; 45947e07394SAndrew Turner pmap_stage2_invalidate_range = NULL; 46047e07394SAndrew Turner pmap_stage2_invalidate_all = NULL; 46147e07394SAndrew Turner 46247e07394SAndrew Turner return (0); 46347e07394SAndrew Turner } 46447e07394SAndrew Turner 46547e07394SAndrew Turner static vm_size_t 46647e07394SAndrew Turner el2_hyp_size(struct vm *vm) 46747e07394SAndrew Turner { 46847e07394SAndrew Turner return (round_page(sizeof(struct hyp) + 46947e07394SAndrew Turner sizeof(struct hypctx *) * vm_get_maxcpus(vm))); 47047e07394SAndrew Turner } 47147e07394SAndrew Turner 47247e07394SAndrew Turner static vm_size_t 47347e07394SAndrew Turner el2_hypctx_size(void) 47447e07394SAndrew Turner { 47547e07394SAndrew Turner return (round_page(sizeof(struct hypctx))); 47647e07394SAndrew Turner } 47747e07394SAndrew Turner 47847e07394SAndrew Turner static vm_offset_t 47947e07394SAndrew Turner el2_map_enter(vm_offset_t data, vm_size_t size, vm_prot_t prot) 48047e07394SAndrew Turner { 48147e07394SAndrew Turner vmem_addr_t addr; 48247e07394SAndrew Turner int err __diagused; 48347e07394SAndrew Turner bool rv __diagused; 48447e07394SAndrew Turner 48547e07394SAndrew Turner err = vmem_alloc(el2_mem_alloc, size, M_NEXTFIT | M_WAITOK, &addr); 48647e07394SAndrew Turner MPASS(err == 0); 48747e07394SAndrew Turner rv = vmmpmap_enter(addr, size, vtophys(data), prot); 48847e07394SAndrew Turner MPASS(rv); 48947e07394SAndrew Turner 49047e07394SAndrew Turner return (addr); 49147e07394SAndrew Turner } 49247e07394SAndrew Turner 49347e07394SAndrew Turner void * 49447e07394SAndrew Turner vmmops_init(struct vm *vm, pmap_t pmap) 49547e07394SAndrew Turner { 49647e07394SAndrew Turner struct hyp *hyp; 49747e07394SAndrew Turner vm_size_t size; 49847e07394SAndrew Turner 49947e07394SAndrew Turner size = el2_hyp_size(vm); 50047e07394SAndrew Turner hyp = malloc_aligned(size, PAGE_SIZE, M_HYP, M_WAITOK | M_ZERO); 50147e07394SAndrew Turner 50247e07394SAndrew Turner hyp->vm = vm; 50347e07394SAndrew Turner hyp->vgic_attached = false; 50447e07394SAndrew Turner 50547e07394SAndrew Turner vtimer_vminit(hyp); 50647e07394SAndrew Turner vgic_vminit(hyp); 50747e07394SAndrew Turner 50847e07394SAndrew Turner hyp->el2_addr = el2_map_enter((vm_offset_t)hyp, size, 50947e07394SAndrew Turner VM_PROT_READ | VM_PROT_WRITE); 51047e07394SAndrew Turner 51147e07394SAndrew Turner return (hyp); 51247e07394SAndrew Turner } 51347e07394SAndrew Turner 51447e07394SAndrew Turner void * 51547e07394SAndrew Turner vmmops_vcpu_init(void *vmi, struct vcpu *vcpu1, int vcpuid) 51647e07394SAndrew Turner { 51747e07394SAndrew Turner struct hyp *hyp = vmi; 51847e07394SAndrew Turner struct hypctx *hypctx; 51947e07394SAndrew Turner vm_size_t size; 52047e07394SAndrew Turner 52147e07394SAndrew Turner size = el2_hypctx_size(); 52247e07394SAndrew Turner hypctx = malloc_aligned(size, PAGE_SIZE, M_HYP, M_WAITOK | M_ZERO); 52347e07394SAndrew Turner 52447e07394SAndrew Turner KASSERT(vcpuid >= 0 && vcpuid < vm_get_maxcpus(hyp->vm), 52547e07394SAndrew Turner ("%s: Invalid vcpuid %d", __func__, vcpuid)); 52647e07394SAndrew Turner hyp->ctx[vcpuid] = hypctx; 52747e07394SAndrew Turner 52847e07394SAndrew Turner hypctx->hyp = hyp; 52947e07394SAndrew Turner hypctx->vcpu = vcpu1; 53047e07394SAndrew Turner 53147e07394SAndrew Turner reset_vm_el01_regs(hypctx); 53247e07394SAndrew Turner reset_vm_el2_regs(hypctx); 53347e07394SAndrew Turner 53447e07394SAndrew Turner vtimer_cpuinit(hypctx); 53547e07394SAndrew Turner vgic_cpuinit(hypctx); 53647e07394SAndrew Turner 53747e07394SAndrew Turner hypctx->el2_addr = el2_map_enter((vm_offset_t)hypctx, size, 53847e07394SAndrew Turner VM_PROT_READ | VM_PROT_WRITE); 53947e07394SAndrew Turner 54047e07394SAndrew Turner return (hypctx); 54147e07394SAndrew Turner } 54247e07394SAndrew Turner 54347e07394SAndrew Turner static int 54447e07394SAndrew Turner arm_vmm_pinit(pmap_t pmap) 54547e07394SAndrew Turner { 54647e07394SAndrew Turner 54747e07394SAndrew Turner pmap_pinit_stage(pmap, PM_STAGE2, vmm_pmap_levels); 54847e07394SAndrew Turner return (1); 54947e07394SAndrew Turner } 55047e07394SAndrew Turner 55147e07394SAndrew Turner struct vmspace * 55247e07394SAndrew Turner vmmops_vmspace_alloc(vm_offset_t min, vm_offset_t max) 55347e07394SAndrew Turner { 55447e07394SAndrew Turner return (vmspace_alloc(min, max, arm_vmm_pinit)); 55547e07394SAndrew Turner } 55647e07394SAndrew Turner 55747e07394SAndrew Turner void 55847e07394SAndrew Turner vmmops_vmspace_free(struct vmspace *vmspace) 55947e07394SAndrew Turner { 56047e07394SAndrew Turner 56147e07394SAndrew Turner pmap_remove_pages(vmspace_pmap(vmspace)); 56247e07394SAndrew Turner vmspace_free(vmspace); 56347e07394SAndrew Turner } 56447e07394SAndrew Turner 56547e07394SAndrew Turner static inline void 56647e07394SAndrew Turner arm64_print_hyp_regs(struct vm_exit *vme) 56747e07394SAndrew Turner { 56847e07394SAndrew Turner printf("esr_el2: 0x%016lx\n", vme->u.hyp.esr_el2); 56947e07394SAndrew Turner printf("far_el2: 0x%016lx\n", vme->u.hyp.far_el2); 57047e07394SAndrew Turner printf("hpfar_el2: 0x%016lx\n", vme->u.hyp.hpfar_el2); 57147e07394SAndrew Turner printf("elr_el2: 0x%016lx\n", vme->pc); 57247e07394SAndrew Turner } 57347e07394SAndrew Turner 57447e07394SAndrew Turner static void 57547e07394SAndrew Turner arm64_gen_inst_emul_data(struct hypctx *hypctx, uint32_t esr_iss, 57647e07394SAndrew Turner struct vm_exit *vme_ret) 57747e07394SAndrew Turner { 57847e07394SAndrew Turner struct vm_guest_paging *paging; 57947e07394SAndrew Turner struct vie *vie; 58047e07394SAndrew Turner uint32_t esr_sas, reg_num; 58147e07394SAndrew Turner 58247e07394SAndrew Turner /* 58347e07394SAndrew Turner * Get the page address from HPFAR_EL2. 58447e07394SAndrew Turner */ 58547e07394SAndrew Turner vme_ret->u.inst_emul.gpa = 58647e07394SAndrew Turner HPFAR_EL2_FIPA_ADDR(hypctx->exit_info.hpfar_el2); 58747e07394SAndrew Turner /* Bits [11:0] are the same as bits [11:0] from the virtual address. */ 58847e07394SAndrew Turner vme_ret->u.inst_emul.gpa += hypctx->exit_info.far_el2 & 58947e07394SAndrew Turner FAR_EL2_HPFAR_PAGE_MASK; 59047e07394SAndrew Turner 59147e07394SAndrew Turner esr_sas = (esr_iss & ISS_DATA_SAS_MASK) >> ISS_DATA_SAS_SHIFT; 59247e07394SAndrew Turner reg_num = (esr_iss & ISS_DATA_SRT_MASK) >> ISS_DATA_SRT_SHIFT; 59347e07394SAndrew Turner 59447e07394SAndrew Turner vie = &vme_ret->u.inst_emul.vie; 59547e07394SAndrew Turner vie->access_size = 1 << esr_sas; 59647e07394SAndrew Turner vie->sign_extend = (esr_iss & ISS_DATA_SSE) ? 1 : 0; 59747e07394SAndrew Turner vie->dir = (esr_iss & ISS_DATA_WnR) ? VM_DIR_WRITE : VM_DIR_READ; 59847e07394SAndrew Turner vie->reg = reg_num; 59947e07394SAndrew Turner 60047e07394SAndrew Turner paging = &vme_ret->u.inst_emul.paging; 60147e07394SAndrew Turner paging->ttbr0_addr = hypctx->ttbr0_el1 & ~(TTBR_ASID_MASK | TTBR_CnP); 60247e07394SAndrew Turner paging->ttbr1_addr = hypctx->ttbr1_el1 & ~(TTBR_ASID_MASK | TTBR_CnP); 60347e07394SAndrew Turner paging->tcr_el1 = hypctx->tcr_el1; 60447e07394SAndrew Turner paging->tcr2_el1 = hypctx->tcr2_el1; 60547e07394SAndrew Turner paging->flags = hypctx->tf.tf_spsr & (PSR_M_MASK | PSR_M_32); 60647e07394SAndrew Turner if ((hypctx->sctlr_el1 & SCTLR_M) != 0) 60747e07394SAndrew Turner paging->flags |= VM_GP_MMU_ENABLED; 60847e07394SAndrew Turner } 60947e07394SAndrew Turner 61047e07394SAndrew Turner static void 61147e07394SAndrew Turner arm64_gen_reg_emul_data(uint32_t esr_iss, struct vm_exit *vme_ret) 61247e07394SAndrew Turner { 61347e07394SAndrew Turner uint32_t reg_num; 61447e07394SAndrew Turner struct vre *vre; 61547e07394SAndrew Turner 61647e07394SAndrew Turner /* u.hyp member will be replaced by u.reg_emul */ 61747e07394SAndrew Turner vre = &vme_ret->u.reg_emul.vre; 61847e07394SAndrew Turner 61947e07394SAndrew Turner vre->inst_syndrome = esr_iss; 62047e07394SAndrew Turner /* ARMv8 Architecture Manual, p. D7-2273: 1 means read */ 62147e07394SAndrew Turner vre->dir = (esr_iss & ISS_MSR_DIR) ? VM_DIR_READ : VM_DIR_WRITE; 62247e07394SAndrew Turner reg_num = ISS_MSR_Rt(esr_iss); 62347e07394SAndrew Turner vre->reg = reg_num; 62447e07394SAndrew Turner } 62547e07394SAndrew Turner 62647e07394SAndrew Turner void 62747e07394SAndrew Turner raise_data_insn_abort(struct hypctx *hypctx, uint64_t far, bool dabort, int fsc) 62847e07394SAndrew Turner { 62947e07394SAndrew Turner uint64_t esr; 63047e07394SAndrew Turner 63147e07394SAndrew Turner if ((hypctx->tf.tf_spsr & PSR_M_MASK) == PSR_M_EL0t) 63247e07394SAndrew Turner esr = EXCP_INSN_ABORT_L << ESR_ELx_EC_SHIFT; 63347e07394SAndrew Turner else 63447e07394SAndrew Turner esr = EXCP_INSN_ABORT << ESR_ELx_EC_SHIFT; 63547e07394SAndrew Turner /* Set the bit that changes from insn -> data abort */ 63647e07394SAndrew Turner if (dabort) 63747e07394SAndrew Turner esr |= EXCP_DATA_ABORT_L << ESR_ELx_EC_SHIFT; 63847e07394SAndrew Turner /* Set the IL bit if set by hardware */ 63947e07394SAndrew Turner esr |= hypctx->tf.tf_esr & ESR_ELx_IL; 64047e07394SAndrew Turner 64147e07394SAndrew Turner vmmops_exception(hypctx, esr | fsc, far); 64247e07394SAndrew Turner } 64347e07394SAndrew Turner 64447e07394SAndrew Turner static int 64547e07394SAndrew Turner handle_el1_sync_excp(struct hypctx *hypctx, struct vm_exit *vme_ret, 64647e07394SAndrew Turner pmap_t pmap) 64747e07394SAndrew Turner { 64847e07394SAndrew Turner uint64_t gpa; 64947e07394SAndrew Turner uint32_t esr_ec, esr_iss; 65047e07394SAndrew Turner 65147e07394SAndrew Turner esr_ec = ESR_ELx_EXCEPTION(hypctx->tf.tf_esr); 65247e07394SAndrew Turner esr_iss = hypctx->tf.tf_esr & ESR_ELx_ISS_MASK; 65347e07394SAndrew Turner 65447e07394SAndrew Turner switch (esr_ec) { 65547e07394SAndrew Turner case EXCP_UNKNOWN: 65647e07394SAndrew Turner vmm_stat_incr(hypctx->vcpu, VMEXIT_UNKNOWN, 1); 65747e07394SAndrew Turner arm64_print_hyp_regs(vme_ret); 65847e07394SAndrew Turner vme_ret->exitcode = VM_EXITCODE_HYP; 65947e07394SAndrew Turner break; 66047e07394SAndrew Turner case EXCP_TRAP_WFI_WFE: 66147e07394SAndrew Turner if ((hypctx->tf.tf_esr & 0x3) == 0) { /* WFI */ 66247e07394SAndrew Turner vmm_stat_incr(hypctx->vcpu, VMEXIT_WFI, 1); 66347e07394SAndrew Turner vme_ret->exitcode = VM_EXITCODE_WFI; 66447e07394SAndrew Turner } else { 66547e07394SAndrew Turner vmm_stat_incr(hypctx->vcpu, VMEXIT_WFE, 1); 66647e07394SAndrew Turner vme_ret->exitcode = VM_EXITCODE_HYP; 66747e07394SAndrew Turner } 66847e07394SAndrew Turner break; 66947e07394SAndrew Turner case EXCP_HVC: 67047e07394SAndrew Turner vmm_stat_incr(hypctx->vcpu, VMEXIT_HVC, 1); 67147e07394SAndrew Turner vme_ret->exitcode = VM_EXITCODE_HVC; 67247e07394SAndrew Turner break; 67347e07394SAndrew Turner case EXCP_MSR: 67447e07394SAndrew Turner vmm_stat_incr(hypctx->vcpu, VMEXIT_MSR, 1); 67547e07394SAndrew Turner arm64_gen_reg_emul_data(esr_iss, vme_ret); 67647e07394SAndrew Turner vme_ret->exitcode = VM_EXITCODE_REG_EMUL; 67747e07394SAndrew Turner break; 67875cb9492SMark Johnston case EXCP_BRK: 67975cb9492SMark Johnston vmm_stat_incr(hypctx->vcpu, VMEXIT_BRK, 1); 68075cb9492SMark Johnston vme_ret->exitcode = VM_EXITCODE_BRK; 68175cb9492SMark Johnston break; 68275cb9492SMark Johnston case EXCP_SOFTSTP_EL0: 68375cb9492SMark Johnston vmm_stat_incr(hypctx->vcpu, VMEXIT_SS, 1); 68475cb9492SMark Johnston vme_ret->exitcode = VM_EXITCODE_SS; 68575cb9492SMark Johnston break; 68647e07394SAndrew Turner case EXCP_INSN_ABORT_L: 68747e07394SAndrew Turner case EXCP_DATA_ABORT_L: 68847e07394SAndrew Turner vmm_stat_incr(hypctx->vcpu, esr_ec == EXCP_DATA_ABORT_L ? 68947e07394SAndrew Turner VMEXIT_DATA_ABORT : VMEXIT_INSN_ABORT, 1); 69047e07394SAndrew Turner switch (hypctx->tf.tf_esr & ISS_DATA_DFSC_MASK) { 69147e07394SAndrew Turner case ISS_DATA_DFSC_TF_L0: 69247e07394SAndrew Turner case ISS_DATA_DFSC_TF_L1: 69347e07394SAndrew Turner case ISS_DATA_DFSC_TF_L2: 69447e07394SAndrew Turner case ISS_DATA_DFSC_TF_L3: 69547e07394SAndrew Turner case ISS_DATA_DFSC_AFF_L1: 69647e07394SAndrew Turner case ISS_DATA_DFSC_AFF_L2: 69747e07394SAndrew Turner case ISS_DATA_DFSC_AFF_L3: 69847e07394SAndrew Turner case ISS_DATA_DFSC_PF_L1: 69947e07394SAndrew Turner case ISS_DATA_DFSC_PF_L2: 70047e07394SAndrew Turner case ISS_DATA_DFSC_PF_L3: 70147e07394SAndrew Turner gpa = HPFAR_EL2_FIPA_ADDR(hypctx->exit_info.hpfar_el2); 70247e07394SAndrew Turner /* Check the IPA is valid */ 70347e07394SAndrew Turner if (gpa >= (1ul << vmm_max_ipa_bits)) { 70447e07394SAndrew Turner raise_data_insn_abort(hypctx, 70547e07394SAndrew Turner hypctx->exit_info.far_el2, 70647e07394SAndrew Turner esr_ec == EXCP_DATA_ABORT_L, 70747e07394SAndrew Turner ISS_DATA_DFSC_ASF_L0); 70847e07394SAndrew Turner vme_ret->inst_length = 0; 70947e07394SAndrew Turner return (HANDLED); 71047e07394SAndrew Turner } 71147e07394SAndrew Turner 71247e07394SAndrew Turner if (vm_mem_allocated(hypctx->vcpu, gpa)) { 71347e07394SAndrew Turner vme_ret->exitcode = VM_EXITCODE_PAGING; 71447e07394SAndrew Turner vme_ret->inst_length = 0; 71547e07394SAndrew Turner vme_ret->u.paging.esr = hypctx->tf.tf_esr; 71647e07394SAndrew Turner vme_ret->u.paging.gpa = gpa; 71747e07394SAndrew Turner } else if (esr_ec == EXCP_INSN_ABORT_L) { 71847e07394SAndrew Turner /* 71947e07394SAndrew Turner * Raise an external abort. Device memory is 72047e07394SAndrew Turner * not executable 72147e07394SAndrew Turner */ 72247e07394SAndrew Turner raise_data_insn_abort(hypctx, 72347e07394SAndrew Turner hypctx->exit_info.far_el2, false, 72447e07394SAndrew Turner ISS_DATA_DFSC_EXT); 72547e07394SAndrew Turner vme_ret->inst_length = 0; 72647e07394SAndrew Turner return (HANDLED); 72747e07394SAndrew Turner } else { 72847e07394SAndrew Turner arm64_gen_inst_emul_data(hypctx, esr_iss, 72947e07394SAndrew Turner vme_ret); 73047e07394SAndrew Turner vme_ret->exitcode = VM_EXITCODE_INST_EMUL; 73147e07394SAndrew Turner } 73247e07394SAndrew Turner break; 73347e07394SAndrew Turner default: 73447e07394SAndrew Turner arm64_print_hyp_regs(vme_ret); 73547e07394SAndrew Turner vme_ret->exitcode = VM_EXITCODE_HYP; 73647e07394SAndrew Turner break; 73747e07394SAndrew Turner } 73847e07394SAndrew Turner 73947e07394SAndrew Turner break; 74047e07394SAndrew Turner 74147e07394SAndrew Turner default: 74247e07394SAndrew Turner vmm_stat_incr(hypctx->vcpu, VMEXIT_UNHANDLED_SYNC, 1); 74347e07394SAndrew Turner arm64_print_hyp_regs(vme_ret); 74447e07394SAndrew Turner vme_ret->exitcode = VM_EXITCODE_HYP; 74547e07394SAndrew Turner break; 74647e07394SAndrew Turner } 74747e07394SAndrew Turner 74847e07394SAndrew Turner /* We don't don't do any instruction emulation here */ 74947e07394SAndrew Turner return (UNHANDLED); 75047e07394SAndrew Turner } 75147e07394SAndrew Turner 75247e07394SAndrew Turner static int 75347e07394SAndrew Turner arm64_handle_world_switch(struct hypctx *hypctx, int excp_type, 75447e07394SAndrew Turner struct vm_exit *vme, pmap_t pmap) 75547e07394SAndrew Turner { 75647e07394SAndrew Turner int handled; 75747e07394SAndrew Turner 75847e07394SAndrew Turner switch (excp_type) { 75947e07394SAndrew Turner case EXCP_TYPE_EL1_SYNC: 76047e07394SAndrew Turner /* The exit code will be set by handle_el1_sync_excp(). */ 76147e07394SAndrew Turner handled = handle_el1_sync_excp(hypctx, vme, pmap); 76247e07394SAndrew Turner break; 76347e07394SAndrew Turner 76447e07394SAndrew Turner case EXCP_TYPE_EL1_IRQ: 76547e07394SAndrew Turner case EXCP_TYPE_EL1_FIQ: 76647e07394SAndrew Turner /* The host kernel will handle IRQs and FIQs. */ 76747e07394SAndrew Turner vmm_stat_incr(hypctx->vcpu, 76847e07394SAndrew Turner excp_type == EXCP_TYPE_EL1_IRQ ? VMEXIT_IRQ : VMEXIT_FIQ,1); 76947e07394SAndrew Turner vme->exitcode = VM_EXITCODE_BOGUS; 77047e07394SAndrew Turner handled = UNHANDLED; 77147e07394SAndrew Turner break; 77247e07394SAndrew Turner 77347e07394SAndrew Turner case EXCP_TYPE_EL1_ERROR: 77447e07394SAndrew Turner case EXCP_TYPE_EL2_SYNC: 77547e07394SAndrew Turner case EXCP_TYPE_EL2_IRQ: 77647e07394SAndrew Turner case EXCP_TYPE_EL2_FIQ: 77747e07394SAndrew Turner case EXCP_TYPE_EL2_ERROR: 77847e07394SAndrew Turner vmm_stat_incr(hypctx->vcpu, VMEXIT_UNHANDLED_EL2, 1); 77947e07394SAndrew Turner vme->exitcode = VM_EXITCODE_BOGUS; 78047e07394SAndrew Turner handled = UNHANDLED; 78147e07394SAndrew Turner break; 78247e07394SAndrew Turner 78347e07394SAndrew Turner default: 78447e07394SAndrew Turner vmm_stat_incr(hypctx->vcpu, VMEXIT_UNHANDLED, 1); 78547e07394SAndrew Turner vme->exitcode = VM_EXITCODE_BOGUS; 78647e07394SAndrew Turner handled = UNHANDLED; 78747e07394SAndrew Turner break; 78847e07394SAndrew Turner } 78947e07394SAndrew Turner 79047e07394SAndrew Turner return (handled); 79147e07394SAndrew Turner } 79247e07394SAndrew Turner 79347e07394SAndrew Turner static void 79447e07394SAndrew Turner ptp_release(void **cookie) 79547e07394SAndrew Turner { 79647e07394SAndrew Turner if (*cookie != NULL) { 79747e07394SAndrew Turner vm_gpa_release(*cookie); 79847e07394SAndrew Turner *cookie = NULL; 79947e07394SAndrew Turner } 80047e07394SAndrew Turner } 80147e07394SAndrew Turner 80247e07394SAndrew Turner static void * 80347e07394SAndrew Turner ptp_hold(struct vcpu *vcpu, vm_paddr_t ptpphys, size_t len, void **cookie) 80447e07394SAndrew Turner { 80547e07394SAndrew Turner void *ptr; 80647e07394SAndrew Turner 80747e07394SAndrew Turner ptp_release(cookie); 80847e07394SAndrew Turner ptr = vm_gpa_hold(vcpu, ptpphys, len, VM_PROT_RW, cookie); 80947e07394SAndrew Turner return (ptr); 81047e07394SAndrew Turner } 81147e07394SAndrew Turner 81247e07394SAndrew Turner /* log2 of the number of bytes in a page table entry */ 81347e07394SAndrew Turner #define PTE_SHIFT 3 81447e07394SAndrew Turner int 81547e07394SAndrew Turner vmmops_gla2gpa(void *vcpui, struct vm_guest_paging *paging, uint64_t gla, 81647e07394SAndrew Turner int prot, uint64_t *gpa, int *is_fault) 81747e07394SAndrew Turner { 81847e07394SAndrew Turner struct hypctx *hypctx; 81947e07394SAndrew Turner void *cookie; 82047e07394SAndrew Turner uint64_t mask, *ptep, pte, pte_addr; 82147e07394SAndrew Turner int address_bits, granule_shift, ia_bits, levels, pte_shift, tsz; 82247e07394SAndrew Turner bool is_el0; 82347e07394SAndrew Turner 82447e07394SAndrew Turner /* Check if the MMU is off */ 82547e07394SAndrew Turner if ((paging->flags & VM_GP_MMU_ENABLED) == 0) { 82647e07394SAndrew Turner *is_fault = 0; 82747e07394SAndrew Turner *gpa = gla; 82847e07394SAndrew Turner return (0); 82947e07394SAndrew Turner } 83047e07394SAndrew Turner 83147e07394SAndrew Turner is_el0 = (paging->flags & PSR_M_MASK) == PSR_M_EL0t; 83247e07394SAndrew Turner 83347e07394SAndrew Turner if (ADDR_IS_KERNEL(gla)) { 83447e07394SAndrew Turner /* If address translation is disabled raise an exception */ 83547e07394SAndrew Turner if ((paging->tcr_el1 & TCR_EPD1) != 0) { 83647e07394SAndrew Turner *is_fault = 1; 83747e07394SAndrew Turner return (0); 83847e07394SAndrew Turner } 83947e07394SAndrew Turner if (is_el0 && (paging->tcr_el1 & TCR_E0PD1) != 0) { 84047e07394SAndrew Turner *is_fault = 1; 84147e07394SAndrew Turner return (0); 84247e07394SAndrew Turner } 84347e07394SAndrew Turner pte_addr = paging->ttbr1_addr; 84447e07394SAndrew Turner tsz = (paging->tcr_el1 & TCR_T1SZ_MASK) >> TCR_T1SZ_SHIFT; 84547e07394SAndrew Turner /* Clear the top byte if TBI is on */ 84647e07394SAndrew Turner if ((paging->tcr_el1 & TCR_TBI1) != 0) 84747e07394SAndrew Turner gla |= (0xfful << 56); 84847e07394SAndrew Turner switch (paging->tcr_el1 & TCR_TG1_MASK) { 84947e07394SAndrew Turner case TCR_TG1_4K: 85047e07394SAndrew Turner granule_shift = PAGE_SHIFT_4K; 85147e07394SAndrew Turner break; 85247e07394SAndrew Turner case TCR_TG1_16K: 85347e07394SAndrew Turner granule_shift = PAGE_SHIFT_16K; 85447e07394SAndrew Turner break; 85547e07394SAndrew Turner case TCR_TG1_64K: 85647e07394SAndrew Turner granule_shift = PAGE_SHIFT_64K; 85747e07394SAndrew Turner break; 85847e07394SAndrew Turner default: 85947e07394SAndrew Turner *is_fault = 1; 86047e07394SAndrew Turner return (EINVAL); 86147e07394SAndrew Turner } 86247e07394SAndrew Turner } else { 86347e07394SAndrew Turner /* If address translation is disabled raise an exception */ 86447e07394SAndrew Turner if ((paging->tcr_el1 & TCR_EPD0) != 0) { 86547e07394SAndrew Turner *is_fault = 1; 86647e07394SAndrew Turner return (0); 86747e07394SAndrew Turner } 86847e07394SAndrew Turner if (is_el0 && (paging->tcr_el1 & TCR_E0PD0) != 0) { 86947e07394SAndrew Turner *is_fault = 1; 87047e07394SAndrew Turner return (0); 87147e07394SAndrew Turner } 87247e07394SAndrew Turner pte_addr = paging->ttbr0_addr; 87347e07394SAndrew Turner tsz = (paging->tcr_el1 & TCR_T0SZ_MASK) >> TCR_T0SZ_SHIFT; 87447e07394SAndrew Turner /* Clear the top byte if TBI is on */ 87547e07394SAndrew Turner if ((paging->tcr_el1 & TCR_TBI0) != 0) 87647e07394SAndrew Turner gla &= ~(0xfful << 56); 87747e07394SAndrew Turner switch (paging->tcr_el1 & TCR_TG0_MASK) { 87847e07394SAndrew Turner case TCR_TG0_4K: 87947e07394SAndrew Turner granule_shift = PAGE_SHIFT_4K; 88047e07394SAndrew Turner break; 88147e07394SAndrew Turner case TCR_TG0_16K: 88247e07394SAndrew Turner granule_shift = PAGE_SHIFT_16K; 88347e07394SAndrew Turner break; 88447e07394SAndrew Turner case TCR_TG0_64K: 88547e07394SAndrew Turner granule_shift = PAGE_SHIFT_64K; 88647e07394SAndrew Turner break; 88747e07394SAndrew Turner default: 88847e07394SAndrew Turner *is_fault = 1; 88947e07394SAndrew Turner return (EINVAL); 89047e07394SAndrew Turner } 89147e07394SAndrew Turner } 89247e07394SAndrew Turner 89347e07394SAndrew Turner /* 89447e07394SAndrew Turner * TODO: Support FEAT_TTST for smaller tsz values and FEAT_LPA2 89547e07394SAndrew Turner * for larger values. 89647e07394SAndrew Turner */ 89747e07394SAndrew Turner switch (granule_shift) { 89847e07394SAndrew Turner case PAGE_SHIFT_4K: 89947e07394SAndrew Turner case PAGE_SHIFT_16K: 90047e07394SAndrew Turner /* 90147e07394SAndrew Turner * See "Table D8-11 4KB granule, determining stage 1 initial 90247e07394SAndrew Turner * lookup level" and "Table D8-21 16KB granule, determining 90347e07394SAndrew Turner * stage 1 initial lookup level" from the "Arm Architecture 90447e07394SAndrew Turner * Reference Manual for A-Profile architecture" revision I.a 90547e07394SAndrew Turner * for the minimum and maximum values. 90647e07394SAndrew Turner * 90747e07394SAndrew Turner * TODO: Support less than 16 when FEAT_LPA2 is implemented 90847e07394SAndrew Turner * and TCR_EL1.DS == 1 90947e07394SAndrew Turner * TODO: Support more than 39 when FEAT_TTST is implemented 91047e07394SAndrew Turner */ 91147e07394SAndrew Turner if (tsz < 16 || tsz > 39) { 91247e07394SAndrew Turner *is_fault = 1; 91347e07394SAndrew Turner return (EINVAL); 91447e07394SAndrew Turner } 91547e07394SAndrew Turner break; 91647e07394SAndrew Turner case PAGE_SHIFT_64K: 91747e07394SAndrew Turner /* TODO: Support 64k granule. It will probably work, but is untested */ 91847e07394SAndrew Turner default: 91947e07394SAndrew Turner *is_fault = 1; 92047e07394SAndrew Turner return (EINVAL); 92147e07394SAndrew Turner } 92247e07394SAndrew Turner 92347e07394SAndrew Turner /* 92447e07394SAndrew Turner * Calculate the input address bits. These are 64 bit in an address 92547e07394SAndrew Turner * with the top tsz bits being all 0 or all 1. 92647e07394SAndrew Turner */ 92747e07394SAndrew Turner ia_bits = 64 - tsz; 92847e07394SAndrew Turner 92947e07394SAndrew Turner /* 93047e07394SAndrew Turner * Calculate the number of address bits used in the page table 93147e07394SAndrew Turner * calculation. This is ia_bits minus the bottom granule_shift 93247e07394SAndrew Turner * bits that are passed to the output address. 93347e07394SAndrew Turner */ 93447e07394SAndrew Turner address_bits = ia_bits - granule_shift; 93547e07394SAndrew Turner 93647e07394SAndrew Turner /* 93747e07394SAndrew Turner * Calculate the number of levels. Each level uses 93847e07394SAndrew Turner * granule_shift - PTE_SHIFT bits of the input address. 93947e07394SAndrew Turner * This is because the table is 1 << granule_shift and each 94047e07394SAndrew Turner * entry is 1 << PTE_SHIFT bytes. 94147e07394SAndrew Turner */ 94247e07394SAndrew Turner levels = howmany(address_bits, granule_shift - PTE_SHIFT); 94347e07394SAndrew Turner 94447e07394SAndrew Turner /* Mask of the upper unused bits in the virtual address */ 94547e07394SAndrew Turner gla &= (1ul << ia_bits) - 1; 94647e07394SAndrew Turner hypctx = (struct hypctx *)vcpui; 94747e07394SAndrew Turner cookie = NULL; 94847e07394SAndrew Turner /* TODO: Check if the level supports block descriptors */ 94947e07394SAndrew Turner for (;levels > 0; levels--) { 95047e07394SAndrew Turner int idx; 95147e07394SAndrew Turner 95247e07394SAndrew Turner pte_shift = (levels - 1) * (granule_shift - PTE_SHIFT) + 95347e07394SAndrew Turner granule_shift; 95447e07394SAndrew Turner idx = (gla >> pte_shift) & 95547e07394SAndrew Turner ((1ul << (granule_shift - PTE_SHIFT)) - 1); 95647e07394SAndrew Turner while (idx > PAGE_SIZE / sizeof(pte)) { 95747e07394SAndrew Turner idx -= PAGE_SIZE / sizeof(pte); 95847e07394SAndrew Turner pte_addr += PAGE_SIZE; 95947e07394SAndrew Turner } 96047e07394SAndrew Turner 96147e07394SAndrew Turner ptep = ptp_hold(hypctx->vcpu, pte_addr, PAGE_SIZE, &cookie); 96247e07394SAndrew Turner if (ptep == NULL) 96347e07394SAndrew Turner goto error; 96447e07394SAndrew Turner pte = ptep[idx]; 96547e07394SAndrew Turner 96647e07394SAndrew Turner /* Calculate the level we are looking at */ 96747e07394SAndrew Turner switch (levels) { 96847e07394SAndrew Turner default: 96947e07394SAndrew Turner goto fault; 97047e07394SAndrew Turner /* TODO: Level -1 when FEAT_LPA2 is implemented */ 97147e07394SAndrew Turner case 4: /* Level 0 */ 97247e07394SAndrew Turner if ((pte & ATTR_DESCR_MASK) != L0_TABLE) 97347e07394SAndrew Turner goto fault; 97447e07394SAndrew Turner /* FALLTHROUGH */ 97547e07394SAndrew Turner case 3: /* Level 1 */ 97647e07394SAndrew Turner case 2: /* Level 2 */ 97747e07394SAndrew Turner switch (pte & ATTR_DESCR_MASK) { 97847e07394SAndrew Turner /* Use L1 macro as all levels are the same */ 97947e07394SAndrew Turner case L1_TABLE: 98047e07394SAndrew Turner /* Check if EL0 can access this address space */ 98147e07394SAndrew Turner if (is_el0 && 98247e07394SAndrew Turner (pte & TATTR_AP_TABLE_NO_EL0) != 0) 98347e07394SAndrew Turner goto fault; 98447e07394SAndrew Turner /* Check if the address space is writable */ 98547e07394SAndrew Turner if ((prot & PROT_WRITE) != 0 && 98647e07394SAndrew Turner (pte & TATTR_AP_TABLE_RO) != 0) 98747e07394SAndrew Turner goto fault; 98847e07394SAndrew Turner if ((prot & PROT_EXEC) != 0) { 98947e07394SAndrew Turner /* Check the table exec attribute */ 99047e07394SAndrew Turner if ((is_el0 && 99147e07394SAndrew Turner (pte & TATTR_UXN_TABLE) != 0) || 99247e07394SAndrew Turner (!is_el0 && 99347e07394SAndrew Turner (pte & TATTR_PXN_TABLE) != 0)) 99447e07394SAndrew Turner goto fault; 99547e07394SAndrew Turner } 99647e07394SAndrew Turner pte_addr = pte & ~ATTR_MASK; 99747e07394SAndrew Turner break; 99847e07394SAndrew Turner case L1_BLOCK: 99947e07394SAndrew Turner goto done; 100047e07394SAndrew Turner default: 100147e07394SAndrew Turner goto fault; 100247e07394SAndrew Turner } 100347e07394SAndrew Turner break; 100447e07394SAndrew Turner case 1: /* Level 3 */ 100547e07394SAndrew Turner if ((pte & ATTR_DESCR_MASK) == L3_PAGE) 100647e07394SAndrew Turner goto done; 100747e07394SAndrew Turner goto fault; 100847e07394SAndrew Turner } 100947e07394SAndrew Turner } 101047e07394SAndrew Turner 101147e07394SAndrew Turner done: 101247e07394SAndrew Turner /* Check if EL0 has access to the block/page */ 101347e07394SAndrew Turner if (is_el0 && (pte & ATTR_S1_AP(ATTR_S1_AP_USER)) == 0) 101447e07394SAndrew Turner goto fault; 101547e07394SAndrew Turner if ((prot & PROT_WRITE) != 0 && (pte & ATTR_S1_AP_RW_BIT) != 0) 101647e07394SAndrew Turner goto fault; 101747e07394SAndrew Turner if ((prot & PROT_EXEC) != 0) { 101847e07394SAndrew Turner if ((is_el0 && (pte & ATTR_S1_UXN) != 0) || 101947e07394SAndrew Turner (!is_el0 && (pte & ATTR_S1_PXN) != 0)) 102047e07394SAndrew Turner goto fault; 102147e07394SAndrew Turner } 102247e07394SAndrew Turner mask = (1ul << pte_shift) - 1; 102347e07394SAndrew Turner *gpa = (pte & ~ATTR_MASK) | (gla & mask); 102447e07394SAndrew Turner *is_fault = 0; 102547e07394SAndrew Turner ptp_release(&cookie); 102647e07394SAndrew Turner return (0); 102747e07394SAndrew Turner 102847e07394SAndrew Turner error: 102947e07394SAndrew Turner ptp_release(&cookie); 103047e07394SAndrew Turner return (EFAULT); 103147e07394SAndrew Turner fault: 103247e07394SAndrew Turner *is_fault = 1; 103347e07394SAndrew Turner ptp_release(&cookie); 103447e07394SAndrew Turner return (0); 103547e07394SAndrew Turner } 103647e07394SAndrew Turner 103747e07394SAndrew Turner int 103847e07394SAndrew Turner vmmops_run(void *vcpui, register_t pc, pmap_t pmap, struct vm_eventinfo *evinfo) 103947e07394SAndrew Turner { 104047e07394SAndrew Turner uint64_t excp_type; 104147e07394SAndrew Turner int handled; 104247e07394SAndrew Turner register_t daif; 104347e07394SAndrew Turner struct hyp *hyp; 104447e07394SAndrew Turner struct hypctx *hypctx; 104547e07394SAndrew Turner struct vcpu *vcpu; 104647e07394SAndrew Turner struct vm_exit *vme; 104747e07394SAndrew Turner int mode; 104847e07394SAndrew Turner 104947e07394SAndrew Turner hypctx = (struct hypctx *)vcpui; 105047e07394SAndrew Turner hyp = hypctx->hyp; 105147e07394SAndrew Turner vcpu = hypctx->vcpu; 105247e07394SAndrew Turner vme = vm_exitinfo(vcpu); 105347e07394SAndrew Turner 105447e07394SAndrew Turner hypctx->tf.tf_elr = (uint64_t)pc; 105547e07394SAndrew Turner 105647e07394SAndrew Turner for (;;) { 105747e07394SAndrew Turner if (hypctx->has_exception) { 105847e07394SAndrew Turner hypctx->has_exception = false; 105947e07394SAndrew Turner hypctx->elr_el1 = hypctx->tf.tf_elr; 106047e07394SAndrew Turner 106147e07394SAndrew Turner mode = hypctx->tf.tf_spsr & (PSR_M_MASK | PSR_M_32); 106247e07394SAndrew Turner 106347e07394SAndrew Turner if (mode == PSR_M_EL1t) { 106447e07394SAndrew Turner hypctx->tf.tf_elr = hypctx->vbar_el1 + 0x0; 106547e07394SAndrew Turner } else if (mode == PSR_M_EL1h) { 106647e07394SAndrew Turner hypctx->tf.tf_elr = hypctx->vbar_el1 + 0x200; 106747e07394SAndrew Turner } else if ((mode & PSR_M_32) == PSR_M_64) { 106847e07394SAndrew Turner /* 64-bit EL0 */ 106947e07394SAndrew Turner hypctx->tf.tf_elr = hypctx->vbar_el1 + 0x400; 107047e07394SAndrew Turner } else { 107147e07394SAndrew Turner /* 32-bit EL0 */ 107247e07394SAndrew Turner hypctx->tf.tf_elr = hypctx->vbar_el1 + 0x600; 107347e07394SAndrew Turner } 107447e07394SAndrew Turner 107547e07394SAndrew Turner /* Set the new spsr */ 107647e07394SAndrew Turner hypctx->spsr_el1 = hypctx->tf.tf_spsr; 107747e07394SAndrew Turner 107847e07394SAndrew Turner /* Set the new cpsr */ 107947e07394SAndrew Turner hypctx->tf.tf_spsr = hypctx->spsr_el1 & PSR_FLAGS; 108047e07394SAndrew Turner hypctx->tf.tf_spsr |= PSR_DAIF | PSR_M_EL1h; 108147e07394SAndrew Turner 108247e07394SAndrew Turner /* 108347e07394SAndrew Turner * Update fields that may change on exeption entry 108447e07394SAndrew Turner * based on how sctlr_el1 is configured. 108547e07394SAndrew Turner */ 10860cdd0032SAndrew Turner if ((hypctx->sctlr_el1 & SCTLR_SPAN) == 0) 108747e07394SAndrew Turner hypctx->tf.tf_spsr |= PSR_PAN; 108847e07394SAndrew Turner if ((hypctx->sctlr_el1 & SCTLR_DSSBS) == 0) 108947e07394SAndrew Turner hypctx->tf.tf_spsr &= ~PSR_SSBS; 109047e07394SAndrew Turner else 109147e07394SAndrew Turner hypctx->tf.tf_spsr |= PSR_SSBS; 109247e07394SAndrew Turner } 109347e07394SAndrew Turner 109447e07394SAndrew Turner daif = intr_disable(); 109547e07394SAndrew Turner 109647e07394SAndrew Turner /* Check if the vcpu is suspended */ 109747e07394SAndrew Turner if (vcpu_suspended(evinfo)) { 109847e07394SAndrew Turner intr_restore(daif); 109947e07394SAndrew Turner vm_exit_suspended(vcpu, pc); 110047e07394SAndrew Turner break; 110147e07394SAndrew Turner } 110247e07394SAndrew Turner 110347e07394SAndrew Turner if (vcpu_debugged(vcpu)) { 110447e07394SAndrew Turner intr_restore(daif); 110547e07394SAndrew Turner vm_exit_debug(vcpu, pc); 110647e07394SAndrew Turner break; 110747e07394SAndrew Turner } 110847e07394SAndrew Turner 110947e07394SAndrew Turner /* Activate the stage2 pmap so the vmid is valid */ 111047e07394SAndrew Turner pmap_activate_vm(pmap); 111147e07394SAndrew Turner hyp->vttbr_el2 = pmap_to_ttbr0(pmap); 111247e07394SAndrew Turner 111347e07394SAndrew Turner /* 111447e07394SAndrew Turner * TODO: What happens if a timer interrupt is asserted exactly 111547e07394SAndrew Turner * here, but for the previous VM? 111647e07394SAndrew Turner */ 111747e07394SAndrew Turner arm64_set_active_vcpu(hypctx); 111847e07394SAndrew Turner vgic_flush_hwstate(hypctx); 111947e07394SAndrew Turner 112047e07394SAndrew Turner /* Call into EL2 to switch to the guest */ 1121*55aa3148SAndrew Turner excp_type = vmm_enter_guest(hyp, hypctx); 112247e07394SAndrew Turner 112347e07394SAndrew Turner vgic_sync_hwstate(hypctx); 112447e07394SAndrew Turner vtimer_sync_hwstate(hypctx); 112547e07394SAndrew Turner 112647e07394SAndrew Turner /* 112747e07394SAndrew Turner * Deactivate the stage2 pmap. vmm_pmap_clean_stage2_tlbi 112847e07394SAndrew Turner * depends on this meaning we activate the VM before entering 112947e07394SAndrew Turner * the vm again 113047e07394SAndrew Turner */ 113147e07394SAndrew Turner PCPU_SET(curvmpmap, NULL); 113247e07394SAndrew Turner intr_restore(daif); 113347e07394SAndrew Turner 113447e07394SAndrew Turner vmm_stat_incr(vcpu, VMEXIT_COUNT, 1); 113547e07394SAndrew Turner if (excp_type == EXCP_TYPE_MAINT_IRQ) 113647e07394SAndrew Turner continue; 113747e07394SAndrew Turner 113847e07394SAndrew Turner vme->pc = hypctx->tf.tf_elr; 113947e07394SAndrew Turner vme->inst_length = INSN_SIZE; 114047e07394SAndrew Turner vme->u.hyp.exception_nr = excp_type; 114147e07394SAndrew Turner vme->u.hyp.esr_el2 = hypctx->tf.tf_esr; 114247e07394SAndrew Turner vme->u.hyp.far_el2 = hypctx->exit_info.far_el2; 114347e07394SAndrew Turner vme->u.hyp.hpfar_el2 = hypctx->exit_info.hpfar_el2; 114447e07394SAndrew Turner 114547e07394SAndrew Turner handled = arm64_handle_world_switch(hypctx, excp_type, vme, 114647e07394SAndrew Turner pmap); 114747e07394SAndrew Turner if (handled == UNHANDLED) 114847e07394SAndrew Turner /* Exit loop to emulate instruction. */ 114947e07394SAndrew Turner break; 115047e07394SAndrew Turner else 115147e07394SAndrew Turner /* Resume guest execution from the next instruction. */ 115247e07394SAndrew Turner hypctx->tf.tf_elr += vme->inst_length; 115347e07394SAndrew Turner } 115447e07394SAndrew Turner 115547e07394SAndrew Turner return (0); 115647e07394SAndrew Turner } 115747e07394SAndrew Turner 115847e07394SAndrew Turner static void 115947e07394SAndrew Turner arm_pcpu_vmcleanup(void *arg) 116047e07394SAndrew Turner { 116147e07394SAndrew Turner struct hyp *hyp; 116247e07394SAndrew Turner int i, maxcpus; 116347e07394SAndrew Turner 116447e07394SAndrew Turner hyp = arg; 116547e07394SAndrew Turner maxcpus = vm_get_maxcpus(hyp->vm); 116647e07394SAndrew Turner for (i = 0; i < maxcpus; i++) { 116747e07394SAndrew Turner if (arm64_get_active_vcpu() == hyp->ctx[i]) { 116847e07394SAndrew Turner arm64_set_active_vcpu(NULL); 116947e07394SAndrew Turner break; 117047e07394SAndrew Turner } 117147e07394SAndrew Turner } 117247e07394SAndrew Turner } 117347e07394SAndrew Turner 117447e07394SAndrew Turner void 117547e07394SAndrew Turner vmmops_vcpu_cleanup(void *vcpui) 117647e07394SAndrew Turner { 117747e07394SAndrew Turner struct hypctx *hypctx = vcpui; 117847e07394SAndrew Turner 117947e07394SAndrew Turner vtimer_cpucleanup(hypctx); 118047e07394SAndrew Turner vgic_cpucleanup(hypctx); 118147e07394SAndrew Turner 118247e07394SAndrew Turner vmmpmap_remove(hypctx->el2_addr, el2_hypctx_size(), true); 118347e07394SAndrew Turner 118447e07394SAndrew Turner free(hypctx, M_HYP); 118547e07394SAndrew Turner } 118647e07394SAndrew Turner 118747e07394SAndrew Turner void 118847e07394SAndrew Turner vmmops_cleanup(void *vmi) 118947e07394SAndrew Turner { 119047e07394SAndrew Turner struct hyp *hyp = vmi; 119147e07394SAndrew Turner 119247e07394SAndrew Turner vtimer_vmcleanup(hyp); 119347e07394SAndrew Turner vgic_vmcleanup(hyp); 119447e07394SAndrew Turner 119547e07394SAndrew Turner smp_rendezvous(NULL, arm_pcpu_vmcleanup, NULL, hyp); 119647e07394SAndrew Turner 119747e07394SAndrew Turner vmmpmap_remove(hyp->el2_addr, el2_hyp_size(hyp->vm), true); 119847e07394SAndrew Turner 119947e07394SAndrew Turner free(hyp, M_HYP); 120047e07394SAndrew Turner } 120147e07394SAndrew Turner 120247e07394SAndrew Turner /* 120347e07394SAndrew Turner * Return register value. Registers have different sizes and an explicit cast 120447e07394SAndrew Turner * must be made to ensure proper conversion. 120547e07394SAndrew Turner */ 120647e07394SAndrew Turner static uint64_t * 120747e07394SAndrew Turner hypctx_regptr(struct hypctx *hypctx, int reg) 120847e07394SAndrew Turner { 120947e07394SAndrew Turner switch (reg) { 121047e07394SAndrew Turner case VM_REG_GUEST_X0 ... VM_REG_GUEST_X29: 121147e07394SAndrew Turner return (&hypctx->tf.tf_x[reg]); 121247e07394SAndrew Turner case VM_REG_GUEST_LR: 121347e07394SAndrew Turner return (&hypctx->tf.tf_lr); 121447e07394SAndrew Turner case VM_REG_GUEST_SP: 121547e07394SAndrew Turner return (&hypctx->tf.tf_sp); 121647e07394SAndrew Turner case VM_REG_GUEST_CPSR: 121747e07394SAndrew Turner return (&hypctx->tf.tf_spsr); 121847e07394SAndrew Turner case VM_REG_GUEST_PC: 121947e07394SAndrew Turner return (&hypctx->tf.tf_elr); 122047e07394SAndrew Turner case VM_REG_GUEST_SCTLR_EL1: 122147e07394SAndrew Turner return (&hypctx->sctlr_el1); 122247e07394SAndrew Turner case VM_REG_GUEST_TTBR0_EL1: 122347e07394SAndrew Turner return (&hypctx->ttbr0_el1); 122447e07394SAndrew Turner case VM_REG_GUEST_TTBR1_EL1: 122547e07394SAndrew Turner return (&hypctx->ttbr1_el1); 122647e07394SAndrew Turner case VM_REG_GUEST_TCR_EL1: 122747e07394SAndrew Turner return (&hypctx->tcr_el1); 122847e07394SAndrew Turner case VM_REG_GUEST_TCR2_EL1: 122947e07394SAndrew Turner return (&hypctx->tcr2_el1); 123047e07394SAndrew Turner default: 123147e07394SAndrew Turner break; 123247e07394SAndrew Turner } 123347e07394SAndrew Turner return (NULL); 123447e07394SAndrew Turner } 123547e07394SAndrew Turner 123647e07394SAndrew Turner int 123747e07394SAndrew Turner vmmops_getreg(void *vcpui, int reg, uint64_t *retval) 123847e07394SAndrew Turner { 123947e07394SAndrew Turner uint64_t *regp; 124047e07394SAndrew Turner int running, hostcpu; 124147e07394SAndrew Turner struct hypctx *hypctx = vcpui; 124247e07394SAndrew Turner 124347e07394SAndrew Turner running = vcpu_is_running(hypctx->vcpu, &hostcpu); 124447e07394SAndrew Turner if (running && hostcpu != curcpu) 124547e07394SAndrew Turner panic("arm_getreg: %s%d is running", vm_name(hypctx->hyp->vm), 124647e07394SAndrew Turner vcpu_vcpuid(hypctx->vcpu)); 124747e07394SAndrew Turner 124847e07394SAndrew Turner regp = hypctx_regptr(hypctx, reg); 124947e07394SAndrew Turner if (regp == NULL) 125047e07394SAndrew Turner return (EINVAL); 125147e07394SAndrew Turner 125247e07394SAndrew Turner *retval = *regp; 125347e07394SAndrew Turner return (0); 125447e07394SAndrew Turner } 125547e07394SAndrew Turner 125647e07394SAndrew Turner int 125747e07394SAndrew Turner vmmops_setreg(void *vcpui, int reg, uint64_t val) 125847e07394SAndrew Turner { 125947e07394SAndrew Turner uint64_t *regp; 126047e07394SAndrew Turner struct hypctx *hypctx = vcpui; 126147e07394SAndrew Turner int running, hostcpu; 126247e07394SAndrew Turner 126347e07394SAndrew Turner running = vcpu_is_running(hypctx->vcpu, &hostcpu); 126447e07394SAndrew Turner if (running && hostcpu != curcpu) 126547e07394SAndrew Turner panic("arm_setreg: %s%d is running", vm_name(hypctx->hyp->vm), 126647e07394SAndrew Turner vcpu_vcpuid(hypctx->vcpu)); 126747e07394SAndrew Turner 126847e07394SAndrew Turner regp = hypctx_regptr(hypctx, reg); 126947e07394SAndrew Turner if (regp == NULL) 127047e07394SAndrew Turner return (EINVAL); 127147e07394SAndrew Turner 127247e07394SAndrew Turner *regp = val; 127347e07394SAndrew Turner return (0); 127447e07394SAndrew Turner } 127547e07394SAndrew Turner 127647e07394SAndrew Turner int 127747e07394SAndrew Turner vmmops_exception(void *vcpui, uint64_t esr, uint64_t far) 127847e07394SAndrew Turner { 127947e07394SAndrew Turner struct hypctx *hypctx = vcpui; 128047e07394SAndrew Turner int running, hostcpu; 128147e07394SAndrew Turner 128247e07394SAndrew Turner running = vcpu_is_running(hypctx->vcpu, &hostcpu); 128347e07394SAndrew Turner if (running && hostcpu != curcpu) 128447e07394SAndrew Turner panic("%s: %s%d is running", __func__, vm_name(hypctx->hyp->vm), 128547e07394SAndrew Turner vcpu_vcpuid(hypctx->vcpu)); 128647e07394SAndrew Turner 128747e07394SAndrew Turner hypctx->far_el1 = far; 128847e07394SAndrew Turner hypctx->esr_el1 = esr; 128947e07394SAndrew Turner hypctx->has_exception = true; 129047e07394SAndrew Turner 129147e07394SAndrew Turner return (0); 129247e07394SAndrew Turner } 129347e07394SAndrew Turner 129447e07394SAndrew Turner int 129547e07394SAndrew Turner vmmops_getcap(void *vcpui, int num, int *retval) 129647e07394SAndrew Turner { 129775cb9492SMark Johnston struct hypctx *hypctx = vcpui; 129847e07394SAndrew Turner int ret; 129947e07394SAndrew Turner 130047e07394SAndrew Turner ret = ENOENT; 130147e07394SAndrew Turner 130247e07394SAndrew Turner switch (num) { 130347e07394SAndrew Turner case VM_CAP_UNRESTRICTED_GUEST: 130447e07394SAndrew Turner *retval = 1; 130547e07394SAndrew Turner ret = 0; 130647e07394SAndrew Turner break; 130775cb9492SMark Johnston case VM_CAP_BRK_EXIT: 130875cb9492SMark Johnston case VM_CAP_SS_EXIT: 130975cb9492SMark Johnston case VM_CAP_MASK_HWINTR: 131075cb9492SMark Johnston *retval = (hypctx->setcaps & (1ul << num)) != 0; 131175cb9492SMark Johnston break; 131247e07394SAndrew Turner default: 131347e07394SAndrew Turner break; 131447e07394SAndrew Turner } 131547e07394SAndrew Turner 131647e07394SAndrew Turner return (ret); 131747e07394SAndrew Turner } 131847e07394SAndrew Turner 131947e07394SAndrew Turner int 132047e07394SAndrew Turner vmmops_setcap(void *vcpui, int num, int val) 132147e07394SAndrew Turner { 132275cb9492SMark Johnston struct hypctx *hypctx = vcpui; 132375cb9492SMark Johnston int ret; 132447e07394SAndrew Turner 132575cb9492SMark Johnston ret = 0; 132675cb9492SMark Johnston 132775cb9492SMark Johnston switch (num) { 132875cb9492SMark Johnston case VM_CAP_BRK_EXIT: 1329abf239cfSAndrew Turner if ((val != 0) == ((hypctx->setcaps & (1ul << num)) != 0)) 133075cb9492SMark Johnston break; 133175cb9492SMark Johnston if (val != 0) 133275cb9492SMark Johnston hypctx->mdcr_el2 |= MDCR_EL2_TDE; 133375cb9492SMark Johnston else 133475cb9492SMark Johnston hypctx->mdcr_el2 &= ~MDCR_EL2_TDE; 133575cb9492SMark Johnston break; 133675cb9492SMark Johnston case VM_CAP_SS_EXIT: 1337abf239cfSAndrew Turner if ((val != 0) == ((hypctx->setcaps & (1ul << num)) != 0)) 133875cb9492SMark Johnston break; 133975cb9492SMark Johnston 134075cb9492SMark Johnston if (val != 0) { 134175cb9492SMark Johnston hypctx->debug_spsr |= (hypctx->tf.tf_spsr & PSR_SS); 134275cb9492SMark Johnston hypctx->debug_mdscr |= hypctx->mdscr_el1 & 134375cb9492SMark Johnston (MDSCR_SS | MDSCR_KDE); 134475cb9492SMark Johnston 134575cb9492SMark Johnston hypctx->tf.tf_spsr |= PSR_SS; 134675cb9492SMark Johnston hypctx->mdscr_el1 |= MDSCR_SS | MDSCR_KDE; 134775cb9492SMark Johnston hypctx->mdcr_el2 |= MDCR_EL2_TDE; 134875cb9492SMark Johnston } else { 134975cb9492SMark Johnston hypctx->tf.tf_spsr &= ~PSR_SS; 135075cb9492SMark Johnston hypctx->tf.tf_spsr |= hypctx->debug_spsr; 135175cb9492SMark Johnston hypctx->debug_spsr &= ~PSR_SS; 135275cb9492SMark Johnston hypctx->mdscr_el1 &= ~(MDSCR_SS | MDSCR_KDE); 135375cb9492SMark Johnston hypctx->mdscr_el1 |= hypctx->debug_mdscr; 135475cb9492SMark Johnston hypctx->debug_mdscr &= ~(MDSCR_SS | MDSCR_KDE); 135575cb9492SMark Johnston hypctx->mdcr_el2 &= ~MDCR_EL2_TDE; 135675cb9492SMark Johnston } 135775cb9492SMark Johnston break; 135875cb9492SMark Johnston case VM_CAP_MASK_HWINTR: 1359abf239cfSAndrew Turner if ((val != 0) == ((hypctx->setcaps & (1ul << num)) != 0)) 136075cb9492SMark Johnston break; 136175cb9492SMark Johnston 136275cb9492SMark Johnston if (val != 0) { 136375cb9492SMark Johnston hypctx->debug_spsr |= (hypctx->tf.tf_spsr & 136475cb9492SMark Johnston (PSR_I | PSR_F)); 136575cb9492SMark Johnston hypctx->tf.tf_spsr |= PSR_I | PSR_F; 136675cb9492SMark Johnston } else { 136775cb9492SMark Johnston hypctx->tf.tf_spsr &= ~(PSR_I | PSR_F); 136875cb9492SMark Johnston hypctx->tf.tf_spsr |= (hypctx->debug_spsr & 136975cb9492SMark Johnston (PSR_I | PSR_F)); 137075cb9492SMark Johnston hypctx->debug_spsr &= ~(PSR_I | PSR_F); 137175cb9492SMark Johnston } 137275cb9492SMark Johnston break; 137375cb9492SMark Johnston default: 137475cb9492SMark Johnston ret = ENOENT; 137575cb9492SMark Johnston break; 137675cb9492SMark Johnston } 137775cb9492SMark Johnston 137875cb9492SMark Johnston if (ret == 0) { 137975cb9492SMark Johnston if (val == 0) 138075cb9492SMark Johnston hypctx->setcaps &= ~(1ul << num); 138175cb9492SMark Johnston else 138275cb9492SMark Johnston hypctx->setcaps |= (1ul << num); 138375cb9492SMark Johnston } 138475cb9492SMark Johnston 138575cb9492SMark Johnston return (ret); 138647e07394SAndrew Turner } 1387