From 8e030f828e011c72f58f26a666cef05850b75d5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Kr=C3=B6ning?= Date: Sat, 7 Dec 2024 12:25:39 +0100 Subject: [PATCH 01/24] fix(idt): panic in `impl fmt::Debug for EntryOptions` --- src/structures/idt.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/structures/idt.rs b/src/structures/idt.rs index d4a8deb8..58e1ec44 100644 --- a/src/structures/idt.rs +++ b/src/structures/idt.rs @@ -1002,8 +1002,8 @@ impl EntryOptions { self } - fn stack_index(&self) -> u16 { - self.bits.get_bits(0..3) - 1 + fn stack_index(&self) -> Option { + self.bits.get_bits(0..3).checked_sub(1) } } @@ -1720,6 +1720,11 @@ mod test { } } + #[test] + fn idt_fmt_debug() { + dbg!(InterruptDescriptorTable::new()); + } + #[test] fn entry_derive_test() { fn foo(_: impl Copy + PartialEq + fmt::Debug) {} From 50dff1ff8a20cd0b3646172f3f14267909123efe Mon Sep 17 00:00:00 2001 From: Alison Davis Date: Sun, 22 Dec 2024 18:20:55 -0600 Subject: [PATCH 02/24] Add MapperFlush method to get page --- src/structures/paging/mapper/mod.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/structures/paging/mapper/mod.rs b/src/structures/paging/mapper/mod.rs index 5db479b8..28c2dff6 100644 --- a/src/structures/paging/mapper/mod.rs +++ b/src/structures/paging/mapper/mod.rs @@ -409,6 +409,12 @@ impl MapperFlush { /// Don't flush the TLB and silence the “must be used” warning. #[inline] pub fn ignore(self) {} + + /// Returns the page to be flushed. + #[inline] + pub fn page(&self) -> Page { + self.0 + } } /// This type represents a change of a page table requiring a complete TLB flush From 61cd811f3bb9a08e84c0cef5639b8c0c5438d937 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Kr=C3=B6ning?= Date: Tue, 24 Dec 2024 13:20:24 +0100 Subject: [PATCH 03/24] docs(xcontrol): add missing period --- src/registers/xcontrol.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registers/xcontrol.rs b/src/registers/xcontrol.rs index dc61ec78..89176366 100644 --- a/src/registers/xcontrol.rs +++ b/src/registers/xcontrol.rs @@ -85,7 +85,7 @@ mod x86_64 { /// ## Safety /// /// This function is unsafe because it's possible to - /// enable features that are not supported by the architecture + /// enable features that are not supported by the architecture. #[inline] pub unsafe fn write(flags: XCr0Flags) { let old_value = Self::read_raw(); From 87a7aff44e4c9bcd7f84893e846a5fc217d68c83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Kr=C3=B6ning?= Date: Tue, 24 Dec 2024 17:11:24 +0100 Subject: [PATCH 04/24] feat: add `update()` to `Cr3`, `Dr7`, `SFMask`, `UCet`, `SCet`, `mxcsr`, `rflags`, and `XCr0` --- src/registers/control.rs | 57 +++++++++++++++++++++++++++++++++ src/registers/debug.rs | 13 ++++++++ src/registers/model_specific.rs | 40 +++++++++++++++++++++++ src/registers/mxcsr.rs | 11 +++++++ src/registers/rflags.rs | 19 +++++++++++ src/registers/xcontrol.rs | 21 ++++++++++++ 6 files changed, 161 insertions(+) diff --git a/src/registers/control.rs b/src/registers/control.rs index f1975751..4631f55a 100644 --- a/src/registers/control.rs +++ b/src/registers/control.rs @@ -361,6 +361,63 @@ mod x86_64 { asm!("mov cr3, {}", in(reg) value, options(nostack, preserves_flags)); } } + + /// Update the P4 table address in the CR3 register. + /// + /// ## Safety + /// + /// Changing the level 4 page table is unsafe, because it's possible to violate memory safety by + /// changing the page mapping. + #[inline] + pub unsafe fn update(f: F) + where + F: FnOnce(&mut PhysFrame, &mut Cr3Flags), + { + let (mut frame, mut flags) = Self::read(); + f(&mut frame, &mut flags); + unsafe { + Self::write(frame, flags); + } + } + + /// Updates the P4 table address in the CR3 register. + /// + /// ## Safety + /// + /// Changing the level 4 page table is unsafe, because it's possible to violate memory safety by + /// changing the page mapping. + /// [`Cr4Flags::PCID`] must be set before calling this method. + #[inline] + pub unsafe fn update_pcid(f: F) + where + F: FnOnce(&mut PhysFrame, &mut Pcid), + { + let (mut frame, mut pcid) = Self::read_pcid(); + f(&mut frame, &mut pcid); + unsafe { + Self::write_pcid(frame, pcid); + } + } + + /// Updates the P4 table address in the CR3 register without flushing existing TLB entries for + /// the PCID. + /// + /// ## Safety + /// + /// Changing the level 4 page table is unsafe, because it's possible to violate memory safety by + /// changing the page mapping. + /// [`Cr4Flags::PCID`] must be set before calling this method. + #[inline] + pub unsafe fn update_pcid_no_flush(f: F) + where + F: FnOnce(&mut PhysFrame, &mut Pcid), + { + let (mut frame, mut pcid) = Self::read_pcid(); + f(&mut frame, &mut pcid); + unsafe { + Self::write_pcid_no_flush(frame, pcid); + } + } } impl Cr4 { diff --git a/src/registers/debug.rs b/src/registers/debug.rs index 0a9f276b..0a41e4bb 100644 --- a/src/registers/debug.rs +++ b/src/registers/debug.rs @@ -499,5 +499,18 @@ mod x86_64 { asm!("mov dr7, {}", in(reg) value, options(nomem, nostack, preserves_flags)); } } + + /// Update the DR7 value. + /// + /// Preserves the value of reserved fields. + #[inline] + pub fn update(f: F) + where + F: FnOnce(&mut Dr7Value), + { + let mut value = Self::read(); + f(&mut value); + Self::write(value); + } } } diff --git a/src/registers/model_specific.rs b/src/registers/model_specific.rs index 0471bfae..cf2dd34b 100644 --- a/src/registers/model_specific.rs +++ b/src/registers/model_specific.rs @@ -525,6 +525,24 @@ mod x86_64 { let mut msr = Self::MSR; unsafe { msr.write(value.bits()) }; } + + /// Update the SFMask register. + /// + /// The SFMASK register is used to specify which RFLAGS bits + /// are cleared during a SYSCALL. In long mode, SFMASK is used + /// to specify which RFLAGS bits are cleared when SYSCALL is + /// executed. If a bit in SFMASK is set to 1, the corresponding + /// bit in RFLAGS is cleared to 0. If a bit in SFMASK is cleared + /// to 0, the corresponding rFLAGS bit is not modified. + #[inline] + pub fn update(f: F) + where + F: FnOnce(&mut RFlags), + { + let mut flags = Self::read(); + f(&mut flags); + Self::write(flags); + } } impl UCet { @@ -560,6 +578,17 @@ mod x86_64 { pub fn write(flags: CetFlags, legacy_bitmap: Page) { Self::write_raw(flags.bits() | legacy_bitmap.start_address().as_u64()); } + + /// Updates IA32_U_CET. + #[inline] + pub fn update(f: F) + where + F: FnOnce(&mut CetFlags, &mut Page), + { + let (mut flags, mut legacy_bitmap) = Self::read(); + f(&mut flags, &mut legacy_bitmap); + Self::write(flags, legacy_bitmap); + } } impl SCet { @@ -595,5 +624,16 @@ mod x86_64 { pub fn write(flags: CetFlags, legacy_bitmap: Page) { Self::write_raw(flags.bits() | legacy_bitmap.start_address().as_u64()); } + + /// Updates IA32_S_CET. + #[inline] + pub fn update(f: F) + where + F: FnOnce(&mut CetFlags, &mut Page), + { + let (mut flags, mut legacy_bitmap) = Self::read(); + f(&mut flags, &mut legacy_bitmap); + Self::write(flags, legacy_bitmap); + } } } diff --git a/src/registers/mxcsr.rs b/src/registers/mxcsr.rs index 46e10778..a25dc431 100644 --- a/src/registers/mxcsr.rs +++ b/src/registers/mxcsr.rs @@ -83,6 +83,17 @@ mod x86_64 { } } + /// Update MXCSR. + #[inline] + pub fn update(f: F) + where + F: FnOnce(&mut MxCsr), + { + let mut mxcsr = self::read(); + f(&mut mxcsr); + self::write(mxcsr); + } + #[cfg(test)] mod test { use crate::registers::mxcsr::*; diff --git a/src/registers/rflags.rs b/src/registers/rflags.rs index 15c580b8..66639877 100644 --- a/src/registers/rflags.rs +++ b/src/registers/rflags.rs @@ -126,6 +126,25 @@ mod x86_64 { } } + /// Updates the RFLAGS register, preserves reserved bits. + /// + /// ## Safety + /// + /// Unsafe because undefined becavior can occur if certain flags are modified. For example, + /// the `DF` flag must be unset in all Rust code. Also, modifying `CF`, `PF`, or any other + /// flags also used by Rust/LLVM can result in undefined behavior too. + #[inline] + pub unsafe fn update(f: F) + where + F: FnOnce(&mut RFlags), + { + let mut flags = self::read(); + f(&mut flags); + unsafe { + self::write(flags); + } + } + #[cfg(test)] mod test { use crate::registers::rflags::read; diff --git a/src/registers/xcontrol.rs b/src/registers/xcontrol.rs index 89176366..5295858c 100644 --- a/src/registers/xcontrol.rs +++ b/src/registers/xcontrol.rs @@ -145,5 +145,26 @@ mod x86_64 { ); } } + + /// Update XCR0 flags. + /// + /// Preserves the value of reserved fields. + /// Panics if invalid combinations of [`XCr0Flags`] are set. + /// + /// ## Safety + /// + /// This function is unsafe because it's possible to + /// enable features that are not supported by the architecture. + #[inline] + pub unsafe fn update(f: F) + where + F: FnOnce(&mut XCr0Flags), + { + let mut flags = Self::read(); + f(&mut flags); + unsafe { + Self::write(flags); + } + } } } From 7c82b6cbe3ad250e88316b84c87d7fdd3bec733f Mon Sep 17 00:00:00 2001 From: Alison Davis Date: Sat, 18 Jan 2025 19:32:30 -0600 Subject: [PATCH 05/24] Add page attribute table support --- src/registers/model_specific.rs | 106 ++++++++++++++++++ .../paging/mapper/mapped_page_table.rs | 8 +- .../paging/mapper/recursive_page_table.rs | 43 ++++--- src/structures/paging/page_table.rs | 30 +++-- 4 files changed, 153 insertions(+), 34 deletions(-) diff --git a/src/registers/model_specific.rs b/src/registers/model_specific.rs index cf2dd34b..cfe324b1 100644 --- a/src/registers/model_specific.rs +++ b/src/registers/model_specific.rs @@ -67,6 +67,10 @@ pub struct UCet; #[derive(Debug)] pub struct SCet; +/// IA32_PAT: Page Attribute Table. +#[derive(Debug)] +pub struct Pat; + impl Efer { /// The underlying model specific register. pub const MSR: Msr = Msr(0xC000_0080); @@ -112,6 +116,22 @@ impl SCet { pub const MSR: Msr = Msr(0x6A2); } +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: [PatFlags; 8] = [ + PatFlags::WRITE_BACK, + PatFlags::WRITE_THROUGH, + PatFlags::UNCACHED, + PatFlags::UNCACHEABLE, + PatFlags::WRITE_BACK, + PatFlags::WRITE_THROUGH, + PatFlags::UNCACHED, + PatFlags::UNCACHEABLE, + ]; +} + bitflags! { /// Flags of the Extended Feature Enable Register. #[repr(transparent)] @@ -161,6 +181,52 @@ bitflags! { } } +#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)] +/// Flags for the [PAT](Pat). +pub struct PatFlags(u8); +impl PatFlags { + /// Disables caching. + pub const UNCACHEABLE: Self = Self(0x00); + /// Uses a write combining cache policy. + pub const WRITE_COMBINING: Self = Self(0x01); + /// Uses a write through cache policy. + pub const WRITE_THROUGH: Self = Self(0x04); + /// Uses a write protected cache policy. + pub const WRITE_PROTECTED: Self = Self(0x05); + /// Uses a write back cache policy. + pub const WRITE_BACK: Self = Self(0x06); + /// Same as uncacheable, but can be overridden by MTRRs. + pub const UNCACHED: Self = Self(0x07); + + /// Converts from bits, returning `None` if the value is invalid. + pub const fn from_bits(bits: u8) -> Option { + match Self(bits) { + Self::UNCACHEABLE + | Self::WRITE_COMBINING + | Self::WRITE_THROUGH + | Self::WRITE_PROTECTED + | Self::WRITE_BACK + | Self::UNCACHED => Some(Self(bits)), + _ => None, + } + } + + /// Converts from bits without checking if the value is valid. + /// + /// # Safety + /// + /// `bits` must correspond to a valid memory type, otherwise a general protection exception will + /// occur if it is written to the PAT. + pub const unsafe fn from_bits_unchecked(bits: u8) -> Self { + Self(bits) + } + + /// Gets the underlying bits. + pub const fn bits(self) -> u8 { + self.0 + } +} + #[cfg(all(feature = "instructions", target_arch = "x86_64"))] mod x86_64 { use super::*; @@ -636,4 +702,44 @@ mod x86_64 { Self::write(flags, legacy_bitmap); } } + + impl Pat { + /// Reads IA32_PAT. + /// + /// # Safety + /// + /// 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 unsafe fn read() -> [PatFlags; 8] { + let bits = unsafe { Self::MSR.read() }; + let mut flags = [PatFlags::UNCACHEABLE; 8]; + for (i, flag) in flags.iter_mut().enumerate() { + *flag = PatFlags((bits >> (8 * i)) as u8); + } + flags + } + + /// Writes IA32_PAT. + /// + /// # 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. + /// + /// 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 unsafe fn write(flags: [PatFlags; 8]) { + let mut bits = 0u64; + for (i, flag) in flags.iter().enumerate() { + bits |= (flag.bits() as u64) << (8 * i); + } + let mut msr = Self::MSR; + unsafe { + msr.write(bits); + } + } + } } diff --git a/src/structures/paging/mapper/mapped_page_table.rs b/src/structures/paging/mapper/mapped_page_table.rs index b50f072e..56c38155 100644 --- a/src/structures/paging/mapper/mapped_page_table.rs +++ b/src/structures/paging/mapper/mapped_page_table.rs @@ -421,7 +421,6 @@ impl Mapper for MappedPageTable<'_, P> { let frame = p1_entry.frame().map_err(|err| match err { FrameError::FrameNotPresent => UnmapError::PageNotMapped, - FrameError::HugeFrame => UnmapError::ParentEntryHugePage, })?; p1_entry.set_unused(); @@ -711,6 +710,9 @@ impl PageTableWalker

{ &self, entry: &'b PageTableEntry, ) -> Result<&'b PageTable, PageTableWalkError> { + if entry.flags().contains(PageTableFlags::HUGE_PAGE) { + return Err(PageTableWalkError::MappedToHugePage); + } let page_table_ptr = self .page_table_frame_mapping .frame_to_pointer(entry.frame()?); @@ -729,6 +731,9 @@ impl PageTableWalker

{ &self, entry: &'b mut PageTableEntry, ) -> Result<&'b mut PageTable, PageTableWalkError> { + if entry.flags().contains(PageTableFlags::HUGE_PAGE) { + return Err(PageTableWalkError::MappedToHugePage); + } let page_table_ptr = self .page_table_frame_mapping .frame_to_pointer(entry.frame()?); @@ -832,7 +837,6 @@ impl From for PageTableWalkError { #[inline] fn from(err: FrameError) -> Self { match err { - FrameError::HugeFrame => PageTableWalkError::MappedToHugePage, FrameError::FrameNotPresent => PageTableWalkError::NotMapped, } } diff --git a/src/structures/paging/mapper/recursive_page_table.rs b/src/structures/paging/mapper/recursive_page_table.rs index ff427ffa..e57ddcb9 100644 --- a/src/structures/paging/mapper/recursive_page_table.rs +++ b/src/structures/paging/mapper/recursive_page_table.rs @@ -322,9 +322,11 @@ impl Mapper for RecursivePageTable<'_> { let p4 = &mut self.p4; let p4_entry = &p4[page.p4_index()]; - p4_entry.frame().map_err(|err| match err { + if p4_entry.flags().contains(PageTableFlags::HUGE_PAGE) { + return Err(UnmapError::ParentEntryHugePage); + } + p4_entry.frame::().map_err(|err| match err { FrameError::FrameNotPresent => UnmapError::PageNotMapped, - FrameError::HugeFrame => UnmapError::ParentEntryHugePage, })?; let p3 = unsafe { &mut *(p3_ptr(page, self.recursive_index)) }; @@ -441,16 +443,20 @@ impl Mapper for RecursivePageTable<'_> { ) -> Result<(PhysFrame, MapperFlush), UnmapError> { let p4 = &mut self.p4; let p4_entry = &p4[page.p4_index()]; - p4_entry.frame().map_err(|err| match err { + if p4_entry.flags().contains(PageTableFlags::HUGE_PAGE) { + return Err(UnmapError::ParentEntryHugePage); + } + p4_entry.frame::().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 { + if p3_entry.flags().contains(PageTableFlags::HUGE_PAGE) { + return Err(UnmapError::ParentEntryHugePage); + } + p3_entry.frame::().map_err(|err| match err { FrameError::FrameNotPresent => UnmapError::PageNotMapped, - FrameError::HugeFrame => UnmapError::ParentEntryHugePage, })?; let p2 = unsafe { &mut *(p2_ptr(page, self.recursive_index)) }; @@ -596,23 +602,29 @@ impl Mapper for RecursivePageTable<'_> { ) -> Result<(PhysFrame, MapperFlush), UnmapError> { let p4 = &mut self.p4; let p4_entry = &p4[page.p4_index()]; - p4_entry.frame().map_err(|err| match err { + if p4_entry.flags().contains(PageTableFlags::HUGE_PAGE) { + return Err(UnmapError::ParentEntryHugePage); + } + p4_entry.frame::().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 { + if p3_entry.flags().contains(PageTableFlags::HUGE_PAGE) { + return Err(UnmapError::ParentEntryHugePage); + } + p3_entry.frame::().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 { + if p2_entry.flags().contains(PageTableFlags::HUGE_PAGE) { + return Err(UnmapError::ParentEntryHugePage); + } + p2_entry.frame::().map_err(|err| match err { FrameError::FrameNotPresent => UnmapError::PageNotMapped, - FrameError::HugeFrame => UnmapError::ParentEntryHugePage, })?; let p1 = unsafe { &mut *(p1_ptr(page, self.recursive_index)) }; @@ -620,7 +632,6 @@ impl Mapper for RecursivePageTable<'_> { let frame = p1_entry.frame().map_err(|err| match err { FrameError::FrameNotPresent => UnmapError::PageNotMapped, - FrameError::HugeFrame => UnmapError::ParentEntryHugePage, })?; p1_entry.set_unused(); @@ -818,9 +829,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, @@ -890,6 +898,9 @@ impl CleanUp for RecursivePageTable<'_> { !(level == PageTableLevel::Four && *i == recursive_index.into()) }) { + if entry.flags().contains(PageTableFlags::HUGE_PAGE) { + continue; + } if let Ok(frame) = entry.frame() { let start = VirtAddr::forward_checked_impl( table_addr, diff --git a/src/structures/paging/page_table.rs b/src/structures/paging/page_table.rs index e9069bcc..65a656b4 100644 --- a/src/structures/paging/page_table.rs +++ b/src/structures/paging/page_table.rs @@ -15,9 +15,6 @@ use bitflags::bitflags; pub enum FrameError { /// The entry does not have the `PRESENT` flag set, so it isn't currently mapped to a frame. FrameNotPresent, - /// The entry does have the `HUGE_PAGE` flag set. The `frame` method has a standard 4KiB frame - /// as return type, so a huge frame can't be returned. - HugeFrame, } /// A 64-bit page table entry. @@ -63,16 +60,12 @@ impl PageTableEntry { /// 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) #[inline] - pub fn frame(&self) -> Result { - if !self.flags().contains(PageTableFlags::PRESENT) { - Err(FrameError::FrameNotPresent) - } else if self.flags().contains(PageTableFlags::HUGE_PAGE) { - Err(FrameError::HugeFrame) - } else { + pub fn frame(&self) -> Result, FrameError> { + if self.flags().contains(PageTableFlags::PRESENT) { Ok(PhysFrame::containing_address(self.addr())) + } else { + Err(FrameError::FrameNotPresent) } } @@ -86,7 +79,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) } @@ -128,17 +120,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; @@ -148,6 +144,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. From e396fefb3fda7b026fe2a14326ebd94947e838ad Mon Sep 17 00:00:00 2001 From: Alison Davis Date: Sat, 18 Jan 2025 23:24:13 -0600 Subject: [PATCH 06/24] Formatting --- src/registers/model_specific.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registers/model_specific.rs b/src/registers/model_specific.rs index cfe324b1..54a3ff18 100644 --- a/src/registers/model_specific.rs +++ b/src/registers/model_specific.rs @@ -727,7 +727,7 @@ mod x86_64 { /// 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. - /// + /// /// The PAT must be supported on the CPU, otherwise a general protection exception will /// occur. Support can be detected using the `cpuid` instruction. #[inline] From 8aaff74419ecb9463100f865d617f6a17be40b48 Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Sun, 19 Jan 2025 10:24:34 +0100 Subject: [PATCH 07/24] fix clippy warnings --- src/registers/segmentation.rs | 2 +- src/registers/xcontrol.rs | 2 +- src/structures/idt.rs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/registers/segmentation.rs b/src/registers/segmentation.rs index e9f729c2..5d5954bc 100644 --- a/src/registers/segmentation.rs +++ b/src/registers/segmentation.rs @@ -76,7 +76,7 @@ impl SegmentSelector { /// * `rpl`: the requested privilege level #[inline] pub const fn new(index: u16, rpl: PrivilegeLevel) -> SegmentSelector { - SegmentSelector(index << 3 | (rpl as u16)) + SegmentSelector((index << 3) | (rpl as u16)) } /// Can be used as a selector into a non-existent segment and assigned to segment registers, diff --git a/src/registers/xcontrol.rs b/src/registers/xcontrol.rs index 5295858c..a9fb62af 100644 --- a/src/registers/xcontrol.rs +++ b/src/registers/xcontrol.rs @@ -73,7 +73,7 @@ mod x86_64 { out("rax") low, out("rdx") high, options(nomem, nostack, preserves_flags), ); - (high as u64) << 32 | (low as u64) + ((high as u64) << 32) | (low as u64) } } diff --git a/src/structures/idt.rs b/src/structures/idt.rs index 58e1ec44..3b24ff68 100644 --- a/src/structures/idt.rs +++ b/src/structures/idt.rs @@ -844,8 +844,8 @@ impl Entry { #[inline] pub fn handler_addr(&self) -> VirtAddr { let addr = self.pointer_low as u64 - | (self.pointer_middle as u64) << 16 - | (self.pointer_high as u64) << 32; + | ((self.pointer_middle as u64) << 16) + | ((self.pointer_high as u64) << 32); // addr is a valid VirtAddr, as the pointer members are either all zero, // or have been set by set_handler_addr (which takes a VirtAddr). VirtAddr::new_truncate(addr) From 1d1bcc2195d8c1165d839508bd47cb9fab2c4cc3 Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Sun, 19 Jan 2025 10:25:23 +0100 Subject: [PATCH 08/24] remove macos-12 from test matrix The macos-12 platform has been removed from GitHub actions. --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1216bcab..bc6a2b0b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -47,7 +47,7 @@ jobs: strategy: fail-fast: false matrix: - platform: [ubuntu-latest, macos-12, macos-latest, windows-latest] + platform: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{ matrix.platform }} timeout-minutes: 15 From 0ba90da5ddd7d69bc451ff8e5a650d13b83e9bf2 Mon Sep 17 00:00:00 2001 From: Alison Davis Date: Sun, 19 Jan 2025 15:04:32 -0600 Subject: [PATCH 09/24] Apply suggestions --- src/registers/model_specific.rs | 83 ++++++++----------- .../paging/mapper/mapped_page_table.rs | 4 + .../paging/mapper/recursive_page_table.rs | 14 ++++ src/structures/paging/page_table.rs | 3 + 4 files changed, 57 insertions(+), 47 deletions(-) diff --git a/src/registers/model_specific.rs b/src/registers/model_specific.rs index 54a3ff18..2260743a 100644 --- a/src/registers/model_specific.rs +++ b/src/registers/model_specific.rs @@ -120,15 +120,15 @@ 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: [PatFlags; 8] = [ - PatFlags::WRITE_BACK, - PatFlags::WRITE_THROUGH, - PatFlags::UNCACHED, - PatFlags::UNCACHEABLE, - PatFlags::WRITE_BACK, - PatFlags::WRITE_THROUGH, - PatFlags::UNCACHED, - PatFlags::UNCACHEABLE, + pub const DEFAULT: [PatMemoryType; 8] = [ + PatMemoryType::WriteBack, + PatMemoryType::WriteThrough, + PatMemoryType::Uncached, + PatMemoryType::Uncacheable, + PatMemoryType::WriteBack, + PatMemoryType::WriteThrough, + PatMemoryType::Uncached, + PatMemoryType::Uncacheable, ]; } @@ -182,48 +182,39 @@ bitflags! { } #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)] -/// Flags for the [PAT](Pat). -pub struct PatFlags(u8); -impl PatFlags { +/// Memory types used in the [PAT](Pat). +#[repr(u8)] +pub enum PatMemoryType { /// Disables caching. - pub const UNCACHEABLE: Self = Self(0x00); + Uncacheable = 0x00, /// Uses a write combining cache policy. - pub const WRITE_COMBINING: Self = Self(0x01); + WriteCombining = 0x01, /// Uses a write through cache policy. - pub const WRITE_THROUGH: Self = Self(0x04); + WriteThrough = 0x04, /// Uses a write protected cache policy. - pub const WRITE_PROTECTED: Self = Self(0x05); + WriteProtected = 0x05, /// Uses a write back cache policy. - pub const WRITE_BACK: Self = Self(0x06); + WriteBack = 0x06, /// Same as uncacheable, but can be overridden by MTRRs. - pub const UNCACHED: Self = Self(0x07); - + Uncached = 0x07, +} +impl PatMemoryType { /// Converts from bits, returning `None` if the value is invalid. pub const fn from_bits(bits: u8) -> Option { - match Self(bits) { - Self::UNCACHEABLE - | Self::WRITE_COMBINING - | Self::WRITE_THROUGH - | Self::WRITE_PROTECTED - | Self::WRITE_BACK - | Self::UNCACHED => Some(Self(bits)), + match bits { + 0x00 => Some(Self::Uncacheable), + 0x01 => Some(Self::WriteCombining), + 0x04 => Some(Self::WriteThrough), + 0x05 => Some(Self::WriteProtected), + 0x06 => Some(Self::WriteBack), + 0x07 => Some(Self::Uncached), _ => None, } } - /// Converts from bits without checking if the value is valid. - /// - /// # Safety - /// - /// `bits` must correspond to a valid memory type, otherwise a general protection exception will - /// occur if it is written to the PAT. - pub const unsafe fn from_bits_unchecked(bits: u8) -> Self { - Self(bits) - } - /// Gets the underlying bits. pub const fn bits(self) -> u8 { - self.0 + self as u8 } } @@ -706,34 +697,32 @@ mod x86_64 { impl Pat { /// Reads IA32_PAT. /// - /// # Safety - /// /// 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 unsafe fn read() -> [PatFlags; 8] { + pub fn read() -> [PatMemoryType; 8] { let bits = unsafe { Self::MSR.read() }; - let mut flags = [PatFlags::UNCACHEABLE; 8]; + let mut flags = [PatMemoryType::Uncacheable; 8]; for (i, flag) in flags.iter_mut().enumerate() { - *flag = PatFlags((bits >> (8 * i)) as u8); + *flag = PatMemoryType::from_bits((bits >> (8 * i)) as u8).unwrap(); } flags } /// 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. - /// - /// 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 unsafe fn write(flags: [PatFlags; 8]) { + pub unsafe fn write(table: [PatMemoryType; 8]) { let mut bits = 0u64; - for (i, flag) in flags.iter().enumerate() { + for (i, flag) in table.iter().enumerate() { bits |= (flag.bits() as u64) << (8 * i); } let mut msr = Self::MSR; diff --git a/src/structures/paging/mapper/mapped_page_table.rs b/src/structures/paging/mapper/mapped_page_table.rs index 56c38155..dfa3602e 100644 --- a/src/structures/paging/mapper/mapped_page_table.rs +++ b/src/structures/paging/mapper/mapped_page_table.rs @@ -421,6 +421,8 @@ impl Mapper for MappedPageTable<'_, P> { let frame = p1_entry.frame().map_err(|err| match err { FrameError::FrameNotPresent => UnmapError::PageNotMapped, + #[allow(deprecated)] + FrameError::HugeFrame => unreachable!(), })?; p1_entry.set_unused(); @@ -837,6 +839,8 @@ impl From for PageTableWalkError { #[inline] fn from(err: FrameError) -> Self { match err { + #[allow(deprecated)] + FrameError::HugeFrame => unreachable!(), FrameError::FrameNotPresent => PageTableWalkError::NotMapped, } } diff --git a/src/structures/paging/mapper/recursive_page_table.rs b/src/structures/paging/mapper/recursive_page_table.rs index e57ddcb9..65785ad2 100644 --- a/src/structures/paging/mapper/recursive_page_table.rs +++ b/src/structures/paging/mapper/recursive_page_table.rs @@ -327,6 +327,8 @@ impl Mapper for RecursivePageTable<'_> { } p4_entry.frame::().map_err(|err| match err { FrameError::FrameNotPresent => UnmapError::PageNotMapped, + #[allow(deprecated)] + FrameError::HugeFrame => unreachable!(), })?; let p3 = unsafe { &mut *(p3_ptr(page, self.recursive_index)) }; @@ -448,6 +450,8 @@ impl Mapper for RecursivePageTable<'_> { } p4_entry.frame::().map_err(|err| match err { FrameError::FrameNotPresent => UnmapError::PageNotMapped, + #[allow(deprecated)] + FrameError::HugeFrame => unreachable!(), })?; let p3 = unsafe { &mut *(p3_ptr(page, self.recursive_index)) }; @@ -457,6 +461,8 @@ impl Mapper for RecursivePageTable<'_> { } p3_entry.frame::().map_err(|err| match err { FrameError::FrameNotPresent => UnmapError::PageNotMapped, + #[allow(deprecated)] + FrameError::HugeFrame => unreachable!(), })?; let p2 = unsafe { &mut *(p2_ptr(page, self.recursive_index)) }; @@ -607,6 +613,8 @@ impl Mapper for RecursivePageTable<'_> { } p4_entry.frame::().map_err(|err| match err { FrameError::FrameNotPresent => UnmapError::PageNotMapped, + #[allow(deprecated)] + FrameError::HugeFrame => unreachable!(), })?; let p3 = unsafe { &mut *(p3_ptr(page, self.recursive_index)) }; @@ -616,6 +624,8 @@ impl Mapper for RecursivePageTable<'_> { } p3_entry.frame::().map_err(|err| match err { FrameError::FrameNotPresent => UnmapError::PageNotMapped, + #[allow(deprecated)] + FrameError::HugeFrame => unreachable!(), })?; let p2 = unsafe { &mut *(p2_ptr(page, self.recursive_index)) }; @@ -625,6 +635,8 @@ impl Mapper for RecursivePageTable<'_> { } p2_entry.frame::().map_err(|err| match err { FrameError::FrameNotPresent => UnmapError::PageNotMapped, + #[allow(deprecated)] + FrameError::HugeFrame => unreachable!(), })?; let p1 = unsafe { &mut *(p1_ptr(page, self.recursive_index)) }; @@ -632,6 +644,8 @@ impl Mapper for RecursivePageTable<'_> { let frame = p1_entry.frame().map_err(|err| match err { FrameError::FrameNotPresent => UnmapError::PageNotMapped, + #[allow(deprecated)] + FrameError::HugeFrame => unreachable!(), })?; p1_entry.set_unused(); diff --git a/src/structures/paging/page_table.rs b/src/structures/paging/page_table.rs index 65a656b4..330be709 100644 --- a/src/structures/paging/page_table.rs +++ b/src/structures/paging/page_table.rs @@ -15,6 +15,9 @@ use bitflags::bitflags; pub enum FrameError { /// The entry does not have the `PRESENT` flag set, so it isn't currently mapped to a frame. FrameNotPresent, + #[deprecated = "`HugeFrame` is no longer returned by the `frame` method, as it can now return huge pages"] + /// The entry does have the `HUGE_PAGE` flag set. + HugeFrame, } /// A 64-bit page table entry. From 2486164b5cfbfcecc08ad8a762bb6b88072c20ed Mon Sep 17 00:00:00 2001 From: Alison Davis Date: Sun, 26 Jan 2025 22:52:24 -0600 Subject: [PATCH 10/24] Fixes --- src/registers/model_specific.rs | 38 ++++++++----------- .../paging/mapper/recursive_page_table.rs | 12 +++--- src/structures/paging/page_table.rs | 4 +- 3 files changed, 24 insertions(+), 30 deletions(-) diff --git a/src/registers/model_specific.rs b/src/registers/model_specific.rs index 2260743a..fa1902c9 100644 --- a/src/registers/model_specific.rs +++ b/src/registers/model_specific.rs @@ -123,12 +123,12 @@ impl Pat { pub const DEFAULT: [PatMemoryType; 8] = [ PatMemoryType::WriteBack, PatMemoryType::WriteThrough, - PatMemoryType::Uncached, PatMemoryType::Uncacheable, + PatMemoryType::StrongUncacheable, PatMemoryType::WriteBack, PatMemoryType::WriteThrough, - PatMemoryType::Uncached, PatMemoryType::Uncacheable, + PatMemoryType::StrongUncacheable, ]; } @@ -185,29 +185,29 @@ bitflags! { /// Memory types used in the [PAT](Pat). #[repr(u8)] pub enum PatMemoryType { - /// Disables caching. - Uncacheable = 0x00, - /// Uses a write combining cache policy. + /// Uncacheable (UC). + StrongUncacheable = 0x00, + /// Uses a write combining (WC) cache policy. WriteCombining = 0x01, - /// Uses a write through cache policy. + /// Uses a write through (WT) cache policy. WriteThrough = 0x04, - /// Uses a write protected cache policy. + /// Uses a write protected (WP) cache policy. WriteProtected = 0x05, - /// Uses a write back cache policy. + /// Uses a write back (WB) cache policy. WriteBack = 0x06, - /// Same as uncacheable, but can be overridden by MTRRs. - Uncached = 0x07, + /// 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::Uncacheable), + 0x00 => Some(Self::StrongUncacheable), 0x01 => Some(Self::WriteCombining), 0x04 => Some(Self::WriteThrough), 0x05 => Some(Self::WriteProtected), 0x06 => Some(Self::WriteBack), - 0x07 => Some(Self::Uncached), + 0x07 => Some(Self::Uncacheable), _ => None, } } @@ -701,12 +701,9 @@ mod x86_64 { /// occur. Support can be detected using the `cpuid` instruction. #[inline] pub fn read() -> [PatMemoryType; 8] { - let bits = unsafe { Self::MSR.read() }; - let mut flags = [PatMemoryType::Uncacheable; 8]; - for (i, flag) in flags.iter_mut().enumerate() { - *flag = PatMemoryType::from_bits((bits >> (8 * i)) as u8).unwrap(); - } - flags + unsafe { Self::MSR.read() } + .to_ne_bytes() + .map(|bits| PatMemoryType::from_bits(bits).unwrap()) } /// Writes IA32_PAT. @@ -721,10 +718,7 @@ mod x86_64 { /// type. #[inline] pub unsafe fn write(table: [PatMemoryType; 8]) { - let mut bits = 0u64; - for (i, flag) in table.iter().enumerate() { - bits |= (flag.bits() as u64) << (8 * i); - } + let bits = u64::from_ne_bytes(table.map(PatMemoryType::bits)); let mut msr = Self::MSR; unsafe { msr.write(bits); diff --git a/src/structures/paging/mapper/recursive_page_table.rs b/src/structures/paging/mapper/recursive_page_table.rs index 65785ad2..3906a1a5 100644 --- a/src/structures/paging/mapper/recursive_page_table.rs +++ b/src/structures/paging/mapper/recursive_page_table.rs @@ -325,7 +325,7 @@ impl Mapper for RecursivePageTable<'_> { if p4_entry.flags().contains(PageTableFlags::HUGE_PAGE) { return Err(UnmapError::ParentEntryHugePage); } - p4_entry.frame::().map_err(|err| match err { + p4_entry.frame().map_err(|err| match err { FrameError::FrameNotPresent => UnmapError::PageNotMapped, #[allow(deprecated)] FrameError::HugeFrame => unreachable!(), @@ -448,7 +448,7 @@ impl Mapper for RecursivePageTable<'_> { if p4_entry.flags().contains(PageTableFlags::HUGE_PAGE) { return Err(UnmapError::ParentEntryHugePage); } - p4_entry.frame::().map_err(|err| match err { + p4_entry.frame().map_err(|err| match err { FrameError::FrameNotPresent => UnmapError::PageNotMapped, #[allow(deprecated)] FrameError::HugeFrame => unreachable!(), @@ -459,7 +459,7 @@ impl Mapper for RecursivePageTable<'_> { if p3_entry.flags().contains(PageTableFlags::HUGE_PAGE) { return Err(UnmapError::ParentEntryHugePage); } - p3_entry.frame::().map_err(|err| match err { + p3_entry.frame().map_err(|err| match err { FrameError::FrameNotPresent => UnmapError::PageNotMapped, #[allow(deprecated)] FrameError::HugeFrame => unreachable!(), @@ -611,7 +611,7 @@ impl Mapper for RecursivePageTable<'_> { if p4_entry.flags().contains(PageTableFlags::HUGE_PAGE) { return Err(UnmapError::ParentEntryHugePage); } - p4_entry.frame::().map_err(|err| match err { + p4_entry.frame().map_err(|err| match err { FrameError::FrameNotPresent => UnmapError::PageNotMapped, #[allow(deprecated)] FrameError::HugeFrame => unreachable!(), @@ -622,7 +622,7 @@ impl Mapper for RecursivePageTable<'_> { if p3_entry.flags().contains(PageTableFlags::HUGE_PAGE) { return Err(UnmapError::ParentEntryHugePage); } - p3_entry.frame::().map_err(|err| match err { + p3_entry.frame().map_err(|err| match err { FrameError::FrameNotPresent => UnmapError::PageNotMapped, #[allow(deprecated)] FrameError::HugeFrame => unreachable!(), @@ -633,7 +633,7 @@ impl Mapper for RecursivePageTable<'_> { if p2_entry.flags().contains(PageTableFlags::HUGE_PAGE) { return Err(UnmapError::ParentEntryHugePage); } - p2_entry.frame::().map_err(|err| match err { + p2_entry.frame().map_err(|err| match err { FrameError::FrameNotPresent => UnmapError::PageNotMapped, #[allow(deprecated)] FrameError::HugeFrame => unreachable!(), diff --git a/src/structures/paging/page_table.rs b/src/structures/paging/page_table.rs index 330be709..6ee1a8dc 100644 --- a/src/structures/paging/page_table.rs +++ b/src/structures/paging/page_table.rs @@ -15,7 +15,7 @@ use bitflags::bitflags; pub enum FrameError { /// The entry does not have the `PRESENT` flag set, so it isn't currently mapped to a frame. FrameNotPresent, - #[deprecated = "`HugeFrame` is no longer returned by the `frame` method, as it can now return huge pages"] + #[deprecated = "`HugeFrame` is no longer returned by the `frame` method"] /// The entry does have the `HUGE_PAGE` flag set. HugeFrame, } @@ -64,7 +64,7 @@ impl PageTableEntry { /// /// - `FrameError::FrameNotPresent` if the entry doesn't have the `PRESENT` flag set. #[inline] - pub fn frame(&self) -> Result, FrameError> { + pub fn frame(&self) -> Result { if self.flags().contains(PageTableFlags::PRESENT) { Ok(PhysFrame::containing_address(self.addr())) } else { From 32327c09859de8ab627c942fd15624c7d7cf470b Mon Sep 17 00:00:00 2001 From: Hqnnqh Date: Mon, 27 Jan 2025 19:53:52 +0100 Subject: [PATCH 11/24] feat(msr): add IA32_APIC_BASE support --- src/registers/model_specific.rs | 105 ++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/src/registers/model_specific.rs b/src/registers/model_specific.rs index fa1902c9..c7815117 100644 --- a/src/registers/model_specific.rs +++ b/src/registers/model_specific.rs @@ -71,6 +71,10 @@ pub struct SCet; #[derive(Debug)] pub struct Pat; +/// IA32_APIC_BASE: status and location of the local APIC +#[derive(Debug)] +pub struct Apic; + impl Efer { /// The underlying model specific register. pub const MSR: Msr = Msr(0xC000_0080); @@ -132,6 +136,11 @@ impl Pat { ]; } +impl Apic { + /// The underlying model specific register. + pub const MSR: Msr = Msr(0x1B); +} + bitflags! { /// Flags of the Extended Feature Enable Register. #[repr(transparent)] @@ -218,6 +227,31 @@ impl PatMemoryType { } } +bitflags! { + /// Flags for the Advanced Programmable Interrupt Controler Base Register. + #[repr(transparent)] + #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)] + pub struct ApicFlags: u64 { + // bits 0 - 7 are reserved. + /// Indicates whether the current processor is the bootstrap processor + const BSP = 1 << 8; + // bits 9 - 10 are reserved. + /// Enables or disables the local Apic + const LAPIC_ENABLE = 1 << 11; + /// Specifies the base address of the APIC registers. This 24-bit value is extended by 12 bits at the low end to form the base address. + const APIC_BASE = 0b111111111111111111111111 << 12; + // bits 36-63 reserved + } +} + +impl ApicFlags { + /// Returns the physical address of the apic registers + #[inline] + pub fn address(&self) -> u64 { + self.bits() & 0b11111111111111111111000000000000 + } +} + #[cfg(all(feature = "instructions", target_arch = "x86_64"))] mod x86_64 { use super::*; @@ -725,4 +759,75 @@ mod x86_64 { } } } + + impl Apic { + /// Reads the IA32_APIC_BASE. + /// + /// The APIC_BASE 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() -> ApicFlags { + ApicFlags::from_bits_truncate(Self::read_raw()) + } + + /// Reads the raw IA32_APIC_BASE. + /// + /// The APIC_BASE 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_raw() -> u64 { + unsafe { Self::MSR.read() } + } + + /// Writes the IA32_APIC_BASE preserving reserved values. + /// + /// Preserves the value of reserved fields. + /// + /// The APIC_BASE must be supported on the CPU, otherwise a general protection exception will + /// occur. Support can be detected using the `cpuid` instruction. + #[inline] + pub fn write(flags: ApicFlags) { + let old_value = Self::read_raw(); + let reserved = old_value & !(ApicFlags::all().bits()); + let new_value = reserved | flags.bits(); + + unsafe { + Self::write_raw(new_value); + } + } + + /// Writes the IA32_APIC_BASE flags. + /// + /// Does not preserve any bits, including reserved fields. + /// + /// The APIC_BASE must be supported on the CPU, otherwise a general protection exception will + /// occur. Support can be detected using the `cpuid` instruction. + /// + /// ## Safety + /// + /// Unsafe because it's possible to set reserved bits to `1`. + #[inline] + pub unsafe fn write_raw(flags: u64) { + let mut msr = Self::MSR; + unsafe { + msr.write(flags); + } + } + + /// Update IA32_APIC_BASE flags. + /// + /// Preserves the value of reserved fields. + /// + /// The APIC_BASE must be supported on the CPU, otherwise a general protection exception will + /// occur. Support can be detected using the `cpuid` instruction. + #[inline] + pub fn update(f: F) + where + F: FnOnce(&mut ApicFlags), + { + let mut flags = Self::read(); + f(&mut flags); + Self::write(flags); + } + } } From 0609af059aedf6bee03c171b515cb10b650df332 Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Mon, 27 Jan 2025 21:20:15 +0100 Subject: [PATCH 12/24] use default python again Currently, ubuntu-latest is ubuntu-24.04. ubuntu-24.04 ships with python 3.12 which is newer than python 3.11 (the version we previously manually installed). --- .github/workflows/release.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b2cd1194..48ae9b9f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -21,12 +21,8 @@ jobs: - name: "Checkout Repository" uses: actions/checkout@v4 - # TODO: Remove when Python 3.11 is the default on the Gihtub Actions image - - name: "Install Python 3.11" - run: sudo apt-get -y install python3.11 - - name: "Run release script" - run: "python3.11 scripts/ci-release.py" + run: "python scripts/ci-release.py" env: CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 6286c5180a1f3794ddd78465d2d27e0ecaecfbe2 Mon Sep 17 00:00:00 2001 From: Hqnnqh Date: Tue, 28 Jan 2025 08:51:38 +0100 Subject: [PATCH 13/24] feat(msr): add X2APIC enable functionality --- src/registers/model_specific.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/registers/model_specific.rs b/src/registers/model_specific.rs index c7815117..70dd49aa 100644 --- a/src/registers/model_specific.rs +++ b/src/registers/model_specific.rs @@ -235,7 +235,10 @@ bitflags! { // bits 0 - 7 are reserved. /// Indicates whether the current processor is the bootstrap processor const BSP = 1 << 8; - // bits 9 - 10 are reserved. + // bit 9 is reserved. + /// Places the local APIC in the x2APIC mode. Processor support for x2APIC feature can be + /// detected using the `cpuid` instruction. (CPUID.(EAX=1):ECX.21) + const X2APIC_ENABLE = 1 << 10; /// Enables or disables the local Apic const LAPIC_ENABLE = 1 << 11; /// Specifies the base address of the APIC registers. This 24-bit value is extended by 12 bits at the low end to form the base address. From 7a97e484a970dac13007a470bf29ec25100de503 Mon Sep 17 00:00:00 2001 From: Hqnnqh Date: Tue, 28 Jan 2025 09:57:29 +0100 Subject: [PATCH 14/24] refactor(msr): handle apic base as seperate value --- src/registers/model_specific.rs | 63 +++++++++++++-------------------- 1 file changed, 24 insertions(+), 39 deletions(-) diff --git a/src/registers/model_specific.rs b/src/registers/model_specific.rs index 70dd49aa..47d0d4e3 100644 --- a/src/registers/model_specific.rs +++ b/src/registers/model_specific.rs @@ -241,17 +241,6 @@ bitflags! { const X2APIC_ENABLE = 1 << 10; /// Enables or disables the local Apic const LAPIC_ENABLE = 1 << 11; - /// Specifies the base address of the APIC registers. This 24-bit value is extended by 12 bits at the low end to form the base address. - const APIC_BASE = 0b111111111111111111111111 << 12; - // bits 36-63 reserved - } -} - -impl ApicFlags { - /// Returns the physical address of the apic registers - #[inline] - pub fn address(&self) -> u64 { - self.bits() & 0b11111111111111111111000000000000 } } @@ -262,7 +251,9 @@ mod x86_64 { use crate::registers::rflags::RFlags; use crate::structures::gdt::SegmentSelector; use crate::structures::paging::Page; + use crate::structures::paging::PhysFrame; use crate::structures::paging::Size4KiB; + use crate::PhysAddr; use crate::PrivilegeLevel; use bit_field::BitField; use core::convert::TryInto; @@ -769,8 +760,9 @@ mod x86_64 { /// The APIC_BASE 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() -> ApicFlags { - ApicFlags::from_bits_truncate(Self::read_raw()) + pub fn read() -> (PhysFrame, ApicFlags) { + let (frame, flags) = Self::read_raw(); + (frame, ApicFlags::from_bits_truncate(flags)) } /// Reads the raw IA32_APIC_BASE. @@ -778,8 +770,12 @@ mod x86_64 { /// The APIC_BASE 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_raw() -> u64 { - unsafe { Self::MSR.read() } + pub fn read_raw() -> (PhysFrame, u64) { + let raw = unsafe { Self::MSR.read() }; + // extract bits 32 - 51 (incl.) + let addr = PhysAddr::new((raw >> 32) & 0xFFFFF); + let frame = PhysFrame::containing_address(addr); + (frame, raw) } /// Writes the IA32_APIC_BASE preserving reserved values. @@ -788,14 +784,18 @@ mod x86_64 { /// /// The APIC_BASE must be supported on the CPU, otherwise a general protection exception will /// occur. Support can be detected using the `cpuid` instruction. + /// + /// ## Safety + /// + /// Unsafe because changing the APIC base address allows hijacking a page of physical memory space in ways that would violate Rust's memory rules. #[inline] - pub fn write(flags: ApicFlags) { - let old_value = Self::read_raw(); - let reserved = old_value & !(ApicFlags::all().bits()); - let new_value = reserved | flags.bits(); + pub unsafe fn write(frame: PhysFrame, flags: ApicFlags) { + let (_, old_flags) = Self::read_raw(); + let reserved = old_flags & !(ApicFlags::all().bits()); + let new_flags = reserved | flags.bits(); unsafe { - Self::write_raw(new_value); + Self::write_raw(frame, new_flags); } } @@ -808,29 +808,14 @@ mod x86_64 { /// /// ## Safety /// - /// Unsafe because it's possible to set reserved bits to `1`. + /// Unsafe because it's possible to set reserved bits to `1` and changing the APIC base address allows hijacking a page of physical memory space in ways that would violate Rust's memory rules. #[inline] - pub unsafe fn write_raw(flags: u64) { + pub unsafe fn write_raw(frame: PhysFrame, flags: u64) { + let addr = frame.start_address(); let mut msr = Self::MSR; unsafe { - msr.write(flags); + msr.write(flags | addr.as_u64()); } } - - /// Update IA32_APIC_BASE flags. - /// - /// Preserves the value of reserved fields. - /// - /// The APIC_BASE must be supported on the CPU, otherwise a general protection exception will - /// occur. Support can be detected using the `cpuid` instruction. - #[inline] - pub fn update(f: F) - where - F: FnOnce(&mut ApicFlags), - { - let mut flags = Self::read(); - f(&mut flags); - Self::write(flags); - } } } From 753388d6895c16dfb71e60fd03a722619ac4b41d Mon Sep 17 00:00:00 2001 From: Hqnnqh Date: Tue, 28 Jan 2025 12:19:28 +0100 Subject: [PATCH 15/24] fix(msr): properly mask APIC_BASE --- src/registers/model_specific.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registers/model_specific.rs b/src/registers/model_specific.rs index 47d0d4e3..c6b299e3 100644 --- a/src/registers/model_specific.rs +++ b/src/registers/model_specific.rs @@ -773,7 +773,7 @@ mod x86_64 { pub fn read_raw() -> (PhysFrame, u64) { let raw = unsafe { Self::MSR.read() }; // extract bits 32 - 51 (incl.) - let addr = PhysAddr::new((raw >> 32) & 0xFFFFF); + let addr = PhysAddr::new(raw & 0x_000F_FFFF_FFFF_F000); let frame = PhysFrame::containing_address(addr); (frame, raw) } From 9e38b0879147ed3ff4ea3fb1bc709449ee4b774d Mon Sep 17 00:00:00 2001 From: Hqnnqh Date: Tue, 28 Jan 2025 21:36:35 +0100 Subject: [PATCH 16/24] style(msr): address cosmetic changes --- src/registers/model_specific.rs | 42 +++++++++++++-------------------- 1 file changed, 16 insertions(+), 26 deletions(-) diff --git a/src/registers/model_specific.rs b/src/registers/model_specific.rs index c6b299e3..f8dbb71f 100644 --- a/src/registers/model_specific.rs +++ b/src/registers/model_specific.rs @@ -72,8 +72,10 @@ pub struct SCet; pub struct Pat; /// IA32_APIC_BASE: status and location of the local APIC +/// +/// IA32_APIC_BASE must be supported on the CPU, otherwise, a general protection exception will occur. Support can be detected using the `cpuid` instruction. #[derive(Debug)] -pub struct Apic; +pub struct ApicBase; impl Efer { /// The underlying model specific register. @@ -136,7 +138,7 @@ impl Pat { ]; } -impl Apic { +impl ApicBase { /// The underlying model specific register. pub const MSR: Msr = Msr(0x1B); } @@ -231,7 +233,7 @@ bitflags! { /// Flags for the Advanced Programmable Interrupt Controler Base Register. #[repr(transparent)] #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)] - pub struct ApicFlags: u64 { + pub struct ApicBaseFlags: u64 { // bits 0 - 7 are reserved. /// Indicates whether the current processor is the bootstrap processor const BSP = 1 << 8; @@ -754,44 +756,35 @@ mod x86_64 { } } - impl Apic { - /// Reads the IA32_APIC_BASE. - /// - /// The APIC_BASE must be supported on the CPU, otherwise a general protection exception will - /// occur. Support can be detected using the `cpuid` instruction. + impl ApicBase { + /// Reads the IA32_APIC_BASE MSR. #[inline] - pub fn read() -> (PhysFrame, ApicFlags) { + pub fn read() -> (PhysFrame, ApicBaseFlags) { let (frame, flags) = Self::read_raw(); - (frame, ApicFlags::from_bits_truncate(flags)) + (frame, ApicBaseFlags::from_bits_truncate(flags)) } - /// Reads the raw IA32_APIC_BASE. - /// - /// The APIC_BASE must be supported on the CPU, otherwise a general protection exception will - /// occur. Support can be detected using the `cpuid` instruction. + /// Reads the raw IA32_APIC_BASE MSR. #[inline] pub fn read_raw() -> (PhysFrame, u64) { let raw = unsafe { Self::MSR.read() }; - // extract bits 32 - 51 (incl.) - let addr = PhysAddr::new(raw & 0x_000F_FFFF_FFFF_F000); + // extract bits 12 - 51 (incl.) + let addr = PhysAddr::new_truncate(raw); let frame = PhysFrame::containing_address(addr); (frame, raw) } - /// Writes the IA32_APIC_BASE preserving reserved values. + /// Writes the IA32_APIC_BASE MSR preserving reserved values. /// /// Preserves the value of reserved fields. /// - /// The APIC_BASE must be supported on the CPU, otherwise a general protection exception will - /// occur. Support can be detected using the `cpuid` instruction. - /// /// ## Safety /// /// Unsafe because changing the APIC base address allows hijacking a page of physical memory space in ways that would violate Rust's memory rules. #[inline] - pub unsafe fn write(frame: PhysFrame, flags: ApicFlags) { + pub unsafe fn write(frame: PhysFrame, flags: ApicBaseFlags) { let (_, old_flags) = Self::read_raw(); - let reserved = old_flags & !(ApicFlags::all().bits()); + let reserved = old_flags & !(ApicBaseFlags::all().bits()); let new_flags = reserved | flags.bits(); unsafe { @@ -799,13 +792,10 @@ mod x86_64 { } } - /// Writes the IA32_APIC_BASE flags. + /// Writes the IA32_APIC_BASE MSR flags. /// /// Does not preserve any bits, including reserved fields. /// - /// The APIC_BASE must be supported on the CPU, otherwise a general protection exception will - /// occur. Support can be detected using the `cpuid` instruction. - /// /// ## Safety /// /// Unsafe because it's possible to set reserved bits to `1` and changing the APIC base address allows hijacking a page of physical memory space in ways that would violate Rust's memory rules. From 01ffca1aaf0e35bf47c3859bd0e20fa5b1a00c96 Mon Sep 17 00:00:00 2001 From: Simon Farnsworth Date: Tue, 11 Feb 2025 18:44:09 +0000 Subject: [PATCH 17/24] Ensure that addition and subtraction of addresses panics on overflow/underflow --- src/addr.rs | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/addr.rs b/src/addr.rs index 7ce471ee..c30ffc25 100644 --- a/src/addr.rs +++ b/src/addr.rs @@ -366,7 +366,7 @@ impl Add for VirtAddr { type Output = Self; #[inline] fn add(self, rhs: u64) -> Self::Output { - VirtAddr::new(self.0 + rhs) + VirtAddr::new(self.0.checked_add(rhs).unwrap()) } } @@ -593,7 +593,7 @@ impl Add for PhysAddr { type Output = Self; #[inline] fn add(self, rhs: u64) -> Self::Output { - PhysAddr::new(self.0 + rhs) + PhysAddr::new(self.0.checked_add(rhs).unwrap()) } } @@ -663,6 +663,30 @@ pub const fn align_up(addr: u64, align: u64) -> u64 { mod tests { use super::*; + #[test] + #[should_panic] + pub fn add_overflow_virtaddr() { + let _ = VirtAddr::new(0xffff_ffff_ffff_ffff) + 1; + } + + #[test] + #[should_panic] + pub fn add_overflow_physaddr() { + let _ = PhysAddr::new(0x000f_ffff_ffff_ffff) + 1; + } + + #[test] + #[should_panic] + pub fn sub_underflow_virtaddr() { + let _ = VirtAddr::new(0) - 1; + } + + #[test] + #[should_panic] + pub fn sub_overflow_physaddr() { + let _ = PhysAddr::new(0) - 1; + } + #[test] pub fn virtaddr_new_truncate() { assert_eq!(VirtAddr::new_truncate(0), VirtAddr(0)); From c36cfb4dfc2391e81ae815ee67f6d4e479d63e77 Mon Sep 17 00:00:00 2001 From: Simon Farnsworth Date: Tue, 11 Feb 2025 18:53:47 +0000 Subject: [PATCH 18/24] Fix test for overflowing physaddr - forgot to commit before pushing --- src/addr.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/addr.rs b/src/addr.rs index c30ffc25..189d4014 100644 --- a/src/addr.rs +++ b/src/addr.rs @@ -672,7 +672,7 @@ mod tests { #[test] #[should_panic] pub fn add_overflow_physaddr() { - let _ = PhysAddr::new(0x000f_ffff_ffff_ffff) + 1; + let _ = PhysAddr::new(0x000f_ffff_ffff_ffff) + 0xffff_0000_0000_0000; } #[test] From c47d9706ab0067b02963fe6155bb7c6e5cb0b912 Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Tue, 11 Feb 2025 22:28:57 +0100 Subject: [PATCH 19/24] don't pass -Crelocation-model=static to host targets If --target is set any flags from RUSTFLAGS only affect that target. --- .github/workflows/build.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index bc6a2b0b..7e21e803 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -89,9 +89,6 @@ jobs: runs-on: ${{ matrix.platform }} timeout-minutes: 15 - env: - RUSTFLAGS: -Crelocation-model=static -Dwarnings - steps: - name: "Checkout Repository" uses: actions/checkout@v4 @@ -137,9 +134,11 @@ jobs: run: qemu-system-x86_64 --version - name: "Run Test Framework" - run: cargo test + run: cargo test --target x86_64-unknown-none shell: bash working-directory: "testing" + env: + RUSTFLAGS: -Crelocation-model=static -Dwarnings check_formatting: name: "Check Formatting" From 35643d205f1496226f11905be28c24e897268f93 Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Sat, 26 Apr 2025 08:12:25 +0200 Subject: [PATCH 20/24] improve panic messages for arithmetic operations Previously, the error message didn't make it immediately obvious what went wrong. Let's be a bit more explicit. Note that "attempt to add/subtract with overflow" is the panic message used by Rust for normal arithmetic operations. --- src/addr.rs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/addr.rs b/src/addr.rs index 189d4014..54502554 100644 --- a/src/addr.rs +++ b/src/addr.rs @@ -366,7 +366,12 @@ impl Add for VirtAddr { type Output = Self; #[inline] fn add(self, rhs: u64) -> Self::Output { - VirtAddr::new(self.0.checked_add(rhs).unwrap()) + VirtAddr::try_new( + self.0 + .checked_add(rhs) + .expect("attempt to add with overflow"), + ) + .expect("attempt to add resulted in non-canonical virtual address") } } @@ -381,7 +386,12 @@ impl Sub for VirtAddr { type Output = Self; #[inline] fn sub(self, rhs: u64) -> Self::Output { - VirtAddr::new(self.0.checked_sub(rhs).unwrap()) + VirtAddr::try_new( + self.0 + .checked_sub(rhs) + .expect("attempt to subtract with overflow"), + ) + .expect("attempt to subtract resulted in non-canonical virtual address") } } @@ -396,7 +406,9 @@ impl Sub for VirtAddr { type Output = u64; #[inline] fn sub(self, rhs: VirtAddr) -> Self::Output { - self.as_u64().checked_sub(rhs.as_u64()).unwrap() + self.as_u64() + .checked_sub(rhs.as_u64()) + .expect("attempt to subtract with overflow") } } From 0de692983963d441cb2673e482ebaa3087eb0d65 Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Sat, 26 Apr 2025 08:25:03 +0200 Subject: [PATCH 21/24] improve docs for Add/Sub ops for VirtAddr Document when the functions panic and mention Step as an alternative for those looking for a successor/predecessor operation. --- src/addr.rs | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/src/addr.rs b/src/addr.rs index 54502554..e40c0c0b 100644 --- a/src/addr.rs +++ b/src/addr.rs @@ -364,6 +364,18 @@ impl fmt::Pointer for VirtAddr { impl Add for VirtAddr { type Output = Self; + + #[cfg_attr(not(feature = "step_trait"), allow(rustdoc::broken_intra_doc_links))] + /// Add an offset to a virtual address. + /// + /// This function performs normal arithmetic addition and doesn't jump the + /// address gap. If you're looking for a successor operation that jumps the + /// address gap, use [`Step::forward`]. + /// + /// # Panics + /// + /// This function will panic on overflow or if the result is not a + /// canonical address. #[inline] fn add(self, rhs: u64) -> Self::Output { VirtAddr::try_new( @@ -376,6 +388,17 @@ impl Add for VirtAddr { } impl AddAssign for VirtAddr { + #[cfg_attr(not(feature = "step_trait"), allow(rustdoc::broken_intra_doc_links))] + /// Add an offset to a virtual address. + /// + /// This function performs normal arithmetic addition and doesn't jump the + /// address gap. If you're looking for a successor operation that jumps the + /// address gap, use [`Step::forward`]. + /// + /// # Panics + /// + /// This function will panic on overflow or if the result is not a + /// canonical address. #[inline] fn add_assign(&mut self, rhs: u64) { *self = *self + rhs; @@ -384,6 +407,18 @@ impl AddAssign for VirtAddr { impl Sub for VirtAddr { type Output = Self; + + #[cfg_attr(not(feature = "step_trait"), allow(rustdoc::broken_intra_doc_links))] + /// Subtract an offset from a virtual address. + /// + /// This function performs normal arithmetic subtraction and doesn't jump + /// the address gap. If you're looking for a predecessor operation that + /// jumps the address gap, use [`Step::backward`]. + /// + /// # Panics + /// + /// This function will panic on overflow or if the result is not a + /// canonical address. #[inline] fn sub(self, rhs: u64) -> Self::Output { VirtAddr::try_new( @@ -396,6 +431,17 @@ impl Sub for VirtAddr { } impl SubAssign for VirtAddr { + #[cfg_attr(not(feature = "step_trait"), allow(rustdoc::broken_intra_doc_links))] + /// Subtract an offset from a virtual address. + /// + /// This function performs normal arithmetic subtraction and doesn't jump + /// the address gap. If you're looking for a predecessor operation that + /// jumps the address gap, use [`Step::backward`]. + /// + /// # Panics + /// + /// This function will panic on overflow or if the result is not a + /// canonical address. #[inline] fn sub_assign(&mut self, rhs: u64) { *self = *self - rhs; @@ -404,6 +450,12 @@ impl SubAssign for VirtAddr { impl Sub for VirtAddr { type Output = u64; + + /// Returns the difference between two addresses. + /// + /// # Panics + /// + /// This function will panic on overflow. #[inline] fn sub(self, rhs: VirtAddr) -> Self::Output { self.as_u64() From 9923b27cb085456bd3fd40c2382b9e45ba48b1cd Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Sat, 26 Apr 2025 08:38:27 +0200 Subject: [PATCH 22/24] fix kani build job Ubuntu 20.04 has been retired. --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7e21e803..cbbd0920 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -176,7 +176,7 @@ jobs: run: cargo +stable semver-checks check-release kani: - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: Swatinem/rust-cache@v2 From cf725dbaa22aeb5ea6dac1d590424dc674661e32 Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Sat, 26 Apr 2025 08:48:05 +0200 Subject: [PATCH 23/24] Revert "Merge pull request #529 from adavis628/master" This reverts commit ffe34b09416bdd7e8d5b95b0b3b76d7ca511af18, reversing changes made to a675195ad03b065a7284d34a1fc9bf8478093f89. --- src/registers/model_specific.rs | 89 ------------------- .../paging/mapper/mapped_page_table.rs | 12 +-- .../paging/mapper/recursive_page_table.rs | 45 +++------- src/structures/paging/page_table.rs | 29 +++--- 4 files changed, 26 insertions(+), 149 deletions(-) diff --git a/src/registers/model_specific.rs b/src/registers/model_specific.rs index f8dbb71f..b9818b89 100644 --- a/src/registers/model_specific.rs +++ b/src/registers/model_specific.rs @@ -67,10 +67,6 @@ pub struct UCet; #[derive(Debug)] pub struct SCet; -/// IA32_PAT: Page Attribute Table. -#[derive(Debug)] -pub struct Pat; - /// IA32_APIC_BASE: status and location of the local APIC /// /// IA32_APIC_BASE must be supported on the CPU, otherwise, a general protection exception will occur. Support can be detected using the `cpuid` instruction. @@ -122,22 +118,6 @@ impl SCet { pub const MSR: Msr = Msr(0x6A2); } -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, - ]; -} - impl ApicBase { /// The underlying model specific register. pub const MSR: Msr = Msr(0x1B); @@ -192,43 +172,6 @@ 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 - } -} - bitflags! { /// Flags for the Advanced Programmable Interrupt Controler Base Register. #[repr(transparent)] @@ -724,38 +667,6 @@ 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); - } - } - } - impl ApicBase { /// Reads the IA32_APIC_BASE MSR. #[inline] diff --git a/src/structures/paging/mapper/mapped_page_table.rs b/src/structures/paging/mapper/mapped_page_table.rs index dfa3602e..b50f072e 100644 --- a/src/structures/paging/mapper/mapped_page_table.rs +++ b/src/structures/paging/mapper/mapped_page_table.rs @@ -421,8 +421,7 @@ impl Mapper for MappedPageTable<'_, P> { let frame = p1_entry.frame().map_err(|err| match err { FrameError::FrameNotPresent => UnmapError::PageNotMapped, - #[allow(deprecated)] - FrameError::HugeFrame => unreachable!(), + FrameError::HugeFrame => UnmapError::ParentEntryHugePage, })?; p1_entry.set_unused(); @@ -712,9 +711,6 @@ impl PageTableWalker

{ &self, entry: &'b PageTableEntry, ) -> Result<&'b PageTable, PageTableWalkError> { - if entry.flags().contains(PageTableFlags::HUGE_PAGE) { - return Err(PageTableWalkError::MappedToHugePage); - } let page_table_ptr = self .page_table_frame_mapping .frame_to_pointer(entry.frame()?); @@ -733,9 +729,6 @@ impl PageTableWalker

{ &self, entry: &'b mut PageTableEntry, ) -> Result<&'b mut PageTable, PageTableWalkError> { - if entry.flags().contains(PageTableFlags::HUGE_PAGE) { - return Err(PageTableWalkError::MappedToHugePage); - } let page_table_ptr = self .page_table_frame_mapping .frame_to_pointer(entry.frame()?); @@ -839,8 +832,7 @@ impl From for PageTableWalkError { #[inline] fn from(err: FrameError) -> Self { match err { - #[allow(deprecated)] - FrameError::HugeFrame => unreachable!(), + FrameError::HugeFrame => PageTableWalkError::MappedToHugePage, FrameError::FrameNotPresent => PageTableWalkError::NotMapped, } } diff --git a/src/structures/paging/mapper/recursive_page_table.rs b/src/structures/paging/mapper/recursive_page_table.rs index 3906a1a5..ff427ffa 100644 --- a/src/structures/paging/mapper/recursive_page_table.rs +++ b/src/structures/paging/mapper/recursive_page_table.rs @@ -322,13 +322,9 @@ impl Mapper for RecursivePageTable<'_> { let p4 = &mut self.p4; let p4_entry = &p4[page.p4_index()]; - if p4_entry.flags().contains(PageTableFlags::HUGE_PAGE) { - return Err(UnmapError::ParentEntryHugePage); - } p4_entry.frame().map_err(|err| match err { FrameError::FrameNotPresent => UnmapError::PageNotMapped, - #[allow(deprecated)] - FrameError::HugeFrame => unreachable!(), + FrameError::HugeFrame => UnmapError::ParentEntryHugePage, })?; let p3 = unsafe { &mut *(p3_ptr(page, self.recursive_index)) }; @@ -445,24 +441,16 @@ impl Mapper for RecursivePageTable<'_> { ) -> Result<(PhysFrame, MapperFlush), UnmapError> { let p4 = &mut self.p4; let p4_entry = &p4[page.p4_index()]; - if p4_entry.flags().contains(PageTableFlags::HUGE_PAGE) { - return Err(UnmapError::ParentEntryHugePage); - } p4_entry.frame().map_err(|err| match err { FrameError::FrameNotPresent => UnmapError::PageNotMapped, - #[allow(deprecated)] - FrameError::HugeFrame => unreachable!(), + FrameError::HugeFrame => UnmapError::ParentEntryHugePage, })?; let p3 = unsafe { &mut *(p3_ptr(page, self.recursive_index)) }; let p3_entry = &p3[page.p3_index()]; - if p3_entry.flags().contains(PageTableFlags::HUGE_PAGE) { - return Err(UnmapError::ParentEntryHugePage); - } p3_entry.frame().map_err(|err| match err { FrameError::FrameNotPresent => UnmapError::PageNotMapped, - #[allow(deprecated)] - FrameError::HugeFrame => unreachable!(), + FrameError::HugeFrame => UnmapError::ParentEntryHugePage, })?; let p2 = unsafe { &mut *(p2_ptr(page, self.recursive_index)) }; @@ -608,35 +596,23 @@ impl Mapper for RecursivePageTable<'_> { ) -> Result<(PhysFrame, MapperFlush), UnmapError> { let p4 = &mut self.p4; let p4_entry = &p4[page.p4_index()]; - if p4_entry.flags().contains(PageTableFlags::HUGE_PAGE) { - return Err(UnmapError::ParentEntryHugePage); - } p4_entry.frame().map_err(|err| match err { FrameError::FrameNotPresent => UnmapError::PageNotMapped, - #[allow(deprecated)] - FrameError::HugeFrame => unreachable!(), + FrameError::HugeFrame => UnmapError::ParentEntryHugePage, })?; let p3 = unsafe { &mut *(p3_ptr(page, self.recursive_index)) }; let p3_entry = &p3[page.p3_index()]; - if p3_entry.flags().contains(PageTableFlags::HUGE_PAGE) { - return Err(UnmapError::ParentEntryHugePage); - } p3_entry.frame().map_err(|err| match err { FrameError::FrameNotPresent => UnmapError::PageNotMapped, - #[allow(deprecated)] - FrameError::HugeFrame => unreachable!(), + FrameError::HugeFrame => UnmapError::ParentEntryHugePage, })?; let p2 = unsafe { &mut *(p2_ptr(page, self.recursive_index)) }; let p2_entry = &p2[page.p2_index()]; - if p2_entry.flags().contains(PageTableFlags::HUGE_PAGE) { - return Err(UnmapError::ParentEntryHugePage); - } p2_entry.frame().map_err(|err| match err { FrameError::FrameNotPresent => UnmapError::PageNotMapped, - #[allow(deprecated)] - FrameError::HugeFrame => unreachable!(), + FrameError::HugeFrame => UnmapError::ParentEntryHugePage, })?; let p1 = unsafe { &mut *(p1_ptr(page, self.recursive_index)) }; @@ -644,8 +620,7 @@ impl Mapper for RecursivePageTable<'_> { let frame = p1_entry.frame().map_err(|err| match err { FrameError::FrameNotPresent => UnmapError::PageNotMapped, - #[allow(deprecated)] - FrameError::HugeFrame => unreachable!(), + FrameError::HugeFrame => UnmapError::ParentEntryHugePage, })?; p1_entry.set_unused(); @@ -843,6 +818,9 @@ 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, @@ -912,9 +890,6 @@ impl CleanUp for RecursivePageTable<'_> { !(level == PageTableLevel::Four && *i == recursive_index.into()) }) { - if entry.flags().contains(PageTableFlags::HUGE_PAGE) { - continue; - } if let Ok(frame) = entry.frame() { let start = VirtAddr::forward_checked_impl( table_addr, diff --git a/src/structures/paging/page_table.rs b/src/structures/paging/page_table.rs index 6ee1a8dc..e9069bcc 100644 --- a/src/structures/paging/page_table.rs +++ b/src/structures/paging/page_table.rs @@ -15,8 +15,8 @@ use bitflags::bitflags; pub enum FrameError { /// The entry does not have the `PRESENT` flag set, so it isn't currently mapped to a frame. FrameNotPresent, - #[deprecated = "`HugeFrame` is no longer returned by the `frame` method"] - /// The entry does have the `HUGE_PAGE` flag set. + /// The entry does have the `HUGE_PAGE` flag set. The `frame` method has a standard 4KiB frame + /// as return type, so a huge frame can't be returned. HugeFrame, } @@ -63,12 +63,16 @@ impl PageTableEntry { /// 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) #[inline] pub fn frame(&self) -> Result { - if self.flags().contains(PageTableFlags::PRESENT) { - Ok(PhysFrame::containing_address(self.addr())) - } else { + if !self.flags().contains(PageTableFlags::PRESENT) { Err(FrameError::FrameNotPresent) + } else if self.flags().contains(PageTableFlags::HUGE_PAGE) { + Err(FrameError::HugeFrame) + } else { + Ok(PhysFrame::containing_address(self.addr())) } } @@ -82,6 +86,7 @@ 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) } @@ -123,21 +128,17 @@ 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. This referred to as the page-level write-through (PWT) bit. + /// policy is used. const WRITE_THROUGH = 1 << 3; - /// Disables caching for the pointed entry if it is cacheable. This referred to as the - /// page-level cache disable (PCD) bit. + /// Disables caching for the pointed entry is cacheable. 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. This is the same bit - /// as `PAT_4KIB_PAGE`. + /// Specifies that the entry maps a huge frame instead of a page table. Only allowed in + /// P2 or P3 tables. 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; @@ -147,8 +148,6 @@ 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. From bc8c991d7769cf6edacae95f7f9b24e24fbe402a Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Sat, 26 Apr 2025 14:02:49 +0200 Subject: [PATCH 24/24] fix warning --- src/structures/paging/page_table.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/structures/paging/page_table.rs b/src/structures/paging/page_table.rs index e9069bcc..baf7b62f 100644 --- a/src/structures/paging/page_table.rs +++ b/src/structures/paging/page_table.rs @@ -64,7 +64,7 @@ impl PageTableEntry { /// /// - `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) #[inline] pub fn frame(&self) -> Result { if !self.flags().contains(PageTableFlags::PRESENT) {