|
| 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