Skip to content

openhcl_boot: provide stronger safety guarantees in the hypercall fra… #1398

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
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
10 changes: 7 additions & 3 deletions openhcl/openhcl_boot/src/arch/aarch64/memory.rs
Original file line number Diff line number Diff line change
@@ -5,10 +5,14 @@
use crate::PartitionInfo;
use crate::ShimParams;
use crate::hvcall;
use crate::hypercall::HvCall;
use aarch64defs::IntermPhysAddrSize;

pub fn setup_vtl2_memory(_shim_params: &ShimParams, _partition_info: &PartitionInfo) {
pub fn setup_vtl2_memory(
hvcall: &mut HvCall,
_shim_params: &ShimParams,
_partition_info: &PartitionInfo,
) {
// TODO: memory acceptance isn't currently supported in the boot shim for aarch64.
let _ = _shim_params.bounce_buffer;

@@ -18,7 +22,7 @@ pub fn setup_vtl2_memory(_shim_params: &ShimParams, _partition_info: &PartitionI
.with_default_vtl_protection_mask(0xF)
.with_enable_vtl_protection(true);

hvcall()
hvcall
.set_register(
hvdef::HvArm64RegisterName::VsmPartitionConfig.into(),
hvdef::HvRegisterValue::from(u64::from(vsm_config)),
6 changes: 3 additions & 3 deletions openhcl/openhcl_boot/src/arch/aarch64/vp.rs
Original file line number Diff line number Diff line change
@@ -4,16 +4,16 @@
//! Setting up VTL2 VPs
use crate::PartitionInfo;
use crate::hypercall::hvcall;
use crate::hypercall::HvCall;

pub fn setup_vtl2_vp(partition_info: &PartitionInfo) {
pub fn setup_vtl2_vp(hvcall: &mut HvCall, partition_info: &PartitionInfo) {
// VTL2 kernel boot processor will try to remote read the GICR before AP's are
// brought up. But, the hypervisor doesn't set the GICR overlay pages until the
// Enable VP VTL hypercall has been made. Without the VP VTLs setup, accessing
// GICR will cause the VTL2 kernel to panic.
// BSP already has the VP VTL enabled at this time, so skip the BSP.
for cpu in 1..partition_info.cpus.len() {
hvcall()
hvcall
.enable_vp_vtl(cpu as u32)
.expect("Enabling VP VTL should not fail");
}
30 changes: 21 additions & 9 deletions openhcl/openhcl_boot/src/arch/x86_64/memory.rs
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@ use super::address_space::init_local_map;
use crate::ShimParams;
use crate::host_params::PartitionInfo;
use crate::host_params::shim_params::IsolationType;
use crate::hypercall::hvcall;
use crate::hypercall::HvCall;
use memory_range::MemoryRange;
use sha2::Digest;
use sha2::Sha384;
@@ -17,7 +17,11 @@ use x86defs::tdx::TDX_SHARED_GPA_BOUNDARY_ADDRESS_BIT;

/// On isolated systems, transitions all VTL2 RAM to be private and accepted, with the appropriate
/// VTL permissions applied.
pub fn setup_vtl2_memory(shim_params: &ShimParams, partition_info: &PartitionInfo) {
pub fn setup_vtl2_memory(
hvcall: &mut HvCall,
shim_params: &ShimParams,
partition_info: &PartitionInfo,
) {
// Only if the partition is VBS-isolated, accept memory and apply vtl 2 protections here.
// Non-isolated partitions can undergo servicing, and additional information
// would be needed to determine whether vtl 2 protections should be applied
@@ -35,7 +39,7 @@ pub fn setup_vtl2_memory(shim_params: &ShimParams, partition_info: &PartitionInf
.with_default_vtl_protection_mask(0xF)
.with_enable_vtl_protection(true);

hvcall()
hvcall
.set_register(
hvdef::HvX64RegisterName::VsmPartitionConfig.into(),
hvdef::HvRegisterValue::from(u64::from(vsm_config)),
@@ -56,7 +60,7 @@ pub fn setup_vtl2_memory(shim_params: &ShimParams, partition_info: &PartitionInf
partition_info.vtl2_ram.iter().map(|entry| entry.range),
accepted_ranges,
) {
hvcall()
hvcall
.apply_vtl2_protections(range)
.expect("applying vtl 2 protections cannot fail");
}
@@ -84,7 +88,7 @@ pub fn setup_vtl2_memory(shim_params: &ShimParams, partition_info: &PartitionInf
partition_info.vtl2_ram.iter().map(|e| e.range),
shim_params.imported_regions().map(|(r, _)| r),
) {
accept_vtl2_memory(shim_params, &mut local_map, range);
accept_vtl2_memory(hvcall, shim_params, &mut local_map, range);
}

let ram_buffer = if let Some(bounce_buffer) = shim_params.bounce_buffer {
@@ -95,7 +99,7 @@ pub fn setup_vtl2_memory(shim_params: &ShimParams, partition_info: &PartitionInf
core::iter::once(bounce_buffer),
partition_info.vtl2_ram.iter().map(|e| e.range),
) {
accept_vtl2_memory(shim_params, &mut local_map, range);
accept_vtl2_memory(hvcall, shim_params, &mut local_map, range);
}

// SAFETY: The bounce buffer is trusted as it is obtained from measured
@@ -115,20 +119,27 @@ pub fn setup_vtl2_memory(shim_params: &ShimParams, partition_info: &PartitionInf
// TODO: No VTL0 memory is currently marked as pending.
for (imported_range, already_accepted) in shim_params.imported_regions() {
if !already_accepted {
accept_pending_vtl2_memory(shim_params, &mut local_map, ram_buffer, imported_range);
accept_pending_vtl2_memory(
hvcall,
shim_params,
&mut local_map,
ram_buffer,
imported_range,
);
}
}
}

/// Accepts VTL2 memory in the specified gpa range.
fn accept_vtl2_memory(
hvcall: &mut HvCall,
shim_params: &ShimParams,
local_map: &mut Option<LocalMap<'_>>,
range: MemoryRange,
) {
match shim_params.isolation_type {
IsolationType::Vbs => {
hvcall()
hvcall
.accept_vtl2_pages(range, hvdef::hypercall::AcceptMemoryType::RAM)
.expect("accepting vtl 2 memory must not fail");
}
@@ -146,6 +157,7 @@ fn accept_vtl2_memory(
/// Accepts VTL2 memory in the specified range that is currently marked as pending, i.e. not
/// yet assigned as exclusive and private.
fn accept_pending_vtl2_memory(
hvcall: &mut HvCall,
shim_params: &ShimParams,
local_map: &mut Option<LocalMap<'_>>,
ram_buffer: &mut [u8],
@@ -155,7 +167,7 @@ fn accept_pending_vtl2_memory(

match isolation_type {
IsolationType::Vbs => {
hvcall()
hvcall
.accept_vtl2_pages(range, hvdef::hypercall::AcceptMemoryType::RAM)
.expect("accepting vtl 2 memory must not fail");
}
4 changes: 2 additions & 2 deletions openhcl/openhcl_boot/src/arch/x86_64/vp.rs
Original file line number Diff line number Diff line change
@@ -4,8 +4,8 @@
//! Setting up VTL2 VPs
use crate::host_params::PartitionInfo;

pub fn setup_vtl2_vp(_partition_info: &PartitionInfo) {
use crate::hypercall::HvCall;
pub fn setup_vtl2_vp(_: &mut HvCall, _partition_info: &PartitionInfo) {
// X64 doesn't require any special VTL2 VP setup in the boot loader at the
// moment.
}
428 changes: 275 additions & 153 deletions openhcl/openhcl_boot/src/hypercall.rs

Large diffs are not rendered by default.

39 changes: 12 additions & 27 deletions openhcl/openhcl_boot/src/main.rs
Original file line number Diff line number Diff line change
@@ -26,7 +26,7 @@ use crate::arch::tdx::get_tdx_tsc_reftime;
use crate::arch::verify_imported_regions_hash;
use crate::boot_logger::boot_logger_init;
use crate::boot_logger::log;
use crate::hypercall::hvcall;
use crate::hypercall::HvCall;
use crate::single_threaded::off_stack;
use arrayvec::ArrayString;
use arrayvec::ArrayVec;
@@ -586,23 +586,7 @@ fn shim_main(shim_params_raw_offset: isize) -> ! {
if p.isolation_type == IsolationType::None {
enable_enlightened_panic();
}

// The support code for the fast hypercalls does not set
// the Guest ID if it is not set yet as opposed to the slow
// hypercall code path where that is done automatically.
// Thus the fast hypercalls will fail as the the Guest ID has
// to be set first hence initialize hypercall support
// explicitly.
//
// In the hardware-isolated case, the hypervisor cannot
// access the guest registers so the fast hypercalls and
// any other methods of passing data to/from the hypervisor
// via the CPU registers (e.g. CPUID, hypercall call code or
// status) do not work, and the `hvcall()` doesn't have
// provisions for the hardware-isolated case.
if !p.isolation_type.is_hardware_isolated() {
hvcall().initialize();
}
let mut hvcall = HvCall::new(p.isolation_type);

// Enable early log output if requested in the static command line.
// Also check for confidential debug mode if we're isolated.
@@ -630,9 +614,9 @@ fn shim_main(shim_params_raw_offset: isize) -> ! {

// Fill out the non-devicetree derived parts of PartitionInfo.
if !p.isolation_type.is_hardware_isolated()
&& hvcall().vtl() == Vtl::Vtl2
&& hvcall.vtl() == Vtl::Vtl2
&& hvdef::HvRegisterVsmCapabilities::from(
hvcall()
hvcall
.get_register(hvdef::HvAllArchRegisterName::VsmCapabilities.into())
.expect("failed to query vsm capabilities")
.as_u64(),
@@ -676,10 +660,10 @@ fn shim_main(shim_params_raw_offset: isize) -> ! {
panic!("no cpus");
}

validate_vp_hw_ids(partition_info);
validate_vp_hw_ids(&mut hvcall, partition_info);

setup_vtl2_vp(partition_info);
setup_vtl2_memory(&p, partition_info);
setup_vtl2_vp(&mut hvcall, partition_info);
setup_vtl2_memory(&mut hvcall, &p, partition_info);
verify_imported_regions_hash(&p);

let mut sidecar_params = off_stack!(PageAlign<SidecarParams>, zeroed());
@@ -689,6 +673,7 @@ fn shim_main(shim_params_raw_offset: isize) -> ! {
partition_info,
&mut sidecar_params.0,
&mut sidecar_output.0,
hvcall.hypercall_page().unwrap_or(0),
);

let mut cmdline = off_stack!(ArrayString<COMMAND_LINE_SIZE>, ArrayString::new_const());
@@ -796,7 +781,7 @@ fn shim_main(shim_params_raw_offset: isize) -> ! {
rt::verify_stack_cookie();

log!("uninitializing hypercalls, about to jump to kernel");
hvcall().uninitialize();
hvcall.uninitialize();

cfg_if::cfg_if! {
if #[cfg(target_arch = "x86_64")] {
@@ -833,7 +818,7 @@ fn shim_main(shim_params_raw_offset: isize) -> ! {
/// Ensure that mshv VP indexes for the CPUs listed in the partition info
/// correspond to the N in the cpu@N devicetree node name. OpenVMM assumes that
/// this will be the case.
fn validate_vp_hw_ids(partition_info: &PartitionInfo) {
fn validate_vp_hw_ids(hvcall: &mut HvCall, partition_info: &PartitionInfo) {
use host_params::MAX_CPU_COUNT;
use hypercall::HwId;

@@ -849,7 +834,7 @@ fn validate_vp_hw_ids(partition_info: &PartitionInfo) {
return;
}

if hvcall().vtl() != Vtl::Vtl2 {
if hvcall.vtl() != Vtl::Vtl2 {
// If we're not using guest VSM, then the guest won't communicate
// directly with the hypervisor, so we can choose the VP indexes
// ourselves.
@@ -863,7 +848,7 @@ fn validate_vp_hw_ids(partition_info: &PartitionInfo) {
hw_ids.extend(partition_info.cpus.iter().map(|c| c.reg as _));
let mut vp_indexes = off_stack!(ArrayVec<u32, MAX_CPU_COUNT>, ArrayVec::new_const());
vp_indexes.clear();
if let Err(err) = hvcall().get_vp_index_from_hw_id(&hw_ids, &mut vp_indexes) {
if let Err(err) = hvcall.get_vp_index_from_hw_id(&hw_ids, &mut vp_indexes) {
panic!(
"failed to get VP index for hardware ID {:#x}: {}",
hw_ids[vp_indexes.len().min(hw_ids.len() - 1)],
7 changes: 2 additions & 5 deletions openhcl/openhcl_boot/src/sidecar.rs
Original file line number Diff line number Diff line change
@@ -63,6 +63,7 @@ pub fn start_sidecar<'a>(
partition_info: &PartitionInfo,
sidecar_params: &'a mut SidecarParams,
sidecar_output: &'a mut SidecarOutput,
hypercall_page_address: u64,
) -> Option<SidecarConfig<'a>> {
if !cfg!(target_arch = "x86_64") || p.isolation_type != IsolationType::None {
return None;
@@ -150,11 +151,7 @@ pub fn start_sidecar<'a>(
nodes,
} = sidecar_params;

*hypercall_page = 0;
#[cfg(target_arch = "x86_64")]
{
*hypercall_page = crate::hypercall::hvcall().hypercall_page();
}
*hypercall_page = hypercall_page_address;
*enable_logging = partition_info.boot_options.sidecar_logging;

let mut base_vp = 0;