From 1d4ffe6af3851644b50f448d9d6596840058a77c Mon Sep 17 00:00:00 2001 From: "alex.lyn" Date: Fri, 30 May 2025 17:45:06 +0800 Subject: [PATCH 1/7] runtime-rs: Implement serializable SocketAddress with Serde This enables consistent JSON representation of socket addresses across system components: (1) Add serde serialization/deserialization with standardized field naming convention. (2) Enforce string-based port/cid and unix/path representation for protocol compatibility. Signed-off-by: alex.lyn --- src/runtime-rs/crates/hypervisor/src/utils.rs | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/src/runtime-rs/crates/hypervisor/src/utils.rs b/src/runtime-rs/crates/hypervisor/src/utils.rs index 6a0e41bc00..d1fe47a5f1 100644 --- a/src/runtime-rs/crates/hypervisor/src/utils.rs +++ b/src/runtime-rs/crates/hypervisor/src/utils.rs @@ -16,6 +16,8 @@ use nix::{ fcntl, sched::{setns, CloneFlags}, }; +use serde::{Deserialize, Serialize}; +use serde_json; use crate::device::Tap; @@ -144,9 +146,56 @@ fn create_fds(device: &str, num_fds: usize) -> Result> { Ok(fds) } +// QGS_SOCKET_PATH: the Unix Domain Socket Path served by Intel TDX Quote Generation Service +const QGS_SOCKET_PATH: &str = "/var/run/tdx-qgs/qgs.socket"; + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct SocketAddress { + #[serde(rename = "type")] + pub typ: String, + + #[serde(rename = "cid", skip_serializing_if = "String::is_empty")] + pub cid: String, + + #[serde(rename = "port", skip_serializing_if = "String::is_empty")] + pub port: String, + + #[serde(rename = "path", skip_serializing_if = "String::is_empty")] + pub path: String, +} + +impl SocketAddress { + pub fn new(port: u32) -> Self { + if port == 0 { + Self { + typ: "unix".to_string(), + cid: "".to_string(), + port: "".to_string(), + path: QGS_SOCKET_PATH.to_string(), + } + } else { + Self { + typ: "vsock".to_string(), + cid: format!("{}", 2), + port: port.to_string(), + path: "".to_string(), + } + } + } +} + +impl std::fmt::Display for SocketAddress { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + serde_json::to_string(self) + .map_err(|_| std::fmt::Error) + .and_then(|s| write!(f, "{}", s)) + } +} + #[cfg(test)] mod tests { use super::create_fds; + use super::SocketAddress; #[test] fn test_ctreate_fds() { @@ -156,4 +205,43 @@ mod tests { assert!(fds.is_ok()); assert_eq!(fds.unwrap().len(), num_fds); } + + #[test] + fn test_vsocket_address_new() { + let socket = SocketAddress::new(8866); + assert_eq!(socket.typ, "vsock"); + assert_eq!(socket.cid, "2"); + assert_eq!(socket.port, "8866"); + } + + #[test] + fn test_unix_address_new() { + let socket = SocketAddress::new(0); + assert_eq!(socket.typ, "unix"); + assert_eq!(socket.path, "/var/run/tdx-qgs/qgs.socket"); + } + + #[test] + fn test_socket_address_display() { + let socket = SocketAddress::new(6688); + let expected_json = r#"{"type":"vsock","cid":"2","port":"6688"}"#; + assert_eq!(format!("{}", socket), expected_json); + } + + #[test] + fn test_socket_address_serialize_deserialize() { + let socket = SocketAddress::new(0); + let serialized = serde_json::to_string(&socket).unwrap(); + let expected_json = r#"{"type":"unix","path":"/var/run/tdx-qgs/qgs.socket"}"#; + assert_eq!(expected_json, serialized); + } + + #[test] + fn test_socket_address_kebab_case() { + let socket = SocketAddress::new(6868); + let serialized = serde_json::to_string(&socket).unwrap(); + assert!(serialized.contains(r#""type":"#)); + assert!(serialized.contains(r#""cid":"#)); + assert!(serialized.contains(r#""port":"#)); + } } From 09fddac2c4f8defd1c3242060d2ded731a59b37c Mon Sep 17 00:00:00 2001 From: "alex.lyn" Date: Wed, 23 Apr 2025 15:33:56 +0800 Subject: [PATCH 2/7] runtime-rs: Introduce 'tdx-guest' object and its builder for TDX CVMs This commit introduces the `tdx-guest` designed to facilitate the launch of CVMs leveraging Intel's TDX. Launching a TDX-based CVM requires various properties, including `quote-generation-socket`, and `mrconfigid`,`sept-ve-disable` .etc. (1) The `quote-generation-socket` property is added to the `tdx-guest` object, which is of type `SocketAddress`, specifies the address of the Quote Generation Service (QGS). (2) The `mrconfigid` property, representing the SHA384 hash for non-owner-defined configurations of the guest TD, is introduced as a runtime or OS configuration parameter. (3) And the `sept-ve-disable` property allows control over whether EPT violation conversions to #VE exceptions are disabled when the guest TD accesses PENDING pages. With the introduction of the `tdx-guest` object and its associated properties, launching TDX-based CVMs is now supported. For example, a TDX guest can be configured via the command line as follows: ```shell -object {"qom-type":"tdx-guest", "id":"tdx", "sept-ve-disable":true,\ "mrconfigid":"vHswGkzG4B3Kikg96sLQ5vPCYx4AtuB4Ubfzz9UOXvZtCGat8b8ok7Ubz4AxDDHh",\ "quote-generation-socket":{"type":"vsock","cid":"2","port":"4050"} \ -machine q35,accel=kvm,confidential-guest-support=tdx ``` Signed-off-by: alex.lyn --- .../hypervisor/src/qemu/cmdline_generator.rs | 103 +++++++++++++++++- 1 file changed, 102 insertions(+), 1 deletion(-) diff --git a/src/runtime-rs/crates/hypervisor/src/qemu/cmdline_generator.rs b/src/runtime-rs/crates/hypervisor/src/qemu/cmdline_generator.rs index 384753dd9e..41475a3bbe 100644 --- a/src/runtime-rs/crates/hypervisor/src/qemu/cmdline_generator.rs +++ b/src/runtime-rs/crates/hypervisor/src/qemu/cmdline_generator.rs @@ -4,12 +4,15 @@ // use crate::device::topology::{PCIePortBusPrefix, TopologyPortDevice, DEFAULT_PCIE_ROOT_BUS}; -use crate::utils::{clear_cloexec, create_vhost_net_fds, open_named_tuntap}; +use crate::utils::{clear_cloexec, create_vhost_net_fds, open_named_tuntap, SocketAddress}; + use crate::{kernel_param::KernelParams, Address, HypervisorConfig}; use anyhow::{anyhow, Context, Result}; use async_trait::async_trait; use kata_types::config::hypervisor::VIRTIO_SCSI; +use serde::{Deserialize, Serialize}; +use serde_json; use std::collections::HashMap; use std::fmt::Display; use std::fs::{read_to_string, File}; @@ -1825,6 +1828,87 @@ impl ToQemuParams for ObjectSevSnpGuest { } } +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "kebab-case")] +pub struct ObjectTdxGuest { + // QOM Object type + qom_type: String, + + // unique ID + id: String, + + // The sept-ve-disable option prevents EPT violation conversions to #VE on guest TD + // accesses of PENDING pages, which is essential for certain guest OS compatibility, + // like Linux TD guests. + sept_ve_disable: bool, + + // Base64 encoded 48 bytes of data (e.g., a sha384 digest). + // ID for non-owner-defined configuration of the guest TD, which identifies the guest TD's run-time/OS configuration via a SHA384 digest. + // Defaults to zero if unspecified. + #[serde(skip_serializing_if = "Option::is_none")] + mrconfigid: Option, + + // Base64 encoded 48 bytes of data (e.g., a sha384 digest). ID for the guest TD's owner. + // Defaults to all zeros. + #[serde(skip_serializing_if = "Option::is_none")] + mrowner: Option, + + // Base64 encoded 48 bytes of data (e.g., a sha384 digest). + // ID for owner-defined configuration of the guest TD, e.g., specific to the workload rather than the run-time or OS. + // Defaults to all zeros. + #[serde(skip_serializing_if = "Option::is_none")] + mrownerconfig: Option, + + // Quote generation socket. + #[serde(skip_serializing_if = "Option::is_none")] + quote_generation_socket: Option, + + // Debug mode + #[serde(skip_serializing_if = "Option::is_none")] + debug: Option, +} + +impl std::fmt::Display for ObjectTdxGuest { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + serde_json::to_string(self) + .map_err(|_| std::fmt::Error) + .and_then(|s| write!(f, "{}", s)) + } +} + +#[allow(clippy::doc_lazy_continuation)] +/// 1. Add property "quote-generation-socket" to tdx-guest +/// https://lore.kernel.org/qemu-devel/Zv7dtghi20DZ9ozz@redhat.com/ +/// 2. Support user configurable mrconfigid/mrowner/mrownerconfig +/// https://patchew.org/QEMU/20241105062408.3533704-1-xiaoyao.li@intel.com/20241105062408.3533704-15-xiaoyao.li@intel.com/ +/// 3. Add command line and validation for TDX type +/// https://lists.libvirt.org/archives/list/devel@lists.libvirt.org/message/6N7KP5F5Z44NI3R5U7STSPWUYXK6QYUO/ +/// Example: +/// -object { "qom-type": "tdx-guest","id": "tdx", "mrconfigid": "mrconfigid2", "debug":true,"sept-ve-disable":true, \ +/// "quote-generation-socket": { "type": "vsock","cid": "2","port": "4050" }} +impl ObjectTdxGuest { + pub fn new(id: &str, mrconfigid: Option, qgs_port: u32, debug: bool) -> Self { + let qgs_socket = SocketAddress::new(qgs_port); + Self { + qom_type: "tdx-guest".to_owned(), + id: id.to_owned(), + mrconfigid, + mrowner: None, + mrownerconfig: None, + sept_ve_disable: true, + quote_generation_socket: Some(qgs_socket), + debug: if debug { Some(debug) } else { None }, + } + } +} + +#[async_trait] +impl ToQemuParams for ObjectTdxGuest { + async fn qemu_params(&self) -> Result> { + Ok(vec!["-object".to_owned(), self.to_string()]) + } +} + /// PCIeRootPortDevice directly attached onto the root bus /// -device pcie-root-port,id=rp0,bus=pcie.0,chassis=0,slot=0,multifunction=off,pref64-reserve=B,mem-reserve=B #[derive(Debug, Default)] @@ -2354,6 +2438,23 @@ impl<'a> QemuCmdLine<'a> { self.cpu.set_type("EPYC-v4"); } + pub fn add_tdx_protection_device( + &mut self, + id: &str, + firmware: &str, + qgs_port: u32, + mrconfigid: &Option, + debug: bool, + ) { + let tdx_object = ObjectTdxGuest::new(id, mrconfigid.clone(), qgs_port, debug); + self.devices.push(Box::new(tdx_object)); + self.devices.push(Box::new(Bios::new(firmware.to_owned()))); + + self.machine + .set_confidential_guest_support("tdx") + .set_nvdimm(false); + } + /// Note: add_pcie_root_port and add_pcie_switch_port follow kata-runtime's related implementations of vfio devices. /// The design origins from https://github.com/qemu/qemu/blob/master/docs/pcie.txt /// From bab77e2d65443aca413c8a050c3c3e354124edff Mon Sep 17 00:00:00 2001 From: "alex.lyn" Date: Wed, 23 Apr 2025 16:41:07 +0800 Subject: [PATCH 3/7] runtime-rs: Introduce Tdx Protection Device and add it into cmdline This patch introduces TdxConfig with key fields, firmare, qgs_port, mrconfigid, and other useful things. With this config, a new ProtectionDeviceConfig type `Tdx(TdxConfig)` is added. With this new type supported, we finally add tdx protection device into the cmdline to launch a TDX-based CVM. Signed-off-by: alex.lyn --- .../src/device/driver/protection_device.rs | 15 +++++++++++++++ .../crates/hypervisor/src/qemu/inner.rs | 7 +++++++ 2 files changed, 22 insertions(+) diff --git a/src/runtime-rs/crates/hypervisor/src/device/driver/protection_device.rs b/src/runtime-rs/crates/hypervisor/src/device/driver/protection_device.rs index 33fa82c38b..83d0718967 100644 --- a/src/runtime-rs/crates/hypervisor/src/device/driver/protection_device.rs +++ b/src/runtime-rs/crates/hypervisor/src/device/driver/protection_device.rs @@ -14,6 +14,7 @@ use async_trait::async_trait; pub enum ProtectionDeviceConfig { SevSnp(SevSnpConfig), Se, + Tdx(TdxConfig), } #[derive(Debug, Clone)] @@ -23,6 +24,20 @@ pub struct SevSnpConfig { pub firmware: String, } +#[derive(Debug, Clone)] +pub struct TdxConfig { + // Object ID + pub id: String, + // Firmware path + pub firmware: String, + // Quote Qeneration Socket port + pub qgs_port: u32, + // mrconfigid + pub mrconfigid: Option, + // Debug mode + pub debug: bool, +} + #[derive(Debug, Clone)] pub struct ProtectionDevice { pub device_id: String, diff --git a/src/runtime-rs/crates/hypervisor/src/qemu/inner.rs b/src/runtime-rs/crates/hypervisor/src/qemu/inner.rs index 123067ea67..98cec4d8ba 100644 --- a/src/runtime-rs/crates/hypervisor/src/qemu/inner.rs +++ b/src/runtime-rs/crates/hypervisor/src/qemu/inner.rs @@ -145,6 +145,13 @@ impl QemuInner { } } ProtectionDeviceConfig::Se => cmdline.add_se_protection_device(), + ProtectionDeviceConfig::Tdx(tdx_config) => cmdline.add_tdx_protection_device( + &tdx_config.id, + &tdx_config.firmware, + tdx_config.qgs_port, + &tdx_config.mrconfigid, + tdx_config.debug, + ), }, DeviceType::PortDevice(port_device) => { let port_type = port_device.config.port_type; From 49ced4d43c806f3cb5bf54265016b331c5f62185 Mon Sep 17 00:00:00 2001 From: "alex.lyn" Date: Wed, 23 Apr 2025 17:09:45 +0800 Subject: [PATCH 4/7] runtime-rs: Prepare Tdx protection device in start sandbox During the prepare for `start sandbox` phase, this commit ensures the correct `ProtectionDeviceConfig` is prepared based on the `GuestProtection` type in a TEE platform. Specifically, for the TDX platform, this commit sets the essential parameters within the ProtectionDeviceConfig, including the TDX ID, firmware path, and the default QGS port (4050). This information is then passed to the underlying VMM for further processing using the existing ResourceManager and DeviceManager infrastructure. Signed-off-by: alex.lyn --- .../crates/hypervisor/src/device/driver/mod.rs | 2 +- .../crates/runtimes/virt_container/src/sandbox.rs | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/runtime-rs/crates/hypervisor/src/device/driver/mod.rs b/src/runtime-rs/crates/hypervisor/src/device/driver/mod.rs index e10d66c005..d1069dade7 100644 --- a/src/runtime-rs/crates/hypervisor/src/device/driver/mod.rs +++ b/src/runtime-rs/crates/hypervisor/src/device/driver/mod.rs @@ -16,7 +16,7 @@ mod virtio_net; mod virtio_vsock; pub use port_device::{PCIePortDevice, PortDeviceConfig}; -pub use protection_device::{ProtectionDevice, ProtectionDeviceConfig, SevSnpConfig}; +pub use protection_device::{ProtectionDevice, ProtectionDeviceConfig, SevSnpConfig, TdxConfig}; pub use vfio::{ bind_device_to_host, bind_device_to_vfio, get_vfio_device, HostDevice, VfioBusMode, VfioConfig, VfioDevice, diff --git a/src/runtime-rs/crates/runtimes/virt_container/src/sandbox.rs b/src/runtime-rs/crates/runtimes/virt_container/src/sandbox.rs index 8864bade94..09a6766aee 100644 --- a/src/runtime-rs/crates/runtimes/virt_container/src/sandbox.rs +++ b/src/runtime-rs/crates/runtimes/virt_container/src/sandbox.rs @@ -30,7 +30,7 @@ use hypervisor::{dragonball::Dragonball, HYPERVISOR_DRAGONBALL}; use hypervisor::{qemu::Qemu, HYPERVISOR_QEMU}; use hypervisor::{utils::get_hvsock_path, HybridVsockConfig, DEFAULT_GUEST_VSOCK_CID}; use hypervisor::{BlockConfig, Hypervisor}; -use hypervisor::{ProtectionDeviceConfig, SevSnpConfig}; +use hypervisor::{ProtectionDeviceConfig, SevSnpConfig, TdxConfig}; use kata_sys_util::hooks::HookStates; use kata_sys_util::protection::{available_guest_protection, GuestProtection}; use kata_types::capabilities::CapabilityBits; @@ -398,6 +398,15 @@ impl VirtSandbox { GuestProtection::Se => { Ok(Some(ProtectionDeviceConfig::Se)) } + GuestProtection::Tdx(_details) => { + Ok(Some(ProtectionDeviceConfig::Tdx(TdxConfig { + id: "tdx".to_owned(), + firmware: hypervisor_config.boot_info.firmware.clone(), + qgs_port: 4050, + mrconfigid: None, + debug: false, + }))) + }, _ => Err(anyhow!("confidential_guest requested by configuration but no supported protection available")) } } From f8d1ee8b1c9c575765f9c7bb12ad3c0b07f3104b Mon Sep 17 00:00:00 2001 From: "alex.lyn" Date: Tue, 27 May 2025 13:56:08 +0800 Subject: [PATCH 5/7] kata-types: Introduce QGS port for TD attestation in Hypervisor config Currently, the TDX Quote Generation Service (QGS) connection in QEMU is hardcoded to vsock port 4050, which limits flexibility for TD attestation. While the users will be able to modify the QGS port. To address this inflexibility, this commit introduces a new qgs_port field within security info and make it default with 4050. Signed-off-by: alex.lyn --- src/libs/kata-types/src/config/hypervisor/mod.rs | 11 +++++++++++ .../crates/runtimes/virt_container/src/sandbox.rs | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/libs/kata-types/src/config/hypervisor/mod.rs b/src/libs/kata-types/src/config/hypervisor/mod.rs index b5b0a26251..7c18d69143 100644 --- a/src/libs/kata-types/src/config/hypervisor/mod.rs +++ b/src/libs/kata-types/src/config/hypervisor/mod.rs @@ -890,6 +890,17 @@ pub struct SecurityInfo { /// e.g. "path" for io.katacontainers.config.hypervisor.path" #[serde(default)] pub enable_annotations: Vec, + + /// qgs_port defines Intel Quote Generation Service port exposed from the host + #[serde( + default = "default_qgs_port", + rename = "tdx_quote_generation_service_socket_port" + )] + pub qgs_port: u32, +} + +fn default_qgs_port() -> u32 { + 4050 } impl SecurityInfo { diff --git a/src/runtime-rs/crates/runtimes/virt_container/src/sandbox.rs b/src/runtime-rs/crates/runtimes/virt_container/src/sandbox.rs index 09a6766aee..2e5936d850 100644 --- a/src/runtime-rs/crates/runtimes/virt_container/src/sandbox.rs +++ b/src/runtime-rs/crates/runtimes/virt_container/src/sandbox.rs @@ -402,7 +402,7 @@ impl VirtSandbox { Ok(Some(ProtectionDeviceConfig::Tdx(TdxConfig { id: "tdx".to_owned(), firmware: hypervisor_config.boot_info.firmware.clone(), - qgs_port: 4050, + qgs_port: hypervisor_config.security_info.qgs_port, mrconfigid: None, debug: false, }))) From 8652aa7417a295257c547964d3446415c85dd050 Mon Sep 17 00:00:00 2001 From: "alex.lyn" Date: Tue, 27 May 2025 14:00:20 +0800 Subject: [PATCH 6/7] kata-types: Enable QGS port via configuration Currently, the TDX Quote Generation Service (QGS) connection in QEMU with default vsock port 4050 for TD attestation. To make it flexible for users to modify the QGS port. Based on the introduced qgs_port, This commit supports the QGS port to be configured via configuration Signed-off-by: alex.lyn --- src/runtime-rs/Makefile | 2 ++ .../config/configuration-qemu-runtime-rs.toml.in | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/src/runtime-rs/Makefile b/src/runtime-rs/Makefile index 909f7a050c..dc47cefc1c 100644 --- a/src/runtime-rs/Makefile +++ b/src/runtime-rs/Makefile @@ -176,6 +176,7 @@ DEFVFIOMODE := guest-kernel DEFBINDMOUNTS := [] DEFDANCONF := /run/kata-containers/dans DEFFORCEGUESTPULL := false +QEMUTDXQUOTEGENERATIONSERVICESOCKETPORT := 4050 SED = sed CLI_DIR = cmd SHIMV2 = containerd-shim-kata-v2 @@ -505,6 +506,7 @@ USER_VARS += KATA_INSTALL_OWNER USER_VARS += KATA_INSTALL_CFG_PERMS USER_VARS += DEFDANCONF USER_VARS += DEFFORCEGUESTPULL +USER_VARS += QEMUTDXQUOTEGENERATIONSERVICESOCKETPORT SOURCES := \ $(shell find . 2>&1 | grep -E '.*\.rs$$') \ diff --git a/src/runtime-rs/config/configuration-qemu-runtime-rs.toml.in b/src/runtime-rs/config/configuration-qemu-runtime-rs.toml.in index 88d14a7252..d3f3bd2f20 100644 --- a/src/runtime-rs/config/configuration-qemu-runtime-rs.toml.in +++ b/src/runtime-rs/config/configuration-qemu-runtime-rs.toml.in @@ -446,6 +446,18 @@ valid_entropy_sources = @DEFVALIDENTROPYSOURCES@ # Warnings will be logged if any error is encountered while scanning for hooks, # but it will not abort container execution. #guest_hook_path = "/usr/share/oci/hooks" + +# Enable connection to Quote Generation Service (QGS) +# The "tdx_quote_generation_service_socket_port" parameter configures how QEMU connects to the TDX Quote Generation Service (QGS). +# This connection is essential for Trusted Domain (TD) attestation, as QGS signs the TDREPORT sent by QEMU via the GetQuote hypercall. +# By default QGS runs on vsock port 4050, but can be modified by the host admin. For QEMU's tdx-guest object, this connection needs to +# be specified in a JSON format, for example: +# -object '{"qom-type":"tdx-guest","id":"tdx","quote-generation-socket":{"type":"vsock","cid":"2","port":"4050"}}' +# It's important to note that setting "tdx_quote_generation_service_socket_port" to 0 enables communication via Unix Domain Sockets (UDS). +# To activate UDS, the QGS service itself must be launched with the "-port=0" parameter and the UDS will always be located at /var/run/tdx-qgs/qgs.socket. +# -object '{"qom-type":"tdx-guest","id":"tdx","quote-generation-socket":{"type":"unix","path":"/var/run/tdx-qgs/qgs.socket"}}' +# tdx_quote_generation_service_socket_port = @QEMUTDXQUOTEGENERATIONSERVICESOCKETPORT@ + # # Use rx Rate Limiter to control network I/O inbound bandwidth(size in bits/sec for SB/VM). # In Qemu, we use classful qdiscs HTB(Hierarchy Token Bucket) to discipline traffic. From c8433c6b700a0a99d97734f65a00a206d0ac31e8 Mon Sep 17 00:00:00 2001 From: "alex.lyn" Date: Tue, 27 May 2025 17:41:04 +0800 Subject: [PATCH 7/7] kata-sys-util: Update TDX platform detection for newer TDX platforms On newer TDX platforms, checking `/sys/firmware/tdx` for `major_version` and `minor_version` is no longer necessary. Instead, we only need to verify that `/sys/module/kvm_intel/parameters/tdx` is set to `'Y'`. This commit addresses the following: (1) Removes the outdated check and corrects related code, primarily impacting `cloud-hypervisor`. (2) Refines the TDX platform detection logic within `arch_guest_protection`. Fixes #11177 Signed-off-by: alex.lyn --- src/libs/kata-sys-util/src/protection.rs | 146 +++--------------- .../hypervisor/ch-config/src/convert.rs | 100 ++++-------- .../crates/hypervisor/ch-config/src/lib.rs | 11 +- .../crates/hypervisor/src/ch/inner.rs | 8 +- .../hypervisor/src/ch/inner_hypervisor.rs | 58 ++----- .../runtimes/virt_container/src/sandbox.rs | 2 +- 6 files changed, 68 insertions(+), 257 deletions(-) diff --git a/src/libs/kata-sys-util/src/protection.rs b/src/libs/kata-sys-util/src/protection.rs index 9cb11a1178..8e06117c88 100644 --- a/src/libs/kata-sys-util/src/protection.rs +++ b/src/libs/kata-sys-util/src/protection.rs @@ -3,8 +3,6 @@ // SPDX-License-Identifier: Apache-2.0 // -#[cfg(target_arch = "x86_64")] -use anyhow::anyhow; #[cfg(any(target_arch = "s390x", target_arch = "x86_64", target_arch = "aarch64"))] use anyhow::Result; use serde::{Deserialize, Serialize}; @@ -25,12 +23,6 @@ use nix::unistd::Uid; #[cfg(target_arch = "x86_64")] use std::fs; -#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)] -pub struct TDXDetails { - pub major_version: u32, - pub minor_version: u32, -} - #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct SevSnpDetails { pub cbitpos: u32, @@ -41,7 +33,7 @@ pub struct SevSnpDetails { pub enum GuestProtection { #[default] NoProtection, - Tdx(TDXDetails), + Tdx, Sev(SevSnpDetails), Snp(SevSnpDetails), Pef, @@ -51,11 +43,7 @@ pub enum GuestProtection { impl fmt::Display for GuestProtection { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - GuestProtection::Tdx(details) => write!( - f, - "tdx (major_version: {}, minor_version: {})", - details.major_version, details.minor_version - ), + GuestProtection::Tdx => write!(f, "tdx"), GuestProtection::Sev(details) => write!(f, "sev (cbitpos: {}", details.cbitpos), GuestProtection::Snp(details) => write!(f, "snp (cbitpos: {}", details.cbitpos), GuestProtection::Pef => write!(f, "pef"), @@ -88,95 +76,29 @@ pub enum ProtectionError { } #[cfg(target_arch = "x86_64")] -pub const TDX_SYS_FIRMWARE_DIR: &str = "/sys/firmware/tdx/"; +pub const TDX_KVM_PARAMETER_PATH: &str = "/sys/module/kvm_intel/parameters/tdx"; #[cfg(target_arch = "x86_64")] pub const SEV_KVM_PARAMETER_PATH: &str = "/sys/module/kvm_amd/parameters/sev"; #[cfg(target_arch = "x86_64")] pub const SNP_KVM_PARAMETER_PATH: &str = "/sys/module/kvm_amd/parameters/sev_snp"; -// Module directory below TDX_SYS_FIRMWARE_DIR. -#[cfg(target_arch = "x86_64")] -const TDX_FW_MODULE_DIR: &str = "tdx_module"; - -// File in TDX_FW_MODULE_DIR that specifies TDX major version number. -#[cfg(target_arch = "x86_64")] -const TDX_MAJOR_FILE: &str = "major_version"; - -// File in TDX_FW_MODULE_DIR that specifies TDX minor version number. -#[cfg(target_arch = "x86_64")] -const TDX_MINOR_FILE: &str = "minor_version"; - #[cfg(target_arch = "x86_64")] pub fn available_guest_protection() -> Result { - arch_guest_protection( - TDX_SYS_FIRMWARE_DIR, - SEV_KVM_PARAMETER_PATH, - SNP_KVM_PARAMETER_PATH, - ) + arch_guest_protection(SEV_KVM_PARAMETER_PATH, SNP_KVM_PARAMETER_PATH) } #[cfg(target_arch = "x86_64")] pub fn arch_guest_protection( - tdx_path: &str, sev_path: &str, snp_path: &str, ) -> Result { - let metadata = fs::metadata(tdx_path); - - if metadata.is_ok() && metadata.unwrap().is_dir() { - let module_dir = safe_path::scoped_join(tdx_path, TDX_FW_MODULE_DIR).map_err(|e| { - ProtectionError::CannotResolvePath( - TDX_FW_MODULE_DIR.to_string(), - PathBuf::from(tdx_path), - anyhow!(e), - ) - })?; - - let major_file = - safe_path::scoped_join(module_dir.clone(), TDX_MAJOR_FILE).map_err(|e| { - ProtectionError::CannotResolvePath( - TDX_MAJOR_FILE.to_string(), - module_dir.clone(), - anyhow!(e), - ) - })?; - - let minor_file = - safe_path::scoped_join(module_dir.clone(), TDX_MINOR_FILE).map_err(|e| { - ProtectionError::CannotResolvePath( - TDX_MINOR_FILE.to_string(), - module_dir, - anyhow!(e), - ) - })?; - - const HEX_BASE: u32 = 16; - const HEX_PREFIX: &str = "0x"; - - let major_version_str = std::fs::read_to_string(major_file.clone()).map_err(|e| { - ProtectionError::FileMissing(major_file.clone().to_string_lossy().into(), e) - })?; - - let major_version_str = major_version_str.trim_start_matches(HEX_PREFIX); - - let major_version = u32::from_str_radix(major_version_str, HEX_BASE) - .map_err(|e| ProtectionError::FileInvalid(major_file, anyhow!(e)))?; - - let minor_version_str = std::fs::read_to_string(minor_file.clone()).map_err(|e| { - ProtectionError::FileMissing(minor_file.clone().to_string_lossy().into(), e) - })?; - - let minor_version_str = minor_version_str.trim_start_matches(HEX_PREFIX); - - let minor_version = u32::from_str_radix(minor_version_str, HEX_BASE) - .map_err(|e| ProtectionError::FileInvalid(minor_file, anyhow!(e)))?; - - let details = TDXDetails { - major_version, - minor_version, - }; - - return Ok(GuestProtection::Tdx(details)); + // Check if /sys/module/kvm_intel/parameters/tdx is set to 'Y' + if Path::new(TDX_KVM_PARAMETER_PATH).exists() { + if let Ok(content) = fs::read(TDX_KVM_PARAMETER_PATH) { + if !content.is_empty() && content[0] == b'Y' { + return Ok(GuestProtection::Tdx); + } + } } let check_contents = |file_name: &str| -> Result { @@ -301,12 +223,12 @@ mod tests { let mut snp_file = fs::File::create(snp_file_path).unwrap(); writeln!(snp_file, "Y").unwrap(); - let actual = arch_guest_protection("/xyz/tmp", "/xyz/tmp", path.to_str().unwrap()); + let actual = arch_guest_protection("/xyz/tmp", path.to_str().unwrap()); assert!(actual.is_ok()); assert_eq!(actual.unwrap(), GuestProtection::Snp); writeln!(snp_file, "N").unwrap(); - let actual = arch_guest_protection("/xyz/tmp", "/xyz/tmp", path.to_str().unwrap()); + let actual = arch_guest_protection("/xyz/tmp", path.to_str().unwrap()); assert!(actual.is_ok()); assert_eq!(actual.unwrap(), GuestProtection::NoProtection); } @@ -320,12 +242,12 @@ mod tests { let mut sev_file = fs::File::create(sev_file_path).unwrap(); writeln!(sev_file, "Y").unwrap(); - let actual = arch_guest_protection("/xyz/tmp", sev_path.to_str().unwrap(), "/xyz/tmp"); + let actual = arch_guest_protection(sev_path.to_str().unwrap(), "/xyz/tmp"); assert!(actual.is_ok()); assert_eq!(actual.unwrap(), GuestProtection::Sev); writeln!(sev_file, "N").unwrap(); - let actual = arch_guest_protection("/xyz/tmp", sev_path.to_str().unwrap(), "/xyz/tmp"); + let actual = arch_guest_protection(sev_path.to_str().unwrap(), "/xyz/tmp"); assert!(actual.is_ok()); assert_eq!(actual.unwrap(), GuestProtection::NoProtection); } @@ -342,49 +264,19 @@ mod tests { std::fs::create_dir_all(tdx_path.clone()).unwrap(); - let actual = arch_guest_protection(invalid_dir, invalid_dir, invalid_dir); + let actual = arch_guest_protection(invalid_dir, invalid_dir); assert!(actual.is_ok()); assert_eq!(actual.unwrap(), GuestProtection::NoProtection); - let actual = arch_guest_protection(tdx_path.to_str().unwrap(), invalid_dir, invalid_dir); + let actual = arch_guest_protection(invalid_dir, invalid_dir); assert!(actual.is_err()); - let tdx_module = tdx_path.join(TDX_FW_MODULE_DIR); - std::fs::create_dir_all(tdx_module.clone()).unwrap(); - - let major_file = tdx_module.join(TDX_MAJOR_FILE); - std::fs::File::create(&major_file).unwrap(); - - let minor_file = tdx_module.join(TDX_MINOR_FILE); - std::fs::File::create(&minor_file).unwrap(); - - let result = arch_guest_protection(tdx_path.to_str().unwrap(), invalid_dir, invalid_dir); - assert!(result.is_err()); - - std::fs::write(&major_file, b"invalid").unwrap(); - std::fs::write(&minor_file, b"invalid").unwrap(); - - let result = arch_guest_protection(tdx_path.to_str().unwrap(), invalid_dir, invalid_dir); - assert!(result.is_err()); - - // Fake a TDX 1.0 environment - std::fs::write(&major_file, b"0x00000001").unwrap(); - std::fs::write(&minor_file, b"0x00000000").unwrap(); - - let result = arch_guest_protection(tdx_path.to_str().unwrap(), invalid_dir, invalid_dir); + let result = arch_guest_protection(invalid_dir, invalid_dir); assert!(result.is_ok()); let result = result.unwrap(); - let details = match &result { - GuestProtection::Tdx(details) => details, - _ => panic!(), - }; - - assert_eq!(details.major_version, 1); - assert_eq!(details.minor_version, 0); - let displayed_value = result.to_string(); - assert_eq!(displayed_value, "tdx (major_version: 1, minor_version: 0)"); + assert_eq!(displayed_value, "tdx"); } } diff --git a/src/runtime-rs/crates/hypervisor/ch-config/src/convert.rs b/src/runtime-rs/crates/hypervisor/ch-config/src/convert.rs index ebd56c3a9b..09443323c5 100644 --- a/src/runtime-rs/crates/hypervisor/ch-config/src/convert.rs +++ b/src/runtime-rs/crates/hypervisor/ch-config/src/convert.rs @@ -549,7 +549,7 @@ fn get_platform_cfg(guest_protection_to_use: GuestProtection) -> Option bool { - matches!(guest_protection_to_use, GuestProtection::Tdx(_)) + matches!(guest_protection_to_use, GuestProtection::Tdx) } #[cfg(test)] mod tests { use super::*; - use kata_sys_util::protection::{SevSnpDetails, TDXDetails}; + use kata_sys_util::protection::SevSnpDetails; #[test] fn test_guest_protection_is_tdx() { - let tdx_details = TDXDetails { - major_version: 1, - minor_version: 0, - }; - let sev_snp_details = SevSnpDetails { cbitpos: 42 }; #[derive(Debug)] @@ -547,7 +542,7 @@ mod tests { result: false, }, TestData { - protection: GuestProtection::Tdx(tdx_details), + protection: GuestProtection::Tdx, result: true, }, ]; diff --git a/src/runtime-rs/crates/hypervisor/src/ch/inner.rs b/src/runtime-rs/crates/hypervisor/src/ch/inner.rs index c8f1c2d905..40f3c5d330 100644 --- a/src/runtime-rs/crates/hypervisor/src/ch/inner.rs +++ b/src/runtime-rs/crates/hypervisor/src/ch/inner.rs @@ -197,7 +197,6 @@ impl Persist for CloudHypervisorInner { #[cfg(test)] mod tests { use super::*; - use kata_sys_util::protection::TDXDetails; #[actix_rt::test] async fn test_save_clh() { @@ -209,12 +208,7 @@ mod tests { clh.vm_path = String::from("/opt/kata/bin/cloud-hypervisor"); clh.run_dir = String::from("/var/run/kata-containers/") + &clh.id; - let details = TDXDetails { - major_version: 1, - minor_version: 0, - }; - - clh.guest_protection_to_use = GuestProtection::Tdx(details); + clh.guest_protection_to_use = GuestProtection::Tdx; let state = clh.save().await.unwrap(); assert_eq!(state.id, clh.id); diff --git a/src/runtime-rs/crates/hypervisor/src/ch/inner_hypervisor.rs b/src/runtime-rs/crates/hypervisor/src/ch/inner_hypervisor.rs index 41660ea14e..954ce30343 100644 --- a/src/runtime-rs/crates/hypervisor/src/ch/inner_hypervisor.rs +++ b/src/runtime-rs/crates/hypervisor/src/ch/inner_hypervisor.rs @@ -568,7 +568,7 @@ impl CloudHypervisorInner { if protection == GuestProtection::NoProtection { // User wants protection, but none available. return Err(anyhow!(GuestProtectionError::NoProtectionAvailable)); - } else if let GuestProtection::Tdx(_) = protection { + } else if let GuestProtection::Tdx = protection { info!(sl!(), "guest protection available and requested"; "guest-protection" => protection.to_string()); } else { return Err(anyhow!(GuestProtectionError::ExpectedTDXProtection( @@ -577,7 +577,7 @@ impl CloudHypervisorInner { } } else if protection == GuestProtection::NoProtection { debug!(sl!(), "no guest protection available"); - } else if let GuestProtection::Tdx(_) = protection { + } else if let GuestProtection::Tdx = protection { // CH requires TDX protection to be used. return Err(anyhow!(GuestProtectionError::TDXProtectionMustBeUsedWithCH)); } else { @@ -956,15 +956,13 @@ fn get_ch_vcpu_tids(proc_path: &str) -> Result> { #[cfg(test)] mod tests { use super::*; - use kata_sys_util::protection::{SevSnpDetails, TDXDetails}; + use kata_sys_util::protection::SevSnpDetails; #[cfg(target_arch = "x86_64")] - use kata_sys_util::protection::TDX_SYS_FIRMWARE_DIR; + use kata_sys_util::protection::TDX_KVM_PARAMETER_PATH; use kata_types::config::hypervisor::{Hypervisor as HypervisorConfig, SecurityInfo}; use serial_test::serial; - #[cfg(target_arch = "x86_64")] - use std::path::PathBuf; use test_utils::{assert_result, skip_if_not_root}; use std::fs::File; @@ -985,11 +983,6 @@ mod tests { // available_guest_protection() requires super user privs. skip_if_not_root!(); - let tdx_details = TDXDetails { - major_version: 1, - minor_version: 0, - }; - let sev_snp_details = SevSnpDetails { cbitpos: 42 }; #[derive(Debug)] @@ -1020,8 +1013,8 @@ mod tests { result: Ok(GuestProtection::Snp(sev_snp_details.clone())), }, TestData { - value: Some(GuestProtection::Tdx(tdx_details.clone())), - result: Ok(GuestProtection::Tdx(tdx_details.clone())), + value: Some(GuestProtection::Tdx), + result: Ok(GuestProtection::Tdx), }, ]; @@ -1055,26 +1048,11 @@ mod tests { // available_guest_protection() requires super user privs. skip_if_not_root!(); - let tdx_details = TDXDetails { - major_version: 1, - minor_version: 0, - }; - // Use the hosts protection, not a fake one. set_fake_guest_protection(None); - let tdx_fw_path = PathBuf::from(TDX_SYS_FIRMWARE_DIR); - - // Simple test for Intel TDX - let have_tdx = if tdx_fw_path.exists() { - if let Ok(metadata) = std::fs::metadata(tdx_fw_path.clone()) { - metadata.is_dir() - } else { - false - } - } else { - false - }; + let have_tdx = fs::read(TDX_KVM_PARAMETER_PATH) + .map_or(false, |content| !content.is_empty() && content[0] == b'Y'); let protection = task::spawn_blocking(|| -> Result { get_guest_protection() }) @@ -1083,16 +1061,13 @@ mod tests { .unwrap(); if std::env::var("DEBUG").is_ok() { - let msg = format!( - "tdx_fw_path: {:?}, have_tdx: {:?}, protection: {:?}", - tdx_fw_path, have_tdx, protection - ); + let msg = format!("have_tdx: {:?}, protection: {:?}", have_tdx, protection); eprintln!("DEBUG: {}", msg); } if have_tdx { - assert_eq!(protection, GuestProtection::Tdx(tdx_details)); + assert_eq!(protection, GuestProtection::Tdx); } else { assert_eq!(protection, GuestProtection::NoProtection); } @@ -1115,11 +1090,6 @@ mod tests { guest_protection_to_use: GuestProtection, } - let tdx_details = TDXDetails { - major_version: 1, - minor_version: 0, - }; - let tests = &[ TestData { confidential_guest: false, @@ -1135,15 +1105,15 @@ mod tests { }, TestData { confidential_guest: false, - available_protection: Some(GuestProtection::Tdx(tdx_details.clone())), + available_protection: Some(GuestProtection::Tdx), result: Err(anyhow!(GuestProtectionError::TDXProtectionMustBeUsedWithCH)), - guest_protection_to_use: GuestProtection::Tdx(tdx_details.clone()), + guest_protection_to_use: GuestProtection::Tdx, }, TestData { confidential_guest: true, - available_protection: Some(GuestProtection::Tdx(tdx_details.clone())), + available_protection: Some(GuestProtection::Tdx), result: Ok(()), - guest_protection_to_use: GuestProtection::Tdx(tdx_details), + guest_protection_to_use: GuestProtection::Tdx, }, TestData { confidential_guest: false, diff --git a/src/runtime-rs/crates/runtimes/virt_container/src/sandbox.rs b/src/runtime-rs/crates/runtimes/virt_container/src/sandbox.rs index 2e5936d850..bf504f394b 100644 --- a/src/runtime-rs/crates/runtimes/virt_container/src/sandbox.rs +++ b/src/runtime-rs/crates/runtimes/virt_container/src/sandbox.rs @@ -398,7 +398,7 @@ impl VirtSandbox { GuestProtection::Se => { Ok(Some(ProtectionDeviceConfig::Se)) } - GuestProtection::Tdx(_details) => { + GuestProtection::Tdx => { Ok(Some(ProtectionDeviceConfig::Tdx(TdxConfig { id: "tdx".to_owned(), firmware: hypervisor_config.boot_info.firmware.clone(),