x86/registers/bits32/
paging.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2025.
4
5// This is inspired and adapted for Tock from the [x86](https://github.com/gz/rust-x86) crate.
6
7//! Description of the data-structures for IA-32 paging mode.
8
9use core::fmt;
10use core::ops;
11use kernel::utilities::registers::register_bitfields;
12use tock_registers::LocalRegisterCopy;
13
14/// A wrapper for a physical address.
15#[repr(transparent)]
16#[derive(Copy, Clone, Eq, Ord, PartialEq, PartialOrd)]
17pub struct PAddr(pub u32);
18
19impl From<u32> for PAddr {
20    fn from(num: u32) -> Self {
21        PAddr(num)
22    }
23}
24
25impl From<usize> for PAddr {
26    fn from(num: usize) -> Self {
27        PAddr(num as u32)
28    }
29}
30
31impl From<i32> for PAddr {
32    fn from(num: i32) -> Self {
33        PAddr(num as u32)
34    }
35}
36
37#[allow(clippy::from_over_into)]
38impl Into<u32> for PAddr {
39    fn into(self) -> u32 {
40        self.0
41    }
42}
43
44#[allow(clippy::from_over_into)]
45impl Into<usize> for PAddr {
46    fn into(self) -> usize {
47        self.0 as usize
48    }
49}
50
51impl ops::Rem for PAddr {
52    type Output = PAddr;
53
54    fn rem(self, rhs: PAddr) -> Self::Output {
55        PAddr(self.0 % rhs.0)
56    }
57}
58
59impl ops::Rem<u32> for PAddr {
60    type Output = u32;
61
62    fn rem(self, rhs: u32) -> Self::Output {
63        self.0 % rhs
64    }
65}
66
67impl ops::Rem<usize> for PAddr {
68    type Output = u32;
69
70    fn rem(self, rhs: usize) -> Self::Output {
71        self.0 % (rhs as u32)
72    }
73}
74
75impl ops::BitAnd for PAddr {
76    type Output = Self;
77
78    fn bitand(self, rhs: Self) -> Self {
79        PAddr(self.0 & rhs.0)
80    }
81}
82
83impl ops::BitAnd<u32> for PAddr {
84    type Output = u32;
85
86    fn bitand(self, rhs: u32) -> Self::Output {
87        Into::<u32>::into(self) & rhs
88    }
89}
90
91impl ops::BitOr for PAddr {
92    type Output = PAddr;
93
94    fn bitor(self, rhs: PAddr) -> Self::Output {
95        PAddr(self.0 | rhs.0)
96    }
97}
98
99impl ops::BitOr<u32> for PAddr {
100    type Output = u32;
101
102    fn bitor(self, rhs: u32) -> Self::Output {
103        self.0 | rhs
104    }
105}
106
107impl fmt::LowerHex for PAddr {
108    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
109        self.0.fmt(f)
110    }
111}
112
113/// A PD Entry consists of an address and a bunch of flags.
114#[repr(transparent)]
115#[derive(Clone, Copy)]
116pub struct PDEntry(pub u32);
117
118impl fmt::Debug for PDEntry {
119    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
120        write!(f, "PDEntry {{ {:#x}, {:?} }}", self.address(), self.flags())
121    }
122}
123
124register_bitfields![u32,
125    /// PD configuration bits description
126    pub PDFLAGS [
127        /// Present; must be 1 to map a 4-MByte page.
128        P OFFSET(0) NUMBITS(1),
129        /// Read/write; if 0, writes may not be allowed to the 4-MByte page referenced by this entry.
130        RW OFFSET(1) NUMBITS(1),
131        /// User/supervisor; if 0, user-mode accesses are not allowed to the 4-MByte page referenced by this entry.
132        US OFFSET(2) NUMBITS(1),
133        /// Page-level write-through.
134        PWT OFFSET(3) NUMBITS(1),
135        /// Page-level cache disable.
136        PCD OFFSET(4) NUMBITS(1),
137        /// Accessed; indicates whether software has accessed the 4-MByte page referenced by this entry.
138        A OFFSET(5) NUMBITS(1),
139        /// Dirty; indicates whether software has written to the 4-MByte page referenced by this entry.
140        D OFFSET(6) NUMBITS(1),
141        /// Page size; if set this entry maps a 4-MByte page; otherwise, this entry references a page directory.
142        PS OFFSET(7) NUMBITS(1),
143        /// Global; if CR4.PGE = 1, determines whether the translation is global; ignored otherwise.
144        G OFFSET(8) NUMBITS(1),
145        /// If the PAT is supported, indirectly determines the memory type used to access the 4-MByte page referenced by this entry;
146        /// otherwise, reserved (must be 0)
147        PAT OFFSET(12) NUMBITS(1),
148    ],
149    pub PTFLAGS [
150        /// Present; must be 1 to map a 4-MByte page.
151        P OFFSET(0) NUMBITS(1),
152        /// Read/write; if 0, writes may not be allowed to the 4-MByte page referenced by this entry.
153        RW OFFSET(1) NUMBITS(1),
154        /// User/supervisor; if 0, user-mode accesses are not allowed to the 4-MByte page referenced by this entry.
155        US OFFSET(2) NUMBITS(1),
156        /// Page-level write-through.
157        PWT OFFSET(3) NUMBITS(1),
158        /// Page-level cache disable.
159        PCD OFFSET(4) NUMBITS(1),
160        /// Accessed; indicates whether software has accessed the 4-MByte page referenced by this entry.
161        A OFFSET(5) NUMBITS(1),
162        /// Dirty; indicates whether software has written to the 4-MByte page referenced by this entry.
163        D OFFSET(6) NUMBITS(1),
164        /// If the PAT is supported, indirectly determines the memory type used to access the 4-MByte page referenced by this entry;
165        /// otherwise, reserved (must be 0)
166        PAT OFFSET(7) NUMBITS(1),
167        /// Global; if CR4.PGE = 1, determines whether the translation is global; ignored otherwise.
168        G OFFSET(8) NUMBITS(1),
169    ],
170];
171
172pub type PDFlags = LocalRegisterCopy<u32, PDFLAGS::Register>;
173pub type PTFlags = LocalRegisterCopy<u32, PTFLAGS::Register>;
174
175/// Mask to find the physical address of an entry in a page-table.
176const ADDRESS_MASK: u32 = !0xfff;
177const ADDRESS_MASK_PSE: u32 = !0x3fffff;
178
179/// Size of a base page (4 KiB)
180pub const BASE_PAGE_SIZE: usize = 4096;
181
182/// Page tables have 512 = 4096 / 32 entries.
183pub const PAGE_SIZE_ENTRIES: usize = 1024;
184
185/// A page directory.
186pub type PD = [PDEntry; PAGE_SIZE_ENTRIES];
187
188/// A page table.
189pub type PT = [PTEntry; PAGE_SIZE_ENTRIES];
190
191impl PDEntry {
192    /// Creates a new PDEntry.
193    ///
194    /// # Arguments
195    ///
196    ///  * `pt` - The physical address of the page table.
197    ///  * `flags`- Additional flags for the entry.
198    ///
199    /// # Implementation notes
200    ///
201    /// This doesn't support PSE-36 or PSE-40.
202    pub fn new(pt: PAddr, flags: LocalRegisterCopy<u32, PDFLAGS::Register>) -> PDEntry {
203        let mask = if flags.is_set(PDFLAGS::PS) {
204            ADDRESS_MASK_PSE
205        } else {
206            ADDRESS_MASK
207        };
208        let pt_val = pt & mask;
209        assert!(pt_val == pt.into());
210        assert!(pt % BASE_PAGE_SIZE == 0);
211        PDEntry(pt_val | flags.get())
212    }
213
214    /// Retrieves the physical address in this entry.
215    pub fn address(self) -> PAddr {
216        if self.flags().is_set(PDFLAGS::PS) {
217            PAddr::from(self.0 & ADDRESS_MASK_PSE)
218        } else {
219            PAddr::from(self.0 & ADDRESS_MASK)
220        }
221    }
222
223    /// Returns the flags corresponding to this entry.
224    pub fn flags(self) -> LocalRegisterCopy<u32, PDFLAGS::Register> {
225        LocalRegisterCopy::new(self.0)
226    }
227}
228
229/// A PT Entry consists of an address and a bunch of flags.
230#[repr(transparent)]
231#[derive(Clone, Copy)]
232pub struct PTEntry(pub u32);
233
234impl fmt::Debug for PTEntry {
235    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
236        write!(f, "PTEntry {{ {:#x}, {:?} }}", self.address(), self.flags())
237    }
238}
239
240impl PTEntry {
241    /// Creates a new PTEntry.
242    ///
243    /// # Arguments
244    ///
245    ///  * `page` - The physical address of the backing 4 KiB page.
246    ///  * `flags`- Additional flags for the entry.
247    pub fn new(page: PAddr, flags: LocalRegisterCopy<u32, PTFLAGS::Register>) -> PTEntry {
248        let page_val = page & ADDRESS_MASK;
249        assert!(page_val == page.into());
250        assert!(page % BASE_PAGE_SIZE == 0);
251        PTEntry(page_val | flags.get())
252    }
253
254    /// Retrieves the physical address in this entry.
255    pub fn address(self) -> PAddr {
256        PAddr::from(self.0 & ADDRESS_MASK)
257    }
258
259    /// Returns the flags corresponding to this entry.
260    pub fn flags(self) -> LocalRegisterCopy<u32, PTFLAGS::Register> {
261        LocalRegisterCopy::new(self.0)
262    }
263}