mirror of
https://github.com/hexagonal-sun/moss-kernel.git
synced 2026-04-21 15:48:39 -04:00
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.
This commit is contained in:
@@ -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
|
||||
},
|
||||
);
|
||||
|
||||
@@ -30,7 +30,7 @@ pub(super) trait TableMapperTable: PgTable<Descriptor: TableMapper> + 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<PgTableArray<Self>>) -> 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 {
|
||||
|
||||
@@ -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 << <L2Table as PgTable>::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 << <L1Table as PgTable>::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);
|
||||
|
||||
@@ -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),+ $(,)?) => {
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 << <L2Table as PgTable>::Descriptor::MAP_SHIFT)),
|
||||
L2Descriptor::new_next_table(ksym_pa!(self.l3[1])),
|
||||
&invalidator,
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user