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 settings
173            bits |= Self::XN_SMALL.bits();
174        }
175
176        Self::from_bits_retain(bits)
177    }
178
179    /// Creates descriptor attributes from MappingFlags for a Small Page (4KB).
180    #[inline]
181    pub const fn from_mapping_flags_small_page(flags: MappingFlags) -> Self {
182        let mut bits = Self::SMALL_PAGE.bits();
183
184        if flags.is_empty() {
185            return Self::from_bits_retain(0);
186        }
187
188        let attr = Self::common_flags(flags);
189
190        // B(2) and C(3) are at the same positions for L1 and L2
191        bits |= attr & (Self::B.bits() | Self::C.bits());
192
193        // Other attributes are shifted right by 6 bits for L2 Small Page:
194        // - AP[1:0]: 11:10 -> 5:4
195        // - TEX[2:0]: 14:12 -> 8:6
196        // - AP[2]: 15 -> 9
197        // - S: 16 -> 10
198        // - nG: 17 -> 11
199        // Mask covers bits 10 to 17
200        let shift_mask = Self::AP0.bits()
201            | Self::AP1.bits()
202            | Self::TEX0.bits()
203            | Self::TEX1.bits()
204            | Self::TEX2.bits()
205            | Self::AP2.bits()
206            | Self::S.bits()
207            | Self::NG.bits();
208
209        bits |= (attr & shift_mask) >> 6;
210
211        // Execute Never for Small Pages (XN is bit 0 in L2)
212        if !flags.contains(MappingFlags::EXECUTE) {
213            bits |= Self::TYPE_BIT0.bits();
214        }
215
216        Self::from_bits_retain(bits)
217    }
218
219    /// Returns the descriptor type.
220    pub const fn descriptor_type(&self) -> u32 {
221        self.bits() & 0b11
222    }
223
224    /// Checks if this is a valid descriptor.
225    pub const fn is_valid(&self) -> bool {
226        self.descriptor_type() != 0
227    }
228
229    /// Checks if this is a Section descriptor (L1).
230    pub const fn is_section(&self) -> bool {
231        self.descriptor_type() == 0b10
232    }
233
234    /// Checks if this is a Page Table descriptor (L1).
235    pub const fn is_page_table(&self) -> bool {
236        self.descriptor_type() == 0b01
237    }
238
239    /// Checks if this is a Small Page descriptor (L2).
240    pub const fn is_small_page(&self) -> bool {
241        self.descriptor_type() == 0b10
242    }
243}
244
245impl From<DescriptorAttr> for MappingFlags {
246    #[inline]
247    fn from(attr: DescriptorAttr) -> Self {
248        if !attr.is_valid() {
249            return Self::empty();
250        }
251
252        let mut flags = Self::READ;
253
254        // Check write permission from AP bits
255        let ap = ((attr.bits() >> 10) & 0b11) | (((attr.bits() >> 15) & 1) << 2);
256        match ap {
257            0b001 | 0b011 => flags |= Self::WRITE, // Privileged RW or User RW
258            _ => {}
259        }
260
261        // Check user access
262        if (ap & 0b10) != 0 {
263            flags |= Self::USER;
264        }
265
266        // Check execute permission (XN bit)
267        if !attr.contains(DescriptorAttr::XN_SMALL) {
268            flags |= Self::EXECUTE;
269        }
270
271        // Check memory type
272        let tex = (attr.bits() >> 12) & 0b111;
273        let c = (attr.bits() >> 3) & 1;
274        let b = (attr.bits() >> 2) & 1;
275
276        if tex == 0 && c == 0 && b == 1 {
277            flags |= Self::DEVICE;
278        } else if tex == 1 && c == 0 && b == 0 {
279            flags |= Self::UNCACHED;
280        }
281
282        flags
283    }
284}
285
286/// An ARMv7-A Short-descriptor page table entry (32-bit).
287///
288/// This can represent:
289/// - L1 Section descriptor (1MB mapping)
290/// - L1 Page Table descriptor (points to L2 table)
291/// - L2 Small Page descriptor (4KB mapping)
292#[derive(Clone, Copy)]
293#[repr(transparent)]
294pub struct A32PTE(u32);
295
296impl A32PTE {
297    /// Physical address mask for Section (bits [31:20] for 1MB alignment)
298    const SECTION_ADDR_MASK: u32 = 0xfff0_0000;
299
300    /// Physical address mask for Page Table (bits [31:10] for 1KB alignment)
301    const PAGE_TABLE_ADDR_MASK: u32 = 0xffff_fc00;
302
303    /// Physical address mask for Small Page (bits [31:12] for 4KB alignment)
304    const SMALL_PAGE_ADDR_MASK: u32 = 0xffff_f000;
305
306    /// Creates an empty descriptor with all bits set to zero.
307    pub const fn empty() -> Self {
308        Self(0)
309    }
310
311    /// Creates a Section descriptor (1MB block).
312    #[inline]
313    pub const fn new_section(paddr: PhysAddr, flags: MappingFlags) -> Self {
314        let attr = DescriptorAttr::from_mapping_flags_section(flags);
315        Self(attr.bits() | (paddr.as_usize() as u32 & Self::SECTION_ADDR_MASK))
316    }
317
318    /// Creates a Small Page descriptor (4KB page).
319    #[inline]
320    pub const fn new_small_page(paddr: PhysAddr, flags: MappingFlags) -> Self {
321        let attr = DescriptorAttr::from_mapping_flags_small_page(flags);
322        Self(attr.bits() | (paddr.as_usize() as u32 & Self::SMALL_PAGE_ADDR_MASK))
323    }
324
325    /// Returns the descriptor type field.
326    pub const fn descriptor_type(&self) -> u32 {
327        self.0 & 0b11
328    }
329
330    /// Checks if this is a Section descriptor.
331    pub const fn is_section(&self) -> bool {
332        (self.0 & 0b11) == 0b10
333    }
334}
335
336impl GenericPTE for A32PTE {
337    #[inline]
338    fn new_page(paddr: PhysAddr, flags: MappingFlags, is_huge: bool) -> Self {
339        if is_huge {
340            // 1MB Section
341            Self::new_section(paddr, flags)
342        } else {
343            // 4KB Small Page
344            Self::new_small_page(paddr, flags)
345        }
346    }
347
348    #[inline]
349    fn new_table(paddr: PhysAddr) -> Self {
350        // Page Table descriptor (L1 -> L2)
351        let attr = DescriptorAttr::PAGE_TABLE;
352        Self(attr.bits() | (paddr.as_usize() as u32 & Self::PAGE_TABLE_ADDR_MASK))
353    }
354
355    fn paddr(&self) -> PhysAddr {
356        let desc_type = self.descriptor_type();
357        let addr = match desc_type {
358            0b01 => self.0 & Self::PAGE_TABLE_ADDR_MASK, // Page Table
359            0b10 => {
360                if (self.0 & Self::SECTION_ADDR_MASK) >= 0x10_0000 {
361                    self.0 & Self::SECTION_ADDR_MASK // Section
362                } else {
363                    self.0 & Self::SMALL_PAGE_ADDR_MASK // Small Page
364                }
365            }
366            0b11 => self.0 & Self::SMALL_PAGE_ADDR_MASK, // Small Page (XN=1)
367            _ => 0,
368        };
369        PhysAddr::from(addr as usize)
370    }
371
372    fn flags(&self) -> MappingFlags {
373        DescriptorAttr::from_bits_truncate(self.0).into()
374    }
375
376    fn set_paddr(&mut self, paddr: PhysAddr) {
377        let desc_type = self.descriptor_type();
378        match desc_type {
379            0b01 => {
380                // Page Table
381                self.0 = (self.0 & !Self::PAGE_TABLE_ADDR_MASK)
382                    | (paddr.as_usize() as u32 & Self::PAGE_TABLE_ADDR_MASK);
383            }
384            0b10 => {
385                // Section or Small Page
386                if self.is_section() {
387                    self.0 = (self.0 & !Self::SECTION_ADDR_MASK)
388                        | (paddr.as_usize() as u32 & Self::SECTION_ADDR_MASK);
389                } else {
390                    self.0 = (self.0 & !Self::SMALL_PAGE_ADDR_MASK)
391                        | (paddr.as_usize() as u32 & Self::SMALL_PAGE_ADDR_MASK);
392                }
393            }
394            _ => {}
395        }
396    }
397
398    fn set_flags(&mut self, flags: MappingFlags, is_huge: bool) {
399        let paddr = self.paddr();
400        *self = if is_huge {
401            Self::new_section(paddr, flags)
402        } else {
403            Self::new_small_page(paddr, flags)
404        };
405    }
406
407    fn bits(self) -> usize {
408        self.0 as usize
409    }
410
411    fn is_unused(&self) -> bool {
412        self.0 == 0
413    }
414
415    fn is_present(&self) -> bool {
416        self.descriptor_type() != 0
417    }
418
419    fn is_huge(&self) -> bool {
420        self.is_section()
421    }
422
423    fn clear(&mut self) {
424        self.0 = 0;
425    }
426}
427
428impl fmt::Debug for A32PTE {
429    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
430        let mut f = f.debug_struct("A32PTE");
431        f.field("raw", &format_args!("{:#010x}", self.0))
432            .field(
433                "type",
434                &match self.descriptor_type() {
435                    0b00 => "Invalid",
436                    0b01 => "PageTable",
437                    0b10 => {
438                        if self.is_section() {
439                            "Section"
440                        } else {
441                            "SmallPage"
442                        }
443                    }
444                    0b11 => "Reserved",
445                    _ => "Unknown",
446                },
447            )
448            .field("paddr", &self.paddr())
449            .field("flags", &self.flags())
450            .finish()
451    }
452}
453
454#[cfg(test)]
455mod tests {
456    use super::*;
457
458    #[test]
459    fn test_section_descriptor() {
460        let paddr = PhysAddr::from(0x4000_0000);
461        let flags = MappingFlags::READ | MappingFlags::WRITE | MappingFlags::EXECUTE;
462        let pte = A32PTE::new_section(paddr, flags);
463
464        assert!(pte.is_present());
465        assert!(pte.is_huge());
466        assert_eq!(pte.paddr(), paddr);
467        assert!(pte.flags().contains(MappingFlags::READ));
468        assert!(pte.flags().contains(MappingFlags::WRITE));
469    }
470
471    #[test]
472    fn test_small_page_descriptor() {
473        let paddr = PhysAddr::from(0x4000_1000);
474        let flags = MappingFlags::READ | MappingFlags::WRITE;
475        let pte = A32PTE::new_small_page(paddr, flags);
476
477        assert!(pte.is_present());
478        assert_eq!(pte.paddr(), paddr);
479        assert!(pte.flags().contains(MappingFlags::READ));
480    }
481
482    #[test]
483    fn test_page_table_descriptor() {
484        let paddr = PhysAddr::from(0x4000_0400);
485        let pte = A32PTE::new_table(paddr);
486
487        assert!(pte.is_present());
488        assert!(!pte.is_huge());
489        assert_eq!(pte.paddr(), paddr);
490    }
491}