-
Notifications
You must be signed in to change notification settings - Fork 143
feat(sev): add AMD SEV support #542
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 1 commit
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
19a03a9
feat(sev): add AMD SEV support
zyuiop 1b26f6c
x86_64/sev: move SEV structures to codebase
zyuiop 0a8a281
x86_64/memory_encryption: don't enable ME by default
zyuiop 073c196
x86_64/memory_encryption: fix review and clippy issues
zyuiop 6558dd8
memory_encryption: refactor for Intel TDX support
zyuiop 684aaa7
memory_encryption: enable memory_encryption flag in CI
zyuiop 8a3abd1
memory_encryption: reduce function duplication in page_table.rs
zyuiop 96f7233
memory_encryption: remove feature `dynamic_flags`
zyuiop 1560047
memory_encryption: prevent setting the encryption bit in PhysAddr
zyuiop 8519ada
memory_encryption: missing documentation
zyuiop 3e05fa7
ci: test memory_encryption separately
zyuiop 43e3db9
memory_encryption: address PR feedback
zyuiop c13dd06
memory_encryption: keep `const` functions const if `memory_encryption…
zyuiop 29e9466
memory_encryption: exclude memory_encryption feature when checking fo…
zyuiop 65bdd52
memory_encryption: fix `const_fn` name already taken
zyuiop 6fb583a
memory_encryption: fix formatting
zyuiop 648ae66
memory_encryption: review fixes
zyuiop a872185
memory_encryption: review fixes + fix imports changes
zyuiop bd7a9ef
memory_encryption: fix import style
zyuiop 5370c17
ci: only run semver checks on default features
zyuiop File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
use core::sync::atomic::AtomicBool; | ||
use core::sync::atomic::Ordering::SeqCst; | ||
|
||
use bit_field::BitField; | ||
|
||
use crate::registers::model_specific::Msr; | ||
use crate::structures::paging; | ||
use crate::structures::paging::PageTableFlags; | ||
|
||
const MSR_AMD_SEV: Msr = Msr::new(0xc0010131); | ||
|
||
const SEV_INITIALIZED: AtomicBool = AtomicBool::new(false); | ||
Freax13 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
|
||
/// Retrieves the AMD SEV status, after calling `init` | ||
pub fn sev_state<'a>() -> Option<&'a SevState> { | ||
AMD_SEV_STATUS.as_ref() | ||
} | ||
|
||
/// Initializes AMD Secure Encrypted Virtualization, if enabled. | ||
/// | ||
/// It is required to call this function in an SEV enabled guest, as it sets-up a dynamic page table | ||
/// flag for memory encryption. If this is not set, page table operations may cause panics due to | ||
/// invalid canonical addresses. | ||
pub fn init<'a>() -> Option<&'a SevState> { | ||
if SEV_INITIALIZED.fetch_or(true, SeqCst) { | ||
return sev_state(); | ||
} | ||
|
||
// https://github.com/torvalds/linux/blob/900241a5cc15e6e0709a012051cc72d224cd6a6e/arch/x86/mm/mem_encrypt_identity.c#L566 | ||
// Check for SME support | ||
let (cpuid_max, _) = unsafe { core::arch::x86_64::__get_cpuid_max(0x8000_0000) }; | ||
if cpuid_max < 0x8000_001F { | ||
return None; | ||
} | ||
|
||
// Check if SME is available on the current CPU then read the C-Bit position and define the mask | ||
let sme_features = unsafe { core::arch::x86_64::__cpuid(0x8000_001F) }; | ||
let sme_available = (sme_features.eax & 0x3) != 0; // either SME or SEV bit is set | ||
if !sme_available { | ||
return None; | ||
} | ||
Freax13 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
let cbit_mask = (1u64) << sme_features.ebx.get_bits(0..=5) as u16; | ||
|
||
// Check the MSR to see if SME is currently enabled | ||
let sme_status = unsafe { MSR_AMD_SEV.read() }; | ||
let sev_enabled = (sme_status & 0x1) == 1; | ||
let sev_es_enabled = (sme_status & 0x2) == 0x2; | ||
let snp_enabled = (sme_status & 0x4) == 0x4; | ||
|
||
if !sev_enabled && !snp_enabled { | ||
return None; | ||
} | ||
|
||
let state = Some(SevState { | ||
sev_enabled, | ||
snp_enabled, | ||
sev_es_enabled, | ||
c_bit_flag: PageTableFlags::from_bits_retain(cbit_mask), | ||
}); | ||
|
||
// Update the memory mask | ||
unsafe { | ||
assign_static_immutable(&paging::page_table::PHYSICAL_ADDRESS_MASK, |pa| { | ||
*pa &= !cbit_mask | ||
}); | ||
Freax13 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
assign_static_immutable(&AMD_SEV_STATUS, |opt| *opt = state); | ||
} | ||
|
||
sev_state() | ||
} | ||
|
||
unsafe fn assign_static_immutable<T, F>(attr: &'static T, op: F) | ||
where | ||
F: FnOnce(&mut T), | ||
{ | ||
let ptr = attr as *const T; | ||
unsafe { | ||
let ptr = ptr as u64; | ||
let ptr = ptr as *mut T; | ||
op(ptr.as_mut().unwrap()); | ||
} | ||
} | ||
Freax13 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
static AMD_SEV_STATUS: Option<SevState> = None; | ||
|
||
|
||
/// Represents the current enablement state of AMD Secure Encrypted Virtualization features | ||
#[derive(Copy, Clone, Debug)] | ||
pub struct SevState { | ||
/// True if Secure Encrypted Virtualization is enabled | ||
pub sev_enabled: bool, | ||
|
||
/// True if Secure Nested Paging is enabled. | ||
/// | ||
/// Implies `sev_es_enabled` = true and `sev_enabled` = true | ||
pub snp_enabled: bool, | ||
|
||
/// True if Encrypted State is enabled. | ||
/// | ||
/// Implies `sev_enabled` = true | ||
pub sev_es_enabled: bool, | ||
|
||
/// Custom flag used to set the encryption bit in page table entries | ||
pub c_bit_flag: PageTableFlags, | ||
} | ||
|
||
impl PageTableFlags { | ||
#[inline] | ||
fn c_bit_mask() -> PageTableFlags { | ||
sev_state().map(|s| s.c_bit_flag) | ||
.expect("fatal: memory encryption is not enabled") | ||
Freax13 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
/// Sets the encryption bit on the page table entry. | ||
/// | ||
/// Requires memory encryption to be enabled, or this will panic. | ||
pub fn set_encrypted(&mut self, encrypted: bool) { | ||
self.set(Self::c_bit_mask(), encrypted); | ||
} | ||
|
||
/// Checks if the encryption bit is set on the page table entry. | ||
/// | ||
/// Requires memory encryption to be enabled, or this will panic. | ||
pub fn is_encrypted(&self) -> bool { | ||
self.contains(Self::c_bit_mask()) | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if we should enable this by default. Most users won't be running their code in an SEV VM, and there's likely a non-zero performance impact.
People have also been using this crate with AMD SEV-SNP and Intel TDX for a while, even without explicit support in this crate, and we don't want to accidentally break their code. In practice, on all currently available platforms, the C-bit/S-bit is always an address bit (I think this is even guaranteed on Intel TDX). People have been considering it as such and not a page table bit, so they're just setting the bit in
PhysAddr
/PhysFrame
directly.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right - it should not be enabled by default, I will fix.
The reason I'm making this patch is that I tried running an unmodified Hermit OS in an QEMU SEV VM, and it did not work, because of the c-bit.
In practice, I could retract the patch and do what you suggest with physical address bits in Hermit directly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hermit OS will need to be modified anyway; SEV/SEV-ES/SEV-SNP/TDX all require kernel modifications.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FYI: At Hermit we are also working on SEV support, but as it indeed requires numerous kernel modifications, this is not (yet) ready for publication. We also made our own quick-and-dirty changes to this crate - not too different from what is proposed here, but also not yet ready and generic. So a clean and upstream version of SEV support in this crate would be much appreciated 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's great to hear. Out of curiosity, what generation are you targeting, SEV, SEV-ES, or SEV-SNP?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, is there any way we could get in touch? I should probably have started with that, but better late than never.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, SEV without SNP doesn't make much sense anymore, right? 😁
Sure, just drop us a mail 😉