diff --git a/Cargo.lock b/Cargo.lock index 82ed6e6a4..7ce766e7b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -514,9 +514,9 @@ dependencies = [ [[package]] name = "igvm" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7984b10433b50e06a06bd50c69bca4888a5d7de8975f64ea4c2a7687eb99b09d" +checksum = "7b4ae8479aa3163c8a0fa716aa6ef08a6553e1097f8a89544f46fee695b5a162" dependencies = [ "bitfield-struct 0.7.0", "crc32fast", @@ -531,9 +531,9 @@ dependencies = [ [[package]] name = "igvm_defs" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b64ec5588c475372ae830475d3ee9a7bd255407dcb9f03faf6d493556eb6105a" +checksum = "e4f70c18b574e5c7fa6222c1f0ebd8bfe2e14b762573b799faf8697c044b0e2a" dependencies = [ "bitfield-struct 0.7.0", "open-enum", diff --git a/Cargo.toml b/Cargo.toml index fbc771473..541273881 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/bootlib/src/igvm_params.rs b/bootlib/src/igvm_params.rs index b5243bd78..4de05add1 100644 --- a/bootlib/src/igvm_params.rs +++ b/bootlib/src/igvm_params.rs @@ -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, } diff --git a/igvmbuilder/src/gpa_map.rs b/igvmbuilder/src/gpa_map.rs index ac6fc038e..56f56aa69 100644 --- a/igvmbuilder/src/gpa_map.rs +++ b/igvmbuilder/src/gpa_map.rs @@ -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, @@ -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)? } }; diff --git a/igvmbuilder/src/igvm_builder.rs b/igvmbuilder/src/igvm_builder.rs index 883ad5bca..8bfde7ef6 100644 --- a/igvmbuilder/src/igvm_builder.rs +++ b/igvmbuilder/src/igvm_builder.rs @@ -193,6 +193,7 @@ impl IgvmBuilder { fn create_param_block(&self) -> Result> { 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) @@ -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, @@ -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, }); } @@ -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, }); } diff --git a/kernel/src/igvm_params.rs b/kernel/src/igvm_params.rs index cfbc13de1..a22ad4714 100644 --- a/kernel/src/igvm_params.rs +++ b/kernel/src/igvm_params.rs @@ -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}; @@ -76,8 +76,34 @@ impl IgvmParams<'_> { pub fn find_kernel_region(&self) -> Result, 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::::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::::new( + kernel_base, + kernel_size.try_into().unwrap(), + )) } pub fn reserved_kernel_area_size(&self) -> usize { diff --git a/kernel/src/stage2.rs b/kernel/src/stage2.rs index bc91a6c8d..996826398 100755 --- a/kernel/src/stage2.rs +++ b/kernel/src/stage2.rs @@ -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