tdx_guest/
lib.rs

1// SPDX-License-Identifier: BSD-3-Clause
2// Copyright(c) 2023-2024 Intel Corporation.
3
4#![cfg_attr(not(test), no_std)]
5#![allow(dead_code)]
6#![allow(unused_variables)]
7
8extern crate alloc;
9
10mod asm;
11pub mod tdcall;
12pub mod tdvmcall;
13mod ve;
14
15use core::sync::atomic::{AtomicBool, Ordering::Relaxed};
16
17use raw_cpuid::{native_cpuid::cpuid_count, CpuIdResult};
18use tdcall::{InitError, TdgVpInfo};
19use ve::{handle_io, handle_mmio};
20
21pub use self::{
22    tdcall::{get_veinfo, TdgVeInfo, TdxVirtualExceptionType},
23    tdvmcall::{cpuid, hlt, print, rdmsr, wrmsr},
24};
25
26pub const SHARED_BIT: u8 = 51;
27pub const SHARED_MASK: u64 = 1u64 << SHARED_BIT;
28
29static TDX_ENABLED: AtomicBool = AtomicBool::new(false);
30
31pub type TdxGpa = usize;
32
33pub trait TdxTrapFrame {
34    fn rax(&self) -> usize;
35    fn set_rax(&mut self, rax: usize);
36    fn rbx(&self) -> usize;
37    fn set_rbx(&mut self, rbx: usize);
38    fn rcx(&self) -> usize;
39    fn set_rcx(&mut self, rcx: usize);
40    fn rdx(&self) -> usize;
41    fn set_rdx(&mut self, rdx: usize);
42    fn rsi(&self) -> usize;
43    fn set_rsi(&mut self, rsi: usize);
44    fn rdi(&self) -> usize;
45    fn set_rdi(&mut self, rdi: usize);
46    fn rip(&self) -> usize;
47    fn set_rip(&mut self, rip: usize);
48    fn r8(&self) -> usize;
49    fn set_r8(&mut self, r8: usize);
50    fn r9(&self) -> usize;
51    fn set_r9(&mut self, r9: usize);
52    fn r10(&self) -> usize;
53    fn set_r10(&mut self, r10: usize);
54    fn r11(&self) -> usize;
55    fn set_r11(&mut self, r11: usize);
56    fn r12(&self) -> usize;
57    fn set_r12(&mut self, r12: usize);
58    fn r13(&self) -> usize;
59    fn set_r13(&mut self, r13: usize);
60    fn r14(&self) -> usize;
61    fn set_r14(&mut self, r14: usize);
62    fn r15(&self) -> usize;
63    fn set_r15(&mut self, r15: usize);
64    fn rbp(&self) -> usize;
65    fn set_rbp(&mut self, rbp: usize);
66}
67
68#[inline(always)]
69pub fn tdx_is_enabled() -> bool {
70    TDX_ENABLED.load(Relaxed)
71}
72
73pub fn init_tdx() -> Result<TdgVpInfo, InitError> {
74    check_tdx_guest()?;
75    let info = tdcall::get_tdinfo()?;
76    TDX_ENABLED.store(true, Relaxed);
77    Ok(info)
78}
79
80fn check_tdx_guest() -> Result<(), InitError> {
81    const TDX_CPUID_LEAF_ID: u64 = 0x21;
82    let cpuid_leaf = cpuid_count(0, 0).eax as u64;
83    if cpuid_leaf < TDX_CPUID_LEAF_ID {
84        return Err(InitError::TdxCpuLeafIdError);
85    }
86    let cpuid_result: CpuIdResult = cpuid_count(TDX_CPUID_LEAF_ID as u32, 0);
87    if &cpuid_result.ebx.to_ne_bytes() != b"Inte"
88        || &cpuid_result.edx.to_ne_bytes() != b"lTDX"
89        || &cpuid_result.ecx.to_ne_bytes() != b"    "
90    {
91        return Err(InitError::TdxVendorIdError);
92    }
93    Ok(())
94}
95
96pub fn handle_virtual_exception(trapframe: &mut dyn TdxTrapFrame, ve_info: &TdgVeInfo) {
97    let mut instr_len = ve_info.exit_instruction_length;
98    match ve_info.exit_reason.into() {
99        TdxVirtualExceptionType::Hlt => {
100            hlt();
101        }
102        TdxVirtualExceptionType::Io => {
103            if !handle_io(trapframe, ve_info) {
104                serial_println!("Handle tdx ioexit errors, ready to halt");
105                hlt();
106            }
107        }
108        TdxVirtualExceptionType::MsrRead => {
109            let msr = unsafe { rdmsr(trapframe.rcx() as u32).unwrap() };
110            trapframe.set_rax((msr as u32 & u32::MAX) as usize);
111            trapframe.set_rdx(((msr >> 32) as u32 & u32::MAX) as usize);
112        }
113        TdxVirtualExceptionType::MsrWrite => {
114            let data = trapframe.rax() as u64 | ((trapframe.rdx() as u64) << 32);
115            unsafe { wrmsr(trapframe.rcx() as u32, data).unwrap() };
116        }
117        TdxVirtualExceptionType::CpuId => {
118            let cpuid_info = cpuid(trapframe.rax() as u32, trapframe.rcx() as u32).unwrap();
119            let mask = 0xFFFF_FFFF_0000_0000_usize;
120            trapframe.set_rax((trapframe.rax() & mask) | cpuid_info.eax);
121            trapframe.set_rbx((trapframe.rbx() & mask) | cpuid_info.ebx);
122            trapframe.set_rcx((trapframe.rcx() & mask) | cpuid_info.ecx);
123            trapframe.set_rdx((trapframe.rdx() & mask) | cpuid_info.edx);
124        }
125        TdxVirtualExceptionType::EptViolation => {
126            if is_protected_gpa(ve_info.guest_physical_address as TdxGpa) {
127                serial_println!("Unexpected EPT-violation on private memory");
128                hlt();
129            }
130            instr_len = handle_mmio(trapframe, ve_info).unwrap() as u32;
131        }
132        TdxVirtualExceptionType::Other => {
133            serial_println!("Unknown TDX vitrual exception type");
134            hlt();
135        }
136        _ => return,
137    }
138    trapframe.set_rip(trapframe.rip() + instr_len as usize);
139}
140
141pub(crate) fn is_protected_gpa(gpa: TdxGpa) -> bool {
142    (gpa as u64 & SHARED_MASK) == 0
143}