From f977d246136f2db88e96d4f677b036cf3dfae555 Mon Sep 17 00:00:00 2001 From: "Vineel Kovvuri[MSFT]" Date: Thu, 20 Aug 2026 16:13:15 -0700 Subject: [PATCH 1/2] Make PageTableHal a stateful (&self) trait By declaring only associated functions in the PageTableHal trait, it becomes impossible to cleanly define any kind of state that can be controlled for testing purposes. Also, the current usage of these associated functions is not truly stateless. Instead, their state is hidden behind the hardware registers. This may be fine for the non test code, where the generic Arch type parameter bounded by PageTableHal is passed all the way from PageTable to PageTableHal implicitly. But its usage complicates testing by forcing hacky global statics, and on top of that, to keep those statics from pounding on each other forces to serialize the tests with `serial_test`. This commit converts every PageTableHal method from an associated function to a &self method so tests can carry per instance state instead of relying on globals. The arch instance is now plumbed explicitly through the paging core rather than being invoked purely by generic type. No functional change to mapping/query/unmap behavior. Signed-off-by: Vineel Kovvuri[MSFT] --- src/aarch64.rs | 79 ++++++---- src/arch.rs | 20 +-- src/paging.rs | 263 +++++++++++++++++++------------ src/tests/paging_tests.rs | 239 +++++++++++++++------------- src/tests/test_page_allocator.rs | 17 +- src/x64.rs | 46 +++--- 6 files changed, 381 insertions(+), 283 deletions(-) diff --git a/src/aarch64.rs b/src/aarch64.rs index 2856ac2..a4a1a9e 100644 --- a/src/aarch64.rs +++ b/src/aarch64.rs @@ -78,6 +78,7 @@ const TCR_EL1_DEFAULTS: u64 = TCR_ORGN0_WB_WA | TCR_IRGN0_WB_WA | TCR_SH0_INNER_SHAREABLE | TCR_T0SZ_48_BIT_VA | TCR_EL1_TG1_16KB | TCR_EL1_ED1; pub struct AArch64PageTable { + arch: PageTableArchAArch64, internal: PageTableInternal, } @@ -86,8 +87,9 @@ impl AArch64PageTable

{ if paging_type == PagingType::Paging5Level { return Err(PtError::UnsupportedPagingType); } - let internal = PageTableInternal::new(page_allocator, paging_type)?; - Ok(Self { internal }) + let arch = PageTableArchAArch64; + let internal = PageTableInternal::new(page_allocator, &arch, paging_type)?; + Ok(Self { arch, internal }) } /// Create a page table from existing page table base. This can be used to @@ -100,8 +102,9 @@ impl AArch64PageTable

{ /// safety of that base. /// pub unsafe fn from_existing(base: u64, page_allocator: P, paging_type: PagingType) -> Result { - let internal = unsafe { PageTableInternal::from_existing(base, page_allocator, paging_type)? }; - Ok(Self { internal }) + let arch = PageTableArchAArch64; + let internal = unsafe { PageTableInternal::from_existing(page_allocator, &arch, base, paging_type)? }; + Ok(Self { arch, internal }) } /// Consumes the page table structure and returns the page table root. @@ -123,7 +126,7 @@ impl AArch64PageTable

{ /// The crate's reserved self-map and zero-VA root entries are skipped so the /// iterator only reports genuine mappings. pub fn iter_mapped_regions(&self, start_address: Option) -> impl Iterator + '_ { - self.internal.iter_mapped_regions(start_address) + self.internal.iter_mapped_regions(&self.arch, start_address) } /// Opens a page table manager for the currently active page tables. @@ -178,23 +181,23 @@ impl PageTable for AArch64PageTable

{ size: u64, attributes: crate::MemoryAttributes, ) -> Result<(), PtError> { - self.internal.map_memory_region(address, size, attributes) + self.internal.map_memory_region(&self.arch, address, size, attributes) } fn unmap_memory_region(&mut self, address: u64, size: u64) -> Result<(), PtError> { - self.internal.unmap_memory_region(address, size) + self.internal.unmap_memory_region(&self.arch, address, size) } fn install_page_table(&mut self) -> Result<(), PtError> { - self.internal.install_page_table() + self.internal.install_page_table(&self.arch) } fn query_memory_region(&self, address: u64, size: u64) -> Result { - self.internal.query_memory_region(address, size) + self.internal.query_memory_region(&self.arch, address, size) } fn dump_page_tables(&self, address: u64, size: u64) -> Result<(), PtError> { - self.internal.dump_page_tables(address, size) + self.internal.dump_page_tables(&self.arch, address, size) } } @@ -208,42 +211,42 @@ impl PageTableHal for PageTableArchAArch64 { /// SAFETY: This function is unsafe because it directly manipulates the page table memory at the given base address /// to zero it. The caller must ensure that the base address is valid and points to a page table that can be /// safely zeroed. - unsafe fn zero_page(base: VirtualAddress) { + unsafe fn zero_page(&self, base: VirtualAddress) { unsafe { reg::zero_page(base.into()) }; } - fn paging_type_supported(paging_type: crate::PagingType) -> Result<(), PtError> { + fn paging_type_supported(&self, paging_type: crate::PagingType) -> Result<(), PtError> { match paging_type { crate::PagingType::Paging4Level | crate::PagingType::Paging5Level => Ok(()), } } - fn get_zero_va(paging_type: crate::PagingType) -> Result { + fn get_zero_va(&self, paging_type: crate::PagingType) -> Result { match paging_type { crate::PagingType::Paging4Level => Ok(ZERO_VA_4_LEVEL.into()), crate::PagingType::Paging5Level => Err(PtError::UnsupportedPagingType), } } - fn invalidate_tlb(va: VirtualAddress) { + fn invalidate_tlb(&self, va: VirtualAddress) { reg::update_translation_table_entry(0, va.into()); } - fn get_max_va(page_type: crate::PagingType) -> Result { + fn get_max_va(&self, page_type: crate::PagingType) -> Result { match page_type { crate::PagingType::Paging4Level => Ok(MAX_VA_4_LEVEL.into()), crate::PagingType::Paging5Level => Ok(MAX_VA_5_LEVEL.into()), } } - fn is_table_active(base: u64) -> bool { + fn is_table_active(&self, base: u64) -> bool { reg::is_this_page_table_active(base.into()) } /// SAFETY: This function is unsafe because it updates the HW page table registers to install a new page table. /// The caller must ensure that the base address is valid and points to a properly constructed page table. #[cfg_attr(coverage, coverage(off))] // This manipulates hardware registers that can't be meaningfully tested. - unsafe fn install_page_table(base: u64, paging_type: PagingType) -> Result<(), PtError> { + unsafe fn install_page_table(&self, base: u64, paging_type: PagingType) -> Result<(), PtError> { if paging_type != PagingType::Paging4Level { log::error!("Only 4-level page tables are supported on AArch64"); return Err(PtError::UnsupportedPagingType); @@ -300,7 +303,7 @@ impl PageTableHal for PageTableArchAArch64 { Ok(()) } - fn level_supports_pa_entry(level: PageLevel) -> bool { + fn level_supports_pa_entry(&self, level: PageLevel) -> bool { matches!(level, PageLevel::Level3 | PageLevel::Level2 | PageLevel::Level1) } @@ -312,7 +315,7 @@ impl PageTableHal for PageTableArchAArch64 { /// covers 512GB of memory, each PDP entry covers 1GB of memory, each PD entry covers 2MB of memory, and /// each PT entry covers 4KB of memory, but when we recurse in the self map to a given level, we shift what /// each entry covers to be the size of the next level down for each recursion into the self map we did. - fn get_self_mapped_base(level: PageLevel, va: VirtualAddress, paging_type: PagingType) -> u64 { + fn get_self_mapped_base(&self, level: PageLevel, va: VirtualAddress, paging_type: PagingType) -> u64 { match paging_type { PagingType::Paging4Level => match level { PageLevel::Level5 => unimplemented!(), @@ -334,7 +337,7 @@ impl PageTableHal for PageTableArchAArch64 { } } - fn invalidate_tlb_all() { + fn invalidate_tlb_all(&self) { reg::invalidate_tlb(); } } @@ -345,29 +348,33 @@ mod hal_tests { #[test] fn test_paging_type_supported() { - assert!(PageTableArchAArch64::paging_type_supported(PagingType::Paging4Level).is_ok()); - assert!(PageTableArchAArch64::paging_type_supported(PagingType::Paging5Level).is_ok()); + let arch = PageTableArchAArch64; + assert!(arch.paging_type_supported(PagingType::Paging4Level).is_ok()); + assert!(arch.paging_type_supported(PagingType::Paging5Level).is_ok()); } #[test] fn test_get_zero_va() { - assert_eq!(PageTableArchAArch64::get_zero_va(PagingType::Paging4Level).unwrap(), ZERO_VA_4_LEVEL.into()); - assert!(PageTableArchAArch64::get_zero_va(PagingType::Paging5Level).is_err()); + let arch = PageTableArchAArch64; + assert_eq!(arch.get_zero_va(PagingType::Paging4Level).unwrap(), ZERO_VA_4_LEVEL.into()); + assert!(arch.get_zero_va(PagingType::Paging5Level).is_err()); } #[test] fn test_get_max_va() { - assert_eq!(PageTableArchAArch64::get_max_va(PagingType::Paging4Level).unwrap(), MAX_VA_4_LEVEL.into()); - assert_eq!(PageTableArchAArch64::get_max_va(PagingType::Paging5Level).unwrap(), MAX_VA_5_LEVEL.into()); + let arch = PageTableArchAArch64; + assert_eq!(arch.get_max_va(PagingType::Paging4Level).unwrap(), MAX_VA_4_LEVEL.into()); + assert_eq!(arch.get_max_va(PagingType::Paging5Level).unwrap(), MAX_VA_5_LEVEL.into()); } #[test] fn test_level_supports_pa_entry() { - assert!(!PageTableArchAArch64::level_supports_pa_entry(PageLevel::Level5)); - assert!(!PageTableArchAArch64::level_supports_pa_entry(PageLevel::Level4)); - assert!(PageTableArchAArch64::level_supports_pa_entry(PageLevel::Level3)); - assert!(PageTableArchAArch64::level_supports_pa_entry(PageLevel::Level2)); - assert!(PageTableArchAArch64::level_supports_pa_entry(PageLevel::Level1)); + let arch = PageTableArchAArch64; + assert!(!arch.level_supports_pa_entry(PageLevel::Level5)); + assert!(!arch.level_supports_pa_entry(PageLevel::Level4)); + assert!(arch.level_supports_pa_entry(PageLevel::Level3)); + assert!(arch.level_supports_pa_entry(PageLevel::Level2)); + assert!(arch.level_supports_pa_entry(PageLevel::Level1)); } #[test] @@ -380,20 +387,22 @@ mod hal_tests { #[test] fn test_get_self_mapped_base_4_level() { let va: VirtualAddress = 0u64.into(); + let arch = PageTableArchAArch64; + assert_eq!( - PageTableArchAArch64::get_self_mapped_base(PageLevel::Level4, va, PagingType::Paging4Level), + arch.get_self_mapped_base(PageLevel::Level4, va, PagingType::Paging4Level), FOUR_LEVEL_LEVEL4_SELF_MAP_BASE ); assert_eq!( - PageTableArchAArch64::get_self_mapped_base(PageLevel::Level3, va, PagingType::Paging4Level), + arch.get_self_mapped_base(PageLevel::Level3, va, PagingType::Paging4Level), FOUR_LEVEL_LEVEL3_SELF_MAP_BASE ); assert_eq!( - PageTableArchAArch64::get_self_mapped_base(PageLevel::Level2, va, PagingType::Paging4Level), + arch.get_self_mapped_base(PageLevel::Level2, va, PagingType::Paging4Level), FOUR_LEVEL_LEVEL2_SELF_MAP_BASE ); assert_eq!( - PageTableArchAArch64::get_self_mapped_base(PageLevel::Level1, va, PagingType::Paging4Level), + arch.get_self_mapped_base(PageLevel::Level1, va, PagingType::Paging4Level), FOUR_LEVEL_LEVEL1_SELF_MAP_BASE ); } diff --git a/src/arch.rs b/src/arch.rs index 2d15d92..3caed4f 100644 --- a/src/arch.rs +++ b/src/arch.rs @@ -19,18 +19,18 @@ pub(crate) trait PageTableHal { /// SAFETY: This function is unsafe because it directly manipulates the page table memory at the given base address /// to zero it. The caller must ensure that the base address is valid and points to a page table that can be /// safely zeroed. - unsafe fn zero_page(base: VirtualAddress); - fn paging_type_supported(paging_type: PagingType) -> Result<(), PtError>; - fn get_zero_va(paging_type: PagingType) -> Result; - fn invalidate_tlb(va: VirtualAddress); - fn invalidate_tlb_all(); - fn get_max_va(page_type: PagingType) -> Result; - fn is_table_active(base: u64) -> bool; + unsafe fn zero_page(&self, base: VirtualAddress); + fn paging_type_supported(&self, paging_type: PagingType) -> Result<(), PtError>; + fn get_zero_va(&self, paging_type: PagingType) -> Result; + fn invalidate_tlb(&self, va: VirtualAddress); + fn invalidate_tlb_all(&self); + fn get_max_va(&self, page_type: PagingType) -> Result; + fn is_table_active(&self, base: u64) -> bool; /// SAFETY: This function is unsafe because it updates the HW page table registers to install a new page table. /// The caller must ensure that the base address is valid and points to a properly constructed page table. - unsafe fn install_page_table(base: u64, paging_type: PagingType) -> Result<(), PtError>; - fn level_supports_pa_entry(level: PageLevel) -> bool; - fn get_self_mapped_base(level: PageLevel, va: VirtualAddress, paging_type: PagingType) -> u64; + unsafe fn install_page_table(&self, base: u64, paging_type: PagingType) -> Result<(), PtError>; + fn level_supports_pa_entry(&self, level: PageLevel) -> bool; + fn get_self_mapped_base(&self, level: PageLevel, va: VirtualAddress, paging_type: PagingType) -> u64; } pub(crate) trait PageTableEntry { diff --git a/src/paging.rs b/src/paging.rs index 584adf6..3ee4804 100644 --- a/src/paging.rs +++ b/src/paging.rs @@ -7,6 +7,8 @@ //! //! SPDX-License-Identifier: Apache-2.0 //! +#![allow(clippy::too_many_arguments)] + use core::{marker::PhantomData, slice}; use crate::{ @@ -37,12 +39,12 @@ pub struct PageTableInternal { base: PhysicalAddress, page_allocator: P, pub(crate) paging_type: PagingType, - _arch: PhantomData, + marker: PhantomData, } impl PageTableInternal { - pub fn new(mut page_allocator: P, paging_type: PagingType) -> Result { - Arch::paging_type_supported(paging_type)?; + pub fn new(mut page_allocator: P, arch: &Arch, paging_type: PagingType) -> Result { + arch.paging_type_supported(paging_type)?; let root_level = PageLevel::root_level(paging_type); // Allocate the top level page table @@ -58,16 +60,17 @@ impl PageTableInternal { // we have not installed this page table, we can't use our VA range to zero page or // rely on self-map, so we have to rely on the identity mapping for the root page - unsafe { Arch::zero_page(base.into()) }; + unsafe { arch.zero_page(base.into()) }; // SAFETY: We just allocated the page and the top level is zeroed so it is safe to use it. - let mut pt = unsafe { Self::from_existing(base, page_allocator, paging_type)? }; + let mut pt = unsafe { Self::from_existing(page_allocator, arch, base, paging_type)? }; let self_map_va = - VirtualAddress::new(Arch::get_self_mapped_base(root_level, VirtualAddress::new(0), paging_type)); + VirtualAddress::new(arch.get_self_mapped_base(root_level, VirtualAddress::new(0), paging_type)); // Setup the self-mapping for the top level page table. - let self_map_entry = get_entry::( + let self_map_entry = get_entry( + arch, root_level, paging_type, PageTableStateWithAddress::NotSelfMapped(pt.base), @@ -80,15 +83,16 @@ impl PageTableInternal { // Setup the zero VA entry to allow for zeroing pages before putting them in the page table. let mut table_base = pt.base; let mut level = root_level; - let zero_va = Arch::get_zero_va(paging_type)?; + let zero_va = arch.get_zero_va(paging_type)?; let mut index = ZERO_VA_INDEX as usize; while let Some(next_level) = level.next_level() { let new_table = pt.page_allocator.allocate_page(PAGE_SIZE, PAGE_SIZE, false)?; // SAFETY: We just allocated the page, so it is safe to use it. - unsafe { Arch::zero_page(new_table.into()) }; + unsafe { arch.zero_page(new_table.into()) }; - let entry = get_entry::( + let entry = get_entry( + arch, level, paging_type, PageTableStateWithAddress::NotSelfMapped(table_base), @@ -105,7 +109,7 @@ impl PageTableInternal { // Create the leaf zero VA entry. let entry = - get_entry::(level, paging_type, PageTableStateWithAddress::NotSelfMapped(table_base), index as u64)?; + get_entry(arch, level, paging_type, PageTableStateWithAddress::NotSelfMapped(table_base), index as u64)?; entry.update_fields(Arch::DEFAULT_ATTRIBUTES, PhysicalAddress::new(0), true, level, zero_va)?; entry.set_present_bit(false, zero_va); @@ -121,15 +125,20 @@ impl PageTableInternal { /// PFNs in the provided base, so that caller is responsible for ensuring /// safety of that base. /// - pub unsafe fn from_existing(base: u64, page_allocator: P, paging_type: PagingType) -> Result { - Arch::paging_type_supported(paging_type)?; + pub unsafe fn from_existing( + page_allocator: P, + arch: &Arch, + base: u64, + paging_type: PagingType, + ) -> Result { + arch.paging_type_supported(paging_type)?; let base = PhysicalAddress::new(base); if !base.is_page_aligned() { return Err(PtError::UnalignedPageBase); } - Ok(Self { base, page_allocator, paging_type, _arch: PhantomData }) + Ok(Self { base, page_allocator, paging_type, marker: PhantomData }) } /// Consumes the page table structure and returns the page table root. @@ -137,7 +146,7 @@ impl PageTableInternal { self.base.into() } - pub fn allocate_page(&mut self, state: PageTableState) -> Result { + pub fn allocate_page(&mut self, arch: &Arch, state: PageTableState) -> Result { let base = self.page_allocator.allocate_page(PAGE_SIZE, PAGE_SIZE, false)?; let base_pa = PhysicalAddress::new(base); if !base_pa.is_page_aligned() { @@ -150,19 +159,15 @@ impl PageTableInternal { // entries in the page table. let zero_va = match state { PageTableState::ActiveSelfMapped => { - let va = Arch::get_zero_va(self.paging_type)?; + let va = arch.get_zero_va(self.paging_type)?; // if we have set up the zero VA, we need to map the PA we just allocated into this range to zero it // as we are relying on the self map to map these pages and we want to ensure break before make // semantics. // the page_base doesn't matter here because we don't use it in self-map mode, but let's still set // the right address in case it gets used in the future and it is easy to persist - let zero_entry = get_entry::( - PageLevel::Level1, - self.paging_type, - PageTableStateWithAddress::SelfMapped(va), - 0, - )?; + let zero_entry = + get_entry(arch, PageLevel::Level1, self.paging_type, PageTableStateWithAddress::SelfMapped(va), 0)?; zero_entry.update_fields( Arch::DEFAULT_ATTRIBUTES | MemoryAttributes::ExecuteProtect, @@ -172,7 +177,7 @@ impl PageTableInternal { va, )?; - Arch::invalidate_tlb(va); + arch.invalidate_tlb(va); va } @@ -183,7 +188,7 @@ impl PageTableInternal { // SAFETY: We just allocated the page and we have set up the zero VA to point to it or are relying on the // contract that the caller has this page mapped, so it is safe to zero it. - unsafe { Arch::zero_page(zero_va) }; + unsafe { arch.zero_page(zero_va) }; Ok(base_pa) } @@ -241,6 +246,7 @@ impl PageTableInternal { fn map_memory_region_internal( &mut self, + arch: &Arch, start_va: VirtualAddress, end_va: VirtualAddress, level: PageLevel, @@ -254,7 +260,7 @@ impl PageTableInternal { PageTableState::ActiveSelfMapped => PageTableStateWithAddress::SelfMapped(start_va), _ => PageTableStateWithAddress::NotSelfMapped(base), }; - let table = PageTableRange::::new(level, start_va, end_va, self.paging_type, state_with_address)?; + let table = PageTableRange::new(arch, level, start_va, end_va, self.paging_type, state_with_address)?; // there is a limitation in Rust's slice::iter_mut that will crash if we try to use a slice for the top level // of the self map. This can only occur in the query, due to map/unmap explicitly ensuring we are not @@ -272,14 +278,14 @@ impl PageTableInternal { // We only split if the attributes of this entry are changing, otherwise, skip this entry and move // to the next if entry.get_attributes() != attributes { - self.split_large_page(va, entry, state, level)?; + self.split_large_page(arch, va, entry, state, level)?; } else { va = va.get_next_va(level)?; continue; } } - if Arch::level_supports_pa_entry(level) + if arch.level_supports_pa_entry(level) && va.is_level_aligned(level) && va.length_through(end_va)? >= level.entry_va_size() { @@ -302,7 +308,7 @@ impl PageTableInternal { } if !entry.get_present_bit() { - let pa = self.allocate_page(state)?; + let pa = self.allocate_page(arch, state)?; // non-leaf pages should always have the most permissive memory attributes. entry.update_fields(Arch::DEFAULT_ATTRIBUTES, pa, false, level, va)?; } @@ -320,6 +326,7 @@ impl PageTableInternal { let next_level_end_va = VirtualAddress::min(curr_va_ceil, end_va); self.map_memory_region_internal( + arch, next_level_start_va, next_level_end_va, next_level, @@ -337,6 +344,7 @@ impl PageTableInternal { fn unmap_memory_region_internal( &mut self, + arch: &Arch, start_va: VirtualAddress, end_va: VirtualAddress, level: PageLevel, @@ -349,7 +357,7 @@ impl PageTableInternal { PageTableState::ActiveSelfMapped => PageTableStateWithAddress::SelfMapped(start_va), _ => PageTableStateWithAddress::NotSelfMapped(base), }; - let table = PageTableRange::::new(level, start_va, end_va, self.paging_type, state_with_address)?; + let table = PageTableRange::new(arch, level, start_va, end_va, self.paging_type, state_with_address)?; // there is a limitation in Rust's slice::iter_mut that will crash if we try to use a slice for the top level // of the self map. This can only occur in the query, due to map/unmap explicitly ensuring we are not @@ -364,14 +372,14 @@ impl PageTableInternal { && entry.get_present_bit() && (!va.is_level_aligned(level) || va.length_through(end_va)? < level.entry_va_size()) { - self.split_large_page(va, entry, state, level)?; + self.split_large_page(arch, va, entry, state, level)?; } // This is at least either the entirety of a large page or a single page. if entry.get_present_bit() { if entry.points_to_pa(level) { entry.unmap(va); - self.invalidate_selfmap(va, state, level)?; + self.invalidate_selfmap(arch, va, state, level)?; } else { // This should always have another level if this is not a PA entry. let next_level = level.next_level().unwrap(); @@ -389,6 +397,7 @@ impl PageTableInternal { let next_level_end_va = VirtualAddress::min(curr_va_ceil, end_va); self.unmap_memory_region_internal( + arch, next_level_start_va, next_level_end_va, next_level, @@ -403,9 +412,9 @@ impl PageTableInternal { Ok(()) } - #[allow(clippy::too_many_arguments)] fn query_memory_region_internal( &self, + arch: &Arch, start_va: VirtualAddress, end_va: VirtualAddress, level: PageLevel, @@ -420,7 +429,7 @@ impl PageTableInternal { PageTableState::ActiveSelfMapped => PageTableStateWithAddress::SelfMapped(start_va), _ => PageTableStateWithAddress::NotSelfMapped(base), }; - let table = PageTableRange::::new(level, start_va, end_va, self.paging_type, state_with_address)?; + let table = PageTableRange::new(arch, level, start_va, end_va, self.paging_type, state_with_address)?; // there is a limitation in Rust's slice::iter_mut that will crash if we try to use a slice for the top level // of the self map. This can only occur in the query, due to map/unmap explicitly ensuring we are not // attempting those operations on the self map VA, but this pattern is replicated to all the other functions @@ -485,6 +494,7 @@ impl PageTableInternal { // no mapping may be the case, but we need to continue walking down the page tables to see if we // find any mapped regions and need to fail the query with InconsistentMappingAcrossRange match self.query_memory_region_internal( + arch, next_level_start_va, next_level_end_va, next_level, @@ -518,6 +528,7 @@ impl PageTableInternal { /// and mapping to the new page table. fn split_large_page( &mut self, + arch: &Arch, va: VirtualAddress, entry: &mut Arch::PTE, state: PageTableState, @@ -544,7 +555,7 @@ impl PageTableInternal { let large_page_end: u64 = large_page_start + level.entry_va_size() - 1; let attributes = entry.get_attributes(); - let pa = self.allocate_page(state)?; + let pa = self.allocate_page(arch, state)?; // in order to use the self map, we have to add the PA to the page table, otherwise it is not part of // the self map. This means we will temporarily unmap the large page entry that was here, but as soon as @@ -558,9 +569,10 @@ impl PageTableInternal { entry.update_fields(Arch::DEFAULT_ATTRIBUTES, pa, false, level, va)?; // Invalidate the selfmap when needed. - self.invalidate_selfmap(va, state, level)?; + self.invalidate_selfmap(arch, va, state, level)?; self.map_memory_region_internal( + arch, large_page_start.into(), large_page_end.into(), next_level, @@ -572,6 +584,7 @@ impl PageTableInternal { fn dump_page_tables_internal( &self, + arch: &Arch, start_va: VirtualAddress, end_va: VirtualAddress, level: PageLevel, @@ -581,11 +594,12 @@ impl PageTableInternal { let mut va = start_va; // special case handling for zero VA and self map - if va == Arch::get_zero_va(self.paging_type)? { + if va == arch.get_zero_va(self.paging_type)? { log::info!("VA {va:#x?} is the zero VA"); - } else if u64::from(va) == Arch::get_self_mapped_base(PageLevel::Level1, va, self.paging_type) { + } else if u64::from(va) == arch.get_self_mapped_base(PageLevel::Level1, va, self.paging_type) { log::info!("VA {va:#x?} is the self-mapped VA, only dumping the root entry"); - let entry = get_entry::( + let entry = get_entry( + arch, PageLevel::root_level(self.paging_type), self.paging_type, PageTableStateWithAddress::NotSelfMapped(base), @@ -599,7 +613,7 @@ impl PageTableInternal { PageTableState::ActiveSelfMapped => PageTableStateWithAddress::SelfMapped(start_va), _ => PageTableStateWithAddress::NotSelfMapped(base), }; - let table = PageTableRange::::new(level, start_va, end_va, self.paging_type, state_with_address)?; + let table = PageTableRange::new(arch, level, start_va, end_va, self.paging_type, state_with_address)?; // there is a limitation in Rust's slice::iter_mut that will crash if we try to use a slice for the top level // of the self map. This can only occur in the query, due to map/unmap explicitly ensuring we are not // attempting those operations on the self map VA, but this pattern is replicated to all the other functions @@ -638,6 +652,7 @@ impl PageTableInternal { if entry.get_present_bit() && !entry.points_to_pa(level) { let next_base = entry.get_next_address(); self.dump_page_tables_internal( + arch, next_level_start_va, next_level_end_va, level.next_level().unwrap(), @@ -652,7 +667,13 @@ impl PageTableInternal { Ok(()) } - fn invalidate_selfmap(&self, va: VirtualAddress, state: PageTableState, level: PageLevel) -> Result<(), PtError> { + fn invalidate_selfmap( + &self, + arch: &Arch, + va: VirtualAddress, + state: PageTableState, + level: PageLevel, + ) -> Result<(), PtError> { if !matches!(state, PageTableState::ActiveSelfMapped) { return Ok(()); } @@ -666,17 +687,17 @@ impl PageTableInternal { // may get pulled in by speculative execution, so we need to ensure the wrong mapping invalidated before // the entry may be used again. if let Ok(tb_entry) = - get_entry::(PageLevel::Level1, self.paging_type, PageTableStateWithAddress::SelfMapped(va), 0) + get_entry(arch, PageLevel::Level1, self.paging_type, PageTableStateWithAddress::SelfMapped(va), 0) { // Invalidate the TLB entry for the self-mapped region - Arch::invalidate_tlb(tb_entry.entry_ptr_address().into()); + arch.invalidate_tlb(tb_entry.entry_ptr_address().into()); } } _ => { // For pages larger then level2, there are multiple levels of self map that could have been // speculatively pulled in, instead of walking all these we will simply invalidate the full // TLB in this uncommon scenario. - Arch::invalidate_tlb_all(); + arch.invalidate_tlb_all(); } } @@ -703,8 +724,8 @@ impl PageTableInternal { /// This is used to determine if we can use the self-map to zero pages and reference the page table pages. /// If our page table base is not in cr3, self-mapped entries won't work for this page table. Similarly, if the /// expected self-map entry is not present or does not point to the page table base, we can't use the self-map. - fn get_state(&self) -> PageTableState { - if !Arch::is_table_active(self.base.into()) { + fn get_state(&self, arch: &Arch) -> PageTableState { + if !arch.is_table_active(self.base.into()) { return PageTableState::Inactive; } @@ -712,7 +733,8 @@ impl PageTableInternal { // this is always read from the physical address of the page table, because we are trying to determine whether // we are self-mapped or not. The root should always be accessible, only assume active for now. - let self_map_entry = match get_entry::( + let self_map_entry = match get_entry( + arch, root_level, self.paging_type, PageTableStateWithAddress::NotSelfMapped(self.base), @@ -729,12 +751,18 @@ impl PageTableInternal { } } - pub fn map_memory_region(&mut self, address: u64, size: u64, attributes: MemoryAttributes) -> Result<(), PtError> { + pub fn map_memory_region( + &mut self, + arch: &Arch, + address: u64, + size: u64, + attributes: MemoryAttributes, + ) -> Result<(), PtError> { let address = VirtualAddress::new(address); self.validate_address_range(address, size)?; - let max_va = Arch::get_max_va(self.paging_type)?; + let max_va = arch.get_max_va(self.paging_type)?; // Overflow check, size is 0-based let top_va = (address + (size - 1))?; @@ -747,21 +775,22 @@ impl PageTableInternal { let end_va = (address + (size - 1))?; self.map_memory_region_internal( + arch, start_va, end_va, PageLevel::root_level(self.paging_type), self.base, attributes, - self.get_state(), + self.get_state(arch), ) } - pub fn unmap_memory_region(&mut self, address: u64, size: u64) -> Result<(), PtError> { + pub fn unmap_memory_region(&mut self, arch: &Arch, address: u64, size: u64) -> Result<(), PtError> { let address = VirtualAddress::new(address); self.validate_address_range(address, size)?; - let max_va = Arch::get_max_va(self.paging_type)?; + let max_va = arch.get_max_va(self.paging_type)?; // Overflow check, size is 0-based let top_va = (address + (size - 1))?; @@ -773,20 +802,21 @@ impl PageTableInternal { let end_va = (address + (size - 1))?; self.unmap_memory_region_internal( + arch, start_va, end_va, PageLevel::root_level(self.paging_type), self.base, - self.get_state(), + self.get_state(arch), ) } - pub fn install_page_table(&mut self) -> Result<(), PtError> { + pub fn install_page_table(&mut self, arch: &Arch) -> Result<(), PtError> { // SAFETY: The page table structure should guarantee that the page table is correct. - unsafe { Arch::install_page_table(self.base.into(), self.paging_type) } + unsafe { arch.install_page_table(self.base.into(), self.paging_type) } } - pub fn query_memory_region(&self, address: u64, size: u64) -> Result { + pub fn query_memory_region(&self, arch: &Arch, address: u64, size: u64) -> Result { let address = VirtualAddress::new(address); self.validate_address_range(address, size)?; @@ -796,17 +826,18 @@ impl PageTableInternal { let mut prev_attributes = RangeMappingState::Uninitialized; self.query_memory_region_internal( + arch, start_va, end_va, PageLevel::root_level(self.paging_type), self.base, &mut prev_attributes, - self.get_state(), + self.get_state(arch), MemoryAttributes::empty(), ) } - pub fn dump_page_tables(&self, address: u64, size: u64) -> Result<(), PtError> { + pub fn dump_page_tables(&self, arch: &Arch, address: u64, size: u64) -> Result<(), PtError> { if self.validate_address_range(address.into(), size).is_err() { log::error!("Invalid address range for page table dump! Address: {address:#x?}, Size: {size:#x?}"); return Err(PtError::InvalidMemoryRange); @@ -820,11 +851,12 @@ impl PageTableInternal { Arch::PTE::dump_entry_header(); log::info!("Root @ {:#X}", u64::from(self.base)); self.dump_page_tables_internal( + arch, start_va, end_va, PageLevel::root_level(self.paging_type), self.base, - self.get_state(), + self.get_state(arch), )?; Ok(()) @@ -844,8 +876,12 @@ impl PageTableInternal { /// /// The crate's reserved self-map and zero-VA root entries are skipped so the /// iterator only reports genuine mappings. - pub fn iter_mapped_regions(&self, start_address: Option) -> PageTableIterator<'_, Arch> { - PageTableIterator::new(self.base, self.paging_type, self.get_state(), start_address) + pub fn iter_mapped_regions<'a>( + &'a self, + arch: &'a Arch, + start_address: Option, + ) -> PageTableIterator<'a, Arch> { + PageTableIterator::new(arch, self.base, self.paging_type, self.get_state(arch), start_address) } } @@ -873,6 +909,7 @@ fn seek_start_index(start_va: u64, base_va: u64, level: PageLevel) -> usize { /// A depth-first iterator over the present leaf mappings of a page table. pub(crate) struct PageTableIterator<'a, Arch: PageTableHal> { + arch: &'a Arch, paging_type: PagingType, state: PageTableState, root_level: PageLevel, @@ -880,11 +917,16 @@ pub(crate) struct PageTableIterator<'a, Arch: PageTableHal> { start_va: u64, frames: [WalkFrame; MAX_PAGE_TABLE_DEPTH], depth: usize, - _marker: PhantomData<&'a Arch>, } -impl PageTableIterator<'_, Arch> { - fn new(base: PhysicalAddress, paging_type: PagingType, state: PageTableState, start_address: Option) -> Self { +impl<'a, Arch: PageTableHal> PageTableIterator<'a, Arch> { + fn new( + arch: &'a Arch, + base: PhysicalAddress, + paging_type: PagingType, + state: PageTableState, + start_address: Option, + ) -> Self { let root_level = PageLevel::root_level(paging_type); // Set the start address if not None, otherwise starts at VA 0. @@ -902,7 +944,8 @@ impl PageTableIterator<'_, Arch> { // We only skip the self-map and zero-VA root indices when the root self-map entry is present // and points back to the page table base. Otherwise (e.g. a page table not created by this // crate), those indices may contain genuine mappings that must be reported. - let skip_reserved_root_entries = match get_entry::( + let skip_reserved_root_entries = match get_entry( + arch, root_level, paging_type, PageTableStateWithAddress::NotSelfMapped(base), @@ -913,6 +956,7 @@ impl PageTableIterator<'_, Arch> { }; Self { + arch, paging_type, state, root_level, @@ -920,7 +964,6 @@ impl PageTableIterator<'_, Arch> { start_va, frames: [root_frame; MAX_PAGE_TABLE_DEPTH], depth: 1, - _marker: PhantomData, } } @@ -969,7 +1012,7 @@ impl Iterator for PageTableIterator<'_, Arch> { // SAFETY: We are using the page table as provided to the HW and are parsing it in the same manner as defined // by the architecture. This is inherently unsafe because we are trusting that the page table is valid. The // rest of the code in this module is designed to ensure that the page table is valid and consistent. - let slice = unsafe { get_table::(level, self.paging_type, state_with_address) }; + let slice = unsafe { get_table::(self.arch, level, self.paging_type, state_with_address) }; let entry = &slice[index]; if !entry.get_present_bit() { @@ -1028,6 +1071,7 @@ pub(crate) enum PageTableStateWithAddress { /// does when accessing the page table entries and the entire rest of the module is designed to ensure that the page /// table is valid and consistent before this function is called. pub unsafe fn get_table<'a, T, Arch: PageTableHal>( + arch: &Arch, level: PageLevel, paging_type: PagingType, state: PageTableStateWithAddress, @@ -1035,7 +1079,7 @@ pub unsafe fn get_table<'a, T, Arch: PageTableHal>( // the base depends on whether we are self-mapped or not. If we are self-mapped, the state contains the VA to use // to get the base of the page table. If we are not self-mapped, we use the physical address as the base. let base = match state { - PageTableStateWithAddress::SelfMapped(virt) => Arch::get_self_mapped_base(level, virt, paging_type), + PageTableStateWithAddress::SelfMapped(virt) => arch.get_self_mapped_base(level, virt, paging_type), PageTableStateWithAddress::NotSelfMapped(phys) => phys.into(), }; @@ -1045,6 +1089,7 @@ pub unsafe fn get_table<'a, T, Arch: PageTableHal>( } pub(crate) fn get_entry<'a, Arch: PageTableHal>( + arch: &Arch, level: PageLevel, paging_type: PagingType, state: PageTableStateWithAddress, @@ -1053,12 +1098,14 @@ pub(crate) fn get_entry<'a, Arch: PageTableHal>( // SAFETY: We are using the page table as provided to the HW and are parsing it in the same manner as defined // by the architecture. This is inherently unsafe because we are trusting that the page table is valid. The // rest of the code in this module is designed to ensure that the page table is valid and consistent. - let slice = unsafe { get_table::(level, paging_type, state) }; + let slice = unsafe { get_table::(arch, level, paging_type, state) }; slice.get_mut(index as usize).ok_or(PtError::NoMapping) } #[derive(Debug, PartialEq)] struct PageTableRange<'a, Arch: PageTableHal> { + arch: &'a Arch, + /// Physical page table base address slice: &'a mut [Arch::PTE], @@ -1068,6 +1115,7 @@ struct PageTableRange<'a, Arch: PageTableHal> { impl<'a, Arch: PageTableHal> PageTableRange<'a, Arch> { pub fn new( + arch: &'a Arch, level: PageLevel, start_va: VirtualAddress, end_va: VirtualAddress, @@ -1077,7 +1125,7 @@ impl<'a, Arch: PageTableHal> PageTableRange<'a, Arch> { // SAFETY: We are using the page table as provided to the HW and are parsing it in the same manner as defined // by the architecture. This is inherently unsafe because we are trusting that the page table is valid. The // rest of the code in this module is designed to ensure that the page table is valid and consistent. - let slice = unsafe { get_table::(level, paging_type, state) }; + let slice = unsafe { get_table::(arch, level, paging_type, state) }; let start = start_va.get_index(level) as usize; let end = end_va.get_index(level) as usize; if start_va > end_va || start > end || end >= slice.len() { @@ -1089,7 +1137,7 @@ impl<'a, Arch: PageTableHal> PageTableRange<'a, Arch> { ); return Err(PtError::InvalidMemoryRange); } - Ok(Self { slice: &mut slice[start..=end], _level: level }) + Ok(Self { arch, slice: &mut slice[start..=end], _level: level }) } } @@ -1114,31 +1162,31 @@ mod tests { const MAX_ENTRIES: usize = 512; const DEFAULT_ATTRIBUTES: MemoryAttributes = MemoryAttributes::empty(); - fn paging_type_supported(_paging_type: PagingType) -> Result<(), PtError> { + fn paging_type_supported(&self, _paging_type: PagingType) -> Result<(), PtError> { Ok(()) } - fn get_self_mapped_base(_level: PageLevel, _va: VirtualAddress, _paging_type: PagingType) -> u64 { + fn get_self_mapped_base(&self, _level: PageLevel, _va: VirtualAddress, _paging_type: PagingType) -> u64 { // for the test we can't use the real self map, so just return the PT base BASE.load(std::sync::atomic::Ordering::Relaxed) } - fn get_zero_va(_paging_type: PagingType) -> Result { + fn get_zero_va(&self, _paging_type: PagingType) -> Result { Ok(VirtualAddress::new(0x1000)) } - fn get_max_va(_paging_type: PagingType) -> Result { + fn get_max_va(&self, _paging_type: PagingType) -> Result { Ok(VirtualAddress::new(0xFFFF_FFFF_FFFF_0000)) } - fn is_table_active(_base: u64) -> bool { + fn is_table_active(&self, _base: u64) -> bool { ACTIVE.load(std::sync::atomic::Ordering::Relaxed) } - unsafe fn zero_page(_va: VirtualAddress) {} - unsafe fn install_page_table(_base: u64, _paging_type: PagingType) -> Result<(), PtError> { + unsafe fn zero_page(&self, _va: VirtualAddress) {} + unsafe fn install_page_table(&self, _base: u64, _paging_type: PagingType) -> Result<(), PtError> { Ok(()) } - fn invalidate_tlb(_va: VirtualAddress) {} - fn level_supports_pa_entry(_level: PageLevel) -> bool { + fn invalidate_tlb(&self, _va: VirtualAddress) {} + fn level_supports_pa_entry(&self, _level: PageLevel) -> bool { true } - fn invalidate_tlb_all() {} + fn invalidate_tlb_all(&self) {} } #[derive(Debug, Clone, Copy, PartialEq)] @@ -1237,17 +1285,18 @@ mod tests { } } - fn make_table() -> (PageTableInternal, DummyAllocator) { + fn make_table() -> (PageTableInternal, DummyAllocator, DummyArch) { let allocator = DummyAllocator::new(); let allocator_clone = allocator.clone(); - let pt = PageTableInternal::new(allocator, PagingType::Paging4Level).unwrap(); - (pt, allocator_clone) + let arch = DummyArch; + let pt = PageTableInternal::new(allocator, &arch, PagingType::Paging4Level).unwrap(); + (pt, allocator_clone, arch) } #[test] #[serial] fn test_get_state_variants() { - let (pt, allocator) = make_table(); + let (pt, allocator, arch) = make_table(); // Cleanup function to ensure memory is freed let cleanup = || { @@ -1256,7 +1305,7 @@ mod tests { // By default, the table is not active, so should be Inactive ACTIVE.store(false, std::sync::atomic::Ordering::Relaxed); - assert_eq!(pt.get_state(), PageTableState::Inactive); + assert_eq!(pt.get_state(&arch), PageTableState::Inactive); // Set table as active, but self-map entry is not present or doesn't match base ACTIVE.store(true, std::sync::atomic::Ordering::Relaxed); @@ -1264,6 +1313,7 @@ mod tests { // Overwrite the self-map entry to not present let root_level = PageLevel::root_level(pt.paging_type); let entry = get_entry::( + &arch, root_level, pt.paging_type, PageTableStateWithAddress::NotSelfMapped(pt.base), @@ -1272,13 +1322,13 @@ mod tests { .unwrap(); entry.set_present_bit(false, VirtualAddress::new(0)); - assert_eq!(pt.get_state(), PageTableState::ActiveIdentityMapped); + assert_eq!(pt.get_state(&arch), PageTableState::ActiveIdentityMapped); // Now set the self-map entry to present and point to the correct base entry.set_present_bit(true, VirtualAddress::new(0)); entry.update_fields(DummyArch::DEFAULT_ATTRIBUTES, pt.base, true, root_level, VirtualAddress::new(0)).unwrap(); - assert_eq!(pt.get_state(), PageTableState::ActiveSelfMapped); + assert_eq!(pt.get_state(&arch), PageTableState::ActiveSelfMapped); cleanup(); } @@ -1286,7 +1336,7 @@ mod tests { #[test] #[serial] fn test_validate_address_range() { - let (pt, allocator) = make_table(); + let (pt, allocator, _) = make_table(); assert!(pt.validate_address_range(VirtualAddress::new(0x1000), 0x2000).is_ok()); assert_eq!(pt.validate_address_range(VirtualAddress::new(0x1001), 0x2000), Err(PtError::UnalignedAddress)); @@ -1299,8 +1349,8 @@ mod tests { #[test] #[serial] fn test_allocate_page_alignment() { - let (mut pt, allocator) = make_table(); - let pa: u64 = pt.allocate_page(PageTableState::Inactive).unwrap().into(); + let (mut pt, allocator, arch) = make_table(); + let pa: u64 = pt.allocate_page(&arch, PageTableState::Inactive).unwrap().into(); assert_eq!(pa % PAGE_SIZE, 0); allocator.cleanup(); @@ -1309,11 +1359,16 @@ mod tests { #[test] #[serial] fn test_split_large_page_error() { - let (mut pt, allocator) = make_table(); + let (mut pt, allocator, arch) = make_table(); let mut entry = DummyPTE::new(); entry.set_present_bit(false, VirtualAddress::new(0x0)); - let res = - pt.split_large_page(VirtualAddress::new(0x0), &mut entry, PageTableState::Inactive, PageLevel::Level1); + let res = pt.split_large_page( + &arch, + VirtualAddress::new(0x0), + &mut entry, + PageTableState::Inactive, + PageLevel::Level1, + ); assert_eq!(res, Err(PtError::InvalidParameter)); allocator.cleanup(); @@ -1327,7 +1382,9 @@ mod tests { assert!(!ptr.is_null()); let base_pa = PhysicalAddress::new(ptr as u64); + let arch = DummyArch; let res = PageTableRange::::new( + &arch, PageLevel::Level1, VirtualAddress::new(3), VirtualAddress::new(2), @@ -1345,8 +1402,8 @@ mod tests { #[test] #[serial] fn test_dump_page_tables_invalid_range() { - let (pt, allocator) = make_table(); - let res = pt.dump_page_tables(0x1001, 0x1000); + let (pt, allocator, arch) = make_table(); + let res = pt.dump_page_tables(&arch, 0x1001, 0x1000); assert_eq!(res, Err(PtError::InvalidMemoryRange)); allocator.cleanup(); @@ -1355,10 +1412,12 @@ mod tests { #[test] fn test_from_existing_unaligned() { let allocator = DummyAllocator::new(); + let arch = DummyArch; let res = unsafe { PageTableInternal::::from_existing( - 0x123, allocator.clone(), + &arch, + 0x123, PagingType::Paging4Level, ) }; @@ -1370,11 +1429,11 @@ mod tests { #[test] #[serial] fn test_map_memory_region_top_va_overflow() { - let (mut pt, allocator) = make_table(); + let (mut pt, allocator, arch) = make_table(); // max_va is 0xFFFF_FFFF_FFFF_0000, so use an address near the top and a size that overflows let addr = 0xFFFF_FFFF_FFFF_0000; let size = 0x2000; // This will make top_va > max_va - let res = pt.map_memory_region(addr, size, MemoryAttributes::empty()); + let res = pt.map_memory_region(&arch, addr, size, MemoryAttributes::empty()); assert_eq!(res, Err(PtError::InvalidMemoryRange)); allocator.cleanup(); @@ -1383,10 +1442,10 @@ mod tests { #[test] #[serial] fn test_unmap_memory_region_top_va_overflow() { - let (mut pt, allocator) = make_table(); + let (mut pt, allocator, arch) = make_table(); let addr = 0xFFFF_FFFF_FFFF_0000; let size = 0x2000; - let res = pt.unmap_memory_region(addr, size); + let res = pt.unmap_memory_region(&arch, addr, size); assert_eq!(res, Err(PtError::InvalidMemoryRange)); allocator.cleanup(); @@ -1399,10 +1458,10 @@ mod tests { // level to the page table base, so the walk reads the root table for each level. A freshly // created table exposes no genuine leaf mappings, so the iterator yields nothing, but the // `ActiveSelfMapped` branch of the iterator's state handling is still executed. - let (pt, allocator) = make_table(); + let (pt, allocator, arch) = make_table(); let count = - PageTableIterator::::new(pt.base, pt.paging_type, PageTableState::ActiveSelfMapped, None) + PageTableIterator::::new(&arch, pt.base, pt.paging_type, PageTableState::ActiveSelfMapped, None) .count(); assert_eq!(count, 0, "a freshly created table exposes no genuine mappings"); diff --git a/src/tests/paging_tests.rs b/src/tests/paging_tests.rs index 68943fa..928f89d 100644 --- a/src/tests/paging_tests.rs +++ b/src/tests/paging_tests.rs @@ -23,22 +23,24 @@ use crate::{ use std::slice; macro_rules! all_archs { - ($body:expr) => {{ + (|$arch:ident| $body:expr) => {{ // Test on x64 { type Arch = PageTableArchX64; + let $arch = PageTableArchX64; $body } // Test on aarch64 { type Arch = PageTableArchAArch64; + let $arch = PageTableArchAArch64; $body } }}; } macro_rules! all_configs { - ($body:expr) => {{ + (|$arch:ident, $pt:ident| $body:expr) => {{ // Test on x64 - 5 level { #[allow(unused)] @@ -46,8 +48,10 @@ macro_rules! all_configs { type PageTableType = X64PageTable; #[allow(unused)] type PageTableTypeStub = X64PageTable; - let paging_type = PagingType::Paging5Level; - $body(paging_type) + let $pt = PagingType::Paging5Level; + #[allow(unused)] + let $arch = PageTableArchX64; + $body } // Test on x64 - 4 level { @@ -56,8 +60,10 @@ macro_rules! all_configs { type PageTableType = X64PageTable; #[allow(unused)] type PageTableTypeStub = X64PageTable; - let paging_type = PagingType::Paging4Level; - $body(paging_type) + let $pt = PagingType::Paging4Level; + #[allow(unused)] + let $arch = PageTableArchX64; + $body } // Test on aarch64 - 4 level { @@ -66,8 +72,10 @@ macro_rules! all_configs { type PageTableType = AArch64PageTable; #[allow(unused)] type PageTableTypeStub = AArch64PageTable; - let paging_type = PagingType::Paging4Level; - $body(paging_type) + let $pt = PagingType::Paging4Level; + #[allow(unused)] + let $arch = PageTableArchAArch64; + $body } }}; } @@ -95,6 +103,7 @@ fn set_logger() { } fn subtree_num_pages( + arch: &Arch, mut address: VirtualAddress, mut size: u64, level: PageLevel, @@ -118,7 +127,7 @@ fn subtree_num_pages( if !address.is_level_aligned(level) { let prefix_size: u64 = size.min(entry_size - (u64::from(address) & size_mask)); pages += 1; - pages += subtree_num_pages::(address, prefix_size, next_level)?; + pages += subtree_num_pages::(arch, address, prefix_size, next_level)?; address = (address + prefix_size)?; size -= prefix_size; }; @@ -128,9 +137,9 @@ fn subtree_num_pages( // If this level supports large pages, then no pages are needed for the // aligned middle. - if !Arch::level_supports_pa_entry(level) { + if !arch.level_supports_pa_entry(level) { pages += mid_size / entry_size; - pages += subtree_num_pages::(address, mid_size, next_level)?; + pages += subtree_num_pages::(arch, address, mid_size, next_level)?; } address = (address + mid_size)?; @@ -139,13 +148,14 @@ fn subtree_num_pages( if size > 0 { pages += 1; - pages += subtree_num_pages::(address, size, next_level)?; + pages += subtree_num_pages::(arch, address, size, next_level)?; } Ok(pages) } fn num_page_tables_required( + arch: &Arch, address: u64, size: u64, paging_type: PagingType, @@ -165,22 +175,22 @@ fn num_page_tables_required( // zero VA pages pages += PageLevel::root_level(paging_type).height() as u64; // The the tree structure before the root. - pages += subtree_num_pages::(address, size, PageLevel::root_level(paging_type))?; + pages += subtree_num_pages::(arch, address, size, PageLevel::root_level(paging_type))?; Ok(pages) } -fn get_self_mapped_base(paging_type: PagingType) -> u64 { - Arch::get_self_mapped_base(PageLevel::root_level(paging_type), VirtualAddress::new(0), paging_type) +fn get_self_mapped_base(arch: &Arch, paging_type: PagingType) -> u64 { + arch.get_self_mapped_base(PageLevel::root_level(paging_type), VirtualAddress::new(0), paging_type) } #[test] fn test_find_num_page_tables() { - all_archs!({ + all_archs!(|arch| { // Mapping one page of physical address require 4 page tables(PML4/PDP/PD/PT) let address = 0x0; let size = PAGE_SIZE; // 4k - let res = num_page_tables_required::(address, size, PagingType::Paging4Level); + let res = num_page_tables_required::(&arch, address, size, PagingType::Paging4Level); assert!(res.is_ok()); let table_count = res.unwrap(); assert_eq!(table_count, 4 + 3); @@ -188,7 +198,7 @@ fn test_find_num_page_tables() { // Mapping 511 pages of physical address require 4 page tables(PML4/PDP/PD/PT) let address = PAGE_SIZE; let size = 511 * PAGE_SIZE; - let res = num_page_tables_required::(address, size, PagingType::Paging4Level); + let res = num_page_tables_required::(&arch, address, size, PagingType::Paging4Level); assert!(res.is_ok()); let table_count = res.unwrap(); assert_eq!(table_count, 4 + 3); @@ -196,7 +206,7 @@ fn test_find_num_page_tables() { // Mapping 512 pages of physical address require 3 page tables because of 2mb pages.(PML4/PDP/PD) let address = 0x0; let size = 512 * PAGE_SIZE; - let res = num_page_tables_required::(address, size, PagingType::Paging4Level); + let res = num_page_tables_required::(&arch, address, size, PagingType::Paging4Level); assert!(res.is_ok()); let table_count = res.unwrap(); assert_eq!(table_count, 3 + 3); @@ -205,7 +215,7 @@ fn test_find_num_page_tables() { // (PML5(1)/PML4(1)/PDPE(1)/PDP(1)/PT(1)) let address = 0x0; let size = 513 * PAGE_SIZE; - let res = num_page_tables_required::(address, size, PagingType::Paging4Level); + let res = num_page_tables_required::(&arch, address, size, PagingType::Paging4Level); assert!(res.is_ok()); let table_count = res.unwrap(); assert_eq!(table_count, 4 + 3); @@ -213,7 +223,7 @@ fn test_find_num_page_tables() { // Mapping 1gb of physical address require 2 page tables because of 1Gb pages.(PML4/PDP) let address = 0x0; let size = 512 * 512 * PAGE_SIZE; - let res = num_page_tables_required::(address, size, PagingType::Paging4Level); + let res = num_page_tables_required::(&arch, address, size, PagingType::Paging4Level); assert!(res.is_ok()); let table_count = res.unwrap(); assert_eq!(table_count, 2 + 3); @@ -221,7 +231,7 @@ fn test_find_num_page_tables() { // Mapping 1 1GbPage + 1 2mb page + 1 4kb page require 4 page tables.(PML4/PDP/PD/PT) let address = 0x0; let size = (512 * 512 * PAGE_SIZE) + (512 * PAGE_SIZE) + PAGE_SIZE; - let res = num_page_tables_required::(address, size, PagingType::Paging4Level); + let res = num_page_tables_required::(&arch, address, size, PagingType::Paging4Level); assert!(res.is_ok()); let table_count = res.unwrap(); assert_eq!(table_count, 4 + 3); @@ -229,7 +239,7 @@ fn test_find_num_page_tables() { // Mapping 2mb starting at 2mb/2 should take 5 pages. (PML4/PDP/PD(1)/PT(2)) let address = 256 * PAGE_SIZE; let size = 512 * PAGE_SIZE; - let res = num_page_tables_required::(address, size, PagingType::Paging4Level); + let res = num_page_tables_required::(&arch, address, size, PagingType::Paging4Level); assert!(res.is_ok()); let table_count = res.unwrap(); assert_eq!(table_count, 5 + 3); @@ -237,7 +247,7 @@ fn test_find_num_page_tables() { // Mapping 10Gb starting at 4kb should take 6 pages. (PML4/PDP/PD(2)/PT(2)) let address = PAGE_SIZE; let size = 10 * 512 * 512 * PAGE_SIZE; - let res = num_page_tables_required::(address, size, PagingType::Paging4Level); + let res = num_page_tables_required::(&arch, address, size, PagingType::Paging4Level); assert!(res.is_ok()); let table_count = res.unwrap(); assert_eq!(table_count, 6 + 3); @@ -251,8 +261,8 @@ fn test_map_memory_address_simple() { let address = 0; let size = 0x400000; - all_configs!(|paging_type| { - let num_pages = num_page_tables_required::(address, size, paging_type).unwrap(); + all_configs!(|arch, paging_type| { + let num_pages = num_page_tables_required::(&arch, address, size, paging_type).unwrap(); let page_allocator = TestPageAllocator::new(num_pages, paging_type); let pt = PageTableType::new(page_allocator.clone(), paging_type); @@ -267,7 +277,7 @@ fn test_map_memory_address_simple() { assert_eq!(page_allocator.pages_allocated(), num_pages); - page_allocator.validate_pages::(address, size, attributes); + page_allocator.validate_pages::(&arch, address, size, attributes); }); } @@ -275,11 +285,11 @@ fn test_map_memory_address_simple() { fn test_map_memory_address_0_to_ffff_ffff() { let address = 0; - all_configs!(|paging_type| { + all_configs!(|arch, paging_type| { let mut size = PAGE_SIZE; while size < 0xffff_ffff { - let num_pages = num_page_tables_required::(address, size, paging_type).unwrap(); + let num_pages = num_page_tables_required::(&arch, address, size, paging_type).unwrap(); let page_allocator = TestPageAllocator::new(num_pages, paging_type); let pt = PageTableType::new(page_allocator.clone(), paging_type); @@ -295,7 +305,7 @@ fn test_map_memory_address_0_to_ffff_ffff() { pt.dump_page_tables(address, size).unwrap(); assert_eq!(page_allocator.pages_allocated(), num_pages); - page_allocator.validate_pages::(address, size, attributes); + page_allocator.validate_pages::(&arch, address, size, attributes); size <<= 1; } @@ -308,10 +318,10 @@ fn test_map_memory_address_single_page_from_0_to_ffff_ffff() { let size = PAGE_SIZE; let address_increment = PAGE_SIZE << 3; - all_configs!(|paging_type| { + all_configs!(|arch, paging_type| { let mut address = 0; while address < 0xffff_ffff { - let num_pages = num_page_tables_required::(address, size, paging_type).unwrap(); + let num_pages = num_page_tables_required::(&arch, address, size, paging_type).unwrap(); let page_allocator = TestPageAllocator::new(num_pages, paging_type); let pt = PageTableType::new(page_allocator.clone(), paging_type); @@ -324,7 +334,7 @@ fn test_map_memory_address_single_page_from_0_to_ffff_ffff() { assert!(res.is_ok()); assert_eq!(page_allocator.pages_allocated(), num_pages); - page_allocator.validate_pages::(address, size, attributes); + page_allocator.validate_pages::(&arch, address, size, attributes); address += address_increment; } @@ -337,11 +347,11 @@ fn test_map_memory_address_multiple_page_from_0_to_ffff_ffff() { let address_increment = PAGE_SIZE << 3; let size = PAGE_SIZE << 1; - all_configs!(|paging_type| { + all_configs!(|arch, paging_type| { let mut address = 0; while address < 0xffff_ffff { - let num_pages = num_page_tables_required::(address, size, paging_type).unwrap(); + let num_pages = num_page_tables_required::(&arch, address, size, paging_type).unwrap(); let page_allocator = TestPageAllocator::new(num_pages, paging_type); let pt = PageTableType::new(page_allocator.clone(), paging_type); @@ -354,7 +364,7 @@ fn test_map_memory_address_multiple_page_from_0_to_ffff_ffff() { assert!(res.is_ok()); assert_eq!(page_allocator.pages_allocated(), num_pages); - page_allocator.validate_pages::(address, size, attributes); + page_allocator.validate_pages::(&arch, address, size, attributes); address += address_increment; } @@ -366,7 +376,7 @@ fn test_map_memory_address_unaligned() { let address = 0x1; let size = 200; - all_configs!(|paging_type| { + all_configs!(|arch, paging_type| { let max_pages: u64 = 10; let page_allocator = TestPageAllocator::new(max_pages, paging_type); @@ -387,7 +397,7 @@ fn test_map_memory_address_zero_size() { let address = 0x1000; let size = 0; - all_configs!(|paging_type| { + all_configs!(|arch, paging_type| { let max_pages: u64 = 10; let page_allocator = TestPageAllocator::new(max_pages, paging_type); @@ -410,8 +420,8 @@ fn test_unmap_memory_address_simple() { let address = 0x1000; let size = PAGE_SIZE * 512 * 512 * 10; - all_configs!(|paging_type| { - let num_pages = num_page_tables_required::(address, size, paging_type).unwrap(); + all_configs!(|arch, paging_type| { + let num_pages = num_page_tables_required::(&arch, address, size, paging_type).unwrap(); let page_allocator = TestPageAllocator::new(num_pages, paging_type); let pt = PageTableType::new(page_allocator.clone(), paging_type); @@ -433,10 +443,10 @@ fn test_unmap_memory_address_simple() { fn test_unmap_memory_address_0_to_ffff_ffff() { let address = 0; - all_configs!(|paging_type| { + all_configs!(|arch, paging_type| { let mut size = PAGE_SIZE; while size < 0xffff_ffff { - let num_pages = num_page_tables_required::(address, size, paging_type).unwrap(); + let num_pages = num_page_tables_required::(&arch, address, size, paging_type).unwrap(); let page_allocator = TestPageAllocator::new(num_pages, paging_type); let pt = PageTableType::new(page_allocator.clone(), paging_type); @@ -462,10 +472,10 @@ fn test_unmap_memory_address_single_page_from_0_to_ffff_ffff() { let size = PAGE_SIZE; let address_increment = PAGE_SIZE << 3; - all_configs!(|paging_type| { + all_configs!(|arch, paging_type| { let mut address = 0; while address < 0xffff_ffff { - let num_pages = num_page_tables_required::(address, size, paging_type).unwrap(); + let num_pages = num_page_tables_required::(&arch, address, size, paging_type).unwrap(); let page_allocator = TestPageAllocator::new(num_pages, paging_type); let pt = PageTableType::new(page_allocator.clone(), paging_type); @@ -490,10 +500,10 @@ fn test_unmap_memory_address_multiple_page_from_0_to_ffff_ffff() { let size = PAGE_SIZE << 1; let address_increment = PAGE_SIZE << 3; - all_configs!(|paging_type| { + all_configs!(|arch, paging_type| { let mut address = 0; while address < 0xffff_ffff { - let num_pages = num_page_tables_required::(address, size, paging_type).unwrap(); + let num_pages = num_page_tables_required::(&arch, address, size, paging_type).unwrap(); let page_allocator = TestPageAllocator::new(num_pages, paging_type); let pt = PageTableType::new(page_allocator.clone(), paging_type); @@ -518,7 +528,7 @@ fn test_unmap_memory_address_unaligned() { let address = 0x1; let size = 200; - all_configs!(|paging_type| { + all_configs!(|arch, paging_type| { let max_pages: u64 = 10; let page_allocator = TestPageAllocator::new(max_pages, paging_type); @@ -538,7 +548,7 @@ fn test_unmap_memory_address_zero_size() { let address = 0x1000; let size = 0; - all_configs!(|paging_type| { + all_configs!(|arch, paging_type| { let max_pages: u64 = 10; let page_allocator = TestPageAllocator::new(max_pages, paging_type); @@ -558,8 +568,8 @@ fn test_unmap_memory_address_with_different_attributes() { let address = 0x8000; let size = PAGE_SIZE * 4; // 4 pages - all_configs!(|paging_type| { - let num_pages = num_page_tables_required::(address, size, paging_type).unwrap(); + all_configs!(|arch, paging_type| { + let num_pages = num_page_tables_required::(&arch, address, size, paging_type).unwrap(); let page_allocator = TestPageAllocator::new(num_pages, paging_type); let pt = PageTableType::new(page_allocator.clone(), paging_type); @@ -593,8 +603,8 @@ fn test_unmap_memory_address_partially_unmapped() { let address = 0x4000; let size = PAGE_SIZE * 4; // 4 pages - all_configs!(|paging_type| { - let num_pages = num_page_tables_required::(address, size, paging_type).unwrap(); + all_configs!(|arch, paging_type| { + let num_pages = num_page_tables_required::(&arch, address, size, paging_type).unwrap(); let page_allocator = TestPageAllocator::new(num_pages, paging_type); let pt = PageTableType::new(page_allocator.clone(), paging_type); @@ -629,8 +639,8 @@ fn test_query_memory_address_simple() { let address = 0x1000; let size = PAGE_SIZE * 512 * 512 * 10; - all_configs!(|paging_type| { - let num_pages = num_page_tables_required::(address, size, paging_type).unwrap(); + all_configs!(|arch, paging_type| { + let num_pages = num_page_tables_required::(&arch, address, size, paging_type).unwrap(); let page_allocator = TestPageAllocator::new(num_pages, paging_type); let pt = PageTableType::new(page_allocator.clone(), paging_type); @@ -651,8 +661,8 @@ fn test_query_memory_address_simple() { #[test] fn test_query_self_map() { - all_configs!(|paging_type| { - let address = get_self_mapped_base::(paging_type); + all_configs!(|arch, paging_type| { + let address = get_self_mapped_base(&arch, paging_type); let size = PAGE_SIZE; let page_allocator = TestPageAllocator::new(10, paging_type); @@ -671,10 +681,10 @@ fn test_query_self_map() { fn test_query_memory_address_0_to_ffff_ffff() { let address = 0x1000; - all_configs!(|paging_type| { + all_configs!(|arch, paging_type| { let mut size = PAGE_SIZE; while size < 0xffff_ffff { - let num_pages = num_page_tables_required::(address, size, paging_type).unwrap(); + let num_pages = num_page_tables_required::(&arch, address, size, paging_type).unwrap(); let page_allocator = TestPageAllocator::new(num_pages, paging_type); let pt = PageTableType::new(page_allocator.clone(), paging_type); @@ -701,10 +711,10 @@ fn test_query_memory_address_single_page_from_0_to_ffff_ffff() { let size = PAGE_SIZE; let step = PAGE_SIZE << 3; - all_configs!(|paging_type| { + all_configs!(|arch, paging_type| { let mut address = 0; while address < 0xffff_ffff { - let num_pages = num_page_tables_required::(address, size, paging_type).unwrap(); + let num_pages = num_page_tables_required::(&arch, address, size, paging_type).unwrap(); let page_allocator = TestPageAllocator::new(num_pages, paging_type); let pt = PageTableType::new(page_allocator.clone(), paging_type); @@ -730,10 +740,10 @@ fn test_query_memory_address_multiple_page_from_0_to_ffff_ffff() { let size = PAGE_SIZE << 1; let step = PAGE_SIZE << 3; - all_configs!(|paging_type| { + all_configs!(|arch, paging_type| { let mut address = 0; while address < 0xffff_ffff { - let num_pages = num_page_tables_required::(address, size, paging_type).unwrap(); + let num_pages = num_page_tables_required::(&arch, address, size, paging_type).unwrap(); let page_allocator = TestPageAllocator::new(num_pages, paging_type); let pt = PageTableType::new(page_allocator.clone(), paging_type); @@ -758,7 +768,7 @@ fn test_query_memory_address_multiple_page_from_0_to_ffff_ffff() { fn test_query_memory_address_unaligned() { let max_pages: u64 = 10; - all_configs!(|paging_type| { + all_configs!(|arch, paging_type| { let page_allocator = TestPageAllocator::new(max_pages, paging_type); let pt = PageTableType::new(page_allocator.clone(), paging_type); @@ -777,7 +787,7 @@ fn test_query_memory_address_unaligned() { fn test_query_memory_address_zero_size() { let max_pages: u64 = 10; - all_configs!(|paging_type| { + all_configs!(|arch, paging_type| { let page_allocator = TestPageAllocator::new(max_pages, paging_type); let pt = PageTableType::new(page_allocator.clone(), paging_type); @@ -797,8 +807,8 @@ fn test_query_memory_address_inconsistent_mappings() { let address = 0x1000; let size = 0x3000; - all_configs!(|paging_type| { - let num_pages = num_page_tables_required::(address, size, paging_type).unwrap(); + all_configs!(|arch, paging_type| { + let num_pages = num_page_tables_required::(&arch, address, size, paging_type).unwrap(); let page_allocator = TestPageAllocator::new(num_pages, paging_type); let pt = PageTableType::new(page_allocator.clone(), paging_type); @@ -827,7 +837,7 @@ fn test_query_memory_address_inconsistent_mappings_across_2mb_boundary() { let address = 0; let size = 0x400000; - all_configs!(|paging_type| { + all_configs!(|arch, paging_type| { let page_allocator = TestPageAllocator::new(0x1000, paging_type); let pt = PageTableType::new(page_allocator.clone(), paging_type); @@ -877,8 +887,8 @@ fn test_remap_memory_address_simple() { let address = 0x1000; let size = PAGE_SIZE * 512 * 512 * 10; - all_configs!(|paging_type| { - let num_pages = num_page_tables_required::(address, size, paging_type).unwrap(); + all_configs!(|arch, paging_type| { + let num_pages = num_page_tables_required::(&arch, address, size, paging_type).unwrap(); let page_allocator = TestPageAllocator::new(num_pages, paging_type); let pt = PageTableType::new(page_allocator.clone(), paging_type); @@ -901,11 +911,11 @@ fn test_remap_memory_address_simple() { fn test_remap_memory_address_0_to_ffff_ffff() { let address = 0; - all_configs!(|paging_type| { + all_configs!(|arch, paging_type| { let mut size = PAGE_SIZE; while size < 0xffff_ffff { - let num_pages = num_page_tables_required::(address, size, paging_type).unwrap(); + let num_pages = num_page_tables_required::(&arch, address, size, paging_type).unwrap(); let page_allocator = TestPageAllocator::new(num_pages, paging_type); let pt = PageTableType::new(page_allocator.clone(), paging_type); @@ -932,11 +942,11 @@ fn test_remap_memory_address_single_page_from_0_to_ffff_ffff() { let address_increment = PAGE_SIZE << 3; let size = PAGE_SIZE; - all_configs!(|paging_type| { + all_configs!(|arch, paging_type| { let mut address = 0; while address < 0xffff_ffff { - let num_pages = num_page_tables_required::(address, size, paging_type).unwrap(); + let num_pages = num_page_tables_required::(&arch, address, size, paging_type).unwrap(); let page_allocator = TestPageAllocator::new(num_pages, paging_type); let pt = PageTableType::new(page_allocator.clone(), paging_type); @@ -962,11 +972,11 @@ fn test_remap_memory_address_multiple_page_from_0_to_ffff_ffff() { let address_increment = PAGE_SIZE << 3; let size = PAGE_SIZE << 1; - all_configs!(|paging_type| { + all_configs!(|arch, paging_type| { let mut address = 0; while address < 0xffff_ffff { - let num_pages = num_page_tables_required::(address, size, paging_type).unwrap(); + let num_pages = num_page_tables_required::(&arch, address, size, paging_type).unwrap(); let page_allocator = TestPageAllocator::new(num_pages, paging_type); let pt = PageTableType::new(page_allocator.clone(), paging_type); @@ -992,7 +1002,7 @@ fn test_remap_memory_address_unaligned() { let address = 0x1; let size = 200; - all_configs!(|paging_type| { + all_configs!(|arch, paging_type| { let max_pages: u64 = 10; let page_allocator = TestPageAllocator::new(max_pages, paging_type); @@ -1014,7 +1024,7 @@ fn test_remap_memory_address_zero_size() { let address = 0x1000; let size = 0; - all_configs!(|paging_type| { + all_configs!(|arch, paging_type| { let max_pages: u64 = 10; let page_allocator = TestPageAllocator::new(max_pages, paging_type); @@ -1037,8 +1047,8 @@ fn test_remap_memory_address_mixed_attributes() { let base_address = 0x3000; let total_size = PAGE_SIZE * 4; // 4 pages - all_configs!(|paging_type| { - let num_pages = num_page_tables_required::(base_address, total_size, paging_type).unwrap(); + all_configs!(|arch, paging_type| { + let num_pages = num_page_tables_required::(&arch, base_address, total_size, paging_type).unwrap(); let page_allocator = TestPageAllocator::new(num_pages, paging_type); let pt = PageTableType::new(page_allocator.clone(), paging_type); @@ -1091,8 +1101,8 @@ fn test_remap_memory_address_partially_mapped_range() { let total_size = PAGE_SIZE * 4; // 4 pages let half_size = PAGE_SIZE * 2; - all_configs!(|paging_type| { - let num_pages = num_page_tables_required::(base_address, total_size, paging_type).unwrap(); + all_configs!(|arch, paging_type| { + let num_pages = num_page_tables_required::(&arch, base_address, total_size, paging_type).unwrap(); let page_allocator = TestPageAllocator::new(num_pages, paging_type); let pt = PageTableType::new(page_allocator.clone(), paging_type); @@ -1138,8 +1148,8 @@ fn test_from_existing_page_table() { let address = 0x1000; let size = PAGE_SIZE * 512 * 512 * 10; - all_configs!(|paging_type| { - let num_pages = num_page_tables_required::(address, size, paging_type).unwrap(); + all_configs!(|arch, paging_type| { + let num_pages = num_page_tables_required::(&arch, address, size, paging_type).unwrap(); let page_allocator = TestPageAllocator::new(num_pages, paging_type); let pt = PageTableType::new(page_allocator.clone().clone(), paging_type); @@ -1173,8 +1183,8 @@ fn test_dump_page_tables() { set_logger(); - all_configs!(|paging_type| { - let num_pages = num_page_tables_required::(address, size, paging_type).unwrap(); + all_configs!(|arch, paging_type| { + let num_pages = num_page_tables_required::(&arch, address, size, paging_type).unwrap(); let page_allocator = TestPageAllocator::new(num_pages, paging_type); let pt = PageTableType::new(page_allocator.clone(), paging_type); @@ -1245,7 +1255,7 @@ fn test_large_page_splitting() { Remap, } - all_configs!(|paging_type| { + all_configs!(|arch, paging_type| { let orig_attributes = MemoryAttributes::empty() | Arch::DEFAULT_ATTRIBUTES; let remap_attributes = MemoryAttributes::ExecuteProtect | Arch::DEFAULT_ATTRIBUTES; @@ -1253,7 +1263,8 @@ fn test_large_page_splitting() { let TestConfig { mapped_range, split_range, page_increase } = test_config; for action in [TestAction::Unmap, TestAction::Remap] { let num_pages = - num_page_tables_required::(mapped_range.address, mapped_range.size, paging_type).unwrap(); + num_page_tables_required::(&arch, mapped_range.address, mapped_range.size, paging_type) + .unwrap(); let page_allocator = TestPageAllocator::new(num_pages + page_increase, paging_type); let pt = PageTableType::new(page_allocator.clone(), paging_type); @@ -1304,8 +1315,8 @@ fn test_map_unmap_remap_large_page_subregion() { let subregion_address = base_address + subregion_offset; let subregion_size = PAGE_SIZE; - all_configs!(|paging_type| { - let num_pages = num_page_tables_required::(base_address, large_page_size, paging_type).unwrap(); + all_configs!(|arch, paging_type| { + let num_pages = num_page_tables_required::(&arch, base_address, large_page_size, paging_type).unwrap(); let page_allocator = TestPageAllocator::new(num_pages + 1, paging_type); // +1 for possible PT split let pt = PageTableType::new(page_allocator.clone(), paging_type); @@ -1359,7 +1370,7 @@ fn test_map_unmap_remap_large_page_subregion() { fn test_install_page_table() { let address = 0x1000; - all_configs!(|paging_type| { + all_configs!(|arch, paging_type| { let page_allocator = TestPageAllocator::new(0x1000, paging_type); let pt = PageTableType::new(page_allocator.clone(), paging_type); @@ -1466,8 +1477,8 @@ fn test_map_large_page_remap_subset_with_same_attributes() { let base_address = 0x400000; // Purposefully choose a 2MB aligned address let subregion_size = SIZE_2MB - PAGE_SIZE; - all_configs!(|paging_type| { - let num_pages = num_page_tables_required::(base_address, large_page_size, paging_type).unwrap(); + all_configs!(|arch, paging_type| { + let num_pages = num_page_tables_required::(&arch, base_address, large_page_size, paging_type).unwrap(); let page_allocator = TestPageAllocator::new(num_pages + 1, paging_type); // +1 for possible PT split let pt = PageTableType::new(page_allocator.clone(), paging_type); @@ -1523,8 +1534,8 @@ fn test_iter_mapped_regions_covers_simple_mapping() { let address = 0; let size = 0x400000; // 4MB - all_configs!(|paging_type| { - let num_pages = num_page_tables_required::(address, size, paging_type).unwrap(); + all_configs!(|arch, paging_type| { + let num_pages = num_page_tables_required::(&arch, address, size, paging_type).unwrap(); let page_allocator = TestPageAllocator::new(num_pages, paging_type); let mut pt = PageTableType::new(page_allocator.clone(), paging_type).unwrap(); @@ -1556,7 +1567,7 @@ fn test_iter_mapped_regions_covers_simple_mapping() { fn test_iter_mapped_regions_skips_reserved_entries() { // A freshly created table contains only the crate's reserved self-map and // zero-VA entries, which must never be reported as genuine mappings. - all_configs!(|paging_type| { + all_configs!(|arch, paging_type| { let page_allocator = TestPageAllocator::new(16, paging_type); let pt = PageTableType::new(page_allocator.clone(), paging_type).unwrap(); @@ -1573,11 +1584,11 @@ fn test_iter_mapped_regions_multiple_disjoint() { let region_a = (0x40000000u64, SIZE_2MB); // 1GB base, read-only let region_b = (0x80000000u64, SIZE_2MB); // 2GB base, execute-protected - all_configs!(|paging_type| { + all_configs!(|arch, paging_type| { // Sum of the per-region requirements is a safe over-estimate of the // pages needed when both are mapped into the same table. - let pages_a = num_page_tables_required::(region_a.0, region_a.1, paging_type).unwrap(); - let pages_b = num_page_tables_required::(region_b.0, region_b.1, paging_type).unwrap(); + let pages_a = num_page_tables_required::(&arch, region_a.0, region_a.1, paging_type).unwrap(); + let pages_b = num_page_tables_required::(&arch, region_b.0, region_b.1, paging_type).unwrap(); let page_allocator = TestPageAllocator::new(pages_a + pages_b, paging_type); let mut pt = PageTableType::new(page_allocator.clone(), paging_type).unwrap(); @@ -1614,8 +1625,8 @@ fn test_iter_mapped_regions_canonicalizes_high_half() { let paging_type = PagingType::Paging5Level; let address = 0xFF00_0000_0000_0000u64; let size = SIZE_2MB; - - let num_pages = num_page_tables_required::(address, size, paging_type).unwrap(); + let arch = PageTableArchX64; + let num_pages = num_page_tables_required::(&arch, address, size, paging_type).unwrap(); let page_allocator = TestPageAllocator::new(num_pages, paging_type); let mut pt = X64PageTable::new(page_allocator.clone(), paging_type).unwrap(); @@ -1649,7 +1660,8 @@ fn test_iter_mapped_regions_reports_reserved_indices_for_foreign_table() { let address = 0u64; let size = SIZE_2MB; - let num_pages = num_page_tables_required::(address, size, paging_type).unwrap(); + let arch = PageTableArchX64; + let num_pages = num_page_tables_required::(&arch, address, size, paging_type).unwrap(); let page_allocator = TestPageAllocator::new(num_pages, paging_type); let mut pt = X64PageTable::new(page_allocator.clone(), paging_type).unwrap(); @@ -1699,9 +1711,9 @@ fn test_iter_mapped_regions_start_address_skips_earlier() { let region_a = (0x40000000u64, SIZE_2MB); // 1GB, read-only let region_b = (0x80000000u64, SIZE_2MB); // 2GB, execute-protected - all_configs!(|paging_type| { - let pages_a = num_page_tables_required::(region_a.0, region_a.1, paging_type).unwrap(); - let pages_b = num_page_tables_required::(region_b.0, region_b.1, paging_type).unwrap(); + all_configs!(|arch, paging_type| { + let pages_a = num_page_tables_required::(&arch, region_a.0, region_a.1, paging_type).unwrap(); + let pages_b = num_page_tables_required::(&arch, region_b.0, region_b.1, paging_type).unwrap(); let page_allocator = TestPageAllocator::new(pages_a + pages_b, paging_type); let mut pt = PageTableType::new(page_allocator.clone(), paging_type).unwrap(); @@ -1731,8 +1743,8 @@ fn test_iter_mapped_regions_start_within_region() { let address = 0x40000000u64; // 1GB let size = 0x400000u64; // 4MB - all_configs!(|paging_type| { - let num_pages = num_page_tables_required::(address, size, paging_type).unwrap(); + all_configs!(|arch, paging_type| { + let num_pages = num_page_tables_required::(&arch, address, size, paging_type).unwrap(); let page_allocator = TestPageAllocator::new(num_pages, paging_type); let mut pt = PageTableType::new(page_allocator.clone(), paging_type).unwrap(); @@ -1755,8 +1767,8 @@ fn test_iter_mapped_regions_start_zero_matches_none() { let address = 0u64; let size = 0x400000u64; // 4MB - all_configs!(|paging_type| { - let num_pages = num_page_tables_required::(address, size, paging_type).unwrap(); + all_configs!(|arch, paging_type| { + let num_pages = num_page_tables_required::(&arch, address, size, paging_type).unwrap(); let page_allocator = TestPageAllocator::new(num_pages, paging_type); let mut pt = PageTableType::new(page_allocator.clone(), paging_type).unwrap(); @@ -1776,8 +1788,8 @@ fn test_iter_mapped_regions_start_after_all_mappings_is_empty() { let address = 0x40000000u64; // 1GB let size = SIZE_2MB; - all_configs!(|paging_type| { - let num_pages = num_page_tables_required::(address, size, paging_type).unwrap(); + all_configs!(|arch, paging_type| { + let num_pages = num_page_tables_required::(&arch, address, size, paging_type).unwrap(); let page_allocator = TestPageAllocator::new(num_pages, paging_type); let mut pt = PageTableType::new(page_allocator.clone(), paging_type).unwrap(); @@ -1802,7 +1814,8 @@ fn test_iter_mapped_regions_start_address_high_half() { let address = 0xFF00_0000_0000_0000u64; let size = SIZE_2MB; - let num_pages = num_page_tables_required::(address, size, paging_type).unwrap(); + let arch = PageTableArchX64; + let num_pages = num_page_tables_required::(&arch, address, size, paging_type).unwrap(); let page_allocator = TestPageAllocator::new(num_pages, paging_type); let mut pt = X64PageTable::new(page_allocator.clone(), paging_type).unwrap(); @@ -1833,8 +1846,8 @@ fn test_iter_mapped_regions_start_seeks_deep_non_root_index() { let base = 0x40000000u64; // 1GB-aligned: the eight 2MB pages share one L2 table. let size = 8 * SIZE_2MB; - all_configs!(|paging_type| { - let num_pages = num_page_tables_required::(base, size, paging_type).unwrap(); + all_configs!(|arch, paging_type| { + let num_pages = num_page_tables_required::(&arch, base, size, paging_type).unwrap(); let page_allocator = TestPageAllocator::new(num_pages, paging_type); let mut pt = PageTableType::new(page_allocator.clone(), paging_type).unwrap(); diff --git a/src/tests/test_page_allocator.rs b/src/tests/test_page_allocator.rs index 2946843..6e2fddd 100644 --- a/src/tests/test_page_allocator.rs +++ b/src/tests/test_page_allocator.rs @@ -6,6 +6,8 @@ //! //! SPDX-License-Identifier: Apache-2.0 //! + +#![allow(clippy::too_many_arguments)] use crate::{ MemoryAttributes, PagingType, PtError, arch::{PageTableEntry, PageTableHal}, @@ -84,7 +86,13 @@ impl TestPageAllocator { // TestPageAllocator Page Tables // Memory // - pub fn validate_pages(&self, address: u64, size: u64, attributes: MemoryAttributes) { + pub fn validate_pages( + &self, + arch: &Arch, + address: u64, + size: u64, + attributes: MemoryAttributes, + ) { log::info!("Validating pages from {:#x} to {:#x}", address, address + size); let address = VirtualAddress::new(address); let start_va = address; @@ -95,6 +103,7 @@ impl TestPageAllocator { let mut page_index = 0; self.validate_pages_internal::( + arch, start_va, end_va, PageLevel::root_level(self.paging_type), @@ -105,6 +114,7 @@ impl TestPageAllocator { fn validate_pages_internal( &self, + arch: &Arch, start_va: VirtualAddress, end_va: VirtualAddress, level: PageLevel, @@ -130,7 +140,7 @@ impl TestPageAllocator { } }; let leaf = - unsafe { self.validate_page_entry::(page, index, va.into(), page_base, level, attributes) }; + unsafe { self.validate_page_entry::(arch, page, index, va.into(), page_base, level, attributes) }; // We only consume further pages from PageAllocator memory // for page tables higher than PT type @@ -149,6 +159,7 @@ impl TestPageAllocator { if !leaf { let next_level = level.next_level().unwrap(); self.validate_pages_internal::( + arch, next_level_start_va, next_level_end_va, next_level, @@ -163,6 +174,7 @@ impl TestPageAllocator { unsafe fn validate_page_entry( &self, + arch: &Arch, page_table_ptr: *const u64, index: u64, virtual_address: u64, @@ -171,6 +183,7 @@ impl TestPageAllocator { expected_attributes: MemoryAttributes, ) -> bool { let pte = get_entry::( + arch, level, self.paging_type, PageTableStateWithAddress::NotSelfMapped(PhysicalAddress::new(page_table_ptr as u64)), diff --git a/src/x64.rs b/src/x64.rs index 1105f91..09a2562 100644 --- a/src/x64.rs +++ b/src/x64.rs @@ -37,13 +37,15 @@ pub const PT: PageLevel = PageLevel::Level1; pub const MAX_ENTRIES: usize = (PAGE_SIZE / 8) as usize; pub struct X64PageTable { + arch: PageTableArchX64, internal: PageTableInternal, } impl X64PageTable

{ pub fn new(page_allocator: P, paging_type: PagingType) -> Result { - let internal = PageTableInternal::new(page_allocator, paging_type)?; - Ok(Self { internal }) + let arch = PageTableArchX64; + let internal = PageTableInternal::new(page_allocator, &arch, paging_type)?; + Ok(Self { arch, internal }) } /// Create a page table from existing page table base. This can be used to @@ -56,8 +58,9 @@ impl X64PageTable

{ /// safety of that base. /// pub unsafe fn from_existing(base: u64, page_allocator: P, paging_type: PagingType) -> Result { - let internal = unsafe { PageTableInternal::from_existing(base, page_allocator, paging_type)? }; - Ok(Self { internal }) + let arch = PageTableArchX64; + let internal = unsafe { PageTableInternal::from_existing(page_allocator, &arch, base, paging_type)? }; + Ok(Self { arch, internal }) } /// Consumes the page table structure and returns the page table root. @@ -79,7 +82,7 @@ impl X64PageTable

{ /// The crate's reserved self-map and zero-VA root entries are skipped so the /// iterator only reports genuine mappings. pub fn iter_mapped_regions(&self, start_address: Option) -> impl Iterator + '_ { - self.internal.iter_mapped_regions(start_address) + self.internal.iter_mapped_regions(&self.arch, start_address) } /// Opens a page table manager for the currently active page tables. @@ -111,25 +114,25 @@ impl PageTable for X64PageTable

{ attributes: crate::MemoryAttributes, ) -> Result<(), PtError> { check_canonical_range(address, size, self.internal.paging_type)?; - self.internal.map_memory_region(address, size, attributes) + self.internal.map_memory_region(&self.arch, address, size, attributes) } fn unmap_memory_region(&mut self, address: u64, size: u64) -> Result<(), PtError> { check_canonical_range(address, size, self.internal.paging_type)?; - self.internal.unmap_memory_region(address, size) + self.internal.unmap_memory_region(&self.arch, address, size) } fn install_page_table(&mut self) -> Result<(), PtError> { - self.internal.install_page_table() + self.internal.install_page_table(&self.arch) } fn query_memory_region(&self, address: u64, size: u64) -> Result { check_canonical_range(address, size, self.internal.paging_type)?; - self.internal.query_memory_region(address, size) + self.internal.query_memory_region(&self.arch, address, size) } fn dump_page_tables(&self, address: u64, size: u64) -> Result<(), PtError> { - self.internal.dump_page_tables(address, size) + self.internal.dump_page_tables(&self.arch, address, size) } } @@ -224,44 +227,44 @@ impl PageTableHal for PageTableArchX64 { /// # Safety /// This function is unsafe because it operates on raw pointers. It requires the caller to ensure the VA passed in /// is mapped. - unsafe fn zero_page(page: VirtualAddress) { + unsafe fn zero_page(&self, page: VirtualAddress) { // This cast must occur as a mutable pointer to a u8, as otherwise the compiler can optimize out the write, // which must not happen as that would violate break before make and have garbage in the page table. unsafe { ptr::write_bytes(Into::::into(page) as *mut u8, 0, PAGE_SIZE as usize) }; } - fn paging_type_supported(paging_type: PagingType) -> Result<(), PtError> { + fn paging_type_supported(&self, paging_type: PagingType) -> Result<(), PtError> { match paging_type { PagingType::Paging5Level => Ok(()), PagingType::Paging4Level => Ok(()), } } - fn get_zero_va(paging_type: PagingType) -> Result { + fn get_zero_va(&self, paging_type: PagingType) -> Result { match paging_type { PagingType::Paging5Level => Ok(ZERO_VA_5_LEVEL.into()), PagingType::Paging4Level => Ok(ZERO_VA_4_LEVEL.into()), } } - fn invalidate_tlb(va: VirtualAddress) { + fn invalidate_tlb(&self, va: VirtualAddress) { invalidate_tlb(va); } - fn get_max_va(paging_type: PagingType) -> Result { + fn get_max_va(&self, paging_type: PagingType) -> Result { match paging_type { PagingType::Paging5Level => Ok(MAX_VA_5_LEVEL.into()), PagingType::Paging4Level => Ok(MAX_VA_4_LEVEL.into()), } } - fn is_table_active(base: u64) -> bool { + fn is_table_active(&self, base: u64) -> bool { read_cr3() == (base & CR3_PAGE_BASE_ADDRESS_MASK) } /// SAFETY: This function is unsafe because it updates the HW page table registers to install a new page table. /// The caller must ensure that the base address is valid and points to a properly constructed page table. - unsafe fn install_page_table(base: u64, _paging_type: PagingType) -> Result<(), PtError> { + unsafe fn install_page_table(&self, base: u64, _paging_type: PagingType) -> Result<(), PtError> { // The implementation doesn't currently support switching page table types at runtime. // Skip this check in test builds since CR4 always reads as 0 (no hardware). #[cfg(target_os = "uefi")] @@ -279,7 +282,7 @@ impl PageTableHal for PageTableArchX64 { Ok(()) } - fn level_supports_pa_entry(level: crate::structs::PageLevel) -> bool { + fn level_supports_pa_entry(&self, level: crate::structs::PageLevel) -> bool { matches!(level, PageLevel::Level3 | PageLevel::Level2 | PageLevel::Level1) } @@ -291,7 +294,7 @@ impl PageTableHal for PageTableArchX64 { /// covers 512GB of memory, each PDP entry covers 1GB of memory, each PD entry covers 2MB of memory, and /// each PT entry covers 4KB of memory, but when we recurse in the self map to a given level, we shift what /// each entry covers to be the size of the next level down for each recursion into the self map we did. - fn get_self_mapped_base(level: PageLevel, va: VirtualAddress, paging_type: PagingType) -> u64 { + fn get_self_mapped_base(&self, level: PageLevel, va: VirtualAddress, paging_type: PagingType) -> u64 { match paging_type { PagingType::Paging4Level => match level { // PML5 is not used in 4-level paging, so we return an unimplemented error. @@ -327,7 +330,7 @@ impl PageTableHal for PageTableArchX64 { } } - fn invalidate_tlb_all() { + fn invalidate_tlb_all(&self) { // SAFETY: The CR3 is not being changed, but re-written to flush the TLB. unsafe { write_cr3(read_cr3()) }; } @@ -423,7 +426,8 @@ mod unittests { // SAFETY: We have exclusive access to the page buffer unsafe { - PageTableArchX64::zero_page(va); + let arch = PageTableArchX64; + arch.zero_page(va); } // Assert all bytes are zero From f9e3d1fbfe0e732e8fc9a6997c8e6cbadfe3b243 Mon Sep 17 00:00:00 2001 From: "Vineel Kovvuri[MSFT]" Date: Mon, 17 Aug 2026 08:42:04 -0700 Subject: [PATCH 2/2] Make DummyArch stateful to drop serial_test Replace the global ACTIVE/BASE statics in the paging unit tests with per instance AtomicBool/AtomicU64 fields on DummyArch, shared with DummyAllocator via Rc. Each test now owns its own arch state instead of mutating process wide globals, so the tests no longer need #[serial] and can run in parallel. This also makes the tests more useful under nextest. Signed-off-by: Vineel Kovvuri[MSFT] --- Cargo.toml | 1 - src/paging.rs | 72 +++++++++++++++++++++++++++++---------------------- 2 files changed, 41 insertions(+), 32 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6a71282..4a8f3ed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,6 @@ log = { version = "^0.4", default-features = false } cfg-if = "1.0.0" [dev-dependencies] -serial_test = "3.5" [features] supervisor = [] diff --git a/src/paging.rs b/src/paging.rs index 3ee4804..a1f64d8 100644 --- a/src/paging.rs +++ b/src/paging.rs @@ -1145,18 +1145,34 @@ impl<'a, Arch: PageTableHal> PageTableRange<'a, Arch> { #[cfg_attr(coverage, coverage(off))] mod tests { use super::*; - use serial_test::serial; use std::{ alloc::{Layout, alloc_zeroed}, - sync::atomic::{AtomicBool, AtomicU64}, + cell::RefCell, + rc::Rc, + sync::atomic::{AtomicBool, AtomicU64, Ordering}, }; - static ACTIVE: AtomicBool = AtomicBool::new(false); - static BASE: AtomicU64 = AtomicU64::new(0); - // Dummy Arch implementation for testing - #[derive(PartialEq, Debug)] - struct DummyArch; + #[derive(Debug)] + struct DummyArch { + is_table_active: AtomicBool, + base: AtomicU64, + } + + impl DummyArch { + fn new() -> Self { + Self { is_table_active: AtomicBool::new(false), base: AtomicU64::new(0) } + } + + fn set_active(&self, active: bool) { + self.is_table_active.store(active, Ordering::Relaxed); + } + + fn set_base(&self, base: u64) { + self.base.store(base, Ordering::Relaxed); + } + } + impl PageTableHal for DummyArch { type PTE = DummyPTE; const MAX_ENTRIES: usize = 512; @@ -1167,7 +1183,7 @@ mod tests { } fn get_self_mapped_base(&self, _level: PageLevel, _va: VirtualAddress, _paging_type: PagingType) -> u64 { // for the test we can't use the real self map, so just return the PT base - BASE.load(std::sync::atomic::Ordering::Relaxed) + self.base.load(Ordering::Relaxed) } fn get_zero_va(&self, _paging_type: PagingType) -> Result { Ok(VirtualAddress::new(0x1000)) @@ -1176,7 +1192,7 @@ mod tests { Ok(VirtualAddress::new(0xFFFF_FFFF_FFFF_0000)) } fn is_table_active(&self, _base: u64) -> bool { - ACTIVE.load(std::sync::atomic::Ordering::Relaxed) + self.is_table_active.load(Ordering::Relaxed) } unsafe fn zero_page(&self, _va: VirtualAddress) {} unsafe fn install_page_table(&self, _base: u64, _paging_type: PagingType) -> Result<(), PtError> { @@ -1249,11 +1265,12 @@ mod tests { // Dummy PageAllocator for testing #[derive(Clone, Debug)] struct DummyAllocator { - allocated_pages: std::rc::Rc>>, + allocated_pages: Rc>>, + arch: Rc, } impl DummyAllocator { - fn new() -> Self { - Self { allocated_pages: std::rc::Rc::new(std::cell::RefCell::new(Vec::new())) } + fn new(arch: Rc) -> Self { + Self { allocated_pages: Rc::new(RefCell::new(Vec::new())), arch } } fn cleanup(&self) { @@ -1278,23 +1295,22 @@ mod tests { self.allocated_pages.borrow_mut().push(addr); if is_root { - BASE.store(addr, std::sync::atomic::Ordering::Relaxed); + self.arch.set_base(addr); } Ok(addr) } } - fn make_table() -> (PageTableInternal, DummyAllocator, DummyArch) { - let allocator = DummyAllocator::new(); + fn make_table() -> (PageTableInternal, DummyAllocator, Rc) { + let arch = Rc::new(DummyArch::new()); + let allocator = DummyAllocator::new(arch.clone()); let allocator_clone = allocator.clone(); - let arch = DummyArch; - let pt = PageTableInternal::new(allocator, &arch, PagingType::Paging4Level).unwrap(); + let pt = PageTableInternal::new(allocator, &*arch, PagingType::Paging4Level).unwrap(); (pt, allocator_clone, arch) } #[test] - #[serial] fn test_get_state_variants() { let (pt, allocator, arch) = make_table(); @@ -1304,11 +1320,11 @@ mod tests { }; // By default, the table is not active, so should be Inactive - ACTIVE.store(false, std::sync::atomic::Ordering::Relaxed); + arch.set_active(false); assert_eq!(pt.get_state(&arch), PageTableState::Inactive); // Set table as active, but self-map entry is not present or doesn't match base - ACTIVE.store(true, std::sync::atomic::Ordering::Relaxed); + arch.set_active(true); // Overwrite the self-map entry to not present let root_level = PageLevel::root_level(pt.paging_type); @@ -1334,7 +1350,6 @@ mod tests { } #[test] - #[serial] fn test_validate_address_range() { let (pt, allocator, _) = make_table(); @@ -1347,7 +1362,6 @@ mod tests { } #[test] - #[serial] fn test_allocate_page_alignment() { let (mut pt, allocator, arch) = make_table(); let pa: u64 = pt.allocate_page(&arch, PageTableState::Inactive).unwrap().into(); @@ -1357,7 +1371,6 @@ mod tests { } #[test] - #[serial] fn test_split_large_page_error() { let (mut pt, allocator, arch) = make_table(); let mut entry = DummyPTE::new(); @@ -1382,7 +1395,7 @@ mod tests { assert!(!ptr.is_null()); let base_pa = PhysicalAddress::new(ptr as u64); - let arch = DummyArch; + let arch = DummyArch::new(); let res = PageTableRange::::new( &arch, PageLevel::Level1, @@ -1391,7 +1404,8 @@ mod tests { PagingType::Paging4Level, PageTableStateWithAddress::NotSelfMapped(base_pa), ); - assert_eq!(res, Err(PtError::InvalidMemoryRange)); + assert!(res.is_err()); + assert_eq!(res.unwrap_err(), PtError::InvalidMemoryRange); // Clean up the manually allocated memory unsafe { @@ -1400,7 +1414,6 @@ mod tests { } #[test] - #[serial] fn test_dump_page_tables_invalid_range() { let (pt, allocator, arch) = make_table(); let res = pt.dump_page_tables(&arch, 0x1001, 0x1000); @@ -1411,8 +1424,8 @@ mod tests { #[test] fn test_from_existing_unaligned() { - let allocator = DummyAllocator::new(); - let arch = DummyArch; + let arch = Rc::new(DummyArch::new()); + let allocator = DummyAllocator::new(arch.clone()); let res = unsafe { PageTableInternal::::from_existing( allocator.clone(), @@ -1427,7 +1440,6 @@ mod tests { } #[test] - #[serial] fn test_map_memory_region_top_va_overflow() { let (mut pt, allocator, arch) = make_table(); // max_va is 0xFFFF_FFFF_FFFF_0000, so use an address near the top and a size that overflows @@ -1440,7 +1452,6 @@ mod tests { } #[test] - #[serial] fn test_unmap_memory_region_top_va_overflow() { let (mut pt, allocator, arch) = make_table(); let addr = 0xFFFF_FFFF_FFFF_0000; @@ -1452,7 +1463,6 @@ mod tests { } #[test] - #[serial] fn test_iter_mapped_regions_self_mapped_state() { // Exercise the iterator's self-mapped state handling. `DummyArch` resolves every self-mapped // level to the page table base, so the walk reads the root table for each level. A freshly