Skip to content

Commit cbd39d6

Browse files
authored
feat(sev): add AMD SEV support (#542)
* feat(sev): add AMD SEV support * x86_64/sev: move SEV structures to codebase x86_64/sev: remove SEV detection, replace with a simpler c-bit defining routine * x86_64/memory_encryption: don't enable ME by default * x86_64/memory_encryption: fix review and clippy issues * memory_encryption: refactor for Intel TDX support * memory_encryption: enable memory_encryption flag in CI * memory_encryption: reduce function duplication in page_table.rs * memory_encryption: remove feature `dynamic_flags` * memory_encryption: prevent setting the encryption bit in PhysAddr * memory_encryption: missing documentation * ci: test memory_encryption separately * memory_encryption: address PR feedback * memory_encryption: keep `const` functions const if `memory_encryption` is not enabled * memory_encryption: exclude memory_encryption feature when checking for semver * memory_encryption: fix `const_fn` name already taken * memory_encryption: fix formatting * memory_encryption: review fixes * memory_encryption: review fixes + fix imports changes * memory_encryption: fix import style * ci: only run semver checks on default features
1 parent 284bdca commit cbd39d6

File tree

6 files changed

+137
-3
lines changed

6 files changed

+137
-3
lines changed

.github/workflows/build.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,13 @@ jobs:
3535
with:
3636
toolchain: ${{ matrix.rust }}
3737
- run: cargo build --no-default-features --features instructions
38+
- run: cargo build --no-default-features --features memory_encryption
3839
- run: cargo build --no-default-features
3940
- run: cargo doc --no-default-features --features instructions
41+
- run: cargo doc --no-default-features --features memory_encryption
4042
- run: cargo doc --no-default-features
4143
- run: cargo test --no-default-features --features instructions
44+
- run: cargo test --no-default-features --features memory_encryption
4245
- run: cargo test --no-default-features
4346

4447
test:
@@ -173,7 +176,7 @@ jobs:
173176
cache-targets: false
174177
- run: cargo install cargo-semver-checks --locked
175178
- name: Check semver
176-
run: cargo +stable semver-checks check-release
179+
run: cargo +stable semver-checks check-release --default-features
177180

178181
kani:
179182
runs-on: ubuntu-latest

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,12 @@ bit_field = "0.10.1"
2424
bitflags = "2.3.2"
2525
volatile = "0.4.4"
2626
rustversion = "1.0.5"
27+
dep_const_fn = { package = "const_fn", version = "0.4.11" }
2728

2829
[features]
2930
default = ["nightly", "instructions"]
3031
instructions = []
32+
memory_encryption = []
3133
nightly = ["const_fn", "step_trait", "abi_x86_interrupt", "asm_const"]
3234
abi_x86_interrupt = []
3335
# deprecated, no longer needed

src/addr.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,16 @@ use core::fmt;
55
#[cfg(feature = "step_trait")]
66
use core::iter::Step;
77
use core::ops::{Add, AddAssign, Sub, SubAssign};
8+
#[cfg(feature = "memory_encryption")]
9+
use core::sync::atomic::Ordering;
810

11+
#[cfg(feature = "memory_encryption")]
12+
use crate::structures::mem_encrypt::ENC_BIT_MASK;
913
use crate::structures::paging::page_table::PageTableLevel;
1014
use crate::structures::paging::{PageOffset, PageTableIndex};
15+
1116
use bit_field::BitField;
17+
use dep_const_fn::const_fn;
1218

1319
const ADDRESS_SPACE_SIZE: u64 = 0x1_0000_0000_0000;
1420

@@ -503,7 +509,10 @@ impl PhysAddr {
503509
/// ## Panics
504510
///
505511
/// This function panics if a bit in the range 52 to 64 is set.
512+
// If the `memory_encryption` feature has been enabled and an encryption bit has been
513+
// configured, this also panics if the encryption bit is manually set in the address.
506514
#[inline]
515+
#[const_fn(cfg(not(feature = "memory_encryption")))]
507516
pub const fn new(addr: u64) -> Self {
508517
// TODO: Replace with .ok().expect(msg) when that works on stable.
509518
match Self::try_new(addr) {
@@ -513,11 +522,19 @@ impl PhysAddr {
513522
}
514523

515524
/// Creates a new physical address, throwing bits 52..64 away.
525+
#[cfg(not(feature = "memory_encryption"))]
516526
#[inline]
517527
pub const fn new_truncate(addr: u64) -> PhysAddr {
518528
PhysAddr(addr % (1 << 52))
519529
}
520530

531+
/// Creates a new physical address, throwing bits 52..64 and the encryption bit away.
532+
#[cfg(feature = "memory_encryption")]
533+
#[inline]
534+
pub fn new_truncate(addr: u64) -> PhysAddr {
535+
PhysAddr((addr % (1 << 52)) & !ENC_BIT_MASK.load(Ordering::Relaxed))
536+
}
537+
521538
/// Creates a new physical address, without any checks.
522539
///
523540
/// ## Safety
@@ -531,7 +548,10 @@ impl PhysAddr {
531548
/// Tries to create a new physical address.
532549
///
533550
/// Fails if any bits in the range 52 to 64 are set.
551+
/// If the `memory_encryption` feature has been enabled and an encryption bit has been
552+
/// configured, this also fails if the encryption bit is manually set in the address.
534553
#[inline]
554+
#[const_fn(cfg(not(feature = "memory_encryption")))]
535555
pub const fn try_new(addr: u64) -> Result<Self, PhysAddrNotValid> {
536556
let p = Self::new_truncate(addr);
537557
if p.0 == addr {

src/structures/mem_encrypt.rs

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
//! Provides glue to support memory encryption features of some CPUs.
2+
//!
3+
//! Memory encryption typically relies on using a physical address bit in the page table entry to
4+
//! mark a page as encrypted/decrypted. This module provides a function that, given the position and
5+
//! type of that physical address bit, updates the dynamic page-table flags structure to ensure that
6+
//! the flag is treated properly and not returned as part of the physical address.
7+
8+
use core::sync::atomic::{AtomicBool, AtomicU64, Ordering};
9+
10+
use crate::structures::paging::page_table::PHYSICAL_ADDRESS_MASK;
11+
use crate::structures::paging::PageTableFlags;
12+
13+
/// Position of the encryption (C/S) bit in the physical address
14+
pub(crate) static ENC_BIT_MASK: AtomicU64 = AtomicU64::new(0);
15+
16+
/// Is the encryption bit reversed (i.e. its presence denote that the page is _decrypted_ rather
17+
/// than encrypted)
18+
static ENC_BIT_REVERSED: AtomicBool = AtomicBool::new(false);
19+
20+
/// Defines the configuration for memory encryption
21+
#[derive(Debug)]
22+
pub enum MemoryEncryptionConfiguration {
23+
/// Defines that a memory page should be accessed encrypted if this bit of its physical address
24+
/// is set in the page table entry.
25+
///
26+
/// Use this for AMD Secure Memory Encryption (AMD-SME) and Secure Encrypted Virtualization (SEV)
27+
EncryptedBit(u8),
28+
29+
/// Defines that a memory page should be accessed decrypted if this bit of its physical address
30+
/// is set in the page table entry.
31+
///
32+
/// Use this for Intel Trust Domain Extension (Intel TDX)
33+
SharedBit(u8),
34+
}
35+
36+
/// Enable memory encryption by defining the physical address bit that is used to mark a page
37+
/// encrypted (or shared) in a page table entry
38+
///
39+
/// # Safety
40+
/// Caller must make sure that any existing page table entry is discarded or adapted to take this
41+
/// bit into consideration.
42+
/// The configuration provided by caller must be correct, otherwise physical address bits will
43+
/// incorrectly be considered as page table flags.
44+
pub unsafe fn enable_memory_encryption(configuration: MemoryEncryptionConfiguration) {
45+
let (bit_position, reversed) = match configuration {
46+
MemoryEncryptionConfiguration::EncryptedBit(pos) => (pos, false),
47+
MemoryEncryptionConfiguration::SharedBit(pos) => (pos, true),
48+
};
49+
50+
let c_bit_mask = 1u64 << bit_position;
51+
52+
PHYSICAL_ADDRESS_MASK.fetch_and(!c_bit_mask, Ordering::Relaxed);
53+
ENC_BIT_MASK.store(c_bit_mask, Ordering::Relaxed);
54+
ENC_BIT_REVERSED.store(reversed, Ordering::Release);
55+
}
56+
57+
impl PageTableFlags {
58+
#[inline]
59+
fn enc_bit_flag() -> Option<PageTableFlags> {
60+
let bit_mask = ENC_BIT_MASK.load(Ordering::Relaxed);
61+
62+
if bit_mask > 0 {
63+
Some(PageTableFlags::from_bits_retain(bit_mask))
64+
} else {
65+
None
66+
}
67+
}
68+
69+
/// Marks the page for encryption in the page table entry.
70+
///
71+
/// # Panics
72+
///
73+
/// Panics if memory encryption has not been previously configured by calling [`enable_memory_encryption`]
74+
pub fn set_encrypted(&mut self, encrypted: bool) {
75+
let flag = Self::enc_bit_flag().expect("memory encryption is not enabled");
76+
self.set(flag, encrypted ^ ENC_BIT_REVERSED.load(Ordering::Relaxed));
77+
}
78+
79+
/// Checks if memory encryption is enabled on the page
80+
pub fn is_encrypted(&self) -> bool {
81+
if let Some(c_bit_flag) = Self::enc_bit_flag() {
82+
self.contains(c_bit_flag) ^ ENC_BIT_REVERSED.load(Ordering::Relaxed)
83+
} else {
84+
false
85+
}
86+
}
87+
}

src/structures/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ pub mod gdt;
66

77
pub mod idt;
88

9+
#[cfg(feature = "memory_encryption")]
10+
pub mod mem_encrypt;
911
pub mod paging;
1012
pub mod port;
1113
pub mod tss;

src/structures/paging/page_table.rs

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,14 @@ use core::fmt;
44
#[cfg(feature = "step_trait")]
55
use core::iter::Step;
66
use core::ops::{Index, IndexMut};
7+
#[cfg(feature = "memory_encryption")]
8+
use core::sync::atomic::{AtomicU64, Ordering};
79

810
use super::{PageSize, PhysFrame, Size4KiB};
911
use crate::addr::PhysAddr;
1012

1113
use bitflags::bitflags;
14+
use dep_const_fn::const_fn;
1215

1316
/// The error returned by the `PageTableEntry::frame` method.
1417
#[derive(Debug, Clone, Copy, PartialEq)]
@@ -20,6 +23,10 @@ pub enum FrameError {
2023
HugeFrame,
2124
}
2225

26+
/// The mask used to remove flags from a page table entry to obtain the physical address
27+
#[cfg(feature = "memory_encryption")]
28+
pub(crate) static PHYSICAL_ADDRESS_MASK: AtomicU64 = AtomicU64::new(0x000f_ffff_ffff_f000u64);
29+
2330
/// A 64-bit page table entry.
2431
#[derive(Clone)]
2532
#[repr(transparent)]
@@ -48,14 +55,15 @@ impl PageTableEntry {
4855

4956
/// Returns the flags of this entry.
5057
#[inline]
58+
#[const_fn(cfg(not(feature = "memory_encryption")))]
5159
pub const fn flags(&self) -> PageTableFlags {
52-
PageTableFlags::from_bits_truncate(self.entry)
60+
PageTableFlags::from_bits_retain(self.entry & !Self::physical_address_mask())
5361
}
5462

5563
/// Returns the physical address mapped by this entry, might be zero.
5664
#[inline]
5765
pub fn addr(&self) -> PhysAddr {
58-
PhysAddr::new(self.entry & 0x000f_ffff_ffff_f000)
66+
PhysAddr::new(self.entry & Self::physical_address_mask())
5967
}
6068

6169
/// Returns the physical frame mapped by this entry.
@@ -95,6 +103,18 @@ impl PageTableEntry {
95103
pub fn set_flags(&mut self, flags: PageTableFlags) {
96104
self.entry = self.addr().as_u64() | flags.bits();
97105
}
106+
107+
#[inline(always)]
108+
#[cfg(not(feature = "memory_encryption"))]
109+
const fn physical_address_mask() -> u64 {
110+
0x000f_ffff_ffff_f000u64
111+
}
112+
113+
#[inline(always)]
114+
#[cfg(feature = "memory_encryption")]
115+
fn physical_address_mask() -> u64 {
116+
PHYSICAL_ADDRESS_MASK.load(Ordering::Relaxed)
117+
}
98118
}
99119

100120
impl Default for PageTableEntry {

0 commit comments

Comments
 (0)