From cb844ecd1dfeaaede924f8d7d14f65da91435df1 Mon Sep 17 00:00:00 2001 From: Sebastien Boeuf Date: Wed, 16 Feb 2022 15:39:54 +0100 Subject: [PATCH] hypervisor: Add support for TDX exit reason to KVM Relying on the recent additions to the kvm-ioctls crate, this commit implements the support for providing the exit reason details to the caller, which allows the identification of the type of hypercall that was issued. It also introduces a way for the consumer to set the status code that must be sent back to the guest. Signed-off-by: Sebastien Boeuf --- hypervisor/src/cpu.rs | 20 ++++++++++++ hypervisor/src/kvm/mod.rs | 65 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 83 insertions(+), 2 deletions(-) diff --git a/hypervisor/src/cpu.rs b/hypervisor/src/cpu.rs index 8c15a070e..b5303854f 100644 --- a/hypervisor/src/cpu.rs +++ b/hypervisor/src/cpu.rs @@ -27,6 +27,8 @@ use crate::MpState; use crate::SuspendRegisters; #[cfg(target_arch = "x86_64")] use crate::Xsave; +#[cfg(feature = "tdx")] +use crate::{TdxExitDetails, TdxExitStatus}; #[cfg(feature = "mshv")] use mshv_bindings::*; use thiserror::Error; @@ -240,6 +242,12 @@ pub enum HypervisorCpuError { #[cfg(feature = "tdx")] #[error("Failed to initialize TDX: {0}")] InitializeTdx(#[source] std::io::Error), + /// + /// Unknown TDX VM call + /// + #[cfg(feature = "tdx")] + #[error("Unknown TDX VM call")] + UnknownTdxVmCall, } #[derive(Debug)] @@ -256,6 +264,8 @@ pub enum VmExit<'a> { Reset, Shutdown, Hyperv, + #[cfg(feature = "tdx")] + Tdx, } /// @@ -469,4 +479,14 @@ pub trait Vcpu: Send + Sync { /// Set the "immediate_exit" state /// fn set_immediate_exit(&self, exit: bool); + #[cfg(feature = "tdx")] + /// + /// Returns the details about TDX exit reason + /// + fn get_tdx_exit_details(&mut self) -> Result; + #[cfg(feature = "tdx")] + /// + /// Set the status code for TDX exit + /// + fn set_tdx_status(&mut self, status: TdxExitStatus); } diff --git a/hypervisor/src/kvm/mod.rs b/hypervisor/src/kvm/mod.rs index 0ec3a4888..6959439e1 100644 --- a/hypervisor/src/kvm/mod.rs +++ b/hypervisor/src/kvm/mod.rs @@ -81,7 +81,7 @@ pub use { kvm_bindings::kvm_clock_data as ClockData, kvm_bindings::kvm_create_device as CreateDevice, kvm_bindings::kvm_device_attr as DeviceAttr, kvm_bindings::kvm_irq_routing_entry as IrqRoutingEntry, kvm_bindings::kvm_mp_state as MpState, - kvm_bindings::kvm_userspace_memory_region as MemoryRegion, + kvm_bindings::kvm_run, kvm_bindings::kvm_userspace_memory_region as MemoryRegion, kvm_bindings::kvm_vcpu_events as VcpuEvents, kvm_ioctls::DeviceFd, kvm_ioctls::IoEventAddress, kvm_ioctls::VcpuExit, }; @@ -89,6 +89,17 @@ pub use { #[cfg(target_arch = "x86_64")] const KVM_CAP_SGX_ATTRIBUTE: u32 = 196; +#[cfg(feature = "tdx")] +const KVM_EXIT_TDX: u32 = 35; +#[cfg(feature = "tdx")] +const TDG_VP_VMCALL_GET_QUOTE: u64 = 0x10002; +#[cfg(feature = "tdx")] +const TDG_VP_VMCALL_SETUP_EVENT_NOTIFY_INTERRUPT: u64 = 0x10004; +#[cfg(feature = "tdx")] +const TDG_VP_VMCALL_SUCCESS: u64 = 0; +#[cfg(feature = "tdx")] +const TDG_VP_VMCALL_INVALID_OPERAND: u64 = 0x8000000000000000; + #[cfg(feature = "tdx")] ioctl_iowr_nr!(KVM_MEMORY_ENCRYPT_OP, KVMIO, 0xba, std::os::raw::c_ulong); @@ -103,6 +114,18 @@ enum TdxCommand { Finalize, } +#[cfg(feature = "tdx")] +pub enum TdxExitDetails { + GetQuote, + SetupEventNotifyInterrupt, +} + +#[cfg(feature = "tdx")] +pub enum TdxExitStatus { + Success, + InvalidOperand, +} + #[derive(Clone, Copy, Debug, PartialEq, Deserialize, Serialize)] pub struct KvmVmState {} @@ -1030,7 +1053,8 @@ impl cpu::Vcpu for KvmVcpu { Ok(cpu::VmExit::MmioWrite(addr, data)) } VcpuExit::Hyperv => Ok(cpu::VmExit::Hyperv), - + #[cfg(feature = "tdx")] + VcpuExit::Unsupported(KVM_EXIT_TDX) => Ok(cpu::VmExit::Tdx), r => Err(cpu::HypervisorCpuError::RunVcpu(anyhow!( "Unexpected exit reason on vcpu run: {:?}", r @@ -1602,6 +1626,43 @@ impl cpu::Vcpu for KvmVcpu { fn set_immediate_exit(&self, exit: bool) { self.fd.set_kvm_immediate_exit(exit.into()); } + + /// + /// Returns the details about TDX exit reason + /// + #[cfg(feature = "tdx")] + fn get_tdx_exit_details(&mut self) -> cpu::Result { + let kvm_run = self.fd.get_kvm_run(); + let tdx_vmcall = unsafe { &mut kvm_run.__bindgen_anon_1.tdx.u.vmcall }; + + tdx_vmcall.status_code = TDG_VP_VMCALL_INVALID_OPERAND; + + if tdx_vmcall.type_ != 0 { + return Err(cpu::HypervisorCpuError::UnknownTdxVmCall); + } + + match tdx_vmcall.subfunction { + TDG_VP_VMCALL_GET_QUOTE => Ok(TdxExitDetails::GetQuote), + TDG_VP_VMCALL_SETUP_EVENT_NOTIFY_INTERRUPT => { + Ok(TdxExitDetails::SetupEventNotifyInterrupt) + } + _ => Err(cpu::HypervisorCpuError::UnknownTdxVmCall), + } + } + + /// + /// Set the status code for TDX exit + /// + #[cfg(feature = "tdx")] + fn set_tdx_status(&mut self, status: TdxExitStatus) { + let kvm_run = self.fd.get_kvm_run(); + let tdx_vmcall = unsafe { &mut kvm_run.__bindgen_anon_1.tdx.u.vmcall }; + + tdx_vmcall.status_code = match status { + TdxExitStatus::Success => TDG_VP_VMCALL_SUCCESS, + TdxExitStatus::InvalidOperand => TDG_VP_VMCALL_INVALID_OPERAND, + }; + } } /// Device struct for KVM