Skip to main content

page_table_entry/arch/
arm.rs

1//! ARMv7-A Short-descriptor translation table format.
2//!
3//! This module implements page table entries for ARMv7-A architecture using
4//! the Short-descriptor format, which supports 2-level page tables:
5//! - L1 (Translation Table): 4096 entries, each mapping 1MB or pointing to L2
6//! - L2 (Page Table): 256 entries, each mapping 4KB (Small Page)
7
8use core::fmt;
9
10use memory_addr::PhysAddr;
11
12use crate::{GenericPTE, MappingFlags};
13
14bitflags::bitflags! {
15    /// ARMv7-A Short-descriptor page table entry flags.
16    ///
17    /// Reference: ARM Architecture Reference Manual ARMv7-A/R Edition
18    /// Section B3.5: Short-descriptor translation table format
19    #[derive(Debug, Clone, Copy)]
20    pub struct DescriptorAttr: u32 {
21        // Common bits for all descriptor types
22
23        /// Bit[0]: Descriptor type bit 0
24        /// Combined with bit[1] to determine descriptor type:
25        /// - 00: Invalid/Fault
26        /// - 01: Page Table (L1) or Large Page (L2)
27        /// - 10: Section (L1) or Small Page (L2)
28        /// - 11: Section (L1, PXN enabled) or Small Page (L2)
29        const TYPE_BIT0 = 1 << 0;
30
31        /// Bit[1]: Descriptor type bit 1
32        const TYPE_BIT1 = 1 << 1;
33
34        // Section/Page specific attributes (bits [2-17])
35
36        /// Bit[2]: Bufferable (B) - Part of memory type encoding
37        const B = 1 << 2;
38
39        /// Bit[3]: Cacheable (C) - Part of memory type encoding
40        const C = 1 << 3;
41
42        /// Bit[4]: Execute Never (XN) for Small Pages
43        const XN_SMALL = 1 << 4;
44
45        /// Bits[5:4]: Domain for Sections (only applies to L1 Section entries)
46        const DOMAIN_MASK = 0b1111 << 5;
47
48        /// Bit[9]: Implementation defined
49        const IMP = 1 << 9;
50
51        /// Bits[11:10]: Access Permission bits [1:0]
52        /// AP[2:0] encoding (with AP[2] from bit 15):
53        /// - 000: No access
54        /// - 001: Privileged RW, User no access
55        /// - 010: Privileged RW, User RO
56        /// - 011: Privileged RW, User RW
57        /// - 101: Privileged RO, User no access
58        /// - 110: Privileged RO, User RO (deprecated)
59        /// - 111: Privileged RO, User RO
60        const AP0 = 1 << 10;
61        /// Bit[11]: Access Permission bit 1
62        const AP1 = 1 << 11;
63
64        /// Bits[14:12]: Type Extension (TEX) - Extended memory type
65        const TEX0 = 1 << 12;
66        /// Bit[13]: Type Extension bit 1
67        const TEX1 = 1 << 13;
68        /// Bit[14]: Type Extension bit 2
69        const TEX2 = 1 << 14;
70
71        /// Bit[15]: Access Permission bit [2]
72        const AP2 = 1 << 15;
73
74        /// Bit[16]: Shareable (S)
75        const S = 1 << 16;
76
77        /// Bit[17]: Not Global (nG)
78        const NG = 1 << 17;
79
80        /// Bit[18]: For Section: Not Secure (NS)
81        const NS = 1 << 19;
82
83        // Combined flags for common use cases
84
85        /// Section descriptor type (1MB block)
86        const SECTION = Self::TYPE_BIT1.bits();
87
88        /// Page table descriptor type (points to L2)
89        const PAGE_TABLE = Self::TYPE_BIT0.bits();
90
91        /// Small page descriptor type (4KB page in L2)
92        const SMALL_PAGE = Self::TYPE_BIT1.bits();
93
94        /// Normal memory attributes (Inner/Outer Write-Back, Write-Allocate, Cacheable)
95        /// TEX=001, C=1, B=1 -> Normal memory, Cacheable
96        const NORMAL_MEMORY = Self::TEX0.bits() | Self::C.bits() | Self::B.bits();
97
98        /// Device memory attributes (Device, nGnRnE)
99        /// TEX=000, C=0, B=1 -> Device memory
100        const DEVICE_MEMORY = Self::B.bits();
101
102        /// Shareable attribute for normal memory
103        const NORMAL_SHAREABLE = Self::NORMAL_MEMORY.bits() | Self::S.bits();
104
105        /// Access permission: Privileged RW, User no access
106        const AP_PRIV_RW = Self::AP0.bits();
107
108        /// Access permission: Privileged RW, User RW
109        const AP_USER_RW = Self::AP0.bits() | Self::AP1.bits();
110
111        /// Access permission: Privileged RO, User no access
112        const AP_PRIV_RO = Self::AP2.bits() | Self::AP0.bits();
113
114        /// Access permission: Privileged RO, User RO
115        const AP_USER_RO = Self::AP2.bits() | Self::AP0.bits() | Self::AP1.bits();
116    }
117}
118
119impl DescriptorAttr {
120    /// Returns the common flags for both Section (L1) and Small Page (L2)
121    /// descriptors.
122    #[inline]
123    pub const fn common_flags(flags: MappingFlags) -> u32 {
124        let mut bits = 0;
125
126        // Memory type
127        if flags.contains(MappingFlags::DEVICE) {
128            bits |= Self::DEVICE_MEMORY.bits();
129        } else if flags.contains(MappingFlags::UNCACHED) {
130            // Uncached normal memory: TEX=001, C=0, B=0
131            bits |= Self::TEX0.bits();
132        } else {
133            // Normal cacheable memory with shareable
134            bits |= Self::NORMAL_SHAREABLE.bits();
135        }
136
137        // Access permissions
138        let has_write = flags.contains(MappingFlags::WRITE);
139        let has_user = flags.contains(MappingFlags::USER);
140
141        if has_user {
142            if has_write {
143                bits |= Self::AP_USER_RW.bits();
144            } else {
145                bits |= Self::AP_USER_RO.bits();
146            }
147        } else if has_write {
148            bits |= Self::AP_PRIV_RW.bits();
149        } else {
150            bits |= Self::AP_PRIV_RO.bits();
151        }
152
153        bits
154    }
155
156    /// Creates descriptor attributes from MappingFlags for a Section (1MB).
157    #[inline]
158    pub const fn from_mapping_flags_section(flags: MappingFlags) -> Self {
159        let mut bits = Self::SECTION.bits();
160
161        if flags.is_empty() {
162            return Self::from_bits_retain(0);
163        }
164
165        bits |= Self::common_flags(flags);
166
167        // Execute Never is in bit 4 for Sections when using LPAE
168        // For standard ARMv7-A, XN is controlled via domain + TEX[0]
169        // We use simplified model: if not executable, set appropriate bits
170        if !flags.contains(MappingFlags::EXECUTE) {
171            // XN for sections: can be indicated via domain or TEX settings
172            // Here we assume XN support via bit 4 for supersection or AP
173            // settings
174            bits |= Self::XN_SMALL.bits();
175        }
176
177        Self::from_bits_retain(bits)
178    }
179
180    /// Creates descriptor attributes from MappingFlags for a Small Page (4KB).
181    #[inline]
182    pub const fn from_mapping_flags_small_page(flags: MappingFlags) -> Self {
183        let mut bits = Self::SMALL_PAGE.bits();
184
185        if flags.is_empty() {
186            return Self::from_bits_retain(0);
187        }
188
189        let attr = Self::common_flags(flags);
190
191        // B(2) and C(3) are at the same positions for L1 and L2
192        bits |= attr & (Self::B.bits() | Self::C.bits());
193
194        // Other attributes are shifted right by 6 bits for L2 Small Page:
195        // - AP[1:0]: 11:10 -> 5:4
196        // - TEX[2:0]: 14:12 -> 8:6
197        // - AP[2]: 15 -> 9
198        // - S: 16 -> 10
199        // - nG: 17 -> 11
200        // Mask covers bits 10 to 17
201        let shift_mask = Self::AP0.bits()
202            | Self::AP1.bits()
203            | Self::TEX0.bits()
204            | Self::TEX1.bits()
205            | Self::TEX2.bits()
206            | Self::AP2.bits()
207            | Self::S.bits()
208            | Self::NG.bits();
209
210        bits |= (attr & shift_mask) >> 6;
211
212        // Execute Never for Small Pages (XN is bit 0 in L2)
213        if !flags.contains(MappingFlags::EXECUTE) {
214            bits |= Self::TYPE_BIT0.bits();
215        }
216
217        Self::from_bits_retain(bits)
218    }
219
220    /// Returns the descriptor type.
221    pub const fn descriptor_type(&self) -> u32 {
222        self.bits() & 0b11
223    }
224
225    /// Checks if this is a valid descriptor.
226    pub const fn is_valid(&self) -> bool {
227        self.descriptor_type() != 0
228    }
229
230    /// Checks if this is a Section descriptor (L1).
231    pub const fn is_section(&self) -> bool {
232        self.descriptor_type() == 0b10
233    }
234
235    /// Checks if this is a Page Table descriptor (L1).
236    pub const fn is_page_table(&self) -> bool {
237        self.descriptor_type() == 0b01
238    }
239
240    /// Checks if this is a Small Page descriptor (L2).
241    pub const fn is_small_page(&self) -> bool {
242        self.descriptor_type() == 0b10
243    }
244}
245
246impl From<DescriptorAttr> for MappingFlags {
247    #[inline]
248    fn from(attr: DescriptorAttr) -> Self {
249        if !attr.is_valid() {
250            return Self::empty();
251        }
252
253        let mut flags = Self::READ;
254
255        // Check write permission from AP bits
256        let ap = ((attr.bits() >> 10) & 0b11) | (((attr.bits() >> 15) & 1) << 2);
257        match ap {
258            0b001 | 0b011 => flags |= Self::WRITE, // Privileged RW or User RW
259            _ => {}
260        }
261
262        // Check user access
263        if (ap & 0b10) != 0 {
264            flags |= Self::USER;
265        }
266
267        // Check execute permission (XN bit)
268        if !attr.contains(DescriptorAttr::XN_SMALL) {
269            flags |= Self::EXECUTE;
270        }
271
272        // Check memory type
273        let tex = (attr.bits() >> 12) & 0b111;
274        let c = (attr.bits() >> 3) & 1;
275        let b = (attr.bits() >> 2) & 1;
276
277        if tex == 0 && c == 0 && b == 1 {
278            flags |= Self::DEVICE;
279        } else if tex == 1 && c == 0 && b == 0 {
280            flags |= Self::UNCACHED;
281        }
282
283        flags
284    }
285}
286
287/// An ARMv7-A Short-descriptor page table entry (32-bit).
288///
289/// This can represent:
290/// - L1 Section descriptor (1MB mapping)
291/// - L1 Page Table descriptor (points to L2 table)
292/// - L2 Small Page descriptor (4KB mapping)
293#[derive(Clone, Copy)]
294#[repr(transparent)]
295pub struct A32PTE(u32);
296
297impl A32PTE {
298    /// Physical address mask for Section (bits [31:20] for 1MB alignment)
299    const SECTION_ADDR_MASK: u32 = 0xfff0_0000;
300
301    /// Physical address mask for Page Table (bits [31:10] for 1KB alignment)
302    const PAGE_TABLE_ADDR_MASK: u32 = 0xffff_fc00;
303
304    /// Physical address mask for Small Page (bits [31:12] for 4KB alignment)
305    const SMALL_PAGE_ADDR_MASK: u32 = 0xffff_f000;
306
307    /// Creates an empty descriptor with all bits set to zero.
308    pub const fn empty() -> Self {
309        Self(0)
310    }
311
312    /// Creates a Section descriptor (1MB block).
313    #[inline]
314    pub const fn new_section(paddr: PhysAddr, flags: MappingFlags) -> Self {
315        let attr = DescriptorAttr::from_mapping_flags_section(flags);
316        Self(attr.bits() | (paddr.as_usize() as u32 & Self::SECTION_ADDR_MASK))
317    }
318
319    /// Creates a Small Page descriptor (4KB page).
320    #[inline]
321    pub const fn new_small_page(paddr: PhysAddr, flags: MappingFlags) -> Self {
322        let attr = DescriptorAttr::from_mapping_flags_small_page(flags);
323        Self(attr.bits() | (paddr.as_usize() as u32 & Self::SMALL_PAGE_ADDR_MASK))
324    }
325
326    /// Returns the descriptor type field.
327    pub const fn descriptor_type(&self) -> u32 {
328        self.0 & 0b11
329    }
330
331    /// Checks if this is a Section descriptor.
332    pub const fn is_section(&self) -> bool {
333        (self.0 & 0b11) == 0b10
334    }
335}
336
337impl GenericPTE for A32PTE {
338    #[inline]
339    fn new_page(paddr: PhysAddr, flags: MappingFlags, is_huge: bool) -> Self {
340        if is_huge {
341            // 1MB Section
342            Self::new_section(paddr, flags)
343        } else {
344            // 4KB Small Page
345            Self::new_small_page(paddr, flags)
346        }
347    }
348
349    #[inline]
350    fn new_table(paddr: PhysAddr) -> Self {
351        // Page Table descriptor (L1 -> L2)
352        let attr = DescriptorAttr::PAGE_TABLE;
353        Self(attr.bits() | (paddr.as_usize() as u32 & Self::PAGE_TABLE_ADDR_MASK))
354    }
355
356    fn paddr(&self) -> PhysAddr {
357        let desc_type = self.descriptor_type();
358        let addr = match desc_type {
359            0b01 => self.0 & Self::PAGE_TABLE_ADDR_MASK, // Page Table
360            0b10 => {
361                if (self.0 & Self::SECTION_ADDR_MASK) >= 0x10_0000 {
362                    self.0 & Self::SECTION_ADDR_MASK // Section
363                } else {
364                    self.0 & Self::SMALL_PAGE_ADDR_MASK // Small Page
365                }
366            }
367            0b11 => self.0 & Self::SMALL_PAGE_ADDR_MASK, // Small Page (XN=1)
368            _ => 0,
369        };
370        PhysAddr::from(addr as usize)
371    }
372
373    fn flags(&self) -> MappingFlags {
374        DescriptorAttr::from_bits_truncate(self.0).into()
375    }
376
377    fn set_paddr(&mut self, paddr: PhysAddr) {
378        let desc_type = self.descriptor_type();
379        match desc_type {
380            0b01 => {
381                // Page Table
382                self.0 = (self.0 & !Self::PAGE_TABLE_ADDR_MASK)
383                    | (paddr.as_usize() as u32 & Self::PAGE_TABLE_ADDR_MASK);
384            }
385            0b10 => {
386                // Section or Small Page
387                if self.is_section() {
388                    self.0 = (self.0 & !Self::SECTION_ADDR_MASK)
389                        | (paddr.as_usize() as u32 & Self::SECTION_ADDR_MASK);
390                } else {
391                    self.0 = (self.0 & !Self::SMALL_PAGE_ADDR_MASK)
392                        | (paddr.as_usize() as u32 & Self::SMALL_PAGE_ADDR_MASK);
393                }
394            }
395            _ => {}
396        }
397    }
398
399    fn set_flags(&mut self, flags: MappingFlags, is_huge: bool) {
400        let paddr = self.paddr();
401        *self = if is_huge {
402            Self::new_section(paddr, flags)
403        } else {
404            Self::new_small_page(paddr, flags)
405        };
406    }
407
408    fn bits(self) -> usize {
409        self.0 as usize
410    }
411
412    fn is_unused(&self) -> bool {
413        self.0 == 0
414    }
415
416    fn is_present(&self) -> bool {
417        self.descriptor_type() != 0
418    }
419
420    fn is_huge(&self) -> bool {
421        self.is_section()
422    }
423
424    fn clear(&mut self) {
425        self.0 = 0;
426    }
427}
428
429impl fmt::Debug for A32PTE {
430    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
431        let mut f = f.debug_struct("A32PTE");
432        f.field("raw", &format_args!("{:#010x}", self.0))
433            .field(
434                "type",
435                &match self.descriptor_type() {
436                    0b00 => "Invalid",
437                    0b01 => "PageTable",
438                    0b10 => {
439                        if self.is_section() {
440                            "Section"
441                        } else {
442                            "SmallPage"
443                        }
444                    }
445                    0b11 => "Reserved",
446                    _ => "Unknown",
447                },
448            )
449            .field("paddr", &self.paddr())
450            .field("flags", &self.flags())
451            .finish()
452    }
453}
454
455#[cfg(test)]
456mod tests {
457    use super::*;
458
459    #[test]
460    fn test_section_descriptor() {
461        let paddr = PhysAddr::from(0x4000_0000);
462        let flags = MappingFlags::READ | MappingFlags::WRITE | MappingFlags::EXECUTE;
463        let pte = A32PTE::new_section(paddr, flags);
464
465        assert!(pte.is_present());
466        assert!(pte.is_huge());
467        assert_eq!(pte.paddr(), paddr);
468        assert!(pte.flags().contains(MappingFlags::READ));
469        assert!(pte.flags().contains(MappingFlags::WRITE));
470    }
471
472    #[test]
473    fn test_small_page_descriptor() {
474        let paddr = PhysAddr::from(0x4000_1000);
475        let flags = MappingFlags::READ | MappingFlags::WRITE;
476        let pte = A32PTE::new_small_page(paddr, flags);
477
478        assert!(pte.is_present());
479        assert_eq!(pte.paddr(), paddr);
480        assert!(pte.flags().contains(MappingFlags::READ));
481    }
482
483    #[test]
484    fn test_page_table_descriptor() {
485        let paddr = PhysAddr::from(0x4000_0400);
486        let pte = A32PTE::new_table(paddr);
487
488        assert!(pte.is_present());
489        assert!(!pte.is_huge());
490        assert_eq!(pte.paddr(), paddr);
491    }
492}