Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
183 changes: 18 additions & 165 deletions litebox_platform_lvbs/src/host/bootparam.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,198 +3,51 @@

//! VTL1 kernel boot parameters (compatible with Linux kernel's boot_params structure and command line)

use crate::{
debug_serial_println,
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 crate::debug_serial_println;
use litebox_common_linux::errno::Errno;
use spin::Once;

// 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<u32> = 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() } {
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<u32, VtlMemoryError> {
pub fn get_num_possible_cpus() -> Result<u32, Errno> {
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::<u32>().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 as u32);
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 parse_boot_info(possible_cpus: u32, mem_pa: u64, mem_size: u64) {
debug_serial_println!("VTL1 Boot Info possible_cpus: {}, mem_pa: {:#x}, mem_size: {:#x}", possible_cpus, mem_pa, mem_size );
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
}
2 changes: 0 additions & 2 deletions litebox_platform_lvbs/src/mshv/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,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;

Expand Down
36 changes: 5 additions & 31 deletions litebox_platform_lvbs/src/mshv/vsm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ 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_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,
Expand Down Expand Up @@ -116,13 +116,10 @@ pub fn mshv_vsm_enable_aps(_cpu_present_mask_pfn: u64) -> Result<i64, Errno> {

/// 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<i64, Errno> {
pub fn mshv_vsm_boot_aps(cpu_online_mask_pfn: u64) -> Result<i64, Errno> {
debug_serial_println!("VSM: Boot APs");
let cpu_online_mask_page_addr =
PhysAddr::try_new(cpu_online_mask_pfn << PAGE_SHIFT).map_err(|_| Errno::EINVAL)?;
let boot_signal_page_addr =
PhysAddr::try_new(boot_signal_pfn << PAGE_SHIFT).map_err(|_| Errno::EINVAL)?;

if let Some(cpu_mask) =
unsafe { crate::platform_low().copy_from_vtl0_phys::<CpuMask>(cpu_online_mask_page_addr) }
Expand All @@ -131,48 +128,25 @@ pub fn mshv_vsm_boot_aps(cpu_online_mask_pfn: u64, boot_signal_pfn: u64) -> Resu
cpu_mask.for_each_cpu(|cpu_id| {
debug_serial_print!("{}, ", cpu_id);
});
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::<AlignedPage>(boot_signal_page_addr)
}) else {
serial_println!("Failed to get boot signal page");
return Err(Errno::EINVAL);
};

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;
}
cpu_mask.for_each_cpu(|cpu_id| {
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 {
serial_println!("Failed to initialize one or more APs: {:?}", e);
return Err(Errno::EINVAL);
}

// Store the cpu_online_mask for later use
CPU_ONLINE_MASK.call_once(|| cpu_mask);

if unsafe {
crate::platform_low()
.copy_to_vtl0_phys::<AlignedPage>(boot_signal_page_addr, &boot_signal_page_buf)
} {
Ok(0)
} else {
serial_println!("Failed to copy boot signal page to VTL0");
Err(Errno::EINVAL)
}
Ok(0)
} else {
serial_println!("Failed to get cpu_online_mask");
Err(Errno::EINVAL)
Expand Down Expand Up @@ -974,7 +948,7 @@ fn mshv_vsm_allocate_ringbuffer_memory(phys_addr: u64, size: usize) -> Result<i6
pub fn vsm_dispatch(func_id: VsmFunction, params: &[u64]) -> i64 {
let 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]),
Expand Down
14 changes: 1 addition & 13 deletions litebox_platform_lvbs/src/mshv/vtl1_mem_layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,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
Expand All @@ -25,10 +24,6 @@ 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;

// TODO: get addresses from VTL call params rather than use these static indexes
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;
Expand All @@ -52,11 +47,4 @@ pub fn get_heap_start_address() -> u64 {
#[inline]
pub fn get_address_of_special_page(page: usize) -> u64 {
get_memory_base_address() + (page as u64) * PAGE_SIZE as u64
}

/// Error for VSM memory
#[derive(Debug, PartialEq)]
pub enum VtlMemoryError {
InvalidBootParams,
InvalidCmdLine,
}
}
11 changes: 7 additions & 4 deletions litebox_runner_lvbs/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,11 +118,17 @@ unsafe fn apply_relocations() {

#[expect(clippy::missing_safety_doc)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn _start() -> ! {
pub unsafe extern "C" fn _start(possible_cpus: u64, mem_pa: u64, mem_size: u64) -> ! {
// possible_cpus and start memory address and vtl1 memory size are captured
// from rdi/rsi/rdx immediately as function parameters, before any code runs.
// They're stored on the stack and survive relocations (which only patch binary
// addresses, not stack).

let core_id = get_core_id();
if core_id == 0 {
unsafe {
apply_relocations();
parse_boot_info(possible_cpus as u32, mem_pa, mem_size);
}
}

Expand All @@ -148,10 +154,7 @@ unsafe extern "C" fn kernel_main() -> ! {
serial_println!("==============================");
serial_println!(" Hello from LiteBox for LVBS! ");
serial_println!("==============================");

parse_boot_info();
}

let platform = litebox_runner_lvbs::init();
litebox_runner_lvbs::run(platform)
}
Loading