From f7d17a2ea2cf94d86268732ab281861d5e30ecdc Mon Sep 17 00:00:00 2001 From: Jon Lange Date: Sun, 19 Nov 2023 06:08:29 -0800 Subject: [PATCH 1/8] Add IGVM parameter block for IGVM-based launch Signed-off-by: Jon Lange --- Cargo.lock | 5 ++++ Cargo.toml | 1 + igvm_params/Cargo.toml | 8 +++++ igvm_params/src/lib.rs | 66 ++++++++++++++++++++++++++++++++++++++++++ src/config.rs | 13 ++++++++- src/igvm_params.rs | 48 ++++++++++++++++++++++++++++++ src/lib.rs | 1 + src/stage2.rs | 10 ++++++- stage1/stage1.S | 4 +++ 9 files changed, 154 insertions(+), 2 deletions(-) create mode 100644 igvm_params/Cargo.toml create mode 100644 igvm_params/src/lib.rs create mode 100644 src/igvm_params.rs diff --git a/Cargo.lock b/Cargo.lock index 8fb042bd0..e1239f859 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -149,6 +149,10 @@ dependencies = [ "polyval", ] +[[package]] +name = "igvm_params" +version = "0.1.0" + [[package]] name = "inout" version = "0.1.3" @@ -267,6 +271,7 @@ dependencies = [ "bitflags 2.4.1", "gdbstub", "gdbstub_arch", + "igvm_params", "intrusive-collections", "log", "memoffset", diff --git a/Cargo.toml b/Cargo.toml index 98dacde2e..3efeab969 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,7 @@ intrusive-collections = "0.9.6" log = { version = "0.4.17", features = ["max_level_info", "release_max_level_info"] } packit = { git = "https://github.com/coconut-svsm/packit", version = "0.1.0" } aes-gcm = { version = "0.10.3", default-features = false, features = ["aes", "alloc"] } +igvm_params = { path = "igvm_params" } [target."x86_64-unknown-none".dev-dependencies] test = { version = "0.1.0", path = "test" } diff --git a/igvm_params/Cargo.toml b/igvm_params/Cargo.toml new file mode 100644 index 000000000..3310cb060 --- /dev/null +++ b/igvm_params/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "igvm_params" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/igvm_params/src/lib.rs b/igvm_params/src/lib.rs new file mode 100644 index 000000000..253badfc9 --- /dev/null +++ b/igvm_params/src/lib.rs @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) Microsoft Corporation +// +// Author: Jon Lange (jlange@microsoft.com) + +//! This crate provides definitions of IGVM parameters to be parsed by +//! COCONUT-SVSM to determine its configuration. It is provided as a separate +//! crate since the same definitions must be known to the utility that +//! constructs the IGVM file. + +#![no_std] + +/// The IGVM parameter page is an unmeasured page containing individual +/// parameters that are provided by the host loader. +#[repr(C, packed)] +#[derive(Clone, Debug)] +pub struct IgvmParamPage { + /// The number of vCPUs that are configured for the guest VM. + pub cpu_count: u32, + + /// A flag indicating whether the default state of guest memory is shared + /// (not assigned to the guest) or private (assigned to the guest). + /// Shared pages must undergo a page state change to private before they + /// can be accepted for guest use. A zero value here means that the + /// default state is private, and a non-zero value means that the default + /// state is shared. + pub default_shared_pages: u32, +} + +/// The IGVM parameter block is a measured page constructed by the IGVM file +/// builder which describes where the additional IGVM parameter information +/// has been placed into the guest address space. +#[repr(C, packed)] +#[derive(Clone, Debug)] +pub struct IgvmParamBlock { + /// The total size of the parameter area, beginning with the parameter + /// block itself and including any additional parameter pages which follow. + pub param_area_size: u32, + + /// The offset, in bytes, from the base of the parameter block to the base + /// of the parameter page. + pub param_page_offset: u32, + + /// The offset, in bytes, from the base of the parameter block to the base + /// of the memory map (which is in IGVM format). + pub memory_map_offset: u32, + + /// The guest physical address of the CPUID page. + pub cpuid_page: u32, + + /// The guest physical address of the secrets page. + pub secrets_page: u32, + + /// A flag indicating whether the kernel should proceed with the flow + /// to launch guest firmware once kernel initialization is complete. + pub launch_fw: u8, + + _reserved: [u8; 7], + + /// 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, +} diff --git a/src/config.rs b/src/config.rs index a47805fa4..b2763cf56 100644 --- a/src/config.rs +++ b/src/config.rs @@ -10,46 +10,57 @@ use crate::acpi::tables::{load_acpi_cpu_info, ACPICPUInfo}; use crate::address::PhysAddr; use crate::error::SvsmError; use crate::fw_cfg::FwCfg; +use crate::igvm_params::IgvmParams; use crate::utils::MemoryRegion; use alloc::vec::Vec; #[derive(Debug)] pub enum SvsmConfig<'a> { FirmwareConfig(FwCfg<'a>), + IgvmConfig(IgvmParams<'a>), } impl<'a> SvsmConfig<'a> { pub fn find_kernel_region(&self) -> Result, SvsmError> { match self { SvsmConfig::FirmwareConfig(fw_cfg) => fw_cfg.find_kernel_region(), + SvsmConfig::IgvmConfig(igvm_params) => igvm_params.find_kernel_region(), } } pub fn get_cpuid_page_address(&self) -> u64 { match self { SvsmConfig::FirmwareConfig(_) => 0x9f000, + SvsmConfig::IgvmConfig(igvm_params) => igvm_params.get_cpuid_page_address(), } } pub fn get_secrets_page_address(&self) -> u64 { match self { SvsmConfig::FirmwareConfig(_) => 0x9e000, + SvsmConfig::IgvmConfig(igvm_params) => igvm_params.get_secrets_page_address(), } } pub fn page_state_change_required(&self) -> bool { - true + match self { + SvsmConfig::FirmwareConfig(_) => true, + SvsmConfig::IgvmConfig(igvm_params) => igvm_params.page_state_change_required(), + } } pub fn get_memory_regions(&self) -> Result>, SvsmError> { match self { SvsmConfig::FirmwareConfig(fw_cfg) => fw_cfg.get_memory_regions(), + &SvsmConfig::IgvmConfig(_) => todo!(), } } pub fn load_cpu_info(&self) -> Result, SvsmError> { match self { SvsmConfig::FirmwareConfig(fw_cfg) => load_acpi_cpu_info(fw_cfg), + &SvsmConfig::IgvmConfig(_) => todo!(), } } pub fn should_launch_fw(&self) -> bool { match self { SvsmConfig::FirmwareConfig(_) => true, + SvsmConfig::IgvmConfig(_) => false, } } } diff --git a/src/igvm_params.rs b/src/igvm_params.rs new file mode 100644 index 000000000..bfc32ebc8 --- /dev/null +++ b/src/igvm_params.rs @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) Microsoft Corporation +// +// Author: Jon Lange (jlange@microsoft.com) + +use crate::address::{PhysAddr, VirtAddr}; +use crate::error::SvsmError; +use crate::utils::MemoryRegion; + +use igvm_params::{IgvmParamBlock, IgvmParamPage}; + +#[derive(Clone, Debug)] +pub struct IgvmParams<'a> { + igvm_param_block: &'a IgvmParamBlock, + igvm_param_page: &'a IgvmParamPage, +} + +impl IgvmParams<'_> { + pub fn new(addr: VirtAddr) -> Self { + let param_block = unsafe { &*addr.as_ptr::() }; + let param_page_address = addr + param_block.param_page_offset.try_into().unwrap(); + let param_page = unsafe { &*param_page_address.as_ptr::() }; + + Self { + igvm_param_block: param_block, + igvm_param_page: param_page, + } + } + + 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)) + } + + pub fn page_state_change_required(&self) -> bool { + self.igvm_param_page.default_shared_pages != 0 + } + + pub fn get_cpuid_page_address(&self) -> u64 { + self.igvm_param_block.cpuid_page as u64 + } + + pub fn get_secrets_page_address(&self) -> u64 { + self.igvm_param_block.secrets_page as u64 + } +} diff --git a/src/lib.rs b/src/lib.rs index 1d3827f6c..d9aa75d52 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -25,6 +25,7 @@ pub mod fs; pub mod fw_cfg; pub mod fw_meta; pub mod greq; +pub mod igvm_params; pub mod io; pub mod kernel_launch; pub mod locking; diff --git a/src/stage2.rs b/src/stage2.rs index fd3111311..c5d0755b5 100755 --- a/src/stage2.rs +++ b/src/stage2.rs @@ -21,6 +21,7 @@ use svsm::cpu::idt::stage2::{early_idt_init, early_idt_init_no_ghcb}; use svsm::cpu::percpu::{this_cpu_mut, PerCpu}; use svsm::elf; use svsm::fw_cfg::FwCfg; +use svsm::igvm_params::IgvmParams; use svsm::kernel_launch::KernelLaunchInfo; use svsm::mm::alloc::{memory_info, print_memory_info, root_mem_init}; use svsm::mm::init_kernel_mapping_info; @@ -152,6 +153,7 @@ pub struct Stage1LaunchInfo { kernel_elf_end: u32, kernel_fs_start: u32, kernel_fs_end: u32, + igvm_params: u32, } #[no_mangle] @@ -161,7 +163,13 @@ pub extern "C" fn stage2_main(launch_info: &Stage1LaunchInfo) { let kernel_elf_start: PhysAddr = PhysAddr::from(launch_info.kernel_elf_start as u64); let kernel_elf_end: PhysAddr = PhysAddr::from(launch_info.kernel_elf_end as u64); - let config = SvsmConfig::FirmwareConfig(FwCfg::new(&CONSOLE_IO)); + let config = if launch_info.igvm_params != 0 { + let igvm_params = IgvmParams::new(VirtAddr::from(launch_info.igvm_params as u64)); + SvsmConfig::IgvmConfig(igvm_params) + } else { + SvsmConfig::FirmwareConfig(FwCfg::new(&CONSOLE_IO)) + }; + let r = config .find_kernel_region() .expect("Failed to find memory region for SVSM kernel"); diff --git a/stage1/stage1.S b/stage1/stage1.S index 901557b5b..82812450f 100644 --- a/stage1/stage1.S +++ b/stage1/stage1.S @@ -51,6 +51,10 @@ startup_32: movl $STAGE2_START, %esp /* Write startup information to stage2 stack */ + xorl %eax, %eax + pushl %eax + pushl %eax + leal kernel_fs_bin_end(%ebp), %edi pushl %edi From 6803f04b638309bf6233fecaa7fb0735c96d13da Mon Sep 17 00:00:00 2001 From: Jon Lange Date: Thu, 30 Nov 2023 16:40:15 -0800 Subject: [PATCH 2/8] Map IGVM params in stage 2 for use by the kernel Signed-off-by: Jon Lange --- src/igvm_params.rs | 8 ++++++++ src/kernel_launch.rs | 1 + src/stage2.rs | 38 +++++++++++++++++++++++++++++++++++++- 3 files changed, 46 insertions(+), 1 deletion(-) diff --git a/src/igvm_params.rs b/src/igvm_params.rs index bfc32ebc8..cdf6d19cb 100644 --- a/src/igvm_params.rs +++ b/src/igvm_params.rs @@ -6,6 +6,7 @@ use crate::address::{PhysAddr, VirtAddr}; use crate::error::SvsmError; +use crate::mm::PAGE_SIZE; use crate::utils::MemoryRegion; use igvm_params::{IgvmParamBlock, IgvmParamPage}; @@ -28,6 +29,13 @@ impl IgvmParams<'_> { } } + pub fn size(&self) -> usize { + // Calculate the total size of the parameter area. The + // parameter area always begins at the kernel base + // address. + self.igvm_param_block.param_area_size.try_into().unwrap() + } + 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(); diff --git a/src/kernel_launch.rs b/src/kernel_launch.rs index cd5815a7e..f51d46c01 100644 --- a/src/kernel_launch.rs +++ b/src/kernel_launch.rs @@ -23,6 +23,7 @@ pub struct KernelLaunchInfo { pub kernel_fs_end: u64, pub cpuid_page: u64, pub secrets_page: u64, + pub igvm_params: u64, } impl KernelLaunchInfo { diff --git a/src/stage2.rs b/src/stage2.rs index c5d0755b5..7533bf6ce 100755 --- a/src/stage2.rs +++ b/src/stage2.rs @@ -192,6 +192,12 @@ pub extern "C" fn stage2_main(launch_info: &Stage1LaunchInfo) { let kernel_vaddr_alloc_info = kernel_elf.image_load_vaddr_alloc_info(); let kernel_vaddr_alloc_base = kernel_vaddr_alloc_info.range.vaddr_begin; + // Determine the starting physical address at which the kernel should be + // relocated. If IGVM parameters are present, then this will follow the + // IGVM parameters. Otherwise, it will be the base of the kernel + // region. + let mut loaded_kernel_phys_end = kernel_region_phys_start; + // Map, validate and populate the SVSM kernel ELF's PT_LOAD segments. The // segments' virtual address range might not necessarily be contiguous, // track their total extent along the way. Physical memory is successively @@ -200,7 +206,6 @@ pub extern "C" fn stage2_main(launch_info: &Stage1LaunchInfo) { // physical memory occupied by the loaded ELF image. let mut loaded_kernel_virt_start: Option = None; let mut loaded_kernel_virt_end = VirtAddr::null(); - let mut loaded_kernel_phys_end = kernel_region_phys_start; for segment in kernel_elf.image_load_segment_iter(kernel_vaddr_alloc_base) { // All ELF segments should be aligned to the page size. If not, there's // the risk of pvalidating a page twice, bail out if so. Note that the @@ -267,6 +272,36 @@ pub extern "C" fn stage2_main(launch_info: &Stage1LaunchInfo) { } } + // If IGVM parameters are present, then map them into the address space + // after the kernel. + let mut igvm_params_virt_address = VirtAddr::null(); + if let SvsmConfig::IgvmConfig(ref igvm_params) = config { + igvm_params_virt_address = loaded_kernel_virt_end; + let igvm_params_phys_address = loaded_kernel_phys_end; + let igvm_params_size = igvm_params.size(); + + map_and_validate( + &config, + igvm_params_virt_address, + igvm_params_phys_address, + igvm_params_size, + ); + + let igvm_params_src_addr = VirtAddr::from(launch_info.igvm_params as u64); + let igvm_src = + unsafe { slice::from_raw_parts(igvm_params_src_addr.as_ptr::(), igvm_params_size) }; + let igvm_dest = unsafe { + slice::from_raw_parts_mut( + igvm_params_virt_address.as_mut_ptr::(), + igvm_params_size, + ) + }; + igvm_dest.copy_from_slice(igvm_src); + + loaded_kernel_virt_end = loaded_kernel_virt_end + igvm_params_size; + loaded_kernel_phys_end = loaded_kernel_phys_end + igvm_params_size; + } + // Map the rest of the memory region to right after the kernel image. let heap_area_phys_start = loaded_kernel_phys_end; let heap_area_virt_start = loaded_kernel_virt_end; @@ -292,6 +327,7 @@ pub extern "C" fn stage2_main(launch_info: &Stage1LaunchInfo) { kernel_fs_end: u64::from(launch_info.kernel_fs_end), cpuid_page: config.get_cpuid_page_address(), secrets_page: config.get_secrets_page_address(), + igvm_params: u64::from(igvm_params_virt_address), }; let mem_info = memory_info(); From 90e71fb3fd18d3c9c1888a2a7d146b2d046e1831 Mon Sep 17 00:00:00 2001 From: Jon Lange Date: Fri, 24 Nov 2023 16:51:58 -0800 Subject: [PATCH 3/8] Consume the IGVM parameters during kernel launch Signed-off-by: Jon Lange --- src/svsm.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/svsm.rs b/src/svsm.rs index ed2078163..f8608e506 100755 --- a/src/svsm.rs +++ b/src/svsm.rs @@ -33,6 +33,7 @@ use svsm::error::SvsmError; use svsm::fs::{initialize_fs, populate_ram_fs}; use svsm::fw_cfg::FwCfg; use svsm::greq::driver::guest_request_driver_init; +use svsm::igvm_params::IgvmParams; use svsm::kernel_launch::KernelLaunchInfo; use svsm::mm::alloc::{memory_info, print_memory_info, root_mem_init}; use svsm::mm::memory::init_memory_map; @@ -423,7 +424,13 @@ pub extern "C" fn svsm_main() { // a remote GDB connection //debug_break(); - let config = SvsmConfig::FirmwareConfig(FwCfg::new(&CONSOLE_IO)); + let launch_info = &*LAUNCH_INFO; + let config = if launch_info.igvm_params != 0 { + let igvm_params = IgvmParams::new(VirtAddr::from(launch_info.igvm_params)); + SvsmConfig::IgvmConfig(igvm_params) + } else { + SvsmConfig::FirmwareConfig(FwCfg::new(&CONSOLE_IO)) + }; invalidate_stage2(&config).expect("Failed to invalidate Stage2 memory"); From 16da1ceea6ca679cc07b158327603a241c40c19f Mon Sep 17 00:00:00 2001 From: Jon Lange Date: Tue, 5 Dec 2023 10:30:55 -0800 Subject: [PATCH 4/8] Map the IGVM parameters into the page tables constructed by the kernel Signed-off-by: Jon Lange --- src/kernel_launch.rs | 3 ++- src/stage2.rs | 6 ++++-- src/svsm.rs | 4 ++-- src/svsm_paging.rs | 16 ++++++++++++++++ 4 files changed, 24 insertions(+), 5 deletions(-) diff --git a/src/kernel_launch.rs b/src/kernel_launch.rs index f51d46c01..968a50672 100644 --- a/src/kernel_launch.rs +++ b/src/kernel_launch.rs @@ -23,7 +23,8 @@ pub struct KernelLaunchInfo { pub kernel_fs_end: u64, pub cpuid_page: u64, pub secrets_page: u64, - pub igvm_params: u64, + pub igvm_params_phys_addr: u64, + pub igvm_params_virt_addr: u64, } impl KernelLaunchInfo { diff --git a/src/stage2.rs b/src/stage2.rs index 7533bf6ce..657ade241 100755 --- a/src/stage2.rs +++ b/src/stage2.rs @@ -275,9 +275,10 @@ pub extern "C" fn stage2_main(launch_info: &Stage1LaunchInfo) { // If IGVM parameters are present, then map them into the address space // after the kernel. let mut igvm_params_virt_address = VirtAddr::null(); + let mut igvm_params_phys_address = PhysAddr::null(); if let SvsmConfig::IgvmConfig(ref igvm_params) = config { igvm_params_virt_address = loaded_kernel_virt_end; - let igvm_params_phys_address = loaded_kernel_phys_end; + igvm_params_phys_address = loaded_kernel_phys_end; let igvm_params_size = igvm_params.size(); map_and_validate( @@ -327,7 +328,8 @@ pub extern "C" fn stage2_main(launch_info: &Stage1LaunchInfo) { kernel_fs_end: u64::from(launch_info.kernel_fs_end), cpuid_page: config.get_cpuid_page_address(), secrets_page: config.get_secrets_page_address(), - igvm_params: u64::from(igvm_params_virt_address), + igvm_params_phys_addr: u64::from(igvm_params_phys_address), + igvm_params_virt_addr: u64::from(igvm_params_virt_address), }; let mem_info = memory_info(); diff --git a/src/svsm.rs b/src/svsm.rs index f8608e506..483b97cc1 100755 --- a/src/svsm.rs +++ b/src/svsm.rs @@ -425,8 +425,8 @@ pub extern "C" fn svsm_main() { //debug_break(); let launch_info = &*LAUNCH_INFO; - let config = if launch_info.igvm_params != 0 { - let igvm_params = IgvmParams::new(VirtAddr::from(launch_info.igvm_params)); + let config = if launch_info.igvm_params_virt_addr != 0 { + let igvm_params = IgvmParams::new(VirtAddr::from(launch_info.igvm_params_virt_addr)); SvsmConfig::IgvmConfig(igvm_params) } else { SvsmConfig::FirmwareConfig(FwCfg::new(&CONSOLE_IO)) diff --git a/src/svsm_paging.rs b/src/svsm_paging.rs index 4c49a2d61..7e0457a5d 100644 --- a/src/svsm_paging.rs +++ b/src/svsm_paging.rs @@ -9,6 +9,7 @@ use crate::config::SvsmConfig; use crate::cpu::percpu::this_cpu_mut; use crate::elf; use crate::error::SvsmError; +use crate::igvm_params::IgvmParams; use crate::kernel_launch::KernelLaunchInfo; use crate::mm; use crate::mm::pagetable::{set_init_pgtable, PTEntryFlags, PageTable, PageTableRef}; @@ -45,6 +46,21 @@ pub fn init_page_table(launch_info: &KernelLaunchInfo, kernel_elf: &elf::Elf64Fi phys = phys + segment_len; } + // Map the IGVM parameters if present. + if launch_info.igvm_params_virt_addr != 0 { + let igvm_params_virt_addr = VirtAddr::from(launch_info.igvm_params_virt_addr); + let igvm_params = IgvmParams::new(igvm_params_virt_addr); + + pgtable + .map_region( + igvm_params_virt_addr, + igvm_params_virt_addr + igvm_params.size(), + PhysAddr::from(launch_info.igvm_params_phys_addr), + PTEntryFlags::data(), + ) + .expect("Failed to map IGVM parameters"); + } + // Map subsequent heap area. pgtable .map_region( From 3d94a0df63c27b04c24199adb4b8ca0cb56bdd80 Mon Sep 17 00:00:00 2001 From: Jon Lange Date: Mon, 27 Nov 2023 14:41:57 -0800 Subject: [PATCH 5/8] Parse the IGVM memory map Signed-off-by: Jon Lange --- Cargo.lock | 88 ++++++++++++++++++++++++++++++++++++++++++++-- Cargo.toml | 1 + src/config.rs | 2 +- src/igvm_params.rs | 57 ++++++++++++++++++++++++++++++ 4 files changed, 144 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e1239f859..a2bf663a3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -43,6 +43,17 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "bitfield-struct" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48adbf9313c68e023d32f441d19942226212ade3999fb22872c37a70a1b5366a" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -149,6 +160,18 @@ dependencies = [ "polyval", ] +[[package]] +name = "igvm_defs" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a5930f41716c295685e466befcd97169e6a00e61b5b037bf32e579b5c932e0a" +dependencies = [ + "bitfield-struct", + "open-enum", + "static_assertions", + "zerocopy 0.7.26", +] + [[package]] name = "igvm_params" version = "0.1.0" @@ -213,12 +236,32 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +[[package]] +name = "open-enum" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba485b94b3e73fa752d98cfcab74647a4a537269682cc1ee5256aa020432506d" +dependencies = [ + "open-enum-derive", +] + +[[package]] +name = "open-enum-derive" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed1c261430059cab8b2b51eec42a3c15750439ec6c013cd8fe41d4a450de776" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "packit" version = "0.1.0" source = "git+https://github.com/coconut-svsm/packit#540b471ee8da1d28fee8d9490888c84a48da04a8" dependencies = [ - "zerocopy", + "zerocopy 0.6.5", ] [[package]] @@ -257,6 +300,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "subtle" version = "2.5.0" @@ -271,6 +320,7 @@ dependencies = [ "bitflags 2.4.1", "gdbstub", "gdbstub_arch", + "igvm_defs", "igvm_params", "intrusive-collections", "log", @@ -279,6 +329,17 @@ dependencies = [ "test", ] +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "syn" version = "2.0.38" @@ -329,7 +390,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96f8f25c15a0edc9b07eb66e7e6e97d124c0505435c382fde1ab7ceb188aa956" dependencies = [ "byteorder", - "zerocopy-derive", + "zerocopy-derive 0.6.5", +] + +[[package]] +name = "zerocopy" +version = "0.7.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e97e415490559a91254a2979b4829267a57d2fcd741a98eee8b722fb57289aa0" +dependencies = [ + "byteorder", + "zerocopy-derive 0.7.26", ] [[package]] @@ -340,5 +411,16 @@ checksum = "855e0f6af9cd72b87d8a6c586f3cb583f5cdcc62c2c80869d8cd7e96fdf7ee20" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.38", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd7e48ccf166952882ca8bd778a43502c64f33bf94c12ebe2a7f08e5a0f6689f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", ] diff --git a/Cargo.toml b/Cargo.toml index 3efeab969..10a75a1e9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,7 @@ log = { version = "0.4.17", features = ["max_level_info", "release_max_level_inf packit = { git = "https://github.com/coconut-svsm/packit", version = "0.1.0" } aes-gcm = { version = "0.10.3", default-features = false, features = ["aes", "alloc"] } igvm_params = { path = "igvm_params" } +igvm_defs = { version = "0.1.0" } [target."x86_64-unknown-none".dev-dependencies] test = { version = "0.1.0", path = "test" } diff --git a/src/config.rs b/src/config.rs index b2763cf56..a0383f210 100644 --- a/src/config.rs +++ b/src/config.rs @@ -48,7 +48,7 @@ impl<'a> SvsmConfig<'a> { pub fn get_memory_regions(&self) -> Result>, SvsmError> { match self { SvsmConfig::FirmwareConfig(fw_cfg) => fw_cfg.get_memory_regions(), - &SvsmConfig::IgvmConfig(_) => todo!(), + SvsmConfig::IgvmConfig(igvm_params) => igvm_params.get_memory_regions(), } } pub fn load_cpu_info(&self) -> Result, SvsmError> { diff --git a/src/igvm_params.rs b/src/igvm_params.rs index cdf6d19cb..4605d52ce 100644 --- a/src/igvm_params.rs +++ b/src/igvm_params.rs @@ -4,17 +4,32 @@ // // Author: Jon Lange (jlange@microsoft.com) +extern crate alloc; + use crate::address::{PhysAddr, VirtAddr}; use crate::error::SvsmError; +use crate::error::SvsmError::Firmware; use crate::mm::PAGE_SIZE; use crate::utils::MemoryRegion; +use alloc::vec::Vec; +use core::mem::size_of; +use igvm_defs::{MemoryMapEntryType, IGVM_VHS_MEMORY_MAP_ENTRY}; use igvm_params::{IgvmParamBlock, IgvmParamPage}; +const IGVM_MEMORY_ENTRIES_PER_PAGE: usize = PAGE_SIZE / size_of::(); + +#[derive(Clone, Debug)] +#[repr(C, align(64))] +pub struct IgvmMemoryMap { + memory_map: [IGVM_VHS_MEMORY_MAP_ENTRY; IGVM_MEMORY_ENTRIES_PER_PAGE], +} + #[derive(Clone, Debug)] pub struct IgvmParams<'a> { igvm_param_block: &'a IgvmParamBlock, igvm_param_page: &'a IgvmParamPage, + igvm_memory_map: &'a IgvmMemoryMap, } impl IgvmParams<'_> { @@ -22,10 +37,13 @@ impl IgvmParams<'_> { let param_block = unsafe { &*addr.as_ptr::() }; let param_page_address = addr + param_block.param_page_offset.try_into().unwrap(); let param_page = unsafe { &*param_page_address.as_ptr::() }; + let memory_map_address = addr + param_block.memory_map_offset.try_into().unwrap(); + let memory_map = unsafe { &*memory_map_address.as_ptr::() }; Self { igvm_param_block: param_block, igvm_param_page: param_page, + igvm_memory_map: memory_map, } } @@ -53,4 +71,43 @@ impl IgvmParams<'_> { pub fn get_secrets_page_address(&self) -> u64 { self.igvm_param_block.secrets_page as u64 } + + pub fn get_memory_regions(&self) -> Result>, SvsmError> { + // Count the number of memory entries present. They must be + // non-overlapping and strictly increasing. + let mut number_of_entries = 0; + let mut next_page_number = 0; + for i in 0..IGVM_MEMORY_ENTRIES_PER_PAGE { + let entry = &self.igvm_memory_map.memory_map[i]; + if entry.number_of_pages == 0 { + break; + } + if entry.starting_gpa_page_number < next_page_number { + return Err(Firmware); + } + let next_supplied_page_number = entry.starting_gpa_page_number + entry.number_of_pages; + if next_supplied_page_number < next_page_number { + return Err(Firmware); + } + next_page_number = next_supplied_page_number; + number_of_entries += 1; + } + + // Now loop over the supplied entires and add a region for each + // known type. + let mut regions: Vec> = Vec::new(); + for i in 0..number_of_entries { + let entry = &self.igvm_memory_map.memory_map[i]; + if entry.entry_type == MemoryMapEntryType::MEMORY { + let starting_page: usize = entry.starting_gpa_page_number.try_into().unwrap(); + let number_of_pages: usize = entry.number_of_pages.try_into().unwrap(); + regions.push(MemoryRegion::new( + PhysAddr::new(starting_page * PAGE_SIZE), + number_of_pages * PAGE_SIZE, + )); + } + } + + Ok(regions) + } } From 37da39bdba9f145fd241ea42174a0a7687d4d813 Mon Sep 17 00:00:00 2001 From: Jon Lange Date: Mon, 27 Nov 2023 15:15:46 -0800 Subject: [PATCH 6/8] Create ACPI CPU information from IGVM parameters Signed-off-by: Jon Lange --- src/config.rs | 2 +- src/igvm_params.rs | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/config.rs b/src/config.rs index a0383f210..ef6cb046a 100644 --- a/src/config.rs +++ b/src/config.rs @@ -54,7 +54,7 @@ impl<'a> SvsmConfig<'a> { pub fn load_cpu_info(&self) -> Result, SvsmError> { match self { SvsmConfig::FirmwareConfig(fw_cfg) => load_acpi_cpu_info(fw_cfg), - &SvsmConfig::IgvmConfig(_) => todo!(), + SvsmConfig::IgvmConfig(igvm_params) => igvm_params.load_cpu_info(), } } pub fn should_launch_fw(&self) -> bool { diff --git a/src/igvm_params.rs b/src/igvm_params.rs index 4605d52ce..669fa1736 100644 --- a/src/igvm_params.rs +++ b/src/igvm_params.rs @@ -6,6 +6,7 @@ extern crate alloc; +use crate::acpi::tables::ACPICPUInfo; use crate::address::{PhysAddr, VirtAddr}; use crate::error::SvsmError; use crate::error::SvsmError::Firmware; @@ -110,4 +111,16 @@ impl IgvmParams<'_> { Ok(regions) } + + pub fn load_cpu_info(&self) -> Result, SvsmError> { + let mut cpus: Vec = Vec::new(); + for i in 0..self.igvm_param_page.cpu_count { + let cpu = ACPICPUInfo { + apic_id: i, + enabled: true, + }; + cpus.push(cpu); + } + Ok(cpus) + } } From 89cfa27598364069cfcc6152070a13127a1e36ec Mon Sep 17 00:00:00 2001 From: Jon Lange Date: Tue, 28 Nov 2023 03:09:47 -0800 Subject: [PATCH 7/8] Optionally launch FW when configured by IGVM parameters Signed-off-by: Jon Lange --- src/config.rs | 2 +- src/igvm_params.rs | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/config.rs b/src/config.rs index ef6cb046a..dc03676fe 100644 --- a/src/config.rs +++ b/src/config.rs @@ -60,7 +60,7 @@ impl<'a> SvsmConfig<'a> { pub fn should_launch_fw(&self) -> bool { match self { SvsmConfig::FirmwareConfig(_) => true, - SvsmConfig::IgvmConfig(_) => false, + SvsmConfig::IgvmConfig(igvm_params) => igvm_params.should_launch_fw(), } } } diff --git a/src/igvm_params.rs b/src/igvm_params.rs index 669fa1736..0d2864e9b 100644 --- a/src/igvm_params.rs +++ b/src/igvm_params.rs @@ -123,4 +123,8 @@ impl IgvmParams<'_> { } Ok(cpus) } + + pub fn should_launch_fw(&self) -> bool { + self.igvm_param_block.launch_fw != 0 + } } From d5d232ca793697be75dadd9bc39ff2adfa72ea90 Mon Sep 17 00:00:00 2001 From: Jon Lange Date: Sun, 10 Dec 2023 13:58:35 -0800 Subject: [PATCH 8/8] Reserve space at the base of the kernel area for IGVM info such as the initial VMSA Signed-off-by: Jon Lange --- igvm_params/src/lib.rs | 6 +++++- src/igvm_params.rs | 7 +++++++ src/stage2.rs | 3 +++ src/svsm_paging.rs | 30 ++++++++++++++++++++++++------ 4 files changed, 39 insertions(+), 7 deletions(-) diff --git a/igvm_params/src/lib.rs b/igvm_params/src/lib.rs index 253badfc9..3b429226c 100644 --- a/igvm_params/src/lib.rs +++ b/igvm_params/src/lib.rs @@ -56,7 +56,11 @@ pub struct IgvmParamBlock { /// to launch guest firmware once kernel initialization is complete. pub launch_fw: u8, - _reserved: [u8; 7], + _reserved: [u8; 3], + + /// The amount of space that must be reserved at the base of the kernel + /// 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, diff --git a/src/igvm_params.rs b/src/igvm_params.rs index 0d2864e9b..571463743 100644 --- a/src/igvm_params.rs +++ b/src/igvm_params.rs @@ -61,6 +61,13 @@ impl IgvmParams<'_> { Ok(MemoryRegion::::new(kernel_base, kernel_size)) } + pub fn reserved_kernel_area_size(&self) -> usize { + self.igvm_param_block + .kernel_reserved_size + .try_into() + .unwrap() + } + pub fn page_state_change_required(&self) -> bool { self.igvm_param_page.default_shared_pages != 0 } diff --git a/src/stage2.rs b/src/stage2.rs index 657ade241..fdc023f29 100755 --- a/src/stage2.rs +++ b/src/stage2.rs @@ -197,6 +197,9 @@ pub extern "C" fn stage2_main(launch_info: &Stage1LaunchInfo) { // IGVM parameters. Otherwise, it will be the base of the kernel // region. let mut loaded_kernel_phys_end = kernel_region_phys_start; + if let SvsmConfig::IgvmConfig(ref igvm_params) = config { + loaded_kernel_phys_end = loaded_kernel_phys_end + igvm_params.reserved_kernel_area_size(); + } // Map, validate and populate the SVSM kernel ELF's PT_LOAD segments. The // segments' virtual address range might not necessarily be contiguous, diff --git a/src/svsm_paging.rs b/src/svsm_paging.rs index 7e0457a5d..04650d205 100644 --- a/src/svsm_paging.rs +++ b/src/svsm_paging.rs @@ -18,14 +18,35 @@ use crate::sev::ghcb::PageStateChangeOp; use crate::sev::{pvalidate, PvalidateOp}; use crate::types::{PageSize, PAGE_SIZE}; +struct IgvmParamInfo<'a> { + virt_addr: VirtAddr, + igvm_params: Option>, +} + pub fn init_page_table(launch_info: &KernelLaunchInfo, kernel_elf: &elf::Elf64File) { let vaddr = mm::alloc::allocate_zeroed_page().expect("Failed to allocate root page-table"); let mut pgtable = PageTableRef::new(unsafe { &mut *vaddr.as_mut_ptr::() }); + let igvm_param_info = if launch_info.igvm_params_virt_addr != 0 { + let addr = VirtAddr::from(launch_info.igvm_params_virt_addr); + IgvmParamInfo { + virt_addr: addr, + igvm_params: Some(IgvmParams::new(addr)), + } + } else { + IgvmParamInfo { + virt_addr: VirtAddr::null(), + igvm_params: None, + } + }; // Install mappings for the kernel's ELF segments each. // The memory backing the kernel ELF segments gets allocated back to back // from the physical memory region by the Stage2 loader. let mut phys = PhysAddr::from(launch_info.kernel_region_phys_start); + if let Some(ref igvm_params) = igvm_param_info.igvm_params { + phys = phys + igvm_params.reserved_kernel_area_size(); + } + for segment in kernel_elf.image_load_segment_iter(launch_info.kernel_region_virt_start) { let vaddr_start = VirtAddr::from(segment.vaddr_range.vaddr_begin); let vaddr_end = VirtAddr::from(segment.vaddr_range.vaddr_end); @@ -47,14 +68,11 @@ pub fn init_page_table(launch_info: &KernelLaunchInfo, kernel_elf: &elf::Elf64Fi } // Map the IGVM parameters if present. - if launch_info.igvm_params_virt_addr != 0 { - let igvm_params_virt_addr = VirtAddr::from(launch_info.igvm_params_virt_addr); - let igvm_params = IgvmParams::new(igvm_params_virt_addr); - + if let Some(ref igvm_params) = igvm_param_info.igvm_params { pgtable .map_region( - igvm_params_virt_addr, - igvm_params_virt_addr + igvm_params.size(), + igvm_param_info.virt_addr, + igvm_param_info.virt_addr + igvm_params.size(), PhysAddr::from(launch_info.igvm_params_phys_addr), PTEntryFlags::data(), )