Skip to content

Commit

Permalink
Implement KVM_TDX_CAPABILITIES
Browse files Browse the repository at this point in the history
Implements the API for running the KVM_TDX_CAPABILITIES sub-ioctl()
for the Intel TDX architecture.

The documentation for the structures implemented comes from the Intel TDX
Module 1.5 ABI spec.

Signed-off-by: Jake Correnti <[email protected]>
  • Loading branch information
jakecorrenti committed Feb 7, 2024
1 parent 7b8614c commit 30daa4a
Show file tree
Hide file tree
Showing 6 changed files with 364 additions and 3 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
43 changes: 43 additions & 0 deletions src/vm/linux/ioctl.rs
Original file line number Diff line number Diff line change
@@ -1 +1,44 @@
// 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.
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,
}
}
}
3 changes: 3 additions & 0 deletions src/vm/linux/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
// SPDX-License-Identifier: Apache-2.0

pub mod ioctl;
pub mod types;
97 changes: 97 additions & 0 deletions src/vm/linux/types.rs
Original file line number Diff line number Diff line change
@@ -1 +1,98 @@
// 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],
}
}
}
217 changes: 217 additions & 0 deletions src/vm/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,220 @@
// 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<Self, TdxError> {
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<TdxCapabilities, TdxError> {
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<CpuidConfig>,
}

#[derive(Debug)]
pub struct TdxError {
pub code: i32,
pub message: String,
}

impl From<kvm_ioctls::Error> 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()),
},
}
}
}
6 changes: 3 additions & 3 deletions tests/launch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}

0 comments on commit 30daa4a

Please sign in to comment.