use crate::vmx;
use bitfield_struct::bitfield;
use open_enum::open_enum;
use zerocopy::AsBytes;
use zerocopy::FromBytes;
use zerocopy::FromZeroes;
pub const TDX_SHARED_GPA_BOUNDARY_BITS: u8 = 47;
pub const TDX_SHARED_GPA_BOUNDARY_ADDRESS_BIT: u64 = 1 << TDX_SHARED_GPA_BOUNDARY_BITS;
open_enum! {
pub enum TdCallLeaf: u64 {
VP_VMCALL = 0,
VP_INFO = 1,
MR_RTMR_EXTEND = 2,
VP_VEINFO_GET = 3,
MR_REPORT = 4,
VP_CPUIDVE_SET = 5,
MEM_PAGE_ACCEPT = 6,
VM_RD = 7,
VM_WR = 8,
VP_RD = 9,
VP_WR = 10,
MEM_PAGE_ATTR_RD = 23,
MEM_PAGE_ATTR_WR = 24,
VP_ENTER = 25,
VP_INVGLA = 27,
}
}
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum TdgMemPageLevel {
Size4k = 0,
Size2Mb = 1,
Size1Gb = 2,
}
impl TdgMemPageLevel {
const fn from_bits(value: u64) -> Self {
match value {
0 => Self::Size4k,
1 => Self::Size2Mb,
2 => Self::Size1Gb,
_ => panic!("invalid TdgMemPageLevel value"),
}
}
const fn into_bits(self) -> u64 {
self as u64
}
}
#[bitfield(u16)]
pub struct GpaVmAttributes {
pub read: bool,
pub write: bool,
pub kernel_execute: bool,
pub user_execute: bool,
#[bits(3)]
reserved: u8,
suppress_ve: bool,
#[bits(7)]
reserved2: u8,
pub valid: bool,
}
impl GpaVmAttributes {
pub const FULL_ACCESS: Self = Self::new()
.with_read(true)
.with_write(true)
.with_kernel_execute(true)
.with_user_execute(true)
.with_valid(true);
}
impl GpaVmAttributes {
pub fn to_mask(self) -> GpaVmAttributesMask {
GpaVmAttributesMask::from(u16::from(self)).with_inv_ept(false)
}
}
#[bitfield(u16)]
pub struct GpaVmAttributesMask {
read: bool,
write: bool,
kernel_execute: bool,
user_execute: bool,
#[bits(3)]
reserved: u8,
suppress_ve: bool,
#[bits(7)]
reserved2: u8,
inv_ept: bool,
}
#[bitfield(u64)]
#[derive(PartialEq, Eq)]
pub struct TdgMemPageGpaAttr {
#[bits(16)]
pub l1: GpaVmAttributes,
#[bits(16)]
pub l2_vm1: GpaVmAttributes,
#[bits(16)]
pub l2_vm2: GpaVmAttributes,
#[bits(16)]
pub l2_vm3: GpaVmAttributes,
}
#[bitfield(u64)]
pub struct TdgMemPageAcceptRcx {
#[bits(3)]
pub level: TdgMemPageLevel,
#[bits(9)]
pub reserved: u64,
#[bits(40)]
pub gpa_page_number: u64,
#[bits(12)]
pub reserved2: u64,
}
#[bitfield(u64)]
pub struct TdgMemPageAttrGpaMappingReadRcxResult {
#[bits(3)]
pub level: TdgMemPageLevel,
#[bits(9)]
pub reserved: u64,
#[bits(40)]
pub gpa_page_number: u64,
#[bits(10)]
pub reserved2: u64,
#[bits(1)]
pub pending: u8,
#[bits(1)]
pub reserved3: u64,
}
#[bitfield(u64)]
#[derive(PartialEq, Eq)]
pub struct TdgMemPageAttrWriteRcx {
#[bits(3)]
pub level: TdgMemPageLevel,
#[bits(9)]
pub reserved: u64,
#[bits(40)]
pub gpa_page_number: u64,
#[bits(12)]
pub reserved2: u64,
}
#[bitfield(u64)]
pub struct TdgMemPageAttrWriteR8 {
#[bits(16)]
pub reserved: u64,
#[bits(16)]
pub l2_vm1: GpaVmAttributesMask,
#[bits(16)]
pub l2_vm2: GpaVmAttributesMask,
#[bits(16)]
pub l2_vm3: GpaVmAttributesMask,
}
#[repr(u64)]
pub enum TdVmCallSubFunction {
IoInstr = 0x1e,
RdMsr = 0x1f,
WrMsr = 0x20,
MapGpa = 0x10001,
}
open_enum! {
#[derive(AsBytes, FromBytes, FromZeroes)]
pub enum TdCallResultCode: u32 {
SUCCESS = 0x00000000,
NON_RECOVERABLE_VCPU = 0x40000001,
NON_RECOVERABLE_TD = 0x60000002,
INTERRUPTED_RESUMABLE = 0x80000003,
INTERRUPTED_RESTARTABLE = 0x80000004,
NON_RECOVERABLE_TD_NON_ACCESSIBLE = 0x60000005,
INVALID_RESUMPTION = 0xC0000006,
NON_RECOVERABLE_TD_WRONG_APIC_MODE = 0xE0000007,
CROSS_TD_FAULT = 0x80000008,
CROSS_TD_TRAP = 0x90000009,
NON_RECOVERABLE_TD_CORRUPTED_MD = 0x6000000A,
OPERAND_INVALID = 0xC0000100,
OPERAND_ADDR_RANGE_ERROR = 0xC0000101,
OPERAND_BUSY = 0x80000200,
PREVIOUS_TLB_EPOCH_BUSY = 0x80000201,
SYS_BUSY = 0x80000202,
RND_NO_ENTROPY = 0x80000203,
OPERAND_BUSY_HOST_PRIORITY = 0x80000204,
HOST_PRIORITY_BUSY_TIMEOUT = 0x90000205,
PAGE_METADATA_INCORRECT = 0xC0000300,
PAGE_ALREADY_FREE = 0x00000301,
PAGE_NOT_OWNED_BY_TD = 0xC0000302,
PAGE_NOT_FREE = 0xC0000303,
TD_ASSOCIATED_PAGES_EXIST = 0xC0000400,
SYS_INIT_NOT_PENDING = 0xC0000500,
SYS_LP_INIT_NOT_DONE = 0xC0000502,
SYS_LP_INIT_DONE = 0xC0000503,
SYS_NOT_READY = 0xC0000505,
SYS_SHUTDOWN = 0xC0000506,
SYS_KEY_CONFIG_NOT_PENDING = 0xC0000507,
SYS_STATE_INCORRECT = 0xC0000508,
SYS_INVALID_HANDOFF = 0xC0000509,
SYS_INCOMPATIBLE_SIGSTRUCT = 0xC000050A,
SYS_LP_INIT_NOT_PENDING = 0xC000050B,
SYS_CONFIG_NOT_PENDING = 0xC000050C,
INCOMPATIBLE_SEAM_CAPABILITIES = 0xC000050D,
TD_FATAL = 0xE0000604,
TD_NON_DEBUG = 0xC0000605,
TDCS_NOT_ALLOCATED = 0xC0000606,
LIFECYCLE_STATE_INCORRECT = 0xC0000607,
OP_STATE_INCORRECT = 0xC0000608,
NO_VCPUS = 0xC0000609,
TDCX_NUM_INCORRECT = 0xC0000610,
VCPU_STATE_INCORRECT = 0xC0000700,
VCPU_ASSOCIATED = 0x80000701,
VCPU_NOT_ASSOCIATED = 0x80000702,
NO_VALID_VE_INFO = 0xC0000704,
MAX_VCPUS_EXCEEDED = 0xC0000705,
TSC_ROLLBACK = 0xC0000706,
TD_VMCS_FIELD_NOT_INITIALIZED = 0xC0000730,
MCS_FIELD_ERROR = 0xC0000731,
KEY_GENERATION_FAILED = 0x80000800,
TD_KEYS_NOT_CONFIGURED = 0x80000810,
KEY_STATE_INCORRECT = 0xC0000811,
KEY_CONFIGURED = 0x00000815,
WBCACHE_NOT_COMPLETE = 0x80000817,
HKID_NOT_FREE = 0xC0000820,
NO_HKID_READY_TO_WBCACHE = 0x00000821,
WBCACHE_RESUME_ERROR = 0xC0000823,
FLUSHVP_NOT_DONE = 0x80000824,
NUM_ACTIVATED_HKIDS_NOT_SUPPORTED = 0xC0000825,
INCORRECT_CPUID_VALUE = 0xC0000900,
LIMIT_CPUID_MAXVAL_SET = 0xC0000901,
INCONSISTENT_CPUID_FIELD = 0xC0000902,
CPUID_MAX_SUBLEAVES_UNRECOGNIZED = 0xC0000903,
CPUID_LEAF_1F_FORMAT_UNRECOGNIZED = 0xC0000904,
INVALID_WBINVD_SCOPE = 0xC0000905,
INVALID_PKG_ID = 0xC0000906,
ENABLE_MONITOR_FSM_NOT_SET = 0xC0000907,
CPUID_LEAF_NOT_SUPPORTED = 0xC0000908,
SMRR_NOT_LOCKED = 0xC0000910,
INVALID_SMRR_CONFIGURATION = 0xC0000911,
SMRR_OVERLAPS_CMR = 0xC0000912,
SMRR_LOCK_NOT_SUPPORTED = 0xC0000913,
SMRR_NOT_SUPPORTED = 0xC0000914,
INCONSISTENT_MSR = 0xC0000920,
INCORRECT_MSR_VALUE = 0xC0000921,
SEAMREPORT_NOT_AVAILABLE = 0xC0000930,
SEAMDB_GETREF_NOT_AVAILABLE = 0xC0000931,
SEAMDB_REPORT_NOT_AVAILABLE = 0xC0000932,
SEAMVERIFYREPORT_NOT_AVAILABLE = 0xC0000933,
INVALID_TDMR = 0xC0000A00,
NON_ORDERED_TDMR = 0xC0000A01,
TDMR_OUTSIDE_CMRS = 0xC0000A02,
TDMR_ALREADY_INITIALIZED = 0x00000A03,
INVALID_PAMT = 0xC0000A10,
PAMT_OUTSIDE_CMRS = 0xC0000A11,
PAMT_OVERLAP = 0xC0000A12,
INVALID_RESERVED_IN_TDMR = 0xC0000A20,
NON_ORDERED_RESERVED_IN_TDMR = 0xC0000A21,
CMR_LIST_INVALID = 0xC0000A22,
EPT_WALK_FAILED = 0xC0000B00,
EPT_ENTRY_FREE = 0xC0000B01,
EPT_ENTRY_NOT_FREE = 0xC0000B02,
EPT_ENTRY_NOT_PRESENT = 0xC0000B03,
EPT_ENTRY_NOT_LEAF = 0xC0000B04,
EPT_ENTRY_LEAF = 0xC0000B05,
GPA_RANGE_NOT_BLOCKED = 0xC0000B06,
GPA_RANGE_ALREADY_BLOCKED = 0x00000B07,
TLB_TRACKING_NOT_DONE = 0xC0000B08,
EPT_INVALID_PROMOTE_CONDITIONS = 0xC0000B09,
PAGE_ALREADY_ACCEPTED = 0x00000B0A,
PAGE_SIZE_MISMATCH = 0xC0000B0B,
GPA_RANGE_BLOCKED = 0xC0000B0C,
EPT_ENTRY_STATE_INCORRECT = 0xC0000B0D,
EPT_PAGE_NOT_FREE = 0xC0000B0E,
L2_SEPT_WALK_FAILED = 0xC0000B0F,
L2_SEPT_ENTRY_NOT_FREE = 0xC0000B10,
PAGE_ATTR_INVALID = 0xC0000B11,
L2_SEPT_PAGE_NOT_PROVIDED = 0xC0000B12,
METADATA_FIELD_ID_INCORRECT = 0xC0000C00,
METADATA_FIELD_NOT_WRITABLE = 0xC0000C01,
METADATA_FIELD_NOT_READABLE = 0xC0000C02,
METADATA_FIELD_VALUE_NOT_VALID = 0xC0000C03,
METADATA_LIST_OVERFLOW = 0xC0000C04,
INVALID_METADATA_LIST_HEADER = 0xC0000C05,
REQUIRED_METADATA_FIELD_MISSING = 0xC0000C06,
METADATA_ELEMENT_SIZE_INCORRECT = 0xC0000C07,
METADATA_LAST_ELEMENT_INCORRECT = 0xC0000C08,
METADATA_FIELD_CURRENTLY_NOT_WRITABLE = 0xC0000C09,
METADATA_WR_MASK_NOT_VALID = 0xC0000C0A,
METADATA_FIRST_FIELD_ID_IN_CONTEXT = 0x00000C0B,
METADATA_FIELD_SKIP = 0x00000C0C,
SERVTD_ALREADY_BOUND_FOR_TYPE = 0xC0000D00,
SERVTD_TYPE_MISMATCH = 0xC0000D01,
SERVTD_ATTR_MISMATCH = 0xC0000D02,
SERVTD_INFO_HASH_MISMATCH = 0xC0000D03,
SERVTD_UUID_MISMATCH = 0xC0000D04,
SERVTD_NOT_BOUND = 0xC0000D05,
SERVTD_BOUND = 0xC0000D06,
TARGET_UUID_MISMATCH = 0xC0000D07,
TARGET_UUID_UPDATED = 0xC0000D08,
INVALID_MBMD = 0xC0000E00,
INCORRECT_MBMD_MAC = 0xC0000E01,
NOT_WRITE_BLOCKED = 0xC0000E02,
ALREADY_WRITE_BLOCKED = 0x00000E03,
NOT_EXPORTED = 0xC0000E04,
MIGRATION_STREAM_STATE_INCORRECT = 0xC0000E05,
MAX_MIGS_NUM_EXCEEDED = 0xC0000E06,
EXPORTED_DIRTY_PAGES_REMAIN = 0xC0000E07,
MIGRATION_DECRYPTION_KEY_NOT_SET = 0xC0000E08,
TD_NOT_MIGRATABLE = 0xC0000E09,
PREVIOUS_EXPORT_CLEANUP_INCOMPLETE = 0xC0000E0A,
NUM_MIGS_HIGHER_THAN_CREATED = 0xC0000E0B,
IMPORT_MISMATCH = 0xC0000E0C,
MIGRATION_EPOCH_OVERFLOW = 0xC0000E0D,
MAX_EXPORTS_EXCEEDED = 0xC0000E0E,
INVALID_PAGE_MAC = 0xC0000E0F,
MIGRATED_IN_CURRENT_EPOCH = 0xC0000E10,
DISALLOWED_IMPORT_OVER_REMOVED = 0xC0000E11,
SOME_VCPUS_NOT_MIGRATED = 0xC0000E12,
ALL_VCPUS_IMPORTED = 0xC0000E13,
MIN_MIGS_NOT_CREATED = 0xC0000E14,
VCPU_ALREADY_EXPORTED = 0xC0000E15,
INVALID_MIGRATION_DECRYPTION_KEY = 0xC0000E16,
INVALID_CPUSVN = 0xC0001000,
INVALID_REPORTMACSTRUCT = 0xC0001001,
L2_EXIT_HOST_ROUTED_ASYNC = 0x00001100,
L2_EXIT_HOST_ROUTED_TDVMCALL = 0x00001101,
L2_EXIT_PENDING_INTERRUPT = 0x00001102,
PENDING_INTERRUPT = 0x00001120,
TD_EXIT_BEFORE_L2_ENTRY = 0x00001140,
TD_EXIT_ON_L2_VM_EXIT = 0x00001141,
TD_EXIT_ON_L2_TO_L1 = 0x00001142,
GLA_NOT_CANONICAL = 0xC0001160,
}
}
impl TdCallResultCode {
const fn from_bits(value: u64) -> Self {
Self(value as u32)
}
const fn into_bits(self) -> u64 {
self.0 as u64
}
}
#[bitfield(u64)]
pub struct TdCallResult {
pub details: u32,
#[bits(32)]
pub code: TdCallResultCode,
}
open_enum! {
#[derive(AsBytes, FromBytes, FromZeroes)]
pub enum TdVmCallR10Result: u64 {
SUCCESS = 0,
RETRY = 1,
OPERAND_INVALID = 0x80000000_00000000,
GPA_INUSE = 0x80000000_00000001,
ALIGN_ERROR = 0x80000000_00000002,
}
}
#[repr(u64)]
#[derive(Debug)]
pub enum FieldSize {
Invalid = 0,
Size16Bit = 1,
Size32Bit = 2,
Size64Bit = 3,
}
impl FieldSize {
const fn from_bits(value: u64) -> Self {
match value {
0 => FieldSize::Invalid,
1 => FieldSize::Size16Bit,
2 => FieldSize::Size32Bit,
3 => FieldSize::Size64Bit,
_ => panic!("Invalid field size"),
}
}
const fn into_bits(self) -> u64 {
self as u64
}
}
open_enum! {
pub enum TdVpsClassCode: u8 {
TD_VMCS = 0,
VAPIC = 1,
VE_INFO = 2,
GUEST_GPR_STATE = 16,
GUEST_STATE = 17,
GUEST_EXT_STATE = 18,
GUEST_MSR_STATE = 19,
MANAGEMENT = 32,
CPUID_CONTROL = 33,
EPT_VIOLATION_LOG = 34,
VMCS_1 = 36,
MSR_BITMAPS_1 = 37,
MSR_BITMAPS_SHADOW_1 = 38,
VMCS_2 = 44,
MSR_BITMAPS_2 = 45,
MSR_BITMAPS_SHADOW_2 = 46,
VMCS_3 = 52,
}
}
open_enum! {
pub enum TdxContextCode: u8 {
PLATFORM = 0,
TD = 1,
TD_VCPU = 2,
}
}
impl TdxContextCode {
const fn from_bits(value: u64) -> Self {
Self(value as u8)
}
const fn into_bits(self) -> u64 {
self.0 as u64
}
}
pub const TDX_FIELD_CODE_L2_CTLS_VM1: TdxExtendedFieldCode =
TdxExtendedFieldCode(0xA020000300000051);
pub const TDX_FIELD_CODE_L2_CTLS_VM2: TdxExtendedFieldCode =
TdxExtendedFieldCode(0xA020000300000051);
#[bitfield(u64)]
pub struct TdxExtendedFieldCode {
#[bits(24)]
pub field_code: u32,
#[bits(8)]
_reserved0: u64,
#[bits(2)]
pub field_size: FieldSize,
#[bits(4)]
pub last_element: u8,
#[bits(9)]
pub last_field: u16,
#[bits(3)]
_reserved1: u64,
pub increment_size: bool,
pub write_mask_valid: bool,
#[bits(3)]
pub context_code: TdxContextCode,
#[bits(1)]
_reserved2: u64,
#[bits(6)]
pub class_code: u8,
#[bits(1)]
_reserved3: u64,
pub non_arch: bool,
}
#[bitfield(u64)]
pub struct TdxInstructionInfo {
pub info: u32, pub length: u32,
}
#[bitfield(u64)]
pub struct TdxL2Ctls {
pub enable_shared_ept: bool,
pub enable_tdvmcall: bool,
#[bits(62)]
pub reserved: u64,
}
#[bitfield(u64)]
pub struct TdxVpEnterRaxResult {
#[bits(32)]
pub vmx_exit: vmx::VmxExit,
#[bits(32)]
pub tdx_exit: TdCallResultCode,
}
#[bitfield(u64)]
pub struct TdxExtendedExitQualification {
#[bits(4)]
pub ty: TdxExtendedExitQualificationType,
#[bits(60)]
_reserved: u64,
}
open_enum! {
pub enum TdxExtendedExitQualificationType: u8 {
NONE = 0,
PENDING_EPT_VIOLATION = 6,
}
}
impl TdxExtendedExitQualificationType {
const fn from_bits(value: u64) -> Self {
Self(value as u8)
}
const fn into_bits(self) -> u64 {
self.0 as u64
}
}
#[repr(C)]
#[derive(Debug, AsBytes, FromBytes, FromZeroes)]
pub struct TdxL2EnterGuestState {
pub gps: [u64; 16],
pub rflags: u64,
pub rip: u64,
pub ssp: u64,
pub rvi: u8, pub svi: u8, pub reserved: [u8; 6],
}
impl TdxL2EnterGuestState {
pub const RAX: usize = 0;
pub const RCX: usize = 1;
pub const RDX: usize = 2;
pub const RBX: usize = 3;
pub const RSP: usize = 4;
pub const RBP: usize = 5;
pub const RSI: usize = 6;
pub const RDI: usize = 7;
pub const R8: usize = 8;
pub const R9: usize = 9;
pub const R10: usize = 10;
pub const R11: usize = 11;
pub const R12: usize = 12;
pub const R13: usize = 13;
pub const R14: usize = 14;
pub const R15: usize = 15;
pub fn rax(&self) -> u64 {
self.gps[Self::RAX]
}
pub fn set_rax(&mut self, v: u64) {
self.gps[Self::RAX] = v
}
pub fn rcx(&self) -> u64 {
self.gps[Self::RCX]
}
pub fn set_rcx(&mut self, v: u64) {
self.gps[Self::RCX] = v
}
pub fn rdx(&self) -> u64 {
self.gps[Self::RDX]
}
pub fn set_rdx(&mut self, v: u64) {
self.gps[Self::RDX] = v
}
pub fn rbx(&self) -> u64 {
self.gps[Self::RBX]
}
pub fn set_rbx(&mut self, v: u64) {
self.gps[Self::RBX] = v
}
pub fn rsp(&self) -> u64 {
self.gps[Self::RSP]
}
pub fn set_rsp(&mut self, v: u64) {
self.gps[Self::RSP] = v
}
pub fn rbp(&self) -> u64 {
self.gps[Self::RBP]
}
pub fn set_rbp(&mut self, v: u64) {
self.gps[Self::RBP] = v
}
pub fn rsi(&self) -> u64 {
self.gps[Self::RSI]
}
pub fn set_rsi(&mut self, v: u64) {
self.gps[Self::RSI] = v
}
pub fn rdi(&self) -> u64 {
self.gps[Self::RDI]
}
pub fn set_rdi(&mut self, v: u64) {
self.gps[Self::RDI] = v
}
pub fn r8(&self) -> u64 {
self.gps[Self::R8]
}
pub fn set_r8(&mut self, v: u64) {
self.gps[Self::R8] = v
}
pub fn r9(&self) -> u64 {
self.gps[Self::R9]
}
pub fn set_r9(&mut self, v: u64) {
self.gps[Self::R9] = v
}
pub fn r10(&self) -> u64 {
self.gps[Self::R10]
}
pub fn set_r10(&mut self, v: u64) {
self.gps[Self::R10] = v
}
pub fn r11(&self) -> u64 {
self.gps[Self::R11]
}
pub fn set_r11(&mut self, v: u64) {
self.gps[Self::R11] = v
}
pub fn r12(&self) -> u64 {
self.gps[Self::R12]
}
pub fn set_r12(&mut self, v: u64) {
self.gps[Self::R12] = v
}
pub fn r13(&self) -> u64 {
self.gps[Self::R13]
}
pub fn set_r13(&mut self, v: u64) {
self.gps[Self::R13] = v
}
pub fn r14(&self) -> u64 {
self.gps[Self::R14]
}
pub fn set_r14(&mut self, v: u64) {
self.gps[Self::R14] = v
}
pub fn r15(&self) -> u64 {
self.gps[Self::R15]
}
pub fn set_r15(&mut self, v: u64) {
self.gps[Self::R15] = v
}
}
#[bitfield(u64)]
pub struct TdxGlaListInfo {
#[bits(9)]
pub first_entry: u64,
#[bits(3)]
_reserved_z0: u64,
#[bits(40)]
pub list_gpa: u64,
#[bits(10)]
pub num_entries: u64,
#[bits(2)]
_reserved_z1: u64,
}
#[bitfield(u64)]
pub struct TdGlaVmAndFlags {
pub list: bool,
#[bits(51)]
_reserved_z0: u64,
#[bits(2)]
pub vm_index: u64,
#[bits(10)]
_reserved_z1: u64,
}
#[bitfield(u64)]
#[derive(AsBytes, FromBytes, FromZeroes)]
pub struct TdxVmFlags {
#[bits(2)]
pub invd_translations: u8,
#[bits(50)]
_reserved: u64,
#[bits(2)]
pub vm_index: u8,
#[bits(10)]
_reserved_2: u64,
}
pub const TDX_VP_ENTER_INVD_INVEPT: u8 = 1;
pub const TDX_VP_ENTER_INVD_INVVPID: u8 = 2;
pub const TDX_VP_ENTER_INVD_INVVPID_NON_GLOBAL: u8 = 3;