From 45591bc18f31508c9a0fc797de600ace688e39ea Mon Sep 17 00:00:00 2001 From: Jon Lange Date: Wed, 6 Nov 2024 10:07:19 -0800 Subject: [PATCH] platform/tdp: replace tdx-tdcall crate with local implementation The `tdx-tdcall` crate is built for different security and design assumptions than what is required for COCONUT-SVSM. The TDCALL/TDVMCALL functionality required can be implemented locally, providing better integration with the COCONUT-SVSM environment. Signed-off-by: Jon Lange --- Cargo.lock | 125 ++------------------ Cargo.toml | 1 - kernel/Cargo.toml | 1 - kernel/src/error.rs | 3 +- kernel/src/lib.rs | 1 + kernel/src/platform/tdp.rs | 69 ++++------- kernel/src/tdx/error.rs | 33 ++++++ kernel/src/tdx/mod.rs | 10 ++ kernel/src/tdx/tdcall.rs | 234 +++++++++++++++++++++++++++++++++++++ 9 files changed, 312 insertions(+), 165 deletions(-) create mode 100644 kernel/src/tdx/error.rs create mode 100644 kernel/src/tdx/mod.rs create mode 100644 kernel/src/tdx/tdcall.rs diff --git a/Cargo.lock b/Cargo.lock index dd58cad3d..2fb87e77f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -113,12 +113,6 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" -[[package]] -name = "bit_field" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" - [[package]] name = "bitfield-struct" version = "0.6.2" @@ -127,7 +121,7 @@ checksum = "adc0846593a56638b74e136a45610f9934c052e14761bebca6b092d5522599e3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn", ] [[package]] @@ -222,7 +216,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.66", + "syn", ] [[package]] @@ -319,7 +313,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn", ] [[package]] @@ -571,15 +565,6 @@ dependencies = [ "libc", ] -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" -dependencies = [ - "spin", -] - [[package]] name = "libc" version = "0.2.155" @@ -601,16 +586,6 @@ dependencies = [ name = "libmstpm" version = "0.1.0" -[[package]] -name = "lock_api" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" -dependencies = [ - "autocfg", - "scopeguard", -] - [[package]] name = "log" version = "0.4.21" @@ -670,7 +645,7 @@ checksum = "2f51a157e01c7343a7c31f540309b3b8b2c9751f3adb6d040373e3139aa2e2e0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn", ] [[package]] @@ -788,38 +763,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "rustversion" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "scroll" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fda28d4b4830b807a8b43f7b0e6b5df875311b3e7621d84577188c175b6ec1ec" -dependencies = [ - "scroll_derive", -] - -[[package]] -name = "scroll_derive" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aaaae8f38bb311444cfb7f1979af0bc9240d95795f75f9ceddf6a59b79ceffa0" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "sec1" version = "0.7.3" @@ -855,15 +798,6 @@ dependencies = [ "rand_core", ] -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" -dependencies = [ - "lock_api", -] - [[package]] name = "spki" version = "0.7.3" @@ -910,7 +844,6 @@ dependencies = [ "log", "packit", "syscall", - "tdx-tdcall", "test", "zerocopy 0.8.2", ] @@ -924,17 +857,6 @@ dependencies = [ "svsm", ] -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - [[package]] name = "syn" version = "2.0.66" @@ -950,19 +872,6 @@ dependencies = [ name = "syscall" version = "0.1.0" -[[package]] -name = "tdx-tdcall" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6ea90455a0e1ee10fe5dd2ddd377d32c6df739ce156b6db5ad87e4c72d460ff" -dependencies = [ - "lazy_static", - "log", - "scroll", - "spin", - "x86_64", -] - [[package]] name = "test" version = "0.1.0" @@ -984,7 +893,7 @@ checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn", ] [[package]] @@ -1006,7 +915,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn", ] [[package]] @@ -1058,12 +967,6 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" -[[package]] -name = "volatile" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "442887c63f2c839b346c192d047a7c87e73d0689c9157b00b53dcc27dd5ea793" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -1143,18 +1046,6 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" -[[package]] -name = "x86_64" -version = "0.14.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96cb6fd45bfeab6a5055c5bffdb08768bd0c069f1d946debe585bbb380a7c062" -dependencies = [ - "bit_field", - "bitflags 2.5.0", - "rustversion", - "volatile", -] - [[package]] name = "zerocopy" version = "0.7.34" @@ -1182,7 +1073,7 @@ checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn", ] [[package]] @@ -1193,7 +1084,7 @@ checksum = "2cf1fea9437ee18b719f41c597b00c1745d7ff77184daf6ac8c61110a0115161" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 682019d5a..acdc59aa7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,7 +42,6 @@ libfuzzer-sys = "0.4" log = "0.4.17" p384 = { version = "0.13.0" } sha2 = "0.10.8" -tdx-tdcall = "0.2.1" uuid = "1.6.1" # Add the derive feature by default because all crates use it. zerocopy = { version = "0.8.2", features = ["alloc", "derive"] } diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 1a8df7b6d..e5f07584a 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -33,7 +33,6 @@ igvm_defs = { workspace = true, features = ["unstable"] } intrusive-collections.workspace = true log = { workspace = true, features = ["max_level_info", "release_max_level_info"] } packit.workspace = true -tdx-tdcall.workspace = true libmstpm = { workspace = true, optional = true } zerocopy.workspace = true diff --git a/kernel/src/error.rs b/kernel/src/error.rs index ddd7631af..15f773e41 100644 --- a/kernel/src/error.rs +++ b/kernel/src/error.rs @@ -27,6 +27,7 @@ use crate::sev::msr_protocol::GhcbMsrError; use crate::sev::SevSnpError; use crate::syscall::ObjError; use crate::task::TaskError; +use crate::tdx::TdxError; use elf::ElfError; use syscall::SysCallError; @@ -58,7 +59,7 @@ pub enum SvsmError { /// Errors related to SEV-SNP operations, like PVALIDATE or RMPUPDATE SevSnp(SevSnpError), /// Errors related to TDX operations - Tdx, + Tdx(TdxError), /// Generic errors related to memory management Mem, /// Errors related to the memory allocator diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs index 250b94e0b..9ae4adca5 100644 --- a/kernel/src/lib.rs +++ b/kernel/src/lib.rs @@ -37,6 +37,7 @@ pub mod string; pub mod svsm_paging; pub mod syscall; pub mod task; +pub mod tdx; pub mod types; pub mod utils; #[cfg(all(feature = "mstpm", not(test)))] diff --git a/kernel/src/platform/tdp.rs b/kernel/src/platform/tdp.rs index 9889f6249..5496cf1f5 100644 --- a/kernel/src/platform/tdp.rs +++ b/kernel/src/platform/tdp.rs @@ -4,21 +4,21 @@ // // Author: Peter Fang -use crate::address::{Address, PhysAddr, VirtAddr}; +use crate::address::{PhysAddr, VirtAddr}; use crate::console::init_svsm_console; use crate::cpu::cpuid::CpuidResult; use crate::cpu::percpu::PerCpu; use crate::error::SvsmError; use crate::io::IOPort; -use crate::mm::virt_to_frame; use crate::platform::{PageEncryptionMasks, PageStateChangeOp, PageValidateOp, SvsmPlatform}; -use crate::types::{PageSize, PAGE_SIZE, PAGE_SIZE_2M}; +use crate::tdx::tdcall::{ + td_accept_physical_memory, td_accept_virtual_memory, tdvmcall_halt, tdvmcall_io_read, + tdvmcall_io_write, +}; +use crate::tdx::TdxError; +use crate::types::PageSize; use crate::utils::immut_after_init::ImmutAfterInitCell; use crate::utils::MemoryRegion; -use tdx_tdcall::tdx::{ - td_accept_memory, tdvmcall_halt, tdvmcall_io_read_16, tdvmcall_io_read_32, tdvmcall_io_read_8, - tdvmcall_io_write_16, tdvmcall_io_write_32, tdvmcall_io_write_8, -}; static GHCI_IO_DRIVER: GHCIIOPort = GHCIIOPort::new(); static VTOM: ImmutAfterInitCell = ImmutAfterInitCell::uninit(); @@ -58,11 +58,11 @@ impl SvsmPlatform for TdpPlatform { } fn setup_percpu(&self, _cpu: &PerCpu) -> Result<(), SvsmError> { - Err(SvsmError::Tdx) + Err(TdxError::Unimplemented.into()) } fn setup_percpu_current(&self, _cpu: &PerCpu) -> Result<(), SvsmError> { - Err(SvsmError::Tdx) + Err(TdxError::Unimplemented.into()) } fn get_page_encryption_masks(&self) -> PageEncryptionMasks { @@ -93,7 +93,7 @@ impl SvsmPlatform for TdpPlatform { _size: PageSize, _op: PageStateChangeOp, ) -> Result<(), SvsmError> { - Err(SvsmError::Tdx) + Err(TdxError::Unimplemented.into()) } fn validate_physical_page_range( @@ -102,12 +102,12 @@ impl SvsmPlatform for TdpPlatform { op: PageValidateOp, ) -> Result<(), SvsmError> { match op { - PageValidateOp::Validate => { - td_accept_memory(region.start().into(), region.len().try_into().unwrap()); + PageValidateOp::Validate => td_accept_physical_memory(region), + PageValidateOp::Invalidate => { + // No work is required at invalidation time. + Ok(()) } - PageValidateOp::Invalidate => {} } - Ok(()) } fn validate_virtual_page_range( @@ -116,34 +116,13 @@ impl SvsmPlatform for TdpPlatform { op: PageValidateOp, ) -> Result<(), SvsmError> { match op { - PageValidateOp::Validate => { - let mut va = region.start(); - let va_end = region.end(); - while va < va_end { - let frame = virt_to_frame(va); - // Validate a 2 MB page if and only if the addresses are - // appropriately aligned and the underlying mapping is a - // 2 MB mapping. - let size = if va.is_aligned(PAGE_SIZE_2M) - && va + PAGE_SIZE_2M <= va_end - && frame.size() >= PAGE_SIZE_2M - { - PAGE_SIZE_2M - } else { - PAGE_SIZE - }; - - td_accept_memory(frame.address().into(), size.try_into().unwrap()); - va = va + size; - } - } - PageValidateOp::Invalidate => {} + PageValidateOp::Validate => td_accept_virtual_memory(region), + PageValidateOp::Invalidate => Ok(()), } - Ok(()) } fn configure_alternate_injection(&mut self, _alt_inj_requested: bool) -> Result<(), SvsmError> { - Err(SvsmError::Tdx) + Err(TdxError::Unimplemented.into()) } fn change_apic_registration_state(&self, _incr: bool) -> Result { @@ -159,7 +138,7 @@ impl SvsmPlatform for TdpPlatform { } fn post_irq(&self, _icr: u64) -> Result<(), SvsmError> { - Err(SvsmError::Tdx) + Err(TdxError::Unimplemented.into()) } fn eoi(&self) {} @@ -187,26 +166,26 @@ impl GHCIIOPort { impl IOPort for GHCIIOPort { fn outb(&self, port: u16, value: u8) { - tdvmcall_io_write_8(port, value); + tdvmcall_io_write(port, value); } fn inb(&self, port: u16) -> u8 { - tdvmcall_io_read_8(port) + tdvmcall_io_read::(port) as u8 } fn outw(&self, port: u16, value: u16) { - tdvmcall_io_write_16(port, value); + tdvmcall_io_write(port, value); } fn inw(&self, port: u16) -> u16 { - tdvmcall_io_read_16(port) + tdvmcall_io_read::(port) as u16 } fn outl(&self, port: u16, value: u32) { - tdvmcall_io_write_32(port, value); + tdvmcall_io_write(port, value); } fn inl(&self, port: u16) -> u32 { - tdvmcall_io_read_32(port) + tdvmcall_io_read::(port) } } diff --git a/kernel/src/tdx/error.rs b/kernel/src/tdx/error.rs new file mode 100644 index 000000000..499309ef5 --- /dev/null +++ b/kernel/src/tdx/error.rs @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) Microsoft Corporation +// +// Author: Jon Lange + +use crate::error::SvsmError; + +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum TdxError { + Unknown(u64), + Unimplemented, + PageAlreadyAccepted, + PageSizeMismatch, +} + +impl From for SvsmError { + fn from(err: TdxError) -> SvsmError { + SvsmError::Tdx(err) + } +} + +pub fn tdx_result(err: u64) -> Result { + let code = err >> 32; + if code < 0x8000_0000 { + return Ok(code); + } + match code { + 0xC000_0B0A => Err(TdxError::PageAlreadyAccepted), + 0xC000_0B0B => Err(TdxError::PageSizeMismatch), + _ => Err(TdxError::Unknown(err)), + } +} diff --git a/kernel/src/tdx/mod.rs b/kernel/src/tdx/mod.rs new file mode 100644 index 000000000..705b4b418 --- /dev/null +++ b/kernel/src/tdx/mod.rs @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) Microsoft Corporation +// +// Author: Jon Lange + +pub mod error; +pub mod tdcall; + +pub use error::TdxError; diff --git a/kernel/src/tdx/tdcall.rs b/kernel/src/tdx/tdcall.rs new file mode 100644 index 000000000..9582c4da0 --- /dev/null +++ b/kernel/src/tdx/tdcall.rs @@ -0,0 +1,234 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// +// Copyright (c) Microsoft Corporation +// +// Author: Jon Lange + +use super::error::{tdx_result, TdxError}; +use crate::address::{Address, PhysAddr, VirtAddr}; +use crate::error::SvsmError; +use crate::mm::pagetable::PageFrame; +use crate::mm::{virt_to_frame, PerCPUPageMappingGuard}; +use crate::types::{PAGE_SHIFT, PAGE_SIZE, PAGE_SIZE_2M}; +use crate::utils::MemoryRegion; + +use core::arch::asm; + +const TDG_VP_TDVMCALL: u32 = 0; +const TDCALL_TDG_MEM_PAGE_ACCEPT: u32 = 6; + +const TDVMCALL_HLT: u32 = 12; +const TDVMCALL_IO: u32 = 30; + +fn gpa_info(frame: PageFrame) -> u64 { + // Callers are expected to guarantee alignment before calling this + // function. If the caller verifies the alignment, then it can be done + // once without having to do it in a loop. + debug_assert!(u64::from(frame.address()) & (frame.size() - 1) as u64 == 0); + match frame { + PageFrame::Size4K(gpa) => u64::from(gpa), + PageFrame::Size2M(gpa) => u64::from(gpa) | 1, + PageFrame::Size1G(gpa) => u64::from(gpa) | 2, + } +} + +fn tdg_mem_page_accept(frame: PageFrame) -> u64 { + let mut ret: u64; + unsafe { + asm!("tdcall", + in("rax") TDCALL_TDG_MEM_PAGE_ACCEPT, + in("rcx") gpa_info(frame), + lateout("rax") ret, + options(att_syntax)); + } + ret +} + +pub fn td_accept_physical_memory(region: MemoryRegion) -> Result<(), SvsmError> { + let mut addr = region.start(); + if !addr.is_aligned(PAGE_SIZE) { + return Err(SvsmError::InvalidAddress); + } + + let end = region.end(); + + while addr < end { + if addr.is_aligned(PAGE_SIZE_2M) && addr + PAGE_SIZE_2M <= end { + let ret = tdx_result(tdg_mem_page_accept(PageFrame::Size2M(addr))); + match ret { + Err(TdxError::PageAlreadyAccepted) => { + // The caller is expected not to accept a page twice unless + // doing so is known to be safe. If the TDX module + // indicates that the page was already accepted, it must + // mean that the page was not removed after a previous + // attempt to accept. In this case, the page must be + // zeroed now because the caller expects every accepted + // page to be zeroed. + unsafe { + let mapping = PerCPUPageMappingGuard::create(addr, addr + PAGE_SIZE_2M, 0)?; + mapping + .virt_addr() + .as_mut_ptr::() + .write_bytes(0, PAGE_SIZE_2M); + } + addr = addr + PAGE_SIZE_2M; + continue; + } + Ok(_) => { + addr = addr + PAGE_SIZE_2M; + continue; + } + Err(TdxError::PageSizeMismatch) => { + // Fall through to the 4 KB path below. + } + Err(e) => { + return Err(e.into()); + } + } + } + + let ret = tdx_result(tdg_mem_page_accept(PageFrame::Size4K(addr))); + if let Err(e) = ret { + if e != TdxError::PageAlreadyAccepted { + return Err(e.into()); + } + + // Zero the 4 KB page. + unsafe { + let mapping = PerCPUPageMappingGuard::create(addr, addr + PAGE_SIZE, 0)?; + mapping + .virt_addr() + .as_mut_ptr::() + .write_bytes(0, PAGE_SIZE); + } + } + + addr = addr + PAGE_SIZE; + } + + Ok(()) +} + +fn td_accept_virtual_4k(vaddr: VirtAddr, paddr: PhysAddr) -> Result<(), SvsmError> { + let ret = tdx_result(tdg_mem_page_accept(PageFrame::Size4K(paddr))); + match ret { + Err(TdxError::PageAlreadyAccepted) => { + // Zero the 4 KB page. + unsafe { + vaddr.as_mut_ptr::().write_bytes(0, PAGE_SIZE); + } + Ok(()) + } + Err(e) => Err(e.into()), + Ok(_) => Ok(()), + } +} + +fn td_accept_virtual_2m(vaddr: VirtAddr, paddr: PhysAddr) -> Result<(), SvsmError> { + let ret = tdx_result(tdg_mem_page_accept(PageFrame::Size2M(paddr))); + match ret { + Err(TdxError::PageAlreadyAccepted) => { + // Zero the 2M page. + unsafe { + vaddr.as_mut_ptr::().write_bytes(0, PAGE_SIZE_2M); + } + Ok(()) + } + Err(TdxError::PageSizeMismatch) => { + // Process this 2 MB page as a series of 4 KB pages. + for offset in 0usize..512usize { + td_accept_virtual_4k( + vaddr + (offset << PAGE_SHIFT), + paddr + (offset << PAGE_SHIFT), + )?; + } + Ok(()) + } + Err(e) => Err(e.into()), + Ok(_) => Ok(()), + } +} + +pub fn td_accept_virtual_memory(region: MemoryRegion) -> Result<(), SvsmError> { + let mut vaddr = region.start(); + if !vaddr.is_aligned(PAGE_SIZE) { + return Err(SvsmError::InvalidAddress); + } + + let vaddr_end = region.end(); + while vaddr < vaddr_end { + let frame = virt_to_frame(vaddr); + let size = if vaddr.is_aligned(PAGE_SIZE_2M) + && vaddr + PAGE_SIZE_2M <= vaddr_end + && frame.size() >= PAGE_SIZE_2M + { + td_accept_virtual_2m(vaddr, frame.address())?; + PAGE_SIZE_2M + } else { + td_accept_virtual_4k(vaddr, frame.address())?; + PAGE_SIZE + }; + + vaddr = vaddr + size; + } + Ok(()) +} + +pub fn tdvmcall_halt() { + let pass_regs = (1 << 10) | (1 << 11) | (1 << 12); + unsafe { + asm!("tdcall", + in("eax") TDG_VP_TDVMCALL, + in("ecx") pass_regs, + in("r10d") 0, + in("r11d") TDVMCALL_HLT, + in("r12d") 0, + lateout("r10d") _, + lateout("r11d") _, + lateout("r12d") _, + options(att_syntax)); + } +} + +fn tdvmcall_io(port: u16, data: u32, size: usize, write: bool) -> u32 { + let pass_regs = (1 << 10) | (1 << 11) | (1 << 12) | (1 << 13) | (1 << 14) | (1 << 15); + let mut ret: u64; + let mut output: u32; + unsafe { + asm!("tdcall", + in("eax") 0, + in("ecx") pass_regs, + in("r10d") 0, + in("r11d") TDVMCALL_IO, + in("r12d") size, + in("r13d") write as u32, + in("r14d") port as u32, + in("r15d") data, + lateout("r10d") ret, + lateout("r11d") output, + lateout("r12d") _, + lateout("r13d") _, + lateout("r14d") _, + lateout("r15d") _, + options(att_syntax)); + } + + // Ignore errors here. The caller cannot handle them, and since the + // I/O operation was performed by an untrusted source, the error + // information is not meaningfully different than a maliciously unreliable + // operation. + debug_assert!(tdx_result(ret).is_ok()); + + output +} + +pub fn tdvmcall_io_write(port: u16, data: T) +where + u32: From, +{ + let _ = tdvmcall_io(port, u32::from(data), size_of::(), true); +} + +pub fn tdvmcall_io_read(port: u16) -> u32 { + tdvmcall_io(port, 0, size_of::(), false) +}