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
ept_attr_to_pat(uint8_t attr)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
ept_map_table(uint64_t pfn)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
ept_map_page(uint64_t pfn,uint_t prot,uint8_t attr)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
ept_pte_parse(uint64_t pte,pfn_t * pfnp,uint_t * protp)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
ept_get_pmtp(pfn_t root_pfn,bool track_dirty)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
ept_hw_ad_supported(void)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