1extern crate alloc;
11
12use alloc::fmt;
13use core::fmt::Write;
14
15use x86_64::registers::rflags::{self, RFlags};
16
17use crate::asm::asm_td_vmcall;
18
19#[repr(u64)]
21pub enum TdVmcallNum {
22 Cpuid = 0x0000a,
23 Hlt = 0x0000c,
24 Io = 0x0001e,
25 Rdmsr = 0x0001f,
26 Wrmsr = 0x00020,
27 RequestMmio = 0x00030,
28 Wbinvd = 0x00036,
29 GetTdVmcallInfo = 0x10000,
30 Mapgpa = 0x10001,
31 GetQuote = 0x10002,
32 SetupEventNotifyInterrupt = 0x10004,
33 Service = 0x10005,
34}
35
36const SERIAL_IO_PORT: u16 = 0x3F8;
37const SERIAL_LINE_STS: u16 = 0x3FD;
38const IO_READ: u64 = 0;
39const IO_WRITE: u64 = 1;
40
41#[derive(Debug, PartialEq)]
42pub enum TdVmcallError {
43 TdxRetry,
45 TdxOperandInvalid,
47 TdxGpaInuse,
49 TdxAlignError,
51 Other,
52}
53
54impl From<u64> for TdVmcallError {
55 fn from(val: u64) -> Self {
56 match val {
57 0x1 => Self::TdxRetry,
58 0x8000_0000_0000_0000 => Self::TdxOperandInvalid,
59 0x8000_0000_0000_0001 => Self::TdxGpaInuse,
60 0x8000_0000_0000_0002 => Self::TdxAlignError,
61 _ => Self::Other,
62 }
63 }
64}
65
66#[repr(C)]
67#[derive(Default)]
68pub(crate) struct TdVmcallArgs {
69 r10: u64,
70 r11: u64,
71 r12: u64,
72 r13: u64,
73 r14: u64,
74 r15: u64,
75}
76
77#[repr(C)]
78#[derive(Debug, Default)]
79pub struct CpuIdInfo {
80 pub eax: usize,
81 pub ebx: usize,
82 pub ecx: usize,
83 pub edx: usize,
84}
85
86pub enum Direction {
87 In,
88 Out,
89}
90
91pub enum Operand {
92 Dx,
93 Immediate,
94}
95
96pub enum IoSize {
97 Size1 = 1,
98 Size2 = 2,
99 Size4 = 4,
100 Size8 = 8,
101}
102
103pub fn cpuid(eax: u32, ecx: u32) -> Result<CpuIdInfo, TdVmcallError> {
104 let mut args = TdVmcallArgs {
105 r11: TdVmcallNum::Cpuid as u64,
106 r12: eax as u64,
107 r13: ecx as u64,
108 ..Default::default()
109 };
110 td_vmcall(&mut args)?;
111 Ok(CpuIdInfo {
112 eax: args.r12 as usize,
113 ebx: args.r13 as usize,
114 ecx: args.r14 as usize,
115 edx: args.r15 as usize,
116 })
117}
118
119pub fn hlt() {
120 let interrupt_blocked = !rflags::read().contains(RFlags::INTERRUPT_FLAG);
121 let mut args = TdVmcallArgs {
122 r11: TdVmcallNum::Hlt as u64,
123 r12: interrupt_blocked as u64,
124 ..Default::default()
125 };
126 let _ = td_vmcall(&mut args);
127}
128
129pub unsafe fn rdmsr(index: u32) -> Result<u64, TdVmcallError> {
132 let mut args = TdVmcallArgs {
133 r11: TdVmcallNum::Rdmsr as u64,
134 r12: index as u64,
135 ..Default::default()
136 };
137 td_vmcall(&mut args)?;
138 Ok(args.r11)
139}
140
141pub unsafe fn wrmsr(index: u32, value: u64) -> Result<(), TdVmcallError> {
144 let mut args = TdVmcallArgs {
145 r11: TdVmcallNum::Wrmsr as u64,
146 r12: index as u64,
147 r13: value,
148 ..Default::default()
149 };
150 td_vmcall(&mut args)
151}
152
153pub fn perform_cache_operation(cache_operation: u64) -> Result<(), TdVmcallError> {
156 let mut args = TdVmcallArgs {
157 r11: TdVmcallNum::Wbinvd as u64,
158 r12: cache_operation,
159 ..Default::default()
160 };
161 td_vmcall(&mut args)
162}
163
164pub unsafe fn read_mmio(size: IoSize, mmio_gpa: u64) -> Result<u64, TdVmcallError> {
167 let mut args = TdVmcallArgs {
168 r11: TdVmcallNum::RequestMmio as u64,
169 r12: size as u64,
170 r13: 0,
171 r14: mmio_gpa,
172 ..Default::default()
173 };
174 td_vmcall(&mut args)?;
175 Ok(args.r11)
176}
177
178pub unsafe fn write_mmio(size: IoSize, mmio_gpa: u64, data: u64) -> Result<(), TdVmcallError> {
181 let mut args = TdVmcallArgs {
182 r11: TdVmcallNum::RequestMmio as u64,
183 r12: size as u64,
184 r13: 1,
185 r14: mmio_gpa,
186 r15: data,
187 ..Default::default()
188 };
189 td_vmcall(&mut args)
190}
191
192pub fn map_gpa(gpa: u64, size: u64) -> Result<(), (u64, TdVmcallError)> {
197 let mut args = TdVmcallArgs {
198 r11: TdVmcallNum::Mapgpa as u64,
199 r12: gpa,
200 r13: size,
201 ..Default::default()
202 };
203 td_vmcall(&mut args).map_err(|e| (args.r11, e))
204}
205
206pub fn get_quote(shared_gpa: u64, size: u64) -> Result<(), TdVmcallError> {
215 let mut args = TdVmcallArgs {
216 r11: TdVmcallNum::GetQuote as u64,
217 r12: shared_gpa,
218 r13: size,
219 ..Default::default()
220 };
221 td_vmcall(&mut args)
222}
223
224pub fn setup_event_notify_interrupt(interrupt_vector: u64) -> Result<(), TdVmcallError> {
234 let mut args = TdVmcallArgs {
235 r11: TdVmcallNum::SetupEventNotifyInterrupt as u64,
236 r12: interrupt_vector,
237 ..Default::default()
238 };
239 td_vmcall(&mut args)
240}
241
242pub fn get_tdvmcall_info(interrupt_vector: u64) -> Result<(), TdVmcallError> {
245 let mut args = TdVmcallArgs {
246 r11: TdVmcallNum::GetTdVmcallInfo as u64,
247 r12: 0,
249 ..Default::default()
250 };
251 td_vmcall(&mut args)
252}
253
254pub fn get_td_service(
277 shared_gpa_input: u64,
278 shared_gpa_output: u64,
279 interrupt_vector: u64,
280 time_out: u64,
281) -> Result<(), TdVmcallError> {
282 let mut args = TdVmcallArgs {
283 r11: TdVmcallNum::Service as u64,
284 r12: shared_gpa_input,
285 r13: shared_gpa_output,
286 r14: interrupt_vector,
287 r15: time_out,
288 ..Default::default()
289 };
290 td_vmcall(&mut args)
291}
292
293macro_rules! io_read {
294 ($port:expr, $ty:ty) => {{
295 let mut args = TdVmcallArgs {
296 r11: TdVmcallNum::Io as u64,
297 r12: core::mem::size_of::<$ty>() as u64,
298 r13: IO_READ,
299 r14: $port as u64,
300 ..Default::default()
301 };
302 td_vmcall(&mut args)?;
303 Ok(args.r11 as u32)
304 }};
305}
306
307pub fn io_read(size: IoSize, port: u16) -> Result<u32, TdVmcallError> {
308 match size {
309 IoSize::Size1 => io_read!(port, u8),
310 IoSize::Size2 => io_read!(port, u16),
311 IoSize::Size4 => io_read!(port, u32),
312 _ => unreachable!(),
313 }
314}
315
316macro_rules! io_write {
317 ($port:expr, $byte:expr, $size:expr) => {{
318 let mut args = TdVmcallArgs {
319 r11: TdVmcallNum::Io as u64,
320 r12: core::mem::size_of_val(&$byte) as u64,
321 r13: IO_WRITE,
322 r14: $port as u64,
323 r15: $byte as u64,
324 ..Default::default()
325 };
326 td_vmcall(&mut args)
327 }};
328}
329
330pub fn io_write(size: IoSize, port: u16, byte: u32) -> Result<(), TdVmcallError> {
331 match size {
332 IoSize::Size1 => io_write!(port, byte as u8, u8),
333 IoSize::Size2 => io_write!(port, byte as u16, u16),
334 IoSize::Size4 => io_write!(port, byte, u32),
335 _ => unreachable!(),
336 }
337}
338
339fn td_vmcall(args: &mut TdVmcallArgs) -> Result<(), TdVmcallError> {
340 let result = unsafe { asm_td_vmcall(args) };
341 match result {
342 0 => Ok(()),
343 _ => Err(result.into()),
344 }
345}
346
347struct Serial;
348
349impl Write for Serial {
350 fn write_str(&mut self, s: &str) -> fmt::Result {
351 for &byte in s.as_bytes() {
352 io_write!(SERIAL_IO_PORT, byte, u8).unwrap();
353 }
354 Ok(())
355 }
356}
357
358pub fn print(args: fmt::Arguments) {
359 Serial
360 .write_fmt(args)
361 .expect("Failed to write to serial port");
362}
363
364#[macro_export]
365macro_rules! serial_print {
366 ($fmt: literal $(, $($arg: tt)+)?) => {
367 $crate::tdvmcall::print(format_args!($fmt $(, $($arg)+)?));
368 }
369}
370
371#[macro_export]
372macro_rules! serial_println {
373 ($fmt: literal $(, $($arg: tt)+)?) => {
374 $crate::tdvmcall::print(format_args!(concat!($fmt, "\n") $(, $($arg)+)?))
375 }
376}