Remove local tdx-guest crate

This commit is contained in:
Hsy-Intel 2024-04-28 15:31:56 +08:00 committed by Tate, Hongliang Tian
parent ce0968fbab
commit 5e127b2da0
14 changed files with 5 additions and 1133 deletions

View File

@ -16,7 +16,6 @@ header:
paths-ignore:
# These directories are licensed under licenses other than MPL-2.0.
- 'kernel/libs/comp-sys/cargo-component'
- 'framework/libs/tdx-guest'
license:
content: |
SPDX-License-Identifier: MPL-2.0
@ -28,17 +27,6 @@ header:
- ".S"
comment_style_id: SlashAsterisk
# Files under tdx-guest are licensed under BSD-3-Clause license.
- paths:
- 'framework/libs/tdx-guest/**'
paths-ignore:
- 'Cargo.toml'
- '.gitignore'
license:
content: |
SPDX-License-Identifier: BSD-3-Clause
Copyright(c) 2023-2024 Intel Corporation.
# Files under cargo-component are licensed under Apache-2.0 or MIT license.
- paths:
- 'kernel/libs/comp-sys/cargo-component/**'

3
Cargo.lock generated
View File

@ -1367,9 +1367,12 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
[[package]]
name = "tdx-guest"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "675bd99b7b81320678a9e9e524041b1f57fa68c965524413740d6cbe83d687f6"
dependencies = [
"bitflags 1.3.2",
"lazy_static",
"rand_core",
"raw-cpuid",
"x86_64",
]

View File

@ -9,7 +9,6 @@ members = [
"framework/libs/linux-bzimage/setup",
"framework/libs/ktest",
"framework/libs/ktest-proc-macro",
"framework/libs/tdx-guest",
"kernel",
"kernel/aster-nix",
"kernel/comps/block",

View File

@ -77,7 +77,6 @@ NON_OSDK_CRATES := \
framework/libs/linux-bzimage/boot-params \
framework/libs/ktest \
framework/libs/ktest-proc-macro \
framework/libs/tdx-guest \
kernel/libs/cpio-decoder \
kernel/libs/int-to-c-enum \
kernel/libs/int-to-c-enum/derive \

View File

@ -27,7 +27,7 @@ log = "0.4"
pod = { git = "https://github.com/asterinas/pod", rev = "d7dba56" }
spin = "0.9.4"
static_assertions = "1.1.0"
tdx-guest = { path = "../libs/tdx-guest", optional = true }
tdx-guest = { version = "0.1.0", optional = true }
trapframe = { git = "https://github.com/asterinas/trapframe-rs", rev = "2f37590" }
unwinding = { version = "0.2.1", default-features = false, features = ["fde-gnu-eh-frame-hdr", "hide-trace", "panic", "personality", "unwinder"] }
volatile = { version = "0.4.5", features = ["unstable"] }

View File

@ -1,2 +0,0 @@
/target
/Cargo.lock

View File

@ -1,12 +0,0 @@
[package]
name = "tdx-guest"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
x86_64 = "0.14.10"
bitflags = "1.3"
raw-cpuid = "10"
lazy_static = { version = "1.4.0", features = ["spin_no_std"] }

View File

@ -1,15 +0,0 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright(c) 2023-2024 Intel Corporation.
use core::arch::global_asm;
use crate::{tdcall::TdcallArgs, tdvmcall::TdVmcallArgs};
global_asm!(include_str!("tdcall.asm"));
global_asm!(include_str!("tdvmcall.asm"));
// TODO: Use sysv64
extern "win64" {
pub(crate) fn asm_td_call(args: *mut TdcallArgs) -> u64;
pub(crate) fn asm_td_vmcall(args: *mut TdVmcallArgs) -> u64;
}

View File

@ -1,81 +0,0 @@
# SPDX-License-Identifier: BSD-3-Clause
# Copyright(c) 2023-2024 Intel Corporation.
.section .text
# Arguments offsets in TdVmcallArgs struct
.equ TDCALL_ARG_RAX, 0x0
.equ TDCALL_ARG_RCX, 0x8
.equ TDCALL_ARG_RDX, 0x10
.equ TDCALL_ARG_R8, 0x18
.equ TDCALL_ARG_R9, 0x20
.equ TDCALL_ARG_R10, 0x28
.equ TDCALL_ARG_R11, 0x30
.equ TDCALL_ARG_R12, 0x38
.equ TDCALL_ARG_R13, 0x40
# asm_td_call -> u64 (
# args: *mut TdcallArgs, //rcx
# )
.global asm_td_call
asm_td_call:
endbr64
# Save the registers accroding to MS x64 calling convention
push rbp
mov rbp, rsp
push r15
push r14
push r13
push r12
push rbx
push rsi
push rdi
# Use RDI to save RCX value
mov rdi, rcx
# Test if input pointer is valid
test rdi, rdi
jz td_call_exit
# Copy the input operands from memory to registers
mov rax, [rdi + TDCALL_ARG_RAX]
mov rcx, [rdi + TDCALL_ARG_RCX]
mov rdx, [rdi + TDCALL_ARG_RDX]
mov r8, [rdi + TDCALL_ARG_R8]
mov r9, [rdi + TDCALL_ARG_R9]
mov r10, [rdi + TDCALL_ARG_R10]
mov r11, [rdi + TDCALL_ARG_R11]
mov r12, [rdi + TDCALL_ARG_R12]
mov r13, [rdi + TDCALL_ARG_R13]
# tdcall
.byte 0x66,0x0f,0x01,0xcc
# Exit if tdcall reports failure.
test rax, rax
jnz td_call_exit
# Copy the output operands from registers to the struct
mov [rdi + TDCALL_ARG_RAX], rax
mov [rdi + TDCALL_ARG_RCX], rcx
mov [rdi + TDCALL_ARG_RDX], rdx
mov [rdi + TDCALL_ARG_R8], r8
mov [rdi + TDCALL_ARG_R9], r9
mov [rdi + TDCALL_ARG_R10], r10
mov [rdi + TDCALL_ARG_R11], r11
mov [rdi + TDCALL_ARG_R12], r12
mov [rdi + TDCALL_ARG_R13], r13
td_call_exit:
# Pop out saved registers from stack
pop rdi
pop rsi
pop rbx
pop r12
pop r13
pop r14
pop r15
pop rbp
ret

View File

@ -1,99 +0,0 @@
# SPDX-License-Identifier: BSD-3-Clause
# Copyright(c) 2023-2024 Intel Corporation.
.section .text
# Mask used to control which part of the guest TD GPR and XMM
# state is exposed to the VMM. A bit value of 1 indicates the
# corresponding register is passed to VMM. Refer to TDX Module
# ABI specification section TDG.VP.VMCALL for detail.
# Here we expose R10 - R15 to VMM in td_vm_call()
.equ TDVMCALL_EXPOSE_REGS_MASK, 0xfc00
# TDG.VP.VMCALL leaf number
.equ TDVMCALL, 0
# Arguments offsets in TdVmcallArgs struct
.equ VMCALL_ARG_R10, 0x0
.equ VMCALL_ARG_R11, 0x8
.equ VMCALL_ARG_R12, 0x10
.equ VMCALL_ARG_R13, 0x18
.equ VMCALL_ARG_R14, 0x20
.equ VMCALL_ARG_R15, 0x28
# asm_td_vmcall -> u64 (
# args: *mut TdVmcallArgs,
# )
.global asm_td_vmcall
asm_td_vmcall:
endbr64
# Save the registers accroding to MS x64 calling convention
push rbp
mov rbp, rsp
push r15
push r14
push r13
push r12
push rbx
push rsi
push rdi
# Use RDI to save RCX value
mov rdi, rcx
# Test if input pointer is valid
test rdi, rdi
jz vmcall_exit
# Copy the input operands from memory to registers
mov r10, [rdi + VMCALL_ARG_R10]
mov r11, [rdi + VMCALL_ARG_R11]
mov r12, [rdi + VMCALL_ARG_R12]
mov r13, [rdi + VMCALL_ARG_R13]
mov r14, [rdi + VMCALL_ARG_R14]
mov r15, [rdi + VMCALL_ARG_R15]
# Set TDCALL leaf number
mov rax, TDVMCALL
# Set exposed register mask
mov ecx, TDVMCALL_EXPOSE_REGS_MASK
# TDCALL
.byte 0x66,0x0f,0x01,0xcc
# RAX should always be zero for TDVMCALL, panic if it is not.
test rax, rax
jnz vmcall_panic
# Copy the output operands from registers to the struct
mov [rdi + VMCALL_ARG_R10], r10
mov [rdi + VMCALL_ARG_R11], r11
mov [rdi + VMCALL_ARG_R12], r12
mov [rdi + VMCALL_ARG_R13], r13
mov [rdi + VMCALL_ARG_R14], r14
mov [rdi + VMCALL_ARG_R15], r15
mov rax, r10
vmcall_exit:
# Clean the registers that are exposed to VMM to
# protect against speculative attack, others will
# be restored to the values saved in stack
xor r10, r10
xor r11, r11
# Pop out saved registers from stack
pop rdi
pop rsi
pop rbx
pop r12
pop r13
pop r14
pop r15
pop rbp
ret
vmcall_panic:
ud2

View File

@ -1,51 +0,0 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright(c) 2023-2024 Intel Corporation.
#![cfg_attr(not(test), no_std)]
#![allow(dead_code)]
#![allow(unused_variables)]
extern crate alloc;
mod asm;
pub mod tdcall;
pub mod tdvmcall;
use core::sync::atomic::{AtomicBool, Ordering::Relaxed};
use raw_cpuid::{native_cpuid::cpuid_count, CpuIdResult};
use tdcall::{InitError, TdgVpInfo};
pub use self::{
tdcall::{get_veinfo, TdxVirtualExceptionType},
tdvmcall::print,
};
static TDX_ENABLED: AtomicBool = AtomicBool::new(false);
#[inline(always)]
pub fn tdx_is_enabled() -> bool {
TDX_ENABLED.load(Relaxed)
}
pub fn init_tdx() -> Result<TdgVpInfo, InitError> {
check_tdx_guest()?;
TDX_ENABLED.store(true, Relaxed);
Ok(tdcall::get_tdinfo()?)
}
fn check_tdx_guest() -> Result<(), InitError> {
const TDX_CPUID_LEAF_ID: u64 = 0x21;
let cpuid_leaf = cpuid_count(0, 0).eax as u64;
if cpuid_leaf < TDX_CPUID_LEAF_ID {
return Err(InitError::TdxCpuLeafIdError);
}
let cpuid_result: CpuIdResult = cpuid_count(TDX_CPUID_LEAF_ID as u32, 0);
if &cpuid_result.ebx.to_ne_bytes() != b"Inte"
|| &cpuid_result.edx.to_ne_bytes() != b"lTDX"
|| &cpuid_result.ecx.to_ne_bytes() != b" "
{
return Err(InitError::TdxVendorIdError);
}
Ok(())
}

View File

@ -1,536 +0,0 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright(c) 2023-2024 Intel Corporation.
//! The TDCALL instruction causes a VM exit to the Intel TDX module.
//! It is used to call guest-side Intel TDX functions. For more information about
//! TDCALL, please refer to the [Intel® TDX Module v1.5 ABI Specification](https://cdrdv2.intel.com/v1/dl/getContent/733579)
use core::fmt;
use bitflags::bitflags;
use crate::asm::asm_td_call;
/// TDCALL Instruction Leaf Numbers Definition.
#[repr(u64)]
pub enum TdcallNum {
VpInfo = 1,
MrRtmrExtend = 2,
VpVeinfoGet = 3,
MrReport = 4,
VpCpuidveSet = 5,
MemPageAccept = 6,
VmRd = 7,
VmWr = 8,
MrVerifyreport = 22,
MemPageAttrRd = 23,
MemPageAttrWr = 24,
}
bitflags! {
/// GuestTdAttributes is defined as a 64b field that specifies various guest TD attributes.
/// It is reported to the guest TD by TDG.VP.INFO and as part of TDREPORT_STRUCT returned by TDG.MR.REPORT.
pub struct GuestTdAttributes: u64 {
/// Guest TD runs in off-TD debug mode.
/// Its VCPU state and private memory are accessible by the host VMM.
const DEBUG = 1 << 0;
/// TD is migratable (using a Migration TD).
const MIGRATABLE = 1 << 29;
/// TD is allowed to use Supervisor Protection Keys.
const PKS = 1 << 30;
/// TD is allowed to use Key Locker. Must be 0.
const KL = 1 << 31;
/// TD is allowed to use Perfmon and PERF_METRICS capabilities.
const PERFMON = 1 << 63;
}
}
bitflags! {
/// Controls whether CPUID executed by the guest TD will cause #VE unconditionally.
struct CpuidveFlag: u64 {
/// Flags that when CPL is 0, a CPUID executed
/// by the guest TD will cause a #VE unconditionally.
const SUPERVISOR = 1 << 0;
/// Flags that when CPL > 0, a CPUID executed
/// by the guest TD will cause a #VE unconditionally.
const USER = 1 << 1;
}
}
bitflags! {
/// GPA Attributes (Single VM) Definition.
pub struct GpaAttr: u16 {
/// Read.
const R = 1;
/// Write.
const W = 1 << 1;
/// Execute (Supervisor).
const XS = 1 << 2;
/// Execute (User).
const XU = 1 << 3;
/// Verify Guest Paging.
const VGP = 1 << 4;
/// Paging-Write Access.
const PWA = 1 << 5;
/// Supervisor Shadow Stack.
const SSS = 1 << 6;
/// Suppress #VE.
const SVE = 1 << 7;
/// Indicates that the other bits are valid.
/// If its value is 0, other fields are reserved and must be 0.
const VALID = 1 << 15;
}
}
pub struct PageAttr {
/// Actual GPA mapping of the page.
gpa_mapping: u64,
/// Guest-visible page attributes.
gpa_attr: GpaAttrAll,
}
/// GPA Attributes (all VMs) Definition.
pub struct GpaAttrAll {
/// L1 GPA attributes.
l1_attr: GpaAttr,
/// GPA attributes for L2 VM #1.
vm1_attr: GpaAttr,
/// GPA attributes for L2 VM #2.
vm2_attr: GpaAttr,
/// GPA attributes for L2 VM #3.
vm3_attr: GpaAttr,
}
impl From<u64> for GpaAttrAll {
fn from(val: u64) -> Self {
GpaAttrAll {
l1_attr: GpaAttr::from_bits_truncate((val & 0xFFFF) as u16),
vm1_attr: GpaAttr::from_bits_truncate(((val >> 16) & 0xFFFF) as u16),
vm2_attr: GpaAttr::from_bits_truncate(((val >> 32) & 0xFFFF) as u16),
vm3_attr: GpaAttr::from_bits_truncate(((val >> 48) & 0xFFFF) as u16),
}
}
}
impl From<GpaAttrAll> for u64 {
fn from(s: GpaAttrAll) -> Self {
let field1 = s.l1_attr.bits() as u64;
let field2 = (s.vm1_attr.bits() as u64) << 16;
let field3 = (s.vm2_attr.bits() as u64) << 32;
let field4 = (s.vm3_attr.bits() as u64) << 48;
field4 | field3 | field2 | field1
}
}
#[repr(C)]
#[derive(Debug)]
pub struct TdReport {
/// REPORTMACSTRUCT for the TDG.MR.REPORT.
pub report_mac: ReportMac,
/// Additional attestable elements in the TDs TCB are not reflected in the
/// REPORTMACSTRUCT.CPUSVN includes the Intel TDX module measurements.
pub tee_tcb_info: [u8; 239],
pub reserved: [u8; 17],
/// TDs attestable properties.
pub tdinfo: TdInfo,
}
#[repr(C)]
#[derive(Debug)]
pub struct ReportMac {
/// Type Header Structure.
pub report_type: ReportType,
pub cpu_svn: [u8; 16],
/// SHA384 of TEE_TCB_INFO for TEEs implemented using Intel TDX.
pub tee_tcb_info_hash: [u8; 48],
/// SHA384 of TEE_INFO: a TEE-specific info structure (TDINFO_STRUCT or SGXINFO)
/// or 0 if no TEE is represented.
pub tee_info_hash: [u8; 48],
/// A set of data used for communication between the caller and the target.
pub report_data: [u8; 64],
pub reserved: [u8; 32],
/// The MAC over the REPORTMACSTRUCT with model-specific MAC.
pub mac: [u8; 32],
}
#[derive(Debug)]
pub enum TeeType {
SGX,
TDX,
}
/// REPORTTYPE indicates the reported Trusted Execution Environment (TEE) type,
/// sub-type and version.
#[repr(C)]
#[derive(Debug)]
pub struct ReportType {
/// Trusted Execution Environment (TEE) Type. 0x00: SGX, 0x81: TDX.
pub tee_type: TeeType,
/// TYPE-specific subtype.
pub sub_type: u8,
/// TYPE-specific version.
pub version: u8,
pub reserved: u8,
}
/// TDINFO_STRUCT is defined as the TDX-specific TEE_INFO part of TDG.MR.REPORT.
/// It contains the measurements and initial configuration of the TD that was
/// locked at initialization and a set of measurement registers that are run-time
/// extendable. These values are copied from the TDCS by the TDG.MR.REPORT function.
/// Refer to the [TDX Module Base Spec] for additional details.
#[repr(C)]
#[derive(Debug)]
pub struct TdInfo {
/// TDs ATTRIBUTES.
pub attributes: u64,
/// TDs XFAM.
pub xfam: u64,
/// Measurement of the initial contents of the TD.
pub mrtd: [u8; 48],
/// Software-defined ID for non-owner-defined configuration of the
/// guest TD e.g., run-time or OS configuration.
pub mr_config_id: [u8; 48],
/// Software-defined ID for the guest TDs owner.
pub mr_owner: [u8; 48],
/// Software-defined ID for owner-defined configuration of the
/// guest TD e.g., specific to the workload rather than the run-time or OS.
pub mr_owner_config: [u8; 48],
/// Array of NUM_RTMRS (4) run-time extendable measurement registers.
pub rtmr0: [u8; 48],
pub rtmr1: [u8; 48],
pub rtmr2: [u8; 48],
pub rtmr3: [u8; 48],
/// If is one or more bound or pre-bound service TDs, SERVTD_HASH is the SHA384 hash of the
/// TDINFO_STRUCTs of those service TDs bound. Else, SERVTD_HASH is 0.
pub servtd_hash: [u8; 48],
pub reserved: [u8; 64],
}
#[repr(C)]
#[derive(Debug)]
pub struct TdgVeInfo {
pub exit_reason: u32,
/// the 64-bit value that would have been saved into the VMCS as an exit qualification
/// if a legacy VM exit had occurred instead of the virtualization exception.
pub exit_qualification: u64,
/// the 64-bit value that would have been saved into the VMCS as a guestlinear address
/// if a legacy VM exit had occurred instead of the virtualization exception.
pub guest_linear_address: u64,
/// the 64-bit value that would have been saved into the VMCS as a guestphysical address
/// if a legacy VM exit had occurred instead of the virtualization exception.
pub guest_physical_address: u64,
/// The 32-bit value that would have been saved into the VMCS as VM-exit instruction
/// length if a legacy VM exit had occurred instead of the virtualization exception.
pub exit_instruction_length: u32,
/// The 32-bit value that would have been saved into the VMCS as VM-exit instruction
/// information if a legacy VM exit had occurred instead of the virtualization exception.
pub exit_instruction_info: u32,
}
#[derive(Debug)]
pub enum Gpaw {
Bit48,
Bit52,
}
impl From<u64> for Gpaw {
fn from(val: u64) -> Self {
match val {
48 => Self::Bit48,
52 => Self::Bit52,
_ => panic!("Invalid gpaw"),
}
}
}
impl From<Gpaw> for u64 {
fn from(s: Gpaw) -> Self {
match s {
Gpaw::Bit48 => 48,
Gpaw::Bit52 => 52,
}
}
}
impl fmt::Display for Gpaw {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Gpaw::Bit48 => write!(f, "48-bit"),
Gpaw::Bit52 => write!(f, "52-bit"),
}
}
}
#[derive(Debug)]
pub struct TdgVpInfo {
/// The effective GPA width (in bits) for this TD (do not confuse with MAXPA).
/// SHARED bit is at GPA bit GPAW-1.
///
/// Only GPAW values 48 and 52 are possible.
pub gpaw: Gpaw,
/// The TD's ATTRIBUTES (provided as input to TDH.MNG.INIT)
pub attributes: GuestTdAttributes,
pub num_vcpus: u32,
pub max_vcpus: u32,
pub vcpu_index: u32,
/// Indicates that the TDG.SYS.RD/RDM/RDCALL function are avaliable.
pub sys_rd: u32,
}
#[derive(Debug, PartialEq)]
pub enum TdCallError {
/// There is no valid #VE information.
TdxNoValidVeInfo,
/// Operand is invalid.
TdxOperandInvalid,
/// The operand is busy (e.g., it is locked in Exclusive mode).
TdxOperandBusy,
/// Page has already been accepted.
TdxPageAlreadyAccepted,
/// Requested page size does not match the current GPA mapping size.
TdxPageSizeMismatch,
Other,
}
impl From<u64> for TdCallError {
fn from(val: u64) -> Self {
match val {
0xC000_0704 => Self::TdxNoValidVeInfo,
0xC000_0100 => Self::TdxOperandInvalid,
0x8000_0200 => Self::TdxOperandBusy,
0x0000_0B0A => Self::TdxPageAlreadyAccepted,
0xC000_0B0B => Self::TdxPageSizeMismatch,
_ => Self::Other,
}
}
}
#[repr(C)]
#[derive(Default)]
pub(crate) struct TdcallArgs {
rax: u64,
rcx: u64,
rdx: u64,
r8: u64,
r9: u64,
r10: u64,
r11: u64,
r12: u64,
r13: u64,
}
pub enum TdxVirtualExceptionType {
Hlt,
Io,
MsrRead,
MsrWrite,
CpuId,
VmCall,
Mwait,
Monitor,
EptViolation,
Wbinvd,
Rdpmc,
Other,
}
impl From<u32> for TdxVirtualExceptionType {
fn from(val: u32) -> Self {
match val {
10 => Self::CpuId,
12 => Self::Hlt,
15 => Self::Rdpmc,
18 => Self::VmCall,
30 => Self::Io,
31 => Self::MsrRead,
32 => Self::MsrWrite,
36 => Self::Mwait,
39 => Self::Monitor,
48 => Self::EptViolation,
54 => Self::Wbinvd,
_ => Self::Other,
}
}
}
#[derive(Debug)]
pub enum InitError {
TdxVendorIdError,
TdxCpuLeafIdError,
TdxGetVpInfoError(TdCallError),
}
impl From<TdCallError> for InitError {
fn from(error: TdCallError) -> Self {
InitError::TdxGetVpInfoError(error)
}
}
/// Get guest TD execution environment information.
pub fn get_tdinfo() -> Result<TdgVpInfo, TdCallError> {
let mut args = TdcallArgs {
rax: TdcallNum::VpInfo as u64,
..Default::default()
};
td_call(&mut args)?;
let td_info = TdgVpInfo {
gpaw: Gpaw::from(args.rcx),
attributes: GuestTdAttributes::from_bits_truncate(args.rdx),
num_vcpus: args.r8 as u32,
max_vcpus: (args.r8 >> 32) as u32,
vcpu_index: args.r9 as u32,
sys_rd: args.r10 as u32,
};
Ok(td_info)
}
/// Get Virtualization Exception Information for the recent #VE exception.
pub fn get_veinfo() -> Result<TdgVeInfo, TdCallError> {
let mut args = TdcallArgs {
rax: TdcallNum::VpVeinfoGet as u64,
..Default::default()
};
td_call(&mut args)?;
let ve_info = TdgVeInfo {
exit_reason: args.rcx as u32,
exit_qualification: args.rdx,
guest_linear_address: args.r8,
guest_physical_address: args.r9,
exit_instruction_length: args.r10 as u32,
exit_instruction_info: (args.r10 >> 32) as u32,
};
Ok(ve_info)
}
/// Extend a TDCS.RTMR measurement register.
pub fn extend_rtmr() -> Result<(), TdCallError> {
let mut args = TdcallArgs {
rax: TdcallNum::MrRtmrExtend as u64,
..Default::default()
};
td_call(&mut args)
}
/// TDG.MR.REPORT creates a TDREPORT_STRUCT structure that contains the measurements/configuration
/// information of the guest TD that called the function, measurements/configuration information
/// of the Intel TDX module and a REPORTMACSTRUCT.
pub fn get_report(report_gpa: &[u8], data_gpa: &[u8]) -> Result<(), TdCallError> {
let mut args = TdcallArgs {
rax: TdcallNum::MrReport as u64,
rcx: report_gpa.as_ptr() as u64,
rdx: data_gpa.as_ptr() as u64,
..Default::default()
};
td_call(&mut args)
}
/// Verify a cryptographic REPORTMACSTRUCT that describes the contents of a TD,
/// to determine that it was created on the current TEE on the current platform.
pub fn verify_report(report_mac_gpa: &[u8]) -> Result<(), TdCallError> {
let mut args = TdcallArgs {
rax: TdcallNum::MrVerifyreport as u64,
rcx: report_mac_gpa.as_ptr() as u64,
..Default::default()
};
td_call(&mut args)
}
/// Accept a pending private page and initialize it to all-0 using the TD ephemeral private key.
/// # Safety
/// The 'gpa' parameter must be a valid address.
pub unsafe fn accept_page(sept_level: u64, gpa: u64) -> Result<(), TdCallError> {
let mut args = TdcallArgs {
rax: TdcallNum::MemPageAccept as u64,
rcx: sept_level | gpa,
..Default::default()
};
td_call(&mut args)
}
/// Read the GPA mapping and attributes of a TD private page.
pub fn read_page_attr(gpa: &[u8]) -> Result<PageAttr, TdCallError> {
let mut args = TdcallArgs {
rax: TdcallNum::MemPageAttrRd as u64,
rcx: gpa.as_ptr() as u64,
..Default::default()
};
td_call(&mut args)?;
let page_attr = PageAttr {
gpa_mapping: args.rcx,
gpa_attr: GpaAttrAll::from(args.rdx),
};
Ok(page_attr)
}
/// Write the attributes of a private page. Create or remove L2 page aliases as required.
pub fn write_page_attr(page_attr: PageAttr, attr_flags: u64) -> Result<PageAttr, TdCallError> {
let mut args = TdcallArgs {
rax: TdcallNum::MemPageAttrWr as u64,
rcx: page_attr.gpa_mapping,
rdx: u64::from(page_attr.gpa_attr),
r8: attr_flags,
..Default::default()
};
td_call(&mut args)?;
let page_attr = PageAttr {
gpa_mapping: args.rcx,
gpa_attr: GpaAttrAll::from(args.rdx),
};
Ok(page_attr)
}
/// Read a TD-scope metadata field (control structure field) of a TD.
pub fn read_td_metadata(field_identifier: u64) -> Result<u64, TdCallError> {
let mut args = TdcallArgs {
rax: TdcallNum::VmRd as u64,
rdx: field_identifier,
..Default::default()
};
td_call(&mut args)?;
Ok(args.r8)
}
/// Write a TD-scope metadata field (control structure field) of a TD.
///
/// - data: data to write to the field
///
/// - write_mask: a 64b write mask to indicate which bits of the value
/// in R8 are to be written to the field.
///
/// It returns previous contents of the field.
pub fn write_td_metadata(
field_identifier: u64,
data: u64,
write_mask: u64,
) -> Result<u64, TdCallError> {
let mut args = TdcallArgs {
rax: TdcallNum::VmWr as u64,
rdx: field_identifier,
r8: data,
r9: write_mask,
..Default::default()
};
td_call(&mut args).map(|_| args.r8)
}
/// TDG.VP.CPUIDVE.SET controls unconditional #VE on CPUID execution by the guest TD.
///
/// Note: TDG.VP.CPUIDVE.SET is provided for backward compatibility.
///
/// The guest TD may control the same settings by writing to the
/// VCPU-scope metadata fields CPUID_SUPERVISOR_VE and CPUID_USER_VE using TDG.VP.WR.
pub fn set_cpuidve(cpuidve_flag: u64) -> Result<(), TdCallError> {
let mut args = TdcallArgs {
rax: TdcallNum::VpCpuidveSet as u64,
rcx: cpuidve_flag,
..Default::default()
};
td_call(&mut args)
}
fn td_call(args: &mut TdcallArgs) -> Result<(), TdCallError> {
let result = unsafe { asm_td_call(args) };
match result {
0 => Ok(()),
_ => Err(result.into()),
}
}

View File

@ -1,321 +0,0 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright(c) 2023-2024 Intel Corporation.
//! The TDVMCALL helps invoke services from the host VMM. From the perspective of the host VMM, the TDVMCALL is a trap-like, VM exit into
//! the host VMM, reported via the SEAMRET instruction flow.
//! By design, after the SEAMRET, the host VMM services the request specified in the parameters
//! passed by the TD during the TDG.VP.VMCALL (that are passed via SEAMRET to the VMM), then
//! resumes the TD via a SEAMCALL [TDH.VP.ENTER] invocation.
extern crate alloc;
use alloc::fmt;
use core::fmt::Write;
use bitflags::bitflags;
use x86_64::{
registers::rflags::{self, RFlags},
structures::port::PortRead,
};
use crate::asm::asm_td_vmcall;
/// TDVMCALL Instruction Leaf Numbers Definition.
#[repr(u64)]
pub enum TdVmcallNum {
Cpuid = 0x0000a,
Hlt = 0x0000c,
Io = 0x0001e,
Rdmsr = 0x0001f,
Wrmsr = 0x00020,
RequestMmio = 0x00030,
Wbinvd = 0x00036,
Pconfig = 0x00041,
Mapgpa = 0x10001,
}
const SERIAL_IO_PORT: u16 = 0x3F8;
const SERIAL_LINE_STS: u16 = 0x3FD;
const IO_READ: u64 = 0;
const IO_WRITE: u64 = 1;
#[derive(Debug, PartialEq)]
pub enum TdVmcallError {
/// TDCALL[TDG.VP.VMCALL] sub-function invocation must be retried.
TdxRetry,
/// Invalid operand to TDG.VP.VMCALL sub-function.
TdxInvalidOperand,
/// GPA already mapped.
TdxGpaInuse,
/// Operand (address) aligned error.
TdxAlignError,
Other,
}
impl From<u64> for TdVmcallError {
fn from(val: u64) -> Self {
match val {
0x1 => Self::TdxRetry,
0x8000_0000_0000_0000 => Self::TdxInvalidOperand,
0x8000_0000_0000_0001 => Self::TdxGpaInuse,
0x8000_0000_0000_0002 => Self::TdxAlignError,
_ => Self::Other,
}
}
}
#[repr(C)]
#[derive(Default)]
pub(crate) struct TdVmcallArgs {
r10: u64,
r11: u64,
r12: u64,
r13: u64,
r14: u64,
r15: u64,
}
#[repr(C)]
#[derive(Debug, Default)]
pub struct CpuIdInfo {
pub eax: usize,
pub ebx: usize,
pub ecx: usize,
pub edx: usize,
}
pub enum Direction {
In,
Out,
}
pub enum Operand {
Dx,
Immediate,
}
pub enum IoSize {
Size1 = 1,
Size2 = 2,
Size4 = 4,
Size8 = 8,
}
pub fn cpuid(eax: u32, ecx: u32) -> Result<CpuIdInfo, TdVmcallError> {
let mut args = TdVmcallArgs {
r11: TdVmcallNum::Cpuid as u64,
r12: eax as u64,
r13: ecx as u64,
..Default::default()
};
td_vmcall(&mut args)?;
Ok(CpuIdInfo {
eax: args.r12 as usize,
ebx: args.r13 as usize,
ecx: args.r14 as usize,
edx: args.r15 as usize,
})
}
pub fn hlt() {
let interrupt_blocked = !rflags::read().contains(RFlags::INTERRUPT_FLAG);
let mut args = TdVmcallArgs {
r11: TdVmcallNum::Hlt as u64,
r12: interrupt_blocked as u64,
..Default::default()
};
let _ = td_vmcall(&mut args);
}
/// # Safety
/// Make sure the index is valid.
pub unsafe fn rdmsr(index: u32) -> Result<u64, TdVmcallError> {
let mut args = TdVmcallArgs {
r11: TdVmcallNum::Rdmsr as u64,
r12: index as u64,
..Default::default()
};
td_vmcall(&mut args)?;
Ok(args.r11)
}
/// # Safety
/// Make sure the index and the corresponding value are valid.
pub unsafe fn wrmsr(index: u32, value: u64) -> Result<(), TdVmcallError> {
let mut args = TdVmcallArgs {
r11: TdVmcallNum::Wrmsr as u64,
r12: index as u64,
r13: value,
..Default::default()
};
td_vmcall(&mut args)
}
/// Used to help perform WBINVD or WBNOINVD operation.
/// - cache_operation: 0: WBINVD, 1: WBNOINVD
pub fn perform_cache_operation(cache_operation: u64) -> Result<(), TdVmcallError> {
let mut args = TdVmcallArgs {
r11: TdVmcallNum::Wbinvd as u64,
r12: cache_operation,
..Default::default()
};
td_vmcall(&mut args)
}
/// # Safety
/// Make sure the mmio address is valid.
pub unsafe fn read_mmio(size: IoSize, mmio_gpa: u64) -> Result<u64, TdVmcallError> {
let mut args = TdVmcallArgs {
r11: TdVmcallNum::RequestMmio as u64,
r12: size as u64,
r13: 0,
r14: mmio_gpa,
..Default::default()
};
td_vmcall(&mut args)?;
Ok(args.r11)
}
/// # Safety
/// Make sure the mmio address is valid.
pub unsafe fn write_mmio(size: IoSize, mmio_gpa: u64, data: u64) -> Result<(), TdVmcallError> {
let mut args = TdVmcallArgs {
r11: TdVmcallNum::RequestMmio as u64,
r12: size as u64,
r13: 1,
r14: mmio_gpa,
r15: data,
..Default::default()
};
td_vmcall(&mut args)
}
/// MapGPA TDG.VP.VMCALL is used to help request the host VMM to map a GPA range as private
/// or shared-memory mappings. This API may also be used to convert page mappings from
/// private to shared. The GPA range passed in this operation can indicate if the mapping is
/// requested for a shared or private memory via the GPA.Shared bit in the start address.
pub fn map_gpa(gpa: u64, size: u64) -> Result<(), (u64, TdVmcallError)> {
let mut args = TdVmcallArgs {
r11: TdVmcallNum::Mapgpa as u64,
r12: gpa,
r13: size,
..Default::default()
};
td_vmcall(&mut args).map_err(|e| (args.r11, e))
}
macro_rules! io_read {
($port:expr, $ty:ty) => {{
let mut args = TdVmcallArgs {
r11: TdVmcallNum::Io as u64,
r12: core::mem::size_of::<$ty>() as u64,
r13: IO_READ,
r14: $port as u64,
..Default::default()
};
td_vmcall(&mut args)?;
Ok(args.r11 as u32)
}};
}
pub fn io_read(size: IoSize, port: u16) -> Result<u32, TdVmcallError> {
match size {
IoSize::Size1 => io_read!(port, u8),
IoSize::Size2 => io_read!(port, u16),
IoSize::Size4 => io_read!(port, u32),
_ => unreachable!(),
}
}
macro_rules! io_write {
($port:expr, $byte:expr, $size:expr) => {{
let mut args = TdVmcallArgs {
r11: TdVmcallNum::Io as u64,
r12: core::mem::size_of_val(&$byte) as u64,
r13: IO_WRITE,
r14: $port as u64,
r15: $byte as u64,
..Default::default()
};
td_vmcall(&mut args)
}};
}
pub fn io_write(size: IoSize, port: u16, byte: u32) -> Result<(), TdVmcallError> {
match size {
IoSize::Size1 => io_write!(port, byte, u8),
IoSize::Size2 => io_write!(port, byte, u16),
IoSize::Size4 => io_write!(port, byte, u32),
_ => unreachable!(),
}
}
fn td_vmcall(args: &mut TdVmcallArgs) -> Result<(), TdVmcallError> {
let result = unsafe { asm_td_vmcall(args) };
match result {
0 => Ok(()),
_ => Err(result.into()),
}
}
bitflags! {
/// LineSts: Line Status
struct LineSts: u8 {
const INPUT_FULL = 1;
const OUTPUT_EMPTY = 1 << 5;
}
}
fn read_line_sts() -> LineSts {
LineSts::from_bits_truncate(unsafe { PortRead::read_from_port(SERIAL_LINE_STS) })
}
struct Serial;
impl Serial {
fn serial_write_byte(byte: u8) {
match byte {
// Backspace/Delete
8 | 0x7F => {
while !read_line_sts().contains(LineSts::OUTPUT_EMPTY) {}
io_write!(SERIAL_IO_PORT, 8, u8).unwrap();
while !read_line_sts().contains(LineSts::OUTPUT_EMPTY) {}
io_write!(SERIAL_IO_PORT, b' ', u8).unwrap();
while !read_line_sts().contains(LineSts::OUTPUT_EMPTY) {}
io_write!(SERIAL_IO_PORT, 8, u8).unwrap();
}
_ => {
while !read_line_sts().contains(LineSts::OUTPUT_EMPTY) {}
io_write!(SERIAL_IO_PORT, byte, u8).unwrap();
}
}
}
}
impl Write for Serial {
fn write_str(&mut self, s: &str) -> fmt::Result {
for &c in s.as_bytes() {
Serial::serial_write_byte(c);
}
Ok(())
}
}
pub fn print(args: fmt::Arguments) {
Serial
.write_fmt(args)
.expect("Failed to write to serial port");
}
#[macro_export]
macro_rules! serial_print {
($fmt: literal $(, $($arg: tt)+)?) => {
$crate::tdvmcall::print(format_args!($fmt $(, $($arg)+)?));
}
}
#[macro_export]
macro_rules! serial_println {
($fmt: literal $(, $($arg: tt)+)?) => {
$crate::tdvmcall::print(format_args!(concat!($fmt, "\n") $(, $($arg)+)?))
}
}

View File

@ -41,7 +41,7 @@ smoltcp = { version = "0.9.1", default-features = false, features = [
"socket-dhcpv4",
] }
ktest = { path = "../../framework/libs/ktest" }
tdx-guest = { path = "../../framework/libs/tdx-guest", optional = true }
tdx-guest = { version = "0.1.0", optional = true }
# parse elf file
xmas-elf = "0.8.0"