Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
13 changes: 13 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
@@ -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).
Expand Down
8 changes: 8 additions & 0 deletions src/addr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
Expand Down
2 changes: 2 additions & 0 deletions src/instructions/random.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ impl RdRand {
}
}
}

/// Uniformly sampled u32.
/// May fail in rare circumstances or heavy load.
#[inline]
Expand All @@ -49,6 +50,7 @@ impl RdRand {
}
}
}

/// Uniformly sampled u16.
/// May fail in rare circumstances or heavy load.
#[inline]
Expand Down
2 changes: 1 addition & 1 deletion src/instructions/tlb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<S>::steps_between_impl(&pages.start, &pages.end).0;
Expand Down
109 changes: 106 additions & 3 deletions src/registers/model_specific.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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)]
Expand Down Expand Up @@ -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<Self> {
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::*;
Expand Down Expand Up @@ -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()) };
}
Expand All @@ -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()) };
}
Expand All @@ -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()) };
}
Expand Down Expand Up @@ -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);
}
}
}
}
23 changes: 3 additions & 20 deletions src/registers/xcontrol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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.
Expand Down Expand Up @@ -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);
}
}

Expand Down
3 changes: 1 addition & 2 deletions src/structures/idt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<PageFaultHandlerFunc>,
Expand Down
12 changes: 7 additions & 5 deletions src/structures/paging/frame.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<S: PageSize = Size4KiB> {
// TODO: Make private when our minimum supported stable Rust version is 1.61
pub(crate) start_address: PhysAddr,
Expand Down Expand Up @@ -225,8 +229,7 @@ impl<S: PageSize> Sub<PhysFrame<S>> for PhysFrame<S> {
}

/// 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<S: PageSize = Size4KiB> {
/// The start of the range, inclusive.
pub start: PhysFrame<S>,
Expand Down Expand Up @@ -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<S: PageSize = Size4KiB> {
/// The start of the range, inclusive.
pub start: PhysFrame<S>,
Expand Down
Loading