From f282cc001aa936c7e0e937bfce61bdfd06f0f583 Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Fri, 12 Feb 2021 15:17:18 +0000 Subject: [PATCH] tdx: Add abstraction to call TDX ioctls to hypervisor Add API to the hypervisor interface and implement for KVM to allow the special TDX KVM ioctls on the VM and vCPU FDs. Signed-off-by: Rob Bradford --- hypervisor/Cargo.toml | 1 + hypervisor/src/cpu.rs | 11 ++++ hypervisor/src/kvm/mod.rs | 130 ++++++++++++++++++++++++++++++++++++++ hypervisor/src/vm.rs | 36 +++++++++++ vmm/Cargo.toml | 2 +- 5 files changed, 179 insertions(+), 1 deletion(-) 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 }