diff --git a/bootlib/src/igvm_params.rs b/bootlib/src/igvm_params.rs index 419cea410..e362a28a6 100644 --- a/bootlib/src/igvm_params.rs +++ b/bootlib/src/igvm_params.rs @@ -39,6 +39,10 @@ pub struct IgvmParamBlock { /// of the memory map (which is in IGVM format). pub memory_map_offset: u32, + /// The offset, in bytes, of the guest context, or zero if no guest + /// context is present. + pub guest_context_offset: u32, + /// The guest physical address of the CPUID page. pub cpuid_page: u32, @@ -71,8 +75,6 @@ pub struct IgvmParamBlock { /// without parsing any metadata. pub fw_metadata: u32, - _reserved2: u32, - /// 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, @@ -83,3 +85,36 @@ pub struct IgvmParamBlock { /// The guest physical address of the base of the kernel memory region. pub kernel_base: u64, } + +/// The IGVM context page is a measured page that is used to specify the start +/// context for the guest VMPL. If present, it overrides the processor state +/// initialized at reset. +#[derive(Copy, Debug, Clone)] +#[repr(C, packed)] +pub struct IgvmGuestContext { + pub cr0: u64, + pub cr3: u64, + pub cr4: u64, + pub efer: u64, + pub gdt_base: u64, + pub gdt_limit: u32, + pub code_selector: u16, + pub data_selector: u16, + pub rip: u64, + pub rax: u64, + pub rcx: u64, + pub rdx: u64, + pub rbx: u64, + pub rsp: u64, + pub rbp: u64, + pub rsi: u64, + pub rdi: u64, + pub r8: u64, + pub r9: u64, + pub r10: u64, + pub r11: u64, + pub r12: u64, + pub r13: u64, + pub r14: u64, + pub r15: u64, +} diff --git a/igvmbld/igvmbld.c b/igvmbld/igvmbld.c index a0128b535..df67414cd 100644 --- a/igvmbld/igvmbld.c +++ b/igvmbld/igvmbld.c @@ -25,6 +25,7 @@ typedef struct { uint32_t param_area_size; uint32_t param_page_offset; uint32_t memory_map_offset; + uint32_t guest_context_offset; uint32_t cpuid_page; uint32_t secrets_page; uint16_t debug_serial_port; @@ -33,7 +34,6 @@ typedef struct { uint32_t fw_start; uint32_t fw_size; uint32_t fw_metadata; - uint32_t _reserved2; uint32_t kernel_reserved_size; uint32_t kernel_size; uint64_t kernel_base; diff --git a/src/config.rs b/src/config.rs index bd29e18e4..86251b415 100644 --- a/src/config.rs +++ b/src/config.rs @@ -15,6 +15,7 @@ use crate::mm::{PAGE_SIZE, SIZE_1G}; use crate::serial::SERIAL_PORT; use crate::utils::MemoryRegion; use alloc::vec::Vec; +use cpuarch::vmsa::VMSA; fn check_ovmf_regions( flash_regions: &[MemoryRegion], @@ -154,4 +155,11 @@ impl<'a> SvsmConfig<'a> { SvsmConfig::IgvmConfig(_) => true, } } + + pub fn initialize_guest_vmsa(&self, vmsa: &mut VMSA) { + match self { + SvsmConfig::FirmwareConfig(_) => (), + SvsmConfig::IgvmConfig(igvm_params) => igvm_params.initialize_guest_vmsa(vmsa), + } + } } diff --git a/src/igvm_params.rs b/src/igvm_params.rs index 00e50d267..257c98143 100644 --- a/src/igvm_params.rs +++ b/src/igvm_params.rs @@ -8,6 +8,7 @@ extern crate alloc; use crate::acpi::tables::ACPICPUInfo; use crate::address::{PhysAddr, VirtAddr}; +use crate::cpu::efer::EFERFlags; use crate::error::SvsmError; use crate::error::SvsmError::Firmware; use crate::mm::{PerCPUPageMappingGuard, PAGE_SIZE}; @@ -16,8 +17,9 @@ use crate::sev::{rmp_adjust, RMPFlags}; use crate::types::PageSize; use crate::utils::MemoryRegion; use alloc::vec::Vec; +use cpuarch::vmsa::VMSA; -use bootlib::igvm_params::{IgvmParamBlock, IgvmParamPage}; +use bootlib::igvm_params::{IgvmGuestContext, IgvmParamBlock, IgvmParamPage}; use core::mem::size_of; use igvm_defs::{IgvmEnvironmentInfo, MemoryMapEntryType, IGVM_VHS_MEMORY_MAP_ENTRY}; @@ -34,6 +36,7 @@ pub struct IgvmParams<'a> { igvm_param_block: &'a IgvmParamBlock, igvm_param_page: &'a IgvmParamPage, igvm_memory_map: &'a IgvmMemoryMap, + igvm_guest_context_address: VirtAddr, } impl IgvmParams<'_> { @@ -43,11 +46,17 @@ impl IgvmParams<'_> { 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::() }; + let guest_context_address = if param_block.guest_context_offset != 0 { + addr + param_block.guest_context_offset.try_into().unwrap() + } else { + VirtAddr::null() + }; Self { igvm_param_block: param_block, igvm_param_page: param_page, igvm_memory_map: memory_map, + igvm_guest_context_address: guest_context_address, } } @@ -202,4 +211,59 @@ impl IgvmParams<'_> { pub fn fw_in_low_memory(&self) -> bool { self.igvm_param_block.fw_in_low_memory != 0 } + + pub fn initialize_guest_vmsa(&self, vmsa: &mut VMSA) { + if self.igvm_param_block.guest_context_offset != 0 { + let guest_context = + unsafe { &*self.igvm_guest_context_address.as_ptr::() }; + + // Copy the specified registers into the VMSA. + vmsa.cr0 = guest_context.cr0; + vmsa.cr3 = guest_context.cr3; + vmsa.cr4 = guest_context.cr4; + vmsa.efer = guest_context.efer; + vmsa.rip = guest_context.rip; + vmsa.rax = guest_context.rax; + vmsa.rcx = guest_context.rcx; + vmsa.rdx = guest_context.rdx; + vmsa.rbx = guest_context.rbx; + vmsa.rsp = guest_context.rsp; + vmsa.rbp = guest_context.rbp; + vmsa.rsi = guest_context.rsi; + vmsa.rdi = guest_context.rdi; + vmsa.r8 = guest_context.r8; + vmsa.r9 = guest_context.r9; + vmsa.r10 = guest_context.r10; + vmsa.r11 = guest_context.r11; + vmsa.r12 = guest_context.r12; + vmsa.r13 = guest_context.r13; + vmsa.r14 = guest_context.r14; + vmsa.r15 = guest_context.r15; + vmsa.gdt.base = guest_context.gdt_base; + vmsa.gdt.limit = guest_context.gdt_limit; + vmsa.cs.selector = guest_context.code_selector; + + // Set the code segment attributes based on EFER.LMA. + let efer_lma = EFERFlags::LMA; + if (vmsa.efer & efer_lma.bits()) != 0 { + vmsa.cs.flags = 0xA9B; + } else { + vmsa.cs.flags = 0xC9B; + vmsa.cs.limit = 0xFFFFFFFF; + } + + let efer_svme = EFERFlags::SVME; + vmsa.efer &= !efer_svme.bits(); + + // Modify the data segment attributes to be compatible with + // protected mode. + vmsa.ds.selector = guest_context.data_selector; + vmsa.ds.flags = 0xA93; + vmsa.ds.limit = 0xFFFFFFFF; + vmsa.ss = vmsa.ds; + vmsa.es = vmsa.ds; + vmsa.fs = vmsa.ds; + vmsa.gs = vmsa.ds; + } + } } diff --git a/src/svsm.rs b/src/svsm.rs index fc228d1bf..484296b0f 100755 --- a/src/svsm.rs +++ b/src/svsm.rs @@ -206,10 +206,12 @@ fn prepare_fw_launch(fw_metadata: Option<&SevFWMetaData>) -> Result<(), SvsmErro Ok(()) } -fn launch_fw() -> Result<(), SvsmError> { +fn launch_fw(config: &SvsmConfig) -> Result<(), SvsmError> { let vmsa_pa = this_cpu_mut().guest_vmsa_ref().vmsa_phys().unwrap(); let vmsa = this_cpu_mut().guest_vmsa(); + config.initialize_guest_vmsa(vmsa); + log::info!("VMSA PA: {:#x}", vmsa_pa); let sev_features = vmsa.sev_features; @@ -461,7 +463,7 @@ pub extern "C" fn svsm_main() { virt_log_usage(); if config.should_launch_fw() { - if let Err(e) = launch_fw() { + if let Err(e) = launch_fw(&config) { panic!("Failed to launch FW: {:#?}", e); } }