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
2 changes: 1 addition & 1 deletion litebox_platform_lvbs/src/arch/x86/ioport.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use core::{arch::asm, fmt};
use spin::{Mutex, Once};

// LVBS uses COM PORT 2 for printing out debug messages
const COM_PORT_2: u16 = 0x2F8;
const COM_PORT_2: u16 = 0x3F8;

const INTERRUPT_ENABLE_OFFSET: u16 = 1;
const OUT_FIFO_CONTROL_OFFSET: u16 = 2;
Expand Down
183 changes: 16 additions & 167 deletions litebox_platform_lvbs/src/host/bootparam.rs
Original file line number Diff line number Diff line change
@@ -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<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() } {
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<u32, VtlMemoryError> {
pub fn get_num_possible_cpus() -> Result<u32, Errno> {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems that no one is using this function anymore.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes you are right. Would mind though if we keep this a little while longer ,please ? I want to do the multi instance support for TA and dynamic TA loading and if we still do not need it, we can remove it

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);
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
}
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 @@ -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;

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

/// 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, VsmError> {
pub fn mshv_vsm_boot_aps(cpu_online_mask_pfn: u64) -> Result<i64, VsmError> {
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::<CpuMask>(cpu_online_mask_page_addr)
Expand All @@ -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::<AlignedPage>(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 {
Expand All @@ -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::<AlignedPage>(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
Expand Down Expand Up @@ -920,7 +898,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: Result<i64, VsmError> = 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
28 changes: 4 additions & 24 deletions litebox_platform_lvbs/src/mshv/vtl1_mem_layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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;
Expand Down Expand Up @@ -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,
}
Loading
Loading