mirror of
https://github.com/kata-containers/kata-containers.git
synced 2025-07-16 16:32:03 +00:00
Merge pull request #11179 from Apokleos/tdx-qemu-rs
runtime-rs: Add TDX Support to runtime-rs for Confidential Containers (CoCo)
This commit is contained in:
commit
2d570db08b
@ -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<GuestProtection, ProtectionError> {
|
||||
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<GuestProtection, ProtectionError> {
|
||||
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<bool, ProtectionError> {
|
||||
@ -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");
|
||||
}
|
||||
}
|
||||
|
@ -890,6 +890,17 @@ pub struct SecurityInfo {
|
||||
/// e.g. "path" for io.katacontainers.config.hypervisor.path"
|
||||
#[serde(default)]
|
||||
pub enable_annotations: Vec<String>,
|
||||
|
||||
/// 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 {
|
||||
|
@ -181,6 +181,7 @@ DEFVFIOMODE := guest-kernel
|
||||
DEFBINDMOUNTS := []
|
||||
DEFDANCONF := /run/kata-containers/dans
|
||||
DEFFORCEGUESTPULL := false
|
||||
QEMUTDXQUOTEGENERATIONSERVICESOCKETPORT := 4050
|
||||
SED = sed
|
||||
CLI_DIR = cmd
|
||||
SHIMV2 = containerd-shim-kata-v2
|
||||
@ -510,6 +511,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$$') \
|
||||
|
@ -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.
|
||||
|
@ -549,7 +549,7 @@ fn get_platform_cfg(guest_protection_to_use: GuestProtection) -> Option<Platform
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use kata_sys_util::protection::{SevSnpDetails, TDXDetails};
|
||||
use kata_sys_util::protection::SevSnpDetails;
|
||||
use kata_types::config::hypervisor::{
|
||||
BlockDeviceInfo, Hypervisor as HypervisorConfig, SecurityInfo,
|
||||
};
|
||||
@ -725,11 +725,6 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_get_serial_cfg() {
|
||||
let tdx_details = TDXDetails {
|
||||
major_version: 1,
|
||||
minor_version: 0,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
struct TestData {
|
||||
debug: bool,
|
||||
@ -758,7 +753,7 @@ mod tests {
|
||||
},
|
||||
TestData {
|
||||
debug: false,
|
||||
guest_protection: GuestProtection::Tdx(tdx_details.clone()),
|
||||
guest_protection: GuestProtection::Tdx,
|
||||
result: ConsoleConfig {
|
||||
file: None,
|
||||
mode: ConsoleOutputMode::Off,
|
||||
@ -767,7 +762,7 @@ mod tests {
|
||||
},
|
||||
TestData {
|
||||
debug: true,
|
||||
guest_protection: GuestProtection::Tdx(tdx_details.clone()),
|
||||
guest_protection: GuestProtection::Tdx,
|
||||
result: ConsoleConfig {
|
||||
file: None,
|
||||
mode: ConsoleOutputMode::Off,
|
||||
@ -813,11 +808,6 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_get_console_cfg() {
|
||||
let tdx_details = TDXDetails {
|
||||
major_version: 1,
|
||||
minor_version: 0,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
struct TestData {
|
||||
debug: bool,
|
||||
@ -846,7 +836,7 @@ mod tests {
|
||||
},
|
||||
TestData {
|
||||
debug: false,
|
||||
guest_protection: GuestProtection::Tdx(tdx_details.clone()),
|
||||
guest_protection: GuestProtection::Tdx,
|
||||
result: ConsoleConfig {
|
||||
file: None,
|
||||
mode: ConsoleOutputMode::Off,
|
||||
@ -855,7 +845,7 @@ mod tests {
|
||||
},
|
||||
TestData {
|
||||
debug: true,
|
||||
guest_protection: GuestProtection::Tdx(tdx_details.clone()),
|
||||
guest_protection: GuestProtection::Tdx,
|
||||
result: ConsoleConfig {
|
||||
file: None,
|
||||
mode: ConsoleOutputMode::Tty,
|
||||
@ -899,11 +889,6 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_get_platform_cfg() {
|
||||
let tdx_details = TDXDetails {
|
||||
major_version: 1,
|
||||
minor_version: 0,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
struct TestData {
|
||||
guest_protection: GuestProtection,
|
||||
@ -916,7 +901,7 @@ mod tests {
|
||||
result: None,
|
||||
},
|
||||
TestData {
|
||||
guest_protection: GuestProtection::Tdx(tdx_details.clone()),
|
||||
guest_protection: GuestProtection::Tdx,
|
||||
result: Some(PlatformConfig {
|
||||
tdx: true,
|
||||
num_pci_segments: DEFAULT_NUM_PCI_SEGMENTS,
|
||||
@ -1153,11 +1138,6 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_cpuinfo_to_cpusconfig() {
|
||||
let tdx_details = TDXDetails {
|
||||
major_version: 1,
|
||||
minor_version: 0,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
struct TestData {
|
||||
cpu_info: CpuInfo,
|
||||
@ -1248,7 +1228,7 @@ mod tests {
|
||||
default_maxvcpus: 13,
|
||||
..Default::default()
|
||||
},
|
||||
guest_protection: GuestProtection::Tdx(tdx_details.clone()),
|
||||
guest_protection: GuestProtection::Tdx,
|
||||
result: Ok(CpusConfig {
|
||||
boot_vcpus: 1,
|
||||
max_vcpus: 1,
|
||||
@ -1294,11 +1274,6 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_bootinfo_to_payloadconfig() {
|
||||
let tdx_details = TDXDetails {
|
||||
major_version: 1,
|
||||
minor_version: 0,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
struct TestData {
|
||||
boot_info: BootInfo,
|
||||
@ -1404,19 +1379,19 @@ mod tests {
|
||||
..Default::default()
|
||||
},
|
||||
cmdline: None,
|
||||
guest_protection: GuestProtection::Tdx(tdx_details.clone()),
|
||||
guest_protection: GuestProtection::Tdx,
|
||||
result: Err(PayloadConfigError::TDXFirmwareMissing),
|
||||
},
|
||||
TestData {
|
||||
boot_info: boot_info_with_initrd,
|
||||
cmdline: Some(cmdline.to_string()),
|
||||
guest_protection: GuestProtection::Tdx(tdx_details.clone()),
|
||||
guest_protection: GuestProtection::Tdx,
|
||||
result: Ok(payload_config_with_initrd),
|
||||
},
|
||||
TestData {
|
||||
boot_info: boot_info_without_initrd,
|
||||
cmdline: Some(cmdline.to_string()),
|
||||
guest_protection: GuestProtection::Tdx(tdx_details.clone()),
|
||||
guest_protection: GuestProtection::Tdx,
|
||||
result: Ok(payload_config_without_initrd),
|
||||
},
|
||||
];
|
||||
@ -1455,11 +1430,6 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_memoryinfo_to_memoryconfig() {
|
||||
let tdx_details = TDXDetails {
|
||||
major_version: 1,
|
||||
minor_version: 0,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
struct TestData {
|
||||
mem_info: MemoryInfo,
|
||||
@ -1494,7 +1464,7 @@ mod tests {
|
||||
|
||||
..Default::default()
|
||||
},
|
||||
guest_protection: GuestProtection::Tdx(tdx_details.clone()),
|
||||
guest_protection: GuestProtection::Tdx,
|
||||
result: Ok(MemoryConfig {
|
||||
size: (17 * MIB),
|
||||
shared: true,
|
||||
@ -1509,7 +1479,7 @@ mod tests {
|
||||
|
||||
..Default::default()
|
||||
},
|
||||
guest_protection: GuestProtection::Tdx(tdx_details.clone()),
|
||||
guest_protection: GuestProtection::Tdx,
|
||||
result: Ok(MemoryConfig {
|
||||
size: usable_max_mem_bytes,
|
||||
shared: true,
|
||||
@ -1524,7 +1494,7 @@ mod tests {
|
||||
|
||||
..Default::default()
|
||||
},
|
||||
guest_protection: GuestProtection::Tdx(tdx_details.clone()),
|
||||
guest_protection: GuestProtection::Tdx,
|
||||
result: Err(MemoryConfigError::DefaultMemSizeTooBig),
|
||||
},
|
||||
TestData {
|
||||
@ -1552,7 +1522,7 @@ mod tests {
|
||||
},
|
||||
TestData {
|
||||
mem_info: mem_info_confidential_guest,
|
||||
guest_protection: GuestProtection::Tdx(tdx_details.clone()),
|
||||
guest_protection: GuestProtection::Tdx,
|
||||
result: Ok(mem_cfg_confidential_guest),
|
||||
},
|
||||
];
|
||||
@ -1642,11 +1612,6 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_named_hypervisor_config_to_vmconfig() {
|
||||
let tdx_details = TDXDetails {
|
||||
major_version: 1,
|
||||
minor_version: 0,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
struct TestData {
|
||||
cfg: NamedHypervisorConfig,
|
||||
@ -1817,7 +1782,7 @@ mod tests {
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let platform_config_tdx = get_platform_cfg(GuestProtection::Tdx(tdx_details.clone()));
|
||||
let platform_config_tdx = get_platform_cfg(GuestProtection::Tdx);
|
||||
|
||||
let vmconfig_tdx_image = VmConfig {
|
||||
cpus: cpus_config_tdx.clone(),
|
||||
@ -1926,7 +1891,7 @@ mod tests {
|
||||
vsock_socket_path: vsock_socket_path.into(),
|
||||
|
||||
cfg: hypervisor_cfg_tdx_image,
|
||||
guest_protection_to_use: GuestProtection::Tdx(tdx_details.clone()),
|
||||
guest_protection_to_use: GuestProtection::Tdx,
|
||||
|
||||
..Default::default()
|
||||
};
|
||||
@ -1936,7 +1901,7 @@ mod tests {
|
||||
vsock_socket_path: vsock_socket_path.into(),
|
||||
|
||||
cfg: hypervisor_cfg_tdx_initrd,
|
||||
guest_protection_to_use: GuestProtection::Tdx(tdx_details.clone()),
|
||||
guest_protection_to_use: GuestProtection::Tdx,
|
||||
|
||||
..Default::default()
|
||||
};
|
||||
@ -2175,11 +2140,6 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_check_tdx_rootfs_settings() {
|
||||
let tdx_details = TDXDetails {
|
||||
major_version: 1,
|
||||
minor_version: 0,
|
||||
};
|
||||
|
||||
let sev_snp_details = SevSnpDetails { cbitpos: 42 };
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -2233,7 +2193,7 @@ mod tests {
|
||||
use_image: true,
|
||||
container_rootfs_driver: "container",
|
||||
vm_rootfs_driver: "vm",
|
||||
guest_protection_to_use: GuestProtection::Tdx(tdx_details.clone()),
|
||||
guest_protection_to_use: GuestProtection::Tdx,
|
||||
result: Err(VmConfigError::TDXContainerRootfsNotVirtioBlk),
|
||||
},
|
||||
// Partially correct
|
||||
@ -2241,28 +2201,28 @@ mod tests {
|
||||
use_image: true,
|
||||
container_rootfs_driver: VIRTIO_BLK_PCI,
|
||||
vm_rootfs_driver: "vm",
|
||||
guest_protection_to_use: GuestProtection::Tdx(tdx_details.clone()),
|
||||
guest_protection_to_use: GuestProtection::Tdx,
|
||||
result: Err(VmConfigError::TDXVMRootfsNotVirtioBlk),
|
||||
},
|
||||
TestData {
|
||||
use_image: true,
|
||||
container_rootfs_driver: VIRTIO_BLK_MMIO,
|
||||
vm_rootfs_driver: "vm",
|
||||
guest_protection_to_use: GuestProtection::Tdx(tdx_details.clone()),
|
||||
guest_protection_to_use: GuestProtection::Tdx,
|
||||
result: Err(VmConfigError::TDXVMRootfsNotVirtioBlk),
|
||||
},
|
||||
TestData {
|
||||
use_image: true,
|
||||
container_rootfs_driver: "container",
|
||||
vm_rootfs_driver: VIRTIO_BLK_PCI,
|
||||
guest_protection_to_use: GuestProtection::Tdx(tdx_details.clone()),
|
||||
guest_protection_to_use: GuestProtection::Tdx,
|
||||
result: Err(VmConfigError::TDXContainerRootfsNotVirtioBlk),
|
||||
},
|
||||
TestData {
|
||||
use_image: true,
|
||||
container_rootfs_driver: "container",
|
||||
vm_rootfs_driver: VIRTIO_BLK_MMIO,
|
||||
guest_protection_to_use: GuestProtection::Tdx(tdx_details.clone()),
|
||||
guest_protection_to_use: GuestProtection::Tdx,
|
||||
result: Err(VmConfigError::TDXContainerRootfsNotVirtioBlk),
|
||||
},
|
||||
// Same types
|
||||
@ -2270,14 +2230,14 @@ mod tests {
|
||||
use_image: true,
|
||||
container_rootfs_driver: VIRTIO_BLK_MMIO,
|
||||
vm_rootfs_driver: VIRTIO_BLK_MMIO,
|
||||
guest_protection_to_use: GuestProtection::Tdx(tdx_details.clone()),
|
||||
guest_protection_to_use: GuestProtection::Tdx,
|
||||
result: Ok(()),
|
||||
},
|
||||
TestData {
|
||||
use_image: true,
|
||||
container_rootfs_driver: VIRTIO_BLK_PCI,
|
||||
vm_rootfs_driver: VIRTIO_BLK_PCI,
|
||||
guest_protection_to_use: GuestProtection::Tdx(tdx_details.clone()),
|
||||
guest_protection_to_use: GuestProtection::Tdx,
|
||||
result: Ok(()),
|
||||
},
|
||||
// Alternate types
|
||||
@ -2285,14 +2245,14 @@ mod tests {
|
||||
use_image: true,
|
||||
container_rootfs_driver: VIRTIO_BLK_MMIO,
|
||||
vm_rootfs_driver: VIRTIO_BLK_PCI,
|
||||
guest_protection_to_use: GuestProtection::Tdx(tdx_details.clone()),
|
||||
guest_protection_to_use: GuestProtection::Tdx,
|
||||
result: Ok(()),
|
||||
},
|
||||
TestData {
|
||||
use_image: true,
|
||||
container_rootfs_driver: VIRTIO_BLK_PCI,
|
||||
vm_rootfs_driver: VIRTIO_BLK_MMIO,
|
||||
guest_protection_to_use: GuestProtection::Tdx(tdx_details.clone()),
|
||||
guest_protection_to_use: GuestProtection::Tdx,
|
||||
result: Ok(()),
|
||||
},
|
||||
// Using an initrd (not currently supported)
|
||||
@ -2300,28 +2260,28 @@ mod tests {
|
||||
use_image: false,
|
||||
container_rootfs_driver: VIRTIO_BLK_PCI,
|
||||
vm_rootfs_driver: VIRTIO_BLK_PCI,
|
||||
guest_protection_to_use: GuestProtection::Tdx(tdx_details.clone()),
|
||||
guest_protection_to_use: GuestProtection::Tdx,
|
||||
result: Err(VmConfigError::TDXDisallowsInitrd),
|
||||
},
|
||||
TestData {
|
||||
use_image: false,
|
||||
container_rootfs_driver: "container",
|
||||
vm_rootfs_driver: "vm",
|
||||
guest_protection_to_use: GuestProtection::Tdx(tdx_details.clone()),
|
||||
guest_protection_to_use: GuestProtection::Tdx,
|
||||
result: Err(VmConfigError::TDXDisallowsInitrd),
|
||||
},
|
||||
TestData {
|
||||
use_image: false,
|
||||
container_rootfs_driver: VIRTIO_BLK_PCI,
|
||||
vm_rootfs_driver: "vm",
|
||||
guest_protection_to_use: GuestProtection::Tdx(tdx_details.clone()),
|
||||
guest_protection_to_use: GuestProtection::Tdx,
|
||||
result: Err(VmConfigError::TDXDisallowsInitrd),
|
||||
},
|
||||
TestData {
|
||||
use_image: false,
|
||||
container_rootfs_driver: VIRTIO_BLK_MMIO,
|
||||
vm_rootfs_driver: "vm",
|
||||
guest_protection_to_use: GuestProtection::Tdx(tdx_details.clone()),
|
||||
guest_protection_to_use: GuestProtection::Tdx,
|
||||
result: Err(VmConfigError::TDXDisallowsInitrd),
|
||||
},
|
||||
];
|
||||
|
@ -502,21 +502,16 @@ pub struct NamedHypervisorConfig {
|
||||
|
||||
// Returns true if the enabled guest protection is Intel TDX.
|
||||
pub fn guest_protection_is_tdx(guest_protection_to_use: GuestProtection) -> 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,
|
||||
},
|
||||
];
|
||||
|
@ -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);
|
||||
|
@ -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<HashMap<u32, u32>> {
|
||||
#[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<GuestProtection> { 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,
|
||||
|
@ -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,
|
||||
|
@ -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<String>,
|
||||
// Debug mode
|
||||
pub debug: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ProtectionDevice {
|
||||
pub device_id: String,
|
||||
|
@ -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<String>,
|
||||
|
||||
// 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<String>,
|
||||
|
||||
// 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<String>,
|
||||
|
||||
// Quote generation socket.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
quote_generation_socket: Option<SocketAddress>,
|
||||
|
||||
// Debug mode
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
debug: Option<bool>,
|
||||
}
|
||||
|
||||
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<String>, 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<Vec<String>> {
|
||||
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=<X>B,mem-reserve=<Y>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<String>,
|
||||
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
|
||||
///
|
||||
|
@ -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;
|
||||
|
@ -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<Vec<File>> {
|
||||
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":"#));
|
||||
}
|
||||
}
|
||||
|
@ -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 => {
|
||||
Ok(Some(ProtectionDeviceConfig::Tdx(TdxConfig {
|
||||
id: "tdx".to_owned(),
|
||||
firmware: hypervisor_config.boot_info.firmware.clone(),
|
||||
qgs_port: hypervisor_config.security_info.qgs_port,
|
||||
mrconfigid: None,
|
||||
debug: false,
|
||||
})))
|
||||
},
|
||||
_ => Err(anyhow!("confidential_guest requested by configuration but no supported protection available"))
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user