From b9dc420aeee2f6eace92ec10d8a659583649ef7b Mon Sep 17 00:00:00 2001 From: Matthew Leach Date: Wed, 15 Apr 2026 16:34:52 +0100 Subject: [PATCH] libkernel: memory: paging: make PageTableEntry contain MAP_SHIFT We currently define the MAP_SHIFT for a page-table level in two places, `TableMapper` and `PgTable`. Store only one shift trait constant value in the `PageTableEntry` trait. --- .../src/arch/arm64/memory/pg_descriptors.rs | 23 +++---- libkernel/src/arch/arm64/memory/pg_tables.rs | 11 ++-- libkernel/src/arch/arm64/memory/pg_walk.rs | 8 +-- .../src/arch/x86_64/memory/pg_descriptors.rs | 65 +++++++++---------- libkernel/src/memory/paging/mod.rs | 11 ++-- src/arch/arm64/memory/fixmap.rs | 2 +- 6 files changed, 55 insertions(+), 65 deletions(-) diff --git a/libkernel/src/arch/arm64/memory/pg_descriptors.rs b/libkernel/src/arch/arm64/memory/pg_descriptors.rs index 9c84c6d..055d2f8 100644 --- a/libkernel/src/arch/arm64/memory/pg_descriptors.rs +++ b/libkernel/src/arch/arm64/memory/pg_descriptors.rs @@ -36,12 +36,12 @@ macro_rules! define_descriptor { ( $(#[$outer:meta])* $name:ident, + shift: $shift:literal, // Optional: Implement TableMapper if this section is present $( table: $table_bits:literal, )? // Optional: Implement PaMapper if this section is present $( map: { bits: $map_bits:literal, - shift: $tbl_shift:literal, oa_len: $oa_len:literal, }, )? @@ -54,6 +54,7 @@ macro_rules! define_descriptor { impl PageTableEntry for $name { type RawDescriptor = u64; const INVALID: u64 = 0; + const MAP_SHIFT: usize = $shift; fn is_valid(self) -> bool { (self.0 & 0b11) != 0 } fn as_raw(self) -> Self::RawDescriptor { self.0 } @@ -91,7 +92,7 @@ macro_rules! define_descriptor { XN OFFSET(54) NUMBITS(1) [ NotExecutable = 1, Executable = 0 ], // Software defined bit COW OFFSET(55) NUMBITS(1) [ CowShared = 1, NotCowShared = 0 ], - OUTPUT_ADDR OFFSET($tbl_shift) NUMBITS($oa_len) [] + OUTPUT_ADDR OFFSET($shift) NUMBITS($oa_len) [] ] ]; } @@ -163,17 +164,16 @@ macro_rules! define_descriptor { impl PaMapper for $name { type MemoryType = MemoryType; - const MAP_SHIFT: usize = $tbl_shift; fn could_map(region: PhysMemoryRegion, va: VA) -> bool { - let is_aligned = |addr: usize| (addr & ((1 << $tbl_shift) - 1)) == 0; + let is_aligned = |addr: usize| (addr & ((1 << Self::MAP_SHIFT) - 1)) == 0; is_aligned(region.start_address().value()) && is_aligned(va.value()) - && region.size() >= (1 << $tbl_shift) + && region.size() >= (1 << Self::MAP_SHIFT) } fn new_map_pa(page_address: PA, memory_type: MemoryType, perms: PtePermissions) -> Self { - let is_aligned = |addr: usize| (addr & ((1 << $tbl_shift) - 1)) == 0; + let is_aligned = |addr: usize| (addr & ((1 << Self::MAP_SHIFT) - 1)) == 0; if !is_aligned(page_address.value()) { panic!("Cannot map non-aligned physical address"); } @@ -181,7 +181,7 @@ macro_rules! define_descriptor { let reg = InMemoryRegister::new(0); use [<$name Fields>]::BlockPageFields; - reg.modify(BlockPageFields::OUTPUT_ADDR.val((page_address.value() >> $tbl_shift) as u64) + reg.modify(BlockPageFields::OUTPUT_ADDR.val((page_address.value() >> Self::MAP_SHIFT) as u64) + BlockPageFields::AF::Accessed); match memory_type { @@ -209,7 +209,7 @@ macro_rules! define_descriptor { let reg = InMemoryRegister::new(self.0); let addr = reg.read(BlockPageFields::OUTPUT_ADDR); - Some(PA::from_value((addr << $tbl_shift) as usize)) + Some(PA::from_value((addr << Self::MAP_SHIFT) as usize)) } } } @@ -220,16 +220,17 @@ macro_rules! define_descriptor { define_descriptor!( /// A Level 0 descriptor. Can only be an invalid or table descriptor. L0Descriptor, + shift: 39, table: 0b11, ); define_descriptor!( /// A Level 1 descriptor. Can be a block, table, or invalid descriptor. L1Descriptor, + shift: 30, table: 0b11, map: { bits: 0b01, // L1 Block descriptor has bits[1:0] = 01 - shift: 30, // Maps a 1GiB block oa_len: 18, // Output address length for 48-bit PA }, ); @@ -237,10 +238,10 @@ define_descriptor!( define_descriptor!( /// A Level 2 descriptor. Can be a block, table, or invalid descriptor. L2Descriptor, + shift: 21, table: 0b11, map: { bits: 0b01, // L2 Block descriptor has bits[1:0] = 01 - shift: 21, // Maps a 2MiB block oa_len: 27, // Output address length for 48-bit PA }, ); @@ -248,10 +249,10 @@ define_descriptor!( define_descriptor!( /// A Level 3 descriptor. Can be a page or invalid descriptor. L3Descriptor, + shift: 12, // Note: No 'table' capability at L3. map: { bits: 0b11, // L3 Page descriptor has bits[1:0] = 11 - shift: 12, // Maps a 4KiB page oa_len: 36, // Output address length for 48-bit PA }, ); diff --git a/libkernel/src/arch/arm64/memory/pg_tables.rs b/libkernel/src/arch/arm64/memory/pg_tables.rs index 218a5c0..7a3e40c 100644 --- a/libkernel/src/arch/arm64/memory/pg_tables.rs +++ b/libkernel/src/arch/arm64/memory/pg_tables.rs @@ -30,7 +30,7 @@ pub(super) trait TableMapperTable: PgTable + Clone + Co } macro_rules! impl_pgtable { - ($(#[$outer:meta])* $table:ident, $shift:expr, $desc_type:ident) => { + ($(#[$outer:meta])* $table:ident, $desc_type:ident) => { #[derive(Clone, Copy)] $(#[$outer])* pub struct $table { @@ -38,7 +38,6 @@ macro_rules! impl_pgtable { } impl PgTable for $table { - const SHIFT: usize = $shift; type Descriptor = $desc_type; fn from_ptr(ptr: TVA>) -> Self { @@ -73,25 +72,25 @@ macro_rules! impl_pgtable { } impl_pgtable!(/// Level 0 page table (512 GiB per entry). - L0Table, 39, L0Descriptor); + L0Table, L0Descriptor); impl TableMapperTable for L0Table { type NextLevel = L1Table; } impl_pgtable!(/// Level 1 page table (1 GiB per entry). - L1Table, 30, L1Descriptor); + L1Table, L1Descriptor); impl TableMapperTable for L1Table { type NextLevel = L2Table; } impl_pgtable!(/// Level 2 page table (2 MiB per entry). - L2Table, 21, L2Descriptor); + L2Table, L2Descriptor); impl TableMapperTable for L2Table { type NextLevel = L3Table; } impl_pgtable!(/// Level 3 page table (4 KiB per entry). - L3Table, 12, L3Descriptor); + L3Table, L3Descriptor); /// Describes the attributes of a memory range to be mapped. pub struct MapAttributes { diff --git a/libkernel/src/arch/arm64/memory/pg_walk.rs b/libkernel/src/arch/arm64/memory/pg_walk.rs index 2a353df..61c907c 100644 --- a/libkernel/src/arch/arm64/memory/pg_walk.rs +++ b/libkernel/src/arch/arm64/memory/pg_walk.rs @@ -55,13 +55,13 @@ where PM: PageTableMapper, F: FnMut(VA, L3Descriptor) -> L3Descriptor, { - let table_coverage = 1 << T::SHIFT; + let table_coverage = 1 << T::Descriptor::MAP_SHIFT; let start_idx = Self::pg_index(region.start_address()); let end_idx = Self::pg_index(region.end_address_inclusive()); // Calculate the base address of the *entire* table. - let table_base_va = region.start_address().align(1 << (T::SHIFT + 9)); + let table_base_va = region.start_address().align(1 << (T::Descriptor::MAP_SHIFT + 9)); for idx in start_idx..=end_idx { let entry_va = table_base_va.add_bytes(idx * table_coverage); @@ -269,7 +269,7 @@ mod tests { // This VA range will cross an L2 entry boundary, forcing a walk over // two L3 tables. L2 entry covers 2MiB. Let's map a region around a 2MiB // boundary. - let l2_boundary = 1 << L2Table::SHIFT; // 2MiB + let l2_boundary = 1 << ::Descriptor::MAP_SHIFT; // 2MiB let va_start = VA::from_value(l2_boundary - 5 * PAGE_SIZE); let num_pages = 10; let region = VirtMemoryRegion::new(va_start, num_pages * PAGE_SIZE); @@ -302,7 +302,7 @@ mod tests { fn walk_region_spanning_l2_tables() { let mut harness = TestHarness::new(6); // This VA range will cross an L1 entry boundary, forcing a walk over two L2 tables. - let l1_boundary = 1 << L1Table::SHIFT; // 1GiB + let l1_boundary = 1 << ::Descriptor::MAP_SHIFT; // 1GiB let va_start = VA::from_value(l1_boundary - 5 * PAGE_SIZE); let num_pages = 10; let region = VirtMemoryRegion::new(va_start, num_pages * PAGE_SIZE); diff --git a/libkernel/src/arch/x86_64/memory/pg_descriptors.rs b/libkernel/src/arch/x86_64/memory/pg_descriptors.rs index e018aab..488780e 100644 --- a/libkernel/src/arch/x86_64/memory/pg_descriptors.rs +++ b/libkernel/src/arch/x86_64/memory/pg_descriptors.rs @@ -27,11 +27,7 @@ macro_rules! define_descriptor { ( $(#[$outer:meta])* $name:ident, - // Optional: Implement PaMapper if this section is present - $( map: { - shift: $tbl_shift:literal, - }, - )? + shift: $shift:literal ) => { #[repr(transparent)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -41,6 +37,7 @@ macro_rules! define_descriptor { impl PageTableEntry for $name { type RawDescriptor = u64; const INVALID: Self::RawDescriptor = 0; + const MAP_SHIFT: usize = $shift; fn is_valid(self) -> bool { (self.0 & 0b1) != 0 } fn as_raw(self) -> u64 { self.0 } fn from_raw(v: u64) -> Self { Self(v) } @@ -117,12 +114,36 @@ macro_rules! define_descriptor { } } } + } +} +define_descriptor!( + /// A page-map level 4 entry descriptor. Can only be an invalid or table + /// descriptor. + PML4E, shift: 39 +); + +define_descriptor!( + /// A page directory pointer entry. Can be a block, table, or invalid descriptor. + PDPE, shift: 30 +); + +define_descriptor!( + /// A page directory entry. Can be a block, table, or invalid descriptor. + PDE, shift: 21 +); + +define_descriptor!( + /// A page table entry. Can be a page or invalid descriptor. + PTE, shift: 12 +); + +macro_rules! impl_pa_mapper { + ($($name:ident),+ $(,)?) => { $( paste! { impl PaMapper for $name { type MemoryType = MemoryType; - const MAP_SHIFT: usize = $tbl_shift; fn could_map(region: PhysMemoryRegion, va: VA) -> bool { let is_aligned = |addr: usize| (addr & ((1 << Self::MAP_SHIFT) - 1)) == 0; @@ -175,38 +196,10 @@ macro_rules! define_descriptor { } } )? - }; + } } -define_descriptor!( - /// A page-map level 4 entry descriptor. Can only be an invalid or table - /// descriptor. - PML4E, -); - -define_descriptor!( - /// A page directory pointer entry. Can be a block, table, or invalid descriptor. - PDPE, - map: { - shift: 30, // Maps a 1GiB block - }, -); - -define_descriptor!( - /// A page directory entry. Can be a block, table, or invalid descriptor. - PDE, - map: { - shift: 21, // Maps a 2MiB block - }, -); - -define_descriptor!( - /// A page table entry. Can be a page or invalid descriptor. - PTE, - map: { - shift: 12, // Maps a 4KiB page - }, -); +impl_pa_mapper!(PDPE, PDE, PTE); macro_rules! impl_table_mapper { ($($name:ident),+ $(,)?) => { diff --git a/libkernel/src/memory/paging/mod.rs b/libkernel/src/memory/paging/mod.rs index 40b056f..4d8a5a9 100644 --- a/libkernel/src/memory/paging/mod.rs +++ b/libkernel/src/memory/paging/mod.rs @@ -18,6 +18,9 @@ pub trait PageTableEntry: Sized + Copy + Clone { /// The raw value for an invalid (not present) descriptor. const INVALID: Self::RawDescriptor; + /// The VA shift used for indexing at this descriptor level. + const MAP_SHIFT: usize; + /// Returns `true` if the entry is valid (i.e., not an Invalid/Fault entry). fn is_valid(self) -> bool; @@ -48,9 +51,6 @@ pub trait PaMapper: PageTableEntry { /// The memory attribute type for this descriptor's architecture. type MemoryType: Copy; - /// How many bytes this descriptor type maps. - const MAP_SHIFT: usize; - /// Constructs a new valid page descriptor that maps a physical address. fn new_map_pa(page_address: PA, memory_type: Self::MemoryType, perms: PtePermissions) -> Self; @@ -97,9 +97,6 @@ pub trait PgTable: Clone + Copy { /// Bitmask used to extract the page table index from a shifted virtual address. const LEVEL_MASK: usize = Self::DESCRIPTORS_PER_PAGE - 1; - /// Bit shift used to extract the index for this page table level. - const SHIFT: usize; - /// The descriptor (page table entry) type for this level. type Descriptor: PageTableEntry; @@ -111,7 +108,7 @@ pub trait PgTable: Clone + Copy { /// Compute the index into this page table from a virtual address. fn pg_index(va: VA) -> usize { - (va.value() >> Self::SHIFT) & Self::LEVEL_MASK + (va.value() >> Self::Descriptor::MAP_SHIFT) & Self::LEVEL_MASK } /// Get the descriptor for a given virtual address. diff --git a/src/arch/arm64/memory/fixmap.rs b/src/arch/arm64/memory/fixmap.rs index 28ced7f..5c86e15 100644 --- a/src/arch/arm64/memory/fixmap.rs +++ b/src/arch/arm64/memory/fixmap.rs @@ -102,7 +102,7 @@ impl Fixmap { ); L2Table::from_ptr(TVA::from_ptr(&mut self.l2 as *mut _)).set_desc( - VA::from_value(FIXMAP_BASE.value() + (1 << L2Table::SHIFT)), + VA::from_value(FIXMAP_BASE.value() + (1 << ::Descriptor::MAP_SHIFT)), L2Descriptor::new_next_table(ksym_pa!(self.l3[1])), &invalidator, );