diff --git a/framework/libs/tdx-guest/src/lib.rs b/framework/libs/tdx-guest/src/lib.rs index a38128d3a..56f6fbeb3 100644 --- a/framework/libs/tdx-guest/src/lib.rs +++ b/framework/libs/tdx-guest/src/lib.rs @@ -8,18 +8,18 @@ pub mod asm; pub mod tdcall; pub mod tdvmcall; -pub use self::tdcall::{deadloop, tdg_vp_veinfo_get, TdxVirtualExceptionType}; +pub use self::tdcall::{get_veinfo, TdxVirtualExceptionType}; pub use self::tdvmcall::print; -use alloc::string::{String, ToString}; use raw_cpuid::{native_cpuid::cpuid_count, CpuIdResult}; -use tdcall::{tdg_vp_vmcall, InitError, TdgVeInfo, TdgVpInfo}; +use tdcall::{InitError, TdgVeInfo, TdgVpInfo}; +use tdvmcall::*; const TDX_CPUID_LEAF_ID: u64 = 0x21; pub fn tdx_early_init() -> Result { match is_tdx_guest() { - Ok(_) => Ok(tdcall::tdg_vp_info()?), + Ok(_) => Ok(tdcall::get_tdinfo()?), Err(err) => Err(err), } } @@ -30,9 +30,9 @@ fn is_tdx_guest() -> Result<(), InitError> { return Err(InitError::TdxCpuLeafIdError); } let cpuid_result: CpuIdResult = cpuid_count(TDX_CPUID_LEAF_ID as u32, 0); - if convert_ascii(cpuid_result.ebx) == "Inte" - && convert_ascii(cpuid_result.edx) == "lTDX" - && convert_ascii(cpuid_result.ecx) == " " + if &cpuid_result.ebx.to_ne_bytes() == b"Inte" + && &cpuid_result.ebx.to_ne_bytes() == b"lTDX" + && &cpuid_result.ecx.to_ne_bytes() == b" " { Ok(()) } else { @@ -40,16 +40,6 @@ fn is_tdx_guest() -> Result<(), InitError> { } } -fn convert_ascii(reg: u32) -> String { - let bytes = [ - (reg & 0xFF) as u8, - ((reg >> 8) & 0xFF) as u8, - ((reg >> 16) & 0xFF) as u8, - ((reg >> 24) & 0xFF) as u8, - ]; - String::from_utf8_lossy(&bytes).to_string() -} - pub trait TdxTrapFrame { fn rax(&self) -> usize; fn set_rax(&mut self, rax: usize); @@ -69,13 +59,35 @@ pub trait TdxTrapFrame { pub fn virtual_exception_handler(trapframe: &mut impl TdxTrapFrame, ve_info: &TdgVeInfo) { match ve_info.exit_reason.into() { - TdxVirtualExceptionType::Hlt - | TdxVirtualExceptionType::Io - | TdxVirtualExceptionType::MsrRead - | TdxVirtualExceptionType::MsrWrite - | TdxVirtualExceptionType::CpuId => tdg_vp_vmcall(trapframe, ve_info), + TdxVirtualExceptionType::Hlt => { + serial_println!("Ready to halt"); + hlt(); + } + TdxVirtualExceptionType::Io => { + if !handle_io(trapframe, ve_info) { + serial_println!("Handle tdx ioexit errors, ready to halt"); + hlt(); + } + } + TdxVirtualExceptionType::MsrRead => { + let msr = rdmsr(trapframe.rcx() as u32).unwrap(); + trapframe.set_rax((msr as u32 & u32::MAX) as usize); + trapframe.set_rdx(((msr >> 32) as u32 & u32::MAX) as usize); + } + TdxVirtualExceptionType::MsrWrite => { + let data = trapframe.rax() as u64 | ((trapframe.rdx() as u64) << 32); + wrmsr(trapframe.rcx() as u32, data).unwrap(); + } + TdxVirtualExceptionType::CpuId => { + let cpuid_info = cpuid(trapframe.rax() as u32, trapframe.rcx() as u32).unwrap(); + let mask = 0xFFFF_FFFF_0000_0000_usize; + trapframe.set_rax((trapframe.rax() & mask) | cpuid_info.eax); + trapframe.set_rbx((trapframe.rbx() & mask) | cpuid_info.ebx); + trapframe.set_rcx((trapframe.rcx() & mask) | cpuid_info.ecx); + trapframe.set_rdx((trapframe.rdx() & mask) | cpuid_info.edx); + } TdxVirtualExceptionType::Other => panic!("Unknown TDX vitrual exception type"), _ => return, - }; + } trapframe.set_rip(trapframe.rip() + ve_info.exit_instruction_length as usize); } diff --git a/framework/libs/tdx-guest/src/tdcall.rs b/framework/libs/tdx-guest/src/tdcall.rs index 4a293abef..609afedd6 100644 --- a/framework/libs/tdx-guest/src/tdcall.rs +++ b/framework/libs/tdx-guest/src/tdcall.rs @@ -1,25 +1,34 @@ -use super::tdvmcall::*; -use crate::{asm::asm_td_call, serial_println, TdxTrapFrame}; +//! 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 crate::asm::asm_td_call; use bitflags::bitflags; use core::fmt; -const TDCALL_VP_INFO: u64 = 1; -const TDCALL_MR_RTMR_EXTEND: u64 = 2; -const TDCALL_VP_VEINFO_GET: u64 = 3; -const TDCALL_MR_REPORT: u64 = 4; -const TDCALL_VP_CPUIDVE_SET: u64 = 5; -const TDCALL_MEM_PAGE_ACCEPT: u64 = 6; -const TDCALL_VM_RD: u64 = 7; -const TDCALL_VM_WR: u64 = 8; -const TDCALL_MR_VERIFYREPORT: u64 = 22; -const TDCALL_MEM_PAGE_ATTR_RD: u64 = 23; -const TDCALL_MEM_PAGE_ATTR_WR: u64 = 24; +/// 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! { - pub struct Attributes: u64 { + /// 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; + const DEBUG = 1 << 0; /// TD is migratable (using a Migration TD). const MIGRATABLE = 1 << 29; /// TD is allowed to use Supervisor Protection Keys. @@ -36,7 +45,7 @@ bitflags! { struct CpuidveFlag: u64 { /// Flags that when CPL is 0, a CPUID executed /// by the guest TD will cause a #VE unconditionally. - const SUPERVISOR = 1; + 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; @@ -254,7 +263,7 @@ pub struct TdgVpInfo { /// Only GPAW values 48 and 52 are possible. pub gpaw: Gpaw, /// The TD's ATTRIBUTES (provided as input to TDH.MNG.INIT) - pub attributes: Attributes, + pub attributes: GuestTdAttributes, pub num_vcpus: u32, pub max_vcpus: u32, pub vcpu_index: u32, @@ -350,16 +359,16 @@ impl From for InitError { } /// Get guest TD execution environment information. -pub(crate) fn tdg_vp_info() -> Result { +pub fn get_tdinfo() -> Result { let mut args = TdcallArgs { - rax: TDCALL_VP_INFO, + rax: TdcallNum::VpInfo as u64, ..Default::default() }; match td_call(&mut args) { Ok(()) => { let td_info = TdgVpInfo { gpaw: Gpaw::from(args.rcx), - attributes: Attributes::from_bits_truncate(args.rdx), + 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, @@ -372,9 +381,9 @@ pub(crate) fn tdg_vp_info() -> Result { } /// Get Virtualization Exception Information for the recent #VE exception. -pub fn tdg_vp_veinfo_get() -> Result { +pub fn get_veinfo() -> Result { let mut args = TdcallArgs { - rax: TDCALL_VP_VEINFO_GET, + rax: TdcallNum::VpVeinfoGet as u64, ..Default::default() }; match td_call(&mut args) { @@ -393,64 +402,10 @@ pub fn tdg_vp_veinfo_get() -> Result { } } -/// Perform a TD Exit to the host VMM. -pub(crate) fn tdg_vp_vmcall(trapframe: &mut impl TdxTrapFrame, ve_info: &TdgVeInfo) { - match ve_info.exit_reason.into() { - TdxVirtualExceptionType::Hlt => { - serial_println!("Ready to halt"); - tdvmcall_hlt(); - } - TdxVirtualExceptionType::Io => { - if !tdvmcall_io(trapframe, ve_info) { - serial_println!("Handle tdx ioexit errors, ready to halt"); - tdvmcall_hlt(); - } - } - TdxVirtualExceptionType::MsrRead => { - let msr = tdvmcall_rdmsr(trapframe.rcx() as u32).unwrap(); - trapframe.set_rax((msr as u32 & u32::MAX) as usize); - trapframe.set_rdx(((msr >> 32) as u32 & u32::MAX) as usize); - } - TdxVirtualExceptionType::MsrWrite => { - let data = trapframe.rax() as u64 | ((trapframe.rdx() as u64) << 32); - tdvmcall_wrmsr(trapframe.rcx() as u32, data).unwrap(); - } - TdxVirtualExceptionType::CpuId => { - let cpuid = tdvmcall_cpuid(trapframe.rax() as u32, trapframe.rcx() as u32).unwrap(); - let mask = 0xFFFF_FFFF_0000_0000_usize; - trapframe.set_rax((trapframe.rax() & mask) | cpuid.eax); - trapframe.set_rbx((trapframe.rbx() & mask) | cpuid.ebx); - trapframe.set_rcx((trapframe.rcx() & mask) | cpuid.ecx); - trapframe.set_rdx((trapframe.rdx() & mask) | cpuid.edx); - } - _ => { - unreachable!() - } - }; -} - -#[allow(dead_code)] -pub fn deadloop() { - #[allow(clippy::empty_loop)] - loop { - x86_64::instructions::interrupts::enable(); - x86_64::instructions::hlt(); - } -} - -fn td_call(args: &mut TdcallArgs) -> Result<(), TdCallError> { - let td_call_result = unsafe { asm_td_call(args) }; - if td_call_result == 0 { - Ok(()) - } else { - Err(td_call_result.into()) - } -} - /// Extend a TDCS.RTMR measurement register. -fn tdg_mr_rtmr_extend() -> Result<(), TdCallError> { +pub fn extend_rtmr() -> Result<(), TdCallError> { let mut args = TdcallArgs { - rax: TDCALL_MR_RTMR_EXTEND, + rax: TdcallNum::MrRtmrExtend as u64, ..Default::default() }; match td_call(&mut args) { @@ -462,9 +417,9 @@ fn tdg_mr_rtmr_extend() -> Result<(), TdCallError> { /// 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. -fn tdg_mr_report(report_addr: u64, data_addr: u64) -> Result<(), TdCallError> { +pub fn get_report(report_addr: u64, data_addr: u64) -> Result<(), TdCallError> { let mut args = TdcallArgs { - rax: TDCALL_MR_REPORT, + rax: TdcallNum::MrReport as u64, rcx: report_addr, rdx: data_addr, ..Default::default() @@ -477,9 +432,9 @@ fn tdg_mr_report(report_addr: u64, data_addr: u64) -> Result<(), TdCallError> { /// 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. -fn tdg_mr_verifyreport(report_mac_addr: u64) -> Result<(), TdCallError> { +pub fn verify_report(report_mac_addr: u64) -> Result<(), TdCallError> { let mut args = TdcallArgs { - rax: TDCALL_MR_VERIFYREPORT, + rax: TdcallNum::MrVerifyreport as u64, rcx: report_mac_addr, ..Default::default() }; @@ -490,9 +445,9 @@ fn tdg_mr_verifyreport(report_mac_addr: u64) -> Result<(), TdCallError> { } /// Accept a pending private page and initialize it to all-0 using the TD ephemeral private key. -fn tdg_mem_page_accept(sept_level: u64, addr: u64) -> Result<(), TdCallError> { +pub fn accept_page(sept_level: u64, addr: u64) -> Result<(), TdCallError> { let mut args = TdcallArgs { - rax: TDCALL_MEM_PAGE_ACCEPT, + rax: TdcallNum::MemPageAccept as u64, rcx: sept_level | (addr << 12), ..Default::default() }; @@ -503,9 +458,9 @@ fn tdg_mem_page_accept(sept_level: u64, addr: u64) -> Result<(), TdCallError> { } /// Read the GPA mapping and attributes of a TD private page. -fn tdg_mem_page_attr_rd(phy_addr: u64) -> Result { +pub fn read_page_attr(phy_addr: u64) -> Result { let mut args = TdcallArgs { - rax: TDCALL_MEM_PAGE_ATTR_RD, + rax: TdcallNum::MemPageAttrRd as u64, rcx: phy_addr, ..Default::default() }; @@ -522,9 +477,9 @@ fn tdg_mem_page_attr_rd(phy_addr: u64) -> Result { } /// Write the attributes of a private page. Create or remove L2 page aliases as required. -fn tdg_mem_page_attr_wr(page_attr: PageAttr, attr_flags: u64) -> Result { +pub fn write_page_attr(page_attr: PageAttr, attr_flags: u64) -> Result { let mut args = TdcallArgs { - rax: TDCALL_MEM_PAGE_ATTR_WR, + rax: TdcallNum::MemPageAttrWr as u64, rcx: page_attr.gpa_mapping, rdx: u64::from(page_attr.gpa_attr), r8: attr_flags, @@ -543,9 +498,9 @@ fn tdg_mem_page_attr_wr(page_attr: PageAttr, attr_flags: u64) -> Result Result { +pub fn read_td_metadata(field_identifier: u64) -> Result { let mut args = TdcallArgs { - rax: TDCALL_VM_RD, + rax: TdcallNum::VmRd as u64, rdx: field_identifier, ..Default::default() }; @@ -563,18 +518,19 @@ fn tdg_vm_rd(field_identifier: u64) -> Result { /// in R8 are to be written to the field. /// /// It returns previous contents of the field. -fn tdg_vm_wr(field_identifier: u64, data: u64, write_mask: u64) -> Result { +pub fn write_td_metadata( + field_identifier: u64, + data: u64, + write_mask: u64, +) -> Result { let mut args = TdcallArgs { - rax: TDCALL_VM_WR, + rax: TdcallNum::VmWr as u64, rdx: field_identifier, r8: data, r9: write_mask, ..Default::default() }; - match td_call(&mut args) { - Ok(()) => Ok(args.r8), - Err(res) => Err(res), - } + td_call(&mut args).map(|_| args.r8) } /// TDG.VP.CPUIDVE.SET controls unconditional #VE on CPUID execution by the guest TD. @@ -583,9 +539,9 @@ fn tdg_vm_wr(field_identifier: u64, data: u64, write_mask: u64) -> Result Result<(), TdCallError> { +pub fn set_cpuidve(cpuidve_flag: u64) -> Result<(), TdCallError> { let mut args = TdcallArgs { - rax: TDCALL_VP_CPUIDVE_SET, + rax: TdcallNum::VpCpuidveSet as u64, rcx: cpuidve_flag, ..Default::default() }; @@ -594,3 +550,12 @@ fn tdg_vp_cpuidve_set(cpuidve_flag: u64) -> Result<(), TdCallError> { Err(res) => Err(res), } } + +fn td_call(args: &mut TdcallArgs) -> Result<(), TdCallError> { + let td_call_result = unsafe { asm_td_call(args) }; + if td_call_result == 0 { + Ok(()) + } else { + Err(td_call_result.into()) + } +} diff --git a/framework/libs/tdx-guest/src/tdvmcall.rs b/framework/libs/tdx-guest/src/tdvmcall.rs index f78bb6593..f961a5673 100644 --- a/framework/libs/tdx-guest/src/tdvmcall.rs +++ b/framework/libs/tdx-guest/src/tdvmcall.rs @@ -1,3 +1,8 @@ +//! 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 crate::{asm::asm_td_vmcall, tdcall::TdgVeInfo, TdxTrapFrame}; @@ -9,15 +14,19 @@ use x86_64::{ structures::port::PortRead, }; -const TDVMCALL_CPUID: u64 = 0x0000a; -const TDVMCALL_HLT: u64 = 0x0000c; -const TDVMCALL_IO: u64 = 0x0001e; -const TDVMCALL_RDMSR: u64 = 0x0001f; -const TDVMCALL_WRMSR: u64 = 0x00020; -const TDVMCALL_REQUEST_MMIO: u64 = 0x00030; -const TDVMCALL_WBINVD: u64 = 0x00036; -const TDVMCALL_PCONFIG: u64 = 0x00041; -const TDVMCALL_MAPGPA: u64 = 0x10001; +/// 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; @@ -62,26 +71,26 @@ pub(crate) struct TdVmcallArgs { #[repr(C)] #[derive(Debug, Default)] -pub(crate) struct CpuIdInfo { +pub struct CpuIdInfo { pub eax: usize, pub ebx: usize, pub ecx: usize, pub edx: usize, } -enum Direction { +pub enum Direction { In, Out, } -enum Operand { +pub enum Operand { Dx, Immediate, } -pub(crate) fn tdvmcall_cpuid(eax: u32, ecx: u32) -> Result { +pub fn cpuid(eax: u32, ecx: u32) -> Result { let mut args = TdVmcallArgs { - r11: TDVMCALL_CPUID, + r11: TdvmcallNum::Cpuid as u64, r12: eax as u64, r13: ecx as u64, ..Default::default() @@ -97,19 +106,19 @@ pub(crate) fn tdvmcall_cpuid(eax: u32, ecx: u32) -> Result Result { +pub fn rdmsr(index: u32) -> Result { let mut args = TdVmcallArgs { - r11: TDVMCALL_RDMSR, + r11: TdvmcallNum::Rdmsr as u64, r12: index as u64, ..Default::default() }; @@ -119,9 +128,9 @@ pub(crate) fn tdvmcall_rdmsr(index: u32) -> Result { } } -pub(crate) fn tdvmcall_wrmsr(index: u32, value: u64) -> Result<(), TdVmcallError> { +pub fn wrmsr(index: u32, value: u64) -> Result<(), TdVmcallError> { let mut args = TdVmcallArgs { - r11: TDVMCALL_WRMSR, + r11: TdvmcallNum::Wrmsr as u64, r12: index as u64, r13: value, ..Default::default() @@ -133,9 +142,9 @@ pub(crate) fn tdvmcall_wrmsr(index: u32, value: u64) -> Result<(), TdVmcallError } /// Used to help perform WBINVD operation. -pub(crate) fn tdvmcall_wbinvd(wbinvd: u64) -> Result<(), TdVmcallError> { +pub fn wbinvd(wbinvd: u64) -> Result<(), TdVmcallError> { let mut args = TdVmcallArgs { - r11: TDVMCALL_WBINVD, + r11: TdvmcallNum::Wbinvd as u64, r12: wbinvd, ..Default::default() }; @@ -145,13 +154,13 @@ pub(crate) fn tdvmcall_wbinvd(wbinvd: u64) -> Result<(), TdVmcallError> { } } -pub(crate) fn tdvmcall_read_mmio(size: u64, mmio_addr: u64) -> Result { +pub fn read_mmio(size: u64, mmio_addr: u64) -> Result { match size { 1 | 2 | 4 | 8 => {} _ => return Err(TdVmcallError::TdxInvalidOperand), } let mut args = TdVmcallArgs { - r11: TDVMCALL_REQUEST_MMIO, + r11: TdvmcallNum::RequestMmio as u64, r12: size, r13: 0, r14: mmio_addr, @@ -163,11 +172,7 @@ pub(crate) fn tdvmcall_read_mmio(size: u64, mmio_addr: u64) -> Result Result<(), TdVmcallError> { +pub fn write_mmio(size: u64, mmio_addr: u64, data: u64) -> Result<(), TdVmcallError> { match size { 1 | 2 | 4 | 8 => {} _ => { @@ -175,7 +180,7 @@ pub(crate) fn tdvmcall_write_mmio( } } let mut args = TdVmcallArgs { - r11: TDVMCALL_REQUEST_MMIO, + r11: TdvmcallNum::RequestMmio as u64, r12: size, r13: 1, r14: mmio_addr, @@ -188,7 +193,7 @@ pub(crate) fn tdvmcall_write_mmio( } } -pub(crate) fn tdvmcall_io(trapframe: &mut impl TdxTrapFrame, ve_info: &TdgVeInfo) -> bool { +pub fn handle_io(trapframe: &mut impl TdxTrapFrame, ve_info: &TdgVeInfo) -> bool { let size = match ve_info.exit_qualification & 0x3 { 0 => 1, 1 => 2, @@ -220,10 +225,10 @@ pub(crate) fn tdvmcall_io(trapframe: &mut impl TdxTrapFrame, ve_info: &TdgVeInfo true } -macro_rules! tdvmcall_io_read { +macro_rules! io_read { ($port:expr, $ty:ty) => {{ let mut args = TdVmcallArgs { - r11: TDVMCALL_IO, + r11: TdvmcallNum::Io as u64, r12: core::mem::size_of::<$ty>() as u64, r13: IO_READ, r14: $port as u64, @@ -236,19 +241,19 @@ macro_rules! tdvmcall_io_read { }}; } -fn io_read(size: usize, port: u16) -> Result { +pub fn io_read(size: usize, port: u16) -> Result { match size { - 1 => tdvmcall_io_read!(port, u8), - 2 => tdvmcall_io_read!(port, u16), - 4 => tdvmcall_io_read!(port, u32), + 1 => io_read!(port, u8), + 2 => io_read!(port, u16), + 4 => io_read!(port, u32), _ => unreachable!(), } } -macro_rules! tdvmcall_io_write { +macro_rules! io_write { ($port:expr, $byte:expr, $size:expr) => {{ let mut args = TdVmcallArgs { - r11: TDVMCALL_IO, + r11: TdvmcallNum::Io as u64, r12: core::mem::size_of_val(&$byte) as u64, r13: IO_WRITE, r14: $port as u64, @@ -262,11 +267,11 @@ macro_rules! tdvmcall_io_write { }}; } -fn io_write(size: usize, port: u16, byte: u32) -> Result<(), TdVmcallError> { +pub fn io_write(size: usize, port: u16, byte: u32) -> Result<(), TdVmcallError> { match size { - 1 => tdvmcall_io_write!(port, byte, u8), - 2 => tdvmcall_io_write!(port, byte, u16), - 4 => tdvmcall_io_write!(port, byte, u32), + 1 => io_write!(port, byte, u8), + 2 => io_write!(port, byte, u16), + 4 => io_write!(port, byte, u32), _ => unreachable!(), } } @@ -312,15 +317,15 @@ fn serial_write_byte(byte: u8) { match byte { 8 | 0x7F => { while !line_sts().contains(LineSts::OUTPUT_EMPTY) {} - tdvmcall_io_write!(SERIAL_IO_PORT, 8, u8).unwrap(); + io_write!(SERIAL_IO_PORT, 8, u8).unwrap(); while !line_sts().contains(LineSts::OUTPUT_EMPTY) {} - tdvmcall_io_write!(SERIAL_IO_PORT, b' ', u8).unwrap(); + io_write!(SERIAL_IO_PORT, b' ', u8).unwrap(); while !line_sts().contains(LineSts::OUTPUT_EMPTY) {} - tdvmcall_io_write!(SERIAL_IO_PORT, 8, u8).unwrap(); + io_write!(SERIAL_IO_PORT, 8, u8).unwrap(); } _ => { while !line_sts().contains(LineSts::OUTPUT_EMPTY) {} - tdvmcall_io_write!(SERIAL_IO_PORT, byte, u8).unwrap(); + io_write!(SERIAL_IO_PORT, byte, u8).unwrap(); } } }