From fc03c8fbbdc6c184b88bdf59c32c85d6da64322f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Kr=C3=B6ning?= Date: Tue, 13 Jan 2026 13:47:59 +0100 Subject: [PATCH 1/3] refactor(mapper): move mapped_page_table.rs into directory --- .../mapper/{mapped_page_table.rs => mapped_page_table/mod.rs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/structures/paging/mapper/{mapped_page_table.rs => mapped_page_table/mod.rs} (100%) diff --git a/src/structures/paging/mapper/mapped_page_table.rs b/src/structures/paging/mapper/mapped_page_table/mod.rs similarity index 100% rename from src/structures/paging/mapper/mapped_page_table.rs rename to src/structures/paging/mapper/mapped_page_table/mod.rs From 43814cff5c30bdf171c030b402d31a01a36282a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Kr=C3=B6ning?= Date: Tue, 13 Jan 2026 13:49:50 +0100 Subject: [PATCH 2/3] refactor(mapper): move offset_page_table into mapped_page_table --- src/structures/paging/mapper/mapped_page_table/mod.rs | 4 ++++ .../mapper/{ => mapped_page_table}/offset_page_table.rs | 0 src/structures/paging/mapper/mod.rs | 3 +-- 3 files changed, 5 insertions(+), 2 deletions(-) rename src/structures/paging/mapper/{ => mapped_page_table}/offset_page_table.rs (100%) diff --git a/src/structures/paging/mapper/mapped_page_table/mod.rs b/src/structures/paging/mapper/mapped_page_table/mod.rs index 14b70069..2b36788a 100644 --- a/src/structures/paging/mapper/mapped_page_table/mod.rs +++ b/src/structures/paging/mapper/mapped_page_table/mod.rs @@ -1,3 +1,7 @@ +mod offset_page_table; + +#[cfg(target_pointer_width = "64")] +pub use self::offset_page_table::{OffsetPageTable, PhysOffset}; use crate::structures::paging::{ mapper::*, page::AddressNotAligned, diff --git a/src/structures/paging/mapper/offset_page_table.rs b/src/structures/paging/mapper/mapped_page_table/offset_page_table.rs similarity index 100% rename from src/structures/paging/mapper/offset_page_table.rs rename to src/structures/paging/mapper/mapped_page_table/offset_page_table.rs diff --git a/src/structures/paging/mapper/mod.rs b/src/structures/paging/mapper/mod.rs index 4064bc2d..578b85e9 100644 --- a/src/structures/paging/mapper/mod.rs +++ b/src/structures/paging/mapper/mod.rs @@ -2,7 +2,7 @@ pub use self::mapped_page_table::{MappedPageTable, PageTableFrameMapping}; #[cfg(target_pointer_width = "64")] -pub use self::offset_page_table::{OffsetPageTable, PhysOffset}; +pub use self::mapped_page_table::{OffsetPageTable, PhysOffset}; #[cfg(all(feature = "instructions", target_arch = "x86_64"))] pub use self::recursive_page_table::{InvalidPageTable, RecursivePageTable}; @@ -15,7 +15,6 @@ use crate::structures::paging::{ use crate::{PhysAddr, VirtAddr}; mod mapped_page_table; -mod offset_page_table; #[cfg(all(feature = "instructions", target_arch = "x86_64"))] mod recursive_page_table; From a652bf301329e9a71ba4acd1edc55821aa7e44d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Kr=C3=B6ning?= Date: Wed, 7 Jan 2026 16:32:55 +0100 Subject: [PATCH 3/3] feat(mapper): add `MappedPageTable::display` --- Changelog.md | 2 + .../mapper/mapped_page_table/display.rs | 251 ++++++++++++++ .../paging/mapper/mapped_page_table/iter.rs | 320 ++++++++++++++++++ .../paging/mapper/mapped_page_table/mod.rs | 4 + .../mapper/mapped_page_table/range_iter.rs | 186 ++++++++++ src/structures/paging/mapper/mod.rs | 4 +- 6 files changed, 766 insertions(+), 1 deletion(-) create mode 100644 src/structures/paging/mapper/mapped_page_table/display.rs create mode 100644 src/structures/paging/mapper/mapped_page_table/iter.rs create mode 100644 src/structures/paging/mapper/mapped_page_table/range_iter.rs diff --git a/Changelog.md b/Changelog.md index d0281685..c8b4974f 100644 --- a/Changelog.md +++ b/Changelog.md @@ -10,6 +10,8 @@ - [make range types `!Copy`](https://github.com/rust-osdev/x86_64/pull/581) - To migrate, use `.clone()` if necessary. - [make page types `repr(transparent)` and range types `repr(Rust)`](https://github.com/rust-osdev/x86_64/pull/584) +- [add `MappedPageTable::display`](https://github.com/rust-osdev/x86_64/pull/574) + - The mappings of a `MappedPageTable` can now be displayed. # 0.15.4 – 2025-11-24 diff --git a/src/structures/paging/mapper/mapped_page_table/display.rs b/src/structures/paging/mapper/mapped_page_table/display.rs new file mode 100644 index 00000000..c254f60a --- /dev/null +++ b/src/structures/paging/mapper/mapped_page_table/display.rs @@ -0,0 +1,251 @@ +//! Display adapters for [`MappedPageTable`]. + +use core::fmt::{self, Write}; + +use super::range_iter::{MappedPageRangeInclusive, MappedPageRangeInclusiveItem}; +use super::{MappedPageTable, PageTableFrameMapping}; +use crate::structures::paging::frame::PhysFrameRangeInclusive; +use crate::structures::paging::page::PageRangeInclusive; +use crate::structures::paging::{PageSize, PageTableFlags}; + +impl MappedPageTable<'_, P> { + /// Display the page table mappings as a human-readable table. + /// + /// This method returns an object that implements [`fmt::Display`]. + /// For details, see [`MappedPageTableDisplay`]. + /// + /// # Examples + /// + /// ```ignore-i686 + /// use x86_64::structures::paging::MappedPageTable; + /// + /// # let level_4_table = &mut x86_64::structures::paging::page_table::PageTable::new(); + /// # let phys_offset = x86_64::VirtAddr::zero(); + /// let page_table = unsafe { MappedPageTable::from_phys_offset(level_4_table, phys_offset) }; + /// + /// println!("{}", page_table.display()); + /// ``` + /// + /// [`MappedPageTableDisplay`]: Display + pub fn display(&self) -> Display<'_, P> { + Display { page_table: self } + } +} + +/// [`Display`] adapter for [`MappedPageTable`]. +/// +/// This struct formats as a human-readable version of the page table mappings when used with [`format_args!`] and `{}`. +/// It is created using [`MappedPageTable::display`]. +/// +/// This struct also supports formatting with the alternate (`#`) flag for aligned columns with table headers. +/// +/// Note that the [`PRESENT`] flag is not listed explicitly, since only present mappings are formatted. +/// +/// # Examples +/// +/// ```ignore-i686 +/// use x86_64::structures::paging::MappedPageTable; +/// +/// # let level_4_table = &mut x86_64::structures::paging::page_table::PageTable::new(); +/// # let phys_offset = x86_64::VirtAddr::zero(); +/// let page_table = unsafe { MappedPageTable::from_phys_offset(level_4_table, phys_offset) }; +/// +/// println!("{}", page_table.display()); +/// ``` +/// +/// This is how a formatted table looks like: +/// +/// ```text +/// 100000-101000 100000-101000 WRITABLE | ACCESSED | DIRTY +/// 101000-103000 101000-103000 WRITABLE | ACCESSED +/// 103000-105000 103000-105000 WRITABLE +/// 105000-106000 105000-106000 WRITABLE | ACCESSED +/// 106000-107000 106000-107000 WRITABLE +/// 107000-10d000 107000-10d000 WRITABLE | ACCESSED +/// 10d000-111000 10d000-111000 WRITABLE +/// 111000-112000 111000-112000 WRITABLE | ACCESSED +/// 112000-114000 112000-114000 WRITABLE +/// 114000-118000 114000-118000 WRITABLE | ACCESSED +/// 118000-119000 118000-119000 WRITABLE +/// 119000-11a000 119000-11a000 WRITABLE | ACCESSED +/// 11a000-11b000 11a000-11b000 WRITABLE +/// 11b000-11c000 11b000-11c000 WRITABLE | ACCESSED | DIRTY +/// 11c000-120000 11c000-120000 WRITABLE | ACCESSED +/// 120000-121000 120000-121000 WRITABLE +/// 121000-122000 121000-122000 WRITABLE | ACCESSED | DIRTY +/// 122000-123000 122000-123000 WRITABLE +/// 123000-124000 123000-124000 WRITABLE | ACCESSED | DIRTY +/// 124000-125000 124000-125000 WRITABLE +/// ffffff8000000000-ffffff8000001000 11f000-120000 WRITABLE | ACCESSED +/// ffffff8000001000-ffffff8000002000 120000-121000 WRITABLE +/// ffffffffc0000000-ffffffffc0001000 11e000-11f000 WRITABLE | ACCESSED +/// ffffffffffe00000-ffffffffffe01000 11d000-11e000 WRITABLE | ACCESSED +/// fffffffffffff000- 11c000-11d000 WRITABLE +/// ``` +/// +/// This is how a table formatted with the alternate (`#`) flag looks like: +/// +/// ```text +/// size len virtual address physical address flags +/// 4KiB 1 100000- 101000 identity-mapped WRITABLE | ACCESSED | DIRTY +/// 4KiB 2 101000- 103000 identity-mapped WRITABLE | ACCESSED +/// 4KiB 2 103000- 105000 identity-mapped WRITABLE +/// 4KiB 1 105000- 106000 identity-mapped WRITABLE | ACCESSED +/// 4KiB 1 106000- 107000 identity-mapped WRITABLE +/// 4KiB 7 107000- 10e000 identity-mapped WRITABLE | ACCESSED +/// 4KiB 3 10e000- 111000 identity-mapped WRITABLE +/// 4KiB 1 111000- 112000 identity-mapped WRITABLE | ACCESSED +/// 4KiB 2 112000- 114000 identity-mapped WRITABLE +/// 4KiB 4 114000- 118000 identity-mapped WRITABLE | ACCESSED +/// 4KiB 1 118000- 119000 identity-mapped WRITABLE +/// 4KiB 1 119000- 11a000 identity-mapped WRITABLE | ACCESSED +/// 4KiB 1 11a000- 11b000 identity-mapped WRITABLE +/// 4KiB 1 11b000- 11c000 identity-mapped WRITABLE | ACCESSED | DIRTY +/// 4KiB 5 11c000- 121000 identity-mapped WRITABLE | ACCESSED +/// 4KiB 1 121000- 122000 identity-mapped WRITABLE | ACCESSED | DIRTY +/// 4KiB 1 122000- 123000 identity-mapped WRITABLE +/// 4KiB 1 123000- 124000 identity-mapped WRITABLE | ACCESSED | DIRTY +/// 4KiB 1 124000- 125000 identity-mapped WRITABLE +/// 4KiB 1 ffffff8000000000-ffffff8000001000 11f000- 120000 WRITABLE | ACCESSED +/// 4KiB 1 ffffff8000001000-ffffff8000002000 120000- 121000 WRITABLE +/// 4KiB 1 ffffffffc0000000-ffffffffc0001000 11e000- 11f000 WRITABLE | ACCESSED +/// 4KiB 1 ffffffffffe00000-ffffffffffe01000 11d000- 11e000 WRITABLE | ACCESSED +/// 4KiB 1 fffffffffffff000- 11c000- 11d000 WRITABLE +/// ``` +/// +/// [`Display`]: fmt::Display +/// [`PRESENT`]: PageTableFlags::PRESENT +pub struct Display<'a, P: PageTableFrameMapping> { + page_table: &'a MappedPageTable<'a, P>, +} + +impl fmt::Debug for Display<'_, P> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&self.page_table, f) + } +} + +impl fmt::Display for Display<'_, P> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut has_fields = false; + + if f.alternate() { + write!( + f, + "size {:>5} {:>33} {:>33} flags", + "len", "virtual address", "physical address" + )?; + has_fields = true; + } + + for mapped_page_range in self.page_table.range_iter() { + if has_fields { + f.write_char('\n')?; + } + fmt::Display::fmt(&mapped_page_range.display(), f)?; + + has_fields = true; + } + + Ok(()) + } +} + +/// A helper struct for formatting a [`MappedPageRangeInclusiveItem`] as a table row. +struct MappedPageRangeInclusiveItemDisplay<'a> { + item: &'a MappedPageRangeInclusiveItem, +} + +impl MappedPageRangeInclusiveItem { + fn display(&self) -> MappedPageRangeInclusiveItemDisplay<'_> { + MappedPageRangeInclusiveItemDisplay { item: self } + } +} + +impl fmt::Display for MappedPageRangeInclusiveItemDisplay<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.item { + MappedPageRangeInclusiveItem::Size4KiB(range) => fmt::Display::fmt(&range.display(), f), + MappedPageRangeInclusiveItem::Size2MiB(range) => fmt::Display::fmt(&range.display(), f), + MappedPageRangeInclusiveItem::Size1GiB(range) => fmt::Display::fmt(&range.display(), f), + } + } +} + +/// A helper struct for formatting a [`MappedPageRangeInclusive`] as a table row. +struct MappedPageRangeInclusiveDisplay<'a, S: PageSize> { + range: &'a MappedPageRangeInclusive, +} + +impl MappedPageRangeInclusive { + fn display(&self) -> MappedPageRangeInclusiveDisplay<'_, S> { + MappedPageRangeInclusiveDisplay { range: self } + } +} + +impl fmt::Display for MappedPageRangeInclusiveDisplay<'_, S> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if f.alternate() { + let size = S::DEBUG_STR; + write!(f, "{size} ")?; + + let len = self.range.len(); + write!(f, "{len:5} ")?; + } + + let page_range = self.range.page_range(); + // Forward the formatter's options such as the alternate (`#`) flag. + fmt::Pointer::fmt(&page_range.display(), f)?; + f.write_char(' ')?; + + if f.alternate() && self.range.is_identity_mapped() { + write!(f, "{:>33}", "identity-mapped")?; + } else { + let frame_range = self.range.frame_range(); + // Forward the formatter's options such as the alternate (`#`) flag. + fmt::Pointer::fmt(&frame_range.display(), f)?; + } + f.write_char(' ')?; + + // Every entry is present, don't print it explicitly. + let flags = self.range.flags() - PageTableFlags::PRESENT; + // Format the flags as `A | B` instead of `Flags(A | B)`. + bitflags::parser::to_writer(&flags, &mut *f)?; + + Ok(()) + } +} + +/// A helper type for formatting an address range as [`fmt::Pointer`]. +struct AddressRangeDisplay { + start: T, + end: Option, +} + +impl PageRangeInclusive { + fn display(&self) -> AddressRangeDisplay { + let start = self.start.start_address().as_u64(); + let end = self.end.start_address().as_u64().checked_add(S::SIZE); + AddressRangeDisplay { start, end } + } +} + +impl PhysFrameRangeInclusive { + fn display(&self) -> AddressRangeDisplay { + let start = self.start.start_address().as_u64(); + let end = self.end.start_address().as_u64().checked_add(S::SIZE); + AddressRangeDisplay { start, end } + } +} + +impl fmt::Pointer for AddressRangeDisplay { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { start, end } = self; + match (end, f.alternate()) { + (Some(end), false) => write!(f, "{start:x}-{end:x}"), + (Some(end), true) => write!(f, "{start:16x}-{end:16x}"), + (None, false) => write!(f, "{start:x}-{:16}", ""), + (None, true) => write!(f, "{start:16x}-{:16}", ""), + } + } +} diff --git a/src/structures/paging/mapper/mapped_page_table/iter.rs b/src/structures/paging/mapper/mapped_page_table/iter.rs new file mode 100644 index 00000000..507fbf35 --- /dev/null +++ b/src/structures/paging/mapper/mapped_page_table/iter.rs @@ -0,0 +1,320 @@ +//! Iterator over [`MappedPageTable`]s. +//! +//! The main type of this module is [`MappedPageTableIter`] returning [`MappedPageItem`]s. + +use core::ops::Add; + +use super::{MappedPageTable, PageTableFrameMapping, PageTableWalkError, PageTableWalker}; +use crate::structures::paging::{ + Page, PageSize, PageTable, PageTableFlags, PageTableIndex, PhysFrame, Size1GiB, Size2MiB, + Size4KiB, +}; + +/// A mapped page. +#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] +pub struct MappedPage { + /// The page of this mapping. + pub page: Page, + + /// The frame of this mapping. + pub frame: PhysFrame, + + /// The page table flags of this mapping. + pub flags: PageTableFlags, +} + +impl Add for MappedPage { + type Output = Self; + + fn add(self, rhs: u64) -> Self::Output { + Self { + page: self.page + rhs, + frame: self.frame + rhs, + flags: self.flags, + } + } +} + +/// A [`MappedPage`] of any size. +#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] +pub enum MappedPageItem { + /// The [`MappedPage`] has a size of 4KiB. + Size4KiB(MappedPage), + + /// The [`MappedPage`] has a size of 2MiB. + Size2MiB(MappedPage), + + /// The [`MappedPage`] has a size of 1GiB. + Size1GiB(MappedPage), +} + +impl Add for MappedPageItem { + type Output = Self; + + fn add(self, rhs: u64) -> Self::Output { + match self { + Self::Size4KiB(mapped_page) => Self::Size4KiB(mapped_page + rhs), + Self::Size2MiB(mapped_page) => Self::Size2MiB(mapped_page + rhs), + Self::Size1GiB(mapped_page) => Self::Size1GiB(mapped_page + rhs), + } + } +} + +/// An iterator over a [`MappedPageTable`]. +/// +/// This iterator returns every mapped page individually as a [`MappedPageItem`]. +/// +/// This struct is created by [`MappedPageTable::iter`]. +/// +/// # Current implementation +/// +/// Performs a depth-first search for the next [`MappedPageItem`]. +pub struct MappedPageTableIter<'a, P: PageTableFrameMapping> { + page_table_walker: PageTableWalker

, + level_4_table: &'a PageTable, + p4_index: u16, + p3_index: u16, + p2_index: u16, + p1_index: u16, +} + +impl MappedPageTable<'_, P> { + /// Returns an iterator over the page table's [`MappedPageItem`]s. + // When making this public, add an `IntoIterator` impl for `&MappedPageTable<'_, P>` + pub(super) fn iter(&self) -> MappedPageTableIter<'_, &P> { + let page_table_walker = unsafe { PageTableWalker::new(self.page_table_frame_mapping()) }; + MappedPageTableIter { + page_table_walker, + level_4_table: self.level_4_table(), + p4_index: 0, + p3_index: 0, + p2_index: 0, + p1_index: 0, + } + } +} + +impl MappedPageTableIter<'_, P> { + /// Returns the current P4 index. + /// + /// When at then end, this returns [`None`]. + fn p4_index(&self) -> Option { + if self.p4_index == 512 { + return None; + } + + Some(PageTableIndex::new(self.p4_index)) + } + + /// Returns the current P3 index. + /// + /// When at then end, this returns [`None`]. + fn p3_index(&self) -> Option { + if self.p3_index == 512 { + return None; + } + + Some(PageTableIndex::new(self.p3_index)) + } + + /// Returns the current P2 index. + /// + /// When at then end, this returns [`None`]. + fn p2_index(&self) -> Option { + if self.p2_index == 512 { + return None; + } + + Some(PageTableIndex::new(self.p2_index)) + } + + /// Returns the current P1 index. + /// + /// When at then end, this returns [`None`]. + fn p1_index(&self) -> Option { + if self.p1_index == 512 { + return None; + } + + Some(PageTableIndex::new(self.p1_index)) + } + + /// Increments the current P4 index. + /// + /// This sets the lower indixes to zero. + /// When reaching the end, this returns [`None`] . + fn increment_p4_index(&mut self) -> Option<()> { + self.p4_index += 1; + self.p3_index = 0; + self.p2_index = 0; + self.p1_index = 0; + + if self.p4_index == 512 { + // There is no higher index to increment. + return None; + } + + Some(()) + } + + /// Increments the current P3 index. + /// + /// This sets the lower indixes to zero. + /// When reaching the end, this increments the next-higher index and returns [`None`]. + fn increment_p3_index(&mut self) -> Option<()> { + self.p3_index += 1; + self.p2_index = 0; + self.p1_index = 0; + + if self.p3_index == 512 { + self.increment_p4_index()?; + return None; + } + + Some(()) + } + + /// Increments the current P2 index. + /// + /// This sets the lower indixes to zero. + /// When reaching the end, this increments the next-higher index and returns [`None`]. + fn increment_p2_index(&mut self) -> Option<()> { + self.p2_index += 1; + self.p1_index = 0; + + if self.p2_index == 512 { + self.increment_p3_index()?; + return None; + } + + Some(()) + } + + /// Increments the current P1 index. + /// + /// When reaching the end, this increments the next-higher index and returns [`None`]. + fn increment_p1_index(&mut self) -> Option<()> { + self.p1_index += 1; + // There is no lower index to zero. + + if self.p1_index == 512 { + self.increment_p2_index()?; + return None; + } + + Some(()) + } + + /// Searches for the next [`MappedPageItem`] without backtracking. + /// + /// This method explores the page table along the next depth-first search branch. + /// It does not perform any backtracking and returns `None` when reaching a dead end. + fn next_forward(&mut self) -> Option { + let p4 = self.level_4_table; + + // Open the current P3 table. + let p3 = loop { + match self.page_table_walker.next_table(&p4[self.p4_index()?]) { + Ok(page_table) => break page_table, + Err(PageTableWalkError::NotMapped) => { + // This slot is empty. Try again with the next one. + self.increment_p4_index()?; + } + Err(PageTableWalkError::MappedToHugePage) => { + // We cannot return a 512GiB page. + // Ignore the error and try again with the next slot. + self.increment_p4_index()?; + } + } + }; + + // Open the current P2 table. + let p2 = loop { + match self.page_table_walker.next_table(&p3[self.p3_index()?]) { + Ok(page_table) => break page_table, + Err(PageTableWalkError::NotMapped) => { + // This slot is empty. Try again with the next one. + self.increment_p3_index()?; + } + Err(PageTableWalkError::MappedToHugePage) => { + // We have found a 1GiB page. + let page = + Page::from_page_table_indices_1gib(self.p4_index()?, self.p3_index()?); + let entry = &p3[self.p3_index()?]; + let frame = PhysFrame::containing_address(entry.addr()); + let flags = entry.flags(); + let mapped_page = MappedPageItem::Size1GiB(MappedPage { page, frame, flags }); + + // Make sure we don't land here next time. + self.increment_p3_index(); + return Some(mapped_page); + } + } + }; + + // Open the current P1 table. + let p1 = loop { + match self.page_table_walker.next_table(&p2[self.p2_index()?]) { + Ok(page_table) => break page_table, + Err(PageTableWalkError::NotMapped) => { + // This slot is empty. Try again with the next one. + self.increment_p2_index()?; + } + Err(PageTableWalkError::MappedToHugePage) => { + // We have found a 2MiB page. + let page = Page::from_page_table_indices_2mib( + self.p4_index()?, + self.p3_index()?, + self.p2_index()?, + ); + let entry = &p2[self.p2_index()?]; + let frame = PhysFrame::containing_address(entry.addr()); + let flags = entry.flags(); + let mapped_page = MappedPageItem::Size2MiB(MappedPage { page, frame, flags }); + + // Make sure we don't land here next time. + self.increment_p2_index(); + return Some(mapped_page); + } + } + }; + + while !p1[self.p1_index()?] + .flags() + .contains(PageTableFlags::PRESENT) + { + self.increment_p1_index()?; + } + + // We have found a 4KiB page. + let page = Page::from_page_table_indices( + self.p4_index()?, + self.p3_index()?, + self.p2_index()?, + self.p1_index()?, + ); + let entry = &p1[self.p1_index()?]; + let frame = PhysFrame::containing_address(entry.addr()); + let flags = entry.flags(); + let mapped_page = MappedPageItem::Size4KiB(MappedPage { page, frame, flags }); + + // Make sure we don't land here next time. + self.increment_p1_index(); + Some(mapped_page) + } +} + +impl Iterator for MappedPageTableIter<'_, P> { + type Item = MappedPageItem; + + fn next(&mut self) -> Option { + // Call `next_forward` until we have explored all P4 indexes. + while self.p4_index().is_some() { + if let Some(item) = self.next_forward() { + return Some(item); + } + } + + None + } +} diff --git a/src/structures/paging/mapper/mapped_page_table/mod.rs b/src/structures/paging/mapper/mapped_page_table/mod.rs index 2b36788a..5bec2a84 100644 --- a/src/structures/paging/mapper/mapped_page_table/mod.rs +++ b/src/structures/paging/mapper/mapped_page_table/mod.rs @@ -1,5 +1,9 @@ +mod display; +mod iter; mod offset_page_table; +mod range_iter; +pub use self::display::Display; #[cfg(target_pointer_width = "64")] pub use self::offset_page_table::{OffsetPageTable, PhysOffset}; use crate::structures::paging::{ diff --git a/src/structures/paging/mapper/mapped_page_table/range_iter.rs b/src/structures/paging/mapper/mapped_page_table/range_iter.rs new file mode 100644 index 00000000..6aa093f8 --- /dev/null +++ b/src/structures/paging/mapper/mapped_page_table/range_iter.rs @@ -0,0 +1,186 @@ +//! Range iterator over [`MappedPageTable`]s. +//! +//! The main type of this module is [`MappedPageTableRangeInclusiveIter`] returning [`MappedPageRangeInclusiveItem`]s. + +use core::convert::TryFrom; +use core::fmt; +use core::ops::RangeInclusive; + +use super::iter::{MappedPage, MappedPageItem, MappedPageTableIter}; +use super::{MappedPageTable, PageTableFrameMapping}; +use crate::structures::paging::frame::PhysFrameRangeInclusive; +use crate::structures::paging::page::PageRangeInclusive; +use crate::structures::paging::{ + PageSize, PageTableFlags, PhysFrame, Size1GiB, Size2MiB, Size4KiB, +}; + +/// A contiguous range of [`MappedPage`]s. +pub struct MappedPageRangeInclusive { + page_range: PageRangeInclusive, + frame_start: PhysFrame, + flags: PageTableFlags, +} + +impl MappedPageRangeInclusive { + /// Returns the page range. + pub fn page_range(&self) -> PageRangeInclusive { + self.page_range.clone() + } + + /// Returns the frame range. + pub fn frame_range(&self) -> PhysFrameRangeInclusive { + let start = self.frame_start; + let end = start + self.page_range.len() - 1; + PhysFrameRangeInclusive { start, end } + } + + /// Returns the page table flags. + pub fn flags(&self) -> PageTableFlags { + self.flags + } + + /// Returns the number of pages in the range. + pub fn len(&self) -> u64 { + self.page_range.len() + } + + /// Returns whether this is an identity mapping. + pub fn is_identity_mapped(&self) -> bool { + self.page_range.start.start_address().as_u64() == self.frame_start.start_address().as_u64() + } +} + +impl TryFrom>> for MappedPageRangeInclusive { + /// The type returned in the event of a conversion error. + type Error = TryFromMappedPageError; + + /// Tries to create a mapped page range from a range of mapped pages. + /// + /// This returns an error if the number of pages is not equal to the number of frames. + /// This also returns an error if the page table flags are not equal. + fn try_from(value: RangeInclusive>) -> Result { + let page_range = PageRangeInclusive { + start: value.start().page, + end: value.end().page, + }; + + let frame_start = value.start().frame; + let frame_range = PhysFrameRangeInclusive { + start: frame_start, + end: value.end().frame, + }; + if page_range.len() != frame_range.len() { + return Err(TryFromMappedPageError); + } + + let flags = value.start().flags; + if flags != value.end().flags { + return Err(TryFromMappedPageError); + } + + Ok(Self { + page_range, + frame_start, + flags, + }) + } +} + +/// A [`MappedPageRangeInclusive`] of any size. +pub enum MappedPageRangeInclusiveItem { + /// The [`MappedPageRangeInclusive`] has a size of 4KiB. + Size4KiB(MappedPageRangeInclusive), + + /// The [`MappedPageRangeInclusive`] has a size of 2MiB. + Size2MiB(MappedPageRangeInclusive), + + /// The [`MappedPageRangeInclusive`] has a size of 1GiB. + Size1GiB(MappedPageRangeInclusive), +} + +impl TryFrom> for MappedPageRangeInclusiveItem { + /// The type returned in the event of a conversion error. + type Error = TryFromMappedPageError; + + /// Tries to create a mapped page range from a range of mapped pages. + /// + /// This returns an error if the number of pages is not equal to the number of frames + /// or when the page sizes are not equal. + /// This also returns an error if the page table flags are not equal. + fn try_from(value: RangeInclusive) -> Result { + match (*value.start(), *value.end()) { + (MappedPageItem::Size4KiB(start), MappedPageItem::Size4KiB(end)) => { + let range = MappedPageRangeInclusive::try_from(start..=end)?; + Ok(Self::Size4KiB(range)) + } + (MappedPageItem::Size2MiB(start), MappedPageItem::Size2MiB(end)) => { + let range = MappedPageRangeInclusive::try_from(start..=end)?; + Ok(Self::Size2MiB(range)) + } + (MappedPageItem::Size1GiB(start), MappedPageItem::Size1GiB(end)) => { + let range = MappedPageRangeInclusive::try_from(start..=end)?; + Ok(Self::Size1GiB(range)) + } + (_, _) => Err(TryFromMappedPageError), + } + } +} + +/// The error type returned when a conversion from a range of mapped pages to mapped page range fails. +#[derive(PartialEq, Eq, Clone, Debug)] +pub struct TryFromMappedPageError; + +impl fmt::Display for TryFromMappedPageError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("provided mapped pages were not compatible") + } +} + +/// A range iterator over a [`MappedPageTable`]. +/// +/// This iterator returns every contiguous range of page mappings as a [`MappedPageRangeInclusiveItem`]. +/// +/// This struct is created by [`MappedPageTable::range_iter`]. +/// +/// # Current implementation +/// +/// Performs a depth-fist search for the next contiguous range of [`MappedPageItem`]s and returns it as a [`MappedPageRangeInclusiveItem`]. +pub struct MappedPageTableRangeInclusiveIter<'a, P: PageTableFrameMapping> { + iter: MappedPageTableIter<'a, P>, + next_start: Option, +} + +impl MappedPageTable<'_, P> { + /// Returns an iterator over the page table's [`MappedPageRangeInclusiveItem`]s. + pub(super) fn range_iter(&self) -> MappedPageTableRangeInclusiveIter<'_, &P> { + MappedPageTableRangeInclusiveIter { + iter: self.iter(), + next_start: None, + } + } +} + +impl Iterator for MappedPageTableRangeInclusiveIter<'_, P> { + type Item = MappedPageRangeInclusiveItem; + + fn next(&mut self) -> Option { + // Take the start item from last iteration or get a new one. + let start = self.next_start.take().or_else(|| self.iter.next())?; + + // Find the end of the current contiguous range. + let mut end = start; + for mapped_page in &mut self.iter { + if mapped_page != end + 1 { + // The current item is no longer contiguous to the current range, + // so save it for next time. + self.next_start = Some(mapped_page); + break; + } + + end = mapped_page; + } + + let range = MappedPageRangeInclusiveItem::try_from(start..=end).unwrap(); + Some(range) + } +} diff --git a/src/structures/paging/mapper/mod.rs b/src/structures/paging/mapper/mod.rs index 578b85e9..85d12fcb 100644 --- a/src/structures/paging/mapper/mod.rs +++ b/src/structures/paging/mapper/mod.rs @@ -1,6 +1,8 @@ //! Abstractions for reading and modifying the mapping of pages. -pub use self::mapped_page_table::{MappedPageTable, PageTableFrameMapping}; +pub use self::mapped_page_table::{ + Display as MappedPageTableDisplay, MappedPageTable, PageTableFrameMapping, +}; #[cfg(target_pointer_width = "64")] pub use self::mapped_page_table::{OffsetPageTable, PhysOffset}; #[cfg(all(feature = "instructions", target_arch = "x86_64"))]