Skip to content

Commit

Permalink
Merge pull request #590 from AdamCDunlap/paravisor_reserved_mem
Browse files Browse the repository at this point in the history
stage2: Use IGVM memory map to resize SVSM kernel
  • Loading branch information
joergroedel authored Jan 21, 2025
2 parents a5b1ee5 + 7ce600f commit 286ac39
Show file tree
Hide file tree
Showing 7 changed files with 60 additions and 20 deletions.
8 changes: 4 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ bitflags = "2.4"
clap = { version = "4.4.14", default-features = false }
gdbstub = { version = "0.6.6", default-features = false }
gdbstub_arch = { version = "0.2.4" }
igvm = { version = "0.3.2", default-features = false }
igvm_defs = { version = "0.3.2", default-features = false }
igvm = { version = "0.3.4", default-features = false }
igvm_defs = { version = "0.3.4", default-features = false }
intrusive-collections = "0.9.6"
libfuzzer-sys = "0.4"
log = "0.4.17"
Expand Down
13 changes: 10 additions & 3 deletions bootlib/src/igvm_params.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,12 +136,19 @@ pub struct IgvmParamBlock {
/// memory region (e.g. for VMSA contents).
pub kernel_reserved_size: u32,

/// The number of bytes in the kernel memory region.
pub kernel_size: u32,

/// The guest physical address of the base of the kernel memory region.
pub kernel_base: u64,

/// The minimum size to allocate for the kernel in bytes. If the hypervisor supplies a memory
/// region in the memory map that starts at kernel_base and is larger, that size will be used
/// instead.
pub kernel_min_size: u32,

/// The maximum size to allocate for the kernel in bytes. If the hypervisor supplies a memory
/// region in the memory map that starts at kernel_base and is larger, this maximum size will
/// be used instead.
pub kernel_max_size: u32,

/// The value of vTOM used by the guest, or zero if not used.
pub vtom: u64,
}
Expand Down
13 changes: 8 additions & 5 deletions igvmbuilder/src/gpa_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ pub struct GpaMap {
pub general_params: GpaRange,
pub memory_map: GpaRange,
pub guest_context: GpaRange,
// The kernel region represents the maximum allowable size. The hypervisor may request that it
// be smaller to save memory on smaller machine shapes. However, the entire region should not
// overlap any other regions.
pub kernel: GpaRange,
pub vmsa: GpaRange,
pub init_page_tables: GpaRange,
Expand Down Expand Up @@ -116,19 +119,19 @@ impl GpaMap {
let kernel_elf = GpaRange::new(kernel_address, kernel_elf_len as u64)?;
let kernel_fs = GpaRange::new(kernel_elf.get_end(), kernel_fs_len as u64)?;

// Calculate the kernel size and base.
// Choose the kernel base and maximum size.
let kernel = match options.hypervisor {
Hypervisor::Qemu => {
// Place the kernel area at 512 GB with a size of 16 MB.
// Place the kernel area at 512 GB with a maximum size of 16 MB.
GpaRange::new(0x0000008000000000, 0x01000000)?
}
Hypervisor::HyperV => {
// Place the kernel area at 64 MB with a size of 16 MB.
// Place the kernel area at 64 MB with a maximum size of 16 MB.
GpaRange::new(0x04000000, 0x01000000)?
}
Hypervisor::Vanadium => {
// Place the kernel area at 8TiB-2GiB with a size of 16 MB.
GpaRange::new(0x7ff80000000, 0x01000000)?
// Place the kernel area at 8TiB-2GiB with a maximum size of 2 GiB.
GpaRange::new(0x7ff80000000, 0x80000000)?
}
};

Expand Down
8 changes: 5 additions & 3 deletions igvmbuilder/src/igvm_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ impl IgvmBuilder {
fn create_param_block(&self) -> Result<IgvmParamBlock, Box<dyn Error>> {
let param_page_offset = PAGE_SIZE_4K as u32;
let memory_map_offset = param_page_offset + PAGE_SIZE_4K as u32;
let kernel_min_size = 0x1000000; // 16 MiB
let (guest_context_offset, param_area_size) = if self.gpa_map.guest_context.get_size() == 0
{
(0, memory_map_offset + PAGE_SIZE_4K as u32)
Expand Down Expand Up @@ -238,8 +239,9 @@ impl IgvmBuilder {
stage1_size: self.gpa_map.stage1_image.get_size() as u32,
stage1_base: self.gpa_map.stage1_image.get_start(),
kernel_reserved_size: PAGE_SIZE_4K as u32, // Reserved for VMSA
kernel_size: self.gpa_map.kernel.get_size() as u32,
kernel_base: self.gpa_map.kernel.get_start(),
kernel_min_size,
kernel_max_size: self.gpa_map.kernel.get_size() as u32,
vtom,
use_alternate_injection: u8::from(self.options.alt_injection),
is_qemu,
Expand Down Expand Up @@ -329,7 +331,7 @@ impl IgvmBuilder {
self.directives.push(IgvmDirectiveHeader::RequiredMemory {
gpa: param_block.kernel_base,
compatibility_mask: COMPATIBILITY_MASK.get() & !VSM_COMPATIBILITY_MASK,
number_of_bytes: param_block.kernel_size,
number_of_bytes: param_block.kernel_min_size,
vtl2_protectable: false,
});
}
Expand All @@ -338,7 +340,7 @@ impl IgvmBuilder {
self.directives.push(IgvmDirectiveHeader::RequiredMemory {
gpa: param_block.kernel_base,
compatibility_mask: VSM_COMPATIBILITY_MASK,
number_of_bytes: param_block.kernel_size,
number_of_bytes: param_block.kernel_min_size,
vtl2_protectable: true,
});
}
Expand Down
32 changes: 29 additions & 3 deletions kernel/src/igvm_params.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
extern crate alloc;

use crate::acpi::tables::ACPICPUInfo;
use crate::address::{PhysAddr, VirtAddr};
use crate::address::{Address, PhysAddr, VirtAddr};
use crate::cpu::efer::EFERFlags;
use crate::error::SvsmError;
use crate::mm::{GuestPtr, PerCPUPageMappingGuard, PAGE_SIZE};
Expand Down Expand Up @@ -76,8 +76,34 @@ impl IgvmParams<'_> {

pub fn find_kernel_region(&self) -> Result<MemoryRegion<PhysAddr>, SvsmError> {
let kernel_base = PhysAddr::from(self.igvm_param_block.kernel_base);
let kernel_size: usize = self.igvm_param_block.kernel_size.try_into().unwrap();
Ok(MemoryRegion::<PhysAddr>::new(kernel_base, kernel_size))
let mut kernel_size = self.igvm_param_block.kernel_min_size;

// Check the untrusted hypervisor-provided memory map to see if the size of the kernel
// should be adjusted. The base location and mimimum and maximum size specified by the
// measured igvm_param_block are still respected to ensure a malicious memory map cannot
// cause the SVSM kernel to overlap anything important or be so small it causes weird
// failures. But if the hypervisor gives a memory map entry of type HIDDEN that starts at
// kernel_start, use the size of that entry as a guide. This allows the hypervisor to
// adjust the size of the SVSM kernel to what it expects will be needed based on the
// machine shape.
if let Some(memory_map_region) = self.igvm_memory_map.memory_map.iter().find(|region| {
region.entry_type == MemoryMapEntryType::HIDDEN
&& region.starting_gpa_page_number.try_into() == Ok(kernel_base.pfn())
}) {
let region_size_bytes = memory_map_region
.number_of_pages
.try_into()
.unwrap_or(u32::MAX)
.saturating_mul(PAGE_SIZE as u32);
kernel_size = region_size_bytes.clamp(
self.igvm_param_block.kernel_min_size,
self.igvm_param_block.kernel_max_size,
);
}
Ok(MemoryRegion::<PhysAddr>::new(
kernel_base,
kernel_size.try_into().unwrap(),
))
}

pub fn reserved_kernel_area_size(&self) -> usize {
Expand Down
2 changes: 2 additions & 0 deletions kernel/src/stage2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,8 @@ pub extern "C" fn stage2_main(launch_info: &Stage2LaunchInfo) {
.find_kernel_region()
.expect("Failed to find memory region for SVSM kernel");

log::info!("SVSM memory region: {kernel_region:?}");

init_valid_bitmap_alloc(kernel_region).expect("Failed to allocate valid-bitmap");

// The physical memory region we've loaded so far
Expand Down

0 comments on commit 286ac39

Please sign in to comment.