Skip to main content

ax_cpu/arch/x86_64/paging/
ept.rs

1//! Intel Extended Page Table encoding, from AxVM's nested-paging backend.
2//! See Intel SDM Volume 3C, section 28.3.2.
3
4use crate::{PhysAddr, paging::PageTableEntry};
5
6bitflags::bitflags! {
7    /// Hardware flags in an Intel EPT descriptor.
8    #[derive(Clone, Copy, Debug, Eq, PartialEq)]
9    pub struct EptFlags: u64 {
10        /// Reads are permitted through this entry.
11        const READ = 1 << 0;
12        /// Writes are permitted through this entry.
13        const WRITE = 1 << 1;
14        /// Supervisor execution is permitted through this entry.
15        const EXECUTE = 1 << 2;
16        /// Leaf memory type, independently of guest PAT selection.
17        const MEM_TYPE_MASK = 7 << 3;
18        /// Ignore the guest PAT when determining the effective memory type.
19        const IGNORE_PAT = 1 << 6;
20        /// A directory-level entry maps a large page.
21        const HUGE_PAGE = 1 << 7;
22        /// Hardware accessed state, when enabled in EPTP.
23        const ACCESSED = 1 << 8;
24        /// Hardware dirty state, when enabled in EPTP.
25        const DIRTY = 1 << 9;
26        /// User execution permission when mode-based execute control is enabled.
27        const EXECUTE_FOR_USER = 1 << 10;
28    }
29}
30
31/// Non-reserved EPT leaf memory-type encodings.
32#[repr(u8)]
33#[derive(Clone, Copy, Debug, Eq, PartialEq)]
34pub enum EptMemoryType {
35    /// Uncacheable memory.
36    Uncached       = 0,
37    /// Write-combining memory.
38    WriteCombining = 1,
39    /// Write-through memory.
40    WriteThrough   = 4,
41    /// Write-protected memory.
42    WriteProtected = 5,
43    /// Write-back memory.
44    WriteBack      = 6,
45}
46
47impl EptFlags {
48    /// Replaces the memory-type field while retaining every other bit.
49    pub const fn with_memory_type(self, memory_type: EptMemoryType) -> Self {
50        Self::from_bits_retain(
51            (self.bits() & !Self::MEM_TYPE_MASK.bits()) | ((memory_type as u64) << 3),
52        )
53    }
54
55    /// Decodes a leaf memory type; reserved encodings return `None`.
56    pub const fn memory_type(self) -> Option<EptMemoryType> {
57        match (self.bits() & Self::MEM_TYPE_MASK.bits()) >> 3 {
58            0 => Some(EptMemoryType::Uncached),
59            1 => Some(EptMemoryType::WriteCombining),
60            4 => Some(EptMemoryType::WriteThrough),
61            5 => Some(EptMemoryType::WriteProtected),
62            6 => Some(EptMemoryType::WriteBack),
63            _ => None,
64        }
65    }
66}
67
68/// An EPT descriptor, independent of table allocation and EPTP geometry.
69#[repr(transparent)]
70#[derive(Clone, Copy, Default)]
71pub struct EptEntry(u64);
72
73impl EptEntry {
74    const PHYS_ADDR_MASK: u64 = 0x000f_ffff_ffff_f000;
75
76    /// Encodes a host physical page and explicit hardware flags.
77    ///
78    /// This does not install a translation. The table owner must validate the
79    /// CPU's physical width, large-page alignment, supported memory types,
80    /// permissions and optional EPT controls before making this entry live.
81    pub const fn from_parts(paddr: PhysAddr, flags: EptFlags) -> Self {
82        Self((paddr.as_usize() as u64 & Self::PHYS_ADDR_MASK) | flags.bits())
83    }
84
85    /// Returns hardware flags without applying a VM mapping policy.
86    pub fn flags(self) -> EptFlags {
87        EptFlags::from_bits_truncate(self.0)
88    }
89}
90
91impl PageTableEntry for EptEntry {
92    type PteConfig = EptFlags;
93
94    fn new_page(paddr: PhysAddr, mut flags: EptFlags, is_huge: bool) -> Self {
95        flags.set(EptFlags::HUGE_PAGE, is_huge);
96        Self::from_parts(paddr, flags)
97    }
98
99    fn new_table(paddr: PhysAddr) -> Self {
100        Self::from_parts(paddr, EptFlags::READ | EptFlags::WRITE | EptFlags::EXECUTE)
101    }
102
103    fn paddr(&self, _is_dir: bool) -> PhysAddr {
104        PhysAddr::from_usize((self.0 & Self::PHYS_ADDR_MASK) as usize)
105    }
106
107    fn config(&self, _is_dir: bool) -> EptFlags {
108        self.flags()
109    }
110
111    fn present(&self) -> bool {
112        self.flags()
113            .intersects(EptFlags::READ | EptFlags::WRITE | EptFlags::EXECUTE)
114    }
115
116    fn huge(&self, is_dir: bool) -> bool {
117        is_dir && self.flags().contains(EptFlags::HUGE_PAGE)
118    }
119
120    fn unused(&self) -> bool {
121        self.0 == 0
122    }
123
124    fn clear(&mut self) {
125        self.0 = 0;
126    }
127}
128
129impl core::fmt::Debug for EptEntry {
130    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
131        f.debug_struct("EptEntry")
132            .field("raw", &self.0)
133            .field("paddr", &self.paddr(false))
134            .field("flags", &self.flags())
135            .field("memory_type", &self.flags().memory_type())
136            .finish()
137    }
138}