1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 /* This file is dual-licensed; see usr/src/contrib/bhyve/LICENSE */ 12 13 /* 14 * Copyright 2019 Joyent, Inc. 15 * Copyright 2021 Oxide Computer Company 16 */ 17 18 #include <sys/types.h> 19 #include <sys/param.h> 20 #include <sys/atomic.h> 21 #include <sys/kmem.h> 22 #include <sys/machsystm.h> 23 #include <sys/mman.h> 24 #include <sys/x86_archext.h> 25 #include <vm/hat_pte.h> 26 27 #include <sys/vmm_gpt.h> 28 #include <sys/vmm_vm.h> 29 30 #define EPT_R (1 << 0) 31 #define EPT_W (1 << 1) 32 #define EPT_X (1 << 2) 33 #define EPT_RWX (EPT_R | EPT_W | EPT_X) 34 #define EPT_LGPG (1 << 7) 35 #define EPT_ACCESSED (1 << 8) 36 #define EPT_DIRTY (1 << 9) 37 38 #define EPT_PA_MASK (0x000ffffffffff000ull) 39 40 #define EPT_MAX_LEVELS 4 41 CTASSERT(EPT_MAX_LEVELS <= MAX_GPT_LEVEL); 42 43 CTASSERT(EPT_R == PROT_READ); 44 CTASSERT(EPT_W == PROT_WRITE); 45 CTASSERT(EPT_X == PROT_EXEC); 46 47 static uint_t 48 ept_pte_prot(uint64_t pte) 49 { 50 return (pte & EPT_RWX); 51 } 52 53 static inline uint64_t 54 ept_attr_to_pat(uint8_t attr) 55 { 56 uint64_t bits = attr & 0x7; 57 return (bits << 3); 58 } 59 60 static uint64_t 61 ept_map_table(uint64_t pfn) 62 { 63 const uint64_t paddr = pfn_to_pa(pfn) & EPT_PA_MASK; 64 return (paddr | EPT_RWX); 65 } 66 67 static uint64_t 68 ept_map_page(uint64_t pfn, uint_t prot, uint8_t attr) 69 { 70 const uint64_t paddr = pfn_to_pa(pfn) & EPT_PA_MASK; 71 const uint64_t pat = ept_attr_to_pat(attr); 72 const uint64_t rprot = prot & EPT_RWX; 73 return (paddr | pat | rprot); 74 } 75 76 static uint64_t 77 ept_pte_pfn(uint64_t pte) 78 { 79 return (mmu_btop(pte & PT_PADDR)); 80 } 81 82 static bool 83 ept_pte_is_present(uint64_t pte) 84 { 85 return ((pte & EPT_RWX) != 0); 86 } 87 88 static uint_t 89 ept_reset_bits(volatile uint64_t *entry, uint64_t mask, uint64_t bits) 90 { 91 uint64_t pte, newpte, oldpte = 0; 92 93 /* 94 * We use volatile and atomic ops here because we may be 95 * racing against hardware modifying these bits. 96 */ 97 VERIFY3P(entry, !=, NULL); 98 oldpte = *entry; 99 do { 100 pte = oldpte; 101 newpte = (pte & ~mask) | bits; 102 oldpte = atomic_cas_64(entry, pte, newpte); 103 } while (oldpte != pte); 104 105 return (oldpte & mask); 106 } 107 108 static uint_t 109 ept_reset_dirty(uint64_t *entry, bool on) 110 { 111 return (ept_reset_bits(entry, EPT_DIRTY, 112 on ? (EPT_DIRTY | EPT_ACCESSED) : 0)); 113 } 114 115 static uint_t 116 ept_reset_accessed(uint64_t *entry, bool on) 117 { 118 return (ept_reset_bits(entry, EPT_DIRTY | EPT_ACCESSED, 119 on ? EPT_ACCESSED : 0)); 120 } 121 122 static uint64_t 123 ept_get_pmtp(pfn_t root_pfn) 124 { 125 /* TODO: enable AD tracking when required */ 126 return ((root_pfn << PAGESHIFT | 127 (EPT_MAX_LEVELS - 1) << 3 | MTRR_TYPE_WB)); 128 } 129 130 vmm_pte_ops_t ept_pte_ops = { 131 .vpeo_map_table = ept_map_table, 132 .vpeo_map_page = ept_map_page, 133 .vpeo_pte_pfn = ept_pte_pfn, 134 .vpeo_pte_is_present = ept_pte_is_present, 135 .vpeo_pte_prot = ept_pte_prot, 136 .vpeo_reset_dirty = ept_reset_dirty, 137 .vpeo_reset_accessed = ept_reset_accessed, 138 .vpeo_get_pmtp = ept_get_pmtp, 139 }; 140