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/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..a1f64d8 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 }) } } @@ -1097,48 +1145,64 @@ 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; 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) + self.base.load(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 { - ACTIVE.load(std::sync::atomic::Ordering::Relaxed) + fn is_table_active(&self, _base: u64) -> bool { + self.is_table_active.load(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)] @@ -1201,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) { @@ -1230,24 +1295,24 @@ 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) { - 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 pt = PageTableInternal::new(allocator, PagingType::Paging4Level).unwrap(); - (pt, allocator_clone) + 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 = || { @@ -1255,15 +1320,16 @@ 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); + 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); let entry = get_entry::( + &arch, root_level, pt.paging_type, PageTableStateWithAddress::NotSelfMapped(pt.base), @@ -1272,21 +1338,20 @@ 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(); } #[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)); @@ -1297,23 +1362,26 @@ 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(); } #[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,14 +1395,17 @@ mod tests { assert!(!ptr.is_null()); let base_pa = PhysicalAddress::new(ptr as u64); + let arch = DummyArch::new(); let res = PageTableRange::::new( + &arch, PageLevel::Level1, VirtualAddress::new(3), VirtualAddress::new(2), 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 { @@ -1343,10 +1414,9 @@ 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(); @@ -1354,11 +1424,13 @@ mod tests { #[test] fn test_from_existing_unaligned() { - let allocator = DummyAllocator::new(); + let arch = Rc::new(DummyArch::new()); + let allocator = DummyAllocator::new(arch.clone()); let res = unsafe { PageTableInternal::::from_existing( - 0x123, allocator.clone(), + &arch, + 0x123, PagingType::Paging4Level, ) }; @@ -1368,41 +1440,38 @@ 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(); } #[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(); } #[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 // 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