diff --git a/Cargo.lock b/Cargo.lock index c75d3eef42..2b977f5d10 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3399,7 +3399,6 @@ dependencies = [ "thiserror 2.0.12", "tracing", "tracing-subscriber", - "underhill_confidentiality", "vbs_defs", "x86defs", "zerocopy 0.8.24", @@ -4828,6 +4827,7 @@ dependencies = [ "sha2", "sidecar_defs", "tdcall", + "tdx_guest_device", "underhill_confidentiality", "x86defs", "zerocopy 0.8.24", @@ -6728,6 +6728,7 @@ version = "0.0.0" dependencies = [ "hvdef", "memory_range", + "tdx_guest_device", "thiserror 2.0.12", "tracing", "x86defs", @@ -6737,6 +6738,7 @@ dependencies = [ name = "tdx_guest_device" version = "0.0.0" dependencies = [ + "bitfield-struct 0.10.1", "nix 0.27.1", "static_assertions", "thiserror 2.0.12", diff --git a/openhcl/openhcl_attestation_protocol/Cargo.toml b/openhcl/openhcl_attestation_protocol/Cargo.toml index dca610aae4..2d13f41811 100644 --- a/openhcl/openhcl_attestation_protocol/Cargo.toml +++ b/openhcl/openhcl_attestation_protocol/Cargo.toml @@ -11,7 +11,7 @@ open_enum.workspace = true guid.workspace = true mesh.workspace = true sev_guest_device.workspace = true -tdx_guest_device.workspace = true +tdx_guest_device = { workspace = true, features = ["std"] } base64.workspace = true base64-serde.workspace = true diff --git a/openhcl/openhcl_boot/Cargo.toml b/openhcl/openhcl_boot/Cargo.toml index f8d8a70e0a..8a6369823c 100644 --- a/openhcl/openhcl_boot/Cargo.toml +++ b/openhcl/openhcl_boot/Cargo.toml @@ -30,6 +30,7 @@ zerocopy.workspace = true [target.'cfg(target_arch = "x86_64")'.dependencies] safe_intrinsics.workspace = true tdcall.workspace = true +tdx_guest_device.workspace = true x86defs.workspace = true [build-dependencies] diff --git a/openhcl/openhcl_boot/src/arch/x86_64/tdx.rs b/openhcl/openhcl_boot/src/arch/x86_64/tdx.rs index df6b57917f..9b351a9d33 100644 --- a/openhcl/openhcl_boot/src/arch/x86_64/tdx.rs +++ b/openhcl/openhcl_boot/src/arch/x86_64/tdx.rs @@ -13,6 +13,8 @@ use tdcall::Tdcall; use tdcall::TdcallInput; use tdcall::TdcallOutput; use tdcall::tdcall_map_gpa; +use tdx_guest_device::protocol::TdReport; +use x86defs::tdx::TdCallResult; /// Perform a tdcall instruction with the specified inputs. fn tdcall(input: TdcallInput) -> TdcallOutput { @@ -119,3 +121,8 @@ pub fn get_tdx_tsc_reftime() -> Option { } None } + +/// Gets the TdReport. +pub fn get_tdreport(report: &mut TdReport) -> Result<(), TdCallResult> { + tdcall::tdcall_mr_report(&mut TdcallInstruction, report) +} diff --git a/openhcl/openhcl_boot/src/main.rs b/openhcl/openhcl_boot/src/main.rs index b649eb47b3..37548be213 100644 --- a/openhcl/openhcl_boot/src/main.rs +++ b/openhcl/openhcl_boot/src/main.rs @@ -73,6 +73,7 @@ fn build_kernel_command_line( cmdline: &mut ArrayString, partition_info: &PartitionInfo, can_trust_host: bool, + is_confidential_debug: bool, sidecar: Option<&SidecarConfig<'_>>, ) -> Result<(), CommandLineTooLong> { // For reference: @@ -254,6 +255,14 @@ fn build_kernel_command_line( )?; } + if is_confidential_debug { + write!( + cmdline, + "{}=1 ", + underhill_confidentiality::OPENHCL_CONFIDENTIAL_DEBUG_ENV_VAR_NAME + )?; + } + // Only when explicitly supported by Host. // TODO: Move from command line to device tree when stabilized. if partition_info.nvme_keepalive && !partition_info.vtl2_pool_memory.is_empty() { @@ -581,6 +590,29 @@ fn get_ref_time(isolation: IsolationType) -> Option { } } +fn get_hw_debug_bit(isolation: IsolationType) -> bool { + match isolation { + #[cfg(target_arch = "x86_64")] + IsolationType::Tdx => { + use tdx_guest_device::protocol::TdReport; + + use crate::arch::tdx::get_tdreport; + + let mut report = off_stack!(PageAlign, zeroed()); + match get_tdreport(&mut report.0) { + Ok(()) => report.0.td_info.td_info_base.attributes.debug(), + Err(_) => false, + } + } + #[cfg(target_arch = "x86_64")] + IsolationType::Snp => { + // Not implemented yet for SNP. + false + } + _ => false, + } +} + fn shim_main(shim_params_raw_offset: isize) -> ! { let p = shim_parameters(shim_params_raw_offset); if p.isolation_type == IsolationType::None { @@ -615,8 +647,10 @@ fn shim_main(shim_params_raw_offset: isize) -> ! { log!("openhcl_boot: early debugging enabled"); } - let can_trust_host = - p.isolation_type == IsolationType::None || static_options.confidential_debug; + let hw_debug_bit = get_hw_debug_bit(p.isolation_type); + let can_trust_host = p.isolation_type == IsolationType::None + || static_options.confidential_debug + || hw_debug_bit; let boot_reftime = get_ref_time(p.isolation_type); @@ -628,6 +662,12 @@ fn shim_main(shim_params_raw_offset: isize) -> ! { Err(e) => panic!("unable to read device tree params {}", e), }; + // Confidential debug will show up in boot_options only if included in the + // static command line, or if can_trust_host is true (so the dynamic command + // line has been parsed). + let is_confidential_debug = (can_trust_host && p.isolation_type != IsolationType::None) + || partition_info.boot_options.confidential_debug; + // Fill out the non-devicetree derived parts of PartitionInfo. if !p.isolation_type.is_hardware_isolated() && hvcall().vtl() == Vtl::Vtl2 @@ -697,6 +737,7 @@ fn shim_main(shim_params_raw_offset: isize) -> ! { &mut cmdline, partition_info, can_trust_host, + is_confidential_debug, sidecar.as_ref(), ) .unwrap(); diff --git a/openhcl/underhill_core/src/worker.rs b/openhcl/underhill_core/src/worker.rs index 3aafe3a8b4..fc61ea99e8 100644 --- a/openhcl/underhill_core/src/worker.rs +++ b/openhcl/underhill_core/src/worker.rs @@ -127,6 +127,7 @@ use tracing::Instrument; use tracing::instrument; use uevent::UeventListener; use underhill_attestation::AttestationType; +use underhill_confidentiality::confidential_debug_enabled; use underhill_threadpool::AffinitizedThreadpool; use underhill_threadpool::ThreadpoolBuilder; use virt::Partition; @@ -1589,6 +1590,10 @@ async fn new_underhill_vm( ); } + if confidential_debug_enabled() { + tracing::warn!(CVM_ALLOWED, "confidential debug enabled"); + } + // Create the `AttestationVmConfig` from `dps`, which will be used in // - stateful mode (the attestation is not suppressed) // - stateless mode (isolated VM with attestation suppressed) diff --git a/support/tdx_guest_device/Cargo.toml b/support/tdx_guest_device/Cargo.toml index e2c2f870f1..46e515808c 100644 --- a/support/tdx_guest_device/Cargo.toml +++ b/support/tdx_guest_device/Cargo.toml @@ -6,7 +6,11 @@ name = "tdx_guest_device" edition.workspace = true rust-version.workspace = true +[features] +std = [] + [dependencies] +bitfield-struct.workspace = true static_assertions.workspace = true zerocopy.workspace = true [target.'cfg(target_os = "linux")'.dependencies] diff --git a/support/tdx_guest_device/src/ioctl.rs b/support/tdx_guest_device/src/ioctl.rs index b0b5206f5b..d18bfd5fe1 100644 --- a/support/tdx_guest_device/src/ioctl.rs +++ b/support/tdx_guest_device/src/ioctl.rs @@ -3,6 +3,7 @@ //! The module implements the Linux TDX Guest APIs based on ioctl. +#![cfg(feature = "std")] // UNSAFETY: unsafe needed to make ioctl calls. #![expect(unsafe_code)] diff --git a/support/tdx_guest_device/src/lib.rs b/support/tdx_guest_device/src/lib.rs index 039dec9822..4e54deab80 100644 --- a/support/tdx_guest_device/src/lib.rs +++ b/support/tdx_guest_device/src/lib.rs @@ -4,6 +4,8 @@ //! The crate includes the abstraction layer of Linux TDX Guest APIs and //! definitions of data structures according to TDX specification. +#![cfg_attr(not(feature = "std"), no_std)] + pub mod protocol; #[cfg(target_os = "linux")] diff --git a/support/tdx_guest_device/src/protocol.rs b/support/tdx_guest_device/src/protocol.rs index b57ffe5d14..75de30c57a 100644 --- a/support/tdx_guest_device/src/protocol.rs +++ b/support/tdx_guest_device/src/protocol.rs @@ -3,6 +3,7 @@ //! The module includes the definitions of data structures according to TDX specification. +use bitfield_struct::bitfield; use zerocopy::FromBytes; use zerocopy::Immutable; use zerocopy::IntoBytes; @@ -135,12 +136,52 @@ pub struct TdInfo { /// Run-time extendable measurement register. pub type Rtmr = [u8; 48]; +/// See `ATTRIBUTES` in Table 3.9, "Intel TDX Module v1.5 ABI specification", March 2024. +#[bitfield(u64)] +#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)] +pub struct TdAttributes { + #[bits(1)] + pub debug: bool, + #[bits(3)] + _reserved1: u8, + #[bits(1)] + pub hgs_plus_prof: bool, + #[bits(1)] + pub perf_prof: bool, + #[bits(1)] + pub pmt_prof: bool, + #[bits(9)] + _reserved2: u16, + #[bits(7)] + _reserved_p: u8, + #[bits(4)] + _reserved_n: u8, + #[bits(1)] + pub lass: bool, + #[bits(1)] + pub sept_ve_disable: bool, + #[bits(1)] + pub migratable: bool, + #[bits(1)] + pub pks: bool, + #[bits(1)] + pub kl: bool, + #[bits(24)] + _reserved3: u32, + #[bits(6)] + _reserved4: u32, + #[bits(1)] + pub tpa: bool, + #[bits(1)] + pub perfmon: bool, +} + /// See `TDINFO_BASE` in Table 3.34, "Intel TDX Module v1.5 ABI specification", March 2024. #[repr(C)] #[derive(IntoBytes, Immutable, KnownLayout, FromBytes)] pub struct TdInfoBase { /// TD's attributes - pub attributes: [u8; 8], + pub attributes: TdAttributes, /// TD's XFAM pub xfam: [u8; 8], /// Measurement of the initial contents of the TDX in SHA384 diff --git a/support/tee_call/Cargo.toml b/support/tee_call/Cargo.toml index 28dc0015e5..db253e5356 100644 --- a/support/tee_call/Cargo.toml +++ b/support/tee_call/Cargo.toml @@ -8,7 +8,7 @@ rust-version.workspace = true [target.'cfg(target_os = "linux")'.dependencies] sev_guest_device.workspace = true -tdx_guest_device.workspace = true +tdx_guest_device = { workspace = true, features = ["std"] } static_assertions.workspace = true thiserror.workspace = true diff --git a/vm/loader/igvmfilegen/Cargo.toml b/vm/loader/igvmfilegen/Cargo.toml index d54cf19645..21cb1184ca 100644 --- a/vm/loader/igvmfilegen/Cargo.toml +++ b/vm/loader/igvmfilegen/Cargo.toml @@ -14,7 +14,6 @@ loader_defs.workspace = true hvdef.workspace = true memory_range.workspace = true -underhill_confidentiality.workspace = true vbs_defs.workspace = true x86defs.workspace = true diff --git a/vm/loader/igvmfilegen/src/main.rs b/vm/loader/igvmfilegen/src/main.rs index 70d917e496..ecee5cad8b 100644 --- a/vm/loader/igvmfilegen/src/main.rs +++ b/vm/loader/igvmfilegen/src/main.rs @@ -40,7 +40,6 @@ use std::io::Write; use std::path::PathBuf; use tracing_subscriber::EnvFilter; use tracing_subscriber::filter::LevelFilter; -use underhill_confidentiality::OPENHCL_CONFIDENTIAL_DEBUG_ENV_VAR_NAME; use zerocopy::FromBytes; use zerocopy::IntoBytes; @@ -633,20 +632,10 @@ fn load_image<'a, R: IgvmfilegenRegister + GuestArch + 'static>( } }; - let command_line = if loader.loader().confidential_debug() { - tracing::info!("enabling underhill confidential debug environment flag"); - format!( - "{command_line} {}=1", - OPENHCL_CONFIDENTIAL_DEBUG_ENV_VAR_NAME - ) - } else { - command_line.clone() - }; - let command_line = if static_command_line { - CommandLineType::Static(&command_line) + CommandLineType::Static(command_line) } else { - CommandLineType::HostAppendable(&command_line) + CommandLineType::HostAppendable(command_line) }; R::load_openhcl( diff --git a/vm/loader/manifests/openhcl-x64-cvm-dev.json b/vm/loader/manifests/openhcl-x64-cvm-dev.json index 148e3e9343..ba15e061c6 100644 --- a/vm/loader/manifests/openhcl-x64-cvm-dev.json +++ b/vm/loader/manifests/openhcl-x64-cvm-dev.json @@ -14,7 +14,7 @@ }, "image": { "openhcl": { - "command_line": "", + "command_line": "OPENHCL_CONFIDENTIAL_DEBUG=1", "memory_page_count": 163840, "memory_page_base": 32768, "uefi": true @@ -49,7 +49,7 @@ }, "image": { "openhcl": { - "command_line": "", + "command_line": "OPENHCL_CONFIDENTIAL_DEBUG=1", "memory_page_count": 163840, "memory_page_base": 32768, "uefi": true diff --git a/vm/loader/manifests/openhcl-x64-cvm-release.json b/vm/loader/manifests/openhcl-x64-cvm-release.json index 0e5e5142ad..14b0829ff7 100644 --- a/vm/loader/manifests/openhcl-x64-cvm-release.json +++ b/vm/loader/manifests/openhcl-x64-cvm-release.json @@ -14,7 +14,7 @@ }, "image": { "openhcl": { - "command_line": "", + "command_line": "OPENHCL_CONFIDENTIAL_DEBUG=1", "memory_page_count": 163840, "memory_page_base": 32768, "uefi": true @@ -49,7 +49,7 @@ }, "image": { "openhcl": { - "command_line": "", + "command_line": "OPENHCL_CONFIDENTIAL_DEBUG=1", "memory_page_count": 32768, "memory_page_base": 32768, "uefi": true diff --git a/vm/x86/tdcall/Cargo.toml b/vm/x86/tdcall/Cargo.toml index d2bc2afa8e..6a4bff98ad 100644 --- a/vm/x86/tdcall/Cargo.toml +++ b/vm/x86/tdcall/Cargo.toml @@ -13,9 +13,13 @@ tracing = ["dep:tracing"] [dependencies] hvdef.workspace = true memory_range.workspace = true +tdx_guest_device.workspace = true thiserror.workspace = true x86defs.workspace = true +[target.'cfg(target_os = "linux")'.dependencies] +tdx_guest_device = { workspace = true, features = ["std"] } + tracing = { workspace = true, optional = true } [lints] diff --git a/vm/x86/tdcall/src/lib.rs b/vm/x86/tdcall/src/lib.rs index 014e7d7535..adb4ee25c8 100644 --- a/vm/x86/tdcall/src/lib.rs +++ b/vm/x86/tdcall/src/lib.rs @@ -8,6 +8,7 @@ use hvdef::HV_PAGE_SIZE; use memory_range::MemoryRange; +use tdx_guest_device::protocol::TdReport; use thiserror::Error; use x86defs::tdx::TDX_SHARED_GPA_BOUNDARY_ADDRESS_BIT; use x86defs::tdx::TdCallLeaf; @@ -716,3 +717,37 @@ pub fn tdcall_vp_invgla( _ => Err(output.rax), } } + +#[repr(C, align(64))] +struct AddlData { + /// Report data buffer for TDG.MR.REPORT call. + pub report_data: [u8; 64], +} + +/// Issue a TDG.MR.REPORT call with empty additional data. +pub fn tdcall_mr_report(call: &mut impl Tdcall, report: &mut TdReport) -> Result<(), TdCallResult> { + let addl_data = AddlData { + report_data: [0; 64], + }; + + let input = TdcallInput { + leaf: TdCallLeaf::MR_REPORT, + rcx: core::ptr::from_mut::(report) as u64, + rdx: 0, + r8: 0, + r9: 0, + r10: 0, + r11: 0, + r12: addl_data.report_data.as_ptr() as u64, + r13: 0, + r14: 0, + r15: 0, + }; + + let output = call.tdcall(input); + + match output.rax.code() { + TdCallResultCode::SUCCESS => Ok(()), + _ => Err(output.rax), + } +}