diff --git a/litebox_platform_lvbs/src/host/bootparam.rs b/litebox_platform_lvbs/src/host/bootparam.rs index 2cd42b3bf..c8aaefbe3 100644 --- a/litebox_platform_lvbs/src/host/bootparam.rs +++ b/litebox_platform_lvbs/src/host/bootparam.rs @@ -1,200 +1,49 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -//! VTL1 kernel boot parameters (compatible with Linux kernel's boot_params structure and command line) - -use crate::mshv::vtl1_mem_layout::{ - VTL1_BOOT_PARAMS_PAGE, VTL1_CMDLINE_PAGE, VtlMemoryError, get_address_of_special_page, -}; -use core::ffi::{CStr, c_char}; -use num_enum::TryFromPrimitive; +use litebox_common_linux::errno::Errno; use spin::Once; -#[cfg(debug_assertions)] -use crate::debug_serial_println; - -// This module defines a simplified Linux boot params structure -// (based on arch/x86/include/uapi/asm/bootparam.h and -// arch/x86/include/uapi/asm/e820.h). We need this because VTL0 kernel -// passes memory information to VTL1 kernel via boot params. - -const E820_MAX_ENTRIES: usize = 128; - -const E820_RAM: u32 = 1; -const E820_RESERVED: u32 = 2; -const E820_ACPI: u32 = 3; -const E820_NVS: u32 = 4; -const E820_UNUSABLE: u32 = 5; -const E820_PMEM: u32 = 7; -const E820_PRAM: u32 = 12; -const E820_RESERVED_KERN: u32 = 128; - static POSSIBLE_CPUS: Once = Once::new(); static VTL1_MEMORY_INFO: Once<(u64, u64)> = Once::new(); -#[derive(Default, Clone, Copy)] -#[repr(C, packed)] -struct BootE820Entry { - pub addr: u64, - pub size: u64, - pub typ: u32, -} - -#[derive(Clone, Copy)] -#[repr(C, packed)] -struct BootParams { - _unused0: [u8; 720], // fields in this area are not used. - e820_table: [BootE820Entry; E820_MAX_ENTRIES], - _unused1: [u8; 816], // fields in this area are not used. -} - -impl BootParams { - pub fn new() -> Self { - Self { - _unused0: [0; 720], - e820_table: [BootE820Entry::default(); E820_MAX_ENTRIES], - _unused1: [0; 816], - } - } - - #[cfg(debug_assertions)] - pub fn dump(&self) { - for entry in self.e820_table { - let typ_val = entry.typ; - - if E820Type::try_from(typ_val).unwrap_or(E820Type::Unknown) == E820Type::Unknown { - break; - } else { - let addr_val = entry.addr; - let size_val = entry.size; - debug_serial_println!( - "addr: {:#x}, size: {:#x}, type: {:?}", - addr_val, - size_val, - typ_val - ); - } - } - } - - pub fn memory_info(&self) -> Result<(u64, u64), VtlMemoryError> { - for entry in self.e820_table { - let typ_val = entry.typ; - - match E820Type::try_from(typ_val).unwrap_or(E820Type::Unknown) { - E820Type::Ram => { - let addr_val = entry.addr; - let size_val = entry.size; - return Ok((addr_val, size_val)); - } - E820Type::Unknown => { - return Err(VtlMemoryError::InvalidBootParams); - } - _ => {} - } - } - - Err(VtlMemoryError::InvalidBootParams) - } -} - -impl Default for BootParams { - fn default() -> Self { - Self::new() - } -} - -#[cfg(debug_assertions)] -fn dump_boot_params() { - let boot_params = get_address_of_special_page(VTL1_BOOT_PARAMS_PAGE) as *const BootParams; - unsafe { - (*boot_params).dump(); - } -} - -#[cfg(debug_assertions)] -fn dump_cmdline() { - let cmdline = get_address_of_special_page(VTL1_CMDLINE_PAGE) as *const c_char; - if cmdline.is_null() { - return; - } - - if let Some(cmdline_str) = unsafe { CStr::from_ptr(cmdline).to_str().ok() } { - crate::debug_serial_println!("{}", cmdline_str); - } -} - /// Funtion to get the guest physical start address and size of VTL1 memory -pub fn get_vtl1_memory_info() -> Result<(u64, u64), VtlMemoryError> { +pub fn get_vtl1_memory_info() -> Result<(u64, u64), Errno> { if let Some(pair) = VTL1_MEMORY_INFO.get().copied() { Ok(pair) } else { - Err(VtlMemoryError::InvalidBootParams) + Err(Errno::EINVAL) } } /// Funtion to get the number of possible cpus from the command line (Linux kernel's num_possible_cpus()) -pub fn get_num_possible_cpus() -> Result { +pub fn get_num_possible_cpus() -> Result { if let Some(cpus) = POSSIBLE_CPUS.get() { Ok(*cpus) } else { - Err(VtlMemoryError::InvalidCmdLine) + Err(Errno::EINVAL) } } -fn save_vtl1_memory_info() -> Result<(), VtlMemoryError> { - let boot_params = get_address_of_special_page(VTL1_BOOT_PARAMS_PAGE) as *const BootParams; - if let Some((start, size)) = unsafe { (*boot_params).memory_info().ok() } { +fn save_vtl1_memory_info(start: u64, size: u64) -> Result<(), Errno> { + if start > 0 && size > 0 { VTL1_MEMORY_INFO.call_once(|| (start, size)); return Ok(()); } - Err(VtlMemoryError::InvalidBootParams) + Err(Errno::EINVAL) } -fn save_possible_cpus() -> Result<(), VtlMemoryError> { - let cmdline = get_address_of_special_page(VTL1_CMDLINE_PAGE) as *const c_char; - if cmdline.is_null() { - return Err(VtlMemoryError::InvalidCmdLine); - } - - if let Some(cmdline_str) = unsafe { CStr::from_ptr(cmdline).to_str().ok() } { - for token in cmdline_str.split_whitespace() { - if token.starts_with("possible_cpus=") - && let Some((_, v)) = token.split_once('=') - { - let num = v.parse::().unwrap_or(0); - if num > 0 { - POSSIBLE_CPUS.call_once(|| num); - return Ok(()); - } - } - } +fn save_possible_cpus(possible_cpus: u32) -> Result<(), Errno> { + if possible_cpus > 0 { + POSSIBLE_CPUS.call_once(|| possible_cpus); + return Ok(()); } - Err(VtlMemoryError::InvalidCmdLine) + Err(Errno::EINVAL) } /// # Panics /// /// Panics if possible cpus or vtl1 memory extraction fails -pub fn parse_boot_info() { - #[cfg(debug_assertions)] - dump_cmdline(); - #[cfg(debug_assertions)] - dump_boot_params(); - save_possible_cpus().unwrap(); // Panic if CPU extraction fails - save_vtl1_memory_info().unwrap(); // Panic if memory extraction fails -} - -/// E820 entry type -#[derive(Debug, PartialEq, TryFromPrimitive)] -#[repr(u32)] -enum E820Type { - Ram = E820_RAM, - Reserved = E820_RESERVED, - Acpi = E820_ACPI, - Nvs = E820_NVS, - Unusable = E820_UNUSABLE, - Pmem = E820_PMEM, - Pram = E820_PRAM, - ReservedKern = E820_RESERVED_KERN, - Unknown = 0xffff_ffff, +pub fn save_boot_info(possible_cpus: u32, mem_pa: u64, mem_size: u64) { + save_possible_cpus(possible_cpus).unwrap(); // Panic if CPU extraction fails + save_vtl1_memory_info(mem_pa, mem_size).unwrap(); // Panic if memory extraction fails } diff --git a/litebox_platform_lvbs/src/mshv/mod.rs b/litebox_platform_lvbs/src/mshv/mod.rs index 0da18875d..12f8a5207 100644 --- a/litebox_platform_lvbs/src/mshv/mod.rs +++ b/litebox_platform_lvbs/src/mshv/mod.rs @@ -111,8 +111,6 @@ pub const HV_REGISTER_CR_INTERCEPT_CR0_MASK: u32 = 0x000e_0001; pub const HV_REGISTER_CR_INTERCEPT_CR4_MASK: u32 = 0x000e_0002; pub const HV_REGISTER_PENDING_EVENT0: u32 = 0x0001_0004; -pub const HV_SECURE_VTL_BOOT_TOKEN: u8 = 0xdc; - /// VTL call parameters (`param[0]`: function ID, `param[1..4]`: parameters) pub const NUM_VTLCALL_PARAMS: usize = 4; diff --git a/litebox_platform_lvbs/src/mshv/vsm.rs b/litebox_platform_lvbs/src/mshv/vsm.rs index 483de17bb..f66989fa3 100644 --- a/litebox_platform_lvbs/src/mshv/vsm.rs +++ b/litebox_platform_lvbs/src/mshv/vsm.rs @@ -16,12 +16,12 @@ use crate::{ mshv::{ HV_REGISTER_CR_INTERCEPT_CONTROL, HV_REGISTER_CR_INTERCEPT_CR0_MASK, HV_REGISTER_CR_INTERCEPT_CR4_MASK, HV_REGISTER_VSM_PARTITION_CONFIG, - HV_REGISTER_VSM_VP_SECURE_CONFIG_VTL0, HV_SECURE_VTL_BOOT_TOKEN, HV_X64_REGISTER_APIC_BASE, - HV_X64_REGISTER_CR0, HV_X64_REGISTER_CR4, HV_X64_REGISTER_CSTAR, HV_X64_REGISTER_EFER, - HV_X64_REGISTER_LSTAR, HV_X64_REGISTER_SFMASK, HV_X64_REGISTER_STAR, - HV_X64_REGISTER_SYSENTER_CS, HV_X64_REGISTER_SYSENTER_EIP, HV_X64_REGISTER_SYSENTER_ESP, - HvCrInterceptControlFlags, HvPageProtFlags, HvRegisterVsmPartitionConfig, - HvRegisterVsmVpSecureVtlConfig, VsmFunction, X86Cr0Flags, X86Cr4Flags, + HV_REGISTER_VSM_VP_SECURE_CONFIG_VTL0, HV_X64_REGISTER_APIC_BASE, HV_X64_REGISTER_CR0, + HV_X64_REGISTER_CR4, HV_X64_REGISTER_CSTAR, HV_X64_REGISTER_EFER, HV_X64_REGISTER_LSTAR, + HV_X64_REGISTER_SFMASK, HV_X64_REGISTER_STAR, HV_X64_REGISTER_SYSENTER_CS, + HV_X64_REGISTER_SYSENTER_EIP, HV_X64_REGISTER_SYSENTER_ESP, HvCrInterceptControlFlags, + HvPageProtFlags, HvRegisterVsmPartitionConfig, HvRegisterVsmVpSecureVtlConfig, VsmFunction, + X86Cr0Flags, X86Cr4Flags, error::VsmError, heki::{ HekiKdataType, HekiKernelInfo, HekiKernelSymbol, HekiKexecType, HekiPage, HekiPatch, @@ -112,13 +112,10 @@ pub fn mshv_vsm_enable_aps(_cpu_present_mask_pfn: u64) -> Result /// VSM function for enabling VTL and booting APs /// `cpu_online_mask_pfn` indicates the page containing the VTL0's CPU online mask. -/// `boot_signal_pfn` indicates the boot signal page to let VTL0 know that VTL1 is ready. -pub fn mshv_vsm_boot_aps(cpu_online_mask_pfn: u64, boot_signal_pfn: u64) -> Result { +pub fn mshv_vsm_boot_aps(cpu_online_mask_pfn: u64) -> Result { debug_serial_println!("VSM: Boot APs"); let cpu_online_mask_page_addr = PhysAddr::try_new(cpu_online_mask_pfn << PAGE_SHIFT) .map_err(|_| VsmError::InvalidPhysicalAddress)?; - let boot_signal_page_addr = PhysAddr::try_new(boot_signal_pfn << PAGE_SHIFT) - .map_err(|_| VsmError::InvalidPhysicalAddress)?; let Some(cpu_mask) = (unsafe { crate::platform_low().copy_from_vtl0_phys::(cpu_online_mask_page_addr) @@ -135,26 +132,14 @@ pub fn mshv_vsm_boot_aps(cpu_online_mask_pfn: u64, boot_signal_pfn: u64) -> Resu debug_serial_println!(""); } - // boot_signal is an array of bytes whose length is the number of possible cores. Copy the entire page for now. - let Some(mut boot_signal_page_buf) = (unsafe { - crate::platform_low().copy_from_vtl0_phys::(boot_signal_page_addr) - }) else { - return Err(VsmError::BootSignalPageCopyFailed); - }; - let mut error = None; // Initialize VTL for each online CPU and update its boot signal byte cpu_mask.for_each_cpu(|cpu_id| { - if cpu_id > boot_signal_page_buf.0.len() - 1 { - error = Some(HypervCallError::InvalidInput); - return; - } let cpu_id_u32: u32 = cpu_id.truncate(); if let Err(e) = init_vtl_ap(cpu_id_u32) { error = Some(e); } - boot_signal_page_buf.0[cpu_id] = HV_SECURE_VTL_BOOT_TOKEN; }); if let Some(e) = error { @@ -164,14 +149,7 @@ pub fn mshv_vsm_boot_aps(cpu_online_mask_pfn: u64, boot_signal_pfn: u64) -> Resu // Store the cpu_online_mask for later use CPU_ONLINE_MASK.call_once(|| cpu_mask); - if unsafe { - crate::platform_low() - .copy_to_vtl0_phys::(boot_signal_page_addr, &boot_signal_page_buf) - } { - Ok(0) - } else { - Err(VsmError::BootSignalWriteFailed) - } + Ok(0) } /// VSM function for enforcing certain security features of VTL0 @@ -920,7 +898,7 @@ fn mshv_vsm_allocate_ringbuffer_memory(phys_addr: u64, size: usize) -> Result i64 { let result: Result = match func_id { VsmFunction::EnableAPsVtl => mshv_vsm_enable_aps(params[0]), - VsmFunction::BootAPs => mshv_vsm_boot_aps(params[0], params[1]), + VsmFunction::BootAPs => mshv_vsm_boot_aps(params[0]), VsmFunction::LockRegs => mshv_vsm_lock_regs(), VsmFunction::SignalEndOfBoot => Ok(mshv_vsm_end_of_boot()), VsmFunction::ProtectMemory => mshv_vsm_protect_memory(params[0], params[1]), diff --git a/litebox_platform_lvbs/src/mshv/vtl1_mem_layout.rs b/litebox_platform_lvbs/src/mshv/vtl1_mem_layout.rs index 9a87469cf..f64a2181c 100644 --- a/litebox_platform_lvbs/src/mshv/vtl1_mem_layout.rs +++ b/litebox_platform_lvbs/src/mshv/vtl1_mem_layout.rs @@ -3,8 +3,6 @@ //! VTL1 physical memory layout (LVBS-specific) -use thiserror::Error; - pub const PAGE_SIZE: usize = 4096; pub const PAGE_SHIFT: usize = 12; pub const PTES_PER_PAGE: usize = 512; @@ -13,7 +11,6 @@ pub const VSM_PMD_SIZE: usize = PAGE_SIZE * PTES_PER_PAGE; pub const VSM_SK_INITIAL_MAP_SIZE: usize = 16 * 1024 * 1024; pub const VSM_SK_PTE_PAGES_COUNT: usize = VSM_SK_INITIAL_MAP_SIZE / VSM_PMD_SIZE; -pub const VTL1_TOTAL_MEMORY_SIZE: usize = 128 * 1024 * 1024; pub const VTL1_PRE_POPULATED_MEMORY_SIZE: usize = VSM_SK_INITIAL_MAP_SIZE; // physical page frames specified by VTL0 kernel @@ -24,12 +21,15 @@ pub const VTL1_PDPE_PAGE: usize = 3; pub const VTL1_PDE_PAGE: usize = 4; pub const VTL1_PTE_0_PAGE: usize = 5; +// use this stack only for per-core VTL startup +pub const VTL1_KERNEL_STACK_PAGE: usize = VTL1_PTE_0_PAGE + VSM_SK_PTE_PAGES_COUNT; + /// PDPT page for the Phase 1 high-canonical PML4 entry. Placed after the /// VTL0-reserved special pages (GDT, TSS, PT pages, stack, boot params, /// cmdline) so that all 8 VTL0 PTE pages remain available for the /// high-canonical mapping. This page is within the VTL0 identity-mapped /// 16 MiB region but is otherwise unused memory. -pub const VTL1_REMAP_PDPT_PAGE: usize = VTL1_CMDLINE_PAGE + 1; +pub const VTL1_REMAP_PDPT_PAGE: usize = VTL1_KERNEL_STACK_PAGE + 1; /// PDE page for the Phase 1 high-canonical mapping. PDE entries point to /// all 8 VTL0 PTE pages (pages 5–12) to establish 4KB-granularity mappings @@ -47,16 +47,6 @@ const _: () = assert!( "Phase 1 remap assumes all PTE pages fit in a single PDE page" ); -// use this stack only for per-core VTL startup -pub const VTL1_KERNEL_STACK_PAGE: usize = VTL1_PTE_0_PAGE + VSM_SK_PTE_PAGES_COUNT; - -// TODO: VTL1_BOOT_PARAMS_PAGE and VTL1_CMDLINE_PAGE will be removed in an -// upcoming PR: boot parameters will be passed as arguments to _start() instead. -// When that lands, VTL1_REMAP_PDPT_PAGE should follow VTL1_KERNEL_STACK_PAGE -// directly. -pub const VTL1_BOOT_PARAMS_PAGE: usize = VTL1_KERNEL_STACK_PAGE + 1; -pub const VTL1_CMDLINE_PAGE: usize = VTL1_BOOT_PARAMS_PAGE + 1; - // initial heap to add the entire VTL1 physical memory to the kernel page table // We need ~256 KiB to cover the entire VTL1 physical memory (128 MiB) pub const VTL1_INIT_HEAP_START_PAGE: usize = 256; @@ -116,13 +106,3 @@ pub fn get_rela_start_address() -> u64 { pub fn get_rela_end_address() -> u64 { &raw const _rela_end as u64 } - -/// Errors for VTL memory operations. -#[derive(Debug, Error, PartialEq)] -#[non_exhaustive] -pub enum VtlMemoryError { - #[error("invalid boot parameters")] - InvalidBootParams, - #[error("invalid command line")] - InvalidCmdLine, -} diff --git a/litebox_runner_lvbs/src/main.rs b/litebox_runner_lvbs/src/main.rs index e2fd5b792..a744dbd09 100644 --- a/litebox_runner_lvbs/src/main.rs +++ b/litebox_runner_lvbs/src/main.rs @@ -10,7 +10,7 @@ use core::sync::atomic::{AtomicBool, Ordering}; use litebox_platform_lvbs::{ arch::{enable_extended_states, enable_fsgsbase, enable_smep_smap, instrs::hlt_loop}, host::{ - bootparam::parse_boot_info, + bootparam::save_boot_info, per_cpu_variables::{ PerCpuVariablesAsm, allocate_per_cpu_variables, init_per_cpu_variables, }, @@ -430,7 +430,8 @@ unsafe extern "C" fn common_start(is_bsp: bool) -> ! { /// context set up by `hvcall_enable_vp_vtl`. #[expect(clippy::missing_safety_doc)] #[unsafe(no_mangle)] -pub unsafe extern "C" fn _start() -> ! { +#[allow(clippy::cast_possible_truncation)] +pub unsafe extern "C" fn _start(possible_cpus: u64, mem_pa: u64, mem_size: u64) -> ! { unsafe { // Phase 1a: Fix GOT entries from link-time (base 0x0) to PA-based VAs. // The binary is linked at address 0x0 but loaded by VTL0 at an arbitrary @@ -438,7 +439,7 @@ pub unsafe extern "C" fn _start() -> ! { // that globals and function pointers resolve correctly under the // identity map (VA == PA) that VTL0 left us with. apply_relocations(); - + save_boot_info(possible_cpus as u32, mem_pa, mem_size); remap_to_high_canonical(); } } @@ -454,8 +455,6 @@ unsafe extern "C" fn kernel_main(is_bsp: bool) -> ! { serial_println!("=============================="); serial_println!(" Hello from LiteBox for LVBS! "); serial_println!("=============================="); - - parse_boot_info(); } let platform = litebox_runner_lvbs::init(is_bsp);