Skip to content

Commit

Permalink
stage2: Use IGVM memory map to resize SVSM kernel
Browse files Browse the repository at this point in the history
If the IGVM memory map has a HIDDEN entry that matches where the SVSM
kernel should go according to the IgvmParamBlock, then use the size of
that block for the size of the kernel (checked against a minimum and
maximum size in the ParamBlock)

This allows the hypervisor to dynamically resize the SVSM based on the
machine shape without changing the IgvmParamBlock and thus the launch
measurement. This is necessary because the SVSM uses more memory when
there are more vCPUs (and, in the future, guest memory). The location
and minium and maximum size can still be measured in the IgvmParamBlock
to ensure that the kernel region will not overlap anything important or
cause undersize problems.

Signed-off-by: Adam Dunlap <[email protected]>
  • Loading branch information
AdamCDunlap committed Jan 16, 2025
1 parent dda83f0 commit 7ce600f
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 @@ -192,6 +192,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 @@ -237,8 +238,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 @@ -328,7 +330,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 @@ -337,7 +339,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 7ce600f

Please sign in to comment.