diff --git a/Cargo.toml b/Cargo.toml index f9dd177..18bab8d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,4 +13,5 @@ categories = ["os", "os::linux-apis", "hardware-support"] rust-version = "1.71" [dependencies] +bitflags = "2.4.2" kvm-ioctls = "0.16.0" diff --git a/src/vm/linux/ioctl.rs b/src/vm/linux/ioctl.rs index cdc649a..a4fc873 100644 --- a/src/vm/linux/ioctl.rs +++ b/src/vm/linux/ioctl.rs @@ -1 +1,41 @@ // SPDX-License-Identifier: Apache-2.0 + +use crate::vm::linux::types::Capabilities; + +/// Trust Domain eXtensions sub-ioctl() commands +#[repr(u32)] +pub enum CmdId { + GetCapabilities = 0, +} + +/// Contains information for the sub-ioctl() command to be run. This is +/// equivalent to `struct kvm_tdx_cmd` in the kernel. +#[derive(Default)] +#[repr(C)] +pub struct Cmd { + /// TDX command identifier + pub id: u32, + /// Flags for sub-command. If sub-command doesn't use it, set to zero. + pub flags: u32, + /// A u64 representing a generic pointer to the respective ioctl input. + /// This data is read differently according to the TDX ioctl identifier. + pub data: u64, + /// Auxiliary error code. The sub-command may return TDX SEAMCALL status + /// code in addition to -Exxx. + /// Defined for consistency with `struct kvm_sev_cmd` + pub error: u64, + /// Reserved. + pub unused: u64, +} + +impl From<&Capabilities> for Cmd { + fn from(caps: &Capabilities) -> Self { + Self { + id: CmdId::GetCapabilities as u32, + flags: 0, + data: caps as *const Capabilities as _, + error: 0, + unused: 0, + } + } +} diff --git a/src/vm/linux/mod.rs b/src/vm/linux/mod.rs index cdc649a..a6dd89a 100644 --- a/src/vm/linux/mod.rs +++ b/src/vm/linux/mod.rs @@ -1 +1,4 @@ // SPDX-License-Identifier: Apache-2.0 + +pub mod ioctl; +pub mod types; diff --git a/src/vm/linux/types.rs b/src/vm/linux/types.rs index cdc649a..09ddcb5 100644 --- a/src/vm/linux/types.rs +++ b/src/vm/linux/types.rs @@ -1 +1,86 @@ // SPDX-License-Identifier: Apache-2.0 + +pub const NR_CPUID_CONFIGS: usize = 12; + +/// CPUID_CONFIG is designed to enumerate how the host VMM may configure the +/// virtualization done by the Intel TDX module for a single CPUID leaf and +/// sub-leaf. This is equivalent to `struct kvm_tdx_cpuid_config` in the kernel. +#[derive(Debug, Default, Clone, Copy)] +#[repr(C)] +pub struct CpuidConfig { + /// EAX input value to CPUID + pub leaf: u32, + /// ECX input value to CPUID. A value of -1 indicates a CPUID leaf with + /// no sub-leaves. + pub sub_leaf: u32, + /// CPUID configuration information for the EAX register. + pub eax: u32, + /// CPUID configuration information for the EBX register. + pub ebx: u32, + /// CPUID configuration information for the ECX register. + pub ecx: u32, + /// CPUID configuration information for the EDX register. + pub edx: u32, +} + +/// Provides information about the Intel TDX module. This is equivalent to +/// `struct kvm_tdx_capabilities` in the kernel. +#[derive(Debug)] +#[repr(C)] +pub struct Capabilities { + /// Bitmap where if any certain bit is 0, it must be 0 in any TD's + /// ATTRIBUTES, which specifies various guest TD attributes. The value of + /// this field reflects the Intel TDX module capabilities and configuration + /// and CPU capabilities. + pub attrs_fixed0: u64, + /// Bitmap where if any certain bit is 1, it must be 1 in any TD's + /// ATTRIBUTES, which specifies various guest TD attributes. The value of + /// this field reflects the Intel TDX module capabilities and configuration + /// and CPU capabilities. + pub attrs_fixed1: u64, + /// Bitmap where if any certain bit is 0, it must be 0 in any TD's XFAM. + /// XFAM (eXtended Features Available Mask) determines the set of extended + /// features available for use by the guest TD. + pub xfam_fixed0: u64, + /// Bitmap where if any certain bit is 1, it must be 1 in any TD's XFAM. + /// XFAM (eXtended Features Available Mask) determines the set of extended + /// features available for use by the guest TD. + pub xfam_fixed1: u64, + /// Supported Guest Physical Address Width + pub supported_gpaw: u32, + /// Padding space. Ignored + _padding: u32, + /// Reserved space. Ignored. + _reserved: [u64; 251], + + /// Number of CPUID_CONFIG entries + pub nr_cpuid_configs: u32, + /// Enumeration of the CPUID leaves/sub-leaves that contain bit fields whose + /// virtualization by the Intel TDX module is either: + /// + /// - Directly configurable (CONFIG_DIRECT) by the host VMM + /// - Bits that the host VMM may allow to be 1 (ALLOW_DIRECT) and their + /// native value, as returned by the CPU, is 1 + /// + /// Note that the virtualization of many CPUID bit fields not enumerated in + /// this list is configurable indirectly via the XFAM and ATTRIBUTES assigned + /// to a TD by the host VMM. + pub cpuid_configs: [CpuidConfig; NR_CPUID_CONFIGS], +} + +impl Default for Capabilities { + fn default() -> Self { + Self { + attrs_fixed0: 0, + attrs_fixed1: 0, + xfam_fixed0: 0, + xfam_fixed1: 0, + supported_gpaw: 0, + _padding: 0, + _reserved: [0; 251], + + nr_cpuid_configs: NR_CPUID_CONFIGS as u32, + cpuid_configs: [Default::default(); NR_CPUID_CONFIGS], + } + } +} diff --git a/src/vm/mod.rs b/src/vm/mod.rs index ba8eabc..bbd19c7 100644 --- a/src/vm/mod.rs +++ b/src/vm/mod.rs @@ -1,3 +1,194 @@ // SPDX-License-Identifier: Apache-2.0 mod linux; + +use crate::vm::linux::{ + ioctl::Cmd, + types::{Capabilities, CpuidConfig}, +}; +use bitflags::bitflags; +use kvm_ioctls::{Kvm, VmFd}; + +// Defined in linux/arch/x86/include/uapi/asm/kvm.h +const KVM_X86_TDX_VM: u64 = 2; + +/// Handle to the TDX VM file descriptor +pub struct TdxVm(VmFd); + +impl TdxVm { + /// Create a new TDX VM with KVM + pub fn new(kvm_fd: Kvm) -> Result { + let vm_fd = kvm_fd.create_vm_with_type(KVM_X86_TDX_VM)?; + Ok(Self(vm_fd)) + } + + /// Retrieve information about the Intel TDX module + pub fn get_capabilities(&self) -> Result { + let caps = Capabilities::default(); + let mut cmd: Cmd = Cmd::from(&caps); + + unsafe { + if let Err(e) = self.0.encrypt_op(&mut cmd) { + return Err(TdxError::from(e)); + } + } + + Ok(TdxCapabilities { + attributes: Attributes { + fixed0: AttributesFlags::from_bits_truncate(caps.attrs_fixed0), + fixed1: AttributesFlags::from_bits_truncate(caps.attrs_fixed1), + }, + xfam: Xfam { + fixed0: XFAMFlags::from_bits_truncate(caps.xfam_fixed0), + fixed1: XFAMFlags::from_bits_truncate(caps.xfam_fixed1), + }, + supported_gpaw: caps.supported_gpaw, + cpuid_configs: Vec::from(caps.cpuid_configs), + }) + } +} + +bitflags! { + #[derive(Debug)] + pub struct AttributesFlags: u64 { + /// TD Under Debug (TUD) group + + /// Bit 0. Guest TD runs in off-TD debug mode + const DEBUG = 1; + /// Bits 3:1. Reserved for future TUD flags + const TUD_RESERVED = 0x7 << 1; + + /// TD Under Profiling (TUP) group + + /// Bit 4. The TD participates in HGS+ operation + const HGS_PLUS_PROF = 1 << 4; + /// Bit 5. The TD participates in system profiling using performance monitoring + /// counters + const PERF_PROF = 1 << 5; + /// Bit 6. The TD participates in system profiling using core out-of-band + /// telemetry + const PMT_PROF = 1 << 6; + /// Bits 15:7. Reserved for future TUP flags + const TUP_RESERVED = 0x1FF << 7; + + /// Security (SEC) group + + /// Bits 22:16. Reserved for future SEC flags that will indicate positive impact on + /// TD security + const SEC_RESERVED_P = 0x7F << 16; + /// Bits 23:26. Reserved for future SEC flags that will indicate negative impact on + /// TD security + const SEC_RESERVED_N = 0xF << 23; + /// Bit 27. TD is allowed to use Linear Address Space Separation + const LASS = 1 << 27; + /// Bit 28. Disable EPT violation conversion to #VE on guest TD access of + /// PENDING pages + const SEPT_VE_DISABLE = 1 << 28; + /// Bit 29. TD is migratable (using a Migration TD) + const MIGRATABLE = 1 << 29; + /// Bit 30. TD is allowed to use Supervisor Protection Keys + const PKS = 1 << 30; + /// Bit 31. TD is allowed to use Key Locker + const KL = 1 << 31; + + /// RESERVED Group + + /// Bits 55:32. Reserved for future expansion of the SEC group + const SEC_EXP_RESERVED = 0xFFFFFF << 32; + + /// OTHER group + + /// Bits 61:32. Reserved for future OTHER flags + const OTHER_RESERVED = 0x3FFFFFFF << 32; + /// Bit 62. The TD is a TDX Connet Provisioning Agent + const TPA = 1 << 62; + /// Bit 63. TD is allowed to use Perfmon and PERF_METRICS capabilities + const PERFMON = 1 << 63; + } + + #[derive(Debug)] + pub struct XFAMFlags: u64 { + /// Bit 0. Always enabled + const FP = 1; + /// Bit 1. Always enabled + const SSE = 1 << 1; + /// Bit 2. Execution is directly controlled by XCR0 + const AVX = 1 << 2; + /// Bits 4:3. Being deprecated + const MPX = 0x3 << 3; + /// Bits 7:5. Execution is directly contrtolled by XCR0. May be enabled only if + /// AVX is enabled + const AVX512 = 0x7 << 5; + /// Bit 8. Execution is controlled by IA32_RTIT_CTL + const PT = 1 << 8; + /// Bit 9. Execution is controlled by CR4.PKE + const PK = 1 << 9; + /// Bit 10. Execution is controlled by IA32_PASID MSR + const ENQCMD = 1 << 10; + /// Bits 12:11. Execution is controlled by CR4.CET + const CET = 0x3 << 11; + /// Bit 13. Hardware Duty Cycle is controlled by package-scope IA32_PKG_HDC_CTL + /// and LP-scope IA32_PM_CTL1 MSRs + const HDC = 1 << 13; + /// Bit 14. Execution is controlled by CR4.UINTR + const ULI = 1 << 14; + /// Bit 15. Execution is controlled by IA32_LBR_CTL + const LBR = 1 << 15; + /// Bit 16. Execution of Hardware-Controlled Performance State is controlled by + /// IA32_HWP MSRs + const HWP = 1 << 16; + /// Bits 18:17. Advanced Matrix Extensions (AMX) is directly controlled by XCR0 + const AMX = 0x3 << 17; + } +} + +/// Reflects the Intel TDX module capabilities and configuration and CPU +/// capabilities +#[derive(Debug)] +pub struct Attributes { + pub fixed0: AttributesFlags, + pub fixed1: AttributesFlags, +} + +/// Determines the set of extended features available for use by the guest TD +#[derive(Debug)] +pub struct Xfam { + pub fixed0: XFAMFlags, + pub fixed1: XFAMFlags, +} + +/// Provides information about the Intel TDX module +#[derive(Debug)] +pub struct TdxCapabilities { + pub attributes: Attributes, + pub xfam: Xfam, + /// supported Guest Physical Address Width + pub supported_gpaw: u32, + + pub cpuid_configs: Vec, +} + +#[derive(Debug)] +pub struct TdxError { + pub code: i32, + pub message: String, +} + +impl From for TdxError { + fn from(kvm_err: kvm_ioctls::Error) -> Self { + match kvm_err.errno() { + 7 => TdxError { + code: 7, + message: String::from("Invalid value for NR_CPUID_CONFIGS"), + }, + 25 => TdxError { + code: 25, + message: String::from("Inappropriate ioctl for device. Ensure the proper VM type is being used for the ioctl"), + }, + _ => TdxError { + code: kvm_err.errno(), + message: format!("errno: {}", kvm_err.errno()), + }, + } + } +} diff --git a/tests/launch.rs b/tests/launch.rs index 873ee00..972c935 100644 --- a/tests/launch.rs +++ b/tests/launch.rs @@ -2,11 +2,11 @@ use kvm_ioctls::Kvm; -// Defined in linux/arch/x86/include/uapi/asm/kvm.h -const KVM_X86_TDX_VM: u64 = 2; +use tdx::vm::TdxVm; #[test] fn launch() { let kvm_fd = Kvm::new().unwrap(); - let vm_fd = kvm_fd.create_vm_with_type(KVM_X86_TDX_VM).unwrap(); + let tdx_vm = TdxVm::new(kvm_fd).unwrap(); + let _caps = tdx_vm.get_capabilities().unwrap(); }