Skip to content

Commit d944b9e

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

File tree

6 files changed

+766
-1
lines changed

6 files changed

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

0 commit comments

Comments
 (0)