Skip to content

Conversation

@mkroening
Copy link
Member

@mkroening mkroening commented Jan 7, 2026

Hi everyone! :)

In Hermit, we found it useful to be able to print our page tables for debugging. We've had several iterations of such debugging functions but are now happy with the current compact range-based table formatting introduced in hermit-os/kernel#1818.

It can be used as follows:

let mapped_page_table: MappedPageTable<'_, _>;

println!("{}", mapped_page_table.display());

This results in the following output on a fresh QEMU Multiboot page table:

size: 4KiB, len:     1, virt:           0x100000..=          0x100000, phys:                         identity mapped, flags: PageTableFlags(PRESENT | WRITABLE | ACCESSED | DIRTY)
size: 4KiB, len:     5, virt:           0x101000..=          0x105000, phys:                         identity mapped, flags: PageTableFlags(PRESENT | WRITABLE | ACCESSED)
size: 4KiB, len:     1, virt:           0x106000..=          0x106000, phys:                         identity mapped, flags: PageTableFlags(PRESENT | WRITABLE)
size: 4KiB, len:     3, virt:           0x107000..=          0x109000, phys:                         identity mapped, flags: PageTableFlags(PRESENT | WRITABLE | ACCESSED)
size: 4KiB, len:     9, virt:           0x10a000..=          0x112000, phys:                         identity mapped, flags: PageTableFlags(PRESENT | WRITABLE)
size: 4KiB, len:     5, virt:           0x113000..=          0x117000, phys:                         identity mapped, flags: PageTableFlags(PRESENT | WRITABLE | ACCESSED)
size: 4KiB, len:     1, virt:           0x118000..=          0x118000, phys:                         identity mapped, flags: PageTableFlags(PRESENT | WRITABLE)
size: 4KiB, len:     1, virt:           0x119000..=          0x119000, phys:                         identity mapped, flags: PageTableFlags(PRESENT | WRITABLE | ACCESSED)
size: 4KiB, len:     1, virt:           0x11a000..=          0x11a000, phys:                         identity mapped, flags: PageTableFlags(PRESENT | WRITABLE)
size: 4KiB, len:     3, virt:           0x11b000..=          0x11d000, phys:                         identity mapped, flags: PageTableFlags(PRESENT | WRITABLE | ACCESSED)
size: 4KiB, len:     1, virt:           0x11e000..=          0x11e000, phys:                         identity mapped, flags: PageTableFlags(PRESENT | WRITABLE)
size: 4KiB, len:     1, virt:           0x11f000..=          0x11f000, phys:                         identity mapped, flags: PageTableFlags(PRESENT | WRITABLE | ACCESSED)
size: 4KiB, len:     4, virt:           0x120000..=          0x123000, phys:                         identity mapped, flags: PageTableFlags(PRESENT | WRITABLE)
size: 4KiB, len:     2, virt:           0x124000..=          0x125000, phys:                         identity mapped, flags: PageTableFlags(PRESENT | WRITABLE | ACCESSED)
size: 4KiB, len:     1, virt:           0x126000..=          0x126000, phys:                         identity mapped, flags: PageTableFlags(PRESENT | WRITABLE)
size: 4KiB, len:     1, virt:           0x127000..=          0x127000, phys:                         identity mapped, flags: PageTableFlags(PRESENT | WRITABLE | ACCESSED | DIRTY)
size: 4KiB, len:     4, virt:           0x128000..=          0x12b000, phys:                         identity mapped, flags: PageTableFlags(PRESENT | WRITABLE | ACCESSED)
size: 4KiB, len:     1, virt:           0x12c000..=          0x12c000, phys:                         identity mapped, flags: PageTableFlags(PRESENT | WRITABLE)
size: 4KiB, len:     3, virt:           0x12d000..=          0x12f000, phys:                         identity mapped, flags: PageTableFlags(PRESENT | WRITABLE | ACCESSED | DIRTY)
size: 4KiB, len:     1, virt:           0x130000..=          0x130000, phys:                         identity mapped, flags: PageTableFlags(PRESENT | WRITABLE)

We found that this compact display of the current page tables makes related debugging much easier.

To implement this on our side, we had to duplicate some non-public items from this crate: PhysOffset and PageTableWalker.

This PR is an upstreaming effort and incrementally adds the required functionality for displaying the page table to this crate.
It is best reviewed commit by commit.
The code is not documented yet, since I wanted to discuss the API in general first.
The commits do the following:

  1. Make PhysOffset public. This straightforward PageTableFrameMapping implementation makes MappedPageTable usable as an OffsetPageTable, which is useful for generic code that otherwise would need to distinguish between the two.
  2. Derive Clone, Copy for PhysOffset. This is required to be able to clone the PageTableFrameMapping in the eventual MappedPageTableDisplay::fmt(&self) method without moving out of self.
  3. Make OffsetPageTable a type alias. This one is not strictly necessary, but being able to handle OffsetPageTable as an MappedPageTable avoids having to distinguish between the two. User migration should be as easy as replacing OffsetPageTable::new with OffsetPageTable::new_offset. We could even think about deprecating the type alias.
  4. Make PageTableWalker public. This is required to iterate over the page tables efficiently.
  5. Derive Clone, Copy for PageTableWalker. This is also required for cloning in Display::fmt.
  6. Implement MappedPageTable::range_iter via MappedPageTable::iter MappedPageRangeInclusive, MappedPageItem, and MappedPageRangeInclusiveItem. This allows iterating over all mappings of a page table.
  7. Implement MappedPageTable::display via MappedPageRangeInclusiveItem::display and MappedPageRangeInclusive::display.

Alternatively, we could avoid making PhysOffset and PageTableWalker public and do everything internally. Having these types available might make implementing such endeavors externally easier, though.

Another alternative would be avoiding exposing all of these new types for iterating and instead only providing an opaque type for displaying.

Of course, we could also not upstream step six or seven if this is deemed too opinionated or too much new API.

You can see a draft PR migrating our code iteratively to this upstreaming effort here: hermit-os/kernel#2165

Please let me know what you think.
Thanks as always for your great work! :)

This currently includes #575 for CI.

@mkroening mkroening force-pushed the display-page-tables branch from 981ce32 to b340a21 Compare January 7, 2026 16:51
@mkroening mkroening force-pushed the display-page-tables branch from b340a21 to 3f25b46 Compare January 7, 2026 17:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant