Skip to main content

ax_cpu/arch/x86_64/paging/
native.rs

1//! x86_64 page-table entry format.
2
3use ax_memory_addr::{PAGE_SIZE_4K, PhysAddr};
4use page_table_generic::PageTableEntry;
5
6use crate::paging::MappingFlags;
7
8pub(crate) const PAGE_SIZE: usize = PAGE_SIZE_4K;
9pub(crate) const LEVEL_BITS: &[usize] = &[9, 9, 9, 9];
10pub(crate) const MAX_BLOCK_LEVEL: usize = 3;
11
12bitflags::bitflags! {
13    /// Hardware flags shared by boot and runtime x86 paging descriptors.
14    #[derive(Clone, Copy, Debug, Eq, PartialEq)]
15    pub struct DescriptorFlags: u64 {
16        /// The descriptor participates in address translation.
17        const PRESENT = 1 << 0;
18        /// Writes are permitted by this level.
19        const WRITABLE = 1 << 1;
20        /// User accesses are permitted by this level.
21        const USER = 1 << 2;
22        /// Page-level write-through cache selection.
23        const WRITE_THROUGH = 1 << 3;
24        /// Page-level cache-disable selection.
25        const NO_CACHE = 1 << 4;
26        /// The processor has accessed this entry.
27        const ACCESSED = 1 << 5;
28        /// The processor has written this leaf.
29        const DIRTY = 1 << 6;
30        /// A directory-level entry maps a large page.
31        const HUGE_PAGE = 1 << 7;
32        /// The leaf translation is global when CR4.PGE is enabled.
33        const GLOBAL = 1 << 8;
34        /// Instruction fetch is prohibited when EFER.NXE is enabled.
35        const NO_EXECUTE = 1 << 63;
36    }
37}
38
39/// x86_64 page-table entry.
40#[derive(Clone, Copy, Default)]
41#[repr(transparent)]
42pub struct X64Pte(u64);
43
44/// Native stage-one page-table descriptor.
45pub type Pte = X64Pte;
46
47impl X64Pte {
48    const PHYS_ADDR_MASK: u64 = 0x000f_ffff_ffff_f000;
49
50    /// Encodes a physical address and explicit native descriptor flags.
51    ///
52    /// The caller chooses leaf/directory permissions and accessed/dirty policy.
53    /// Installation must satisfy the CPU's physical-address width, page-size
54    /// alignment and enabled paging features. This constructor does not install
55    /// the descriptor or allocate table memory.
56    pub const fn from_parts(paddr: PhysAddr, flags: DescriptorFlags) -> Self {
57        Self((paddr.as_usize() as u64 & Self::PHYS_ADDR_MASK) | flags.bits())
58    }
59
60    /// Returns native flags without interpreting a boot or runtime policy.
61    pub fn flags(self) -> DescriptorFlags {
62        DescriptorFlags::from_bits_truncate(self.0)
63    }
64}
65
66impl PageTableEntry for X64Pte {
67    type PteConfig = MappingFlags;
68
69    fn new_page(paddr: PhysAddr, config: Self::PteConfig, is_huge: bool) -> Self {
70        if config.is_empty() && paddr.as_usize() == 0 {
71            return Self(0);
72        }
73        if config.is_empty() {
74            let huge = if is_huge {
75                DescriptorFlags::HUGE_PAGE.bits()
76            } else {
77                0
78            };
79            return Self((paddr.as_usize() as u64 & Self::PHYS_ADDR_MASK) | huge);
80        }
81
82        let mut flags = DescriptorFlags::PRESENT;
83        if config.contains(MappingFlags::WRITE) {
84            flags |= DescriptorFlags::WRITABLE;
85        }
86        if config.contains(MappingFlags::USER) {
87            flags |= DescriptorFlags::USER;
88        }
89        if config.intersects(MappingFlags::DEVICE | MappingFlags::UNCACHED) {
90            flags |= DescriptorFlags::NO_CACHE | DescriptorFlags::WRITE_THROUGH;
91        }
92        if !config.contains(MappingFlags::EXECUTE) {
93            flags |= DescriptorFlags::NO_EXECUTE;
94        }
95        if is_huge {
96            flags |= DescriptorFlags::HUGE_PAGE;
97        }
98        Self::from_parts(paddr, flags)
99    }
100
101    fn new_table(paddr: PhysAddr) -> Self {
102        Self::from_parts(
103            paddr,
104            DescriptorFlags::PRESENT | DescriptorFlags::WRITABLE | DescriptorFlags::USER,
105        )
106    }
107
108    fn paddr(&self, _is_dir: bool) -> PhysAddr {
109        PhysAddr::from_usize((self.0 & Self::PHYS_ADDR_MASK) as usize)
110    }
111
112    fn config(&self, _is_dir: bool) -> Self::PteConfig {
113        let flags = self.flags();
114        if !flags.contains(DescriptorFlags::PRESENT) {
115            return MappingFlags::empty();
116        }
117        let mut config = MappingFlags::READ;
118        config.set(
119            MappingFlags::WRITE,
120            flags.contains(DescriptorFlags::WRITABLE),
121        );
122        config.set(
123            MappingFlags::EXECUTE,
124            !flags.contains(DescriptorFlags::NO_EXECUTE),
125        );
126        config.set(MappingFlags::USER, flags.contains(DescriptorFlags::USER));
127        config.set(
128            MappingFlags::UNCACHED,
129            flags.contains(DescriptorFlags::NO_CACHE),
130        );
131        config
132    }
133
134    fn present(&self) -> bool {
135        self.flags().contains(DescriptorFlags::PRESENT)
136    }
137
138    fn huge(&self, is_dir: bool) -> bool {
139        is_dir && self.flags().contains(DescriptorFlags::HUGE_PAGE)
140    }
141
142    fn unused(&self) -> bool {
143        self.0 == 0
144    }
145
146    fn clear(&mut self) {
147        self.0 = 0;
148    }
149}
150
151impl core::fmt::Debug for X64Pte {
152    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
153        f.debug_struct("X64Pte")
154            .field("raw", &self.0)
155            .field("config", &self.config(false))
156            .finish()
157    }
158}