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