Skip to content

Commit 142b61c

Browse files
committed
feat(mapper): add MappedPageTable::display
1 parent 5d8fdda commit 142b61c

File tree

6 files changed

+808
-1
lines changed

6 files changed

+808
-1
lines changed

Changelog.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
- `OffsetPageTable`'s `PageTableFrameMapping` implementation is now public as `PhysOffset`.
1010
- [make range types `!Copy`](https://github.com/rust-osdev/x86_64/pull/581)
1111
- To migrate, use `.clone()` if necessary.
12+
- [add `MappedPageTable::display`](https://github.com/rust-osdev/x86_64/pull/574)
13+
- The mappings of a `MappedPageTable` can now be displayed.
1214

1315
# 0.15.4 – 2025-11-24
1416

Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
//! Display adapters for [`MappedPageTable`].
2+
3+
use core::fmt::{self, Write};
4+
5+
use super::range_iter::{MappedPageRangeInclusive, MappedPageRangeInclusiveItem};
6+
use super::{MappedPageTable, PageTableFrameMapping};
7+
use crate::structures::paging::frame::PhysFrameRangeInclusive;
8+
use crate::structures::paging::page::PageRangeInclusive;
9+
use crate::structures::paging::{PageSize, PageTableFlags};
10+
11+
impl<P: PageTableFrameMapping> MappedPageTable<'_, P> {
12+
/// Display the page table mappings as a human-readable table.
13+
///
14+
/// This method returns an object that implements [`fmt::Display`].
15+
/// For details, see [`MappedPageTableDisplay`].
16+
///
17+
/// # Examples
18+
///
19+
/// ```ignore-i686
20+
/// use x86_64::structures::paging::MappedPageTable;
21+
///
22+
/// # let level_4_table = &mut x86_64::structures::paging::page_table::PageTable::new();
23+
/// # let phys_offset = x86_64::VirtAddr::zero();
24+
/// let page_table = unsafe { MappedPageTable::from_phys_offset(level_4_table, phys_offset) };
25+
///
26+
/// println!("{}", page_table.display());
27+
/// ```
28+
///
29+
/// [`MappedPageTableDisplay`]: Display
30+
pub fn display(&self) -> Display<'_, P> {
31+
Display { page_table: self }
32+
}
33+
}
34+
35+
/// [`Display`] adapter for [`MappedPageTable`].
36+
///
37+
/// This struct formats as a human-readable version of the page table mappings when used with [`format_args!`] and `{}`.
38+
/// It is created using [`MappedPageTable::display`].
39+
///
40+
/// This struct also supports formatting with the alternate (`#`) flag for aligned columns with table headers.
41+
///
42+
/// # Examples
43+
///
44+
/// ```ignore-i686
45+
/// use x86_64::structures::paging::MappedPageTable;
46+
///
47+
/// # let level_4_table = &mut x86_64::structures::paging::page_table::PageTable::new();
48+
/// # let phys_offset = x86_64::VirtAddr::zero();
49+
/// let page_table = unsafe { MappedPageTable::from_phys_offset(level_4_table, phys_offset) };
50+
///
51+
/// println!("{}", page_table.display());
52+
/// ```
53+
///
54+
/// This is how a formatted table looks like:
55+
///
56+
/// ```text
57+
/// 100000-101000 100000-101000 WRITABLE | ACCESSED | DIRTY
58+
/// 101000-103000 101000-103000 WRITABLE | ACCESSED
59+
/// 103000-105000 103000-105000 WRITABLE
60+
/// 105000-106000 105000-106000 WRITABLE | ACCESSED
61+
/// 106000-107000 106000-107000 WRITABLE
62+
/// 107000-10d000 107000-10d000 WRITABLE | ACCESSED
63+
/// 10d000-111000 10d000-111000 WRITABLE
64+
/// 111000-112000 111000-112000 WRITABLE | ACCESSED
65+
/// 112000-114000 112000-114000 WRITABLE
66+
/// 114000-118000 114000-118000 WRITABLE | ACCESSED
67+
/// 118000-119000 118000-119000 WRITABLE
68+
/// 119000-11a000 119000-11a000 WRITABLE | ACCESSED
69+
/// 11a000-11b000 11a000-11b000 WRITABLE
70+
/// 11b000-11c000 11b000-11c000 WRITABLE | ACCESSED | DIRTY
71+
/// 11c000-120000 11c000-120000 WRITABLE | ACCESSED
72+
/// 120000-121000 120000-121000 WRITABLE
73+
/// 121000-122000 121000-122000 WRITABLE | ACCESSED | DIRTY
74+
/// 122000-123000 122000-123000 WRITABLE
75+
/// 123000-124000 123000-124000 WRITABLE | ACCESSED | DIRTY
76+
/// 124000-125000 124000-125000 WRITABLE
77+
/// ffffff8000000000-ffffff8000001000 11f000-120000 WRITABLE | ACCESSED
78+
/// ffffff8000001000-ffffff8000002000 120000-121000 WRITABLE
79+
/// ffffffffc0000000-ffffffffc0001000 11e000-11f000 WRITABLE | ACCESSED
80+
/// ffffffffffe00000-ffffffffffe01000 11d000-11e000 WRITABLE | ACCESSED
81+
/// fffffffffffff000- 11c000-11d000 WRITABLE
82+
/// ```
83+
///
84+
/// This is how a table formatted with the alternate (`#`) flag looks like:
85+
///
86+
/// ```text
87+
/// size len virtual address physical address flags
88+
/// 4KiB 1 100000- 101000 identity-mapped WRITABLE | ACCESSED | DIRTY
89+
/// 4KiB 2 101000- 103000 identity-mapped WRITABLE | ACCESSED
90+
/// 4KiB 2 103000- 105000 identity-mapped WRITABLE
91+
/// 4KiB 1 105000- 106000 identity-mapped WRITABLE | ACCESSED
92+
/// 4KiB 1 106000- 107000 identity-mapped WRITABLE
93+
/// 4KiB 7 107000- 10e000 identity-mapped WRITABLE | ACCESSED
94+
/// 4KiB 3 10e000- 111000 identity-mapped WRITABLE
95+
/// 4KiB 1 111000- 112000 identity-mapped WRITABLE | ACCESSED
96+
/// 4KiB 2 112000- 114000 identity-mapped WRITABLE
97+
/// 4KiB 4 114000- 118000 identity-mapped WRITABLE | ACCESSED
98+
/// 4KiB 1 118000- 119000 identity-mapped WRITABLE
99+
/// 4KiB 1 119000- 11a000 identity-mapped WRITABLE | ACCESSED
100+
/// 4KiB 1 11a000- 11b000 identity-mapped WRITABLE
101+
/// 4KiB 1 11b000- 11c000 identity-mapped WRITABLE | ACCESSED | DIRTY
102+
/// 4KiB 5 11c000- 121000 identity-mapped WRITABLE | ACCESSED
103+
/// 4KiB 1 121000- 122000 identity-mapped WRITABLE | ACCESSED | DIRTY
104+
/// 4KiB 1 122000- 123000 identity-mapped WRITABLE
105+
/// 4KiB 1 123000- 124000 identity-mapped WRITABLE | ACCESSED | DIRTY
106+
/// 4KiB 1 124000- 125000 identity-mapped WRITABLE
107+
/// 4KiB 1 ffffff8000000000-ffffff8000001000 11f000- 120000 WRITABLE | ACCESSED
108+
/// 4KiB 1 ffffff8000001000-ffffff8000002000 120000- 121000 WRITABLE
109+
/// 4KiB 1 ffffffffc0000000-ffffffffc0001000 11e000- 11f000 WRITABLE | ACCESSED
110+
/// 4KiB 1 ffffffffffe00000-ffffffffffe01000 11d000- 11e000 WRITABLE | ACCESSED
111+
/// 4KiB 1 fffffffffffff000- 11c000- 11d000 WRITABLE
112+
/// ```
113+
///
114+
/// [`Display`]: fmt::Display
115+
pub struct Display<'a, P: PageTableFrameMapping> {
116+
page_table: &'a MappedPageTable<'a, P>,
117+
}
118+
119+
impl<P: PageTableFrameMapping + fmt::Debug> fmt::Debug for Display<'_, P> {
120+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
121+
fmt::Debug::fmt(&self.page_table, f)
122+
}
123+
}
124+
125+
impl<P: PageTableFrameMapping> fmt::Display for Display<'_, P> {
126+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
127+
let mut has_fields = false;
128+
129+
if f.alternate() {
130+
write!(
131+
f,
132+
"size {:>5} {:>33} {:>33} flags",
133+
"len", "virtual address", "physical address"
134+
)?;
135+
has_fields = true;
136+
}
137+
138+
for mapped_page_range in self.page_table.range_iter() {
139+
if has_fields {
140+
f.write_char('\n')?;
141+
}
142+
fmt::Display::fmt(&mapped_page_range.display(), f)?;
143+
144+
has_fields = true;
145+
}
146+
147+
Ok(())
148+
}
149+
}
150+
151+
/// A helper struct for formatting a [`MappedPageRangeInclusiveItem`] as a table row.
152+
struct MappedPageRangeInclusiveItemDisplay<'a> {
153+
item: &'a MappedPageRangeInclusiveItem,
154+
}
155+
156+
impl MappedPageRangeInclusiveItem {
157+
fn display(&self) -> MappedPageRangeInclusiveItemDisplay<'_> {
158+
MappedPageRangeInclusiveItemDisplay { item: self }
159+
}
160+
}
161+
162+
impl fmt::Display for MappedPageRangeInclusiveItemDisplay<'_> {
163+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
164+
match self.item {
165+
MappedPageRangeInclusiveItem::Size4KiB(range) => fmt::Display::fmt(&range.display(), f),
166+
MappedPageRangeInclusiveItem::Size2MiB(range) => fmt::Display::fmt(&range.display(), f),
167+
MappedPageRangeInclusiveItem::Size1GiB(range) => fmt::Display::fmt(&range.display(), f),
168+
}
169+
}
170+
}
171+
172+
/// A helper struct for formatting a [`MappedPageRangeInclusive`] as a table row.
173+
struct MappedPageRangeInclusiveDisplay<'a, S: PageSize> {
174+
range: &'a MappedPageRangeInclusive<S>,
175+
}
176+
177+
impl<S: PageSize> MappedPageRangeInclusive<S> {
178+
fn display(&self) -> MappedPageRangeInclusiveDisplay<'_, S> {
179+
MappedPageRangeInclusiveDisplay { range: self }
180+
}
181+
}
182+
183+
impl<S: PageSize> fmt::Display for MappedPageRangeInclusiveDisplay<'_, S> {
184+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
185+
if f.alternate() {
186+
let size = S::DEBUG_STR;
187+
write!(f, "{size} ")?;
188+
189+
let len = self.range.len();
190+
write!(f, "{len:5} ")?;
191+
}
192+
193+
let page_range = self.range.page_range();
194+
// Forward the formatter's options such as the alternate (`#`) flag.
195+
fmt::Pointer::fmt(&page_range.display(), f)?;
196+
f.write_char(' ')?;
197+
198+
if f.alternate() && self.range.is_identity_mapped() {
199+
write!(f, "{:>33}", "identity-mapped")?;
200+
} else {
201+
let frame_range = self.range.frame_range();
202+
// Forward the formatter's options such as the alternate (`#`) flag.
203+
fmt::Pointer::fmt(&frame_range.display(), f)?;
204+
}
205+
f.write_char(' ')?;
206+
207+
// Every entry is present, don't print it explicitly.
208+
let flags = self.range.flags() - PageTableFlags::PRESENT;
209+
// Format the flags as `A | B` instead of `Flags(A | B)`.
210+
bitflags::parser::to_writer(&flags, &mut *f)?;
211+
212+
Ok(())
213+
}
214+
}
215+
216+
/// A helper type for formatting an address range as [`fmt::Pointer`].
217+
struct AddressRangeDisplay<T> {
218+
start: T,
219+
end: Option<T>,
220+
}
221+
222+
impl<S: PageSize> PageRangeInclusive<S> {
223+
fn display(&self) -> AddressRangeDisplay<u64> {
224+
let start = self.start.start_address().as_u64();
225+
let end = self.end.start_address().as_u64().checked_add(S::SIZE);
226+
AddressRangeDisplay { start, end }
227+
}
228+
}
229+
230+
impl<S: PageSize> PhysFrameRangeInclusive<S> {
231+
fn display(&self) -> AddressRangeDisplay<u64> {
232+
let start = self.start.start_address().as_u64();
233+
let end = self.end.start_address().as_u64().checked_add(S::SIZE);
234+
AddressRangeDisplay { start, end }
235+
}
236+
}
237+
238+
impl<T: fmt::LowerHex> fmt::Pointer for AddressRangeDisplay<T> {
239+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
240+
let Self { start, end } = self;
241+
match (end, f.alternate()) {
242+
(Some(end), false) => write!(f, "{start:x}-{end:x}"),
243+
(Some(end), true) => write!(f, "{start:16x}-{end:16x}"),
244+
(None, false) => write!(f, "{start:x}-{:16}", ""),
245+
(None, true) => write!(f, "{start:16x}-{:16}", ""),
246+
}
247+
}
248+
}

0 commit comments

Comments
 (0)