diff --git a/hypervisor/Cargo.toml b/hypervisor/Cargo.toml index 7e7560315..c80d18629 100644 --- a/hypervisor/Cargo.toml +++ b/hypervisor/Cargo.toml @@ -8,6 +8,7 @@ license = "Apache-2.0 OR BSD-3-Clause" [features] kvm = ["kvm-ioctls", "kvm-bindings"] mshv = ["mshv-ioctls", "mshv-bindings"] +tdx = [] [dependencies] anyhow = "1.0" diff --git a/hypervisor/src/cpu.rs b/hypervisor/src/cpu.rs index 3bf22ed57..70d299e5b 100644 --- a/hypervisor/src/cpu.rs +++ b/hypervisor/src/cpu.rs @@ -205,6 +205,12 @@ pub enum HypervisorCpuError { /// #[error("Failed to translate GVA: {0}")] TranslateGVA(#[source] anyhow::Error), + /// + /// Failed to initialize TDX on CPU + /// + #[cfg(feature = "tdx")] + #[error("Failed to initialize TDX: {0}")] + InitializeTdx(#[source] std::io::Error), } #[derive(Debug)] @@ -410,4 +416,9 @@ pub trait Vcpu: Send + Sync { /// Translate guest virtual address to guest physical address /// fn translate_gva(&self, gva: u64, flags: u64) -> Result<(u64, hv_translate_gva_result)>; + /// + /// Initialize TDX support on the vCPU + /// + #[cfg(feature = "tdx")] + fn tdx_init(&self, hob_address: u64) -> Result<()>; } diff --git a/hypervisor/src/kvm/mod.rs b/hypervisor/src/kvm/mod.rs index 6307ab868..1750d0e29 100644 --- a/hypervisor/src/kvm/mod.rs +++ b/hypervisor/src/kvm/mod.rs @@ -53,6 +53,8 @@ pub use x86_64::{ #[cfg(target_arch = "aarch64")] pub mod aarch64; pub use kvm_bindings; +#[cfg(feature = "tdx")] +use kvm_bindings::KVMIO; pub use kvm_bindings::{ kvm_create_device, kvm_device_type_KVM_DEV_TYPE_VFIO, kvm_irq_routing, kvm_irq_routing_entry, kvm_userspace_memory_region, KVM_IRQ_ROUTING_MSI, KVM_MEM_LOG_DIRTY_PAGES, KVM_MEM_READONLY, @@ -67,6 +69,8 @@ pub use kvm_ioctls; pub use kvm_ioctls::{Cap, Kvm}; #[cfg(target_arch = "aarch64")] use std::mem; +#[cfg(feature = "tdx")] +use vmm_sys_util::{ioctl::ioctl_with_val, ioctl_expr, ioctl_ioc_nr, ioctl_iowr_nr}; /// /// Export generically-named wrappers of kvm-bindings for Unix-based platforms @@ -79,6 +83,21 @@ pub use { kvm_bindings::kvm_vcpu_events as VcpuEvents, kvm_ioctls::DeviceFd, kvm_ioctls::IoEventAddress, kvm_ioctls::VcpuExit, }; + +#[cfg(feature = "tdx")] +ioctl_iowr_nr!(KVM_MEMORY_ENCRYPT_OP, KVMIO, 0xba, std::os::raw::c_ulong); + +#[cfg(feature = "tdx")] +#[repr(u32)] +enum TdxCommand { + #[allow(dead_code)] + Capabilities = 0, + InitVm, + InitVcpu, + InitMemRegion, + Finalize, +} + #[derive(Clone, Copy, Debug, PartialEq, Deserialize, Serialize)] pub struct KvmVmState {} @@ -367,7 +386,109 @@ impl vm::Vm for KvmVm { .get_dirty_log(slot, memory_size as usize) .map_err(|e| vm::HypervisorVmError::GetDirtyLog(e.into())) } + + /// + /// Initialize TDX for this VM + /// + #[cfg(feature = "tdx")] + fn tdx_init(&self, cpuid: &CpuId, max_vcpus: u32) -> vm::Result<()> { + #[repr(C)] + struct TdxInitVm { + max_vcpus: u32, + reserved: u32, + attributes: u64, + cpuid: u64, + }; + let data = TdxInitVm { + max_vcpus, + reserved: 0, + attributes: 0, + cpuid: cpuid.as_fam_struct_ptr() as u64, + }; + + tdx_command( + &self.fd.as_raw_fd(), + TdxCommand::InitVm, + 0, + &data as *const _ as u64, + ) + .map_err(vm::HypervisorVmError::InitializeTdx) + } + + /// + /// Finalize the TDX setup for this VM + /// + #[cfg(feature = "tdx")] + fn tdx_finalize(&self) -> vm::Result<()> { + tdx_command(&self.fd.as_raw_fd(), TdxCommand::Finalize, 0, 0) + .map_err(vm::HypervisorVmError::FinalizeTdx) + } + + /// + /// Initialize memory regions for the TDX VM + /// + #[cfg(feature = "tdx")] + fn tdx_init_memory_region( + &self, + host_address: u64, + guest_address: u64, + size: u64, + measure: bool, + ) -> vm::Result<()> { + #[repr(C)] + struct TdxInitMemRegion { + host_address: u64, + guest_address: u64, + pages: u64, + }; + let data = TdxInitMemRegion { + host_address, + guest_address, + pages: size / 4096, + }; + + tdx_command( + &self.fd.as_raw_fd(), + TdxCommand::InitMemRegion, + if measure { 1 } else { 0 }, + &data as *const _ as u64, + ) + .map_err(vm::HypervisorVmError::InitMemRegionTdx) + } } + +#[cfg(feature = "tdx")] +fn tdx_command( + fd: &RawFd, + command: TdxCommand, + metadata: u32, + data: u64, +) -> std::result::Result<(), std::io::Error> { + #[repr(C)] + struct TdxIoctlCmd { + command: TdxCommand, + metadata: u32, + data: u64, + }; + let cmd = TdxIoctlCmd { + command, + metadata, + data, + }; + let ret = unsafe { + ioctl_with_val( + fd, + KVM_MEMORY_ENCRYPT_OP(), + &cmd as *const TdxIoctlCmd as std::os::raw::c_ulong, + ) + }; + + if ret < 0 { + return Err(std::io::Error::last_os_error()); + } + Ok(()) +} + /// Wrapper over KVM system ioctls. pub struct KvmHypervisor { kvm: Kvm, @@ -1332,6 +1453,15 @@ impl cpu::Vcpu for KvmVcpu { Ok(()) } + + /// + /// Initialize TDX for this CPU + /// + #[cfg(feature = "tdx")] + fn tdx_init(&self, hob_address: u64) -> cpu::Result<()> { + tdx_command(&self.fd.as_raw_fd(), TdxCommand::InitVcpu, 0, hob_address) + .map_err(cpu::HypervisorCpuError::InitializeTdx) + } } /// Device struct for KVM diff --git a/hypervisor/src/vm.rs b/hypervisor/src/vm.rs index 51a0436b8..de3d2c27d 100644 --- a/hypervisor/src/vm.rs +++ b/hypervisor/src/vm.rs @@ -12,6 +12,8 @@ use crate::aarch64::VcpuInit; use crate::cpu::Vcpu; use crate::device::Device; +#[cfg(feature = "tdx")] +use crate::x86_64::CpuId; #[cfg(all(feature = "kvm", target_arch = "x86_64"))] use crate::ClockData; #[cfg(feature = "kvm")] @@ -163,6 +165,25 @@ pub enum HypervisorVmError { /// #[error("Failed to assert virtual Interrupt: {0}")] AsserttVirtualInterrupt(#[source] anyhow::Error), + + #[cfg(feature = "tdx")] + /// + /// Error initializing TDX on the VM + /// + #[error("Failed to initialize TDX: {0}")] + InitializeTdx(#[source] std::io::Error), + #[cfg(feature = "tdx")] + /// + /// Error finalizing the TDX configuration on the VM + /// + #[error("Failed to finalize TDX: {0}")] + FinalizeTdx(#[source] std::io::Error), + #[cfg(feature = "tdx")] + /// + /// Error initializing the TDX memory region + /// + #[error("Failed to initialize memory region TDX: {0}")] + InitMemRegionTdx(#[source] std::io::Error), } /// /// Result type for returning from a function @@ -235,6 +256,21 @@ pub trait Vm: Send + Sync { fn set_state(&self, state: VmState) -> Result<()>; /// Get dirty pages bitmap fn get_dirty_log(&self, slot: u32, memory_size: u64) -> Result>; + #[cfg(feature = "tdx")] + /// Initalize TDX on this VM + fn tdx_init(&self, cpuid: &CpuId, max_vcpus: u32) -> Result<()>; + #[cfg(feature = "tdx")] + /// Finalize the configuration of TDX on this VM + fn tdx_finalize(&self) -> Result<()>; + #[cfg(feature = "tdx")] + /// Initalize a TDX memory region for this VM + fn tdx_init_memory_region( + &self, + host_address: u64, + guest_address: u64, + size: u64, + measure: bool, + ) -> Result<()>; } pub trait VmmOps: Send + Sync { diff --git a/vmm/Cargo.toml b/vmm/Cargo.toml index a4f24348c..bf081e15c 100644 --- a/vmm/Cargo.toml +++ b/vmm/Cargo.toml @@ -12,7 +12,7 @@ fwdebug = ["devices/fwdebug"] kvm = ["hypervisor/kvm"] mshv = ["hypervisor/mshv"] io_uring = ["virtio-devices/io_uring"] -tdx = ["arch/tdx"] +tdx = ["arch/tdx", "hypervisor/tdx"] [dependencies] acpi_tables = { path = "../acpi_tables", optional = true }