From 73ebafc9568c60e78aa8ce78241903989fe557ab Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Sat, 12 Sep 2026 10:28:33 +0200 Subject: [PATCH] make memory encryption bit an upper limit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ensure that no bit above the C-bit/S-bit is set: - TDX already requires this: "GPA bits higher than the SHARED bit are considered reserved and must be 0. Address translation with any of the reserved bits set to 1 cause a #PF with PFEC (Page Fault Error Code) RSVD bit set." (Architecture Specification: Intel® Trust Domain Extensions (Intel® TDX) Module, 13.11.1. GPAW-Relate EPT Violations). - AMD CPUs don't have the same restriction, however in practice, the C-bit is always the upmost supported physical adddress bit. Client parts and server parts up until Rome support 48 physical address bits and use bit 47 as the C-bit. Server parts starting with Milan support 52 physical address bits and always use bit 51 as the C-bit. Thinking about this, this makes sense: If the CPU allowed bits higher than the C-bit to be used for address bits, this would fragment the physical address space and make large parts of it effectively unusable. --- Changelog.md | 1 + src/addr.rs | 7 ++++--- src/structures/mem_encrypt.rs | 25 +++++++++++++++++++++---- src/structures/paging/page_table.rs | 12 +++++------- 4 files changed, 31 insertions(+), 14 deletions(-) diff --git a/Changelog.md b/Changelog.md index 1daa76f8..773a6a19 100644 --- a/Changelog.md +++ b/Changelog.md @@ -13,6 +13,7 @@ - [add `MappedPageTable::display`](https://github.com/rust-osdev/x86_64/pull/574) - The mappings of a `MappedPageTable` can now be displayed. - [Increase the Minimum Supported Rust Version to 1.98](https://github.com/rust-osdev/x86_64/pull/604) +- [make memory encryption bit an upper limit for physical address bits](https://github.com/rust-osdev/x86_64/pull/603) # 0.15.5 – 2026-07-11 diff --git a/src/addr.rs b/src/addr.rs index f5cdada4..78777420 100644 --- a/src/addr.rs +++ b/src/addr.rs @@ -9,7 +9,7 @@ use core::ops::{Add, AddAssign, Sub, SubAssign}; use core::sync::atomic::Ordering; #[cfg(feature = "memory_encryption")] -use crate::structures::mem_encrypt::ENC_BIT_MASK; +use crate::structures::mem_encrypt::PHYSICAL_ADDRESS_MASK; use crate::structures::paging::page_table::PageTableLevel; use crate::structures::paging::{PageOffset, PageTableIndex}; @@ -571,14 +571,15 @@ impl PhysAddr { #[cfg(feature = "memory_encryption")] #[inline] pub fn new_truncate(addr: u64) -> PhysAddr { - PhysAddr((addr % (1 << 52)) & !ENC_BIT_MASK.load(Ordering::Relaxed)) + PhysAddr(addr & PHYSICAL_ADDRESS_MASK.load(Ordering::Relaxed)) } /// Creates a new physical address, without any checks. /// /// ## Safety /// - /// You must make sure bits 52..64 are zero. This is not checked. + /// You must make sure bits 52..64 are zero and that no bits at or above + /// the encryption bit (if one is configured) are set. This is not checked. #[inline] pub const unsafe fn new_unsafe(addr: u64) -> PhysAddr { PhysAddr(addr) diff --git a/src/structures/mem_encrypt.rs b/src/structures/mem_encrypt.rs index 16ee9b09..b078ba77 100644 --- a/src/structures/mem_encrypt.rs +++ b/src/structures/mem_encrypt.rs @@ -8,15 +8,21 @@ use core::sync::atomic::{AtomicBool, AtomicU64, Ordering}; use crate::structures::paging::PageTableFlags; -use crate::structures::paging::page_table::PHYSICAL_ADDRESS_MASK; /// Position of the encryption (C/S) bit in the physical address +/// +/// If a memory encryption configuration has been set up, this contains a bit +/// mask with just that bit set. pub(crate) static ENC_BIT_MASK: AtomicU64 = AtomicU64::new(0); /// Is the encryption bit reversed (i.e. its presence denote that the page is _decrypted_ rather /// than encrypted) static ENC_BIT_REVERSED: AtomicBool = AtomicBool::new(false); +/// The mask of valid physical address bits. +#[cfg(feature = "memory_encryption")] +pub(crate) static PHYSICAL_ADDRESS_MASK: AtomicU64 = AtomicU64::new(0x000f_ffff_ffff_ffffu64); + /// Defines the configuration for memory encryption #[derive(Debug)] pub enum MemoryEncryptionConfiguration { @@ -34,22 +40,33 @@ pub enum MemoryEncryptionConfiguration { } /// Enable memory encryption by defining the physical address bit that is used to mark a page -/// encrypted (or shared) in a page table entry +/// encrypted (or shared) in a page table entry. +/// +/// Once memory encryption has been enabled [`PhysAddr::new`](crate::addr::PhysAddr::new) will not +/// allow any bits at or above the encryption bit to be set. /// /// # Safety +/// /// Caller must make sure that any existing page table entry is discarded or adapted to take this /// bit into consideration. +/// +/// The caller must ensure that there are no [`PhysAddr`](crate::addr::PhysAddr) instances that +/// have the encryption bit or any bits above it set. +/// /// The configuration provided by caller must be correct, otherwise physical address bits will /// incorrectly be considered as page table flags. +/// +/// This function may only be called once. pub unsafe fn enable_memory_encryption(configuration: MemoryEncryptionConfiguration) { let (bit_position, reversed) = match configuration { MemoryEncryptionConfiguration::EncryptedBit(pos) => (pos, false), MemoryEncryptionConfiguration::SharedBit(pos) => (pos, true), }; - let c_bit_mask = 1u64 << bit_position; + let addr_mask = u64::MAX << bit_position; + PHYSICAL_ADDRESS_MASK.fetch_and(!addr_mask, Ordering::Relaxed); - PHYSICAL_ADDRESS_MASK.fetch_and(!c_bit_mask, Ordering::Relaxed); + let c_bit_mask = 1u64 << bit_position; ENC_BIT_MASK.store(c_bit_mask, Ordering::Relaxed); ENC_BIT_REVERSED.store(reversed, Ordering::Release); } diff --git a/src/structures/paging/page_table.rs b/src/structures/paging/page_table.rs index 1febcb6b..38bec7b3 100644 --- a/src/structures/paging/page_table.rs +++ b/src/structures/paging/page_table.rs @@ -5,10 +5,12 @@ use core::fmt; use core::iter::Step; use core::ops::{Index, IndexMut}; #[cfg(feature = "memory_encryption")] -use core::sync::atomic::{AtomicU64, Ordering}; +use core::sync::atomic::Ordering; use super::{PageSize, PhysFrame, Size4KiB}; use crate::addr::PhysAddr; +#[cfg(feature = "memory_encryption")] +use crate::structures::mem_encrypt::PHYSICAL_ADDRESS_MASK; use bitflags::bitflags; use dep_const_fn::const_fn; @@ -23,10 +25,6 @@ pub enum FrameError { HugeFrame, } -/// The mask used to remove flags from a page table entry to obtain the physical address -#[cfg(feature = "memory_encryption")] -pub(crate) static PHYSICAL_ADDRESS_MASK: AtomicU64 = AtomicU64::new(0x000f_ffff_ffff_f000u64); - /// A 64-bit page table entry. #[derive(Clone)] #[repr(transparent)] @@ -109,13 +107,13 @@ impl PageTableEntry { #[inline(always)] #[cfg(not(feature = "memory_encryption"))] const fn physical_address_mask() -> u64 { - 0x000f_ffff_ffff_f000u64 + 0x000f_ffff_ffff_f000 } #[inline(always)] #[cfg(feature = "memory_encryption")] fn physical_address_mask() -> u64 { - PHYSICAL_ADDRESS_MASK.load(Ordering::Relaxed) + PHYSICAL_ADDRESS_MASK.load(Ordering::Relaxed) & !0xfff } }