diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a5b4e617..d89924a9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -98,7 +98,7 @@ jobs: - name: Cache binaries id: cache-bin - uses: actions/cache@v5 + uses: actions/cache@v6 with: path: binaries key: ${{ runner.OS }}-binaries diff --git a/Changelog.md b/Changelog.md index cd17fadd..5adc5da0 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,18 @@ # Unreleased +## Breaking changes + +- [add `Mapper::clear` to clear any page table entry regardless of the present flag](https://github.com/rust-osdev/x86_64/pull/484) +- [`Mapper::unmap` now also returns the flags of the page ](https://github.com/rust-osdev/x86_64/pull/484) +- [make `OffsetPageTable` a type alias](https://github.com/rust-osdev/x86_64/pull/576) + - To migrate, replace `OffsetPageTable::new` with `OffsetPageTable::from_phys_offset` or `MappedPageTable::from_phys_offset`. + - `OffsetPageTable`'s `PageTableFrameMapping` implementation is now public as `PhysOffset`. +- [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.5 – 2026-07-11 This release is compatible with Rust nightlies starting with `nightly-2026-07-10` (this only applies when the `nightly` feature is used). diff --git a/src/addr.rs b/src/addr.rs index e3645034..f5cdada4 100644 --- a/src/addr.rs +++ b/src/addr.rs @@ -28,6 +28,10 @@ const ADDRESS_SPACE_SIZE: u64 = 0x1_0000_0000_0000; /// On `x86_64`, only the 48 lower bits of a virtual address can be used. The top 16 bits need /// to be copies of bit 47, i.e. the most significant bit. Addresses that fulfil this criterion /// are called “canonical”. This type guarantees that it always represents a canonical address. +/// +/// # Representation +/// +/// This struct has the same representation as a [`u64`]. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(transparent)] pub struct VirtAddr(u64); @@ -41,6 +45,10 @@ pub struct VirtAddr(u64); /// /// On `x86_64`, only the 52 lower bits of a physical address can be used. The top 12 bits need /// to be zero. This type guarantees that it always represents a valid physical address. +/// +/// # Representation +/// +/// This struct has the same representation as a [`u64`]. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(transparent)] pub struct PhysAddr(u64); diff --git a/src/instructions/random.rs b/src/instructions/random.rs index d6b2009b..411fd010 100644 --- a/src/instructions/random.rs +++ b/src/instructions/random.rs @@ -34,6 +34,7 @@ impl RdRand { } } } + /// Uniformly sampled u32. /// May fail in rare circumstances or heavy load. #[inline] @@ -49,6 +50,7 @@ impl RdRand { } } } + /// Uniformly sampled u16. /// May fail in rare circumstances or heavy load. #[inline] diff --git a/src/instructions/tlb.rs b/src/instructions/tlb.rs index d96cc895..911a80e6 100644 --- a/src/instructions/tlb.rs +++ b/src/instructions/tlb.rs @@ -314,7 +314,7 @@ where /// Execute the flush. pub fn flush(&self) { - if let Some(mut pages) = self.page_range { + if let Some(mut pages) = self.page_range.clone() { while !pages.is_empty() { // Calculate out how many pages we still need to flush. let count = Page::::steps_between_impl(&pages.start, &pages.end).0; diff --git a/src/registers/model_specific.rs b/src/registers/model_specific.rs index 79340959..284b37e1 100644 --- a/src/registers/model_specific.rs +++ b/src/registers/model_specific.rs @@ -74,6 +74,10 @@ pub struct SCet; #[derive(Debug)] pub struct ApicBase; +/// IA32_PAT: Page Attribute Table. +#[derive(Debug)] +pub struct Pat; + impl Efer { /// The underlying model specific register. pub const MSR: Msr = Msr(0xC000_0080); @@ -124,6 +128,22 @@ impl ApicBase { pub const MSR: Msr = Msr(0x1B); } +impl Pat { + /// The underlying model specific register. + pub const MSR: Msr = Msr(0x277); + /// The default PAT configuration following a power up or reset of the processor. + pub const DEFAULT: [PatMemoryType; 8] = [ + PatMemoryType::WriteBack, + PatMemoryType::WriteThrough, + PatMemoryType::Uncacheable, + PatMemoryType::StrongUncacheable, + PatMemoryType::WriteBack, + PatMemoryType::WriteThrough, + PatMemoryType::Uncacheable, + PatMemoryType::StrongUncacheable, + ]; +} + bitflags! { /// Flags of the Extended Feature Enable Register. #[repr(transparent)] @@ -190,6 +210,43 @@ bitflags! { } } +#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)] +/// Memory types used in the [PAT](Pat). +#[repr(u8)] +pub enum PatMemoryType { + /// Uncacheable (UC). + StrongUncacheable = 0x00, + /// Uses a write combining (WC) cache policy. + WriteCombining = 0x01, + /// Uses a write through (WT) cache policy. + WriteThrough = 0x04, + /// Uses a write protected (WP) cache policy. + WriteProtected = 0x05, + /// Uses a write back (WB) cache policy. + WriteBack = 0x06, + /// Same as strong uncacheable, but can be overridden to be write combining by MTRRs (UC-). + Uncacheable = 0x07, +} +impl PatMemoryType { + /// Converts from bits, returning `None` if the value is invalid. + pub const fn from_bits(bits: u8) -> Option { + match bits { + 0x00 => Some(Self::StrongUncacheable), + 0x01 => Some(Self::WriteCombining), + 0x04 => Some(Self::WriteThrough), + 0x05 => Some(Self::WriteProtected), + 0x06 => Some(Self::WriteBack), + 0x07 => Some(Self::Uncacheable), + _ => None, + } + } + + /// Gets the underlying bits. + pub const fn bits(self) -> u8 { + self as u8 + } +} + #[cfg(all(feature = "instructions", target_arch = "x86_64"))] mod x86_64 { use super::*; @@ -338,8 +395,13 @@ mod x86_64 { /// /// If [`CR4.FSGSBASE`][Cr4Flags::FSGSBASE] is set, the more efficient /// [`FS::write_base`] can be used instead. + /// + /// ## Safety + /// + /// The caller must ensure that this write operation has no unsafe side + /// effects, as the segment base address might be in use. #[inline] - pub fn write(address: VirtAddr) { + pub unsafe fn write(address: VirtAddr) { let mut msr = Self::MSR; unsafe { msr.write(address.as_u64()) }; } @@ -359,8 +421,13 @@ mod x86_64 { /// /// If [`CR4.FSGSBASE`][Cr4Flags::FSGSBASE] is set, the more efficient /// [`GS::write_base`] can be used instead. + /// + /// ## Safety + /// + /// The caller must ensure that this write operation has no unsafe side + /// effects, as the segment base address might be in use. #[inline] - pub fn write(address: VirtAddr) { + pub unsafe fn write(address: VirtAddr) { let mut msr = Self::MSR; unsafe { msr.write(address.as_u64()) }; } @@ -374,8 +441,12 @@ mod x86_64 { } /// Write a given virtual address to the KernelGsBase register. + /// + /// ## Safety + /// + /// The caller must ensure that a future call to [`GS::swap`] has no unsafe side effects. #[inline] - pub fn write(address: VirtAddr) { + pub unsafe fn write(address: VirtAddr) { let mut msr = Self::MSR; unsafe { msr.write(address.as_u64()) }; } @@ -728,4 +799,36 @@ mod x86_64 { } } } + + impl Pat { + /// Reads IA32_PAT. + /// + /// The PAT must be supported on the CPU, otherwise a general protection exception will + /// occur. Support can be detected using the `cpuid` instruction. + #[inline] + pub fn read() -> [PatMemoryType; 8] { + unsafe { Self::MSR.read() } + .to_ne_bytes() + .map(|bits| PatMemoryType::from_bits(bits).unwrap()) + } + + /// Writes IA32_PAT. + /// + /// The PAT must be supported on the CPU, otherwise a general protection exception will + /// occur. Support can be detected using the `cpuid` instruction. + /// + /// # Safety + /// + /// All affected pages must be flushed from the TLB. Processor caches may also need to be + /// flushed. Additionally, all pages that map to a given frame must have the same memory + /// type. + #[inline] + pub unsafe fn write(table: [PatMemoryType; 8]) { + let bits = u64::from_ne_bytes(table.map(PatMemoryType::bits)); + let mut msr = Self::MSR; + unsafe { + msr.write(bits); + } + } + } } diff --git a/src/registers/xcontrol.rs b/src/registers/xcontrol.rs index a9fb62af..81300520 100644 --- a/src/registers/xcontrol.rs +++ b/src/registers/xcontrol.rs @@ -53,7 +53,7 @@ bitflags! { #[cfg(all(feature = "instructions", target_arch = "x86_64"))] mod x86_64 { use super::*; - use core::arch::asm; + use core::arch::x86_64::{_xgetbv, _xsetbv}; impl XCr0 { /// Read the current set of XCR0 flags. @@ -65,16 +65,7 @@ mod x86_64 { /// Read the current raw XCR0 value. #[inline] pub fn read_raw() -> u64 { - unsafe { - let (low, high): (u32, u32); - asm!( - "xgetbv", - in("ecx") 0, - out("rax") low, out("rdx") high, - options(nomem, nostack, preserves_flags), - ); - ((high as u64) << 32) | (low as u64) - } + unsafe { _xgetbv(0) } } /// Write XCR0 flags. @@ -133,16 +124,8 @@ mod x86_64 { /// enable features that are not supported by the architecture #[inline] pub unsafe fn write_raw(value: u64) { - let low = value as u32; - let high = (value >> 32) as u32; - unsafe { - asm!( - "xsetbv", - in("ecx") 0, - in("rax") low, in("rdx") high, - options(nomem, nostack, preserves_flags), - ); + _xsetbv(0, value); } } diff --git a/src/structures/idt.rs b/src/structures/idt.rs index f15cedcc..b80a2290 100644 --- a/src/structures/idt.rs +++ b/src/structures/idt.rs @@ -293,8 +293,7 @@ pub struct InterruptDescriptorTable { /// The virtual (linear) address that caused the `#PF` is stored in the `CR2` register. /// The saved instruction pointer points to the instruction that caused the `#PF`. /// - /// The page-fault error code is described by the - /// [`PageFaultErrorCode`](struct.PageFaultErrorCode.html) struct. + /// The page-fault error code is described by the [`PageFaultErrorCode`] struct. /// /// The vector number of the `#PF` exception is 14. pub page_fault: Entry, diff --git a/src/structures/paging/frame.rs b/src/structures/paging/frame.rs index 7ad1a717..662d3814 100644 --- a/src/structures/paging/frame.rs +++ b/src/structures/paging/frame.rs @@ -9,8 +9,12 @@ use core::marker::PhantomData; use core::ops::{Add, AddAssign, Sub, SubAssign}; /// A physical memory frame. +/// +/// # Representation +/// +/// This struct has the same representation as a [`u64`]. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[repr(C)] +#[repr(transparent)] pub struct PhysFrame { // TODO: Make private when our minimum supported stable Rust version is 1.61 pub(crate) start_address: PhysAddr, @@ -225,8 +229,7 @@ impl Sub> for PhysFrame { } /// An range of physical memory frames, exclusive the upper bound. -#[derive(Clone, Copy, PartialEq, Eq, Hash)] -#[repr(C)] +#[derive(Clone, PartialEq, Eq, Hash)] pub struct PhysFrameRange { /// The start of the range, inclusive. pub start: PhysFrame, @@ -366,8 +369,7 @@ impl fmt::Display for PfnNotValid { } /// An range of physical memory frames, inclusive the upper bound. -#[derive(Clone, Copy, PartialEq, Eq, Hash)] -#[repr(C)] +#[derive(Clone, PartialEq, Eq, Hash)] pub struct PhysFrameRangeInclusive { /// The start of the range, inclusive. pub start: PhysFrame, 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..31a701d6 --- /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 indices 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 indices 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 indices 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.rs b/src/structures/paging/mapper/mapped_page_table/mod.rs similarity index 86% rename from src/structures/paging/mapper/mapped_page_table.rs rename to src/structures/paging/mapper/mapped_page_table/mod.rs index 5f673c55..d0c24751 100644 --- a/src/structures/paging/mapper/mapped_page_table.rs +++ b/src/structures/paging/mapper/mapped_page_table/mod.rs @@ -1,3 +1,11 @@ +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::{ mapper::*, page::AddressNotAligned, @@ -82,7 +90,7 @@ impl Mapper for MappedPageTable<'_, P> { fn unmap( &mut self, page: Page, - ) -> Result<(PhysFrame, MapperFlush), UnmapError> { + ) -> Result<(PhysFrame, PageTableFlags, MapperFlush), UnmapError> { let p4 = &mut self.level_4_table; let p3 = self .page_table_walker @@ -102,7 +110,39 @@ impl Mapper for MappedPageTable<'_, P> { .map_err(|AddressNotAligned| UnmapError::InvalidFrameAddress(p3_entry.addr()))?; p3_entry.set_unused(); - Ok((frame, MapperFlush::new(page))) + Ok((frame, flags, MapperFlush::new(page))) + } + + fn clear(&mut self, page: Page) -> Result, UnmapError> { + let p4 = &mut self.level_4_table; + let p3 = self + .page_table_walker + .next_table_mut(&mut p4[page.p4_index()])?; + + let p3_entry = &mut p3[page.p3_index()]; + let flags = p3_entry.flags(); + + if !flags.contains(PageTableFlags::HUGE_PAGE) { + return Err(UnmapError::ParentEntryHugePage); + } + + if !flags.contains(PageTableFlags::PRESENT) { + let cloned = p3_entry.clone(); + p3_entry.set_unused(); + return Ok(UnmappedFrame::NotPresent { entry: cloned }); + } + + let frame = PhysFrame::from_start_address(p3_entry.addr()) + .map_err(|AddressNotAligned| UnmapError::InvalidFrameAddress(p3_entry.addr()))?; + let flags = p3_entry.flags(); + + p3_entry.set_unused(); + + Ok(UnmappedFrame::Present { + frame, + flags, + flush: MapperFlush::new(page), + }) } unsafe fn update_flags( @@ -207,7 +247,7 @@ impl Mapper for MappedPageTable<'_, P> { fn unmap( &mut self, page: Page, - ) -> Result<(PhysFrame, MapperFlush), UnmapError> { + ) -> Result<(PhysFrame, PageTableFlags, MapperFlush), UnmapError> { let p4 = &mut self.level_4_table; let p3 = self .page_table_walker @@ -230,7 +270,40 @@ impl Mapper for MappedPageTable<'_, P> { .map_err(|AddressNotAligned| UnmapError::InvalidFrameAddress(p2_entry.addr()))?; p2_entry.set_unused(); - Ok((frame, MapperFlush::new(page))) + Ok((frame, flags, MapperFlush::new(page))) + } + + fn clear(&mut self, page: Page) -> Result, UnmapError> { + let p4 = &mut self.level_4_table; + let p3 = self + .page_table_walker + .next_table_mut(&mut p4[page.p4_index()])?; + let p2 = self + .page_table_walker + .next_table_mut(&mut p3[page.p3_index()])?; + + let p2_entry = &mut p2[page.p2_index()]; + let flags = p2_entry.flags(); + + if !flags.contains(PageTableFlags::HUGE_PAGE) { + return Err(UnmapError::ParentEntryHugePage); + } + + if !flags.contains(PageTableFlags::PRESENT) { + let cloned = p2_entry.clone(); + p2_entry.set_unused(); + return Ok(UnmappedFrame::NotPresent { entry: cloned }); + } + let frame = PhysFrame::from_start_address(p2_entry.addr()) + .map_err(|AddressNotAligned| UnmapError::InvalidFrameAddress(p2_entry.addr()))?; + let flags = p2_entry.flags(); + + p2_entry.set_unused(); + Ok(UnmappedFrame::Present { + frame, + flags, + flush: MapperFlush::new(page), + }) } unsafe fn update_flags( @@ -357,7 +430,7 @@ impl Mapper for MappedPageTable<'_, P> { fn unmap( &mut self, page: Page, - ) -> Result<(PhysFrame, MapperFlush), UnmapError> { + ) -> Result<(PhysFrame, PageTableFlags, MapperFlush), UnmapError> { let p4 = &mut self.level_4_table; let p3 = self .page_table_walker @@ -371,13 +444,47 @@ impl Mapper for MappedPageTable<'_, P> { let p1_entry = &mut p1[page.p1_index()]; - let frame = p1_entry.frame().map_err(|err| match err { + let frame = p1_entry.frame(true).map_err(|err| match err { FrameError::FrameNotPresent => UnmapError::PageNotMapped, - FrameError::HugeFrame => UnmapError::ParentEntryHugePage, + FrameError::HugeFrame => unreachable!(), })?; + let flags = p1_entry.flags(); + + p1_entry.set_unused(); + Ok((frame, flags, MapperFlush::new(page))) + } + + fn clear(&mut self, page: Page) -> Result, UnmapError> { + let p4 = &mut self.level_4_table; + let p3 = self + .page_table_walker + .next_table_mut(&mut p4[page.p4_index()])?; + let p2 = self + .page_table_walker + .next_table_mut(&mut p3[page.p3_index()])?; + let p1 = self + .page_table_walker + .next_table_mut(&mut p2[page.p2_index()])?; + + let p1_entry = &mut p1[page.p1_index()]; + + let frame = match p1_entry.frame(true) { + Ok(frame) => frame, + Err(FrameError::HugeFrame) => unreachable!(), + Err(FrameError::FrameNotPresent) => { + let cloned = p1_entry.clone(); + p1_entry.set_unused(); + return Ok(UnmappedFrame::NotPresent { entry: cloned }); + } + }; + let flags = p1_entry.flags(); p1_entry.set_unused(); - Ok((frame, MapperFlush::new(page))) + Ok(UnmappedFrame::Present { + frame, + flags, + flush: MapperFlush::new(page), + }) } unsafe fn update_flags( @@ -616,7 +723,7 @@ impl CleanUp for MappedPageTable<'_, P> { Page::range_inclusive(start, end), frame_deallocator, ) { - let frame = entry.frame().unwrap(); + let frame = entry.frame(false).unwrap(); entry.set_unused(); frame_deallocator.deallocate_frame(frame); } @@ -665,7 +772,7 @@ impl PageTableWalker

{ ) -> Result<&'b PageTable, PageTableWalkError> { let page_table_ptr = self .page_table_frame_mapping - .frame_to_pointer(entry.frame()?); + .frame_to_pointer(entry.frame(false)?); let page_table: &PageTable = unsafe { &*page_table_ptr }; Ok(page_table) @@ -683,7 +790,7 @@ impl PageTableWalker

{ ) -> Result<&'b mut PageTable, PageTableWalkError> { let page_table_ptr = self .page_table_frame_mapping - .frame_to_pointer(entry.frame()?); + .frame_to_pointer(entry.frame(false)?); let page_table: &mut PageTable = unsafe { &mut *page_table_ptr }; Ok(page_table) diff --git a/src/structures/paging/mapper/mapped_page_table/offset_page_table.rs b/src/structures/paging/mapper/mapped_page_table/offset_page_table.rs new file mode 100644 index 00000000..bbb51461 --- /dev/null +++ b/src/structures/paging/mapper/mapped_page_table/offset_page_table.rs @@ -0,0 +1,78 @@ +#![cfg(target_pointer_width = "64")] + +use crate::structures::paging::{mapper::*, PageTable}; + +/// A Mapper implementation that requires that the complete physical memory is mapped at some +/// offset in the virtual address space. +pub type OffsetPageTable<'a> = MappedPageTable<'a, PhysOffset>; + +impl<'a> OffsetPageTable<'a> { + /// Creates a new `OffsetPageTable` that uses the given offset for converting virtual + /// to physical addresses. + /// + /// The complete physical memory must be mapped in the virtual address space starting at + /// address `phys_offset`. This means that for example physical address `0x5000` can be + /// accessed through virtual address `phys_offset + 0x5000`. This mapping is required because + /// the mapper needs to access page tables, which are not mapped into the virtual address + /// space by default. + /// + /// ## Safety + /// + /// This function is unsafe because the caller must guarantee that the passed `phys_offset` + /// is correct. Also, the passed `level_4_table` must point to the level 4 page table + /// of a valid page table hierarchy. Otherwise this function might break memory safety, e.g. + /// by writing to an illegal memory location. + #[inline] + pub unsafe fn from_phys_offset( + level_4_table: &'a mut PageTable, + phys_offset: VirtAddr, + ) -> Self { + let phys_offset = unsafe { PhysOffset::new(phys_offset) }; + unsafe { MappedPageTable::new(level_4_table, phys_offset) } + } + + /// Returns the offset used for converting virtual to physical addresses. + pub fn phys_offset(&self) -> VirtAddr { + self.page_table_frame_mapping().phys_offset() + } +} + +/// A [`PageTableFrameMapping`] implementation that requires that the complete physical memory is mapped at some +/// offset in the virtual address space. +#[derive(Debug)] +pub struct PhysOffset { + phys_offset: VirtAddr, +} + +impl PhysOffset { + /// Creates a new `PhysOffset` that uses the given offset for converting virtual + /// to physical addresses. + /// + /// The complete physical memory must be mapped in the virtual address space starting at + /// address `phys_offset`. This means that for example physical address `0x5000` can be + /// accessed through virtual address `phys_offset + 0x5000`. This mapping is required because + /// the mapper needs to access page tables, which are not mapped into the virtual address + /// space by default. + /// + /// ## Safety + /// + /// This function is unsafe because the caller must guarantee that the passed `phys_offset` + /// is correct. Otherwise this function might break memory safety, e.g. by writing to an + /// illegal memory location. + #[inline] + pub unsafe fn new(phys_offset: VirtAddr) -> Self { + Self { phys_offset } + } + + /// Returns the offset used for converting virtual to physical addresses. + pub fn phys_offset(&self) -> VirtAddr { + self.phys_offset + } +} + +unsafe impl PageTableFrameMapping for PhysOffset { + fn frame_to_pointer(&self, frame: PhysFrame) -> *mut PageTable { + let virt = self.phys_offset + frame.start_address().as_u64(); + virt.as_mut_ptr() + } +} 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 d0f21716..aa61b563 100644 --- a/src/structures/paging/mapper/mod.rs +++ b/src/structures/paging/mapper/mod.rs @@ -1,21 +1,22 @@ //! 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::offset_page_table::OffsetPageTable; +pub use self::mapped_page_table::{OffsetPageTable, PhysOffset}; #[cfg(all(feature = "instructions", target_arch = "x86_64"))] pub use self::recursive_page_table::{InvalidPageTable, RecursivePageTable}; use crate::structures::paging::{ frame_alloc::{FrameAllocator, FrameDeallocator}, page::PageRangeInclusive, - page_table::PageTableFlags, + page_table::{PageTableEntry, PageTableFlags}, Page, PageSize, PhysFrame, Size1GiB, Size2MiB, Size4KiB, }; use crate::{PhysAddr, VirtAddr}; mod mapped_page_table; -mod offset_page_table; #[cfg(all(feature = "instructions", target_arch = "x86_64"))] mod recursive_page_table; @@ -282,7 +283,18 @@ pub trait Mapper { /// Removes a mapping from the page table and returns the frame that used to be mapped. /// /// Note that no page tables or pages are deallocated. - fn unmap(&mut self, page: Page) -> Result<(PhysFrame, MapperFlush), UnmapError>; + fn unmap( + &mut self, + page: Page, + ) -> Result<(PhysFrame, PageTableFlags, MapperFlush), UnmapError>; + + /// Clears a mapping from the page table and returns the frame that used to be mapped. + /// + /// Unlike [`Mapper::unmap`] this will ignore the present flag of the page and will successfully + /// clear the table entry for any valid page. + /// + /// Note that no page tables or pages are deallocated. + fn clear(&mut self, page: Page) -> Result, UnmapError>; /// Updates the flags of an existing mapping. /// @@ -376,6 +388,27 @@ pub trait Mapper { } } +/// The result of [`Mapper::clear`], representing either +/// the unmapped frame or the entry data if the frame is not marked as present. +#[derive(Debug)] +#[must_use = "Page table changes must be flushed or ignored if the page is present."] +pub enum UnmappedFrame { + /// The frame was present before the [`Mapper::clear`] call + Present { + /// The physical frame that was unmapped + frame: PhysFrame, + /// The flags of the frame that was unmapped + flags: PageTableFlags, + /// The changed page, to flush the TLB + flush: MapperFlush, + }, + /// The frame was not present before the [`Mapper::clear`] call + NotPresent { + /// The page table entry + entry: PageTableEntry, + }, +} + /// This type represents a page whose mapping has changed in the page table. /// /// The old mapping might be still cached in the translation lookaside buffer (TLB), so it needs diff --git a/src/structures/paging/mapper/offset_page_table.rs b/src/structures/paging/mapper/offset_page_table.rs deleted file mode 100644 index 546a8299..00000000 --- a/src/structures/paging/mapper/offset_page_table.rs +++ /dev/null @@ -1,301 +0,0 @@ -#![cfg(target_pointer_width = "64")] - -use crate::structures::paging::{mapper::*, page_table::PageTable}; - -/// A Mapper implementation that requires that the complete physical memory is mapped at some -/// offset in the virtual address space. -#[derive(Debug)] -pub struct OffsetPageTable<'a> { - inner: MappedPageTable<'a, PhysOffset>, -} - -impl<'a> OffsetPageTable<'a> { - /// Creates a new `OffsetPageTable` that uses the given offset for converting virtual - /// to physical addresses. - /// - /// The complete physical memory must be mapped in the virtual address space starting at - /// address `phys_offset`. This means that for example physical address `0x5000` can be - /// accessed through virtual address `phys_offset + 0x5000`. This mapping is required because - /// the mapper needs to access page tables, which are not mapped into the virtual address - /// space by default. - /// - /// ## Safety - /// - /// This function is unsafe because the caller must guarantee that the passed `phys_offset` - /// is correct. Also, the passed `level_4_table` must point to the level 4 page table - /// of a valid page table hierarchy. Otherwise this function might break memory safety, e.g. - /// by writing to an illegal memory location. - #[inline] - pub unsafe fn new(level_4_table: &'a mut PageTable, phys_offset: VirtAddr) -> Self { - let phys_offset = PhysOffset { - offset: phys_offset, - }; - Self { - inner: unsafe { MappedPageTable::new(level_4_table, phys_offset) }, - } - } - - /// Returns an immutable reference to the wrapped level 4 `PageTable` instance. - pub fn level_4_table(&self) -> &PageTable { - self.inner.level_4_table() - } - - /// Returns a mutable reference to the wrapped level 4 `PageTable` instance. - pub fn level_4_table_mut(&mut self) -> &mut PageTable { - self.inner.level_4_table_mut() - } - - /// Returns the offset used for converting virtual to physical addresses. - pub fn phys_offset(&self) -> VirtAddr { - self.inner.page_table_frame_mapping().offset - } -} - -#[derive(Debug)] -struct PhysOffset { - offset: VirtAddr, -} - -unsafe impl PageTableFrameMapping for PhysOffset { - fn frame_to_pointer(&self, frame: PhysFrame) -> *mut PageTable { - let virt = self.offset + frame.start_address().as_u64(); - virt.as_mut_ptr() - } -} - -// delegate all trait implementations to inner - -impl Mapper for OffsetPageTable<'_> { - #[inline] - unsafe fn map_to_with_table_flags( - &mut self, - page: Page, - frame: PhysFrame, - flags: PageTableFlags, - parent_table_flags: PageTableFlags, - allocator: &mut A, - ) -> Result, MapToError> - where - A: FrameAllocator + ?Sized, - { - unsafe { - self.inner - .map_to_with_table_flags(page, frame, flags, parent_table_flags, allocator) - } - } - - #[inline] - fn unmap( - &mut self, - page: Page, - ) -> Result<(PhysFrame, MapperFlush), UnmapError> { - self.inner.unmap(page) - } - - #[inline] - unsafe fn update_flags( - &mut self, - page: Page, - flags: PageTableFlags, - ) -> Result, FlagUpdateError> { - unsafe { self.inner.update_flags(page, flags) } - } - - #[inline] - unsafe fn set_flags_p4_entry( - &mut self, - page: Page, - flags: PageTableFlags, - ) -> Result { - unsafe { self.inner.set_flags_p4_entry(page, flags) } - } - - #[inline] - unsafe fn set_flags_p3_entry( - &mut self, - page: Page, - flags: PageTableFlags, - ) -> Result { - unsafe { self.inner.set_flags_p3_entry(page, flags) } - } - - #[inline] - unsafe fn set_flags_p2_entry( - &mut self, - page: Page, - flags: PageTableFlags, - ) -> Result { - unsafe { self.inner.set_flags_p2_entry(page, flags) } - } - - #[inline] - fn translate_page(&self, page: Page) -> Result, TranslateError> { - self.inner.translate_page(page) - } -} - -impl Mapper for OffsetPageTable<'_> { - #[inline] - unsafe fn map_to_with_table_flags( - &mut self, - page: Page, - frame: PhysFrame, - flags: PageTableFlags, - parent_table_flags: PageTableFlags, - allocator: &mut A, - ) -> Result, MapToError> - where - A: FrameAllocator + ?Sized, - { - unsafe { - self.inner - .map_to_with_table_flags(page, frame, flags, parent_table_flags, allocator) - } - } - - #[inline] - fn unmap( - &mut self, - page: Page, - ) -> Result<(PhysFrame, MapperFlush), UnmapError> { - self.inner.unmap(page) - } - - #[inline] - unsafe fn update_flags( - &mut self, - page: Page, - flags: PageTableFlags, - ) -> Result, FlagUpdateError> { - unsafe { self.inner.update_flags(page, flags) } - } - - #[inline] - unsafe fn set_flags_p4_entry( - &mut self, - page: Page, - flags: PageTableFlags, - ) -> Result { - unsafe { self.inner.set_flags_p4_entry(page, flags) } - } - - #[inline] - unsafe fn set_flags_p3_entry( - &mut self, - page: Page, - flags: PageTableFlags, - ) -> Result { - unsafe { self.inner.set_flags_p3_entry(page, flags) } - } - - #[inline] - unsafe fn set_flags_p2_entry( - &mut self, - page: Page, - flags: PageTableFlags, - ) -> Result { - unsafe { self.inner.set_flags_p2_entry(page, flags) } - } - - #[inline] - fn translate_page(&self, page: Page) -> Result, TranslateError> { - self.inner.translate_page(page) - } -} - -impl Mapper for OffsetPageTable<'_> { - #[inline] - unsafe fn map_to_with_table_flags( - &mut self, - page: Page, - frame: PhysFrame, - flags: PageTableFlags, - parent_table_flags: PageTableFlags, - allocator: &mut A, - ) -> Result, MapToError> - where - A: FrameAllocator + ?Sized, - { - unsafe { - self.inner - .map_to_with_table_flags(page, frame, flags, parent_table_flags, allocator) - } - } - - #[inline] - fn unmap( - &mut self, - page: Page, - ) -> Result<(PhysFrame, MapperFlush), UnmapError> { - self.inner.unmap(page) - } - - #[inline] - unsafe fn update_flags( - &mut self, - page: Page, - flags: PageTableFlags, - ) -> Result, FlagUpdateError> { - unsafe { self.inner.update_flags(page, flags) } - } - - #[inline] - unsafe fn set_flags_p4_entry( - &mut self, - page: Page, - flags: PageTableFlags, - ) -> Result { - unsafe { self.inner.set_flags_p4_entry(page, flags) } - } - - #[inline] - unsafe fn set_flags_p3_entry( - &mut self, - page: Page, - flags: PageTableFlags, - ) -> Result { - unsafe { self.inner.set_flags_p3_entry(page, flags) } - } - - #[inline] - unsafe fn set_flags_p2_entry( - &mut self, - page: Page, - flags: PageTableFlags, - ) -> Result { - unsafe { self.inner.set_flags_p2_entry(page, flags) } - } - - #[inline] - fn translate_page(&self, page: Page) -> Result, TranslateError> { - self.inner.translate_page(page) - } -} - -impl Translate for OffsetPageTable<'_> { - #[inline] - fn translate(&self, addr: VirtAddr) -> TranslateResult { - self.inner.translate(addr) - } -} - -impl CleanUp for OffsetPageTable<'_> { - #[inline] - unsafe fn clean_up(&mut self, frame_deallocator: &mut D) - where - D: FrameDeallocator, - { - unsafe { self.inner.clean_up(frame_deallocator) } - } - - #[inline] - unsafe fn clean_up_addr_range( - &mut self, - range: PageRangeInclusive, - frame_deallocator: &mut D, - ) where - D: FrameDeallocator, - { - unsafe { self.inner.clean_up_addr_range(range, frame_deallocator) } - } -} diff --git a/src/structures/paging/mapper/recursive_page_table.rs b/src/structures/paging/mapper/recursive_page_table.rs index bd3c5981..d421077f 100644 --- a/src/structures/paging/mapper/recursive_page_table.rs +++ b/src/structures/paging/mapper/recursive_page_table.rs @@ -64,7 +64,7 @@ impl<'a> RecursivePageTable<'a> { { return Err(InvalidPageTable::NotRecursive); } - if Ok(Cr3::read().0) != table[recursive_index].frame() { + if Ok(Cr3::read().0) != table[recursive_index].frame(false) { return Err(InvalidPageTable::NotActive); } @@ -205,11 +205,11 @@ impl Mapper for RecursivePageTable<'_> { fn unmap( &mut self, page: Page, - ) -> Result<(PhysFrame, MapperFlush), UnmapError> { + ) -> Result<(PhysFrame, PageTableFlags, MapperFlush), UnmapError> { let p4 = &mut self.p4; let p4_entry = &p4[page.p4_index()]; - p4_entry.frame().map_err(|err| match err { + p4_entry.frame(false).map_err(|err| match err { FrameError::FrameNotPresent => UnmapError::PageNotMapped, FrameError::HugeFrame => UnmapError::ParentEntryHugePage, })?; @@ -229,7 +229,42 @@ impl Mapper for RecursivePageTable<'_> { .map_err(|AddressNotAligned| UnmapError::InvalidFrameAddress(p3_entry.addr()))?; p3_entry.set_unused(); - Ok((frame, MapperFlush::new(page))) + Ok((frame, flags, MapperFlush::new(page))) + } + + fn clear(&mut self, page: Page) -> Result, UnmapError> { + let p4 = &mut self.p4; + let p4_entry = &p4[page.p4_index()]; + + p4_entry.frame(false).map_err(|err| match err { + FrameError::FrameNotPresent => UnmapError::PageNotMapped, + FrameError::HugeFrame => UnmapError::ParentEntryHugePage, + })?; + + let p3 = unsafe { &mut *(p3_ptr(page, self.recursive_index)) }; + let p3_entry = &mut p3[page.p3_index()]; + let flags = p3_entry.flags(); + + if !flags.contains(PageTableFlags::HUGE_PAGE) { + return Err(UnmapError::ParentEntryHugePage); + } + + if !flags.contains(PageTableFlags::PRESENT) { + let cloned = p3_entry.clone(); + p3_entry.set_unused(); + return Ok(UnmappedFrame::NotPresent { entry: cloned }); + } + + let frame = PhysFrame::from_start_address(p3_entry.addr()) + .map_err(|AddressNotAligned| UnmapError::InvalidFrameAddress(p3_entry.addr()))?; + let flags = p3_entry.flags(); + + p3_entry.set_unused(); + Ok(UnmappedFrame::Present { + frame, + flags, + flush: MapperFlush::new(page), + }) } unsafe fn update_flags( @@ -353,17 +388,17 @@ impl Mapper for RecursivePageTable<'_> { fn unmap( &mut self, page: Page, - ) -> Result<(PhysFrame, MapperFlush), UnmapError> { + ) -> Result<(PhysFrame, PageTableFlags, MapperFlush), UnmapError> { let p4 = &mut self.p4; let p4_entry = &p4[page.p4_index()]; - p4_entry.frame().map_err(|err| match err { + p4_entry.frame(false).map_err(|err| match err { FrameError::FrameNotPresent => UnmapError::PageNotMapped, FrameError::HugeFrame => UnmapError::ParentEntryHugePage, })?; let p3 = unsafe { &mut *(p3_ptr(page, self.recursive_index)) }; let p3_entry = &p3[page.p3_index()]; - p3_entry.frame().map_err(|err| match err { + p3_entry.frame(false).map_err(|err| match err { FrameError::FrameNotPresent => UnmapError::PageNotMapped, FrameError::HugeFrame => UnmapError::ParentEntryHugePage, })?; @@ -383,7 +418,48 @@ impl Mapper for RecursivePageTable<'_> { .map_err(|AddressNotAligned| UnmapError::InvalidFrameAddress(p2_entry.addr()))?; p2_entry.set_unused(); - Ok((frame, MapperFlush::new(page))) + Ok((frame, flags, MapperFlush::new(page))) + } + + fn clear(&mut self, page: Page) -> Result, UnmapError> { + let p4 = &mut self.p4; + let p4_entry = &p4[page.p4_index()]; + p4_entry.frame(false).map_err(|err| match err { + FrameError::FrameNotPresent => UnmapError::PageNotMapped, + FrameError::HugeFrame => UnmapError::ParentEntryHugePage, + })?; + + let p3 = unsafe { &mut *(p3_ptr(page, self.recursive_index)) }; + let p3_entry = &p3[page.p3_index()]; + p3_entry.frame(false).map_err(|err| match err { + FrameError::FrameNotPresent => UnmapError::PageNotMapped, + FrameError::HugeFrame => UnmapError::ParentEntryHugePage, + })?; + + let p2 = unsafe { &mut *(p2_ptr(page, self.recursive_index)) }; + let p2_entry = &mut p2[page.p2_index()]; + let flags = p2_entry.flags(); + + if !flags.contains(PageTableFlags::HUGE_PAGE) { + return Err(UnmapError::ParentEntryHugePage); + } + + if !flags.contains(PageTableFlags::PRESENT) { + let cloned = p2_entry.clone(); + p2_entry.set_unused(); + return Ok(UnmappedFrame::NotPresent { entry: cloned }); + } + + let frame = PhysFrame::from_start_address(p2_entry.addr()) + .map_err(|AddressNotAligned| UnmapError::InvalidFrameAddress(p2_entry.addr()))?; + let flags = p2_entry.flags(); + + p2_entry.set_unused(); + Ok(UnmappedFrame::Present { + frame, + flags, + flush: MapperFlush::new(page), + }) } unsafe fn update_flags( @@ -545,24 +621,24 @@ impl Mapper for RecursivePageTable<'_> { fn unmap( &mut self, page: Page, - ) -> Result<(PhysFrame, MapperFlush), UnmapError> { + ) -> Result<(PhysFrame, PageTableFlags, MapperFlush), UnmapError> { let p4 = &mut self.p4; let p4_entry = &p4[page.p4_index()]; - p4_entry.frame().map_err(|err| match err { + p4_entry.frame(false).map_err(|err| match err { FrameError::FrameNotPresent => UnmapError::PageNotMapped, FrameError::HugeFrame => UnmapError::ParentEntryHugePage, })?; let p3 = unsafe { &mut *(p3_ptr(page, self.recursive_index)) }; let p3_entry = &p3[page.p3_index()]; - p3_entry.frame().map_err(|err| match err { + p3_entry.frame(false).map_err(|err| match err { FrameError::FrameNotPresent => UnmapError::PageNotMapped, FrameError::HugeFrame => UnmapError::ParentEntryHugePage, })?; let p2 = unsafe { &mut *(p2_ptr(page, self.recursive_index)) }; let p2_entry = &p2[page.p2_index()]; - p2_entry.frame().map_err(|err| match err { + p2_entry.frame(false).map_err(|err| match err { FrameError::FrameNotPresent => UnmapError::PageNotMapped, FrameError::HugeFrame => UnmapError::ParentEntryHugePage, })?; @@ -570,13 +646,58 @@ impl Mapper for RecursivePageTable<'_> { let p1 = unsafe { &mut *(p1_ptr(page, self.recursive_index)) }; let p1_entry = &mut p1[page.p1_index()]; - let frame = p1_entry.frame().map_err(|err| match err { + let frame = p1_entry.frame(true).map_err(|err| match err { + FrameError::FrameNotPresent => UnmapError::PageNotMapped, + FrameError::HugeFrame => unreachable!(), + })?; + let flags = p1_entry.flags(); + + p1_entry.set_unused(); + Ok((frame, flags, MapperFlush::new(page))) + } + + fn clear(&mut self, page: Page) -> Result, UnmapError> { + let p4 = &mut self.p4; + let p4_entry = &p4[page.p4_index()]; + p4_entry.frame(false).map_err(|err| match err { FrameError::FrameNotPresent => UnmapError::PageNotMapped, FrameError::HugeFrame => UnmapError::ParentEntryHugePage, })?; + let p3 = unsafe { &mut *(p3_ptr(page, self.recursive_index)) }; + let p3_entry = &p3[page.p3_index()]; + p3_entry.frame(false).map_err(|err| match err { + FrameError::FrameNotPresent => UnmapError::PageNotMapped, + FrameError::HugeFrame => UnmapError::ParentEntryHugePage, + })?; + + let p2 = unsafe { &mut *(p2_ptr(page, self.recursive_index)) }; + let p2_entry = &p2[page.p2_index()]; + p2_entry.frame(false).map_err(|err| match err { + FrameError::FrameNotPresent => UnmapError::PageNotMapped, + FrameError::HugeFrame => UnmapError::ParentEntryHugePage, + })?; + + let p1 = unsafe { &mut *(p1_ptr(page, self.recursive_index)) }; + let p1_entry = &mut p1[page.p1_index()]; + + let frame = match p1_entry.frame(true) { + Ok(frame) => frame, + Err(FrameError::FrameNotPresent) => { + let cloned = p1_entry.clone(); + p1_entry.set_unused(); + return Ok(UnmappedFrame::NotPresent { entry: cloned }); + } + Err(FrameError::HugeFrame) => unreachable!(), + }; + let flags = p1_entry.flags(); + p1_entry.set_unused(); - Ok((frame, MapperFlush::new(page))) + Ok(UnmappedFrame::Present { + frame, + flags, + flush: MapperFlush::new(page), + }) } unsafe fn update_flags( @@ -770,9 +891,6 @@ impl Translate for RecursivePageTable<'_> { if p1_entry.is_unused() { return TranslateResult::NotMapped; } - if p1_entry.flags().contains(PageTableFlags::HUGE_PAGE) { - panic!("level 1 entry has huge page bit set") - } let frame = match PhysFrame::from_start_address(p1_entry.addr()) { Ok(frame) => frame, @@ -842,7 +960,7 @@ impl CleanUp for RecursivePageTable<'_> { !(level == PageTableLevel::Four && *i == recursive_index.into()) }) { - if let Ok(frame) = entry.frame() { + if let Ok(frame) = entry.frame(level == PageTableLevel::One) { let start = VirtAddr::forward_checked_impl( table_addr, (offset_per_entry as usize) * i, diff --git a/src/structures/paging/mod.rs b/src/structures/paging/mod.rs index 741f683c..600a7332 100644 --- a/src/structures/paging/mod.rs +++ b/src/structures/paging/mod.rs @@ -6,13 +6,13 @@ pub use self::frame::PhysFrame; pub use self::frame_alloc::{FrameAllocator, FrameDeallocator}; #[doc(no_inline)] pub use self::mapper::MappedPageTable; -#[cfg(target_pointer_width = "64")] -#[doc(no_inline)] -pub use self::mapper::OffsetPageTable; #[cfg(all(feature = "instructions", target_arch = "x86_64"))] #[doc(no_inline)] pub use self::mapper::RecursivePageTable; pub use self::mapper::{Mapper, Translate}; +#[cfg(target_pointer_width = "64")] +#[doc(no_inline)] +pub use self::mapper::{OffsetPageTable, PhysOffset}; pub use self::page::{Page, PageSize, Size1GiB, Size2MiB, Size4KiB}; pub use self::page_table::{PageOffset, PageTable, PageTableFlags, PageTableIndex}; diff --git a/src/structures/paging/page.rs b/src/structures/paging/page.rs index b4e7a4e6..5f0d13b2 100644 --- a/src/structures/paging/page.rs +++ b/src/structures/paging/page.rs @@ -63,8 +63,12 @@ impl PageSize for Size1GiB { impl Sealed for super::Size1GiB {} /// A virtual memory page. +/// +/// # Representation +/// +/// This struct has the same representation as a [`u64`]. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -#[repr(C)] +#[repr(transparent)] pub struct Page { start_address: VirtAddr, size: PhantomData, @@ -347,8 +351,7 @@ impl Step for Page { } /// A range of pages with exclusive upper bound. -#[derive(Clone, Copy, PartialEq, Eq, Hash)] -#[repr(C)] +#[derive(Clone, PartialEq, Eq, Hash)] pub struct PageRange { /// The start of the range, inclusive. pub start: Page, @@ -503,8 +506,7 @@ impl fmt::Debug for PageRange { } /// A range of pages with inclusive upper bound. -#[derive(Clone, Copy, PartialEq, Eq, Hash)] -#[repr(C)] +#[derive(Clone, PartialEq, Eq, Hash)] pub struct PageRangeInclusive { /// The start of the range, inclusive. pub start: Page, diff --git a/src/structures/paging/page_table.rs b/src/structures/paging/page_table.rs index 43cb4b33..1febcb6b 100644 --- a/src/structures/paging/page_table.rs +++ b/src/structures/paging/page_table.rs @@ -68,16 +68,19 @@ impl PageTableEntry { /// Returns the physical frame mapped by this entry. /// + /// Set `is_level_1_entry` to `true` if the `PageTableEntry` is from a + /// level 1 page table. + /// /// Returns the following errors: /// /// - `FrameError::FrameNotPresent` if the entry doesn't have the `PRESENT` flag set. /// - `FrameError::HugeFrame` if the entry has the `HUGE_PAGE` flag set (for huge pages the - /// `addr` function must be used) + /// `addr` function must be used) and `is_level_1_entry` is `false` #[inline] - pub fn frame(&self) -> Result { + pub fn frame(&self, is_level_1_entry: bool) -> Result { if !self.flags().contains(PageTableFlags::PRESENT) { Err(FrameError::FrameNotPresent) - } else if self.flags().contains(PageTableFlags::HUGE_PAGE) { + } else if !is_level_1_entry && self.flags().contains(PageTableFlags::HUGE_PAGE) { Err(FrameError::HugeFrame) } else { Ok(PhysFrame::containing_address(self.addr())) @@ -94,7 +97,6 @@ impl PageTableEntry { /// Map the entry to the specified physical frame with the specified flags. #[inline] pub fn set_frame(&mut self, frame: PhysFrame, flags: PageTableFlags) { - assert!(!flags.contains(PageTableFlags::HUGE_PAGE)); self.set_addr(frame.start_address(), flags) } @@ -148,17 +150,21 @@ bitflags! { /// Controls whether accesses from userspace (i.e. ring 3) are permitted. const USER_ACCESSIBLE = 1 << 2; /// If this bit is set, a “write-through” policy is used for the cache, else a “write-back” - /// policy is used. + /// policy is used. This referred to as the page-level write-through (PWT) bit. const WRITE_THROUGH = 1 << 3; - /// Disables caching for the pointed entry is cacheable. + /// Disables caching for the pointed entry if it is cacheable. This referred to as the + /// page-level cache disable (PCD) bit. const NO_CACHE = 1 << 4; /// Set by the CPU when the mapped frame or page table is accessed. const ACCESSED = 1 << 5; /// Set by the CPU on a write to the mapped frame. const DIRTY = 1 << 6; - /// Specifies that the entry maps a huge frame instead of a page table. Only allowed in - /// P2 or P3 tables. + /// Specifies that the entry maps a huge frame instead of a page table. This is the same bit + /// as `PAT_4KIB_PAGE`. const HUGE_PAGE = 1 << 7; + /// This is the PAT bit for page table entries that point to 4KiB pages. This is the same + /// bit as `HUGE_PAGE`. + const PAT_4KIB_PAGE = 1 << 7; /// Indicates that the mapping is present in all address spaces, so it isn't flushed from /// the TLB on an address space switch. const GLOBAL = 1 << 8; @@ -168,6 +174,8 @@ bitflags! { const BIT_10 = 1 << 10; /// Available to the OS, can be used to store additional data, e.g. custom flags. const BIT_11 = 1 << 11; + /// This is the PAT bit for page table entries that point to huge pages. + const PAT_HUGE_PAGE = 1 << 12; /// Available to the OS, can be used to store additional data, e.g. custom flags. const BIT_52 = 1 << 52; /// Available to the OS, can be used to store additional data, e.g. custom flags.