Skip to content

Commit

Permalink
[SNP Guest VSM] ModifyVtlProtectionMask hypercall handling (#100)
Browse files Browse the repository at this point in the history
Implements ModifyVtlProtectionMask hypercall handling for SNP VMs with
guest VSM.

Tested with:

SNP without guest vsm
TDX-isolated VMs
Non-isolated with and without guest vsm
VBS-isolated with and without guest vsm
  • Loading branch information
sluck-msft authored Oct 25, 2024
1 parent 145788f commit 02fff4f
Show file tree
Hide file tree
Showing 14 changed files with 648 additions and 285 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6777,6 +6777,7 @@ dependencies = [
"futures",
"guestmem",
"hcl",
"hv1_emulator",
"hvdef",
"inspect",
"memory_range",
Expand All @@ -6788,6 +6789,7 @@ dependencies = [
"underhill_threadpool",
"virt_mshv_vtl",
"vm_topology",
"vtl_array",
"x86defs",
]

Expand Down
13 changes: 8 additions & 5 deletions openhcl/hcl/src/ioctl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,19 +189,25 @@ pub enum ApplyVtlProtectionsError {
Hypervisor {
range: MemoryRange,
output: HypercallOutput,
#[source]
hv_error: HvError,
vtl: HvInputVtl,
},
#[error("{failed_operation} when protecting pages {range} for vtl {vtl:?}")]
#[error(
"{failed_operation} when protecting pages {range} with {permissions:x?} for vtl {vtl:?}"
)]
Snp {
#[source]
failed_operation: snp::SnpPageError,
range: MemoryRange,
permissions: x86defs::snp::SevRmpAdjust,
vtl: HvInputVtl,
},
#[error("tdcall failed with {error:?} when protecting pages {range} for vtl {vtl:?}")]
#[error("tdcall failed with {error:?} when protecting pages {range} with permissions {permissions:x?} for vtl {vtl:?}")]
Tdx {
error: TdCallResultCode,
range: MemoryRange,
permissions: x86defs::tdx::TdgMemPageGpaAttr,
vtl: HvInputVtl,
},
#[error("no valid protections for vtl {0:?}")]
Expand Down Expand Up @@ -346,7 +352,6 @@ mod ioctls {
const MSHV_VTL_TDCALL: u16 = 0x32;
const MSHV_VTL_READ_VMX_CR4_FIXED1: u16 = 0x33;
const MSHV_VTL_GUEST_VSM_VMSA_PFN: u16 = 0x34;
#[cfg(debug_assertions)]
const MSHV_VTL_RMPQUERY: u16 = 0x35;
const MSHV_INVLPGB: u16 = 0x36;
const MSHV_TLBSYNC: u16 = 0x37;
Expand Down Expand Up @@ -388,7 +393,6 @@ mod ioctls {

#[repr(C, packed)]
#[derive(Copy, Clone)]
#[cfg(debug_assertions)]
pub struct mshv_rmpquery {
/// Execute the rmpquery instruction on the set of memory pages specified
pub start_pfn: ::std::os::raw::c_ulonglong,
Expand Down Expand Up @@ -502,7 +506,6 @@ mod ioctls {
mshv_rmpadjust
);

#[cfg(debug_assertions)]
ioctl_write_ptr!(
/// Executes the rmpquery instruction on a page range.
hcl_rmpquery_pages,
Expand Down
41 changes: 39 additions & 2 deletions openhcl/hcl/src/ioctl/snp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
use super::hcl_pvalidate_pages;
use super::hcl_rmpadjust_pages;
use super::hcl_rmpquery_pages;
use super::mshv_pvalidate;
use super::mshv_rmpadjust;
use super::mshv_rmpquery;
use super::HclVp;
use super::MshvVtl;
use super::NoRunner;
Expand Down Expand Up @@ -45,9 +47,9 @@ pub enum SnpError {
#[allow(missing_docs)]
pub enum SnpPageError {
#[error("pvalidate failed")]
Pvalidate(SnpError),
Pvalidate(#[source] SnpError),
#[error("rmpadjust failed")]
Rmpadjust(SnpError),
Rmpadjust(#[source] SnpError),
}

impl MshvVtl {
Expand Down Expand Up @@ -128,6 +130,41 @@ impl MshvVtl {

Ok(())
}

/// Gets the current vtl permissions for a page.
pub fn rmpquery_page(&self, gpa: u64, vtl: GuestVtl) -> SevRmpAdjust {
let page_count = 1u64;
let mut flags = [u64::from(SevRmpAdjust::new().with_target_vmpl(match vtl {
GuestVtl::Vtl0 => 2,
GuestVtl::Vtl1 => 1,
})); 1];

let mut page_size = [0; 1];
let mut pages_processed = 0u64;

debug_assert!(flags.len() == page_count as usize);
debug_assert!(page_size.len() == page_count as usize);

let query = mshv_rmpquery {
start_pfn: gpa / HV_PAGE_SIZE,
page_count,
terminate_on_failure: 0,
ram: 0,
padding: Default::default(),
flags: flags.as_mut_ptr(),
page_size: page_size.as_mut_ptr(),
pages_processed: &mut pages_processed,
};

// SAFETY: the input query is the correct type for this ioctl
unsafe {
hcl_rmpquery_pages(self.file.as_raw_fd(), &query).expect("should always succeed");
}

assert!(pages_processed <= page_count);

SevRmpAdjust::from(flags[0])
}
}

impl super::private::BackingPrivate for Snp {
Expand Down
2 changes: 2 additions & 0 deletions openhcl/underhill_mem/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@ rust-version.workspace = true
[target.'cfg(target_os = "linux")'.dependencies]
guestmem.workspace = true
hcl.workspace = true
hv1_emulator.workspace = true
hvdef.workspace = true
memory_range.workspace = true
underhill_threadpool.workspace = true
virt_mshv_vtl.workspace = true
vm_topology.workspace = true
x86defs.workspace = true
vtl_array.workspace = true

inspect.workspace = true
pal_async.workspace = true
Expand Down
10 changes: 5 additions & 5 deletions openhcl/underhill_mem/src/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,16 +58,16 @@ impl MemoryMappings {
}
pub fn isolated_memory_protector(
&self,
) -> anyhow::Result<Option<Box<dyn ProtectIsolatedMemory>>> {
) -> anyhow::Result<Option<Arc<dyn ProtectIsolatedMemory>>> {
match self.isolation {
Some(IsolationType::Snp | IsolationType::Tdx) => {
Ok(self.shared.as_ref().map(|shared| {
Box::new(HardwareIsolatedMemoryProtector::new(
Arc::new(HardwareIsolatedMemoryProtector::new(
shared.clone(),
self.vtl0.clone(),
self.layout.clone(),
self.acceptor.as_ref().unwrap().clone(),
)) as Box<dyn ProtectIsolatedMemory>
)) as Arc<dyn ProtectIsolatedMemory>
}))
}
_ => Ok(None),
Expand Down Expand Up @@ -131,7 +131,7 @@ pub async fn init(params: &Init<'_>) -> anyhow::Result<MemoryMappings> {
tracing::debug!("Applying VTL0 protections");
if hardware_isolated {
for range in memory_range::overlapping_ranges(ram.clone(), accepted_ranges.clone()) {
acceptor.apply_default_vtl_protections(range, Vtl::Vtl0)?;
acceptor.apply_initial_lower_vtl_protections(range)?;
}
}

Expand All @@ -144,7 +144,7 @@ pub async fn init(params: &Init<'_>) -> anyhow::Result<MemoryMappings> {
// For VBS-isolated VMs, the VTL protections are set as
// part of the accept call.
acceptor
.apply_default_vtl_protections(subrange, Vtl::Vtl0)
.apply_initial_lower_vtl_protections(subrange)
.unwrap();
}
};
Expand Down
Loading

0 comments on commit 02fff4f

Please sign in to comment.