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 2025 Oxide Computer Company 16 */ 17 18 #include <sys/vmm_gpt_impl.h> 19 #include <sys/debug.h> 20 21 #define EPT_R (1 << 0) 22 #define EPT_W (1 << 1) 23 #define EPT_X (1 << 2) 24 #define EPT_RWX (EPT_R | EPT_W | EPT_X) 25 #define EPT_LGPG (1 << 7) 26 #define EPT_ACCESSED (1 << 8) 27 #define EPT_DIRTY (1 << 9) 28 29 #define EPT_PA_MASK (0x000ffffffffff000ull) 30 31 #define EPT_MAX_LEVELS 4 32 33 #define EPTP_FLAG_ACCESSED_DIRTY (1 << 6) 34 35 CTASSERT(EPT_R == PROT_READ); 36 CTASSERT(EPT_W == PROT_WRITE); 37 CTASSERT(EPT_X == PROT_EXEC); 38 39 static inline uint64_t 40 ept_attr_to_pat(uint8_t attr) 41 { 42 uint64_t bits = attr & 0x7; 43 return (bits << 3); 44 } 45 46 static uint64_t 47 ept_map_table(uint64_t pfn) 48 { 49 const uint64_t paddr = pfn_to_pa(pfn) & EPT_PA_MASK; 50 return (paddr | EPT_RWX); 51 } 52 53 static uint64_t 54 ept_map_page(uint64_t pfn, uint_t prot, uint8_t attr) 55 { 56 const uint64_t paddr = pfn_to_pa(pfn) & EPT_PA_MASK; 57 const uint64_t pat = ept_attr_to_pat(attr); 58 const uint64_t rprot = prot & EPT_RWX; 59 return (paddr | pat | rprot); 60 } 61 62 static bool 63 ept_pte_parse(uint64_t pte, pfn_t *pfnp, uint_t *protp) 64 { 65 const uint_t prot = pte & EPT_RWX; 66 67 if (prot == 0) { 68 return (false); 69 } 70 71 if (pfnp != NULL) { 72 *pfnp = (pte & PT_PADDR) >> PAGESHIFT; 73 } 74 if (protp != NULL) { 75 *protp = prot; 76 } 77 return (true); 78 } 79 80 static uint64_t 81 ept_get_pmtp(pfn_t root_pfn, bool track_dirty) 82 { 83 const uint64_t ad_flag = track_dirty ? EPTP_FLAG_ACCESSED_DIRTY : 0; 84 return ((root_pfn << PAGESHIFT | ad_flag | 85 (EPT_MAX_LEVELS - 1) << 3 | MTRR_TYPE_WB)); 86 } 87 88 static bool 89 ept_hw_ad_supported(void) 90 { 91 uint64_t ept_caps = rdmsr(MSR_IA32_VMX_EPT_VPID_CAP); 92 return ((ept_caps & IA32_VMX_EPT_VPID_HW_AD) != 0); 93 } 94 95 const struct vmm_pte_impl ept_pte_impl = { 96 .vpi_map_table = ept_map_table, 97 .vpi_map_page = ept_map_page, 98 .vpi_pte_parse = ept_pte_parse, 99 .vpi_bit_accessed = EPT_ACCESSED, 100 .vpi_bit_dirty = EPT_DIRTY, 101 102 .vpi_get_pmtp = ept_get_pmtp, 103 .vpi_hw_ad_supported = ept_hw_ad_supported, 104 }; 105