From 6d32033a41da0fcedce870720f30c2b24e0283f9 Mon Sep 17 00:00:00 2001 From: Mayank Kumar Date: Mon, 10 Mar 2025 22:59:46 +0530 Subject: [PATCH 01/23] feat: opentmk init feat: opentmk init feat: opentmk init feat: opentmk init feat: opentmk init feat: opentmk init feat: opentmk init feat: init 1 feat: init 2 feat: init 1 feat: opentmk feat: opentmk init 3 feat: opentmk init 4 feat: opentmk init 4 --- Cargo.lock | 75 +++ Cargo.toml | 2 + opentmk/Cargo.toml | 32 + opentmk/README.md | 3 + opentmk/build_deploy.sh | 3 + opentmk/src/arch/aarch64/hypercall.rs | 27 + opentmk/src/arch/aarch64/mod.rs | 2 + opentmk/src/arch/aarch64/serial.rs | 240 +++++++ opentmk/src/arch/mod.rs | 16 + opentmk/src/arch/x86_64/hypercall.rs | 53 ++ opentmk/src/arch/x86_64/interrupt.rs | 50 ++ .../arch/x86_64/interrupt_handler_register.rs | 606 ++++++++++++++++++ opentmk/src/arch/x86_64/mod.rs | 4 + opentmk/src/arch/x86_64/serial.rs | 125 ++++ opentmk/src/main.rs | 27 + opentmk/src/slog.rs | 242 +++++++ opentmk/src/sync.rs | 490 ++++++++++++++ opentmk/src/uefi/alloc.rs | 86 +++ opentmk/src/uefi/context.rs | 61 ++ opentmk/src/uefi/hypercall.rs | 604 +++++++++++++++++ opentmk/src/uefi/hypvctx.rs | 403 ++++++++++++ opentmk/src/uefi/init.rs | 57 ++ opentmk/src/uefi/mod.rs | 36 ++ opentmk/src/uefi/rt.rs | 23 + opentmk/src/uefi/tests/hv_misc.rs | 136 ++++ opentmk/src/uefi/tests/hv_processor.rs | 75 +++ opentmk/src/uefi/tests/mod.rs | 14 + .../src/tasks/guest_test/uefi/gpt_efi_disk.rs | 2 +- 28 files changed, 3493 insertions(+), 1 deletion(-) create mode 100644 opentmk/Cargo.toml create mode 100644 opentmk/README.md create mode 100755 opentmk/build_deploy.sh create mode 100644 opentmk/src/arch/aarch64/hypercall.rs create mode 100644 opentmk/src/arch/aarch64/mod.rs create mode 100644 opentmk/src/arch/aarch64/serial.rs create mode 100644 opentmk/src/arch/mod.rs create mode 100644 opentmk/src/arch/x86_64/hypercall.rs create mode 100644 opentmk/src/arch/x86_64/interrupt.rs create mode 100644 opentmk/src/arch/x86_64/interrupt_handler_register.rs create mode 100644 opentmk/src/arch/x86_64/mod.rs create mode 100644 opentmk/src/arch/x86_64/serial.rs create mode 100644 opentmk/src/main.rs create mode 100644 opentmk/src/slog.rs create mode 100644 opentmk/src/sync.rs create mode 100644 opentmk/src/uefi/alloc.rs create mode 100644 opentmk/src/uefi/context.rs create mode 100644 opentmk/src/uefi/hypercall.rs create mode 100644 opentmk/src/uefi/hypvctx.rs create mode 100644 opentmk/src/uefi/init.rs create mode 100644 opentmk/src/uefi/mod.rs create mode 100644 opentmk/src/uefi/rt.rs create mode 100644 opentmk/src/uefi/tests/hv_misc.rs create mode 100644 opentmk/src/uefi/tests/hv_processor.rs create mode 100644 opentmk/src/uefi/tests/mod.rs diff --git a/Cargo.lock b/Cargo.lock index f86c244203..302b3ed249 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3547,6 +3547,9 @@ name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +dependencies = [ + "spin 0.5.2", +] [[package]] name = "libc" @@ -3609,6 +3612,15 @@ dependencies = [ "escape8259", ] +[[package]] +name = "linked_list_allocator" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afa463f5405ee81cdb9cc2baf37e08ec7e4c8209442b5d72c04cfb2cd6e6286" +dependencies = [ + "spinning_top", +] + [[package]] name = "linkme" version = "0.3.31" @@ -4769,6 +4781,27 @@ dependencies = [ "thiserror 2.0.0", ] +[[package]] +name = "opentmk" +version = "0.0.0" +dependencies = [ + "arrayvec", + "bitfield-struct", + "cfg-if", + "hvdef", + "lazy_static", + "linked_list_allocator", + "memory_range", + "minimal_rt", + "minimal_rt_build", + "serde", + "serde_json", + "spin 0.10.0", + "uefi", + "x86_64", + "zerocopy 0.8.14", +] + [[package]] name = "openvmm" version = "0.0.0" @@ -6360,6 +6393,30 @@ dependencies = [ "zerocopy 0.8.14", ] +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "spin" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591" +dependencies = [ + "lock_api", +] + +[[package]] +name = "spinning_top" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b9eb1a2f4c41445a3a0ff9abc5221c5fcd28e1f13cd7c0397706f9ac938ddb0" +dependencies = [ + "lock_api", +] + [[package]] name = "stackfuture" version = "0.3.0" @@ -8736,6 +8793,12 @@ dependencies = [ "vmsocket", ] +[[package]] +name = "volatile" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "442887c63f2c839b346c192d047a7c87e73d0689c9157b00b53dcc27dd5ea793" + [[package]] name = "vpci" version = "0.0.0" @@ -9313,6 +9376,18 @@ dependencies = [ "tap", ] +[[package]] +name = "x86_64" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f042214de98141e9c8706e8192b73f56494087cc55ebec28ce10f26c5c364ae" +dependencies = [ + "bit_field", + "bitflags 2.6.0", + "rustversion", + "volatile", +] + [[package]] name = "x86defs" version = "0.0.0" diff --git a/Cargo.toml b/Cargo.toml index 1dd0ca13fb..fd2106e652 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,6 +42,7 @@ members = [ "vm/loader/igvmfilegen", "vm/vmgs/vmgs_lib", "vm/vmgs/vmgstool", + "opentmk" ] exclude = [ "xsync", @@ -525,6 +526,7 @@ xshell-macros = "0.2" # We add the derive feature here since the vast majority of our crates use it. #zerocopy = { version = "0.7.32", features = ["derive"]} zerocopy = { version = "0.8.14", features = ["derive"]} +linked_list_allocator = "0.10.5" [workspace.metadata.xtask.unused-deps] # Pulled in through "tracing", but we need to pin the version diff --git a/opentmk/Cargo.toml b/opentmk/Cargo.toml new file mode 100644 index 0000000000..752022b057 --- /dev/null +++ b/opentmk/Cargo.toml @@ -0,0 +1,32 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +[package] +name = "opentmk" +edition.workspace = true +rust-version.workspace = true + +[dependencies] +uefi = { workspace = true, features = ["alloc"] } +minimal_rt.workspace = true +linked_list_allocator = { workspace = true } +hvdef = {workspace = true} +zerocopy = {workspace = true} +memory_range = { workspace = true } +arrayvec = {workspace = true} +cfg-if.workspace = true +bitfield-struct.workspace = true +x86_64 = "0.15.2" +lazy_static = { version = "1.4.0", features = ["spin_no_std"] } +serde_json = { version = "1.0", default-features = false, features = ["alloc"] } +spin = "0.10.0" +serde = {version = "1.0", default-features = false} +[lints] +workspace = true + +[build-dependencies] +minimal_rt_build.workspace = true + +[profile.release] +debug = false +strip = "debuginfo" diff --git a/opentmk/README.md b/opentmk/README.md new file mode 100644 index 0000000000..999308fc0b --- /dev/null +++ b/opentmk/README.md @@ -0,0 +1,3 @@ +# `guest_test_uefi` + +See the guide for more info on how to build/run the code in this crate. diff --git a/opentmk/build_deploy.sh b/opentmk/build_deploy.sh new file mode 100755 index 0000000000..0c68e1643e --- /dev/null +++ b/opentmk/build_deploy.sh @@ -0,0 +1,3 @@ +RUST_BACKTRACE=1 cargo build -p opentmk --target x86_64-unknown-uefi +cargo xtask guest-test uefi --bootx64 ./target/x86_64-unknown-uefi/debug/opentmk.efi +qemu-img convert -f raw -O vhdx ./target/x86_64-unknown-uefi/debug/opentmk.img ~/projects/opentmk.vhdx \ No newline at end of file diff --git a/opentmk/src/arch/aarch64/hypercall.rs b/opentmk/src/arch/aarch64/hypercall.rs new file mode 100644 index 0000000000..35011e089a --- /dev/null +++ b/opentmk/src/arch/aarch64/hypercall.rs @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +/// Writes a synthehtic register to tell the hypervisor the OS ID for the boot shim. +fn report_os_id(guest_os_id: u64) { + // On ARM64, to be able to make hypercalls, one needs first to set the Guest OS ID + // synthetic register using a hypercall. Can't use `Hvcall::set_register` at that will + // lead to the infinite recursion as that function will first try initializing hypercalls + // with setting a register. + // + // Only one very specific HvSetVpRegisters hypercall is allowed to set the Guest OS ID + // (this is TLFS section 17.4.4.1.1 and 5.3), and that must be the fast hypercall. + let _ = minimal_rt::arch::hypercall::set_register_fast( + hvdef::HvArm64RegisterName::GuestOsId.into(), + guest_os_id.into(), + ); +} + +pub(crate) fn initialize(guest_os_id: u64) { + // We are assuming we are running under a Microsoft hypervisor. + report_os_id(guest_os_id); +} + +/// Call before jumping to kernel. +pub(crate) fn uninitialize() { + report_os_id(0); +} \ No newline at end of file diff --git a/opentmk/src/arch/aarch64/mod.rs b/opentmk/src/arch/aarch64/mod.rs new file mode 100644 index 0000000000..594be8b42a --- /dev/null +++ b/opentmk/src/arch/aarch64/mod.rs @@ -0,0 +1,2 @@ +pub mod hypercall; +pub mod serial; \ No newline at end of file diff --git a/opentmk/src/arch/aarch64/serial.rs b/opentmk/src/arch/aarch64/serial.rs new file mode 100644 index 0000000000..f68e6cb200 --- /dev/null +++ b/opentmk/src/arch/aarch64/serial.rs @@ -0,0 +1,240 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +//! aarch64 MMIO-based serial port, UART PL011. +//! +//! Used for debug output. Follows +//! [PrimeCell UART (PL011) Technical Reference Manual](https://developer.arm.com/documentation/ddi0183/g/) +//! +//! PL011 Registers: +//! +//! Offset Name Type Reset Bits Description +//! ---------------------------------------------------------------------- +//! 0x000 UARTDR RW 0x--- 12/8 Data Register +//! 0x004 UARTRSR/UARTECR RW 0x0 4/0 Receive Status Register/Error Clear Register +//! 0x018 UARTFR RO 0b-10010--- 9 Flag Register +//! 0x020 UARTILPR RW 0x00 8 IrDA Low-Power Counter Register +//! 0x024 UARTIBRD RW 0x0000 16 Integer Baud Rate Register +//! 0x028 UARTFBRD RW 0x00 6 Fractional Baud Rate Register +//! 0x02C UARTLCR_H RW 0x00 8 Line Control Register +//! 0x030 UARTCR RW 0x0300 16 Control Register +//! 0x034 UARTIFLS RW 0x12 6 Interrupt FIFO Level Select Register +//! 0x038 UARTIMSC RW 0x000 11 Interrupt Mask Set/Clear Register +//! 0x03C UARTRIS RO 0x00- 11 Raw Interrupt Status Register +//! 0x040 UARTMIS RO 0x00- 11 Masked Interrupt Status Register +//! 0x044 UARTICR WO - 11 Interrupt Clear Register +//! 0x048 UARTDMACR RW 0x00 3 DMA Control Register +//! 0xFE0 UARTPeriphID0 RO 0x11 8 UARTPeriphID0 Register +//! 0xFE4 UARTPeriphID1 RO 0x10 8 UARTPeriphID1 Register +//! 0xFE8 UARTPeriphID2 RO 0x_4a 8 UARTPeriphID2 Register +//! 0xFEC UARTPeriphID3 RO 0x00 8 UARTPeriphID3 Register +//! 0xFF0 UARTPCellID0 RO 0x0D 8 UARTPCellID0 Register +//! 0xFF4 UARTPCellID1 RO 0xF0 8 UARTPCellID1 Register +//! 0xFF8 UARTPCellID2 RO 0x05 8 UARTPCellID2 Register +//! 0xFFC UARTPCellID3 RO 0xB1 8 UARTPCellID3 Register + +#![allow(dead_code)] + +use core::hint::spin_loop; +use core::sync::atomic::AtomicBool; +use core::sync::atomic::Ordering; + +#[derive(Debug, Clone, Copy)] +#[repr(u16)] +enum Pl011Register { + /// Data Register + Dr = 0x000, + /// Receive Status Register/Error Clear Register + RsrOrEcr = 0x004, + /// Flag register + Fr = 0x018, + /// Integer Baud Rate Register + Ibrd = 0x024, + /// Fractional Baud Rate Register + Fbrd = 0x028, + /// Line Control Register + LcrHigh = 0x02c, + /// Control Register + Cr = 0x030, + /// Masked Interrupt Status Register + Imsc = 0x038, + /// Interrupt Clear Register + Icr = 0x044, + /// DMA Control Register + DmaCr = 0x048, + /// UARTPeriphID0 Register + PeriphID0 = 0xFE0, + /// UARTPeriphID1 Register + PeriphID1 = 0xFE4, + /// UARTPeriphID2 Register + PeriphID2 = 0xFE8, + /// UARTPeriphID3 Register + PeriphID3 = 0xFEC, + /// UARTPCellID0 Register + PCellID0 = 0xFF0, + /// UARTPCellID1 Register + PCellID1 = 0xFF4, + /// UARTPCellID2 Register + PCellID2 = 0xFF8, + /// UARTPCellID3 Register + PCellID3 = 0xFFC, +} + +const CR_RX_ENABLE: u32 = 0x200; +const CR_TX_ENABLE: u32 = 0x100; +const CR_UART_ENABLE: u32 = 1; +const LCR_H_FIFO_EN: u32 = 0x10; +const LCR_H_8BITS: u32 = 0x60; + +const _FR_TX_EMPTY: u32 = 0x080; +const _FR_RX_FULL: u32 = 0x040; +const FR_TX_FULL: u32 = 0x020; +const _FR_RX_EMPTY: u32 = 0x010; +const FR_BUSY: u32 = 0x008; + +/// The Hyper-V PL011 host emulated PL011's are found at these +/// base addresses. Should come from ACPI or DT of course yet +/// due to having been hardcoded in some products makes that +/// virtually constants. +const PL011_HYPER_V_BASE_1: u64 = 0xeffec000; +const _PL011_HYPER_V_BASE_2: u64 = 0xeffeb000; +const PL011_BASE: u64 = PL011_HYPER_V_BASE_1; + +fn read_register(reg: Pl011Register) -> u32 { + // SAFETY: using the PL011 MMIO address. + unsafe { core::ptr::read_volatile((PL011_BASE + reg as u64) as *const u32) } +} + +fn write_register(reg: Pl011Register, val: u32) { + // SAFETY: using the PL011 MMIO address. + unsafe { + core::ptr::write_volatile((PL011_BASE + reg as u64) as *mut u32, val); + } +} + +fn cell_id() -> u32 { + // This can easily be rewritten employing + // bare arithmetic yet the compiler does a very good job + // so using the domain abstractions. + [ + Pl011Register::PCellID3, + Pl011Register::PCellID2, + Pl011Register::PCellID1, + Pl011Register::PCellID0, + ] + .iter() + .fold(0, |id_running, &r| { + id_running.wrapping_shl(8) | (read_register(r) as u8 as u32) + }) +} + +fn periph_id() -> u32 { + // This can easily be rewritten employing + // bare arithmetic yet the compiler does a very good job + // so using the domain abstractions. + [ + Pl011Register::PeriphID3, + Pl011Register::PeriphID2, + Pl011Register::PeriphID1, + Pl011Register::PeriphID0, + ] + .iter() + .fold(0, |id_running, &r| { + id_running.wrapping_shl(8) | (read_register(r) as u8 as u32) + }) +} + +fn poll_tx_not_full() { + while read_register(Pl011Register::Fr) & FR_TX_FULL != 0 { + spin_loop(); + } +} + +fn poll_not_busy() { + while read_register(Pl011Register::Fr) & FR_BUSY != 0 { + spin_loop(); + } +} + +/// Disables the functional parts of the UART, drains FIFOs, +/// sets baud rate and enables the UART in the polling mode. +/// Might be geared towards the real hardware more than the virtual one. +/// Works with qemu and Hyper-V. +fn reset_and_init() { + // Mask interrupts (lower 11 bits) + write_register(Pl011Register::Imsc, 0x7ff); + // Clear interrupts (lower 11 bits) + write_register(Pl011Register::Icr, 0x7ff); + // Disable DMA on Rx and Tx + write_register(Pl011Register::DmaCr, 0x0); + + // Leave Rx and Tx enabled to drain FIFOs. + write_register(Pl011Register::Cr, CR_RX_ENABLE | CR_TX_ENABLE); + read_register(Pl011Register::Cr); // wait + read_register(Pl011Register::Cr); // wait + poll_not_busy(); + + // Disable Rx, Tx, and UART. + write_register(Pl011Register::Cr, 0x00000000); + + // Set integer and fractional parts of the baud rate, + // hardcoded for now + write_register(Pl011Register::Fbrd, 0x00000004); + write_register(Pl011Register::Ibrd, 0x00000027); + // The UARTLCR_H, UARTIBRD, and UARTFBRD registers form the single 30-bit + // wide UARTLCR Register that is updated on a single write strobe generated by a + // UARTLCR_H write + write_register(Pl011Register::LcrHigh, LCR_H_FIFO_EN | LCR_H_8BITS); + + // Clear the errors + write_register(Pl011Register::RsrOrEcr, 0); + + // Enable Tx and Rx + write_register(Pl011Register::Cr, CR_RX_ENABLE | CR_TX_ENABLE); + read_register(Pl011Register::Cr); // wait + read_register(Pl011Register::Cr); // wait + poll_not_busy(); + + // Enable UART + write_register( + Pl011Register::Cr, + CR_RX_ENABLE | CR_TX_ENABLE | CR_UART_ENABLE, + ); + poll_not_busy(); +} + +/// A PL011 serial port. +pub struct Serial; + +static SUPPORTED: AtomicBool = AtomicBool::new(false); + +impl Serial { + /// Initializes the serial port. + pub fn init() -> Serial { + const SUPPORTED_PL011_CELLS: &[u32] = &[0xB105_F00D]; + + let cell_id = cell_id(); + let supported = SUPPORTED_PL011_CELLS.contains(&cell_id); + if supported { + reset_and_init(); + } + SUPPORTED.store(supported, Ordering::Relaxed); + + Self + } +} + +impl core::fmt::Write for Serial { + fn write_str(&mut self, s: &str) -> core::fmt::Result { + if !SUPPORTED.load(Ordering::Relaxed) { + return Ok(()); + } + + for byte in s.bytes() { + poll_tx_not_full(); + write_register(Pl011Register::Dr, byte.into()); + } + + Ok(()) + } +} diff --git a/opentmk/src/arch/mod.rs b/opentmk/src/arch/mod.rs new file mode 100644 index 0000000000..4bcf7781ee --- /dev/null +++ b/opentmk/src/arch/mod.rs @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +//! Imports and re-exports architecture-specific implementations. + +mod x86_64; + +cfg_if::cfg_if!( + if #[cfg(target_arch = "x86_64")] { + pub use x86_64::*; + } else if #[cfg(target_arch = "aarch64")] { + pub use aarch64::*; + } else { + compile_error!("target_arch is not supported"); + } +); \ No newline at end of file diff --git a/opentmk/src/arch/x86_64/hypercall.rs b/opentmk/src/arch/x86_64/hypercall.rs new file mode 100644 index 0000000000..1337cbe38e --- /dev/null +++ b/opentmk/src/arch/x86_64/hypercall.rs @@ -0,0 +1,53 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use core::ptr::addr_of; +use hvdef::HV_PAGE_SIZE; +use minimal_rt::arch::hypercall::HYPERCALL_PAGE; +use minimal_rt::arch::msr::read_msr; +use minimal_rt::arch::msr::write_msr; +#[expect(unsafe_code)] +/// Writes an MSR to tell the hypervisor the OS ID for the boot shim. +fn report_os_id(guest_os_id: u64) { + // SAFETY: Using the contract established in the Hyper-V TLFS. + unsafe { + write_msr(hvdef::HV_X64_MSR_GUEST_OS_ID, guest_os_id); + }; +} + +#[expect(unsafe_code)] +/// Writes an MSR to tell the hypervisor where the hypercall page is +pub fn write_hypercall_msr(enable: bool) { + // SAFETY: Using the contract established in the Hyper-V TLFS. + let hypercall_contents = hvdef::hypercall::MsrHypercallContents::from(unsafe { + read_msr(hvdef::HV_X64_MSR_HYPERCALL) + }); + + let hypercall_page_num = addr_of!(HYPERCALL_PAGE) as u64 / HV_PAGE_SIZE; + + if!(!enable || !hypercall_contents.enable()) { + return; + } + let new_hv_contents: hvdef::hypercall::MsrHypercallContents = hypercall_contents.with_enable(enable).with_gpn(if enable { + hypercall_page_num + } else { + 0 + }); + + // SAFETY: Using the contract established in the Hyper-V TLFS. + unsafe { write_msr(hvdef::HV_X64_MSR_HYPERCALL, new_hv_contents.into()) }; +} + +/// Has to be called before using hypercalls. +pub(crate) fn initialize(guest_os_id: u64) { + // We are assuming we are running under a Microsoft hypervisor, so there is + // no need to check any cpuid leaves. + report_os_id(guest_os_id); + write_hypercall_msr(true); +} + +/// Call before jumping to kernel. +pub(crate) fn uninitialize() { + write_hypercall_msr(false); + report_os_id(0); +} \ No newline at end of file diff --git a/opentmk/src/arch/x86_64/interrupt.rs b/opentmk/src/arch/x86_64/interrupt.rs new file mode 100644 index 0000000000..d9a6ba7993 --- /dev/null +++ b/opentmk/src/arch/x86_64/interrupt.rs @@ -0,0 +1,50 @@ + +use alloc::boxed::Box; +use alloc::sync::Arc; +use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame}; +use lazy_static::lazy_static; +use core::cell::{Ref, RefCell}; +use core::concat_idents; +use crate::sync::Mutex; + +use crate::{criticallog, infolog}; + +use super::interrupt_handler_register::{register_interrupt_handler, set_common_handler}; + +lazy_static! { + static ref IDT: InterruptDescriptorTable = { + let mut idt = InterruptDescriptorTable::new(); + register_interrupt_handler(&mut idt); + idt.double_fault.set_handler_fn(handler_double_fault); + idt + }; +} + +static mut HANDLERS : [fn(); 256] = [no_op; 256]; +static MUTEX: Mutex<()> = Mutex::new(()); +fn no_op() {} + +fn common_handler(stack_frame: InterruptStackFrame, interrupt: u8) { + unsafe { HANDLERS[interrupt as usize](); } +} + +pub fn set_handler(interrupt: u8, handler: fn()) { + let _lock = MUTEX.lock(); + unsafe { HANDLERS[interrupt as usize] = handler; } +} + + +extern "x86-interrupt" fn handler_double_fault( + stack_frame: InterruptStackFrame, + _error_code: u64, +) -> ! { + criticallog!("EXCEPTION:\n\tERROR_CODE: {}\n\tDOUBLE FAULT\n{:#?}", _error_code, stack_frame); + loop {} +} + +// Initialize the IDT +pub fn init() { + unsafe { IDT.load() }; + set_common_handler(common_handler); + unsafe { x86_64::instructions::interrupts::enable() }; +} \ No newline at end of file diff --git a/opentmk/src/arch/x86_64/interrupt_handler_register.rs b/opentmk/src/arch/x86_64/interrupt_handler_register.rs new file mode 100644 index 0000000000..c015edb666 --- /dev/null +++ b/opentmk/src/arch/x86_64/interrupt_handler_register.rs @@ -0,0 +1,606 @@ +use core::arch::asm; +use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame}; + +use crate::{infolog, sync::Mutex}; + +static mut COMMON_HANDLER: fn(InterruptStackFrame, u8) = common_handler; +static COMMON_HANDLER_MUTEX: Mutex<()> = Mutex::new(()); + +macro_rules! create_fn { + ($name:ident, $i: expr) => { + extern "x86-interrupt" fn $name(stack_frame: InterruptStackFrame) { + unsafe { + asm!(r#" + push rax + push rbx + push rcx + push rdx + push rsi + push rdi + push rbp + push rsp + push r8 + push r9 + push r10 + push r11 + push r12 + push r13 + push r14 + push r15 + + sub rsp, 256 + movups [rsp + 16 * 0], xmm0 + movups [rsp + 16 * 1], xmm1 + movups [rsp + 16 * 2], xmm2 + movups [rsp + 16 * 3], xmm3 + movups [rsp + 16 * 4], xmm4 + movups [rsp + 16 * 5], xmm5 + movups [rsp + 16 * 6], xmm6 + movups [rsp + 16 * 7], xmm7 + movups [rsp + 16 * 8], xmm8 + movups [rsp + 16 * 9], xmm9 + movups [rsp + 16 * 10], xmm10 + movups [rsp + 16 * 11], xmm11 + movups [rsp + 16 * 12], xmm12 + movups [rsp + 16 * 13], xmm13 + movups [rsp + 16 * 14], xmm14 + movups [rsp + 16 * 15], xmm15 +"#); + +unsafe { (COMMON_HANDLER)(stack_frame, $i) }; + +asm!(r#" + + + movups xmm0, [rsp + 16 * 0] + movups xmm1, [rsp + 16 * 1] + movups xmm2, [rsp + 16 * 2] + movups xmm3, [rsp + 16 * 3] + movups xmm4, [rsp + 16 * 4] + movups xmm5, [rsp + 16 * 5] + movups xmm6, [rsp + 16 * 6] + movups xmm7, [rsp + 16 * 7] + movups xmm8, [rsp + 16 * 8] + movups xmm9, [rsp + 16 * 9] + movups xmm10, [rsp + 16 * 10] + movups xmm11, [rsp + 16 * 11] + movups xmm12, [rsp + 16 * 12] + movups xmm13, [rsp + 16 * 13] + movups xmm14, [rsp + 16 * 14] + movups xmm15, [rsp + 16 * 15] + add rsp, 16 * 16 + + pop r15 + pop r14 + pop r13 + pop r12 + pop r11 + pop r10 + pop r9 + pop r8 + pop rsp + pop rbp + pop rdi + pop rsi + pop rdx + pop rcx + pop rbx + pop rax + + "#); + } + } + }; +} + +macro_rules! register_interrupt_handler { + ($idt: expr, $i: expr, $name: ident) => { + $idt[$i].set_handler_fn($name); + }; +} + +fn common_handler(stack_frame: InterruptStackFrame, interrupt: u8) { + infolog!("Default interrupt handler fired: {}", interrupt); +} + +pub fn set_common_handler(handler: fn(InterruptStackFrame, u8)) { + let guard = COMMON_HANDLER_MUTEX.lock(); + unsafe { + COMMON_HANDLER = handler; + } +} + +extern "x86-interrupt" fn no_op(stack_frame: InterruptStackFrame) {} + +pub fn register_interrupt_handler(idt: &mut InterruptDescriptorTable) { + register_interrupt_handler!(idt, 0, handler_0); + register_interrupt_handler!(idt, 1, handler_1); + register_interrupt_handler!(idt, 2, handler_2); + register_interrupt_handler!(idt, 3, handler_3); + register_interrupt_handler!(idt, 4, handler_4); + register_interrupt_handler!(idt, 5, handler_5); + register_interrupt_handler!(idt, 6, handler_6); + register_interrupt_handler!(idt, 7, handler_7); + + register_interrupt_handler!(idt, 32, handler_32); + register_interrupt_handler!(idt, 33, handler_33); + register_interrupt_handler!(idt, 34, handler_34); + register_interrupt_handler!(idt, 35, handler_35); + register_interrupt_handler!(idt, 36, handler_36); + register_interrupt_handler!(idt, 37, handler_37); + register_interrupt_handler!(idt, 38, handler_38); + register_interrupt_handler!(idt, 39, handler_39); + register_interrupt_handler!(idt, 40, handler_40); + register_interrupt_handler!(idt, 41, handler_41); + register_interrupt_handler!(idt, 42, handler_42); + register_interrupt_handler!(idt, 43, handler_43); + register_interrupt_handler!(idt, 44, handler_44); + register_interrupt_handler!(idt, 45, handler_45); + register_interrupt_handler!(idt, 46, handler_46); + register_interrupt_handler!(idt, 47, handler_47); + register_interrupt_handler!(idt, 48, handler_48); + register_interrupt_handler!(idt, 49, handler_49); + register_interrupt_handler!(idt, 50, handler_50); + register_interrupt_handler!(idt, 51, handler_51); + register_interrupt_handler!(idt, 52, handler_52); + register_interrupt_handler!(idt, 53, handler_53); + register_interrupt_handler!(idt, 54, handler_54); + register_interrupt_handler!(idt, 55, handler_55); + register_interrupt_handler!(idt, 56, handler_56); + register_interrupt_handler!(idt, 57, handler_57); + register_interrupt_handler!(idt, 58, handler_58); + register_interrupt_handler!(idt, 59, handler_59); + register_interrupt_handler!(idt, 60, handler_60); + register_interrupt_handler!(idt, 61, handler_61); + register_interrupt_handler!(idt, 62, handler_62); + register_interrupt_handler!(idt, 63, handler_63); + register_interrupt_handler!(idt, 64, handler_64); + register_interrupt_handler!(idt, 65, handler_65); + register_interrupt_handler!(idt, 66, handler_66); + register_interrupt_handler!(idt, 67, handler_67); + register_interrupt_handler!(idt, 68, handler_68); + register_interrupt_handler!(idt, 69, handler_69); + register_interrupt_handler!(idt, 70, handler_70); + register_interrupt_handler!(idt, 71, handler_71); + register_interrupt_handler!(idt, 72, handler_72); + register_interrupt_handler!(idt, 73, handler_73); + register_interrupt_handler!(idt, 74, handler_74); + register_interrupt_handler!(idt, 75, handler_75); + register_interrupt_handler!(idt, 76, handler_76); + register_interrupt_handler!(idt, 77, handler_77); + register_interrupt_handler!(idt, 78, handler_78); + register_interrupt_handler!(idt, 79, handler_79); + register_interrupt_handler!(idt, 80, handler_80); + register_interrupt_handler!(idt, 81, handler_81); + register_interrupt_handler!(idt, 82, handler_82); + register_interrupt_handler!(idt, 83, handler_83); + register_interrupt_handler!(idt, 84, handler_84); + register_interrupt_handler!(idt, 85, handler_85); + register_interrupt_handler!(idt, 86, handler_86); + register_interrupt_handler!(idt, 87, handler_87); + register_interrupt_handler!(idt, 88, handler_88); + register_interrupt_handler!(idt, 89, handler_89); + register_interrupt_handler!(idt, 90, handler_90); + register_interrupt_handler!(idt, 91, handler_91); + register_interrupt_handler!(idt, 92, handler_92); + register_interrupt_handler!(idt, 93, handler_93); + register_interrupt_handler!(idt, 94, handler_94); + register_interrupt_handler!(idt, 95, handler_95); + register_interrupt_handler!(idt, 96, handler_96); + register_interrupt_handler!(idt, 97, handler_97); + register_interrupt_handler!(idt, 98, handler_98); + register_interrupt_handler!(idt, 99, handler_99); + register_interrupt_handler!(idt, 100, handler_100); + register_interrupt_handler!(idt, 101, handler_101); + register_interrupt_handler!(idt, 102, handler_102); + register_interrupt_handler!(idt, 103, handler_103); + register_interrupt_handler!(idt, 104, handler_104); + register_interrupt_handler!(idt, 105, handler_105); + register_interrupt_handler!(idt, 106, handler_106); + register_interrupt_handler!(idt, 107, handler_107); + register_interrupt_handler!(idt, 108, handler_108); + register_interrupt_handler!(idt, 109, handler_109); + register_interrupt_handler!(idt, 110, handler_110); + register_interrupt_handler!(idt, 111, handler_111); + register_interrupt_handler!(idt, 112, handler_112); + register_interrupt_handler!(idt, 113, handler_113); + register_interrupt_handler!(idt, 114, handler_114); + register_interrupt_handler!(idt, 115, handler_115); + register_interrupt_handler!(idt, 116, handler_116); + register_interrupt_handler!(idt, 117, handler_117); + register_interrupt_handler!(idt, 118, handler_118); + register_interrupt_handler!(idt, 119, handler_119); + register_interrupt_handler!(idt, 120, handler_120); + register_interrupt_handler!(idt, 121, handler_121); + register_interrupt_handler!(idt, 122, handler_122); + register_interrupt_handler!(idt, 123, handler_123); + register_interrupt_handler!(idt, 124, handler_124); + register_interrupt_handler!(idt, 125, handler_125); + register_interrupt_handler!(idt, 126, handler_126); + register_interrupt_handler!(idt, 127, handler_127); + register_interrupt_handler!(idt, 128, handler_128); + register_interrupt_handler!(idt, 129, handler_129); + register_interrupt_handler!(idt, 130, handler_130); + register_interrupt_handler!(idt, 131, handler_131); + register_interrupt_handler!(idt, 132, handler_132); + register_interrupt_handler!(idt, 133, handler_133); + register_interrupt_handler!(idt, 134, handler_134); + register_interrupt_handler!(idt, 135, handler_135); + register_interrupt_handler!(idt, 136, handler_136); + register_interrupt_handler!(idt, 137, handler_137); + register_interrupt_handler!(idt, 138, handler_138); + register_interrupt_handler!(idt, 139, handler_139); + register_interrupt_handler!(idt, 140, handler_140); + register_interrupt_handler!(idt, 141, handler_141); + register_interrupt_handler!(idt, 142, handler_142); + register_interrupt_handler!(idt, 143, handler_143); + register_interrupt_handler!(idt, 144, handler_144); + register_interrupt_handler!(idt, 145, handler_145); + register_interrupt_handler!(idt, 146, handler_146); + register_interrupt_handler!(idt, 147, handler_147); + register_interrupt_handler!(idt, 148, handler_148); + register_interrupt_handler!(idt, 149, handler_149); + register_interrupt_handler!(idt, 150, handler_150); + register_interrupt_handler!(idt, 151, handler_151); + register_interrupt_handler!(idt, 152, handler_152); + register_interrupt_handler!(idt, 153, handler_153); + register_interrupt_handler!(idt, 154, handler_154); + register_interrupt_handler!(idt, 155, handler_155); + register_interrupt_handler!(idt, 156, handler_156); + register_interrupt_handler!(idt, 157, handler_157); + register_interrupt_handler!(idt, 158, handler_158); + register_interrupt_handler!(idt, 159, handler_159); + register_interrupt_handler!(idt, 160, handler_160); + register_interrupt_handler!(idt, 161, handler_161); + register_interrupt_handler!(idt, 162, handler_162); + register_interrupt_handler!(idt, 163, handler_163); + register_interrupt_handler!(idt, 164, handler_164); + register_interrupt_handler!(idt, 165, handler_165); + register_interrupt_handler!(idt, 166, handler_166); + register_interrupt_handler!(idt, 167, handler_167); + register_interrupt_handler!(idt, 168, handler_168); + register_interrupt_handler!(idt, 169, handler_169); + register_interrupt_handler!(idt, 170, handler_170); + register_interrupt_handler!(idt, 171, handler_171); + register_interrupt_handler!(idt, 172, handler_172); + register_interrupt_handler!(idt, 173, handler_173); + register_interrupt_handler!(idt, 174, handler_174); + register_interrupt_handler!(idt, 175, handler_175); + register_interrupt_handler!(idt, 176, handler_176); + register_interrupt_handler!(idt, 177, handler_177); + register_interrupt_handler!(idt, 178, handler_178); + register_interrupt_handler!(idt, 179, handler_179); + register_interrupt_handler!(idt, 180, handler_180); + register_interrupt_handler!(idt, 181, handler_181); + register_interrupt_handler!(idt, 182, handler_182); + register_interrupt_handler!(idt, 183, handler_183); + register_interrupt_handler!(idt, 184, handler_184); + register_interrupt_handler!(idt, 185, handler_185); + register_interrupt_handler!(idt, 186, handler_186); + register_interrupt_handler!(idt, 187, handler_187); + register_interrupt_handler!(idt, 188, handler_188); + register_interrupt_handler!(idt, 189, handler_189); + register_interrupt_handler!(idt, 190, handler_190); + register_interrupt_handler!(idt, 191, handler_191); + register_interrupt_handler!(idt, 192, handler_192); + register_interrupt_handler!(idt, 193, handler_193); + register_interrupt_handler!(idt, 194, handler_194); + register_interrupt_handler!(idt, 195, handler_195); + register_interrupt_handler!(idt, 196, handler_196); + register_interrupt_handler!(idt, 197, handler_197); + register_interrupt_handler!(idt, 198, handler_198); + register_interrupt_handler!(idt, 199, handler_199); + register_interrupt_handler!(idt, 200, handler_200); + register_interrupt_handler!(idt, 201, handler_201); + register_interrupt_handler!(idt, 202, handler_202); + register_interrupt_handler!(idt, 203, handler_203); + register_interrupt_handler!(idt, 204, handler_204); + register_interrupt_handler!(idt, 205, handler_205); + register_interrupt_handler!(idt, 206, handler_206); + register_interrupt_handler!(idt, 207, handler_207); + register_interrupt_handler!(idt, 208, handler_208); + register_interrupt_handler!(idt, 209, handler_209); + register_interrupt_handler!(idt, 210, handler_210); + register_interrupt_handler!(idt, 211, handler_211); + register_interrupt_handler!(idt, 212, handler_212); + register_interrupt_handler!(idt, 213, handler_213); + register_interrupt_handler!(idt, 214, handler_214); + register_interrupt_handler!(idt, 215, handler_215); + register_interrupt_handler!(idt, 216, handler_216); + register_interrupt_handler!(idt, 217, handler_217); + register_interrupt_handler!(idt, 218, handler_218); + register_interrupt_handler!(idt, 219, handler_219); + register_interrupt_handler!(idt, 220, handler_220); + register_interrupt_handler!(idt, 221, handler_221); + register_interrupt_handler!(idt, 222, handler_222); + register_interrupt_handler!(idt, 223, handler_223); + register_interrupt_handler!(idt, 224, handler_224); + register_interrupt_handler!(idt, 225, handler_225); + register_interrupt_handler!(idt, 226, handler_226); + register_interrupt_handler!(idt, 227, handler_227); + register_interrupt_handler!(idt, 228, handler_228); + register_interrupt_handler!(idt, 229, handler_229); + register_interrupt_handler!(idt, 230, handler_230); + register_interrupt_handler!(idt, 231, handler_231); + register_interrupt_handler!(idt, 232, handler_232); + register_interrupt_handler!(idt, 233, handler_233); + register_interrupt_handler!(idt, 234, handler_234); + register_interrupt_handler!(idt, 235, handler_235); + register_interrupt_handler!(idt, 236, handler_236); + register_interrupt_handler!(idt, 237, handler_237); + register_interrupt_handler!(idt, 238, handler_238); + register_interrupt_handler!(idt, 239, handler_239); + register_interrupt_handler!(idt, 240, handler_240); + register_interrupt_handler!(idt, 241, handler_241); + register_interrupt_handler!(idt, 242, handler_242); + register_interrupt_handler!(idt, 243, handler_243); + register_interrupt_handler!(idt, 244, handler_244); + register_interrupt_handler!(idt, 245, handler_245); + register_interrupt_handler!(idt, 246, handler_246); + register_interrupt_handler!(idt, 247, handler_247); + register_interrupt_handler!(idt, 248, handler_248); + register_interrupt_handler!(idt, 249, handler_249); + register_interrupt_handler!(idt, 250, handler_250); + register_interrupt_handler!(idt, 251, handler_251); + register_interrupt_handler!(idt, 252, handler_252); + register_interrupt_handler!(idt, 253, handler_253); + register_interrupt_handler!(idt, 254, handler_254); + register_interrupt_handler!(idt, 255, handler_255); +} + +create_fn!(handler_0, 0); +create_fn!(handler_1, 1); +create_fn!(handler_2, 2); +create_fn!(handler_3, 3); +create_fn!(handler_4, 4); +create_fn!(handler_5, 5); +create_fn!(handler_6, 6); +create_fn!(handler_7, 7); +create_fn!(handler_8, 8); +create_fn!(handler_9, 9); +create_fn!(handler_10, 10); +create_fn!(handler_11, 11); +create_fn!(handler_12, 12); +create_fn!(handler_13, 13); +create_fn!(handler_14, 14); +create_fn!(handler_15, 15); +create_fn!(handler_16, 16); +create_fn!(handler_17, 17); +create_fn!(handler_18, 18); +create_fn!(handler_19, 19); +create_fn!(handler_20, 20); +create_fn!(handler_21, 21); +create_fn!(handler_22, 22); +create_fn!(handler_23, 23); +create_fn!(handler_24, 24); +create_fn!(handler_25, 25); +create_fn!(handler_26, 26); +create_fn!(handler_27, 27); +create_fn!(handler_28, 28); +create_fn!(handler_29, 29); +create_fn!(handler_30, 30); +create_fn!(handler_31, 31); +create_fn!(handler_32, 32); +create_fn!(handler_33, 33); +create_fn!(handler_34, 34); +create_fn!(handler_35, 35); +create_fn!(handler_36, 36); +create_fn!(handler_37, 37); +create_fn!(handler_38, 38); +create_fn!(handler_39, 39); +create_fn!(handler_40, 40); +create_fn!(handler_41, 41); +create_fn!(handler_42, 42); +create_fn!(handler_43, 43); +create_fn!(handler_44, 44); +create_fn!(handler_45, 45); +create_fn!(handler_46, 46); +create_fn!(handler_47, 47); +create_fn!(handler_48, 48); +create_fn!(handler_49, 49); +create_fn!(handler_50, 50); +create_fn!(handler_51, 51); +create_fn!(handler_52, 52); +create_fn!(handler_53, 53); +create_fn!(handler_54, 54); +create_fn!(handler_55, 55); +create_fn!(handler_56, 56); +create_fn!(handler_57, 57); +create_fn!(handler_58, 58); +create_fn!(handler_59, 59); +create_fn!(handler_60, 60); +create_fn!(handler_61, 61); +create_fn!(handler_62, 62); +create_fn!(handler_63, 63); +create_fn!(handler_64, 64); +create_fn!(handler_65, 65); +create_fn!(handler_66, 66); +create_fn!(handler_67, 67); +create_fn!(handler_68, 68); +create_fn!(handler_69, 69); +create_fn!(handler_70, 70); +create_fn!(handler_71, 71); +create_fn!(handler_72, 72); +create_fn!(handler_73, 73); +create_fn!(handler_74, 74); +create_fn!(handler_75, 75); +create_fn!(handler_76, 76); +create_fn!(handler_77, 77); +create_fn!(handler_78, 78); +create_fn!(handler_79, 79); +create_fn!(handler_80, 80); +create_fn!(handler_81, 81); +create_fn!(handler_82, 82); +create_fn!(handler_83, 83); +create_fn!(handler_84, 84); +create_fn!(handler_85, 85); +create_fn!(handler_86, 86); +create_fn!(handler_87, 87); +create_fn!(handler_88, 88); +create_fn!(handler_89, 89); +create_fn!(handler_90, 90); +create_fn!(handler_91, 91); +create_fn!(handler_92, 92); +create_fn!(handler_93, 93); +create_fn!(handler_94, 94); +create_fn!(handler_95, 95); +create_fn!(handler_96, 96); +create_fn!(handler_97, 97); +create_fn!(handler_98, 98); +create_fn!(handler_99, 99); +create_fn!(handler_100, 100); +create_fn!(handler_101, 101); +create_fn!(handler_102, 102); +create_fn!(handler_103, 103); +create_fn!(handler_104, 104); +create_fn!(handler_105, 105); +create_fn!(handler_106, 106); +create_fn!(handler_107, 107); +create_fn!(handler_108, 108); +create_fn!(handler_109, 109); +create_fn!(handler_110, 110); +create_fn!(handler_111, 111); +create_fn!(handler_112, 112); +create_fn!(handler_113, 113); +create_fn!(handler_114, 114); +create_fn!(handler_115, 115); +create_fn!(handler_116, 116); +create_fn!(handler_117, 117); +create_fn!(handler_118, 118); +create_fn!(handler_119, 119); +create_fn!(handler_120, 120); +create_fn!(handler_121, 121); +create_fn!(handler_122, 122); +create_fn!(handler_123, 123); +create_fn!(handler_124, 124); +create_fn!(handler_125, 125); +create_fn!(handler_126, 126); +create_fn!(handler_127, 127); +create_fn!(handler_128, 128); +create_fn!(handler_129, 129); +create_fn!(handler_130, 130); +create_fn!(handler_131, 131); +create_fn!(handler_132, 132); +create_fn!(handler_133, 133); +create_fn!(handler_134, 134); +create_fn!(handler_135, 135); +create_fn!(handler_136, 136); +create_fn!(handler_137, 137); +create_fn!(handler_138, 138); +create_fn!(handler_139, 139); +create_fn!(handler_140, 140); +create_fn!(handler_141, 141); +create_fn!(handler_142, 142); +create_fn!(handler_143, 143); +create_fn!(handler_144, 144); +create_fn!(handler_145, 145); +create_fn!(handler_146, 146); +create_fn!(handler_147, 147); +create_fn!(handler_148, 148); +create_fn!(handler_149, 149); +create_fn!(handler_150, 150); +create_fn!(handler_151, 151); +create_fn!(handler_152, 152); +create_fn!(handler_153, 153); +create_fn!(handler_154, 154); +create_fn!(handler_155, 155); +create_fn!(handler_156, 156); +create_fn!(handler_157, 157); +create_fn!(handler_158, 158); +create_fn!(handler_159, 159); +create_fn!(handler_160, 160); +create_fn!(handler_161, 161); +create_fn!(handler_162, 162); +create_fn!(handler_163, 163); +create_fn!(handler_164, 164); +create_fn!(handler_165, 165); +create_fn!(handler_166, 166); +create_fn!(handler_167, 167); +create_fn!(handler_168, 168); +create_fn!(handler_169, 169); +create_fn!(handler_170, 170); +create_fn!(handler_171, 171); +create_fn!(handler_172, 172); +create_fn!(handler_173, 173); +create_fn!(handler_174, 174); +create_fn!(handler_175, 175); +create_fn!(handler_176, 176); +create_fn!(handler_177, 177); +create_fn!(handler_178, 178); +create_fn!(handler_179, 179); +create_fn!(handler_180, 180); +create_fn!(handler_181, 181); +create_fn!(handler_182, 182); +create_fn!(handler_183, 183); +create_fn!(handler_184, 184); +create_fn!(handler_185, 185); +create_fn!(handler_186, 186); +create_fn!(handler_187, 187); +create_fn!(handler_188, 188); +create_fn!(handler_189, 189); +create_fn!(handler_190, 190); +create_fn!(handler_191, 191); +create_fn!(handler_192, 192); +create_fn!(handler_193, 193); +create_fn!(handler_194, 194); +create_fn!(handler_195, 195); +create_fn!(handler_196, 196); +create_fn!(handler_197, 197); +create_fn!(handler_198, 198); +create_fn!(handler_199, 199); +create_fn!(handler_200, 200); +create_fn!(handler_201, 201); +create_fn!(handler_202, 202); +create_fn!(handler_203, 203); +create_fn!(handler_204, 204); +create_fn!(handler_205, 205); +create_fn!(handler_206, 206); +create_fn!(handler_207, 207); +create_fn!(handler_208, 208); +create_fn!(handler_209, 209); +create_fn!(handler_210, 210); +create_fn!(handler_211, 211); +create_fn!(handler_212, 212); +create_fn!(handler_213, 213); +create_fn!(handler_214, 214); +create_fn!(handler_215, 215); +create_fn!(handler_216, 216); +create_fn!(handler_217, 217); +create_fn!(handler_218, 218); +create_fn!(handler_219, 219); +create_fn!(handler_220, 220); +create_fn!(handler_221, 221); +create_fn!(handler_222, 222); +create_fn!(handler_223, 223); +create_fn!(handler_224, 224); +create_fn!(handler_225, 225); +create_fn!(handler_226, 226); +create_fn!(handler_227, 227); +create_fn!(handler_228, 228); +create_fn!(handler_229, 229); +create_fn!(handler_230, 230); +create_fn!(handler_231, 231); +create_fn!(handler_232, 232); +create_fn!(handler_233, 233); +create_fn!(handler_234, 234); +create_fn!(handler_235, 235); +create_fn!(handler_236, 236); +create_fn!(handler_237, 237); +create_fn!(handler_238, 238); +create_fn!(handler_239, 239); +create_fn!(handler_240, 240); +create_fn!(handler_241, 241); +create_fn!(handler_242, 242); +create_fn!(handler_243, 243); +create_fn!(handler_244, 244); +create_fn!(handler_245, 245); +create_fn!(handler_246, 246); +create_fn!(handler_247, 247); +create_fn!(handler_248, 248); +create_fn!(handler_249, 249); +create_fn!(handler_250, 250); +create_fn!(handler_251, 251); +create_fn!(handler_252, 252); +create_fn!(handler_253, 253); +create_fn!(handler_254, 254); +create_fn!(handler_255, 255); diff --git a/opentmk/src/arch/x86_64/mod.rs b/opentmk/src/arch/x86_64/mod.rs new file mode 100644 index 0000000000..81cead476e --- /dev/null +++ b/opentmk/src/arch/x86_64/mod.rs @@ -0,0 +1,4 @@ +pub mod hypercall; +pub mod serial; +pub mod interrupt; +mod interrupt_handler_register; \ No newline at end of file diff --git a/opentmk/src/arch/x86_64/serial.rs b/opentmk/src/arch/x86_64/serial.rs new file mode 100644 index 0000000000..250fbd66cf --- /dev/null +++ b/opentmk/src/arch/x86_64/serial.rs @@ -0,0 +1,125 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +//! Serial output for debugging. + +use core::arch::asm; +use core::fmt; +use core::sync::atomic::AtomicBool; +use crate::sync::Mutex; + +const COM4: u16 = 0x2E8; +static mut MUTEX : Mutex<()> = Mutex::new(()); + +/// Write a byte to a port. +/// +/// # Safety +/// +/// The caller must be sure that the given port is safe to write to, and that the +/// given value is safe for it. +unsafe fn outb(port: u16, data: u8) { + // SAFETY: The caller has assured us this is safe. + unsafe { + asm! { + "out dx, al", + in("dx") port, + in("al") data, + } + } +} + +/// Read a byte from a port. +/// +/// # Safety +/// +/// The caller must be sure that the given port is safe to read from. +unsafe fn inb(port: u16) -> u8 { + let mut data; + // SAFETY: The caller has assured us this is safe. + unsafe { + asm! { + "in al, dx", + in("dx") port, + out("al") data, + } + } + data +} + +/// A trait to access io ports used by the serial device. +pub trait IoAccess { + /// Issue an in byte instruction. + /// + /// # Safety + /// + /// The caller must be sure that the given port is safe to read from. + unsafe fn inb(&self, port: u16) -> u8; + /// Issue an out byte instruction. + /// + /// # Safety + /// + /// The caller must be sure that the given port is safe to write to, and that the + /// given value is safe for it. + unsafe fn outb(&self, port: u16, data: u8); +} + +/// A struct to access io ports using in/out instructions. +pub struct InstrIoAccess; + +impl IoAccess for InstrIoAccess { + unsafe fn inb(&self, port: u16) -> u8 { + // SAFETY: The serial port caller has specified a valid port. + unsafe { inb(port) } + } + + unsafe fn outb(&self, port: u16, data: u8) { + // SAFETY: The serial port caller has specified a valid port and data. + unsafe { outb(port, data) } + } +} + +/// A writer for the COM3 UART. +pub struct Serial { + io: T, +} + +impl Serial { + /// Initialize the serial port. + pub fn init(io: T) -> Self { + // SAFETY: Writing these values to the serial device is safe. + unsafe { + io.outb(COM4 + 1, 0x00); // Disable all interrupts + io.outb(COM4 + 2, 0xC7); // Enable FIFO, clear them, with 14-byte threshold + io.outb(COM4 + 4, 0x0F); + } + + Self { io } + } + + /// Create an instance without calling init. + pub const fn new(io: T) -> Self { + Self { io } + } + + fn write_byte(&self, b: u8) { + // SAFETY: Reading and writing text to the serial device is safe. + unsafe { + while self.io.inb(COM4 + 5) & 0x20 == 0 {} + self.io.outb(COM4, b); + } + } +} + + +impl fmt::Write for Serial { + fn write_str(&mut self, s: &str) -> fmt::Result { + let _guard = unsafe { MUTEX.lock() }; + for &b in s.as_bytes() { + if b == b'\n' { + self.write_byte(b'\r'); + } + self.write_byte(b); + } + Ok(()) + } +} diff --git a/opentmk/src/main.rs b/opentmk/src/main.rs new file mode 100644 index 0000000000..019f594f4d --- /dev/null +++ b/opentmk/src/main.rs @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#![allow(warnings)] +#![no_std] +#![allow(unsafe_code)] +#![feature(naked_functions)] +#![feature(abi_x86_interrupt)] +#![feature(concat_idents)] + +#![doc = include_str!("../README.md")] +// HACK: workaround for building guest_test_uefi as part of the workspace in CI. +#![cfg_attr(all(not(test), target_os = "uefi"), no_main)] +#![cfg_attr(all(not(test), target_os = "uefi"), no_std)] + +// HACK: workaround for building guest_test_uefi as part of the workspace in CI +// +// Actual entrypoint is `uefi::uefi_main`, via the `#[entry]` macro +#[cfg(any(test, not(target_os = "uefi")))] +fn main() {} + +#[macro_use] +extern crate alloc; + +mod uefi; +pub mod arch; +pub mod slog; +pub mod sync; \ No newline at end of file diff --git a/opentmk/src/slog.rs b/opentmk/src/slog.rs new file mode 100644 index 0000000000..2073788370 --- /dev/null +++ b/opentmk/src/slog.rs @@ -0,0 +1,242 @@ +#![feature(panic_location)] + +use core::any::type_name; +use core::fmt::Write; +use core::result; + +use crate::arch::serial::{InstrIoAccess, Serial}; +use crate::sync::Mutex; +use alloc::string::{String, ToString}; +#[no_std] +use serde_json::json; +use serde::Serialize; +pub enum Level { + DEBUG = 0, + INFO = 1, + WARNING = 2, + ERROR = 3, + CRITICAL = 4, +} + +pub fn get_json_string(s: &String, terminate_new_line: bool, level: Level) -> String { + let out = json!({ + "type:": "log", + "message": s, + "level": match level { + Level::DEBUG => "DEBUG", + Level::INFO => "INFO", + Level::WARNING => "WARNING", + Level::ERROR => "ERROR", + Level::CRITICAL => "CRITICAL", + } + }); + let mut out = out.to_string(); + if terminate_new_line { + out.push('\n'); + } + return out; +} + +pub fn get_json_test_assertion_string( + s: &str, + terminate_new_line: bool, + line: String, + assert_result: bool, + testname: &T, +) -> String where T: Serialize { + let out = json!({ + "type:": "assertion", + "message": s, + "level": "CRITICAL", + "line": line, + "assertion_result": assert_result, + "testname": testname, + }); + let mut out = out.to_string(); + if terminate_new_line { + out.push('\n'); + } + return out; +} + +pub static mut SERIAL: Serial = Serial::new(InstrIoAccess {}); + +#[macro_export] +macro_rules! tmk_assert { + ($condition:expr, $message:expr) => {{ + use core::fmt::Write; + let file = core::file!(); + let line = line!(); + let file_line = format!("{}:{}", file, line); + let expn = stringify!($condition); + let result: bool = $condition; + let js = + crate::slog::get_json_test_assertion_string(&expn, true, file_line, result, &$message); + unsafe { crate::slog::SERIAL.write_str(&js) }; + if !result { + panic!("Assertion failed: {}", $message); + } + }}; +} + +#[macro_export] +macro_rules! logt { + ($($arg:tt)*) => { + { + use core::fmt::Write; + let message = format!($($arg)*); + let js = crate::slog::get_json_string(&message, true, crate::slog::Level::INFO); + unsafe { crate::slog::SERIAL.write_str(&js) }; + } + }; +} + +#[macro_export] +macro_rules! errorlog { + ($($arg:tt)*) => { + { + use core::fmt::Write; + let message = format!($($arg)*); + let js = crate::slog::get_json_string(&message, true, crate::slog::Level::ERROR); + unsafe { crate::slog::SERIAL.write_str(&js) }; + } + }; +} + +#[macro_export] +macro_rules! debuglog { + ($($arg:tt)*) => { + { + use core::fmt::Write; + + let message = format!($($arg)*); + let js = crate::slog::get_json_string(&message, true, crate::slog::Level::DEBUG); + unsafe { crate::slog::SERIAL.write_str(&js) }; + } + }; +} + +#[macro_export] +macro_rules! infolog { + ($($arg:tt)*) => { + { + use core::fmt::Write; + + let message = format!($($arg)*); + let js = crate::slog::get_json_string(&message, true, crate::slog::Level::INFO); + unsafe { crate::slog::SERIAL.write_str(&js) }; + } + }; +} + +#[macro_export] +macro_rules! warninglog { + ($($arg:tt)*) => { + { + use core::fmt::Write; + + let message = format!($($arg)*); + let js = crate::slog::get_json_string(&message, true, crate::slog::Level::WARNING); + unsafe { crate::slog::SERIAL.write_str(&js) }; + } + }; +} + +#[macro_export] +macro_rules! criticallog { + ($($arg:tt)*) => { + { + use core::fmt::Write; + + let message = format!($($arg)*); + let js = crate::slog::get_json_string(&message, true, crate::slog::Level::CRITICAL); + unsafe { crate::slog::SERIAL.write_str(&js) }; + } + }; +} + +#[macro_export] +macro_rules! slog { + + ($serial:expr, $($arg:tt)*) => { + let mut serial : &mut Mutex> = &mut $serial; + let message = format!($($arg)*); + let js = slog::get_json_string(&message, true, crate::slog::Level::INFO); + { + let mut serial = serial.lock(); + serial.write_str(&js); + } + }; + +} + +pub trait AssertResult { + fn unpack_assert(self) -> T; + fn expect_assert(self, message: &str) -> T; +} + +pub trait AssertOption { + fn expect_assert(self, message: &str) -> T; +} + +impl AssertOption for Option { + fn expect_assert(self, message: &str) -> T { + match self { + Some(value) => value, + None => { + let call: &core::panic::Location<'_> = core::panic::Location::caller(); + let file_line = format!("{}:{}", call.file(), call.line()); + let expn = type_name::>(); + let js = crate::slog::get_json_test_assertion_string( + expn, true, file_line, false, &message, + ); + unsafe { crate::slog::SERIAL.write_str(&js) }; + panic!("Assertion failed: {}", message); + } + } + } +} + +impl AssertResult for Result +where + E: core::fmt::Debug, +{ + fn unpack_assert(self) -> T { + match self { + Ok(value) => value, + Err(err) => { + let call: &core::panic::Location<'_> = core::panic::Location::caller(); + let file_line = format!("{}:{}", call.file(), call.line()); + let expn = type_name::>(); + let js = crate::slog::get_json_test_assertion_string( + expn, + true, + file_line, + false, + &"ResultTest", + ); + unsafe { crate::slog::SERIAL.write_str(&js) }; + panic!("Assertion failed: {:?}", err); + } + } + } + fn expect_assert(self, message: &str) -> T { + match self { + Ok(value) => { + infolog!("result is ok, condition not met for: {}", message); + value + } + Err(err) => { + let call: &core::panic::Location<'_> = core::panic::Location::caller(); + let file_line = format!("{}:{}", call.file(), call.line()); + let expn = type_name::>(); + let js = crate::slog::get_json_test_assertion_string( + expn, true, file_line, false, &message, + ); + unsafe { crate::slog::SERIAL.write_str(&js) }; + + panic!("Assertion failed: {:?}", err); + } + } + } +} diff --git a/opentmk/src/sync.rs b/opentmk/src/sync.rs new file mode 100644 index 0000000000..a26233bc76 --- /dev/null +++ b/opentmk/src/sync.rs @@ -0,0 +1,490 @@ +use core::{arch::asm, cell::{RefCell, UnsafeCell}, fmt::Error, sync::atomic::{AtomicBool, AtomicUsize, Ordering}}; +pub use spin::Mutex; +use alloc::{boxed::Box, string::{String, ToString}, sync::Arc, vec::Vec}; +use alloc::collections::VecDeque; + +use crate::infolog; + +// pub struct LazyLock { +// lock: AtomicBool, +// init: fn() -> T, +// val: Option>, +// } + +// impl LazyLock { +// pub fn new(init: fn() -> T) -> Self { +// LazyLock { +// lock: AtomicBool::new(false), +// init, +// val: None, +// } +// } + +// pub fn get(&mut self) -> &T { +// if let ok = self.lock.get_mut() { +// if *ok { +// self.val = Some(RefCell::new((self.init)())); + +// } +// } +// if let Some(ref val) = self.val { +// return &val.borrow(); +// } +// panic!("LazyLock not initialized"); +// } + +// pub fn get_mut(&mut self) -> &mut T { +// if let ok = self.lock.get_mut() { +// if ok { +// self.val = Some((self.init)()); +// } +// } +// &mut self.val.unwrap() +// } +// } + +// pub struct Mutex { +// lock: AtomicBool, +// data: UnsafeCell, +// } + +// unsafe impl Sync for Mutex {} + +// impl Mutex { +// pub const fn new(data: T) -> Self { +// Mutex { +// lock: AtomicBool::new(false), +// data: UnsafeCell::new(data), +// } +// } + +// pub fn lock<'a>(&'a self) -> MutexGuard<'a, T> { +// while self.lock.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed).is_err() { +// // Busy-wait until the lock is acquired +// core::hint::spin_loop(); +// } +// MutexGuard { mutex: self } +// } + +// pub fn unlock(&self) { +// self.lock.store(false, Ordering::Release); +// } +// } + +// pub struct MutexGuard<'a, T> { +// mutex: &'a Mutex, +// } + +// impl<'a, T> Drop for MutexGuard<'a, T> { +// fn drop(&mut self) { +// self.mutex.unlock(); +// } +// } + +// impl<'a, T> core::ops::Deref for MutexGuard<'a, T> { +// type Target = T; + +// fn deref(&self) -> &Self::Target { +// unsafe { &*self.mutex.data.get() } +// } +// } + +// impl<'a, T> core::ops::DerefMut for MutexGuard<'a, T> { +// fn deref_mut(&mut self) -> &mut Self::Target { +// unsafe { &mut *self.mutex.data.get() } +// } +// } + +#[derive(Debug)] +pub struct RingBuffer { + buffer: Vec>, + capacity: usize, + head: usize, + tail: usize, + size: usize, +} + +impl RingBuffer { + pub fn new(capacity: usize) -> Self { + RingBuffer { + buffer: Vec::with_capacity(capacity), + capacity, + head: 0, + tail: 0, + size: 0, + } + } + + fn is_empty(&self) -> bool { + self.size == 0 + } + + fn is_full(&self) -> bool { + self.size == self.capacity + } + + pub fn push(&mut self, item: T) -> Result<(), String> { + if self.is_full() { + return Err("Buffer is full".to_string()); + } + + if self.tail == self.buffer.len() { + self.buffer.push(Some(item)); + } else { + self.buffer[self.tail] = Some(item); + } + + self.tail = (self.tail + 1) % self.capacity; + self.size += 1; + + Ok(()) + } + + pub fn pop(&mut self) -> Option { + if self.is_empty() { + return None; + } + + let item = core::mem::replace(&mut self.buffer[self.head], None); + self.head = (self.head + 1) % self.capacity; + self.size -= 1; + + Some(item.unwrap()) + } + + pub fn len(&self) -> usize { + self.size + } +} + + + + +#[cfg(feature = "std")] +use std::error::Error; +use core::fmt; + +/// An unbounded channel implementation with priority send capability. +/// This implementation works in no_std environments using spin-rs. +/// It uses a VecDeque as the underlying buffer and ensures non-blocking operations. +pub struct Channel { + inner: Arc>, +} + +/// The inner data structure holding the channel state +struct ChannelInner { + /// The internal buffer using a VecDeque protected by its own mutex + buffer: Mutex>, + + /// Number of active senders + senders: AtomicUsize, + + /// Number of active receivers + receivers: AtomicUsize, +} + +unsafe impl Send for ChannelInner {} +unsafe impl Sync for ChannelInner {} + +/// Error type for sending operations +#[derive(Debug, Eq, PartialEq)] +pub enum SendError { + /// All receivers have been dropped + Disconnected(T), +} + +impl fmt::Display for SendError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + SendError::Disconnected(_) => write!(f, "send failed because receiver is disconnected"), + } + } +} + +#[cfg(feature = "std")] +impl Error for SendError {} + +/// Error type for receiving operations +#[derive(Debug, Eq, PartialEq)] +pub enum RecvError { + /// Channel is empty + Empty, + /// All senders have been dropped + Disconnected, +} + +impl fmt::Display for RecvError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + RecvError::Empty => write!(f, "receive failed because channel is empty"), + RecvError::Disconnected => write!(f, "receive failed because sender is disconnected"), + } + } +} + +#[cfg(feature = "std")] +impl Error for RecvError {} + +/// Sender half of the channel +pub struct Sender { + inner: Arc>, +} + +/// Receiver half of the channel +pub struct Receiver { + inner: Arc>, +} + +// implement clone for Sender +impl Clone for Sender { + fn clone(&self) -> Self { + self.inner.senders.fetch_add(1, Ordering::SeqCst); + Sender { + inner: self.inner.clone(), + } + } +} + +// implement clone for Receiver +impl Clone for Receiver { + fn clone(&self) -> Self { + self.inner.receivers.fetch_add(1, Ordering::SeqCst); + Receiver { + inner: self.inner.clone(), + } + } +} + +impl Channel { + /// Creates a new unbounded channel + pub fn new() -> Self { + let inner = Arc::new(ChannelInner { + buffer: Mutex::new(VecDeque::new()), + senders: AtomicUsize::new(1), // Start with one sender + receivers: AtomicUsize::new(1), // Start with one receiver + }); + + Self { inner } + } + + /// Splits the channel into a sender and receiver pair + pub fn split(self) -> (Sender, Receiver) { + let sender = Sender { + inner: self.inner.clone(), + }; + + let receiver = Receiver { + inner: self.inner, + }; + + (sender, receiver) + } + + /// Returns the current number of elements in the channel + pub fn len(&self) -> usize { + self.inner.buffer.lock().len() + } + + /// Returns true if the channel is empty + pub fn is_empty(&self) -> bool { + self.inner.buffer.lock().is_empty() + } +} + +impl Sender { + /// Sends an element to the back of the queue + /// Returns Ok(()) if successful, Err(SendError) if all receivers have been dropped + pub fn send(&self, value: T) -> Result<(), SendError> { + // Check if there are any receivers left + if self.inner.receivers.load(Ordering::SeqCst) == 0 { + return Err(SendError::Disconnected(value)); + } + + // Lock the buffer - only locked during the actual send operation + let mut buffer = self.inner.buffer.lock(); + + // Check again after locking + if self.inner.receivers.load(Ordering::SeqCst) == 0 { + return Err(SendError::Disconnected(value)); + } + + // Push to the back of the queue - can't fail since we're unbounded + buffer.push_back(value); + + Ok(()) + } + + /// Sends an element to the front of the queue (highest priority) + /// Returns Ok(()) if successful, Err(SendError) if all receivers have been dropped + pub fn send_priority(&self, value: T) -> Result<(), SendError> { + // Check if there are any receivers left + if self.inner.receivers.load(Ordering::SeqCst) == 0 { + return Err(SendError::Disconnected(value)); + } + + // Lock the buffer - only locked during the actual send operation + let mut buffer = self.inner.buffer.lock(); + + // Check again after locking + if self.inner.receivers.load(Ordering::SeqCst) == 0 { + return Err(SendError::Disconnected(value)); + } + + // Push to the front of the queue - can't fail since we're unbounded + buffer.push_front(value); + + Ok(()) + } + + /// Send a batch of elements at once + /// Returns the number of elements successfully sent (all of them, unless disconnected) + pub fn send_batch(&self, items: I) -> usize + where + I: IntoIterator, + { + // Check if there are any receivers left + if self.inner.receivers.load(Ordering::SeqCst) == 0 { + return 0; + } + + // Lock the buffer once for the entire batch + let mut buffer = self.inner.buffer.lock(); + + // Check again after locking + if self.inner.receivers.load(Ordering::SeqCst) == 0 { + return 0; + } + + let mut count = 0; + + // Push each item to the back of the queue + for item in items { + buffer.push_back(item); + count += 1; + } + + count + } + + /// Returns the current number of elements in the channel + pub fn len(&self) -> usize { + self.inner.buffer.lock().len() + } + + /// Returns true if the channel is empty + pub fn is_empty(&self) -> bool { + self.inner.buffer.lock().is_empty() + } +} + +impl Receiver { + /// Tries to receive an element from the front of the queue without blocking + /// Returns Ok(value) if successful, Err(RecvError) otherwise + pub fn recv(&self) -> Result { + loop { + match self.try_recv() { + Ok(value) => return Ok(value), + Err(RecvError::Empty) => { + // Yield to the scheduler and try again + continue; + }, + Err(err) => return Err(err), + } + } + } + + /// Tries to receive an element from the front of the queue without blocking + /// Returns Ok(value) if successful, Err(RecvError) otherwise + pub fn try_recv(&self) -> Result { + // Use a separate scope for the lock to ensure it's released promptly + let result = { + let mut buffer = self.inner.buffer.lock(); + buffer.pop_front() + }; + + match result { + Some(val) => Ok(val), + None => { + // Check if there are any senders left + if self.inner.senders.load(Ordering::SeqCst) == 0 { + Err(RecvError::Disconnected) + } else { + Err(RecvError::Empty) + } + } + } + } + + + /// Tries to receive multiple elements at once, up to the specified limit + /// Returns a vector of received elements + pub fn recv_batch(&self, max_items: usize) -> Vec + where + T: Send, + { + // If max_items is 0, return an empty vector + if max_items == 0 { + return Vec::new(); + } + + let mut items = Vec::new(); + + // Lock the buffer once for the entire batch + let mut buffer = self.inner.buffer.lock(); + + // Calculate how many items to take + let count = max_items.min(buffer.len()); + + // Reserve capacity for efficiency + items.reserve(count); + + // Take items from the front of the queue + for _ in 0..count { + if let Some(item) = buffer.pop_front() { + items.push(item); + } else { + // This shouldn't happen due to the min() above, but just in case + break; + } + } + + items + } + + /// Peeks at the next element without removing it + pub fn peek(&self) -> Option + where + T: Clone, + { + let buffer = self.inner.buffer.lock(); + buffer.front().cloned() + } + + /// Returns the current number of elements in the channel + pub fn len(&self) -> usize { + self.inner.buffer.lock().len() + } + + /// Returns true if the channel is empty + pub fn is_empty(&self) -> bool { + self.inner.buffer.lock().is_empty() + } +} + +impl Drop for Sender { + fn drop(&mut self) { + self.inner.senders.fetch_sub(1, Ordering::SeqCst); + } +} + +impl Drop for Receiver { + fn drop(&mut self) { + self.inner.receivers.fetch_sub(1, Ordering::SeqCst); + } +} + +impl Default for Channel { + fn default() -> Self { + Self::new() + } +} \ No newline at end of file diff --git a/opentmk/src/uefi/alloc.rs b/opentmk/src/uefi/alloc.rs new file mode 100644 index 0000000000..92619fdee5 --- /dev/null +++ b/opentmk/src/uefi/alloc.rs @@ -0,0 +1,86 @@ +use core::{alloc::GlobalAlloc, cell::RefCell}; + +use linked_list_allocator::LockedHeap; +use spin::mutex::Mutex; +use uefi::{allocator::Allocator, boot::{self, AllocateType, MemoryType}}; + +pub const SIZE_1MB: usize = 1024 * 1024; + +#[global_allocator] +pub static ALLOCATOR: MemoryAllocator = MemoryAllocator { + use_locked_heap: Mutex::new(RefCell::new(false)), + locked_heap: LockedHeap::empty(), + uefi_allocator: Allocator{}, +}; + +pub struct MemoryAllocator { + use_locked_heap: Mutex>, + locked_heap: LockedHeap, + uefi_allocator: Allocator, +} + +#[expect(unsafe_code)] +unsafe impl GlobalAlloc for MemoryAllocator { + #[allow(unsafe_code)] + unsafe fn alloc(&self, layout: core::alloc::Layout) -> *mut u8 { + if *self.use_locked_heap.lock().borrow() { + unsafe { self.locked_heap.alloc(layout) } + } else { + unsafe { self.uefi_allocator.alloc(layout) } + } + } + + unsafe fn dealloc(&self, ptr: *mut u8, layout: core::alloc::Layout) { + if *self.use_locked_heap.lock().borrow() { + unsafe { self.locked_heap.dealloc(ptr, layout) } + } else { + unsafe { self.uefi_allocator.dealloc(ptr, layout) } + } + } + + unsafe fn alloc_zeroed(&self, layout: core::alloc::Layout) -> *mut u8 { + if *self.use_locked_heap.lock().borrow() { + unsafe { self.locked_heap.alloc_zeroed(layout) } + } else { + unsafe { self.uefi_allocator.alloc_zeroed(layout) } + } + } + + unsafe fn realloc(&self, ptr: *mut u8, layout: core::alloc::Layout, new_size: usize) -> *mut u8 { + if *self.use_locked_heap.lock().borrow() { + unsafe { self.locked_heap.realloc(ptr, layout, new_size) } + } else { + unsafe { self.uefi_allocator.realloc(ptr, layout, new_size) } + } + } +} + +impl MemoryAllocator { + + #[expect(unsafe_code)] + pub unsafe fn init(&self, size: usize) -> bool { + let pages = ((SIZE_1MB * size) / 4096) + 1; + let size = pages * 4096; + let mem: Result, uefi::Error> = boot::allocate_pages(AllocateType::AnyPages, MemoryType::BOOT_SERVICES_DATA, pages); + if mem.is_err() { + return false; + } + let ptr = mem.unwrap().as_ptr(); + unsafe { + self.locked_heap.lock().init(ptr, size); + } + *self.use_locked_heap.lock().borrow_mut() = true; + return true; + } + + pub fn get_page_alligned_memory(&self, size: usize) -> *mut u8 { + let pages = ((SIZE_1MB * size) / 4096) + 1; + let size = pages * 4096; + let mem: Result, uefi::Error> = boot::allocate_pages(AllocateType::AnyPages, MemoryType::BOOT_SERVICES_DATA, pages); + if mem.is_err() { + return core::ptr::null_mut(); + } + let ptr = mem.unwrap().as_ptr(); + return ptr; + } +} diff --git a/opentmk/src/uefi/context.rs b/opentmk/src/uefi/context.rs new file mode 100644 index 0000000000..b9e5da65d6 --- /dev/null +++ b/opentmk/src/uefi/context.rs @@ -0,0 +1,61 @@ +use core::ops::Range; + +use alloc::boxed::Box; +use hvdef::Vtl; + + + +pub trait TestCtxTrait { + fn get_vp_count(&self) -> u32; + fn get_current_vp(&self) -> u32; + fn get_current_vtl(&self) -> Vtl; + + fn start_on_vp(&mut self, cmd: VpExecutor); + + fn queue_command_vp(&mut self, cmd: VpExecutor); + + fn switch_to_high_vtl(&mut self); + fn switch_to_low_vtl(&mut self); + + fn setup_partition_vtl(&mut self, vtl: Vtl); + fn setup_interrupt_handler(&mut self); + fn set_interupt_idx(&mut self, interrupt_idx: u8, handler: fn()); + + fn setup_vtl_protection(&mut self); + fn setup_secure_intercept(&mut self, interrupt_idx: u8); + fn apply_vtl_protection_for_memory(&mut self, range: Range, vtl: Vtl); + fn write_msr(&mut self, msr: u32, value: u64); + fn read_msr(&mut self, msr: u32) -> u64; + + fn start_running_vp_with_default_context(&mut self, cmd: VpExecutor); + fn set_default_ctx_to_vp(&mut self, vp_index: u32, vtl: Vtl); + fn enable_vp_vtl_with_default_context(&mut self, vp_index: u32, vtl: Vtl); + + fn get_register(&mut self, reg: u32) -> u128; +} + +pub struct VpExecutor { + vp_index: u32, + vtl: Vtl, + cmd: Option>, +} + +impl VpExecutor { + pub fn new(vp_index: u32, vtl: Vtl) -> Self { + VpExecutor { + vp_index, + vtl, + cmd: None, + } + } + + pub fn command(mut self, cmd: impl FnOnce(&mut dyn TestCtxTrait) + 'static) -> Self { + self.cmd = Some(Box::new(cmd)); + self + } + + pub fn get(mut self) -> (u32, Vtl, Option>) { + let cmd = self.cmd.take(); + (self.vp_index, self.vtl, cmd) + } +} \ No newline at end of file diff --git a/opentmk/src/uefi/hypercall.rs b/opentmk/src/uefi/hypercall.rs new file mode 100644 index 0000000000..3ffe783e70 --- /dev/null +++ b/opentmk/src/uefi/hypercall.rs @@ -0,0 +1,604 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +//! Hypercall infrastructure. + +use arrayvec::ArrayVec; +use hvdef::hypercall::EnablePartitionVtlFlags; +use hvdef::hypercall::InitialVpContextX64; +use hvdef::HvInterruptType; +use hvdef::HvRegisterGuestVsmPartitionConfig; +use hvdef::HvRegisterValue; +use hvdef::HvRegisterVsmPartitionConfig; +use hvdef::HvX64RegisterName; +use minimal_rt::arch::hypercall::{invoke_hypercall_vtl}; +use zerocopy::FromZeros; +use core::arch; +use core::cell::RefCell; +use core::cell::UnsafeCell; +use core::mem::size_of; +use hvdef::hypercall::HvInputVtl; +use hvdef::Vtl; +use hvdef::HV_PAGE_SIZE; +use memory_range::MemoryRange; +use minimal_rt::arch::hypercall::invoke_hypercall; +use zerocopy::IntoBytes; +use zerocopy::FromBytes; + +/// Page-aligned, page-sized buffer for use with hypercalls +#[repr(C, align(4096))] +struct HvcallPage { + buffer: [u8; HV_PAGE_SIZE as usize], + +} + +impl HvcallPage { + pub const fn new() -> Self { + HvcallPage { + buffer: [0; HV_PAGE_SIZE as usize], + + } + } + + /// Address of the hypercall page. + fn address(&self) -> u64 { + let addr = self.buffer.as_ptr() as u64; + + // These should be page-aligned + assert!(addr % HV_PAGE_SIZE == 0); + + addr + } +} + +/// Provides mechanisms to invoke hypercalls within the boot shim. +/// Internally uses static buffers for the hypercall page, the input +/// page, and the output page, so this should not be used in any +/// multi-threaded capacity (which the boot shim currently is not). +pub struct HvCall { + initialized: bool, + pub vtl: Vtl, + input_page: HvcallPage, + output_page: HvcallPage, +} + +/// Returns an [`HvCall`] instance. +/// +/// Panics if another instance is already in use. +// #[track_caller] +// pub fn hvcall() -> core::cell::RefMut<'static, HvCall> { +// HVCALL.borrow_mut() +// } + +#[expect(unsafe_code)] +impl HvCall { + pub const fn new() -> Self { + // SAFETY: The caller must ensure that this is only called once. + unsafe { + HvCall { + initialized: false, + vtl: Vtl::Vtl0, + input_page: HvcallPage::new(), + output_page: HvcallPage::new(), + } + } + + } + fn input_page(&mut self) -> &mut HvcallPage { + &mut self.input_page + } + + fn output_page(&mut self) -> &mut HvcallPage { + &mut self.output_page + } + + /// Returns the address of the hypercall page, mapping it first if + /// necessary. + #[cfg(target_arch = "x86_64")] + pub fn hypercall_page(&mut self) -> u64 { + self.init_if_needed(); + core::ptr::addr_of!(minimal_rt::arch::hypercall::HYPERCALL_PAGE) as u64 + } + + fn init_if_needed(&mut self) { + if !self.initialized { + self.initialize(); + } + } + + pub fn initialize(&mut self) { + assert!(!self.initialized); + + // TODO: revisit os id value. For now, use 1 (which is what UEFI does) + let guest_os_id = hvdef::hypercall::HvGuestOsMicrosoft::new().with_os_id(1); + crate::arch::hypercall::initialize(guest_os_id.into()); + self.initialized = true; + + self.vtl = self + .get_register(hvdef::HvAllArchRegisterName::VsmVpStatus.into(), None) + .map_or(Vtl::Vtl0, |status| { + hvdef::HvRegisterVsmVpStatus::from(status.as_u64()) + .active_vtl() + .try_into() + .unwrap() + }); + } + + /// Call before jumping to kernel. + pub fn uninitialize(&mut self) { + if self.initialized { + crate::arch::hypercall::uninitialize(); + self.initialized = false; + } + } + + /// Returns the environment's VTL. + pub fn vtl(&self) -> Vtl { + assert!(self.initialized); + self.vtl + } + + /// Makes a hypercall. + /// rep_count is Some for rep hypercalls + fn dispatch_hvcall( + &mut self, + code: hvdef::HypercallCode, + rep_count: Option, + ) -> hvdef::hypercall::HypercallOutput { + self.init_if_needed(); + + let control: hvdef::hypercall::Control = hvdef::hypercall::Control::new() + .with_code(code.0) + .with_rep_count(rep_count.unwrap_or_default()); + + // SAFETY: Invoking hypercall per TLFS spec + unsafe { + invoke_hypercall( + control, + self.input_page().address(), + self.output_page().address(), + ) + } + } + + + pub fn set_vp_registers( + &mut self, + vp: u32, + vtl: Option, + vp_context : Option, + ) -> Result<(), hvdef::HvError> { + const HEADER_SIZE: usize = size_of::(); + + let header = hvdef::hypercall::GetSetVpRegisters { + partition_id: hvdef::HV_PARTITION_ID_SELF, + vp_index: vp, + target_vtl: vtl.unwrap_or(HvInputVtl::CURRENT_VTL), + rsvd: [0; 3], + }; + + header.write_to_prefix(self.input_page().buffer.as_mut_slice()); + + let mut input_offset = HEADER_SIZE; + + let mut count = 0; + let mut write_reg = |reg_name: hvdef::HvRegisterName, reg_value: hvdef::HvRegisterValue| { + let reg = hvdef::hypercall::HvRegisterAssoc { + name: reg_name, + pad: Default::default(), + value: reg_value, + }; + + reg.write_to_prefix(&mut self.input_page().buffer[input_offset..]); + + input_offset += size_of::(); + count += 1; + }; + // pub msr_cr_pat: u64, + + write_reg(hvdef::HvX64RegisterName::Cr0.into(), vp_context.unwrap().cr0.into()); + write_reg(hvdef::HvX64RegisterName::Cr3.into(), vp_context.unwrap().cr3.into()); + write_reg(hvdef::HvX64RegisterName::Cr4.into(), vp_context.unwrap().cr4.into()); + write_reg(hvdef::HvX64RegisterName::Rip.into(), vp_context.unwrap().rip.into()); + write_reg(hvdef::HvX64RegisterName::Rsp.into(), vp_context.unwrap().rsp.into()); + write_reg(hvdef::HvX64RegisterName::Rflags.into(), vp_context.unwrap().rflags.into()); + write_reg(hvdef::HvX64RegisterName::Cs.into(), vp_context.unwrap().cs.into()); + write_reg(hvdef::HvX64RegisterName::Ss.into(), vp_context.unwrap().ss.into()); + write_reg(hvdef::HvX64RegisterName::Ds.into(), vp_context.unwrap().ds.into()); + write_reg(hvdef::HvX64RegisterName::Es.into(), vp_context.unwrap().es.into()); + write_reg(hvdef::HvX64RegisterName::Fs.into(), vp_context.unwrap().fs.into()); + write_reg(hvdef::HvX64RegisterName::Gs.into(), vp_context.unwrap().gs.into()); + write_reg(hvdef::HvX64RegisterName::Gdtr.into(), vp_context.unwrap().gdtr.into()); + write_reg(hvdef::HvX64RegisterName::Idtr.into(), vp_context.unwrap().idtr.into()); + write_reg(hvdef::HvX64RegisterName::Ldtr.into(), vp_context.unwrap().ldtr.into()); + write_reg(hvdef::HvX64RegisterName::Tr.into(), vp_context.unwrap().tr.into()); + write_reg(hvdef::HvX64RegisterName::Efer.into(), vp_context.unwrap().efer.into()); + + let output = self.dispatch_hvcall(hvdef::HypercallCode::HvCallSetVpRegisters, Some(count)); + + output.result() + } + + + /// Hypercall for setting a register to a value. + pub fn set_register( + &mut self, + name: hvdef::HvRegisterName, + value: hvdef::HvRegisterValue, + vtl: Option + ) -> Result<(), hvdef::HvError> { + const HEADER_SIZE: usize = size_of::(); + + let header = hvdef::hypercall::GetSetVpRegisters { + partition_id: hvdef::HV_PARTITION_ID_SELF, + vp_index: hvdef::HV_VP_INDEX_SELF, + target_vtl: vtl.unwrap_or(HvInputVtl::CURRENT_VTL), + rsvd: [0; 3], + }; + + header.write_to_prefix(self.input_page().buffer.as_mut_slice()); + + let reg = hvdef::hypercall::HvRegisterAssoc { + name, + pad: Default::default(), + value, + }; + + reg.write_to_prefix(&mut self.input_page().buffer[HEADER_SIZE..]); + + let output = self.dispatch_hvcall(hvdef::HypercallCode::HvCallSetVpRegisters, Some(1)); + + output.result() + } + + /// Hypercall for setting a register to a value. + pub fn get_register( + &mut self, + name: hvdef::HvRegisterName, + vtl: Option, + ) -> Result { + const HEADER_SIZE: usize = size_of::(); + + let header = hvdef::hypercall::GetSetVpRegisters { + partition_id: hvdef::HV_PARTITION_ID_SELF, + vp_index: hvdef::HV_VP_INDEX_SELF, + target_vtl: vtl.unwrap_or(HvInputVtl::CURRENT_VTL), + rsvd: [0; 3], + }; + + header.write_to_prefix(self.input_page().buffer.as_mut_slice()); + name.write_to_prefix(&mut self.input_page().buffer[HEADER_SIZE..]); + + let output = self.dispatch_hvcall(hvdef::HypercallCode::HvCallGetVpRegisters, Some(1)); + output.result()?; + let value = hvdef::HvRegisterValue::read_from_prefix(&self.output_page().buffer).unwrap(); + + Ok(value.0) + } + + /// Hypercall to apply vtl protections to the pages from address start to end + #[cfg_attr(target_arch = "aarch64", allow(dead_code))] + pub fn apply_vtl2_protections(&mut self, range: MemoryRange) -> Result<(), hvdef::HvError> { + const HEADER_SIZE: usize = size_of::(); + const MAX_INPUT_ELEMENTS: usize = (HV_PAGE_SIZE as usize - HEADER_SIZE) / size_of::(); + + let header = hvdef::hypercall::ModifyVtlProtectionMask { + partition_id: hvdef::HV_PARTITION_ID_SELF, + map_flags: hvdef::HV_MAP_GPA_PERMISSIONS_NONE, + target_vtl: HvInputVtl::CURRENT_VTL, + reserved: [0; 3], + }; + + let mut current_page = range.start_4k_gpn(); + while current_page < range.end_4k_gpn() { + let remaining_pages = range.end_4k_gpn() - current_page; + let count = remaining_pages.min(MAX_INPUT_ELEMENTS as u64); + + header.write_to_prefix(self.input_page().buffer.as_mut_slice()); + + let mut input_offset = HEADER_SIZE; + for i in 0..count { + let page_num = current_page + i; + page_num.write_to_prefix(&mut self.input_page().buffer[input_offset..]); + input_offset += size_of::(); + } + + let output = self.dispatch_hvcall( + hvdef::HypercallCode::HvCallModifyVtlProtectionMask, + Some(count as usize), + ); + + output.result()?; + + current_page += count; + } + + Ok(()) + } + + /// Hypercall to apply vtl protections to the pages from address start to end + #[cfg_attr(target_arch = "x86_64", allow(dead_code))] + pub fn apply_vtl_protections(&mut self, range: MemoryRange, vtl: Vtl) -> Result<(), hvdef::HvError> { + const HEADER_SIZE: usize = size_of::(); + const MAX_INPUT_ELEMENTS: usize = (HV_PAGE_SIZE as usize - HEADER_SIZE) / size_of::(); + + let header = hvdef::hypercall::ModifyVtlProtectionMask { + partition_id: hvdef::HV_PARTITION_ID_SELF, + map_flags: hvdef::HV_MAP_GPA_PERMISSIONS_NONE, + target_vtl: HvInputVtl::new() + .with_target_vtl_value(vtl.into()) + .with_use_target_vtl(true), + reserved: [0; 3], + }; + + let mut current_page = range.start_4k_gpn(); + while current_page < range.end_4k_gpn() { + let remaining_pages = range.end_4k_gpn() - current_page; + let count = remaining_pages.min(MAX_INPUT_ELEMENTS as u64); + + header.write_to_prefix(self.input_page().buffer.as_mut_slice()); + + let mut input_offset = HEADER_SIZE; + for i in 0..count { + let page_num = current_page + i; + page_num.write_to_prefix(&mut self.input_page().buffer[input_offset..]); + input_offset += size_of::(); + } + + let output = self.dispatch_hvcall( + hvdef::HypercallCode::HvCallModifyVtlProtectionMask, + Some(count as usize), + ); + + output.result()?; + + current_page += count; + } + + Ok(()) + } + + + #[cfg(target_arch = "x86_64")] + /// Hypercall to get the current VTL VP context + pub fn get_current_vtl_vp_context(&mut self) -> Result { + use hvdef::HvX64RegisterName; + use zerocopy::FromZeros; + let mut context :InitialVpContextX64 = FromZeros::new_zeroed(); + context.cr0 = self.get_register(HvX64RegisterName::Cr0.into(), None)?.as_u64(); + context.cr3 = self.get_register(HvX64RegisterName::Cr3.into(), None)?.as_u64(); + context.cr4 = self.get_register(HvX64RegisterName::Cr4.into(), None)?.as_u64(); + context.rip = self.get_register(HvX64RegisterName::Rip.into(), None)?.as_u64(); + context.rsp = self.get_register(HvX64RegisterName::Rsp.into(), None)?.as_u64(); + context.rflags = self.get_register(HvX64RegisterName::Rflags.into(), None)?.as_u64(); + context.cs = self.get_register(HvX64RegisterName::Cs.into(), None)?.as_segment(); + context.ss = self.get_register(HvX64RegisterName::Ss.into(), None)?.as_segment(); + context.ds = self.get_register(HvX64RegisterName::Ds.into(), None)?.as_segment(); + context.es = self.get_register(HvX64RegisterName::Es.into(), None)?.as_segment(); + context.fs = self.get_register(HvX64RegisterName::Fs.into(), None)?.as_segment(); + context.gs = self.get_register(HvX64RegisterName::Gs.into(), None)?.as_segment(); + context.gdtr = self.get_register(HvX64RegisterName::Gdtr.into(), None)?.as_table(); + context.idtr = self.get_register(HvX64RegisterName::Idtr.into(), None)?.as_table(); + context.tr = self.get_register(HvX64RegisterName::Tr.into(), None)?.as_segment(); + context.efer = self.get_register(HvX64RegisterName::Efer.into(), None)?.as_u64(); + Ok(context) + } + + pub fn high_vtl() { + let control: hvdef::hypercall::Control = hvdef::hypercall::Control::new() + .with_code(hvdef::HypercallCode::HvCallVtlCall.0) + .with_rep_count(0); + + // SAFETY: Invoking hypercall per TLFS spec + unsafe { + invoke_hypercall_vtl( + control, + ); + } + } + + pub fn low_vtl() { + let control: hvdef::hypercall::Control = hvdef::hypercall::Control::new() + .with_code(hvdef::HypercallCode::HvCallVtlReturn.0) + .with_rep_count(0); + // SAFETY: Invoking hypercall per TLFS spec + unsafe { + invoke_hypercall_vtl(control); + } + } + + pub fn enable_vtl_protection(&mut self, vp_index: u32, vtl: HvInputVtl) -> Result<(), hvdef::HvError> { + let hvreg = self.get_register(hvdef::HvX64RegisterName::VsmPartitionConfig.into(), Some(vtl))?; + let mut hvreg: HvRegisterVsmPartitionConfig = HvRegisterVsmPartitionConfig::from_bits(hvreg.as_u64()); + hvreg.set_enable_vtl_protection(true); + // hvreg.set_intercept_page(true); + // hvreg.set_default_vtl_protection_mask(0b11); + // hvreg.set_intercept_enable_vtl_protection(true); + let bits = hvreg.into_bits(); + let hvre: HvRegisterValue = hvdef::HvRegisterValue::from(bits); + self.set_register(HvX64RegisterName::VsmPartitionConfig.into(), hvre, Some(vtl)) + } + + #[cfg(target_arch = "x86_64")] + pub fn enable_vp_vtl(&mut self, vp_index: u32, target_vtl : Vtl, vp_context : Option) -> Result<(), hvdef::HvError> { + let header = hvdef::hypercall::EnableVpVtlX64 { + partition_id: hvdef::HV_PARTITION_ID_SELF, + vp_index, + target_vtl: target_vtl.into(), + reserved: [0; 3], + vp_vtl_context: vp_context.unwrap_or( zerocopy::FromZeros::new_zeroed()), + }; + + header.write_to_prefix(self.input_page().buffer.as_mut_slice()).expect("size of enable_vp_vtl header is not correct"); + + let output = self.dispatch_hvcall(hvdef::HypercallCode::HvCallEnableVpVtl, None); + match output.result() { + Ok(()) | Err(hvdef::HvError::VtlAlreadyEnabled) => Ok(()), + err => err, + } + } + + #[cfg(target_arch = "x86_64")] + pub fn start_virtual_processor(&mut self, vp_index: u32, target_vtl : Vtl, vp_context : Option) -> Result<(), hvdef::HvError> { + let header = hvdef::hypercall::StartVirtualProcessorX64 { + partition_id: hvdef::HV_PARTITION_ID_SELF, + vp_index: vp_index, + target_vtl: target_vtl.into(), + vp_context: vp_context.unwrap_or(zerocopy::FromZeros::new_zeroed()), + rsvd0: 0u8, + rsvd1: 0u16, + }; + + header.write_to_prefix(self.input_page().buffer.as_mut_slice()).expect("size of start_virtual_processor header is not correct"); + + let output = self.dispatch_hvcall(hvdef::HypercallCode::HvCallStartVirtualProcessor, None); + match output.result() { + Ok(()) => Ok(()), + err => panic!("Failed to start virtual processor: {:?}", err), + } + } + + pub fn enable_partition_vtl(&mut self, partition_id: u64, target_vtl : Vtl) -> Result<(), hvdef::HvError> { + let flags: EnablePartitionVtlFlags = + EnablePartitionVtlFlags::new() + .with_enable_mbec(false) + .with_enable_supervisor_shadow_stack(false); + + let header = hvdef::hypercall::EnablePartitionVtl { + partition_id, + target_vtl: target_vtl.into(), + flags, + reserved_z0: 0, + reserved_z1: 0, + }; + + let _ = header.write_to_prefix(self.input_page().buffer.as_mut_slice()); + + let output = self.dispatch_hvcall(hvdef::HypercallCode::HvCallEnablePartitionVtl, None); + match output.result() { + Ok(()) | Err(hvdef::HvError::VtlAlreadyEnabled) => Ok(()), + err => err, + } + } + + /// Hypercall to enable VP VTL + #[cfg(target_arch = "aarch64")] + pub fn enable_vp_vtl(&mut self, vp_index: u32) -> Result<(), hvdef::HvError> { + let header = hvdef::hypercall::EnableVpVtlArm64 { + partition_id: hvdef::HV_PARTITION_ID_SELF, + vp_index, + // The VTL value here is just a u8 and not the otherwise usual + // HvInputVtl value. + target_vtl: Vtl::Vtl2.into(), + reserved: [0; 3], + vp_vtl_context: zerocopy::FromZeroes::new_zeroed(), + }; + + header.write_to_prefix(self.input_page().buffer.as_mut_slice()); + + let output = self.dispatch_hvcall(hvdef::HypercallCode::HvCallEnableVpVtl, None); + match output.result() { + Ok(()) | Err(hvdef::HvError::VtlAlreadyEnabled) => Ok(()), + err => err, + } + } + + /// Hypercall to accept vtl2 pages from address start to end with VTL 2 + /// protections and no host visibility + #[cfg_attr(target_arch = "aarch64", allow(dead_code))] + pub fn accept_vtl2_pages( + &mut self, + range: MemoryRange, + memory_type: hvdef::hypercall::AcceptMemoryType, + ) -> Result<(), hvdef::HvError> { + const HEADER_SIZE: usize = size_of::(); + const MAX_INPUT_ELEMENTS: usize = (HV_PAGE_SIZE as usize - HEADER_SIZE) / size_of::(); + + let mut current_page = range.start_4k_gpn(); + while current_page < range.end_4k_gpn() { + let header = hvdef::hypercall::AcceptGpaPages { + partition_id: hvdef::HV_PARTITION_ID_SELF, + page_attributes: hvdef::hypercall::AcceptPagesAttributes::new() + .with_memory_type(memory_type.0) + .with_host_visibility(hvdef::hypercall::HostVisibilityType::PRIVATE) // no host visibility + .with_vtl_set(1 << 2), // applies vtl permissions for vtl 2 + vtl_permission_set: hvdef::hypercall::VtlPermissionSet { + vtl_permission_from_1: [0; hvdef::hypercall::HV_VTL_PERMISSION_SET_SIZE], + }, + gpa_page_base: current_page, + }; + + let remaining_pages = range.end_4k_gpn() - current_page; + let count = remaining_pages.min(MAX_INPUT_ELEMENTS as u64); + + header.write_to_prefix(self.input_page().buffer.as_mut_slice()); + + let output = self.dispatch_hvcall( + hvdef::HypercallCode::HvCallAcceptGpaPages, + Some(count as usize), + ); + + output.result()?; + + current_page += count; + } + + Ok(()) + } + + /// Get the corresponding VP indices from a list of VP hardware IDs (APIC + /// IDs on x64, MPIDR on ARM64). + /// + /// This always queries VTL0, since the hardware IDs are the same across the + /// VTLs in practice, and the hypercall only succeeds for VTL2 once VTL2 has + /// been enabled (which it might not be at this point). + pub fn get_vp_index_from_hw_id( + &mut self, + hw_ids: &[HwId], + output: &mut ArrayVec, + ) -> Result<(), hvdef::HvError> { + let header = hvdef::hypercall::GetVpIndexFromApicId { + partition_id: hvdef::HV_PARTITION_ID_SELF, + target_vtl: 0, + reserved: [0; 7], + }; + + // Split the call up to avoid exceeding the hypercall input/output size limits. + const MAX_PER_CALL: usize = 512; + + for hw_ids in hw_ids.chunks(MAX_PER_CALL) { + header.write_to_prefix(self.input_page().buffer.as_mut_slice()); + hw_ids.write_to_prefix(&mut self.input_page().buffer[header.as_bytes().len()..]); + + // SAFETY: The input header and rep slice are the correct types for this hypercall. + // The hypercall output is validated right after the hypercall is issued. + let r = self.dispatch_hvcall( + hvdef::HypercallCode::HvCallGetVpIndexFromApicId, + Some(hw_ids.len()), + ); + + let n = r.elements_processed() as usize; + + output.extend( + <[u32]>::ref_from_bytes(&mut self.output_page().buffer[..n * 4]) + .unwrap() + .iter() + .copied(), + ); + r.result()?; + assert_eq!(n, hw_ids.len()); + } + + Ok(()) + } +} + +/// The "hardware ID" used for [`HvCall::get_vp_index_from_hw_id`]. This is the +/// APIC ID on x64. +#[cfg(target_arch = "x86_64")] +pub type HwId = u32; + +/// The "hardware ID" used for [`HvCall::get_vp_index_from_hw_id`]. This is the +/// MPIDR on ARM64. +#[cfg(target_arch = "aarch64")] +pub type HwId = u64; \ No newline at end of file diff --git a/opentmk/src/uefi/hypvctx.rs b/opentmk/src/uefi/hypvctx.rs new file mode 100644 index 0000000000..f41725d928 --- /dev/null +++ b/opentmk/src/uefi/hypvctx.rs @@ -0,0 +1,403 @@ +use super::{ + context::{TestCtxTrait, VpExecutor}, + hypercall::HvCall, +}; +use crate::{debuglog, slog::AssertResult}; +use crate::uefi::alloc::ALLOCATOR; +use crate::{ + infolog, + slog::AssertOption, + sync::{Channel, Receiver, Sender}, +}; +use alloc::collections::btree_map::BTreeMap; +use alloc::collections::linked_list::LinkedList; +use alloc::{boxed::Box, vec::Vec}; +use core::alloc::{GlobalAlloc, Layout}; +use core::arch::asm; +use core::ops::Range; +use core::sync::atomic::{AtomicBool, Ordering}; +use hvdef::hypercall::{HvInputVtl, InitialVpContextX64}; +use hvdef::{HvAllArchRegisterName, HvRegisterName, Vtl}; +use memory_range::MemoryRange; +use minimal_rt::arch::msr::{read_msr, write_msr}; +use spin::Mutex; + +const ALIGNMENT: usize = 4096; + +type ComandTable = + BTreeMap, Vtl)>>; +static mut CMD: Mutex = Mutex::new(BTreeMap::new()); + +fn cmdt() -> &'static Mutex { + unsafe { &CMD } +} + +struct VpContext { + #[cfg(target_arch = "x86_64")] + ctx: InitialVpContextX64, + #[cfg(target_arch = "aarch64")] + ctx: InitialVpContextAarch64, +} + +fn register_command_queue(vp_index: u32) { + unsafe { + debuglog!("registering command queue for vp: {}", vp_index); + if CMD.lock().get(&vp_index).is_none() { + CMD.lock().insert(vp_index, LinkedList::new()); + debuglog!("registered command queue for vp: {}", vp_index); + } else { + debuglog!( + "command queue already registered for vp: {}", + vp_index + ); + } + } +} + +pub struct HvTestCtx { + pub hvcall: HvCall, + pub vp_runing: Vec<(u32, (bool, bool))>, + pub my_vp_idx: u32, + senders: Vec<(u64, Sender<(Box, Vtl)>)>, +} + +impl Drop for HvTestCtx { + fn drop(&mut self) { + self.hvcall.uninitialize(); + } +} + +impl TestCtxTrait for HvTestCtx { + fn start_on_vp(&mut self, cmd: VpExecutor) { + let (vp_index, vtl, cmd) = cmd.get(); + let cmd = cmd.expect_assert("error: failed to get command as cmd is none"); + if vtl >= Vtl::Vtl2 { + panic!("error: can't run on vtl2"); + } + let is_vp_running = self.vp_runing.iter_mut().find(|x| x.0 == vp_index); + + if let Some(running_vtl) = is_vp_running { + debuglog!("both vtl0 and vtl1 are running for VP: {:?}", vp_index); + } else { + if vp_index == 0 { + let vp_context = self + .get_default_context() + .expect("error: failed to get default context"); + self.hvcall + .enable_vp_vtl(0, Vtl::Vtl1, Some(vp_context)) + .expect("error: failed to enable vtl1"); + + cmdt().lock().get_mut(&vp_index).unwrap().push_back(( + Box::new(move |ctx| { + ctx.switch_to_low_vtl(); + }), + Vtl::Vtl1, + )); + self.switch_to_high_vtl(); + self.vp_runing.push((vp_index, (true, true))); + } else { + let my_idx = self.my_vp_idx; + cmdt().lock().get_mut(&self.my_vp_idx).unwrap().push_back(( + Box::new(move |ctx| { + ctx.enable_vp_vtl_with_default_context(vp_index, Vtl::Vtl1); + ctx.start_running_vp_with_default_context(VpExecutor::new( + vp_index, + Vtl::Vtl1, + )); + cmdt().lock().get_mut(&vp_index).unwrap().push_back(( + Box::new(move |ctx| { + ctx.set_default_ctx_to_vp(vp_index, Vtl::Vtl0); + }), + Vtl::Vtl1, + )); + ctx.switch_to_low_vtl(); + }), + Vtl::Vtl1, + )); + + self.switch_to_high_vtl(); + self.vp_runing.push((vp_index, (true, true))); + } + } + cmdt() + .lock() + .get_mut(&vp_index) + .unwrap() + .push_back((cmd, vtl)); + if vp_index == self.my_vp_idx && self.hvcall.vtl != vtl { + if vtl == Vtl::Vtl0 { + self.switch_to_low_vtl(); + } else { + self.switch_to_high_vtl(); + } + } + } + + fn queue_command_vp(&mut self, cmd: VpExecutor) { + let (vp_index, vtl, cmd) = cmd.get(); + let cmd = + cmd.expect_assert("error: failed to get command as cmd is none with queue command vp"); + cmdt() + .lock() + .get_mut(&vp_index) + .unwrap() + .push_back((cmd, vtl)); + } + + fn switch_to_high_vtl(&mut self) { + HvCall::high_vtl(); + } + + fn switch_to_low_vtl(&mut self) { + HvCall::low_vtl(); + } + + fn setup_partition_vtl(&mut self, vtl: Vtl) { + self.hvcall + .enable_partition_vtl(hvdef::HV_PARTITION_ID_SELF, vtl) + .expect_assert("Failed to enable VTL1 for the partition"); + infolog!("enabled vtl protections for the partition."); + } + fn setup_interrupt_handler(&mut self) { + crate::arch::interrupt::init(); + } + + fn setup_vtl_protection(&mut self) { + self.hvcall + .enable_vtl_protection(0, HvInputVtl::CURRENT_VTL) + .expect_assert("Failed to enable VTL protection, vtl1"); + + infolog!("enabled vtl protections for the partition."); + } + + fn setup_secure_intercept(&mut self, interrupt_idx: u8) { + let layout = Layout::from_size_align(4096, ALIGNMENT) + .expect_assert("error: failed to create layout for SIMP page"); + + let ptr = unsafe { ALLOCATOR.alloc(layout) }; + let gpn = (ptr as u64) >> 12; + let reg = (gpn << 12) | 0x1; + + unsafe { write_msr(hvdef::HV_X64_MSR_SIMP, reg.into()) }; + infolog!("Successfuly set the SIMP register."); + + let reg = unsafe { read_msr(hvdef::HV_X64_MSR_SINT0) }; + let mut reg: hvdef::HvSynicSint = reg.into(); + reg.set_vector(interrupt_idx); + reg.set_masked(false); + reg.set_auto_eoi(true); + + self.write_msr(hvdef::HV_X64_MSR_SINT0, reg.into()); + infolog!("Successfuly set the SINT0 register."); + } + + fn apply_vtl_protection_for_memory(&mut self, range: Range, vtl: Vtl) { + self.hvcall + .apply_vtl_protections(MemoryRange::new(range), vtl) + .expect_assert("Failed to apply VTL protections"); + } + + fn write_msr(&mut self, msr: u32, value: u64) { + unsafe { write_msr(msr, value) }; + } + + fn read_msr(&mut self, msr: u32) -> u64 { + unsafe { read_msr(msr) } + } + + fn start_running_vp_with_default_context(&mut self, cmd: VpExecutor) { + let (vp_index, vtl, cmd) = cmd.get(); + let vp_ctx = self + .get_default_context() + .expect_assert("error: failed to get default context"); + self.hvcall + .start_virtual_processor(vp_index, vtl, Some(vp_ctx)) + .expect_assert("error: failed to start vp"); + } + + fn set_default_ctx_to_vp(&mut self, vp_index: u32, vtl: Vtl) { + let i: u8 = match vtl { + Vtl::Vtl0 => 0, + Vtl::Vtl1 => 1, + Vtl::Vtl2 => 2, + _ => panic!("error: invalid vtl"), + }; + let vp_context = self + .get_default_context() + .expect_assert("error: failed to get default context"); + self.hvcall + .set_vp_registers( + vp_index, + Some( + HvInputVtl::new() + .with_target_vtl_value(i) + .with_use_target_vtl(true), + ), + Some(vp_context), + ) + .expect_assert("error: failed to set vp registers"); + } + + fn enable_vp_vtl_with_default_context(&mut self, vp_index: u32, vtl: Vtl) { + let vp_ctx = self + .get_default_context() + .expect_assert("error: failed to get default context"); + self.hvcall + .enable_vp_vtl(vp_index, vtl, Some(vp_ctx)) + .expect_assert("error: failed to enable vp vtl"); + } + + #[cfg(target_arch = "x86_64")] + fn set_interupt_idx(&mut self, interrupt_idx: u8, handler: fn()) { + crate::arch::interrupt::set_handler(interrupt_idx, handler); + } + + fn get_vp_count(&self) -> u32 { + let mut result: u32 = 0; + + unsafe { + // Call CPUID with EAX=1, but work around the rbx constraint + asm!( + "push rbx", // Save rbx + "cpuid", // Execute CPUID + "mov {result}, rbx", // Store ebx to our result variable + "pop rbx", // Restore rbx + in("eax") 1u32, // Input: CPUID leaf 1 + out("ecx") _, // Output registers (not used) + out("edx") _, // Output registers (not used) + result = out(reg) result, // Output: result from ebx + options(nomem, nostack) + ); + } + + // Extract logical processor count from bits [23:16] + (result >> 16) & 0xFF + } + + #[cfg(target_arch = "x86_64")] + fn get_register(&mut self, reg: u32) -> u128 { + use hvdef::HvX64RegisterName; + + let reg = HvX64RegisterName(reg); + self.hvcall + .get_register(reg.into(), None) + .expect_assert("error: failed to get register") + .as_u128() + } + + #[cfg(target_arch = "aarch64")] + fn get_register(&mut self, reg: u32) -> u128 { + use hvdef::HvAarch64RegisterName; + + let reg = HvAarch64RegisterName(reg); + self.hvcall + .get_register(reg.into(), None) + .expect_assert("error: failed to get register") + .as_u128() + } + + fn get_current_vp(&self) -> u32 { + self.my_vp_idx + } + + fn get_current_vtl(&self) -> Vtl { + self.hvcall.vtl + } +} + +impl HvTestCtx { + pub const fn new() -> Self { + HvTestCtx { + hvcall: HvCall::new(), + vp_runing: Vec::new(), + my_vp_idx: 0, + senders: Vec::new(), + } + } + + pub fn init(&mut self) { + self.hvcall.initialize(); + let vp_count = self.get_vp_count(); + for i in 0..vp_count { + register_command_queue(i); + } + } + + fn exec_handler() { + let mut ctx = HvTestCtx::new(); + ctx.init(); + let reg = ctx + .hvcall + .get_register(hvdef::HvAllArchRegisterName::VpIndex.into(), None) + .expect("error: failed to get vp index"); + let reg = reg.as_u64(); + ctx.my_vp_idx = reg as u32; + + loop { + let mut vtl: Option = None; + let mut cmd: Option> = None; + + { + let mut d = unsafe { CMD.lock() }; + let mut d = d.get_mut(&ctx.my_vp_idx); + if d.is_some() { + let mut d = d.unwrap(); + if !d.is_empty() { + let (c, v) = d.front().unwrap(); + if *v == ctx.hvcall.vtl { + let (c, v) = d.pop_front().unwrap(); + cmd = Some(c); + } else { + vtl = Some(*v); + } + } + } + } + + if let Some(vtl) = vtl { + if (vtl == Vtl::Vtl0) { + ctx.switch_to_low_vtl(); + } else { + ctx.switch_to_high_vtl(); + } + } + + if let Some(cmd) = cmd { + cmd(&mut ctx); + } + } + } + + #[cfg(target_arch = "x86_64")] + fn get_default_context(&mut self) -> Result { + return self.run_fn_with_current_context(HvTestCtx::exec_handler); + } + + #[cfg(target_arch = "x86_64")] + fn run_fn_with_current_context(&mut self, func: fn()) -> Result { + use super::alloc::SIZE_1MB; + + let mut vp_context: InitialVpContextX64 = self + .hvcall + .get_current_vtl_vp_context() + .expect("Failed to get VTL1 context"); + let stack_layout = Layout::from_size_align(SIZE_1MB, 16) + .expect("Failed to create layout for stack allocation"); + let x = unsafe { ALLOCATOR.alloc(stack_layout) }; + if x.is_null() { + return Err(false); + } + let sz = stack_layout.size(); + let stack_top = x as u64 + sz as u64; + let fn_ptr = func as fn(); + let fn_address = fn_ptr as u64; + vp_context.rip = fn_address; + vp_context.rsp = stack_top; + // print stack range + let stack_range = Range { + start: x as u64, + end: x as u64 + sz as u64, + }; + Ok(vp_context) + } +} diff --git a/opentmk/src/uefi/init.rs b/opentmk/src/uefi/init.rs new file mode 100644 index 0000000000..1f0535d479 --- /dev/null +++ b/opentmk/src/uefi/init.rs @@ -0,0 +1,57 @@ +use core::alloc::{GlobalAlloc, Layout}; + +use uefi::{boot::{exit_boot_services, MemoryType}, guid, println, CStr16, Status}; + +use crate::infolog; + +use super::{alloc::ALLOCATOR}; + + +fn enable_uefi_vtl_protection() { + let mut buf = vec![0u8; 1024]; + let mut str_buff = vec![0u16; 1024]; + let os_loader_indications_key = + CStr16::from_str_with_buf(&"OsLoaderIndications", str_buff.as_mut_slice()).unwrap(); + + let os_loader_indications_result = uefi::runtime::get_variable( + os_loader_indications_key, + &uefi::runtime::VariableVendor(guid!("610b9e98-c6f6-47f8-8b47-2d2da0d52a91")), + buf.as_mut(), + ) + .expect("Failed to get OsLoaderIndications"); + + let mut os_loader_indications = u32::from_le_bytes( + os_loader_indications_result.0[0..4] + .try_into() + .expect("error in output"), + ); + os_loader_indications |= 0x1u32; + + let os_loader_indications = os_loader_indications.to_le_bytes(); + + let _ = uefi::runtime::set_variable( + os_loader_indications_key, + &uefi::runtime::VariableVendor(guid!("610b9e98-c6f6-47f8-8b47-2d2da0d52a91")), + os_loader_indications_result.1, + &os_loader_indications, + ) + .expect("Failed to set OsLoaderIndications"); + + let os_loader_indications_result = uefi::runtime::get_variable( + os_loader_indications_key, + &uefi::runtime::VariableVendor(guid!("610b9e98-c6f6-47f8-8b47-2d2da0d52a91")), + buf.as_mut(), + ) + .expect("Failed to get OsLoaderIndications"); + + let _ = unsafe { exit_boot_services(MemoryType::BOOT_SERVICES_DATA) }; +} + +pub fn init() -> Result<(), Status> { + let r: bool = unsafe { ALLOCATOR.init(2048) }; + if r == false { + return Err(Status::ABORTED); + } + enable_uefi_vtl_protection(); + Ok(()) +} \ No newline at end of file diff --git a/opentmk/src/uefi/mod.rs b/opentmk/src/uefi/mod.rs new file mode 100644 index 0000000000..c09873dbf2 --- /dev/null +++ b/opentmk/src/uefi/mod.rs @@ -0,0 +1,36 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +mod alloc; +mod context; +pub mod hypercall; +mod hypvctx; +pub mod init; +mod rt; +mod tests; + +use crate::slog::{AssertOption, AssertResult}; +use crate::sync::{Channel, Receiver, Sender}; +use crate::uefi::alloc::ALLOCATOR; +use crate::{infolog, tmk_assert}; +use ::alloc::boxed::Box; +use ::alloc::vec::Vec; +use alloc::SIZE_1MB; +use context::{TestCtxTrait, VpExecutor}; +use core::alloc::{GlobalAlloc, Layout}; +use core::cell::RefCell; +use core::ops::Range; +use core::sync::atomic::{AtomicI32, Ordering}; +use hvdef::hypercall::HvInputVtl; +use hvdef::Vtl; +use hypvctx::HvTestCtx; +use init::init; +use uefi::entry; +use uefi::Status; + +#[entry] +fn uefi_main() -> Status { + init().expect_assert("Failed to initialize environment"); + tests::run_test(); + loop {} +} diff --git a/opentmk/src/uefi/rt.rs b/opentmk/src/uefi/rt.rs new file mode 100644 index 0000000000..4868ad542d --- /dev/null +++ b/opentmk/src/uefi/rt.rs @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +//! Runtime support for the UEFI application environment. + +#![cfg(target_os = "uefi")] +// UNSAFETY: Raw assembly needed for panic handling to abort. +#![expect(unsafe_code)] + +use crate::arch::serial::{Serial, InstrIoAccess}; +use core::fmt::Write; +use crate::slog; +use crate::sync::Mutex; + +#[panic_handler] +fn panic_handler(panic: &core::panic::PanicInfo<'_>) -> ! { + + let io = InstrIoAccess {}; + let mut ser = Mutex::new(Serial::new(io)); + crate::errorlog!("Panic at runtime: {}", panic); + crate::errorlog!("Could not shut down... falling back to invoking an undefined instruction"); + loop{} +} diff --git a/opentmk/src/uefi/tests/hv_misc.rs b/opentmk/src/uefi/tests/hv_misc.rs new file mode 100644 index 0000000000..3bda306d70 --- /dev/null +++ b/opentmk/src/uefi/tests/hv_misc.rs @@ -0,0 +1,136 @@ +// WIP : This test is not yet complete and is not expected to pass. +// +// This test is to verify that the VTL protections are working as expected. +// The stack values in VTL0 are changing after interrupt handling in VTL1. +use crate::slog::{AssertOption, AssertResult}; +use crate::sync::{Channel, Receiver, Sender}; +use crate::uefi::alloc::{ALLOCATOR, SIZE_1MB}; +use crate::uefi::{context, hypvctx}; +use crate::{infolog, tmk_assert}; +use ::alloc::boxed::Box; +use alloc::sync::Arc; +use ::alloc::vec::Vec; +use context::{TestCtxTrait, VpExecutor}; +use hypvctx::HvTestCtx; +use core::alloc::{GlobalAlloc, Layout}; +use core::arch::asm; +use core::cell::RefCell; +use core::ops::Range; +use core::sync::atomic::{AtomicI32, Ordering}; +use hvdef::hypercall::HvInputVtl; +use hvdef::{HvAllArchRegisterName, HvRegisterVsmVpStatus, HvX64RegisterName, Vtl}; +use uefi::entry; +use uefi::Status; + +static mut HEAPX: RefCell<*mut u8> = RefCell::new(0 as *mut u8); +static mut CON: AtomicI32 = AtomicI32::new(0); + +pub fn exec(ctx: &mut hypvctx::HvTestCtx ) { + infolog!("ctx ptr: {:p}", &ctx as *const _); + + let mut vp_count = ctx.get_vp_count(); + tmk_assert!(vp_count == 8, "vp count should be 8"); + + ctx.setup_interrupt_handler(); + + infolog!("set intercept handler successfully!"); + + ctx.setup_partition_vtl(Vtl::Vtl1); + + ctx.start_on_vp( + VpExecutor::new(0, Vtl::Vtl1).command(move |ctx: &mut dyn TestCtxTrait| { + infolog!("successfully started running VTL1 on vp0."); + ctx.setup_secure_intercept(0x30); + ctx.set_interupt_idx(0x30, || { + infolog!("interrupt fired!"); + + let mut hv_test_ctx = HvTestCtx::new(); + hv_test_ctx.init(); + + let c = hv_test_ctx.get_register(HvAllArchRegisterName::VsmVpStatus.0); + + let cp = HvRegisterVsmVpStatus::from_bits(c as u64); + + infolog!("VSM VP Status: {:?}", cp); + + infolog!("interrupt handled!"); + }); + + let layout = + Layout::from_size_align(SIZE_1MB, 4096).expect("msg: failed to create layout"); + let ptr = unsafe { ALLOCATOR.alloc(layout) }; + infolog!("allocated some memory in the heap from vtl1"); + unsafe { + let mut z = HEAPX.borrow_mut(); + *z = ptr; + *ptr.add(10) = 0xAA; + } + + let size = layout.size(); + ctx.setup_vtl_protection(); + + infolog!("enabled vtl protections for the partition."); + + let range = Range { + start: ptr as u64, + end: ptr as u64 + size as u64, + }; + + ctx.apply_vtl_protection_for_memory(range, Vtl::Vtl1); + + infolog!("moving to vtl0 to attempt to read the heap memory"); + + ctx.switch_to_low_vtl(); + }), + ); + + ctx.queue_command_vp(VpExecutor::new(0, Vtl::Vtl1).command(move |ctx| { + infolog!("successfully started running VTL1 on vp0."); + ctx.switch_to_low_vtl(); + })); + infolog!("ctx ptr: {:p}", &ctx as *const _); + + let mut l = 0u64; + unsafe { asm!("mov {}, rsp", out(reg) l) }; + infolog!("rsp: 0x{:x}", l); + unsafe { + infolog!("Attempting to read heap memory from vtl0"); + let heapx = *HEAPX.borrow(); + let val = *(heapx.add(10)); + infolog!( + "reading mutated heap memory from vtl0(it should not be 0xAA): 0x{:x}", + val + ); + tmk_assert!( + val != 0xAA, + "heap memory should not be accessible from vtl0" + ); + } + + unsafe { asm!("mov {}, rsp", out(reg) l) }; + infolog!("rsp: 0x{:x}", l); + + // let (mut tx, mut rx) = Channel::new(1); + // { + // let mut tx = tx.clone(); + // ctx.start_on_vp(VpExecutor::new(2, Vtl::Vtl0).command( + // move |ctx: &mut dyn TestCtxTrait| { + // infolog!("Hello form vtl0 on vp2!"); + // tx.send(()); + // }, + // )); + // } + infolog!("ctx ptr: {:p}", &ctx as *const _); + let c = ctx.get_vp_count(); + + tmk_assert!(c == 8, "vp count should be 8"); + + // rx.recv(); + + infolog!("we are in vtl0 now!"); + infolog!("we reached the end of the test"); + loop { + + } + +} \ No newline at end of file diff --git a/opentmk/src/uefi/tests/hv_processor.rs b/opentmk/src/uefi/tests/hv_processor.rs new file mode 100644 index 0000000000..b71a346065 --- /dev/null +++ b/opentmk/src/uefi/tests/hv_processor.rs @@ -0,0 +1,75 @@ +use alloc::vec::Vec; +use hvdef::Vtl; + +use crate::{ + criticallog, infolog, sync::{self, Mutex}, tmk_assert, uefi::context::{TestCtxTrait, VpExecutor} +}; + +static VP_RUNNING: Mutex> = Mutex::new(Vec::new()); + +pub fn exec(ctx: &mut dyn TestCtxTrait) { + ctx.setup_interrupt_handler(); + ctx.setup_partition_vtl(Vtl::Vtl1); + + let vp_count = ctx.get_vp_count(); + tmk_assert!(vp_count == 8, "vp count should be 8"); + + // Testing BSP VTL Bringup + { + let (mut tx, mut rx) = crate::sync::Channel::new().split(); + ctx.start_on_vp(VpExecutor::new(0, Vtl::Vtl1).command( + move |ctx: &mut dyn TestCtxTrait| { + let vp = ctx.get_current_vp(); + infolog!("vp: {}", vp); + tmk_assert!(vp == 0, "vp should be equal to 0"); + + let vtl = ctx.get_current_vtl(); + infolog!("vtl: {:?}", vtl); + tmk_assert!(vtl == Vtl::Vtl1, "vtl should be Vtl1 for BSP"); + tx.send(()); + ctx.switch_to_low_vtl(); + }, + )); + rx.recv(); + } + + for i in 1..vp_count { + // Testing VTL1 + { + let (mut tx, mut rx) = crate::sync::Channel::new().split(); + ctx.start_on_vp(VpExecutor::new(i, Vtl::Vtl1).command( + move |ctx: &mut dyn TestCtxTrait| { + let vp = ctx.get_current_vp(); + infolog!("vp: {}", vp); + tmk_assert!(vp == i, format!("vp should be equal to {}", i)); + + let vtl = ctx.get_current_vtl(); + infolog!("vtl: {:?}", vtl); + tmk_assert!(vtl == Vtl::Vtl1, format!("vtl should be Vtl0 for VP {}", i)); + tx.send(()); + }, + )); + rx.clone().recv(); + } + + // Testing VTL0 + { + let (mut tx, mut rx) = crate::sync::Channel::new().split(); + ctx.start_on_vp(VpExecutor::new(i, Vtl::Vtl0).command( + move |ctx: &mut dyn TestCtxTrait| { + let vp = ctx.get_current_vp(); + infolog!("vp: {}", vp); + tmk_assert!(vp == i, format!("vp should be equal to {}", i)); + + let vtl = ctx.get_current_vtl(); + infolog!("vtl: {:?}", vtl); + tmk_assert!(vtl == Vtl::Vtl0, format!("vtl should be Vtl0 for VP {}", i)); + tx.send(()); + }, + )); + rx.clone().recv(); + } + } + + criticallog!("All VPs have been tested"); +} diff --git a/opentmk/src/uefi/tests/mod.rs b/opentmk/src/uefi/tests/mod.rs new file mode 100644 index 0000000000..51686205e2 --- /dev/null +++ b/opentmk/src/uefi/tests/mod.rs @@ -0,0 +1,14 @@ +use alloc::sync::Arc; + +use super::hypvctx::HvTestCtx; + +pub mod hv_processor; +pub mod hv_misc; + +pub fn run_test() { + let mut ctx = HvTestCtx::new(); + ctx.init(); + + + hv_processor::exec(&mut ctx); +} \ No newline at end of file diff --git a/xtask/src/tasks/guest_test/uefi/gpt_efi_disk.rs b/xtask/src/tasks/guest_test/uefi/gpt_efi_disk.rs index fb4641a93b..862db7e55c 100644 --- a/xtask/src/tasks/guest_test/uefi/gpt_efi_disk.rs +++ b/xtask/src/tasks/guest_test/uefi/gpt_efi_disk.rs @@ -22,7 +22,7 @@ pub fn create_gpt_efi_disk(out_img: &Path, with_files: &[(&Path, &Path)]) -> Res )); } - let disk_size = 1024 * 1024 * 32; // 32MB disk should be enough for our tests + let disk_size = 1024 * 1024 * 512; // 32MB disk should be enough for our tests let num_sectors = disk_size / SECTOR_SIZE; let mut disk = vec![0; num_sectors * SECTOR_SIZE]; From 351199b347fad68e26c7bdf0e786e663be88752f Mon Sep 17 00:00:00 2001 From: Mayank Kumar Date: Mon, 21 Apr 2025 18:43:11 +0000 Subject: [PATCH 02/23] feat: added docs --- opentmk/src/uefi/hypercall.rs | 80 ++++++++++++++++++++++++++++++----- opentmk/src/uefi/hypvctx.rs | 76 ++++++++++++++++++++++++++++++--- opentmk/src/uefi/mod.rs | 2 +- opentmk/src/uefi/rt.rs | 14 +++--- 4 files changed, 146 insertions(+), 26 deletions(-) diff --git a/opentmk/src/uefi/hypercall.rs b/opentmk/src/uefi/hypercall.rs index 3ffe783e70..8e3aa61312 100644 --- a/opentmk/src/uefi/hypercall.rs +++ b/opentmk/src/uefi/hypercall.rs @@ -11,7 +11,6 @@ use hvdef::HvRegisterGuestVsmPartitionConfig; use hvdef::HvRegisterValue; use hvdef::HvRegisterVsmPartitionConfig; use hvdef::HvX64RegisterName; -use minimal_rt::arch::hypercall::{invoke_hypercall_vtl}; use zerocopy::FromZeros; use core::arch; use core::cell::RefCell; @@ -21,7 +20,7 @@ use hvdef::hypercall::HvInputVtl; use hvdef::Vtl; use hvdef::HV_PAGE_SIZE; use memory_range::MemoryRange; -use minimal_rt::arch::hypercall::invoke_hypercall; +use minimal_rt::arch::hypercall::{invoke_hypercall, HYPERCALL_PAGE}; use zerocopy::IntoBytes; use zerocopy::FromBytes; @@ -32,6 +31,19 @@ struct HvcallPage { } +pub fn invoke_hypercall_vtl(control: hvdef::hypercall::Control) { + // SAFETY: the caller guarantees the safety of this operation. + unsafe { + core::arch::asm! { + "call {hypercall_page}", + hypercall_page = sym HYPERCALL_PAGE, + inout("rcx") u64::from(control) => _, + in("rdx") 0, + in("rax") 0, + } + } +} + impl HvcallPage { pub const fn new() -> Self { HvcallPage { @@ -52,6 +64,59 @@ impl HvcallPage { } /// Provides mechanisms to invoke hypercalls within the boot shim. +/// +/// This module defines the `HvCall` struct and associated methods to interact with +/// hypervisor functionalities through hypercalls. It includes utilities for managing +/// hypercall pages, setting and getting virtual processor (VP) registers, enabling +/// VTL (Virtual Trust Levels), and applying memory protections. +/// +/// # Overview +/// +/// - **Hypercall Pages**: Manages page-aligned buffers for hypercall input and output. +/// - **VP Registers**: Provides methods to set and get VP registers. +/// - **VTL Management**: Includes methods to enable VTLs, apply VTL protections, and +/// manage VTL-specific operations. +/// - **Memory Protections**: Supports applying VTL protections and accepting VTL2 pages. +/// +/// # Safety +/// +/// Many methods in this module involve unsafe operations, such as invoking hypercalls +/// or interacting with low-level memory structures. The caller must ensure the safety +/// of these operations by adhering to the requirements of the hypervisor and the +/// underlying architecture. +/// +/// # Usage +/// +/// This module is designed for use in single-threaded environments, such as the boot +/// shim. It uses static buffers for hypercall pages, so it is not thread-safe. +/// +/// # Features +/// +/// - **Architecture-Specific Implementations**: Some methods are only available for +/// specific architectures (e.g., `x86_64` or `aarch64`). +/// - **Error Handling**: Methods return `Result` types to handle hypervisor errors. +/// +/// # Examples +/// +/// ```rust +/// let mut hv_call = HvCall::new(); +/// hv_call.initialize(); +/// let vtl = hv_call.vtl(); +/// println!("Current VTL: {:?}", vtl); +/// hv_call.uninitialize(); +/// ``` +/// +/// # Modules and Types +/// +/// - `HvCall`: Main struct for managing hypercalls. +/// - `HvcallPage`: Struct for page-aligned buffers. +/// - `HwId`: Type alias for hardware IDs (APIC ID on `x86_64`, MPIDR on `aarch64`). +/// +/// # Notes +/// +/// - This module assumes the presence of a hypervisor that supports the required +/// hypercalls. +/// - The boot shim must ensure that hypercalls are invoked in a valid context. /// Internally uses static buffers for the hypercall page, the input /// page, and the output page, so this should not be used in any /// multi-threaded capacity (which the boot shim currently is not). @@ -62,13 +127,6 @@ pub struct HvCall { output_page: HvcallPage, } -/// Returns an [`HvCall`] instance. -/// -/// Panics if another instance is already in use. -// #[track_caller] -// pub fn hvcall() -> core::cell::RefMut<'static, HvCall> { -// HVCALL.borrow_mut() -// } #[expect(unsafe_code)] impl HvCall { @@ -384,7 +442,7 @@ impl HvCall { Ok(context) } - pub fn high_vtl() { + pub fn vtl_call() { let control: hvdef::hypercall::Control = hvdef::hypercall::Control::new() .with_code(hvdef::HypercallCode::HvCallVtlCall.0) .with_rep_count(0); @@ -397,7 +455,7 @@ impl HvCall { } } - pub fn low_vtl() { + pub fn vtl_return() { let control: hvdef::hypercall::Control = hvdef::hypercall::Control::new() .with_code(hvdef::HypercallCode::HvCallVtlReturn.0) .with_rep_count(0); diff --git a/opentmk/src/uefi/hypvctx.rs b/opentmk/src/uefi/hypvctx.rs index f41725d928..79e74cc057 100644 --- a/opentmk/src/uefi/hypvctx.rs +++ b/opentmk/src/uefi/hypvctx.rs @@ -2,8 +2,8 @@ use super::{ context::{TestCtxTrait, VpExecutor}, hypercall::HvCall, }; -use crate::{debuglog, slog::AssertResult}; use crate::uefi::alloc::ALLOCATOR; +use crate::{debuglog, slog::AssertResult}; use crate::{ infolog, slog::AssertOption, @@ -46,10 +46,7 @@ fn register_command_queue(vp_index: u32) { CMD.lock().insert(vp_index, LinkedList::new()); debuglog!("registered command queue for vp: {}", vp_index); } else { - debuglog!( - "command queue already registered for vp: {}", - vp_index - ); + debuglog!("command queue already registered for vp: {}", vp_index); } } } @@ -67,6 +64,71 @@ impl Drop for HvTestCtx { } } +/// Implementation of the `TestCtxTrait` for the `HvTestCtx` structure, providing +/// various methods to manage and interact with virtual processors (VPs) and +/// Virtual Trust Levels (VTLs) in a hypervisor context. +/// +/// # Methods +/// +/// - `start_on_vp(&mut self, cmd: VpExecutor)`: +/// Starts a virtual processor (VP) on a specified VTL. Handles enabling VTLs, +/// switching between high and low VTLs, and managing VP execution contexts. +/// +/// - `queue_command_vp(&mut self, cmd: VpExecutor)`: +/// Queues a command for a specific VP and VTL. +/// +/// - `switch_to_high_vtl(&mut self)`: +/// Switches the current execution context to a high VTL. +/// +/// - `switch_to_low_vtl(&mut self)`: +/// Switches the current execution context to a low VTL. +/// +/// - `setup_partition_vtl(&mut self, vtl: Vtl)`: +/// Configures the partition to enable a specified VTL. +/// +/// - `setup_interrupt_handler(&mut self)`: +/// Sets up the interrupt handler for the architecture. +/// +/// - `setup_vtl_protection(&mut self)`: +/// Enables VTL protection for the current partition. +/// +/// - `setup_secure_intercept(&mut self, interrupt_idx: u8)`: +/// Configures secure intercept for a specified interrupt index, including +/// setting up the SIMP and SINT0 registers. +/// +/// - `apply_vtl_protection_for_memory(&mut self, range: Range, vtl: Vtl)`: +/// Applies VTL protections to a specified memory range. +/// +/// - `write_msr(&mut self, msr: u32, value: u64)`: +/// Writes a value to a specified Model-Specific Register (MSR). +/// +/// - `read_msr(&mut self, msr: u32) -> u64`: +/// Reads the value of a specified Model-Specific Register (MSR). +/// +/// - `start_running_vp_with_default_context(&mut self, cmd: VpExecutor)`: +/// Starts a VP with the default execution context. +/// +/// - `set_default_ctx_to_vp(&mut self, vp_index: u32, vtl: Vtl)`: +/// Sets the default execution context for a specified VP and VTL. +/// +/// - `enable_vp_vtl_with_default_context(&mut self, vp_index: u32, vtl: Vtl)`: +/// Enables a VTL for a specified VP using the default execution context. +/// +/// - `set_interupt_idx(&mut self, interrupt_idx: u8, handler: fn())`: +/// Sets an interrupt handler for a specified interrupt index. (x86_64 only) +/// +/// - `get_vp_count(&self) -> u32`: +/// Retrieves the number of virtual processors available on the system. +/// +/// - `get_register(&mut self, reg: u32) -> u128`: +/// Retrieves the value of a specified register. Supports both x86_64 and +/// aarch64 architectures. +/// +/// - `get_current_vp(&self) -> u32`: +/// Returns the index of the current virtual processor. +/// +/// - `get_current_vtl(&self) -> Vtl`: +/// Returns the current Virtual Trust Level (VTL). impl TestCtxTrait for HvTestCtx { fn start_on_vp(&mut self, cmd: VpExecutor) { let (vp_index, vtl, cmd) = cmd.get(); @@ -145,11 +207,11 @@ impl TestCtxTrait for HvTestCtx { } fn switch_to_high_vtl(&mut self) { - HvCall::high_vtl(); + HvCall::vtl_call(); } fn switch_to_low_vtl(&mut self) { - HvCall::low_vtl(); + HvCall::vtl_return(); } fn setup_partition_vtl(&mut self, vtl: Vtl) { diff --git a/opentmk/src/uefi/mod.rs b/opentmk/src/uefi/mod.rs index c09873dbf2..08cadaa5b5 100644 --- a/opentmk/src/uefi/mod.rs +++ b/opentmk/src/uefi/mod.rs @@ -32,5 +32,5 @@ use uefi::Status; fn uefi_main() -> Status { init().expect_assert("Failed to initialize environment"); tests::run_test(); - loop {} + Status::SUCCESS } diff --git a/opentmk/src/uefi/rt.rs b/opentmk/src/uefi/rt.rs index 4868ad542d..1623e2f960 100644 --- a/opentmk/src/uefi/rt.rs +++ b/opentmk/src/uefi/rt.rs @@ -7,17 +7,17 @@ // UNSAFETY: Raw assembly needed for panic handling to abort. #![expect(unsafe_code)] -use crate::arch::serial::{Serial, InstrIoAccess}; -use core::fmt::Write; +use crate::arch::serial::{InstrIoAccess, Serial}; use crate::slog; use crate::sync::Mutex; +use core::arch::asm; +use core::fmt::Write; #[panic_handler] fn panic_handler(panic: &core::panic::PanicInfo<'_>) -> ! { - - let io = InstrIoAccess {}; - let mut ser = Mutex::new(Serial::new(io)); crate::errorlog!("Panic at runtime: {}", panic); - crate::errorlog!("Could not shut down... falling back to invoking an undefined instruction"); - loop{} + unsafe { + asm!("int 8H"); + } + loop {} } From 3e1673e74f6ad7bc1afab39cb5a23c88c4a31d1c Mon Sep 17 00:00:00 2001 From: Mayank Kumar Date: Mon, 21 Apr 2025 18:50:56 +0000 Subject: [PATCH 03/23] refactor(opentmk): interrupt handler doesn't need to save state for all the registers --- .../arch/x86_64/interrupt_handler_register.rs | 81 +------------------ 1 file changed, 1 insertion(+), 80 deletions(-) diff --git a/opentmk/src/arch/x86_64/interrupt_handler_register.rs b/opentmk/src/arch/x86_64/interrupt_handler_register.rs index c015edb666..d2433c780b 100644 --- a/opentmk/src/arch/x86_64/interrupt_handler_register.rs +++ b/opentmk/src/arch/x86_64/interrupt_handler_register.rs @@ -9,86 +9,7 @@ static COMMON_HANDLER_MUTEX: Mutex<()> = Mutex::new(()); macro_rules! create_fn { ($name:ident, $i: expr) => { extern "x86-interrupt" fn $name(stack_frame: InterruptStackFrame) { - unsafe { - asm!(r#" - push rax - push rbx - push rcx - push rdx - push rsi - push rdi - push rbp - push rsp - push r8 - push r9 - push r10 - push r11 - push r12 - push r13 - push r14 - push r15 - - sub rsp, 256 - movups [rsp + 16 * 0], xmm0 - movups [rsp + 16 * 1], xmm1 - movups [rsp + 16 * 2], xmm2 - movups [rsp + 16 * 3], xmm3 - movups [rsp + 16 * 4], xmm4 - movups [rsp + 16 * 5], xmm5 - movups [rsp + 16 * 6], xmm6 - movups [rsp + 16 * 7], xmm7 - movups [rsp + 16 * 8], xmm8 - movups [rsp + 16 * 9], xmm9 - movups [rsp + 16 * 10], xmm10 - movups [rsp + 16 * 11], xmm11 - movups [rsp + 16 * 12], xmm12 - movups [rsp + 16 * 13], xmm13 - movups [rsp + 16 * 14], xmm14 - movups [rsp + 16 * 15], xmm15 -"#); - -unsafe { (COMMON_HANDLER)(stack_frame, $i) }; - -asm!(r#" - - - movups xmm0, [rsp + 16 * 0] - movups xmm1, [rsp + 16 * 1] - movups xmm2, [rsp + 16 * 2] - movups xmm3, [rsp + 16 * 3] - movups xmm4, [rsp + 16 * 4] - movups xmm5, [rsp + 16 * 5] - movups xmm6, [rsp + 16 * 6] - movups xmm7, [rsp + 16 * 7] - movups xmm8, [rsp + 16 * 8] - movups xmm9, [rsp + 16 * 9] - movups xmm10, [rsp + 16 * 10] - movups xmm11, [rsp + 16 * 11] - movups xmm12, [rsp + 16 * 12] - movups xmm13, [rsp + 16 * 13] - movups xmm14, [rsp + 16 * 14] - movups xmm15, [rsp + 16 * 15] - add rsp, 16 * 16 - - pop r15 - pop r14 - pop r13 - pop r12 - pop r11 - pop r10 - pop r9 - pop r8 - pop rsp - pop rbp - pop rdi - pop rsi - pop rdx - pop rcx - pop rbx - pop rax - - "#); - } + unsafe { (COMMON_HANDLER)(stack_frame, $i) }; } }; } From 056bf7dd8a78cda67986a6df4d805b11d8d78f6f Mon Sep 17 00:00:00 2001 From: Mayank Kumar Date: Mon, 21 Apr 2025 19:10:01 +0000 Subject: [PATCH 04/23] reafctor(opentmk): remove dead code --- opentmk/src/arch/aarch64/mod.rs | 3 +- opentmk/src/arch/aarch64/serial.rs | 240 ------------------------- opentmk/src/slog.rs | 2 +- opentmk/src/sync.rs | 160 +---------------- opentmk/src/uefi/tests/hv_processor.rs | 2 - opentmk/src/uefi/tests/mod.rs | 2 - 6 files changed, 6 insertions(+), 403 deletions(-) delete mode 100644 opentmk/src/arch/aarch64/serial.rs diff --git a/opentmk/src/arch/aarch64/mod.rs b/opentmk/src/arch/aarch64/mod.rs index 594be8b42a..c9ab11a58c 100644 --- a/opentmk/src/arch/aarch64/mod.rs +++ b/opentmk/src/arch/aarch64/mod.rs @@ -1,2 +1,3 @@ +pub use minimal_rt::arch::aarch64::serial; + pub mod hypercall; -pub mod serial; \ No newline at end of file diff --git a/opentmk/src/arch/aarch64/serial.rs b/opentmk/src/arch/aarch64/serial.rs deleted file mode 100644 index f68e6cb200..0000000000 --- a/opentmk/src/arch/aarch64/serial.rs +++ /dev/null @@ -1,240 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -//! aarch64 MMIO-based serial port, UART PL011. -//! -//! Used for debug output. Follows -//! [PrimeCell UART (PL011) Technical Reference Manual](https://developer.arm.com/documentation/ddi0183/g/) -//! -//! PL011 Registers: -//! -//! Offset Name Type Reset Bits Description -//! ---------------------------------------------------------------------- -//! 0x000 UARTDR RW 0x--- 12/8 Data Register -//! 0x004 UARTRSR/UARTECR RW 0x0 4/0 Receive Status Register/Error Clear Register -//! 0x018 UARTFR RO 0b-10010--- 9 Flag Register -//! 0x020 UARTILPR RW 0x00 8 IrDA Low-Power Counter Register -//! 0x024 UARTIBRD RW 0x0000 16 Integer Baud Rate Register -//! 0x028 UARTFBRD RW 0x00 6 Fractional Baud Rate Register -//! 0x02C UARTLCR_H RW 0x00 8 Line Control Register -//! 0x030 UARTCR RW 0x0300 16 Control Register -//! 0x034 UARTIFLS RW 0x12 6 Interrupt FIFO Level Select Register -//! 0x038 UARTIMSC RW 0x000 11 Interrupt Mask Set/Clear Register -//! 0x03C UARTRIS RO 0x00- 11 Raw Interrupt Status Register -//! 0x040 UARTMIS RO 0x00- 11 Masked Interrupt Status Register -//! 0x044 UARTICR WO - 11 Interrupt Clear Register -//! 0x048 UARTDMACR RW 0x00 3 DMA Control Register -//! 0xFE0 UARTPeriphID0 RO 0x11 8 UARTPeriphID0 Register -//! 0xFE4 UARTPeriphID1 RO 0x10 8 UARTPeriphID1 Register -//! 0xFE8 UARTPeriphID2 RO 0x_4a 8 UARTPeriphID2 Register -//! 0xFEC UARTPeriphID3 RO 0x00 8 UARTPeriphID3 Register -//! 0xFF0 UARTPCellID0 RO 0x0D 8 UARTPCellID0 Register -//! 0xFF4 UARTPCellID1 RO 0xF0 8 UARTPCellID1 Register -//! 0xFF8 UARTPCellID2 RO 0x05 8 UARTPCellID2 Register -//! 0xFFC UARTPCellID3 RO 0xB1 8 UARTPCellID3 Register - -#![allow(dead_code)] - -use core::hint::spin_loop; -use core::sync::atomic::AtomicBool; -use core::sync::atomic::Ordering; - -#[derive(Debug, Clone, Copy)] -#[repr(u16)] -enum Pl011Register { - /// Data Register - Dr = 0x000, - /// Receive Status Register/Error Clear Register - RsrOrEcr = 0x004, - /// Flag register - Fr = 0x018, - /// Integer Baud Rate Register - Ibrd = 0x024, - /// Fractional Baud Rate Register - Fbrd = 0x028, - /// Line Control Register - LcrHigh = 0x02c, - /// Control Register - Cr = 0x030, - /// Masked Interrupt Status Register - Imsc = 0x038, - /// Interrupt Clear Register - Icr = 0x044, - /// DMA Control Register - DmaCr = 0x048, - /// UARTPeriphID0 Register - PeriphID0 = 0xFE0, - /// UARTPeriphID1 Register - PeriphID1 = 0xFE4, - /// UARTPeriphID2 Register - PeriphID2 = 0xFE8, - /// UARTPeriphID3 Register - PeriphID3 = 0xFEC, - /// UARTPCellID0 Register - PCellID0 = 0xFF0, - /// UARTPCellID1 Register - PCellID1 = 0xFF4, - /// UARTPCellID2 Register - PCellID2 = 0xFF8, - /// UARTPCellID3 Register - PCellID3 = 0xFFC, -} - -const CR_RX_ENABLE: u32 = 0x200; -const CR_TX_ENABLE: u32 = 0x100; -const CR_UART_ENABLE: u32 = 1; -const LCR_H_FIFO_EN: u32 = 0x10; -const LCR_H_8BITS: u32 = 0x60; - -const _FR_TX_EMPTY: u32 = 0x080; -const _FR_RX_FULL: u32 = 0x040; -const FR_TX_FULL: u32 = 0x020; -const _FR_RX_EMPTY: u32 = 0x010; -const FR_BUSY: u32 = 0x008; - -/// The Hyper-V PL011 host emulated PL011's are found at these -/// base addresses. Should come from ACPI or DT of course yet -/// due to having been hardcoded in some products makes that -/// virtually constants. -const PL011_HYPER_V_BASE_1: u64 = 0xeffec000; -const _PL011_HYPER_V_BASE_2: u64 = 0xeffeb000; -const PL011_BASE: u64 = PL011_HYPER_V_BASE_1; - -fn read_register(reg: Pl011Register) -> u32 { - // SAFETY: using the PL011 MMIO address. - unsafe { core::ptr::read_volatile((PL011_BASE + reg as u64) as *const u32) } -} - -fn write_register(reg: Pl011Register, val: u32) { - // SAFETY: using the PL011 MMIO address. - unsafe { - core::ptr::write_volatile((PL011_BASE + reg as u64) as *mut u32, val); - } -} - -fn cell_id() -> u32 { - // This can easily be rewritten employing - // bare arithmetic yet the compiler does a very good job - // so using the domain abstractions. - [ - Pl011Register::PCellID3, - Pl011Register::PCellID2, - Pl011Register::PCellID1, - Pl011Register::PCellID0, - ] - .iter() - .fold(0, |id_running, &r| { - id_running.wrapping_shl(8) | (read_register(r) as u8 as u32) - }) -} - -fn periph_id() -> u32 { - // This can easily be rewritten employing - // bare arithmetic yet the compiler does a very good job - // so using the domain abstractions. - [ - Pl011Register::PeriphID3, - Pl011Register::PeriphID2, - Pl011Register::PeriphID1, - Pl011Register::PeriphID0, - ] - .iter() - .fold(0, |id_running, &r| { - id_running.wrapping_shl(8) | (read_register(r) as u8 as u32) - }) -} - -fn poll_tx_not_full() { - while read_register(Pl011Register::Fr) & FR_TX_FULL != 0 { - spin_loop(); - } -} - -fn poll_not_busy() { - while read_register(Pl011Register::Fr) & FR_BUSY != 0 { - spin_loop(); - } -} - -/// Disables the functional parts of the UART, drains FIFOs, -/// sets baud rate and enables the UART in the polling mode. -/// Might be geared towards the real hardware more than the virtual one. -/// Works with qemu and Hyper-V. -fn reset_and_init() { - // Mask interrupts (lower 11 bits) - write_register(Pl011Register::Imsc, 0x7ff); - // Clear interrupts (lower 11 bits) - write_register(Pl011Register::Icr, 0x7ff); - // Disable DMA on Rx and Tx - write_register(Pl011Register::DmaCr, 0x0); - - // Leave Rx and Tx enabled to drain FIFOs. - write_register(Pl011Register::Cr, CR_RX_ENABLE | CR_TX_ENABLE); - read_register(Pl011Register::Cr); // wait - read_register(Pl011Register::Cr); // wait - poll_not_busy(); - - // Disable Rx, Tx, and UART. - write_register(Pl011Register::Cr, 0x00000000); - - // Set integer and fractional parts of the baud rate, - // hardcoded for now - write_register(Pl011Register::Fbrd, 0x00000004); - write_register(Pl011Register::Ibrd, 0x00000027); - // The UARTLCR_H, UARTIBRD, and UARTFBRD registers form the single 30-bit - // wide UARTLCR Register that is updated on a single write strobe generated by a - // UARTLCR_H write - write_register(Pl011Register::LcrHigh, LCR_H_FIFO_EN | LCR_H_8BITS); - - // Clear the errors - write_register(Pl011Register::RsrOrEcr, 0); - - // Enable Tx and Rx - write_register(Pl011Register::Cr, CR_RX_ENABLE | CR_TX_ENABLE); - read_register(Pl011Register::Cr); // wait - read_register(Pl011Register::Cr); // wait - poll_not_busy(); - - // Enable UART - write_register( - Pl011Register::Cr, - CR_RX_ENABLE | CR_TX_ENABLE | CR_UART_ENABLE, - ); - poll_not_busy(); -} - -/// A PL011 serial port. -pub struct Serial; - -static SUPPORTED: AtomicBool = AtomicBool::new(false); - -impl Serial { - /// Initializes the serial port. - pub fn init() -> Serial { - const SUPPORTED_PL011_CELLS: &[u32] = &[0xB105_F00D]; - - let cell_id = cell_id(); - let supported = SUPPORTED_PL011_CELLS.contains(&cell_id); - if supported { - reset_and_init(); - } - SUPPORTED.store(supported, Ordering::Relaxed); - - Self - } -} - -impl core::fmt::Write for Serial { - fn write_str(&mut self, s: &str) -> core::fmt::Result { - if !SUPPORTED.load(Ordering::Relaxed) { - return Ok(()); - } - - for byte in s.bytes() { - poll_tx_not_full(); - write_register(Pl011Register::Dr, byte.into()); - } - - Ok(()) - } -} diff --git a/opentmk/src/slog.rs b/opentmk/src/slog.rs index 2073788370..fcf15630a3 100644 --- a/opentmk/src/slog.rs +++ b/opentmk/src/slog.rs @@ -1,4 +1,5 @@ #![feature(panic_location)] +#[no_std] use core::any::type_name; use core::fmt::Write; @@ -7,7 +8,6 @@ use core::result; use crate::arch::serial::{InstrIoAccess, Serial}; use crate::sync::Mutex; use alloc::string::{String, ToString}; -#[no_std] use serde_json::json; use serde::Serialize; pub enum Level { diff --git a/opentmk/src/sync.rs b/opentmk/src/sync.rs index a26233bc76..08e4b105a6 100644 --- a/opentmk/src/sync.rs +++ b/opentmk/src/sync.rs @@ -2,167 +2,13 @@ use core::{arch::asm, cell::{RefCell, UnsafeCell}, fmt::Error, sync::atomic::{At pub use spin::Mutex; use alloc::{boxed::Box, string::{String, ToString}, sync::Arc, vec::Vec}; use alloc::collections::VecDeque; +#[cfg(feature = "std")] +use std::error::Error; +use core::fmt; use crate::infolog; -// pub struct LazyLock { -// lock: AtomicBool, -// init: fn() -> T, -// val: Option>, -// } - -// impl LazyLock { -// pub fn new(init: fn() -> T) -> Self { -// LazyLock { -// lock: AtomicBool::new(false), -// init, -// val: None, -// } -// } - -// pub fn get(&mut self) -> &T { -// if let ok = self.lock.get_mut() { -// if *ok { -// self.val = Some(RefCell::new((self.init)())); - -// } -// } -// if let Some(ref val) = self.val { -// return &val.borrow(); -// } -// panic!("LazyLock not initialized"); -// } - -// pub fn get_mut(&mut self) -> &mut T { -// if let ok = self.lock.get_mut() { -// if ok { -// self.val = Some((self.init)()); -// } -// } -// &mut self.val.unwrap() -// } -// } - -// pub struct Mutex { -// lock: AtomicBool, -// data: UnsafeCell, -// } - -// unsafe impl Sync for Mutex {} - -// impl Mutex { -// pub const fn new(data: T) -> Self { -// Mutex { -// lock: AtomicBool::new(false), -// data: UnsafeCell::new(data), -// } -// } - -// pub fn lock<'a>(&'a self) -> MutexGuard<'a, T> { -// while self.lock.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed).is_err() { -// // Busy-wait until the lock is acquired -// core::hint::spin_loop(); -// } -// MutexGuard { mutex: self } -// } - -// pub fn unlock(&self) { -// self.lock.store(false, Ordering::Release); -// } -// } - -// pub struct MutexGuard<'a, T> { -// mutex: &'a Mutex, -// } -// impl<'a, T> Drop for MutexGuard<'a, T> { -// fn drop(&mut self) { -// self.mutex.unlock(); -// } -// } - -// impl<'a, T> core::ops::Deref for MutexGuard<'a, T> { -// type Target = T; - -// fn deref(&self) -> &Self::Target { -// unsafe { &*self.mutex.data.get() } -// } -// } - -// impl<'a, T> core::ops::DerefMut for MutexGuard<'a, T> { -// fn deref_mut(&mut self) -> &mut Self::Target { -// unsafe { &mut *self.mutex.data.get() } -// } -// } - -#[derive(Debug)] -pub struct RingBuffer { - buffer: Vec>, - capacity: usize, - head: usize, - tail: usize, - size: usize, -} - -impl RingBuffer { - pub fn new(capacity: usize) -> Self { - RingBuffer { - buffer: Vec::with_capacity(capacity), - capacity, - head: 0, - tail: 0, - size: 0, - } - } - - fn is_empty(&self) -> bool { - self.size == 0 - } - - fn is_full(&self) -> bool { - self.size == self.capacity - } - - pub fn push(&mut self, item: T) -> Result<(), String> { - if self.is_full() { - return Err("Buffer is full".to_string()); - } - - if self.tail == self.buffer.len() { - self.buffer.push(Some(item)); - } else { - self.buffer[self.tail] = Some(item); - } - - self.tail = (self.tail + 1) % self.capacity; - self.size += 1; - - Ok(()) - } - - pub fn pop(&mut self) -> Option { - if self.is_empty() { - return None; - } - - let item = core::mem::replace(&mut self.buffer[self.head], None); - self.head = (self.head + 1) % self.capacity; - self.size -= 1; - - Some(item.unwrap()) - } - - pub fn len(&self) -> usize { - self.size - } -} - - - - -#[cfg(feature = "std")] -use std::error::Error; -use core::fmt; /// An unbounded channel implementation with priority send capability. /// This implementation works in no_std environments using spin-rs. diff --git a/opentmk/src/uefi/tests/hv_processor.rs b/opentmk/src/uefi/tests/hv_processor.rs index b71a346065..6839669694 100644 --- a/opentmk/src/uefi/tests/hv_processor.rs +++ b/opentmk/src/uefi/tests/hv_processor.rs @@ -5,8 +5,6 @@ use crate::{ criticallog, infolog, sync::{self, Mutex}, tmk_assert, uefi::context::{TestCtxTrait, VpExecutor} }; -static VP_RUNNING: Mutex> = Mutex::new(Vec::new()); - pub fn exec(ctx: &mut dyn TestCtxTrait) { ctx.setup_interrupt_handler(); ctx.setup_partition_vtl(Vtl::Vtl1); diff --git a/opentmk/src/uefi/tests/mod.rs b/opentmk/src/uefi/tests/mod.rs index 51686205e2..9f5a7be616 100644 --- a/opentmk/src/uefi/tests/mod.rs +++ b/opentmk/src/uefi/tests/mod.rs @@ -8,7 +8,5 @@ pub mod hv_misc; pub fn run_test() { let mut ctx = HvTestCtx::new(); ctx.init(); - - hv_processor::exec(&mut ctx); } \ No newline at end of file From 6d37a843cc3c077988433ec61ec801d850b0d4a4 Mon Sep 17 00:00:00 2001 From: Mayank Kumar Date: Sun, 11 May 2025 19:51:31 +0000 Subject: [PATCH 05/23] chore: resolve PR feedback --- opentmk/Cargo.toml | 3 + opentmk/src/arch/x86_64/interrupt.rs | 15 +- .../arch/x86_64/interrupt_handler_register.rs | 50 ++- opentmk/src/arch/x86_64/serial.rs | 3 +- opentmk/src/main.rs | 8 +- opentmk/src/slog.rs | 242 ------------- opentmk/src/sync.rs | 13 +- opentmk/src/tests/hv_misc.rs | 145 ++++++++ opentmk/src/tests/hv_processor.rs | 74 ++++ opentmk/src/tests/mod.rs | 9 + opentmk/src/tmk_assert.rs | 115 ++++++ opentmk/src/tmk_logger.rs | 75 ++++ opentmk/src/uefi/alloc.rs | 55 +-- opentmk/src/uefi/context.rs | 33 +- opentmk/src/uefi/hypercall.rs | 333 +++++++++++------- opentmk/src/uefi/hypvctx.rs | 85 ++--- opentmk/src/uefi/init.rs | 25 +- opentmk/src/uefi/mod.rs | 16 +- opentmk/src/uefi/rt.rs | 8 +- opentmk/src/uefi/tests/hv_misc.rs | 45 +-- opentmk/src/uefi/tests/hv_processor.rs | 37 +- opentmk/src/uefi/tests/mod.rs | 2 - 22 files changed, 818 insertions(+), 573 deletions(-) delete mode 100644 opentmk/src/slog.rs create mode 100644 opentmk/src/tests/hv_misc.rs create mode 100644 opentmk/src/tests/hv_processor.rs create mode 100644 opentmk/src/tests/mod.rs create mode 100644 opentmk/src/tmk_assert.rs create mode 100644 opentmk/src/tmk_logger.rs diff --git a/opentmk/Cargo.toml b/opentmk/Cargo.toml index 752022b057..70f4c701c4 100644 --- a/opentmk/Cargo.toml +++ b/opentmk/Cargo.toml @@ -21,6 +21,9 @@ lazy_static = { version = "1.4.0", features = ["spin_no_std"] } serde_json = { version = "1.0", default-features = false, features = ["alloc"] } spin = "0.10.0" serde = {version = "1.0", default-features = false} +log = { version = "0.4", features = ["serde"] } +x86defs.workspace = true + [lints] workspace = true diff --git a/opentmk/src/arch/x86_64/interrupt.rs b/opentmk/src/arch/x86_64/interrupt.rs index d9a6ba7993..952629be30 100644 --- a/opentmk/src/arch/x86_64/interrupt.rs +++ b/opentmk/src/arch/x86_64/interrupt.rs @@ -1,14 +1,7 @@ - -use alloc::boxed::Box; -use alloc::sync::Arc; use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame}; use lazy_static::lazy_static; -use core::cell::{Ref, RefCell}; -use core::concat_idents; use crate::sync::Mutex; -use crate::{criticallog, infolog}; - use super::interrupt_handler_register::{register_interrupt_handler, set_common_handler}; lazy_static! { @@ -24,7 +17,7 @@ static mut HANDLERS : [fn(); 256] = [no_op; 256]; static MUTEX: Mutex<()> = Mutex::new(()); fn no_op() {} -fn common_handler(stack_frame: InterruptStackFrame, interrupt: u8) { +fn common_handler(_stack_frame: InterruptStackFrame, interrupt: u8) { unsafe { HANDLERS[interrupt as usize](); } } @@ -38,13 +31,13 @@ extern "x86-interrupt" fn handler_double_fault( stack_frame: InterruptStackFrame, _error_code: u64, ) -> ! { - criticallog!("EXCEPTION:\n\tERROR_CODE: {}\n\tDOUBLE FAULT\n{:#?}", _error_code, stack_frame); + log::error!("EXCEPTION:\n\tERROR_CODE: {}\n\tDOUBLE FAULT\n{:#?}", _error_code, stack_frame); loop {} } // Initialize the IDT pub fn init() { - unsafe { IDT.load() }; + IDT.load(); set_common_handler(common_handler); - unsafe { x86_64::instructions::interrupts::enable() }; + x86_64::instructions::interrupts::enable(); } \ No newline at end of file diff --git a/opentmk/src/arch/x86_64/interrupt_handler_register.rs b/opentmk/src/arch/x86_64/interrupt_handler_register.rs index d2433c780b..25522d3c38 100644 --- a/opentmk/src/arch/x86_64/interrupt_handler_register.rs +++ b/opentmk/src/arch/x86_64/interrupt_handler_register.rs @@ -1,7 +1,7 @@ -use core::arch::asm; +#![allow(dead_code)] use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame}; -use crate::{infolog, sync::Mutex}; +use crate::sync::Mutex; static mut COMMON_HANDLER: fn(InterruptStackFrame, u8) = common_handler; static COMMON_HANDLER_MUTEX: Mutex<()> = Mutex::new(()); @@ -20,28 +20,52 @@ macro_rules! register_interrupt_handler { }; } -fn common_handler(stack_frame: InterruptStackFrame, interrupt: u8) { - infolog!("Default interrupt handler fired: {}", interrupt); +fn common_handler(_stack_frame: InterruptStackFrame, interrupt: u8) { + log::info!("Default interrupt handler fired: {}", interrupt); } pub fn set_common_handler(handler: fn(InterruptStackFrame, u8)) { - let guard = COMMON_HANDLER_MUTEX.lock(); + let _guard = COMMON_HANDLER_MUTEX.lock(); unsafe { COMMON_HANDLER = handler; } } -extern "x86-interrupt" fn no_op(stack_frame: InterruptStackFrame) {} +extern "x86-interrupt" fn no_op(_stack_frame: InterruptStackFrame) {} pub fn register_interrupt_handler(idt: &mut InterruptDescriptorTable) { - register_interrupt_handler!(idt, 0, handler_0); - register_interrupt_handler!(idt, 1, handler_1); + register_interrupt_handler!(idt, x86defs::Exception::DIVIDE_ERROR.0, handler_0); + register_interrupt_handler!(idt, x86defs::Exception::DEBUG.0, handler_1); register_interrupt_handler!(idt, 2, handler_2); - register_interrupt_handler!(idt, 3, handler_3); - register_interrupt_handler!(idt, 4, handler_4); - register_interrupt_handler!(idt, 5, handler_5); - register_interrupt_handler!(idt, 6, handler_6); - register_interrupt_handler!(idt, 7, handler_7); + register_interrupt_handler!(idt, x86defs::Exception::BREAKPOINT.0, handler_3); + register_interrupt_handler!(idt, x86defs::Exception::OVERFLOW.0, handler_4); + register_interrupt_handler!(idt, x86defs::Exception::BOUND_RANGE_EXCEEDED.0, handler_5); + register_interrupt_handler!(idt, x86defs::Exception::INVALID_OPCODE.0, handler_6); + register_interrupt_handler!(idt, x86defs::Exception::DEVICE_NOT_AVAILABLE.0, handler_7); + // register_interrupt_handler!(idt, x86defs::Exception::DOUBLE_FAULT.0, handler_8); + register_interrupt_handler!(idt, 9, handler_9); + // register_interrupt_handler!(idt, x86defs::Exception::INVALID_TSS.0, handler_10); + // register_interrupt_handler!(idt, x86defs::Exception::SEGMENT_NOT_PRESENT.0, handler_11); + // register_interrupt_handler!(idt, x86defs::Exception::STACK_SEGMENT_FAULT.0, handler_12); + // register_interrupt_handler!(idt, x86defs::Exception::GENERAL_PROTECTION_FAULT.0, handler_13); + // register_interrupt_handler!(idt, x86defs::Exception::PAGE_FAULT.0, handler_14); + // register_interrupt_handler!(idt, 15, handler_15); + // register_interrupt_handler!(idt, x86defs::Exception::FLOATING_POINT_EXCEPTION.0, handler_16); + // register_interrupt_handler!(idt, x86defs::Exception::ALIGNMENT_CHECK.0, handler_17); + // register_interrupt_handler!(idt, x86defs::Exception::MACHINE_CHECK.0, handler_18); + // register_interrupt_handler!(idt, x86defs::Exception::SIMD_FLOATING_POINT_EXCEPTION.0, handler_19); + // register_interrupt_handler!(idt, 20, handler_20); + // register_interrupt_handler!(idt, 21, handler_21); + // register_interrupt_handler!(idt, 22, handler_22); + // register_interrupt_handler!(idt, 23, handler_23); + // register_interrupt_handler!(idt, 24, handler_24); + // register_interrupt_handler!(idt, 25, handler_25); + // register_interrupt_handler!(idt, 26, handler_26); + // register_interrupt_handler!(idt, 27, handler_27); + // register_interrupt_handler!(idt, 28, handler_28); + // register_interrupt_handler!(idt, x86defs::Exception::SEV_VMM_COMMUNICATION.0, handler_29); + // register_interrupt_handler!(idt, 30, handler_30); + // register_interrupt_handler!(idt, 31, handler_31); register_interrupt_handler!(idt, 32, handler_32); register_interrupt_handler!(idt, 33, handler_33); diff --git a/opentmk/src/arch/x86_64/serial.rs b/opentmk/src/arch/x86_64/serial.rs index 250fbd66cf..173794ad8f 100644 --- a/opentmk/src/arch/x86_64/serial.rs +++ b/opentmk/src/arch/x86_64/serial.rs @@ -2,10 +2,9 @@ // Licensed under the MIT License. //! Serial output for debugging. - +#![allow(static_mut_refs)] use core::arch::asm; use core::fmt; -use core::sync::atomic::AtomicBool; use crate::sync::Mutex; const COM4: u16 = 0x2E8; diff --git a/opentmk/src/main.rs b/opentmk/src/main.rs index 019f594f4d..d33c63c8af 100644 --- a/opentmk/src/main.rs +++ b/opentmk/src/main.rs @@ -1,11 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -#![allow(warnings)] #![no_std] #![allow(unsafe_code)] -#![feature(naked_functions)] #![feature(abi_x86_interrupt)] -#![feature(concat_idents)] #![doc = include_str!("../README.md")] // HACK: workaround for building guest_test_uefi as part of the workspace in CI. @@ -23,5 +20,6 @@ extern crate alloc; mod uefi; pub mod arch; -pub mod slog; -pub mod sync; \ No newline at end of file +pub mod tmk_assert; +pub mod sync; +pub mod tmk_logger; diff --git a/opentmk/src/slog.rs b/opentmk/src/slog.rs deleted file mode 100644 index fcf15630a3..0000000000 --- a/opentmk/src/slog.rs +++ /dev/null @@ -1,242 +0,0 @@ -#![feature(panic_location)] -#[no_std] - -use core::any::type_name; -use core::fmt::Write; -use core::result; - -use crate::arch::serial::{InstrIoAccess, Serial}; -use crate::sync::Mutex; -use alloc::string::{String, ToString}; -use serde_json::json; -use serde::Serialize; -pub enum Level { - DEBUG = 0, - INFO = 1, - WARNING = 2, - ERROR = 3, - CRITICAL = 4, -} - -pub fn get_json_string(s: &String, terminate_new_line: bool, level: Level) -> String { - let out = json!({ - "type:": "log", - "message": s, - "level": match level { - Level::DEBUG => "DEBUG", - Level::INFO => "INFO", - Level::WARNING => "WARNING", - Level::ERROR => "ERROR", - Level::CRITICAL => "CRITICAL", - } - }); - let mut out = out.to_string(); - if terminate_new_line { - out.push('\n'); - } - return out; -} - -pub fn get_json_test_assertion_string( - s: &str, - terminate_new_line: bool, - line: String, - assert_result: bool, - testname: &T, -) -> String where T: Serialize { - let out = json!({ - "type:": "assertion", - "message": s, - "level": "CRITICAL", - "line": line, - "assertion_result": assert_result, - "testname": testname, - }); - let mut out = out.to_string(); - if terminate_new_line { - out.push('\n'); - } - return out; -} - -pub static mut SERIAL: Serial = Serial::new(InstrIoAccess {}); - -#[macro_export] -macro_rules! tmk_assert { - ($condition:expr, $message:expr) => {{ - use core::fmt::Write; - let file = core::file!(); - let line = line!(); - let file_line = format!("{}:{}", file, line); - let expn = stringify!($condition); - let result: bool = $condition; - let js = - crate::slog::get_json_test_assertion_string(&expn, true, file_line, result, &$message); - unsafe { crate::slog::SERIAL.write_str(&js) }; - if !result { - panic!("Assertion failed: {}", $message); - } - }}; -} - -#[macro_export] -macro_rules! logt { - ($($arg:tt)*) => { - { - use core::fmt::Write; - let message = format!($($arg)*); - let js = crate::slog::get_json_string(&message, true, crate::slog::Level::INFO); - unsafe { crate::slog::SERIAL.write_str(&js) }; - } - }; -} - -#[macro_export] -macro_rules! errorlog { - ($($arg:tt)*) => { - { - use core::fmt::Write; - let message = format!($($arg)*); - let js = crate::slog::get_json_string(&message, true, crate::slog::Level::ERROR); - unsafe { crate::slog::SERIAL.write_str(&js) }; - } - }; -} - -#[macro_export] -macro_rules! debuglog { - ($($arg:tt)*) => { - { - use core::fmt::Write; - - let message = format!($($arg)*); - let js = crate::slog::get_json_string(&message, true, crate::slog::Level::DEBUG); - unsafe { crate::slog::SERIAL.write_str(&js) }; - } - }; -} - -#[macro_export] -macro_rules! infolog { - ($($arg:tt)*) => { - { - use core::fmt::Write; - - let message = format!($($arg)*); - let js = crate::slog::get_json_string(&message, true, crate::slog::Level::INFO); - unsafe { crate::slog::SERIAL.write_str(&js) }; - } - }; -} - -#[macro_export] -macro_rules! warninglog { - ($($arg:tt)*) => { - { - use core::fmt::Write; - - let message = format!($($arg)*); - let js = crate::slog::get_json_string(&message, true, crate::slog::Level::WARNING); - unsafe { crate::slog::SERIAL.write_str(&js) }; - } - }; -} - -#[macro_export] -macro_rules! criticallog { - ($($arg:tt)*) => { - { - use core::fmt::Write; - - let message = format!($($arg)*); - let js = crate::slog::get_json_string(&message, true, crate::slog::Level::CRITICAL); - unsafe { crate::slog::SERIAL.write_str(&js) }; - } - }; -} - -#[macro_export] -macro_rules! slog { - - ($serial:expr, $($arg:tt)*) => { - let mut serial : &mut Mutex> = &mut $serial; - let message = format!($($arg)*); - let js = slog::get_json_string(&message, true, crate::slog::Level::INFO); - { - let mut serial = serial.lock(); - serial.write_str(&js); - } - }; - -} - -pub trait AssertResult { - fn unpack_assert(self) -> T; - fn expect_assert(self, message: &str) -> T; -} - -pub trait AssertOption { - fn expect_assert(self, message: &str) -> T; -} - -impl AssertOption for Option { - fn expect_assert(self, message: &str) -> T { - match self { - Some(value) => value, - None => { - let call: &core::panic::Location<'_> = core::panic::Location::caller(); - let file_line = format!("{}:{}", call.file(), call.line()); - let expn = type_name::>(); - let js = crate::slog::get_json_test_assertion_string( - expn, true, file_line, false, &message, - ); - unsafe { crate::slog::SERIAL.write_str(&js) }; - panic!("Assertion failed: {}", message); - } - } - } -} - -impl AssertResult for Result -where - E: core::fmt::Debug, -{ - fn unpack_assert(self) -> T { - match self { - Ok(value) => value, - Err(err) => { - let call: &core::panic::Location<'_> = core::panic::Location::caller(); - let file_line = format!("{}:{}", call.file(), call.line()); - let expn = type_name::>(); - let js = crate::slog::get_json_test_assertion_string( - expn, - true, - file_line, - false, - &"ResultTest", - ); - unsafe { crate::slog::SERIAL.write_str(&js) }; - panic!("Assertion failed: {:?}", err); - } - } - } - fn expect_assert(self, message: &str) -> T { - match self { - Ok(value) => { - infolog!("result is ok, condition not met for: {}", message); - value - } - Err(err) => { - let call: &core::panic::Location<'_> = core::panic::Location::caller(); - let file_line = format!("{}:{}", call.file(), call.line()); - let expn = type_name::>(); - let js = crate::slog::get_json_test_assertion_string( - expn, true, file_line, false, &message, - ); - unsafe { crate::slog::SERIAL.write_str(&js) }; - - panic!("Assertion failed: {:?}", err); - } - } - } -} diff --git a/opentmk/src/sync.rs b/opentmk/src/sync.rs index 08e4b105a6..d0fe9eba59 100644 --- a/opentmk/src/sync.rs +++ b/opentmk/src/sync.rs @@ -1,15 +1,10 @@ -use core::{arch::asm, cell::{RefCell, UnsafeCell}, fmt::Error, sync::atomic::{AtomicBool, AtomicUsize, Ordering}}; +use core::sync::atomic::{AtomicUsize, Ordering}; pub use spin::Mutex; -use alloc::{boxed::Box, string::{String, ToString}, sync::Arc, vec::Vec}; +use alloc::{sync::Arc, vec::Vec}; use alloc::collections::VecDeque; -#[cfg(feature = "std")] -use std::error::Error; +use core::error::Error; use core::fmt; -use crate::infolog; - - - /// An unbounded channel implementation with priority send capability. /// This implementation works in no_std environments using spin-rs. /// It uses a VecDeque as the underlying buffer and ensures non-blocking operations. @@ -47,7 +42,6 @@ impl fmt::Display for SendError { } } -#[cfg(feature = "std")] impl Error for SendError {} /// Error type for receiving operations @@ -68,7 +62,6 @@ impl fmt::Display for RecvError { } } -#[cfg(feature = "std")] impl Error for RecvError {} /// Sender half of the channel diff --git a/opentmk/src/tests/hv_misc.rs b/opentmk/src/tests/hv_misc.rs new file mode 100644 index 0000000000..8379c344a0 --- /dev/null +++ b/opentmk/src/tests/hv_misc.rs @@ -0,0 +1,145 @@ +// WIP : This test is not yet complete and is not expected to pass. +// +// This test is to verify that the VTL protections are working as expected. +// The stack values in VTL0 are changing after interrupt handling in VTL1. +#![allow(warnings)] +use crate::slog::{AssertOption, AssertResult}; +use crate::sync::{Channel, Receiver, Sender}; +use crate::uefi::alloc::{ALLOCATOR, SIZE_1MB}; +use crate::uefi::{context, hypvctx}; +use crate::{infolog, tmk_assert}; +use ::alloc::boxed::Box; +use alloc::sync::Arc; +use ::alloc::vec::Vec; +use context::{TestCtxTrait, VpExecutor}; +use hypvctx::HvTestCtx; +use core::alloc::{GlobalAlloc, Layout}; +use core::arch::asm; +use core::cell::RefCell; +use core::ops::Range; +use core::sync::atomic::{AtomicI32, Ordering}; +use hvdef::hypercall::HvInputVtl; +use hvdef::{HvAllArchRegisterName, HvRegisterVsmVpStatus, HvX64RegisterName, Vtl}; + +static mut HEAPX: RefCell<*mut u8> = RefCell::new(0 as *mut u8); +static mut CON: AtomicI32 = AtomicI32::new(0); + +pub fn exec(_opt: Option<()>, ctx: Arc>) { + log::info!("ctx ptr: {:p}", &ctx as *const _); + + let mut cpy = ctx.clone(); + + let mut ctx = ctx.borrow_mut(); + + let mut vp_count = ctx.get_vp_count(); + tmk_assert!(vp_count == 8, "vp count should be 8"); + + ctx.setup_interrupt_handler(); + + log::info!("set intercept handler successfully!"); + + ctx.setup_partition_vtl(Vtl::Vtl1); + + ctx.start_on_vp( + VpExecutor::new(0, Vtl::Vtl1).command(move |ctx: &mut dyn TestCtxTrait| { + log::info!("successfully started running VTL1 on vp0."); + ctx.setup_secure_intercept(0x30); + ctx.set_interupt_idx(0x30, || { + log::info!("interrupt fired!"); + + // let mut hv_test_ctx: HvTestCtx = HvTestCtx::new(); + // hv_test_ctx.init(); + + // let c = hv_test_ctx.get_register(HvAllArchRegisterName::VsmVpStatus.0); + + // let cp: HvRegisterVsmVpStatus = HvRegisterVsmVpStatus::from_bits(c as u64); + + // log::info!("VSM VP Status: {:?}", cp); + + log::info!("interrupt handled!"); + }); + + let layout = + Layout::from_size_align(SIZE_1MB, 4096).expect("msg: failed to create layout"); + let ptr = unsafe { ALLOCATOR.alloc(layout) }; + log::info!("allocated some memory in the heap from vtl1"); + unsafe { + let mut z = HEAPX.borrow_mut(); + *z = ptr; + *ptr.add(10) = 0xAA; + } + + let size = layout.size(); + ctx.setup_vtl_protection(); + + log::info!("enabled vtl protections for the partition."); + + let range = Range { + start: ptr as u64, + end: ptr as u64 + size as u64, + }; + + ctx.apply_vtl_protection_for_memory(range, Vtl::Vtl1); + + log::info!("moving to vtl0 to attempt to read the heap memory"); + + ctx.switch_to_low_vtl(); + }), + ); + + ctx.queue_command_vp(VpExecutor::new(0, Vtl::Vtl1).command(move |ctx| { + log::info!("successfully started running VTL1 on vp0."); + ctx.switch_to_low_vtl(); + })); + log::info!("ctx ptr: {:p}", &ctx as *const _); + log::info!("_opt ptr: {:p}", &_opt as *const _); + let mut l = 0u64; + unsafe { asm!("mov {}, rsp", out(reg) l) }; + log::info!("rsp: 0x{:x}", l); + unsafe { + log::info!("Attempting to read heap memory from vtl0"); + let heapx = *HEAPX.borrow(); + let val = *(heapx.add(10)); + log::info!( + "reading mutated heap memory from vtl0(it should not be 0xAA): 0x{:x}", + val + ); + tmk_assert!( + val != 0xAA, + "heap memory should not be accessible from vtl0" + ); + } + + unsafe { asm!("mov {}, rsp", out(reg) l) }; + log::info!("rsp: 0x{:x}", l); + + // let (mut tx, mut rx) = Channel::new(1); + // { + // let mut tx = tx.clone(); + // ctx.start_on_vp(VpExecutor::new(2, Vtl::Vtl0).command( + // move |ctx: &mut dyn TestCtxTrait| { + // log::info!("Hello form vtl0 on vp2!"); + // tx.send(()); + // }, + // )); + // } + + drop(ctx); + + let mut ctx = cpy.borrow_mut(); + // let mut ctx = cpy.borrow_mut(); + log::info!("ctx ptr: {:p}", &ctx as *const _); + log::info!("opt ptr: {:p}", &_opt as *const _); + let c = ctx.get_vp_count(); + + tmk_assert!(c == 8, "vp count should be 8"); + + // rx.recv(); + + log::info!("we are in vtl0 now!"); + log::info!("we reached the end of the test"); + loop { + + } + +} \ No newline at end of file diff --git a/opentmk/src/tests/hv_processor.rs b/opentmk/src/tests/hv_processor.rs new file mode 100644 index 0000000000..64439039db --- /dev/null +++ b/opentmk/src/tests/hv_processor.rs @@ -0,0 +1,74 @@ +use hvdef::Vtl; + +use crate::{ + criticallog, infolog, + tmk_assert, + uefi::context::{TestCtxTrait, VpExecutor}, +}; + +pub fn exec(ctx: &mut dyn TestCtxTrait) { + ctx.setup_interrupt_handler(); + ctx.setup_partition_vtl(Vtl::Vtl1); + + let vp_count = ctx.get_vp_count(); + tmk_assert!(vp_count == 8, "vp count should be 8"); + + // Testing BSP VTL Bringup + { + let (tx, rx) = crate::sync::Channel::new().split(); + ctx.start_on_vp(VpExecutor::new(0, Vtl::Vtl1).command( + move |ctx: &mut dyn TestCtxTrait| { + let vp = ctx.get_current_vp(); + log::info!("vp: {}", vp); + tmk_assert!(vp == 0, "vp should be equal to 0"); + + let vtl = ctx.get_current_vtl(); + log::info!("vtl: {:?}", vtl); + tmk_assert!(vtl == Vtl::Vtl1, "vtl should be Vtl1 for BSP"); + _ = tx.send(()); + ctx.switch_to_low_vtl(); + }, + )); + _ = rx.recv(); + } + + for i in 1..vp_count { + // Testing VTL1 + { + let (tx, rx) = crate::sync::Channel::new().split(); + ctx.start_on_vp(VpExecutor::new(i, Vtl::Vtl1).command( + move |ctx: &mut dyn TestCtxTrait| { + let vp = ctx.get_current_vp(); + log::info!("vp: {}", vp); + tmk_assert!(vp == i, format!("vp should be equal to {}", i)); + + let vtl = ctx.get_current_vtl(); + log::info!("vtl: {:?}", vtl); + tmk_assert!(vtl == Vtl::Vtl1, format!("vtl should be Vtl1 for VP {}", i)); + _ = tx.send(()); + }, + )); + _ = rx.recv(); + } + + // Testing VTL0 + { + let (tx, rx) = crate::sync::Channel::new().split(); + ctx.start_on_vp(VpExecutor::new(i, Vtl::Vtl0).command( + move |ctx: &mut dyn TestCtxTrait| { + let vp = ctx.get_current_vp(); + log::info!("vp: {}", vp); + tmk_assert!(vp == i, format!("vp should be equal to {}", i)); + + let vtl = ctx.get_current_vtl(); + log::info!("vtl: {:?}", vtl); + tmk_assert!(vtl == Vtl::Vtl0, format!("vtl should be Vtl0 for VP {}", i)); + _ = tx.send(()); + }, + )); + _ = rx.recv(); + } + } + + log::error!("All VPs have been tested"); +} diff --git a/opentmk/src/tests/mod.rs b/opentmk/src/tests/mod.rs new file mode 100644 index 0000000000..2457eb039e --- /dev/null +++ b/opentmk/src/tests/mod.rs @@ -0,0 +1,9 @@ +mod hv_processor; +mod hv_misc; + +use crate::uefi::hypvctx::HvTestCtx; + +pub fn run_test() { + let mut ctx = HvTestCtx::new(); + hv_processor::exec(&mut ctx); +} \ No newline at end of file diff --git a/opentmk/src/tmk_assert.rs b/opentmk/src/tmk_assert.rs new file mode 100644 index 0000000000..a33e3ca447 --- /dev/null +++ b/opentmk/src/tmk_assert.rs @@ -0,0 +1,115 @@ +use core::{any::type_name, fmt::Write}; +use alloc::string::{String, ToString}; +use serde::Serialize; +use serde_json::json; + +pub fn format_asset_json_string( + s: &str, + terminate_new_line: bool, + line: String, + assert_result: bool, + testname: &T, +) -> String +where + T: Serialize, +{ + let out = json!({ + "type:": "assert", + "level": "WARN", + "message": s, + "line": line, + "assertion_result": assert_result, + "testname": testname, + }); + let mut out = out.to_string(); + if terminate_new_line { + out.push('\n'); + } + return out; +} + + +pub fn write_str(s: &str) { + let _ = crate::tmk_logger::LOGGER.get_writter().write_str(s); +} + +#[macro_export] +macro_rules! tmk_assert { + ($condition:expr, $message:expr) => { + { + let file = core::file!(); + let line = line!(); + let file_line = format!("{}:{}", file, line); + let expn = stringify!($condition); + let result: bool = $condition; + let js = crate::tmk_assert::format_asset_json_string( + &expn, true, file_line, result, &$message, + ); + crate::tmk_assert::write_str(&js); + if !result { + panic!("Assertion failed: {}", $message); + } + } + }; +} + +pub trait AssertResult { + fn unpack_assert(self) -> T; + fn expect_assert(self, message: &str) -> T; +} + +pub trait AssertOption { + fn expect_assert(self, message: &str) -> T; +} + +impl AssertOption for Option { + fn expect_assert(self, message: &str) -> T { + match self { + Some(value) => value, + None => { + let call: &core::panic::Location<'_> = core::panic::Location::caller(); + let file_line = format!("{}:{}", call.file(), call.line()); + let expn = type_name::>(); + let js = format_asset_json_string(expn, true, file_line, false, &message); + write_str(&js); + panic!("Assertion failed: {}", message); + } + } + } +} + +impl AssertResult for Result +where + E: core::fmt::Debug, +{ + fn unpack_assert(self) -> T { + match self { + Ok(value) => value, + Err(err) => { + let call: &core::panic::Location<'_> = core::panic::Location::caller(); + let file_line = format!("{}:{}", call.file(), call.line()); + let expn = type_name::>(); + let js = + format_asset_json_string(expn, true, file_line, false, &"ResultTest"); + write_str(&js); + panic!("Assertion failed: {:?}", err); + } + } + } + fn expect_assert(self, message: &str) -> T { + match self { + Ok(value) => { + log::info!("result is ok, condition not met for: {}", message); + value + } + Err(err) => { + let call: &core::panic::Location<'_> = core::panic::Location::caller(); + let file_line = format!("{}:{}", call.file(), call.line()); + let expn = type_name::>(); + let js = format_asset_json_string(expn, true, file_line, false, &message); + write_str(&js); + panic!("Assertion failed: {:?}", err); + } + } + } +} diff --git a/opentmk/src/tmk_logger.rs b/opentmk/src/tmk_logger.rs new file mode 100644 index 0000000000..467d775c49 --- /dev/null +++ b/opentmk/src/tmk_logger.rs @@ -0,0 +1,75 @@ +use core::fmt::Write; + +use alloc::{fmt::format, string::{String, ToString}}; +use log::SetLoggerError; +use serde_json::json; +use spin::{mutex::Mutex, MutexGuard}; + +use crate::arch::serial::{InstrIoAccess, Serial}; + +pub fn format_log_string_to_json( + message: &String, + line: &String, + terminate_new_line: bool, + level: log::Level, +) -> String { + let out = json!({ + "type:": "log", + "level": level.as_str(), + "message": message, + "line": line, + }); + let mut out = out.to_string(); + if terminate_new_line { + out.push('\n'); + } + return out; +} + +pub struct TmkLogger { + pub writter: T, +} + +impl TmkLogger> +where + T: Write + Send, +{ + pub const fn new(provider: T) -> Self { + TmkLogger { + writter: Mutex::new(provider), + } + } + + pub fn get_writter(&self) -> MutexGuard<'_, T> where T: Write + Send { + self.writter.lock() + } +} + +impl log::Log for TmkLogger> +where + T: Write + Send, +{ + fn enabled(&self, _metadata: &log::Metadata<'_>) -> bool { + true + } + + fn log(&self, record: &log::Record<'_>) { + let str = format(*record.args()); + let line = format!( + "{}:{}", + record.file().unwrap_or_default(), + record.line().unwrap_or_default() + ); + let str = format_log_string_to_json(&str, &line, true, record.level()); + _ = self.writter.lock().write_str(str.as_str()); + } + + fn flush(&self) {} +} + +pub static LOGGER: TmkLogger>> = + TmkLogger::new(Serial::new(InstrIoAccess {})); + +pub fn init() -> Result<(), SetLoggerError> { + log::set_logger(&LOGGER).map(|()| log::set_max_level(log::LevelFilter::Debug)) +} diff --git a/opentmk/src/uefi/alloc.rs b/opentmk/src/uefi/alloc.rs index 92619fdee5..edc36d054d 100644 --- a/opentmk/src/uefi/alloc.rs +++ b/opentmk/src/uefi/alloc.rs @@ -2,15 +2,19 @@ use core::{alloc::GlobalAlloc, cell::RefCell}; use linked_list_allocator::LockedHeap; use spin::mutex::Mutex; -use uefi::{allocator::Allocator, boot::{self, AllocateType, MemoryType}}; +use uefi::{ + allocator::Allocator, + boot::{self, AllocateType, MemoryType}, +}; -pub const SIZE_1MB: usize = 1024 * 1024; +pub const SIZE_1MB: usize = 1024 * 1024; +const PAGE_SIZE: usize = 4096; #[global_allocator] pub static ALLOCATOR: MemoryAllocator = MemoryAllocator { use_locked_heap: Mutex::new(RefCell::new(false)), locked_heap: LockedHeap::empty(), - uefi_allocator: Allocator{}, + uefi_allocator: Allocator {}, }; pub struct MemoryAllocator { @@ -24,7 +28,7 @@ unsafe impl GlobalAlloc for MemoryAllocator { #[allow(unsafe_code)] unsafe fn alloc(&self, layout: core::alloc::Layout) -> *mut u8 { if *self.use_locked_heap.lock().borrow() { - unsafe { self.locked_heap.alloc(layout) } + unsafe { self.locked_heap.alloc(layout) } } else { unsafe { self.uefi_allocator.alloc(layout) } } @@ -37,31 +41,38 @@ unsafe impl GlobalAlloc for MemoryAllocator { unsafe { self.uefi_allocator.dealloc(ptr, layout) } } } - + unsafe fn alloc_zeroed(&self, layout: core::alloc::Layout) -> *mut u8 { if *self.use_locked_heap.lock().borrow() { unsafe { self.locked_heap.alloc_zeroed(layout) } - } else { - unsafe { self.uefi_allocator.alloc_zeroed(layout) } - } + } else { + unsafe { self.uefi_allocator.alloc_zeroed(layout) } + } } - - unsafe fn realloc(&self, ptr: *mut u8, layout: core::alloc::Layout, new_size: usize) -> *mut u8 { + + unsafe fn realloc( + &self, + ptr: *mut u8, + layout: core::alloc::Layout, + new_size: usize, + ) -> *mut u8 { if *self.use_locked_heap.lock().borrow() { unsafe { self.locked_heap.realloc(ptr, layout, new_size) } - } else { - unsafe { self.uefi_allocator.realloc(ptr, layout, new_size) } - } + } else { + unsafe { self.uefi_allocator.realloc(ptr, layout, new_size) } + } } } impl MemoryAllocator { - - #[expect(unsafe_code)] - pub unsafe fn init(&self, size: usize) -> bool { + pub fn init(&self, size: usize) -> bool { let pages = ((SIZE_1MB * size) / 4096) + 1; let size = pages * 4096; - let mem: Result, uefi::Error> = boot::allocate_pages(AllocateType::AnyPages, MemoryType::BOOT_SERVICES_DATA, pages); + let mem: Result, uefi::Error> = boot::allocate_pages( + AllocateType::AnyPages, + MemoryType::BOOT_SERVICES_DATA, + pages, + ); if mem.is_err() { return false; } @@ -73,10 +84,14 @@ impl MemoryAllocator { return true; } + #[allow(dead_code)] pub fn get_page_alligned_memory(&self, size: usize) -> *mut u8 { - let pages = ((SIZE_1MB * size) / 4096) + 1; - let size = pages * 4096; - let mem: Result, uefi::Error> = boot::allocate_pages(AllocateType::AnyPages, MemoryType::BOOT_SERVICES_DATA, pages); + let pages = ((SIZE_1MB * size) / PAGE_SIZE) + 1; + let mem: Result, uefi::Error> = boot::allocate_pages( + AllocateType::AnyPages, + MemoryType::BOOT_SERVICES_DATA, + pages, + ); if mem.is_err() { return core::ptr::null_mut(); } diff --git a/opentmk/src/uefi/context.rs b/opentmk/src/uefi/context.rs index b9e5da65d6..3249c0bd3f 100644 --- a/opentmk/src/uefi/context.rs +++ b/opentmk/src/uefi/context.rs @@ -1,3 +1,4 @@ +#![allow(dead_code)] use core::ops::Range; use alloc::boxed::Box; @@ -6,32 +7,30 @@ use hvdef::Vtl; pub trait TestCtxTrait { - fn get_vp_count(&self) -> u32; - fn get_current_vp(&self) -> u32; - fn get_current_vtl(&self) -> Vtl; - - fn start_on_vp(&mut self, cmd: VpExecutor); - - fn queue_command_vp(&mut self, cmd: VpExecutor); - - fn switch_to_high_vtl(&mut self); - fn switch_to_low_vtl(&mut self); + // partition wide Traits + fn get_vp_count(&self) -> u32; + fn setup_vtl_protection(&mut self); fn setup_partition_vtl(&mut self, vtl: Vtl); fn setup_interrupt_handler(&mut self); - fn set_interupt_idx(&mut self, interrupt_idx: u8, handler: fn()); - - fn setup_vtl_protection(&mut self); + fn set_interrupt_idx(&mut self, interrupt_idx: u8, handler: fn()); + fn start_on_vp(&mut self, cmd: VpExecutor); + fn queue_command_vp(&mut self, cmd: VpExecutor); fn setup_secure_intercept(&mut self, interrupt_idx: u8); fn apply_vtl_protection_for_memory(&mut self, range: Range, vtl: Vtl); - fn write_msr(&mut self, msr: u32, value: u64); - fn read_msr(&mut self, msr: u32) -> u64; - - fn start_running_vp_with_default_context(&mut self, cmd: VpExecutor); fn set_default_ctx_to_vp(&mut self, vp_index: u32, vtl: Vtl); + fn start_running_vp_with_default_context(&mut self, cmd: VpExecutor); fn enable_vp_vtl_with_default_context(&mut self, vp_index: u32, vtl: Vtl); + fn write_msr(&mut self, msr: u32, value: u64); + fn read_msr(&mut self, msr: u32) -> u64; + // per vp wide Traits + fn get_current_vp(&self) -> u32; + fn get_current_vtl(&self) -> Vtl; + fn switch_to_high_vtl(&mut self); + fn switch_to_low_vtl(&mut self); fn get_register(&mut self, reg: u32) -> u128; + } pub struct VpExecutor { diff --git a/opentmk/src/uefi/hypercall.rs b/opentmk/src/uefi/hypercall.rs index 8e3aa61312..26aa18db9a 100644 --- a/opentmk/src/uefi/hypercall.rs +++ b/opentmk/src/uefi/hypercall.rs @@ -3,32 +3,26 @@ //! Hypercall infrastructure. +#![allow(dead_code)] use arrayvec::ArrayVec; +use core::mem::size_of; use hvdef::hypercall::EnablePartitionVtlFlags; +use hvdef::hypercall::HvInputVtl; use hvdef::hypercall::InitialVpContextX64; -use hvdef::HvInterruptType; -use hvdef::HvRegisterGuestVsmPartitionConfig; use hvdef::HvRegisterValue; use hvdef::HvRegisterVsmPartitionConfig; use hvdef::HvX64RegisterName; -use zerocopy::FromZeros; -use core::arch; -use core::cell::RefCell; -use core::cell::UnsafeCell; -use core::mem::size_of; -use hvdef::hypercall::HvInputVtl; use hvdef::Vtl; use hvdef::HV_PAGE_SIZE; use memory_range::MemoryRange; use minimal_rt::arch::hypercall::{invoke_hypercall, HYPERCALL_PAGE}; -use zerocopy::IntoBytes; use zerocopy::FromBytes; +use zerocopy::IntoBytes; /// Page-aligned, page-sized buffer for use with hypercalls #[repr(C, align(4096))] struct HvcallPage { buffer: [u8; HV_PAGE_SIZE as usize], - } pub fn invoke_hypercall_vtl(control: hvdef::hypercall::Control) { @@ -48,7 +42,6 @@ impl HvcallPage { pub const fn new() -> Self { HvcallPage { buffer: [0; HV_PAGE_SIZE as usize], - } } @@ -64,7 +57,7 @@ impl HvcallPage { } /// Provides mechanisms to invoke hypercalls within the boot shim. -/// +/// /// This module defines the `HvCall` struct and associated methods to interact with /// hypervisor functionalities through hypercalls. It includes utilities for managing /// hypercall pages, setting and getting virtual processor (VP) registers, enabling @@ -122,25 +115,18 @@ impl HvcallPage { /// multi-threaded capacity (which the boot shim currently is not). pub struct HvCall { initialized: bool, - pub vtl: Vtl, input_page: HvcallPage, output_page: HvcallPage, } - #[expect(unsafe_code)] impl HvCall { pub const fn new() -> Self { - // SAFETY: The caller must ensure that this is only called once. - unsafe { - HvCall { - initialized: false, - vtl: Vtl::Vtl0, - input_page: HvcallPage::new(), - output_page: HvcallPage::new(), - } + HvCall { + initialized: false, + input_page: HvcallPage::new(), + output_page: HvcallPage::new(), } - } fn input_page(&mut self) -> &mut HvcallPage { &mut self.input_page @@ -155,7 +141,7 @@ impl HvCall { #[cfg(target_arch = "x86_64")] pub fn hypercall_page(&mut self) -> u64 { self.init_if_needed(); - core::ptr::addr_of!(minimal_rt::arch::hypercall::HYPERCALL_PAGE) as u64 + core::ptr::addr_of!(HYPERCALL_PAGE) as u64 } fn init_if_needed(&mut self) { @@ -171,15 +157,6 @@ impl HvCall { let guest_os_id = hvdef::hypercall::HvGuestOsMicrosoft::new().with_os_id(1); crate::arch::hypercall::initialize(guest_os_id.into()); self.initialized = true; - - self.vtl = self - .get_register(hvdef::HvAllArchRegisterName::VsmVpStatus.into(), None) - .map_or(Vtl::Vtl0, |status| { - hvdef::HvRegisterVsmVpStatus::from(status.as_u64()) - .active_vtl() - .try_into() - .unwrap() - }); } /// Call before jumping to kernel. @@ -191,9 +168,16 @@ impl HvCall { } /// Returns the environment's VTL. - pub fn vtl(&self) -> Vtl { + pub fn vtl(&mut self) -> Vtl { assert!(self.initialized); - self.vtl + self + .get_register(hvdef::HvAllArchRegisterName::VsmVpStatus.into(), None) + .map_or(Vtl::Vtl0, |status| { + hvdef::HvRegisterVsmVpStatus::from(status.as_u64()) + .active_vtl() + .try_into() + .unwrap() + }) } /// Makes a hypercall. @@ -219,12 +203,11 @@ impl HvCall { } } - pub fn set_vp_registers( &mut self, vp: u32, vtl: Option, - vp_context : Option, + vp_context: Option, ) -> Result<(), hvdef::HvError> { const HEADER_SIZE: usize = size_of::(); @@ -235,55 +218,105 @@ impl HvCall { rsvd: [0; 3], }; - header.write_to_prefix(self.input_page().buffer.as_mut_slice()); + let _ = header.write_to_prefix(self.input_page().buffer.as_mut_slice()); let mut input_offset = HEADER_SIZE; let mut count = 0; - let mut write_reg = |reg_name: hvdef::HvRegisterName, reg_value: hvdef::HvRegisterValue| { + let mut write_reg = |reg_name: hvdef::HvRegisterName, reg_value: HvRegisterValue| { let reg = hvdef::hypercall::HvRegisterAssoc { name: reg_name, pad: Default::default(), value: reg_value, }; - reg.write_to_prefix(&mut self.input_page().buffer[input_offset..]); + let _ = reg.write_to_prefix(&mut self.input_page().buffer[input_offset..]); input_offset += size_of::(); count += 1; }; // pub msr_cr_pat: u64, - write_reg(hvdef::HvX64RegisterName::Cr0.into(), vp_context.unwrap().cr0.into()); - write_reg(hvdef::HvX64RegisterName::Cr3.into(), vp_context.unwrap().cr3.into()); - write_reg(hvdef::HvX64RegisterName::Cr4.into(), vp_context.unwrap().cr4.into()); - write_reg(hvdef::HvX64RegisterName::Rip.into(), vp_context.unwrap().rip.into()); - write_reg(hvdef::HvX64RegisterName::Rsp.into(), vp_context.unwrap().rsp.into()); - write_reg(hvdef::HvX64RegisterName::Rflags.into(), vp_context.unwrap().rflags.into()); - write_reg(hvdef::HvX64RegisterName::Cs.into(), vp_context.unwrap().cs.into()); - write_reg(hvdef::HvX64RegisterName::Ss.into(), vp_context.unwrap().ss.into()); - write_reg(hvdef::HvX64RegisterName::Ds.into(), vp_context.unwrap().ds.into()); - write_reg(hvdef::HvX64RegisterName::Es.into(), vp_context.unwrap().es.into()); - write_reg(hvdef::HvX64RegisterName::Fs.into(), vp_context.unwrap().fs.into()); - write_reg(hvdef::HvX64RegisterName::Gs.into(), vp_context.unwrap().gs.into()); - write_reg(hvdef::HvX64RegisterName::Gdtr.into(), vp_context.unwrap().gdtr.into()); - write_reg(hvdef::HvX64RegisterName::Idtr.into(), vp_context.unwrap().idtr.into()); - write_reg(hvdef::HvX64RegisterName::Ldtr.into(), vp_context.unwrap().ldtr.into()); - write_reg(hvdef::HvX64RegisterName::Tr.into(), vp_context.unwrap().tr.into()); - write_reg(hvdef::HvX64RegisterName::Efer.into(), vp_context.unwrap().efer.into()); + write_reg( + HvX64RegisterName::Cr0.into(), + vp_context.unwrap().cr0.into(), + ); + write_reg( + HvX64RegisterName::Cr3.into(), + vp_context.unwrap().cr3.into(), + ); + write_reg( + HvX64RegisterName::Cr4.into(), + vp_context.unwrap().cr4.into(), + ); + write_reg( + HvX64RegisterName::Rip.into(), + vp_context.unwrap().rip.into(), + ); + write_reg( + HvX64RegisterName::Rsp.into(), + vp_context.unwrap().rsp.into(), + ); + write_reg( + HvX64RegisterName::Rflags.into(), + vp_context.unwrap().rflags.into(), + ); + write_reg( + HvX64RegisterName::Cs.into(), + vp_context.unwrap().cs.into(), + ); + write_reg( + HvX64RegisterName::Ss.into(), + vp_context.unwrap().ss.into(), + ); + write_reg( + HvX64RegisterName::Ds.into(), + vp_context.unwrap().ds.into(), + ); + write_reg( + HvX64RegisterName::Es.into(), + vp_context.unwrap().es.into(), + ); + write_reg( + HvX64RegisterName::Fs.into(), + vp_context.unwrap().fs.into(), + ); + write_reg( + HvX64RegisterName::Gs.into(), + vp_context.unwrap().gs.into(), + ); + write_reg( + HvX64RegisterName::Gdtr.into(), + vp_context.unwrap().gdtr.into(), + ); + write_reg( + HvX64RegisterName::Idtr.into(), + vp_context.unwrap().idtr.into(), + ); + write_reg( + HvX64RegisterName::Ldtr.into(), + vp_context.unwrap().ldtr.into(), + ); + write_reg( + HvX64RegisterName::Tr.into(), + vp_context.unwrap().tr.into(), + ); + write_reg( + HvX64RegisterName::Efer.into(), + vp_context.unwrap().efer.into(), + ); let output = self.dispatch_hvcall(hvdef::HypercallCode::HvCallSetVpRegisters, Some(count)); output.result() } - /// Hypercall for setting a register to a value. pub fn set_register( &mut self, name: hvdef::HvRegisterName, - value: hvdef::HvRegisterValue, - vtl: Option + value: HvRegisterValue, + vtl: Option, ) -> Result<(), hvdef::HvError> { const HEADER_SIZE: usize = size_of::(); @@ -294,7 +327,7 @@ impl HvCall { rsvd: [0; 3], }; - header.write_to_prefix(self.input_page().buffer.as_mut_slice()); + let _ = header.write_to_prefix(self.input_page().buffer.as_mut_slice()); let reg = hvdef::hypercall::HvRegisterAssoc { name, @@ -302,7 +335,7 @@ impl HvCall { value, }; - reg.write_to_prefix(&mut self.input_page().buffer[HEADER_SIZE..]); + let _ = reg.write_to_prefix(&mut self.input_page().buffer[HEADER_SIZE..]); let output = self.dispatch_hvcall(hvdef::HypercallCode::HvCallSetVpRegisters, Some(1)); @@ -314,7 +347,7 @@ impl HvCall { &mut self, name: hvdef::HvRegisterName, vtl: Option, - ) -> Result { + ) -> Result { const HEADER_SIZE: usize = size_of::(); let header = hvdef::hypercall::GetSetVpRegisters { @@ -324,12 +357,12 @@ impl HvCall { rsvd: [0; 3], }; - header.write_to_prefix(self.input_page().buffer.as_mut_slice()); - name.write_to_prefix(&mut self.input_page().buffer[HEADER_SIZE..]); + let _ = header.write_to_prefix(self.input_page().buffer.as_mut_slice()); + let _ = name.write_to_prefix(&mut self.input_page().buffer[HEADER_SIZE..]); let output = self.dispatch_hvcall(hvdef::HypercallCode::HvCallGetVpRegisters, Some(1)); output.result()?; - let value = hvdef::HvRegisterValue::read_from_prefix(&self.output_page().buffer).unwrap(); + let value = HvRegisterValue::read_from_prefix(&self.output_page().buffer).unwrap(); Ok(value.0) } @@ -352,12 +385,12 @@ impl HvCall { let remaining_pages = range.end_4k_gpn() - current_page; let count = remaining_pages.min(MAX_INPUT_ELEMENTS as u64); - header.write_to_prefix(self.input_page().buffer.as_mut_slice()); + let _ = header.write_to_prefix(self.input_page().buffer.as_mut_slice()); let mut input_offset = HEADER_SIZE; for i in 0..count { let page_num = current_page + i; - page_num.write_to_prefix(&mut self.input_page().buffer[input_offset..]); + let _ = page_num.write_to_prefix(&mut self.input_page().buffer[input_offset..]); input_offset += size_of::(); } @@ -376,7 +409,11 @@ impl HvCall { /// Hypercall to apply vtl protections to the pages from address start to end #[cfg_attr(target_arch = "x86_64", allow(dead_code))] - pub fn apply_vtl_protections(&mut self, range: MemoryRange, vtl: Vtl) -> Result<(), hvdef::HvError> { + pub fn apply_vtl_protections( + &mut self, + range: MemoryRange, + vtl: Vtl, + ) -> Result<(), hvdef::HvError> { const HEADER_SIZE: usize = size_of::(); const MAX_INPUT_ELEMENTS: usize = (HV_PAGE_SIZE as usize - HEADER_SIZE) / size_of::(); @@ -384,8 +421,8 @@ impl HvCall { partition_id: hvdef::HV_PARTITION_ID_SELF, map_flags: hvdef::HV_MAP_GPA_PERMISSIONS_NONE, target_vtl: HvInputVtl::new() - .with_target_vtl_value(vtl.into()) - .with_use_target_vtl(true), + .with_target_vtl_value(vtl.into()) + .with_use_target_vtl(true), reserved: [0; 3], }; @@ -394,12 +431,12 @@ impl HvCall { let remaining_pages = range.end_4k_gpn() - current_page; let count = remaining_pages.min(MAX_INPUT_ELEMENTS as u64); - header.write_to_prefix(self.input_page().buffer.as_mut_slice()); + let _ = header.write_to_prefix(self.input_page().buffer.as_mut_slice()); let mut input_offset = HEADER_SIZE; for i in 0..count { let page_num = current_page + i; - page_num.write_to_prefix(&mut self.input_page().buffer[input_offset..]); + let _ = page_num.write_to_prefix(&mut self.input_page().buffer[input_offset..]); input_offset += size_of::(); } @@ -416,29 +453,60 @@ impl HvCall { Ok(()) } - #[cfg(target_arch = "x86_64")] /// Hypercall to get the current VTL VP context pub fn get_current_vtl_vp_context(&mut self) -> Result { - use hvdef::HvX64RegisterName; + use HvX64RegisterName; use zerocopy::FromZeros; - let mut context :InitialVpContextX64 = FromZeros::new_zeroed(); - context.cr0 = self.get_register(HvX64RegisterName::Cr0.into(), None)?.as_u64(); - context.cr3 = self.get_register(HvX64RegisterName::Cr3.into(), None)?.as_u64(); - context.cr4 = self.get_register(HvX64RegisterName::Cr4.into(), None)?.as_u64(); - context.rip = self.get_register(HvX64RegisterName::Rip.into(), None)?.as_u64(); - context.rsp = self.get_register(HvX64RegisterName::Rsp.into(), None)?.as_u64(); - context.rflags = self.get_register(HvX64RegisterName::Rflags.into(), None)?.as_u64(); - context.cs = self.get_register(HvX64RegisterName::Cs.into(), None)?.as_segment(); - context.ss = self.get_register(HvX64RegisterName::Ss.into(), None)?.as_segment(); - context.ds = self.get_register(HvX64RegisterName::Ds.into(), None)?.as_segment(); - context.es = self.get_register(HvX64RegisterName::Es.into(), None)?.as_segment(); - context.fs = self.get_register(HvX64RegisterName::Fs.into(), None)?.as_segment(); - context.gs = self.get_register(HvX64RegisterName::Gs.into(), None)?.as_segment(); - context.gdtr = self.get_register(HvX64RegisterName::Gdtr.into(), None)?.as_table(); - context.idtr = self.get_register(HvX64RegisterName::Idtr.into(), None)?.as_table(); - context.tr = self.get_register(HvX64RegisterName::Tr.into(), None)?.as_segment(); - context.efer = self.get_register(HvX64RegisterName::Efer.into(), None)?.as_u64(); + let mut context: InitialVpContextX64 = FromZeros::new_zeroed(); + context.cr0 = self + .get_register(HvX64RegisterName::Cr0.into(), None)? + .as_u64(); + context.cr3 = self + .get_register(HvX64RegisterName::Cr3.into(), None)? + .as_u64(); + context.cr4 = self + .get_register(HvX64RegisterName::Cr4.into(), None)? + .as_u64(); + context.rip = self + .get_register(HvX64RegisterName::Rip.into(), None)? + .as_u64(); + context.rsp = self + .get_register(HvX64RegisterName::Rsp.into(), None)? + .as_u64(); + context.rflags = self + .get_register(HvX64RegisterName::Rflags.into(), None)? + .as_u64(); + context.cs = self + .get_register(HvX64RegisterName::Cs.into(), None)? + .as_segment(); + context.ss = self + .get_register(HvX64RegisterName::Ss.into(), None)? + .as_segment(); + context.ds = self + .get_register(HvX64RegisterName::Ds.into(), None)? + .as_segment(); + context.es = self + .get_register(HvX64RegisterName::Es.into(), None)? + .as_segment(); + context.fs = self + .get_register(HvX64RegisterName::Fs.into(), None)? + .as_segment(); + context.gs = self + .get_register(HvX64RegisterName::Gs.into(), None)? + .as_segment(); + context.gdtr = self + .get_register(HvX64RegisterName::Gdtr.into(), None)? + .as_table(); + context.idtr = self + .get_register(HvX64RegisterName::Idtr.into(), None)? + .as_table(); + context.tr = self + .get_register(HvX64RegisterName::Tr.into(), None)? + .as_segment(); + context.efer = self + .get_register(HvX64RegisterName::Efer.into(), None)? + .as_u64(); Ok(context) } @@ -446,48 +514,57 @@ impl HvCall { let control: hvdef::hypercall::Control = hvdef::hypercall::Control::new() .with_code(hvdef::HypercallCode::HvCallVtlCall.0) .with_rep_count(0); - - // SAFETY: Invoking hypercall per TLFS spec - unsafe { - invoke_hypercall_vtl( - control, - ); - } + invoke_hypercall_vtl(control); } pub fn vtl_return() { let control: hvdef::hypercall::Control = hvdef::hypercall::Control::new() .with_code(hvdef::HypercallCode::HvCallVtlReturn.0) .with_rep_count(0); - // SAFETY: Invoking hypercall per TLFS spec - unsafe { - invoke_hypercall_vtl(control); - } + invoke_hypercall_vtl(control); } - pub fn enable_vtl_protection(&mut self, vp_index: u32, vtl: HvInputVtl) -> Result<(), hvdef::HvError> { - let hvreg = self.get_register(hvdef::HvX64RegisterName::VsmPartitionConfig.into(), Some(vtl))?; - let mut hvreg: HvRegisterVsmPartitionConfig = HvRegisterVsmPartitionConfig::from_bits(hvreg.as_u64()); + pub fn enable_vtl_protection( + &mut self, + vtl: HvInputVtl, + ) -> Result<(), hvdef::HvError> { + let hvreg = self.get_register( + HvX64RegisterName::VsmPartitionConfig.into(), + Some(vtl), + )?; + let mut hvreg: HvRegisterVsmPartitionConfig = + HvRegisterVsmPartitionConfig::from_bits(hvreg.as_u64()); hvreg.set_enable_vtl_protection(true); // hvreg.set_intercept_page(true); // hvreg.set_default_vtl_protection_mask(0b11); // hvreg.set_intercept_enable_vtl_protection(true); let bits = hvreg.into_bits(); - let hvre: HvRegisterValue = hvdef::HvRegisterValue::from(bits); - self.set_register(HvX64RegisterName::VsmPartitionConfig.into(), hvre, Some(vtl)) + let hvre: HvRegisterValue = HvRegisterValue::from(bits); + self.set_register( + HvX64RegisterName::VsmPartitionConfig.into(), + hvre, + Some(vtl), + ) } #[cfg(target_arch = "x86_64")] - pub fn enable_vp_vtl(&mut self, vp_index: u32, target_vtl : Vtl, vp_context : Option) -> Result<(), hvdef::HvError> { + pub fn enable_vp_vtl( + &mut self, + vp_index: u32, + target_vtl: Vtl, + vp_context: Option, + ) -> Result<(), hvdef::HvError> { let header = hvdef::hypercall::EnableVpVtlX64 { partition_id: hvdef::HV_PARTITION_ID_SELF, vp_index, target_vtl: target_vtl.into(), reserved: [0; 3], - vp_vtl_context: vp_context.unwrap_or( zerocopy::FromZeros::new_zeroed()), + vp_vtl_context: vp_context.unwrap_or(zerocopy::FromZeros::new_zeroed()), }; - header.write_to_prefix(self.input_page().buffer.as_mut_slice()).expect("size of enable_vp_vtl header is not correct"); + header + .write_to_prefix(self.input_page().buffer.as_mut_slice()) + .expect("size of enable_vp_vtl header is not correct"); let output = self.dispatch_hvcall(hvdef::HypercallCode::HvCallEnableVpVtl, None); match output.result() { @@ -497,17 +574,24 @@ impl HvCall { } #[cfg(target_arch = "x86_64")] - pub fn start_virtual_processor(&mut self, vp_index: u32, target_vtl : Vtl, vp_context : Option) -> Result<(), hvdef::HvError> { + pub fn start_virtual_processor( + &mut self, + vp_index: u32, + target_vtl: Vtl, + vp_context: Option, + ) -> Result<(), hvdef::HvError> { let header = hvdef::hypercall::StartVirtualProcessorX64 { partition_id: hvdef::HV_PARTITION_ID_SELF, - vp_index: vp_index, + vp_index, target_vtl: target_vtl.into(), vp_context: vp_context.unwrap_or(zerocopy::FromZeros::new_zeroed()), rsvd0: 0u8, rsvd1: 0u16, }; - header.write_to_prefix(self.input_page().buffer.as_mut_slice()).expect("size of start_virtual_processor header is not correct"); + header + .write_to_prefix(self.input_page().buffer.as_mut_slice()) + .expect("size of start_virtual_processor header is not correct"); let output = self.dispatch_hvcall(hvdef::HypercallCode::HvCallStartVirtualProcessor, None); match output.result() { @@ -516,11 +600,14 @@ impl HvCall { } } - pub fn enable_partition_vtl(&mut self, partition_id: u64, target_vtl : Vtl) -> Result<(), hvdef::HvError> { - let flags: EnablePartitionVtlFlags = - EnablePartitionVtlFlags::new() - .with_enable_mbec(false) - .with_enable_supervisor_shadow_stack(false); + pub fn enable_partition_vtl( + &mut self, + partition_id: u64, + target_vtl: Vtl, + ) -> Result<(), hvdef::HvError> { + let flags: EnablePartitionVtlFlags = EnablePartitionVtlFlags::new() + .with_enable_mbec(false) + .with_enable_supervisor_shadow_stack(false); let header = hvdef::hypercall::EnablePartitionVtl { partition_id, @@ -589,7 +676,7 @@ impl HvCall { let remaining_pages = range.end_4k_gpn() - current_page; let count = remaining_pages.min(MAX_INPUT_ELEMENTS as u64); - header.write_to_prefix(self.input_page().buffer.as_mut_slice()); + let _ = header.write_to_prefix(self.input_page().buffer.as_mut_slice()); let output = self.dispatch_hvcall( hvdef::HypercallCode::HvCallAcceptGpaPages, @@ -625,8 +712,8 @@ impl HvCall { const MAX_PER_CALL: usize = 512; for hw_ids in hw_ids.chunks(MAX_PER_CALL) { - header.write_to_prefix(self.input_page().buffer.as_mut_slice()); - hw_ids.write_to_prefix(&mut self.input_page().buffer[header.as_bytes().len()..]); + let _ = header.write_to_prefix(self.input_page().buffer.as_mut_slice()); + let _ = hw_ids.write_to_prefix(&mut self.input_page().buffer[header.as_bytes().len()..]); // SAFETY: The input header and rep slice are the correct types for this hypercall. // The hypercall output is validated right after the hypercall is issued. @@ -659,4 +746,4 @@ pub type HwId = u32; /// The "hardware ID" used for [`HvCall::get_vp_index_from_hw_id`]. This is the /// MPIDR on ARM64. #[cfg(target_arch = "aarch64")] -pub type HwId = u64; \ No newline at end of file +pub type HwId = u64; diff --git a/opentmk/src/uefi/hypvctx.rs b/opentmk/src/uefi/hypvctx.rs index 79e74cc057..86f24eee96 100644 --- a/opentmk/src/uefi/hypvctx.rs +++ b/opentmk/src/uefi/hypvctx.rs @@ -3,21 +3,16 @@ use super::{ hypercall::HvCall, }; use crate::uefi::alloc::ALLOCATOR; -use crate::{debuglog, slog::AssertResult}; -use crate::{ - infolog, - slog::AssertOption, - sync::{Channel, Receiver, Sender}, -}; +use crate::tmk_assert::AssertResult; +use crate::tmk_assert::AssertOption; use alloc::collections::btree_map::BTreeMap; use alloc::collections::linked_list::LinkedList; use alloc::{boxed::Box, vec::Vec}; use core::alloc::{GlobalAlloc, Layout}; use core::arch::asm; use core::ops::Range; -use core::sync::atomic::{AtomicBool, Ordering}; use hvdef::hypercall::{HvInputVtl, InitialVpContextX64}; -use hvdef::{HvAllArchRegisterName, HvRegisterName, Vtl}; +use hvdef::Vtl; use memory_range::MemoryRange; use minimal_rt::arch::msr::{read_msr, write_msr}; use spin::Mutex; @@ -28,34 +23,26 @@ type ComandTable = BTreeMap, Vtl)>>; static mut CMD: Mutex = Mutex::new(BTreeMap::new()); +#[allow(static_mut_refs)] fn cmdt() -> &'static Mutex { unsafe { &CMD } } -struct VpContext { - #[cfg(target_arch = "x86_64")] - ctx: InitialVpContextX64, - #[cfg(target_arch = "aarch64")] - ctx: InitialVpContextAarch64, -} - fn register_command_queue(vp_index: u32) { - unsafe { - debuglog!("registering command queue for vp: {}", vp_index); - if CMD.lock().get(&vp_index).is_none() { - CMD.lock().insert(vp_index, LinkedList::new()); - debuglog!("registered command queue for vp: {}", vp_index); + log::debug!("registering command queue for vp: {}", vp_index); + if cmdt().lock().get(&vp_index).is_none() { + cmdt().lock().insert(vp_index, LinkedList::new()); + log::debug!("registered command queue for vp: {}", vp_index); } else { - debuglog!("command queue already registered for vp: {}", vp_index); + log::debug!("command queue already registered for vp: {}", vp_index); } - } } pub struct HvTestCtx { pub hvcall: HvCall, pub vp_runing: Vec<(u32, (bool, bool))>, pub my_vp_idx: u32, - senders: Vec<(u64, Sender<(Box, Vtl)>)>, + pub my_vtl: Vtl, } impl Drop for HvTestCtx { @@ -138,8 +125,8 @@ impl TestCtxTrait for HvTestCtx { } let is_vp_running = self.vp_runing.iter_mut().find(|x| x.0 == vp_index); - if let Some(running_vtl) = is_vp_running { - debuglog!("both vtl0 and vtl1 are running for VP: {:?}", vp_index); + if let Some(_running_vtl) = is_vp_running { + log::debug!("both vtl0 and vtl1 are running for VP: {:?}", vp_index); } else { if vp_index == 0 { let vp_context = self @@ -158,7 +145,6 @@ impl TestCtxTrait for HvTestCtx { self.switch_to_high_vtl(); self.vp_runing.push((vp_index, (true, true))); } else { - let my_idx = self.my_vp_idx; cmdt().lock().get_mut(&self.my_vp_idx).unwrap().push_back(( Box::new(move |ctx| { ctx.enable_vp_vtl_with_default_context(vp_index, Vtl::Vtl1); @@ -186,7 +172,7 @@ impl TestCtxTrait for HvTestCtx { .get_mut(&vp_index) .unwrap() .push_back((cmd, vtl)); - if vp_index == self.my_vp_idx && self.hvcall.vtl != vtl { + if vp_index == self.my_vp_idx && self.my_vtl != vtl { if vtl == Vtl::Vtl0 { self.switch_to_low_vtl(); } else { @@ -218,7 +204,7 @@ impl TestCtxTrait for HvTestCtx { self.hvcall .enable_partition_vtl(hvdef::HV_PARTITION_ID_SELF, vtl) .expect_assert("Failed to enable VTL1 for the partition"); - infolog!("enabled vtl protections for the partition."); + log::info!("enabled vtl protections for the partition."); } fn setup_interrupt_handler(&mut self) { crate::arch::interrupt::init(); @@ -226,10 +212,10 @@ impl TestCtxTrait for HvTestCtx { fn setup_vtl_protection(&mut self) { self.hvcall - .enable_vtl_protection(0, HvInputVtl::CURRENT_VTL) + .enable_vtl_protection(HvInputVtl::CURRENT_VTL) .expect_assert("Failed to enable VTL protection, vtl1"); - infolog!("enabled vtl protections for the partition."); + log::info!("enabled vtl protections for the partition."); } fn setup_secure_intercept(&mut self, interrupt_idx: u8) { @@ -241,7 +227,7 @@ impl TestCtxTrait for HvTestCtx { let reg = (gpn << 12) | 0x1; unsafe { write_msr(hvdef::HV_X64_MSR_SIMP, reg.into()) }; - infolog!("Successfuly set the SIMP register."); + log::info!("Successfuly set the SIMP register."); let reg = unsafe { read_msr(hvdef::HV_X64_MSR_SINT0) }; let mut reg: hvdef::HvSynicSint = reg.into(); @@ -250,7 +236,7 @@ impl TestCtxTrait for HvTestCtx { reg.set_auto_eoi(true); self.write_msr(hvdef::HV_X64_MSR_SINT0, reg.into()); - infolog!("Successfuly set the SINT0 register."); + log::info!("Successfuly set the SINT0 register."); } fn apply_vtl_protection_for_memory(&mut self, range: Range, vtl: Vtl) { @@ -268,7 +254,7 @@ impl TestCtxTrait for HvTestCtx { } fn start_running_vp_with_default_context(&mut self, cmd: VpExecutor) { - let (vp_index, vtl, cmd) = cmd.get(); + let (vp_index, vtl, _cmd) = cmd.get(); let vp_ctx = self .get_default_context() .expect_assert("error: failed to get default context"); @@ -282,7 +268,6 @@ impl TestCtxTrait for HvTestCtx { Vtl::Vtl0 => 0, Vtl::Vtl1 => 1, Vtl::Vtl2 => 2, - _ => panic!("error: invalid vtl"), }; let vp_context = self .get_default_context() @@ -310,19 +295,19 @@ impl TestCtxTrait for HvTestCtx { } #[cfg(target_arch = "x86_64")] - fn set_interupt_idx(&mut self, interrupt_idx: u8, handler: fn()) { + fn set_interrupt_idx(&mut self, interrupt_idx: u8, handler: fn()) { crate::arch::interrupt::set_handler(interrupt_idx, handler); } + #[cfg(target_arch = "x86_64")] fn get_vp_count(&self) -> u32 { - let mut result: u32 = 0; - + let mut result: u32; unsafe { // Call CPUID with EAX=1, but work around the rbx constraint asm!( "push rbx", // Save rbx "cpuid", // Execute CPUID - "mov {result}, rbx", // Store ebx to our result variable + "mov {result:r}, rbx", // Store ebx to our result variable "pop rbx", // Restore rbx in("eax") 1u32, // Input: CPUID leaf 1 out("ecx") _, // Output registers (not used) @@ -363,7 +348,7 @@ impl TestCtxTrait for HvTestCtx { } fn get_current_vtl(&self) -> Vtl { - self.hvcall.vtl + self.my_vtl } } @@ -373,7 +358,7 @@ impl HvTestCtx { hvcall: HvCall::new(), vp_runing: Vec::new(), my_vp_idx: 0, - senders: Vec::new(), + my_vtl: Vtl::Vtl0, } } @@ -383,6 +368,7 @@ impl HvTestCtx { for i in 0..vp_count { register_command_queue(i); } + self.my_vtl = self.hvcall.vtl(); } fn exec_handler() { @@ -400,14 +386,14 @@ impl HvTestCtx { let mut cmd: Option> = None; { - let mut d = unsafe { CMD.lock() }; - let mut d = d.get_mut(&ctx.my_vp_idx); + let mut cmdt = cmdt().lock(); + let d = cmdt.get_mut(&ctx.my_vp_idx); if d.is_some() { - let mut d = d.unwrap(); + let d = d.unwrap(); if !d.is_empty() { - let (c, v) = d.front().unwrap(); - if *v == ctx.hvcall.vtl { - let (c, v) = d.pop_front().unwrap(); + let (_c, v) = d.front().unwrap(); + if *v == ctx.my_vtl { + let (c, _v) = d.pop_front().unwrap(); cmd = Some(c); } else { vtl = Some(*v); @@ -417,7 +403,7 @@ impl HvTestCtx { } if let Some(vtl) = vtl { - if (vtl == Vtl::Vtl0) { + if vtl == Vtl::Vtl0 { ctx.switch_to_low_vtl(); } else { ctx.switch_to_high_vtl(); @@ -455,11 +441,6 @@ impl HvTestCtx { let fn_address = fn_ptr as u64; vp_context.rip = fn_address; vp_context.rsp = stack_top; - // print stack range - let stack_range = Range { - start: x as u64, - end: x as u64 + sz as u64, - }; Ok(vp_context) } } diff --git a/opentmk/src/uefi/init.rs b/opentmk/src/uefi/init.rs index 1f0535d479..aa2eac3402 100644 --- a/opentmk/src/uefi/init.rs +++ b/opentmk/src/uefi/init.rs @@ -1,21 +1,19 @@ -use core::alloc::{GlobalAlloc, Layout}; +use uefi::{boot::{exit_boot_services, MemoryType}, guid, CStr16, Status}; -use uefi::{boot::{exit_boot_services, MemoryType}, guid, println, CStr16, Status}; - -use crate::infolog; - -use super::{alloc::ALLOCATOR}; +use super::alloc::ALLOCATOR; +const EFI_GUID: uefi::Guid = guid!("610b9e98-c6f6-47f8-8b47-2d2da0d52a91"); +const OS_LOADER_INDICATIONS: &'static str = "OsLoaderIndications"; fn enable_uefi_vtl_protection() { let mut buf = vec![0u8; 1024]; let mut str_buff = vec![0u16; 1024]; let os_loader_indications_key = - CStr16::from_str_with_buf(&"OsLoaderIndications", str_buff.as_mut_slice()).unwrap(); + CStr16::from_str_with_buf(OS_LOADER_INDICATIONS, str_buff.as_mut_slice()).unwrap(); let os_loader_indications_result = uefi::runtime::get_variable( os_loader_indications_key, - &uefi::runtime::VariableVendor(guid!("610b9e98-c6f6-47f8-8b47-2d2da0d52a91")), + &uefi::runtime::VariableVendor(EFI_GUID), buf.as_mut(), ) .expect("Failed to get OsLoaderIndications"); @@ -31,27 +29,28 @@ fn enable_uefi_vtl_protection() { let _ = uefi::runtime::set_variable( os_loader_indications_key, - &uefi::runtime::VariableVendor(guid!("610b9e98-c6f6-47f8-8b47-2d2da0d52a91")), + &uefi::runtime::VariableVendor(EFI_GUID), os_loader_indications_result.1, &os_loader_indications, ) .expect("Failed to set OsLoaderIndications"); - let os_loader_indications_result = uefi::runtime::get_variable( + let _os_loader_indications_result = uefi::runtime::get_variable( os_loader_indications_key, - &uefi::runtime::VariableVendor(guid!("610b9e98-c6f6-47f8-8b47-2d2da0d52a91")), + &uefi::runtime::VariableVendor(EFI_GUID), buf.as_mut(), ) .expect("Failed to get OsLoaderIndications"); - let _ = unsafe { exit_boot_services(MemoryType::BOOT_SERVICES_DATA) }; + let _memory_map = unsafe { exit_boot_services(MemoryType::BOOT_SERVICES_DATA) }; } pub fn init() -> Result<(), Status> { - let r: bool = unsafe { ALLOCATOR.init(2048) }; + let r: bool = ALLOCATOR.init(2048); if r == false { return Err(Status::ABORTED); } + crate::tmk_logger::init().expect("Failed to init logger"); enable_uefi_vtl_protection(); Ok(()) } \ No newline at end of file diff --git a/opentmk/src/uefi/mod.rs b/opentmk/src/uefi/mod.rs index 08cadaa5b5..ed2b2e0001 100644 --- a/opentmk/src/uefi/mod.rs +++ b/opentmk/src/uefi/mod.rs @@ -9,21 +9,7 @@ pub mod init; mod rt; mod tests; -use crate::slog::{AssertOption, AssertResult}; -use crate::sync::{Channel, Receiver, Sender}; -use crate::uefi::alloc::ALLOCATOR; -use crate::{infolog, tmk_assert}; -use ::alloc::boxed::Box; -use ::alloc::vec::Vec; -use alloc::SIZE_1MB; -use context::{TestCtxTrait, VpExecutor}; -use core::alloc::{GlobalAlloc, Layout}; -use core::cell::RefCell; -use core::ops::Range; -use core::sync::atomic::{AtomicI32, Ordering}; -use hvdef::hypercall::HvInputVtl; -use hvdef::Vtl; -use hypvctx::HvTestCtx; +use crate::tmk_assert::AssertResult; use init::init; use uefi::entry; use uefi::Status; diff --git a/opentmk/src/uefi/rt.rs b/opentmk/src/uefi/rt.rs index 1623e2f960..d8a80b4b53 100644 --- a/opentmk/src/uefi/rt.rs +++ b/opentmk/src/uefi/rt.rs @@ -5,17 +5,11 @@ #![cfg(target_os = "uefi")] // UNSAFETY: Raw assembly needed for panic handling to abort. -#![expect(unsafe_code)] - -use crate::arch::serial::{InstrIoAccess, Serial}; -use crate::slog; -use crate::sync::Mutex; use core::arch::asm; -use core::fmt::Write; #[panic_handler] fn panic_handler(panic: &core::panic::PanicInfo<'_>) -> ! { - crate::errorlog!("Panic at runtime: {}", panic); + log::error!("Panic at runtime: {}", panic); unsafe { asm!("int 8H"); } diff --git a/opentmk/src/uefi/tests/hv_misc.rs b/opentmk/src/uefi/tests/hv_misc.rs index 3bda306d70..56229d0fb0 100644 --- a/opentmk/src/uefi/tests/hv_misc.rs +++ b/opentmk/src/uefi/tests/hv_misc.rs @@ -1,12 +1,13 @@ +#![allow(warnings)] // WIP : This test is not yet complete and is not expected to pass. // // This test is to verify that the VTL protections are working as expected. // The stack values in VTL0 are changing after interrupt handling in VTL1. -use crate::slog::{AssertOption, AssertResult}; +use crate::tmk_assert::{AssertOption, AssertResult}; use crate::sync::{Channel, Receiver, Sender}; use crate::uefi::alloc::{ALLOCATOR, SIZE_1MB}; use crate::uefi::{context, hypvctx}; -use crate::{infolog, tmk_assert}; +use crate::{tmk_assert}; use ::alloc::boxed::Box; use alloc::sync::Arc; use ::alloc::vec::Vec; @@ -26,23 +27,23 @@ static mut HEAPX: RefCell<*mut u8> = RefCell::new(0 as *mut u8); static mut CON: AtomicI32 = AtomicI32::new(0); pub fn exec(ctx: &mut hypvctx::HvTestCtx ) { - infolog!("ctx ptr: {:p}", &ctx as *const _); + log::info!("ctx ptr: {:p}", &ctx as *const _); let mut vp_count = ctx.get_vp_count(); tmk_assert!(vp_count == 8, "vp count should be 8"); ctx.setup_interrupt_handler(); - infolog!("set intercept handler successfully!"); + log::info!("set intercept handler successfully!"); ctx.setup_partition_vtl(Vtl::Vtl1); ctx.start_on_vp( VpExecutor::new(0, Vtl::Vtl1).command(move |ctx: &mut dyn TestCtxTrait| { - infolog!("successfully started running VTL1 on vp0."); + log::info!("successfully started running VTL1 on vp0."); ctx.setup_secure_intercept(0x30); - ctx.set_interupt_idx(0x30, || { - infolog!("interrupt fired!"); + ctx.set_interrupt_idx(0x30, || { + log::info!("interrupt fired!"); let mut hv_test_ctx = HvTestCtx::new(); hv_test_ctx.init(); @@ -51,15 +52,15 @@ pub fn exec(ctx: &mut hypvctx::HvTestCtx ) { let cp = HvRegisterVsmVpStatus::from_bits(c as u64); - infolog!("VSM VP Status: {:?}", cp); + log::info!("VSM VP Status: {:?}", cp); - infolog!("interrupt handled!"); + log::info!("interrupt handled!"); }); let layout = Layout::from_size_align(SIZE_1MB, 4096).expect("msg: failed to create layout"); let ptr = unsafe { ALLOCATOR.alloc(layout) }; - infolog!("allocated some memory in the heap from vtl1"); + log::info!("allocated some memory in the heap from vtl1"); unsafe { let mut z = HEAPX.borrow_mut(); *z = ptr; @@ -69,7 +70,7 @@ pub fn exec(ctx: &mut hypvctx::HvTestCtx ) { let size = layout.size(); ctx.setup_vtl_protection(); - infolog!("enabled vtl protections for the partition."); + log::info!("enabled vtl protections for the partition."); let range = Range { start: ptr as u64, @@ -78,26 +79,26 @@ pub fn exec(ctx: &mut hypvctx::HvTestCtx ) { ctx.apply_vtl_protection_for_memory(range, Vtl::Vtl1); - infolog!("moving to vtl0 to attempt to read the heap memory"); + log::info!("moving to vtl0 to attempt to read the heap memory"); ctx.switch_to_low_vtl(); }), ); ctx.queue_command_vp(VpExecutor::new(0, Vtl::Vtl1).command(move |ctx| { - infolog!("successfully started running VTL1 on vp0."); + log::info!("successfully started running VTL1 on vp0."); ctx.switch_to_low_vtl(); })); - infolog!("ctx ptr: {:p}", &ctx as *const _); + log::info!("ctx ptr: {:p}", &ctx as *const _); let mut l = 0u64; unsafe { asm!("mov {}, rsp", out(reg) l) }; - infolog!("rsp: 0x{:x}", l); + log::info!("rsp: 0x{:x}", l); unsafe { - infolog!("Attempting to read heap memory from vtl0"); + log::info!("Attempting to read heap memory from vtl0"); let heapx = *HEAPX.borrow(); let val = *(heapx.add(10)); - infolog!( + log::info!( "reading mutated heap memory from vtl0(it should not be 0xAA): 0x{:x}", val ); @@ -108,27 +109,27 @@ pub fn exec(ctx: &mut hypvctx::HvTestCtx ) { } unsafe { asm!("mov {}, rsp", out(reg) l) }; - infolog!("rsp: 0x{:x}", l); + log::info!("rsp: 0x{:x}", l); // let (mut tx, mut rx) = Channel::new(1); // { // let mut tx = tx.clone(); // ctx.start_on_vp(VpExecutor::new(2, Vtl::Vtl0).command( // move |ctx: &mut dyn TestCtxTrait| { - // infolog!("Hello form vtl0 on vp2!"); + // log::info!("Hello form vtl0 on vp2!"); // tx.send(()); // }, // )); // } - infolog!("ctx ptr: {:p}", &ctx as *const _); + log::info!("ctx ptr: {:p}", &ctx as *const _); let c = ctx.get_vp_count(); tmk_assert!(c == 8, "vp count should be 8"); // rx.recv(); - infolog!("we are in vtl0 now!"); - infolog!("we reached the end of the test"); + log::info!("we are in vtl0 now!"); + log::info!("we reached the end of the test"); loop { } diff --git a/opentmk/src/uefi/tests/hv_processor.rs b/opentmk/src/uefi/tests/hv_processor.rs index 6839669694..6547087202 100644 --- a/opentmk/src/uefi/tests/hv_processor.rs +++ b/opentmk/src/uefi/tests/hv_processor.rs @@ -1,8 +1,7 @@ -use alloc::vec::Vec; use hvdef::Vtl; use crate::{ - criticallog, infolog, sync::{self, Mutex}, tmk_assert, uefi::context::{TestCtxTrait, VpExecutor} + tmk_assert, uefi::context::{TestCtxTrait, VpExecutor} }; pub fn exec(ctx: &mut dyn TestCtxTrait) { @@ -14,60 +13,60 @@ pub fn exec(ctx: &mut dyn TestCtxTrait) { // Testing BSP VTL Bringup { - let (mut tx, mut rx) = crate::sync::Channel::new().split(); + let (tx, rx) = crate::sync::Channel::new().split(); ctx.start_on_vp(VpExecutor::new(0, Vtl::Vtl1).command( move |ctx: &mut dyn TestCtxTrait| { let vp = ctx.get_current_vp(); - infolog!("vp: {}", vp); + log::info!("vp: {}", vp); tmk_assert!(vp == 0, "vp should be equal to 0"); let vtl = ctx.get_current_vtl(); - infolog!("vtl: {:?}", vtl); + log::info!("vtl: {:?}", vtl); tmk_assert!(vtl == Vtl::Vtl1, "vtl should be Vtl1 for BSP"); - tx.send(()); + _ = tx.send(()); ctx.switch_to_low_vtl(); }, )); - rx.recv(); + _ = rx.recv(); } for i in 1..vp_count { // Testing VTL1 { - let (mut tx, mut rx) = crate::sync::Channel::new().split(); + let (tx, rx) = crate::sync::Channel::new().split(); ctx.start_on_vp(VpExecutor::new(i, Vtl::Vtl1).command( move |ctx: &mut dyn TestCtxTrait| { let vp = ctx.get_current_vp(); - infolog!("vp: {}", vp); + log::info!("vp: {}", vp); tmk_assert!(vp == i, format!("vp should be equal to {}", i)); let vtl = ctx.get_current_vtl(); - infolog!("vtl: {:?}", vtl); - tmk_assert!(vtl == Vtl::Vtl1, format!("vtl should be Vtl0 for VP {}", i)); - tx.send(()); + log::info!("vtl: {:?}", vtl); + tmk_assert!(vtl == Vtl::Vtl1, format!("vtl should be Vtl1 for VP {}", i)); + _ = tx.send(()); }, )); - rx.clone().recv(); + _ = rx.recv(); } // Testing VTL0 { - let (mut tx, mut rx) = crate::sync::Channel::new().split(); + let (tx, rx) = crate::sync::Channel::new().split(); ctx.start_on_vp(VpExecutor::new(i, Vtl::Vtl0).command( move |ctx: &mut dyn TestCtxTrait| { let vp = ctx.get_current_vp(); - infolog!("vp: {}", vp); + log::info!("vp: {}", vp); tmk_assert!(vp == i, format!("vp should be equal to {}", i)); let vtl = ctx.get_current_vtl(); - infolog!("vtl: {:?}", vtl); + log::info!("vtl: {:?}", vtl); tmk_assert!(vtl == Vtl::Vtl0, format!("vtl should be Vtl0 for VP {}", i)); - tx.send(()); + _ = tx.send(()); }, )); - rx.clone().recv(); + _ = rx.recv(); } } - criticallog!("All VPs have been tested"); + log::warn!("All VPs have been tested"); } diff --git a/opentmk/src/uefi/tests/mod.rs b/opentmk/src/uefi/tests/mod.rs index 9f5a7be616..665873ac3d 100644 --- a/opentmk/src/uefi/tests/mod.rs +++ b/opentmk/src/uefi/tests/mod.rs @@ -1,5 +1,3 @@ -use alloc::sync::Arc; - use super::hypvctx::HvTestCtx; pub mod hv_processor; From 53e0da2eef29fc159c969771d57b4058958e6a6c Mon Sep 17 00:00:00 2001 From: Mayank Kumar Date: Mon, 12 May 2025 05:50:20 +0000 Subject: [PATCH 06/23] chore: resolve PR feedback --- Cargo.toml | 12 +++++++-- opentmk/{ => opentmk}/Cargo.toml | 26 +++++++++---------- opentmk/{ => opentmk}/README.md | 2 +- opentmk/{ => opentmk}/build_deploy.sh | 0 .../src/arch/aarch64/hypercall.rs | 0 opentmk/{ => opentmk}/src/arch/aarch64/mod.rs | 0 opentmk/{ => opentmk}/src/arch/mod.rs | 0 .../src/arch/x86_64/hypercall.rs | 0 .../src/arch/x86_64/interrupt.rs | 2 +- .../arch/x86_64/interrupt_handler_register.rs | 2 +- opentmk/{ => opentmk}/src/arch/x86_64/mod.rs | 0 .../{ => opentmk}/src/arch/x86_64/serial.rs | 2 +- opentmk/{ => opentmk}/src/main.rs | 6 ----- opentmk/{ => opentmk}/src/tests/hv_misc.rs | 0 .../{ => opentmk}/src/tests/hv_processor.rs | 0 opentmk/{ => opentmk}/src/tests/mod.rs | 0 opentmk/{ => opentmk}/src/tmk_assert.rs | 10 +++---- opentmk/{ => opentmk}/src/tmk_logger.rs | 2 +- opentmk/{ => opentmk}/src/uefi/alloc.rs | 2 +- opentmk/{ => opentmk}/src/uefi/context.rs | 0 opentmk/{ => opentmk}/src/uefi/hypercall.rs | 0 opentmk/{ => opentmk}/src/uefi/hypvctx.rs | 2 +- opentmk/{ => opentmk}/src/uefi/init.rs | 0 opentmk/{ => opentmk}/src/uefi/mod.rs | 0 opentmk/{ => opentmk}/src/uefi/rt.rs | 0 .../{ => opentmk}/src/uefi/tests/hv_misc.rs | 2 +- .../src/uefi/tests/hv_processor.rs | 7 ++--- opentmk/{ => opentmk}/src/uefi/tests/mod.rs | 0 opentmk/sync/Cargo.toml | 12 +++++++++ opentmk/{src/sync.rs => sync/src/lib.rs} | 5 +++- 30 files changed, 56 insertions(+), 38 deletions(-) rename opentmk/{ => opentmk}/Cargo.toml (62%) rename opentmk/{ => opentmk}/README.md (78%) rename opentmk/{ => opentmk}/build_deploy.sh (100%) rename opentmk/{ => opentmk}/src/arch/aarch64/hypercall.rs (100%) rename opentmk/{ => opentmk}/src/arch/aarch64/mod.rs (100%) rename opentmk/{ => opentmk}/src/arch/mod.rs (100%) rename opentmk/{ => opentmk}/src/arch/x86_64/hypercall.rs (100%) rename opentmk/{ => opentmk}/src/arch/x86_64/interrupt.rs (98%) rename opentmk/{ => opentmk}/src/arch/x86_64/interrupt_handler_register.rs (99%) rename opentmk/{ => opentmk}/src/arch/x86_64/mod.rs (100%) rename opentmk/{ => opentmk}/src/arch/x86_64/serial.rs (99%) rename opentmk/{ => opentmk}/src/main.rs (57%) rename opentmk/{ => opentmk}/src/tests/hv_misc.rs (100%) rename opentmk/{ => opentmk}/src/tests/hv_processor.rs (100%) rename opentmk/{ => opentmk}/src/tests/mod.rs (100%) rename opentmk/{ => opentmk}/src/tmk_assert.rs (88%) rename opentmk/{ => opentmk}/src/tmk_logger.rs (97%) rename opentmk/{ => opentmk}/src/uefi/alloc.rs (99%) rename opentmk/{ => opentmk}/src/uefi/context.rs (100%) rename opentmk/{ => opentmk}/src/uefi/hypercall.rs (100%) rename opentmk/{ => opentmk}/src/uefi/hypvctx.rs (99%) rename opentmk/{ => opentmk}/src/uefi/init.rs (100%) rename opentmk/{ => opentmk}/src/uefi/mod.rs (100%) rename opentmk/{ => opentmk}/src/uefi/rt.rs (100%) rename opentmk/{ => opentmk}/src/uefi/tests/hv_misc.rs (98%) rename opentmk/{ => opentmk}/src/uefi/tests/hv_processor.rs (92%) rename opentmk/{ => opentmk}/src/uefi/tests/mod.rs (100%) create mode 100644 opentmk/sync/Cargo.toml rename opentmk/{src/sync.rs => sync/src/lib.rs} (99%) diff --git a/Cargo.toml b/Cargo.toml index fd2106e652..ed1823f711 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,7 +42,9 @@ members = [ "vm/loader/igvmfilegen", "vm/vmgs/vmgs_lib", "vm/vmgs/vmgstool", - "opentmk" + # opentmk + "opentmk/opentmk", + "opentmk/sync" ] exclude = [ "xsync", @@ -375,6 +377,9 @@ vnc_worker_defs = { path = "workers/vnc_worker_defs" } vnc = { path = "workers/vnc_worker/vnc" } profiler_worker = { path = "openhcl/profiler_worker" } +# opentmk +sync_nostd = { path = "opentmk/sync"} + # crates.io anyhow = "1.0" arbitrary = "1.3" @@ -446,9 +451,11 @@ jiff = "0.1" kvm-bindings = "0.7" # Use of these specific REPO will go away when changes are taken upstream. landlock = "0.3.1" +lazy_static = { version = "1.4.0", features = ["spin_no_std"] } libc = "0.2" libfuzzer-sys = "0.4" libtest-mimic = "0.8" +linked_list_allocator = "0.10.5" linkme = "0.3.9" log = "0.4" macaddr = "1.0" @@ -494,6 +501,7 @@ smallbox = "0.8" smallvec = "1.8" smoltcp = { version = "0.8", default-features = false } socket2 = "0.5" +spin = "0.10.0" stackfuture = "0.3" static_assertions = "1.1" syn = "2" @@ -521,12 +529,12 @@ winapi = "0.3" windows = "0.59" windows-service = "0.7" windows-sys = "0.52" +x86_64 = "0.15.2" xshell = "=0.2.2" # pin to 0.2.2 to work around https://github.com/matklad/xshell/issues/63 xshell-macros = "0.2" # We add the derive feature here since the vast majority of our crates use it. #zerocopy = { version = "0.7.32", features = ["derive"]} zerocopy = { version = "0.8.14", features = ["derive"]} -linked_list_allocator = "0.10.5" [workspace.metadata.xtask.unused-deps] # Pulled in through "tracing", but we need to pin the version diff --git a/opentmk/Cargo.toml b/opentmk/opentmk/Cargo.toml similarity index 62% rename from opentmk/Cargo.toml rename to opentmk/opentmk/Cargo.toml index 70f4c701c4..27eeee0c6c 100644 --- a/opentmk/Cargo.toml +++ b/opentmk/opentmk/Cargo.toml @@ -7,22 +7,22 @@ edition.workspace = true rust-version.workspace = true [dependencies] -uefi = { workspace = true, features = ["alloc"] } -minimal_rt.workspace = true -linked_list_allocator = { workspace = true } -hvdef = {workspace = true} -zerocopy = {workspace = true} -memory_range = { workspace = true } -arrayvec = {workspace = true} -cfg-if.workspace = true +arrayvec.workspace = true bitfield-struct.workspace = true -x86_64 = "0.15.2" -lazy_static = { version = "1.4.0", features = ["spin_no_std"] } +cfg-if.workspace = true +hvdef = {workspace = true} +lazy_static.workspace = true +linked_list_allocator.workspace = true +log.workspace = true +memory_range.workspace = true +minimal_rt.workspace = true +serde = { version = "1.0", default-features = false} serde_json = { version = "1.0", default-features = false, features = ["alloc"] } -spin = "0.10.0" -serde = {version = "1.0", default-features = false} -log = { version = "0.4", features = ["serde"] } +uefi = { workspace = true, features = ["alloc"] } +x86_64.workspace = true x86defs.workspace = true +zerocopy.workspace = true +sync_nostd.workspace = true [lints] workspace = true diff --git a/opentmk/README.md b/opentmk/opentmk/README.md similarity index 78% rename from opentmk/README.md rename to opentmk/opentmk/README.md index 999308fc0b..a2658e8753 100644 --- a/opentmk/README.md +++ b/opentmk/opentmk/README.md @@ -1,3 +1,3 @@ -# `guest_test_uefi` +# OpenTMK See the guide for more info on how to build/run the code in this crate. diff --git a/opentmk/build_deploy.sh b/opentmk/opentmk/build_deploy.sh similarity index 100% rename from opentmk/build_deploy.sh rename to opentmk/opentmk/build_deploy.sh diff --git a/opentmk/src/arch/aarch64/hypercall.rs b/opentmk/opentmk/src/arch/aarch64/hypercall.rs similarity index 100% rename from opentmk/src/arch/aarch64/hypercall.rs rename to opentmk/opentmk/src/arch/aarch64/hypercall.rs diff --git a/opentmk/src/arch/aarch64/mod.rs b/opentmk/opentmk/src/arch/aarch64/mod.rs similarity index 100% rename from opentmk/src/arch/aarch64/mod.rs rename to opentmk/opentmk/src/arch/aarch64/mod.rs diff --git a/opentmk/src/arch/mod.rs b/opentmk/opentmk/src/arch/mod.rs similarity index 100% rename from opentmk/src/arch/mod.rs rename to opentmk/opentmk/src/arch/mod.rs diff --git a/opentmk/src/arch/x86_64/hypercall.rs b/opentmk/opentmk/src/arch/x86_64/hypercall.rs similarity index 100% rename from opentmk/src/arch/x86_64/hypercall.rs rename to opentmk/opentmk/src/arch/x86_64/hypercall.rs diff --git a/opentmk/src/arch/x86_64/interrupt.rs b/opentmk/opentmk/src/arch/x86_64/interrupt.rs similarity index 98% rename from opentmk/src/arch/x86_64/interrupt.rs rename to opentmk/opentmk/src/arch/x86_64/interrupt.rs index 952629be30..1f31036de8 100644 --- a/opentmk/src/arch/x86_64/interrupt.rs +++ b/opentmk/opentmk/src/arch/x86_64/interrupt.rs @@ -1,6 +1,6 @@ use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame}; use lazy_static::lazy_static; -use crate::sync::Mutex; +use sync_nostd::Mutex; use super::interrupt_handler_register::{register_interrupt_handler, set_common_handler}; diff --git a/opentmk/src/arch/x86_64/interrupt_handler_register.rs b/opentmk/opentmk/src/arch/x86_64/interrupt_handler_register.rs similarity index 99% rename from opentmk/src/arch/x86_64/interrupt_handler_register.rs rename to opentmk/opentmk/src/arch/x86_64/interrupt_handler_register.rs index 25522d3c38..596be70cd0 100644 --- a/opentmk/src/arch/x86_64/interrupt_handler_register.rs +++ b/opentmk/opentmk/src/arch/x86_64/interrupt_handler_register.rs @@ -1,7 +1,7 @@ #![allow(dead_code)] use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame}; -use crate::sync::Mutex; +use sync_nostd::Mutex; static mut COMMON_HANDLER: fn(InterruptStackFrame, u8) = common_handler; static COMMON_HANDLER_MUTEX: Mutex<()> = Mutex::new(()); diff --git a/opentmk/src/arch/x86_64/mod.rs b/opentmk/opentmk/src/arch/x86_64/mod.rs similarity index 100% rename from opentmk/src/arch/x86_64/mod.rs rename to opentmk/opentmk/src/arch/x86_64/mod.rs diff --git a/opentmk/src/arch/x86_64/serial.rs b/opentmk/opentmk/src/arch/x86_64/serial.rs similarity index 99% rename from opentmk/src/arch/x86_64/serial.rs rename to opentmk/opentmk/src/arch/x86_64/serial.rs index 173794ad8f..bb16808b60 100644 --- a/opentmk/src/arch/x86_64/serial.rs +++ b/opentmk/opentmk/src/arch/x86_64/serial.rs @@ -5,7 +5,7 @@ #![allow(static_mut_refs)] use core::arch::asm; use core::fmt; -use crate::sync::Mutex; +use sync_nostd::Mutex; const COM4: u16 = 0x2E8; static mut MUTEX : Mutex<()> = Mutex::new(()); diff --git a/opentmk/src/main.rs b/opentmk/opentmk/src/main.rs similarity index 57% rename from opentmk/src/main.rs rename to opentmk/opentmk/src/main.rs index d33c63c8af..041ae6e702 100644 --- a/opentmk/src/main.rs +++ b/opentmk/opentmk/src/main.rs @@ -5,12 +5,7 @@ #![feature(abi_x86_interrupt)] #![doc = include_str!("../README.md")] -// HACK: workaround for building guest_test_uefi as part of the workspace in CI. -#![cfg_attr(all(not(test), target_os = "uefi"), no_main)] -#![cfg_attr(all(not(test), target_os = "uefi"), no_std)] -// HACK: workaround for building guest_test_uefi as part of the workspace in CI -// // Actual entrypoint is `uefi::uefi_main`, via the `#[entry]` macro #[cfg(any(test, not(target_os = "uefi")))] fn main() {} @@ -21,5 +16,4 @@ extern crate alloc; mod uefi; pub mod arch; pub mod tmk_assert; -pub mod sync; pub mod tmk_logger; diff --git a/opentmk/src/tests/hv_misc.rs b/opentmk/opentmk/src/tests/hv_misc.rs similarity index 100% rename from opentmk/src/tests/hv_misc.rs rename to opentmk/opentmk/src/tests/hv_misc.rs diff --git a/opentmk/src/tests/hv_processor.rs b/opentmk/opentmk/src/tests/hv_processor.rs similarity index 100% rename from opentmk/src/tests/hv_processor.rs rename to opentmk/opentmk/src/tests/hv_processor.rs diff --git a/opentmk/src/tests/mod.rs b/opentmk/opentmk/src/tests/mod.rs similarity index 100% rename from opentmk/src/tests/mod.rs rename to opentmk/opentmk/src/tests/mod.rs diff --git a/opentmk/src/tmk_assert.rs b/opentmk/opentmk/src/tmk_assert.rs similarity index 88% rename from opentmk/src/tmk_assert.rs rename to opentmk/opentmk/src/tmk_assert.rs index a33e3ca447..dad6e46dfe 100644 --- a/opentmk/src/tmk_assert.rs +++ b/opentmk/opentmk/src/tmk_assert.rs @@ -3,7 +3,7 @@ use alloc::string::{String, ToString}; use serde::Serialize; use serde_json::json; -pub fn format_asset_json_string( +pub fn format_assert_json_string( s: &str, terminate_new_line: bool, line: String, @@ -42,7 +42,7 @@ macro_rules! tmk_assert { let file_line = format!("{}:{}", file, line); let expn = stringify!($condition); let result: bool = $condition; - let js = crate::tmk_assert::format_asset_json_string( + let js = crate::tmk_assert::format_assert_json_string( &expn, true, file_line, result, &$message, ); crate::tmk_assert::write_str(&js); @@ -70,7 +70,7 @@ impl AssertOption for Option { let call: &core::panic::Location<'_> = core::panic::Location::caller(); let file_line = format!("{}:{}", call.file(), call.line()); let expn = type_name::>(); - let js = format_asset_json_string(expn, true, file_line, false, &message); + let js = format_assert_json_string(expn, true, file_line, false, &message); write_str(&js); panic!("Assertion failed: {}", message); } @@ -90,7 +90,7 @@ where let file_line = format!("{}:{}", call.file(), call.line()); let expn = type_name::>(); let js = - format_asset_json_string(expn, true, file_line, false, &"ResultTest"); + format_assert_json_string(expn, true, file_line, false, &"ResultTest"); write_str(&js); panic!("Assertion failed: {:?}", err); } @@ -106,7 +106,7 @@ where let call: &core::panic::Location<'_> = core::panic::Location::caller(); let file_line = format!("{}:{}", call.file(), call.line()); let expn = type_name::>(); - let js = format_asset_json_string(expn, true, file_line, false, &message); + let js = format_assert_json_string(expn, true, file_line, false, &message); write_str(&js); panic!("Assertion failed: {:?}", err); } diff --git a/opentmk/src/tmk_logger.rs b/opentmk/opentmk/src/tmk_logger.rs similarity index 97% rename from opentmk/src/tmk_logger.rs rename to opentmk/opentmk/src/tmk_logger.rs index 467d775c49..f594f60b63 100644 --- a/opentmk/src/tmk_logger.rs +++ b/opentmk/opentmk/src/tmk_logger.rs @@ -3,7 +3,7 @@ use core::fmt::Write; use alloc::{fmt::format, string::{String, ToString}}; use log::SetLoggerError; use serde_json::json; -use spin::{mutex::Mutex, MutexGuard}; +use sync_nostd::{Mutex, MutexGuard}; use crate::arch::serial::{InstrIoAccess, Serial}; diff --git a/opentmk/src/uefi/alloc.rs b/opentmk/opentmk/src/uefi/alloc.rs similarity index 99% rename from opentmk/src/uefi/alloc.rs rename to opentmk/opentmk/src/uefi/alloc.rs index edc36d054d..f6127573f7 100644 --- a/opentmk/src/uefi/alloc.rs +++ b/opentmk/opentmk/src/uefi/alloc.rs @@ -1,7 +1,7 @@ use core::{alloc::GlobalAlloc, cell::RefCell}; use linked_list_allocator::LockedHeap; -use spin::mutex::Mutex; +use sync_nostd::Mutex; use uefi::{ allocator::Allocator, boot::{self, AllocateType, MemoryType}, diff --git a/opentmk/src/uefi/context.rs b/opentmk/opentmk/src/uefi/context.rs similarity index 100% rename from opentmk/src/uefi/context.rs rename to opentmk/opentmk/src/uefi/context.rs diff --git a/opentmk/src/uefi/hypercall.rs b/opentmk/opentmk/src/uefi/hypercall.rs similarity index 100% rename from opentmk/src/uefi/hypercall.rs rename to opentmk/opentmk/src/uefi/hypercall.rs diff --git a/opentmk/src/uefi/hypvctx.rs b/opentmk/opentmk/src/uefi/hypvctx.rs similarity index 99% rename from opentmk/src/uefi/hypvctx.rs rename to opentmk/opentmk/src/uefi/hypvctx.rs index 86f24eee96..26455dbc22 100644 --- a/opentmk/src/uefi/hypvctx.rs +++ b/opentmk/opentmk/src/uefi/hypvctx.rs @@ -15,7 +15,7 @@ use hvdef::hypercall::{HvInputVtl, InitialVpContextX64}; use hvdef::Vtl; use memory_range::MemoryRange; use minimal_rt::arch::msr::{read_msr, write_msr}; -use spin::Mutex; +use sync_nostd::Mutex; const ALIGNMENT: usize = 4096; diff --git a/opentmk/src/uefi/init.rs b/opentmk/opentmk/src/uefi/init.rs similarity index 100% rename from opentmk/src/uefi/init.rs rename to opentmk/opentmk/src/uefi/init.rs diff --git a/opentmk/src/uefi/mod.rs b/opentmk/opentmk/src/uefi/mod.rs similarity index 100% rename from opentmk/src/uefi/mod.rs rename to opentmk/opentmk/src/uefi/mod.rs diff --git a/opentmk/src/uefi/rt.rs b/opentmk/opentmk/src/uefi/rt.rs similarity index 100% rename from opentmk/src/uefi/rt.rs rename to opentmk/opentmk/src/uefi/rt.rs diff --git a/opentmk/src/uefi/tests/hv_misc.rs b/opentmk/opentmk/src/uefi/tests/hv_misc.rs similarity index 98% rename from opentmk/src/uefi/tests/hv_misc.rs rename to opentmk/opentmk/src/uefi/tests/hv_misc.rs index 56229d0fb0..58e27531d6 100644 --- a/opentmk/src/uefi/tests/hv_misc.rs +++ b/opentmk/opentmk/src/uefi/tests/hv_misc.rs @@ -4,7 +4,7 @@ // This test is to verify that the VTL protections are working as expected. // The stack values in VTL0 are changing after interrupt handling in VTL1. use crate::tmk_assert::{AssertOption, AssertResult}; -use crate::sync::{Channel, Receiver, Sender}; +use sync_nostd::{Channel, Receiver, Sender}; use crate::uefi::alloc::{ALLOCATOR, SIZE_1MB}; use crate::uefi::{context, hypvctx}; use crate::{tmk_assert}; diff --git a/opentmk/src/uefi/tests/hv_processor.rs b/opentmk/opentmk/src/uefi/tests/hv_processor.rs similarity index 92% rename from opentmk/src/uefi/tests/hv_processor.rs rename to opentmk/opentmk/src/uefi/tests/hv_processor.rs index 6547087202..6dec2ae748 100644 --- a/opentmk/src/uefi/tests/hv_processor.rs +++ b/opentmk/opentmk/src/uefi/tests/hv_processor.rs @@ -1,4 +1,5 @@ use hvdef::Vtl; +use sync_nostd::Channel; use crate::{ tmk_assert, uefi::context::{TestCtxTrait, VpExecutor} @@ -13,7 +14,7 @@ pub fn exec(ctx: &mut dyn TestCtxTrait) { // Testing BSP VTL Bringup { - let (tx, rx) = crate::sync::Channel::new().split(); + let (tx, rx) = Channel::new().split(); ctx.start_on_vp(VpExecutor::new(0, Vtl::Vtl1).command( move |ctx: &mut dyn TestCtxTrait| { let vp = ctx.get_current_vp(); @@ -33,7 +34,7 @@ pub fn exec(ctx: &mut dyn TestCtxTrait) { for i in 1..vp_count { // Testing VTL1 { - let (tx, rx) = crate::sync::Channel::new().split(); + let (tx, rx) = Channel::new().split(); ctx.start_on_vp(VpExecutor::new(i, Vtl::Vtl1).command( move |ctx: &mut dyn TestCtxTrait| { let vp = ctx.get_current_vp(); @@ -51,7 +52,7 @@ pub fn exec(ctx: &mut dyn TestCtxTrait) { // Testing VTL0 { - let (tx, rx) = crate::sync::Channel::new().split(); + let (tx, rx) = Channel::new().split(); ctx.start_on_vp(VpExecutor::new(i, Vtl::Vtl0).command( move |ctx: &mut dyn TestCtxTrait| { let vp = ctx.get_current_vp(); diff --git a/opentmk/src/uefi/tests/mod.rs b/opentmk/opentmk/src/uefi/tests/mod.rs similarity index 100% rename from opentmk/src/uefi/tests/mod.rs rename to opentmk/opentmk/src/uefi/tests/mod.rs diff --git a/opentmk/sync/Cargo.toml b/opentmk/sync/Cargo.toml new file mode 100644 index 0000000000..53f9ba2ad6 --- /dev/null +++ b/opentmk/sync/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "sync_nostd" +version = "0.1.0" +rust-version.workspace = true +edition.workspace = true + +[dependencies] +spin.workspace = true + + +[lints] +workspace = true diff --git a/opentmk/src/sync.rs b/opentmk/sync/src/lib.rs similarity index 99% rename from opentmk/src/sync.rs rename to opentmk/sync/src/lib.rs index d0fe9eba59..e3a387c02c 100644 --- a/opentmk/src/sync.rs +++ b/opentmk/sync/src/lib.rs @@ -1,5 +1,8 @@ +#![no_std] +#![allow(unsafe_code)] +extern crate alloc; use core::sync::atomic::{AtomicUsize, Ordering}; -pub use spin::Mutex; +pub use spin::{Mutex, MutexGuard}; use alloc::{sync::Arc, vec::Vec}; use alloc::collections::VecDeque; use core::error::Error; From 7eb38ff9383d370c0501a189c71e5cd557734d42 Mon Sep 17 00:00:00 2001 From: Mayank Kumar Date: Mon, 12 May 2025 11:20:00 +0000 Subject: [PATCH 07/23] refactor: resolve PR feedback --- .../arch/x86_64/interrupt_handler_register.rs | 115 ++- opentmk/opentmk/src/{uefi => }/context.rs | 35 + opentmk/opentmk/src/{uefi => }/hypercall.rs | 781 +++++++++--------- opentmk/opentmk/src/main.rs | 5 + opentmk/opentmk/src/uefi/hypvctx.rs | 10 +- opentmk/opentmk/src/uefi/mod.rs | 2 - opentmk/opentmk/src/uefi/tests/hv_misc.rs | 2 +- .../opentmk/src/uefi/tests/hv_processor.rs | 2 +- 8 files changed, 512 insertions(+), 440 deletions(-) rename opentmk/opentmk/src/{uefi => }/context.rs (60%) rename opentmk/opentmk/src/{uefi => }/hypercall.rs (97%) diff --git a/opentmk/opentmk/src/arch/x86_64/interrupt_handler_register.rs b/opentmk/opentmk/src/arch/x86_64/interrupt_handler_register.rs index 596be70cd0..e6084ec4ce 100644 --- a/opentmk/opentmk/src/arch/x86_64/interrupt_handler_register.rs +++ b/opentmk/opentmk/src/arch/x86_64/interrupt_handler_register.rs @@ -1,5 +1,5 @@ #![allow(dead_code)] -use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame}; +use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame, PageFaultErrorCode}; use sync_nostd::Mutex; @@ -14,6 +14,40 @@ macro_rules! create_fn { }; } +macro_rules! create_fn_create_with_errorcode { + ($name:ident, $i: expr) => { + extern "x86-interrupt" fn $name(stack_frame: InterruptStackFrame, _error_code: u64) { + unsafe { (COMMON_HANDLER)(stack_frame, $i) }; + } + }; +} + +macro_rules! create_fn_divergent_create_with_errorcode { + ($name:ident, $i: expr) => { + extern "x86-interrupt" fn $name(stack_frame: InterruptStackFrame, _error_code: u64) -> ! { + unsafe { (COMMON_HANDLER)(stack_frame, $i) }; + loop{} + } + }; +} + +macro_rules! create_fn_divergent_create { + ($name:ident, $i: expr) => { + extern "x86-interrupt" fn $name(stack_frame: InterruptStackFrame) -> ! { + unsafe { (COMMON_HANDLER)(stack_frame, $i) }; + loop{} + } + }; +} + +macro_rules! create_page_fault_fn { + ($name:ident, $i: expr) => { + extern "x86-interrupt" fn $name(stack_frame:InterruptStackFrame, _error_code: PageFaultErrorCode) { + unsafe { (COMMON_HANDLER)(stack_frame, $i) }; + } + }; +} + macro_rules! register_interrupt_handler { ($idt: expr, $i: expr, $name: ident) => { $idt[$i].set_handler_fn($name); @@ -34,38 +68,33 @@ pub fn set_common_handler(handler: fn(InterruptStackFrame, u8)) { extern "x86-interrupt" fn no_op(_stack_frame: InterruptStackFrame) {} pub fn register_interrupt_handler(idt: &mut InterruptDescriptorTable) { - register_interrupt_handler!(idt, x86defs::Exception::DIVIDE_ERROR.0, handler_0); - register_interrupt_handler!(idt, x86defs::Exception::DEBUG.0, handler_1); - register_interrupt_handler!(idt, 2, handler_2); - register_interrupt_handler!(idt, x86defs::Exception::BREAKPOINT.0, handler_3); - register_interrupt_handler!(idt, x86defs::Exception::OVERFLOW.0, handler_4); - register_interrupt_handler!(idt, x86defs::Exception::BOUND_RANGE_EXCEEDED.0, handler_5); - register_interrupt_handler!(idt, x86defs::Exception::INVALID_OPCODE.0, handler_6); - register_interrupt_handler!(idt, x86defs::Exception::DEVICE_NOT_AVAILABLE.0, handler_7); - // register_interrupt_handler!(idt, x86defs::Exception::DOUBLE_FAULT.0, handler_8); + idt.divide_error.set_handler_fn(handler_0); + idt.debug.set_handler_fn(handler_1); + idt.non_maskable_interrupt.set_handler_fn(handler_2); + idt.breakpoint.set_handler_fn(handler_3); + idt.overflow.set_handler_fn(handler_4); + idt.bound_range_exceeded.set_handler_fn(handler_5); + idt.invalid_opcode.set_handler_fn(handler_6); + idt.device_not_available.set_handler_fn(handler_7); + idt.double_fault.set_handler_fn(handler_8); register_interrupt_handler!(idt, 9, handler_9); - // register_interrupt_handler!(idt, x86defs::Exception::INVALID_TSS.0, handler_10); - // register_interrupt_handler!(idt, x86defs::Exception::SEGMENT_NOT_PRESENT.0, handler_11); - // register_interrupt_handler!(idt, x86defs::Exception::STACK_SEGMENT_FAULT.0, handler_12); - // register_interrupt_handler!(idt, x86defs::Exception::GENERAL_PROTECTION_FAULT.0, handler_13); - // register_interrupt_handler!(idt, x86defs::Exception::PAGE_FAULT.0, handler_14); - // register_interrupt_handler!(idt, 15, handler_15); - // register_interrupt_handler!(idt, x86defs::Exception::FLOATING_POINT_EXCEPTION.0, handler_16); - // register_interrupt_handler!(idt, x86defs::Exception::ALIGNMENT_CHECK.0, handler_17); - // register_interrupt_handler!(idt, x86defs::Exception::MACHINE_CHECK.0, handler_18); - // register_interrupt_handler!(idt, x86defs::Exception::SIMD_FLOATING_POINT_EXCEPTION.0, handler_19); - // register_interrupt_handler!(idt, 20, handler_20); - // register_interrupt_handler!(idt, 21, handler_21); - // register_interrupt_handler!(idt, 22, handler_22); - // register_interrupt_handler!(idt, 23, handler_23); - // register_interrupt_handler!(idt, 24, handler_24); - // register_interrupt_handler!(idt, 25, handler_25); - // register_interrupt_handler!(idt, 26, handler_26); - // register_interrupt_handler!(idt, 27, handler_27); - // register_interrupt_handler!(idt, 28, handler_28); - // register_interrupt_handler!(idt, x86defs::Exception::SEV_VMM_COMMUNICATION.0, handler_29); - // register_interrupt_handler!(idt, 30, handler_30); - // register_interrupt_handler!(idt, 31, handler_31); + idt.invalid_tss.set_handler_fn(handler_10); + idt.segment_not_present.set_handler_fn(handler_11); + idt.stack_segment_fault.set_handler_fn(handler_12); + idt.general_protection_fault.set_handler_fn(handler_13); + idt.page_fault.set_handler_fn(handler_14); + // Vector 15 is reserved + idt.x87_floating_point.set_handler_fn(handler_16); + idt.alignment_check.set_handler_fn(handler_17); + idt.machine_check.set_handler_fn(handler_18); + idt.simd_floating_point.set_handler_fn(handler_19); + idt.virtualization.set_handler_fn(handler_20); + idt.cp_protection_exception.set_handler_fn(handler_21); + // Vector 22-27 is reserved + idt.hv_injection_exception.set_handler_fn(handler_28); + idt.vmm_communication_exception.set_handler_fn(handler_29); + idt.security_exception.set_handler_fn(handler_30); + // Vector 31 is reserved register_interrupt_handler!(idt, 32, handler_32); register_interrupt_handler!(idt, 33, handler_33); @@ -301,20 +330,20 @@ create_fn!(handler_4, 4); create_fn!(handler_5, 5); create_fn!(handler_6, 6); create_fn!(handler_7, 7); -create_fn!(handler_8, 8); +create_fn_divergent_create_with_errorcode!(handler_8, 8); create_fn!(handler_9, 9); -create_fn!(handler_10, 10); -create_fn!(handler_11, 11); -create_fn!(handler_12, 12); -create_fn!(handler_13, 13); -create_fn!(handler_14, 14); +create_fn_create_with_errorcode!(handler_10, 10); +create_fn_create_with_errorcode!(handler_11, 11); +create_fn_create_with_errorcode!(handler_12, 12); +create_fn_create_with_errorcode!(handler_13, 13); +create_page_fault_fn!(handler_14, 14); create_fn!(handler_15, 15); create_fn!(handler_16, 16); -create_fn!(handler_17, 17); -create_fn!(handler_18, 18); +create_fn_create_with_errorcode!(handler_17, 17); +create_fn_divergent_create!(handler_18, 18); create_fn!(handler_19, 19); create_fn!(handler_20, 20); -create_fn!(handler_21, 21); +create_fn_create_with_errorcode!(handler_21, 21); create_fn!(handler_22, 22); create_fn!(handler_23, 23); create_fn!(handler_24, 24); @@ -322,8 +351,8 @@ create_fn!(handler_25, 25); create_fn!(handler_26, 26); create_fn!(handler_27, 27); create_fn!(handler_28, 28); -create_fn!(handler_29, 29); -create_fn!(handler_30, 30); +create_fn_create_with_errorcode!(handler_29, 29); +create_fn_create_with_errorcode!(handler_30, 30); create_fn!(handler_31, 31); create_fn!(handler_32, 32); create_fn!(handler_33, 33); diff --git a/opentmk/opentmk/src/uefi/context.rs b/opentmk/opentmk/src/context.rs similarity index 60% rename from opentmk/opentmk/src/uefi/context.rs rename to opentmk/opentmk/src/context.rs index 3249c0bd3f..da52e8ee32 100644 --- a/opentmk/opentmk/src/uefi/context.rs +++ b/opentmk/opentmk/src/context.rs @@ -5,6 +5,41 @@ use alloc::boxed::Box; use hvdef::Vtl; +pub trait SecureInterceptPlatformTrait { + fn setup_secure_intercept(&mut self, interrupt_idx: u8); +} + +pub trait InterruptPlatformTrait { + fn set_interrupt_idx(&mut self, interrupt_idx: u8, handler: fn()); + fn setup_interrupt_handler(&mut self); +} + +pub trait MsrPlatformTrait { + fn read_msr(&mut self, msr: u32) -> u64; + fn write_msr(&mut self, msr: u32, value: u64); +} + +pub trait VirtualProcessorlatformTrait { + fn get_register(&mut self, reg: u32) -> u128; + fn get_vp_count(&self) -> u32; + fn queue_command_vp(&mut self, cmd: VpExecutor); + fn start_on_vp(&mut self, cmd: VpExecutor); + fn start_running_vp_with_default_context(&mut self, cmd: VpExecutor); +} + +pub trait VtlPlatformTrait { + fn apply_vtl_protection_for_memory(&mut self, range: Range, vtl: Vtl); + fn enable_vp_vtl_with_default_context(&mut self, vp_index: u32, vtl: Vtl); + fn get_current_vtl(&self) -> Vtl; + fn set_default_ctx_to_vp(&mut self, vp_index: u32, vtl: Vtl); + fn setup_partition_vtl(&mut self, vtl: Vtl); + fn setup_vtl_protection(&mut self); + fn switch_to_high_vtl(&mut self); + fn switch_to_low_vtl(&mut self); +} + + + pub trait TestCtxTrait { diff --git a/opentmk/opentmk/src/uefi/hypercall.rs b/opentmk/opentmk/src/hypercall.rs similarity index 97% rename from opentmk/opentmk/src/uefi/hypercall.rs rename to opentmk/opentmk/src/hypercall.rs index 26aa18db9a..9129635272 100644 --- a/opentmk/opentmk/src/uefi/hypercall.rs +++ b/opentmk/opentmk/src/hypercall.rs @@ -121,250 +121,47 @@ pub struct HvCall { #[expect(unsafe_code)] impl HvCall { - pub const fn new() -> Self { - HvCall { - initialized: false, - input_page: HvcallPage::new(), - output_page: HvcallPage::new(), - } - } - fn input_page(&mut self) -> &mut HvcallPage { - &mut self.input_page - } - - fn output_page(&mut self) -> &mut HvcallPage { - &mut self.output_page - } - - /// Returns the address of the hypercall page, mapping it first if - /// necessary. - #[cfg(target_arch = "x86_64")] - pub fn hypercall_page(&mut self) -> u64 { - self.init_if_needed(); - core::ptr::addr_of!(HYPERCALL_PAGE) as u64 - } - - fn init_if_needed(&mut self) { - if !self.initialized { - self.initialize(); - } - } - - pub fn initialize(&mut self) { - assert!(!self.initialized); - - // TODO: revisit os id value. For now, use 1 (which is what UEFI does) - let guest_os_id = hvdef::hypercall::HvGuestOsMicrosoft::new().with_os_id(1); - crate::arch::hypercall::initialize(guest_os_id.into()); - self.initialized = true; - } - - /// Call before jumping to kernel. - pub fn uninitialize(&mut self) { - if self.initialized { - crate::arch::hypercall::uninitialize(); - self.initialized = false; - } - } - - /// Returns the environment's VTL. - pub fn vtl(&mut self) -> Vtl { - assert!(self.initialized); - self - .get_register(hvdef::HvAllArchRegisterName::VsmVpStatus.into(), None) - .map_or(Vtl::Vtl0, |status| { - hvdef::HvRegisterVsmVpStatus::from(status.as_u64()) - .active_vtl() - .try_into() - .unwrap() - }) - } - - /// Makes a hypercall. - /// rep_count is Some for rep hypercalls - fn dispatch_hvcall( - &mut self, - code: hvdef::HypercallCode, - rep_count: Option, - ) -> hvdef::hypercall::HypercallOutput { - self.init_if_needed(); - - let control: hvdef::hypercall::Control = hvdef::hypercall::Control::new() - .with_code(code.0) - .with_rep_count(rep_count.unwrap_or_default()); - - // SAFETY: Invoking hypercall per TLFS spec - unsafe { - invoke_hypercall( - control, - self.input_page().address(), - self.output_page().address(), - ) - } - } - - pub fn set_vp_registers( + /// Hypercall to accept vtl2 pages from address start to end with VTL 2 + /// protections and no host visibility + #[cfg_attr(target_arch = "aarch64", allow(dead_code))] + pub fn accept_vtl2_pages( &mut self, - vp: u32, - vtl: Option, - vp_context: Option, + range: MemoryRange, + memory_type: hvdef::hypercall::AcceptMemoryType, ) -> Result<(), hvdef::HvError> { - const HEADER_SIZE: usize = size_of::(); - - let header = hvdef::hypercall::GetSetVpRegisters { - partition_id: hvdef::HV_PARTITION_ID_SELF, - vp_index: vp, - target_vtl: vtl.unwrap_or(HvInputVtl::CURRENT_VTL), - rsvd: [0; 3], - }; - - let _ = header.write_to_prefix(self.input_page().buffer.as_mut_slice()); - - let mut input_offset = HEADER_SIZE; + const HEADER_SIZE: usize = size_of::(); + const MAX_INPUT_ELEMENTS: usize = (HV_PAGE_SIZE as usize - HEADER_SIZE) / size_of::(); - let mut count = 0; - let mut write_reg = |reg_name: hvdef::HvRegisterName, reg_value: HvRegisterValue| { - let reg = hvdef::hypercall::HvRegisterAssoc { - name: reg_name, - pad: Default::default(), - value: reg_value, + let mut current_page = range.start_4k_gpn(); + while current_page < range.end_4k_gpn() { + let header = hvdef::hypercall::AcceptGpaPages { + partition_id: hvdef::HV_PARTITION_ID_SELF, + page_attributes: hvdef::hypercall::AcceptPagesAttributes::new() + .with_memory_type(memory_type.0) + .with_host_visibility(hvdef::hypercall::HostVisibilityType::PRIVATE) // no host visibility + .with_vtl_set(1 << 2), // applies vtl permissions for vtl 2 + vtl_permission_set: hvdef::hypercall::VtlPermissionSet { + vtl_permission_from_1: [0; hvdef::hypercall::HV_VTL_PERMISSION_SET_SIZE], + }, + gpa_page_base: current_page, }; - let _ = reg.write_to_prefix(&mut self.input_page().buffer[input_offset..]); - - input_offset += size_of::(); - count += 1; - }; - // pub msr_cr_pat: u64, - - write_reg( - HvX64RegisterName::Cr0.into(), - vp_context.unwrap().cr0.into(), - ); - write_reg( - HvX64RegisterName::Cr3.into(), - vp_context.unwrap().cr3.into(), - ); - write_reg( - HvX64RegisterName::Cr4.into(), - vp_context.unwrap().cr4.into(), - ); - write_reg( - HvX64RegisterName::Rip.into(), - vp_context.unwrap().rip.into(), - ); - write_reg( - HvX64RegisterName::Rsp.into(), - vp_context.unwrap().rsp.into(), - ); - write_reg( - HvX64RegisterName::Rflags.into(), - vp_context.unwrap().rflags.into(), - ); - write_reg( - HvX64RegisterName::Cs.into(), - vp_context.unwrap().cs.into(), - ); - write_reg( - HvX64RegisterName::Ss.into(), - vp_context.unwrap().ss.into(), - ); - write_reg( - HvX64RegisterName::Ds.into(), - vp_context.unwrap().ds.into(), - ); - write_reg( - HvX64RegisterName::Es.into(), - vp_context.unwrap().es.into(), - ); - write_reg( - HvX64RegisterName::Fs.into(), - vp_context.unwrap().fs.into(), - ); - write_reg( - HvX64RegisterName::Gs.into(), - vp_context.unwrap().gs.into(), - ); - write_reg( - HvX64RegisterName::Gdtr.into(), - vp_context.unwrap().gdtr.into(), - ); - write_reg( - HvX64RegisterName::Idtr.into(), - vp_context.unwrap().idtr.into(), - ); - write_reg( - HvX64RegisterName::Ldtr.into(), - vp_context.unwrap().ldtr.into(), - ); - write_reg( - HvX64RegisterName::Tr.into(), - vp_context.unwrap().tr.into(), - ); - write_reg( - HvX64RegisterName::Efer.into(), - vp_context.unwrap().efer.into(), - ); - - let output = self.dispatch_hvcall(hvdef::HypercallCode::HvCallSetVpRegisters, Some(count)); - - output.result() - } - - /// Hypercall for setting a register to a value. - pub fn set_register( - &mut self, - name: hvdef::HvRegisterName, - value: HvRegisterValue, - vtl: Option, - ) -> Result<(), hvdef::HvError> { - const HEADER_SIZE: usize = size_of::(); - - let header = hvdef::hypercall::GetSetVpRegisters { - partition_id: hvdef::HV_PARTITION_ID_SELF, - vp_index: hvdef::HV_VP_INDEX_SELF, - target_vtl: vtl.unwrap_or(HvInputVtl::CURRENT_VTL), - rsvd: [0; 3], - }; - - let _ = header.write_to_prefix(self.input_page().buffer.as_mut_slice()); - - let reg = hvdef::hypercall::HvRegisterAssoc { - name, - pad: Default::default(), - value, - }; - - let _ = reg.write_to_prefix(&mut self.input_page().buffer[HEADER_SIZE..]); - - let output = self.dispatch_hvcall(hvdef::HypercallCode::HvCallSetVpRegisters, Some(1)); - - output.result() - } + let remaining_pages = range.end_4k_gpn() - current_page; + let count = remaining_pages.min(MAX_INPUT_ELEMENTS as u64); - /// Hypercall for setting a register to a value. - pub fn get_register( - &mut self, - name: hvdef::HvRegisterName, - vtl: Option, - ) -> Result { - const HEADER_SIZE: usize = size_of::(); + let _ = header.write_to_prefix(self.input_page().buffer.as_mut_slice()); - let header = hvdef::hypercall::GetSetVpRegisters { - partition_id: hvdef::HV_PARTITION_ID_SELF, - vp_index: hvdef::HV_VP_INDEX_SELF, - target_vtl: vtl.unwrap_or(HvInputVtl::CURRENT_VTL), - rsvd: [0; 3], - }; + let output = self.dispatch_hvcall( + hvdef::HypercallCode::HvCallAcceptGpaPages, + Some(count as usize), + ); - let _ = header.write_to_prefix(self.input_page().buffer.as_mut_slice()); - let _ = name.write_to_prefix(&mut self.input_page().buffer[HEADER_SIZE..]); + output.result()?; - let output = self.dispatch_hvcall(hvdef::HypercallCode::HvCallGetVpRegisters, Some(1)); - output.result()?; - let value = HvRegisterValue::read_from_prefix(&self.output_page().buffer).unwrap(); + current_page += count; + } - Ok(value.0) + Ok(()) } /// Hypercall to apply vtl protections to the pages from address start to end @@ -453,77 +250,57 @@ impl HvCall { Ok(()) } - #[cfg(target_arch = "x86_64")] - /// Hypercall to get the current VTL VP context - pub fn get_current_vtl_vp_context(&mut self) -> Result { - use HvX64RegisterName; - use zerocopy::FromZeros; - let mut context: InitialVpContextX64 = FromZeros::new_zeroed(); - context.cr0 = self - .get_register(HvX64RegisterName::Cr0.into(), None)? - .as_u64(); - context.cr3 = self - .get_register(HvX64RegisterName::Cr3.into(), None)? - .as_u64(); - context.cr4 = self - .get_register(HvX64RegisterName::Cr4.into(), None)? - .as_u64(); - context.rip = self - .get_register(HvX64RegisterName::Rip.into(), None)? - .as_u64(); - context.rsp = self - .get_register(HvX64RegisterName::Rsp.into(), None)? - .as_u64(); - context.rflags = self - .get_register(HvX64RegisterName::Rflags.into(), None)? - .as_u64(); - context.cs = self - .get_register(HvX64RegisterName::Cs.into(), None)? - .as_segment(); - context.ss = self - .get_register(HvX64RegisterName::Ss.into(), None)? - .as_segment(); - context.ds = self - .get_register(HvX64RegisterName::Ds.into(), None)? - .as_segment(); - context.es = self - .get_register(HvX64RegisterName::Es.into(), None)? - .as_segment(); - context.fs = self - .get_register(HvX64RegisterName::Fs.into(), None)? - .as_segment(); - context.gs = self - .get_register(HvX64RegisterName::Gs.into(), None)? - .as_segment(); - context.gdtr = self - .get_register(HvX64RegisterName::Gdtr.into(), None)? - .as_table(); - context.idtr = self - .get_register(HvX64RegisterName::Idtr.into(), None)? - .as_table(); - context.tr = self - .get_register(HvX64RegisterName::Tr.into(), None)? - .as_segment(); - context.efer = self - .get_register(HvX64RegisterName::Efer.into(), None)? - .as_u64(); - Ok(context) - } + /// Makes a hypercall. + /// rep_count is Some for rep hypercalls + fn dispatch_hvcall( + &mut self, + code: hvdef::HypercallCode, + rep_count: Option, + ) -> hvdef::hypercall::HypercallOutput { + self.init_if_needed(); - pub fn vtl_call() { let control: hvdef::hypercall::Control = hvdef::hypercall::Control::new() - .with_code(hvdef::HypercallCode::HvCallVtlCall.0) - .with_rep_count(0); - invoke_hypercall_vtl(control); + .with_code(code.0) + .with_rep_count(rep_count.unwrap_or_default()); + + // SAFETY: Invoking hypercall per TLFS spec + unsafe { + invoke_hypercall( + control, + self.input_page().address(), + self.output_page().address(), + ) + } } - pub fn vtl_return() { - let control: hvdef::hypercall::Control = hvdef::hypercall::Control::new() - .with_code(hvdef::HypercallCode::HvCallVtlReturn.0) - .with_rep_count(0); - invoke_hypercall_vtl(control); + /// Enables a VTL for the specified partition. + pub fn enable_partition_vtl( + &mut self, + partition_id: u64, + target_vtl: Vtl, + ) -> Result<(), hvdef::HvError> { + let flags: EnablePartitionVtlFlags = EnablePartitionVtlFlags::new() + .with_enable_mbec(false) + .with_enable_supervisor_shadow_stack(false); + + let header = hvdef::hypercall::EnablePartitionVtl { + partition_id, + target_vtl: target_vtl.into(), + flags, + reserved_z0: 0, + reserved_z1: 0, + }; + + let _ = header.write_to_prefix(self.input_page().buffer.as_mut_slice()); + + let output = self.dispatch_hvcall(hvdef::HypercallCode::HvCallEnablePartitionVtl, None); + match output.result() { + Ok(()) | Err(hvdef::HvError::VtlAlreadyEnabled) => Ok(()), + err => err, + } } + /// Enables VTL protection for the specified VTL. pub fn enable_vtl_protection( &mut self, vtl: HvInputVtl, @@ -548,6 +325,7 @@ impl HvCall { } #[cfg(target_arch = "x86_64")] + /// Enables a VTL for a specific virtual processor (VP) on x86_64. pub fn enable_vp_vtl( &mut self, vp_index: u32, @@ -573,59 +351,6 @@ impl HvCall { } } - #[cfg(target_arch = "x86_64")] - pub fn start_virtual_processor( - &mut self, - vp_index: u32, - target_vtl: Vtl, - vp_context: Option, - ) -> Result<(), hvdef::HvError> { - let header = hvdef::hypercall::StartVirtualProcessorX64 { - partition_id: hvdef::HV_PARTITION_ID_SELF, - vp_index, - target_vtl: target_vtl.into(), - vp_context: vp_context.unwrap_or(zerocopy::FromZeros::new_zeroed()), - rsvd0: 0u8, - rsvd1: 0u16, - }; - - header - .write_to_prefix(self.input_page().buffer.as_mut_slice()) - .expect("size of start_virtual_processor header is not correct"); - - let output = self.dispatch_hvcall(hvdef::HypercallCode::HvCallStartVirtualProcessor, None); - match output.result() { - Ok(()) => Ok(()), - err => panic!("Failed to start virtual processor: {:?}", err), - } - } - - pub fn enable_partition_vtl( - &mut self, - partition_id: u64, - target_vtl: Vtl, - ) -> Result<(), hvdef::HvError> { - let flags: EnablePartitionVtlFlags = EnablePartitionVtlFlags::new() - .with_enable_mbec(false) - .with_enable_supervisor_shadow_stack(false); - - let header = hvdef::hypercall::EnablePartitionVtl { - partition_id, - target_vtl: target_vtl.into(), - flags, - reserved_z0: 0, - reserved_z1: 0, - }; - - let _ = header.write_to_prefix(self.input_page().buffer.as_mut_slice()); - - let output = self.dispatch_hvcall(hvdef::HypercallCode::HvCallEnablePartitionVtl, None); - match output.result() { - Ok(()) | Err(hvdef::HvError::VtlAlreadyEnabled) => Ok(()), - err => err, - } - } - /// Hypercall to enable VP VTL #[cfg(target_arch = "aarch64")] pub fn enable_vp_vtl(&mut self, vp_index: u32) -> Result<(), hvdef::HvError> { @@ -648,47 +373,86 @@ impl HvCall { } } - /// Hypercall to accept vtl2 pages from address start to end with VTL 2 - /// protections and no host visibility - #[cfg_attr(target_arch = "aarch64", allow(dead_code))] - pub fn accept_vtl2_pages( - &mut self, - range: MemoryRange, - memory_type: hvdef::hypercall::AcceptMemoryType, - ) -> Result<(), hvdef::HvError> { - const HEADER_SIZE: usize = size_of::(); - const MAX_INPUT_ELEMENTS: usize = (HV_PAGE_SIZE as usize - HEADER_SIZE) / size_of::(); - - let mut current_page = range.start_4k_gpn(); - while current_page < range.end_4k_gpn() { - let header = hvdef::hypercall::AcceptGpaPages { - partition_id: hvdef::HV_PARTITION_ID_SELF, - page_attributes: hvdef::hypercall::AcceptPagesAttributes::new() - .with_memory_type(memory_type.0) - .with_host_visibility(hvdef::hypercall::HostVisibilityType::PRIVATE) // no host visibility - .with_vtl_set(1 << 2), // applies vtl permissions for vtl 2 - vtl_permission_set: hvdef::hypercall::VtlPermissionSet { - vtl_permission_from_1: [0; hvdef::hypercall::HV_VTL_PERMISSION_SET_SIZE], - }, - gpa_page_base: current_page, - }; - - let remaining_pages = range.end_4k_gpn() - current_page; - let count = remaining_pages.min(MAX_INPUT_ELEMENTS as u64); + #[cfg(target_arch = "x86_64")] + /// Hypercall to get the current VTL VP context + pub fn get_current_vtl_vp_context(&mut self) -> Result { + use HvX64RegisterName; + use zerocopy::FromZeros; + let mut context: InitialVpContextX64 = FromZeros::new_zeroed(); + context.cr0 = self + .get_register(HvX64RegisterName::Cr0.into(), None)? + .as_u64(); + context.cr3 = self + .get_register(HvX64RegisterName::Cr3.into(), None)? + .as_u64(); + context.cr4 = self + .get_register(HvX64RegisterName::Cr4.into(), None)? + .as_u64(); + context.rip = self + .get_register(HvX64RegisterName::Rip.into(), None)? + .as_u64(); + context.rsp = self + .get_register(HvX64RegisterName::Rsp.into(), None)? + .as_u64(); + context.rflags = self + .get_register(HvX64RegisterName::Rflags.into(), None)? + .as_u64(); + context.cs = self + .get_register(HvX64RegisterName::Cs.into(), None)? + .as_segment(); + context.ss = self + .get_register(HvX64RegisterName::Ss.into(), None)? + .as_segment(); + context.ds = self + .get_register(HvX64RegisterName::Ds.into(), None)? + .as_segment(); + context.es = self + .get_register(HvX64RegisterName::Es.into(), None)? + .as_segment(); + context.fs = self + .get_register(HvX64RegisterName::Fs.into(), None)? + .as_segment(); + context.gs = self + .get_register(HvX64RegisterName::Gs.into(), None)? + .as_segment(); + context.gdtr = self + .get_register(HvX64RegisterName::Gdtr.into(), None)? + .as_table(); + context.idtr = self + .get_register(HvX64RegisterName::Idtr.into(), None)? + .as_table(); + context.tr = self + .get_register(HvX64RegisterName::Tr.into(), None)? + .as_segment(); + context.efer = self + .get_register(HvX64RegisterName::Efer.into(), None)? + .as_u64(); + Ok(context) + } - let _ = header.write_to_prefix(self.input_page().buffer.as_mut_slice()); + /// Hypercall for setting a register to a value. + pub fn get_register( + &mut self, + name: hvdef::HvRegisterName, + vtl: Option, + ) -> Result { + const HEADER_SIZE: usize = size_of::(); - let output = self.dispatch_hvcall( - hvdef::HypercallCode::HvCallAcceptGpaPages, - Some(count as usize), - ); + let header = hvdef::hypercall::GetSetVpRegisters { + partition_id: hvdef::HV_PARTITION_ID_SELF, + vp_index: hvdef::HV_VP_INDEX_SELF, + target_vtl: vtl.unwrap_or(HvInputVtl::CURRENT_VTL), + rsvd: [0; 3], + }; - output.result()?; + let _ = header.write_to_prefix(self.input_page().buffer.as_mut_slice()); + let _ = name.write_to_prefix(&mut self.input_page().buffer[HEADER_SIZE..]); - current_page += count; - } + let output = self.dispatch_hvcall(hvdef::HypercallCode::HvCallGetVpRegisters, Some(1)); + output.result()?; + let value = HvRegisterValue::read_from_prefix(&self.output_page().buffer).unwrap(); - Ok(()) + Ok(value.0) } /// Get the corresponding VP indices from a list of VP hardware IDs (APIC @@ -736,6 +500,247 @@ impl HvCall { Ok(()) } + + /// Initializes the hypercall interface if it hasn't been already. + fn init_if_needed(&mut self) { + if !self.initialized { + self.initialize(); + } + } + + /// Initializes the hypercall interface. + pub fn initialize(&mut self) { + assert!(!self.initialized); + + // TODO: revisit os id value. For now, use 1 (which is what UEFI does) + let guest_os_id = hvdef::hypercall::HvGuestOsMicrosoft::new().with_os_id(1); + crate::arch::hypercall::initialize(guest_os_id.into()); + self.initialized = true; + } + + /// Returns a mutable reference to the hypercall input page. + fn input_page(&mut self) -> &mut HvcallPage { + &mut self.input_page + } + + /// Creates a new `HvCall` instance. + pub const fn new() -> Self { + HvCall { + initialized: false, + input_page: HvcallPage::new(), + output_page: HvcallPage::new(), + } + } + + /// Returns a mutable reference to the hypercall output page. + fn output_page(&mut self) -> &mut HvcallPage { + &mut self.output_page + } + + /// Hypercall for setting a register to a value. + pub fn set_register( + &mut self, + name: hvdef::HvRegisterName, + value: HvRegisterValue, + vtl: Option, + ) -> Result<(), hvdef::HvError> { + const HEADER_SIZE: usize = size_of::(); + + let header = hvdef::hypercall::GetSetVpRegisters { + partition_id: hvdef::HV_PARTITION_ID_SELF, + vp_index: hvdef::HV_VP_INDEX_SELF, + target_vtl: vtl.unwrap_or(HvInputVtl::CURRENT_VTL), + rsvd: [0; 3], + }; + + let _ = header.write_to_prefix(self.input_page().buffer.as_mut_slice()); + + let reg = hvdef::hypercall::HvRegisterAssoc { + name, + pad: Default::default(), + value, + }; + + let _ = reg.write_to_prefix(&mut self.input_page().buffer[HEADER_SIZE..]); + + let output = self.dispatch_hvcall(hvdef::HypercallCode::HvCallSetVpRegisters, Some(1)); + + output.result() + } + + /// Sets multiple virtual processor (VP) registers for a given VP and VTL. + pub fn set_vp_registers( + &mut self, + vp: u32, + vtl: Option, + vp_context: Option, + ) -> Result<(), hvdef::HvError> { + const HEADER_SIZE: usize = size_of::(); + + let header = hvdef::hypercall::GetSetVpRegisters { + partition_id: hvdef::HV_PARTITION_ID_SELF, + vp_index: vp, + target_vtl: vtl.unwrap_or(HvInputVtl::CURRENT_VTL), + rsvd: [0; 3], + }; + + let _ = header.write_to_prefix(self.input_page().buffer.as_mut_slice()); + + let mut input_offset = HEADER_SIZE; + + let mut count = 0; + let mut write_reg = |reg_name: hvdef::HvRegisterName, reg_value: HvRegisterValue| { + let reg = hvdef::hypercall::HvRegisterAssoc { + name: reg_name, + pad: Default::default(), + value: reg_value, + }; + + let _ = reg.write_to_prefix(&mut self.input_page().buffer[input_offset..]); + + input_offset += size_of::(); + count += 1; + }; + // pub msr_cr_pat: u64, + + write_reg( + HvX64RegisterName::Cr0.into(), + vp_context.unwrap().cr0.into(), + ); + write_reg( + HvX64RegisterName::Cr3.into(), + vp_context.unwrap().cr3.into(), + ); + write_reg( + HvX64RegisterName::Cr4.into(), + vp_context.unwrap().cr4.into(), + ); + write_reg( + HvX64RegisterName::Rip.into(), + vp_context.unwrap().rip.into(), + ); + write_reg( + HvX64RegisterName::Rsp.into(), + vp_context.unwrap().rsp.into(), + ); + write_reg( + HvX64RegisterName::Rflags.into(), + vp_context.unwrap().rflags.into(), + ); + write_reg( + HvX64RegisterName::Cs.into(), + vp_context.unwrap().cs.into(), + ); + write_reg( + HvX64RegisterName::Ss.into(), + vp_context.unwrap().ss.into(), + ); + write_reg( + HvX64RegisterName::Ds.into(), + vp_context.unwrap().ds.into(), + ); + write_reg( + HvX64RegisterName::Es.into(), + vp_context.unwrap().es.into(), + ); + write_reg( + HvX64RegisterName::Fs.into(), + vp_context.unwrap().fs.into(), + ); + write_reg( + HvX64RegisterName::Gs.into(), + vp_context.unwrap().gs.into(), + ); + write_reg( + HvX64RegisterName::Gdtr.into(), + vp_context.unwrap().gdtr.into(), + ); + write_reg( + HvX64RegisterName::Idtr.into(), + vp_context.unwrap().idtr.into(), + ); + write_reg( + HvX64RegisterName::Ldtr.into(), + vp_context.unwrap().ldtr.into(), + ); + write_reg( + HvX64RegisterName::Tr.into(), + vp_context.unwrap().tr.into(), + ); + write_reg( + HvX64RegisterName::Efer.into(), + vp_context.unwrap().efer.into(), + ); + + let output = self.dispatch_hvcall(hvdef::HypercallCode::HvCallSetVpRegisters, Some(count)); + + output.result() + } + + #[cfg(target_arch = "x86_64")] + /// Starts a virtual processor (VP) with the specified VTL and context on x86_64. + pub fn start_virtual_processor( + &mut self, + vp_index: u32, + target_vtl: Vtl, + vp_context: Option, + ) -> Result<(), hvdef::HvError> { + let header = hvdef::hypercall::StartVirtualProcessorX64 { + partition_id: hvdef::HV_PARTITION_ID_SELF, + vp_index, + target_vtl: target_vtl.into(), + vp_context: vp_context.unwrap_or(zerocopy::FromZeros::new_zeroed()), + rsvd0: 0u8, + rsvd1: 0u16, + }; + + header + .write_to_prefix(self.input_page().buffer.as_mut_slice()) + .expect("size of start_virtual_processor header is not correct"); + + let output = self.dispatch_hvcall(hvdef::HypercallCode::HvCallStartVirtualProcessor, None); + match output.result() { + Ok(()) => Ok(()), + err => panic!("Failed to start virtual processor: {:?}", err), + } + } + + /// Call before jumping to kernel. + pub fn uninitialize(&mut self) { + if self.initialized { + crate::arch::hypercall::uninitialize(); + self.initialized = false; + } + } + + /// Returns the environment's VTL. + pub fn vtl(&mut self) -> Vtl { + assert!(self.initialized); + self + .get_register(hvdef::HvAllArchRegisterName::VsmVpStatus.into(), None) + .map_or(Vtl::Vtl0, |status| { + hvdef::HvRegisterVsmVpStatus::from(status.as_u64()) + .active_vtl() + .try_into() + .unwrap() + }) + } + + /// Invokes the HvCallVtlCall hypercall. + pub fn vtl_call() { + let control: hvdef::hypercall::Control = hvdef::hypercall::Control::new() + .with_code(hvdef::HypercallCode::HvCallVtlCall.0) + .with_rep_count(0); + invoke_hypercall_vtl(control); + } + + /// Invokes the HvCallVtlReturn hypercall. + pub fn vtl_return() { + let control: hvdef::hypercall::Control = hvdef::hypercall::Control::new() + .with_code(hvdef::HypercallCode::HvCallVtlReturn.0) + .with_rep_count(0); + invoke_hypercall_vtl(control); + } } /// The "hardware ID" used for [`HvCall::get_vp_index_from_hw_id`]. This is the diff --git a/opentmk/opentmk/src/main.rs b/opentmk/opentmk/src/main.rs index 041ae6e702..dc49cd0347 100644 --- a/opentmk/opentmk/src/main.rs +++ b/opentmk/opentmk/src/main.rs @@ -6,6 +6,9 @@ #![doc = include_str!("../README.md")] +#![cfg_attr(all(not(test), target_os = "uefi"), no_main)] +#![cfg_attr(all(not(test), target_os = "uefi"), no_std)] + // Actual entrypoint is `uefi::uefi_main`, via the `#[entry]` macro #[cfg(any(test, not(target_os = "uefi")))] fn main() {} @@ -17,3 +20,5 @@ mod uefi; pub mod arch; pub mod tmk_assert; pub mod tmk_logger; +pub mod hypercall; +pub mod context; \ No newline at end of file diff --git a/opentmk/opentmk/src/uefi/hypvctx.rs b/opentmk/opentmk/src/uefi/hypvctx.rs index 26455dbc22..bf4979568e 100644 --- a/opentmk/opentmk/src/uefi/hypvctx.rs +++ b/opentmk/opentmk/src/uefi/hypvctx.rs @@ -1,4 +1,4 @@ -use super::{ +use crate::{ context::{TestCtxTrait, VpExecutor}, hypercall::HvCall, }; @@ -431,12 +431,12 @@ impl HvTestCtx { .expect("Failed to get VTL1 context"); let stack_layout = Layout::from_size_align(SIZE_1MB, 16) .expect("Failed to create layout for stack allocation"); - let x = unsafe { ALLOCATOR.alloc(stack_layout) }; - if x.is_null() { + let allocated_stack_ptr = unsafe { ALLOCATOR.alloc(stack_layout) }; + if allocated_stack_ptr.is_null() { return Err(false); } - let sz = stack_layout.size(); - let stack_top = x as u64 + sz as u64; + let stack_size = stack_layout.size(); + let stack_top = allocated_stack_ptr as u64 + stack_size as u64; let fn_ptr = func as fn(); let fn_address = fn_ptr as u64; vp_context.rip = fn_address; diff --git a/opentmk/opentmk/src/uefi/mod.rs b/opentmk/opentmk/src/uefi/mod.rs index ed2b2e0001..1a5e04d747 100644 --- a/opentmk/opentmk/src/uefi/mod.rs +++ b/opentmk/opentmk/src/uefi/mod.rs @@ -2,8 +2,6 @@ // Licensed under the MIT License. mod alloc; -mod context; -pub mod hypercall; mod hypvctx; pub mod init; mod rt; diff --git a/opentmk/opentmk/src/uefi/tests/hv_misc.rs b/opentmk/opentmk/src/uefi/tests/hv_misc.rs index 58e27531d6..9e4f27bdcd 100644 --- a/opentmk/opentmk/src/uefi/tests/hv_misc.rs +++ b/opentmk/opentmk/src/uefi/tests/hv_misc.rs @@ -6,7 +6,7 @@ use crate::tmk_assert::{AssertOption, AssertResult}; use sync_nostd::{Channel, Receiver, Sender}; use crate::uefi::alloc::{ALLOCATOR, SIZE_1MB}; -use crate::uefi::{context, hypvctx}; +use crate::{context, uefi::hypvctx}; use crate::{tmk_assert}; use ::alloc::boxed::Box; use alloc::sync::Arc; diff --git a/opentmk/opentmk/src/uefi/tests/hv_processor.rs b/opentmk/opentmk/src/uefi/tests/hv_processor.rs index 6dec2ae748..b8fbee2ed0 100644 --- a/opentmk/opentmk/src/uefi/tests/hv_processor.rs +++ b/opentmk/opentmk/src/uefi/tests/hv_processor.rs @@ -2,7 +2,7 @@ use hvdef::Vtl; use sync_nostd::Channel; use crate::{ - tmk_assert, uefi::context::{TestCtxTrait, VpExecutor} + tmk_assert, context::{TestCtxTrait, VpExecutor} }; pub fn exec(ctx: &mut dyn TestCtxTrait) { From 2d2aec48583df98946f234a89bc97e59fd860300 Mon Sep 17 00:00:00 2001 From: Mayank Kumar Date: Wed, 21 May 2025 06:42:46 +0000 Subject: [PATCH 08/23] reafactor: errors for hyper-v error handling --- Cargo.lock | 11 +- opentmk/opentmk/src/context.rs | 104 ++++---- opentmk/opentmk/src/main.rs | 3 +- opentmk/opentmk/src/tmk_assert.rs | 102 ++++---- opentmk/opentmk/src/tmkdefs.rs | 93 +++++++ opentmk/opentmk/src/uefi/hypvctx.rs | 231 ++++++++++++------ opentmk/opentmk/src/uefi/mod.rs | 8 +- opentmk/opentmk/src/uefi/tests/hv_misc.rs | 11 +- .../opentmk/src/uefi/tests/hv_processor.rs | 44 +++- 9 files changed, 415 insertions(+), 192 deletions(-) create mode 100644 opentmk/opentmk/src/tmkdefs.rs diff --git a/Cargo.lock b/Cargo.lock index 302b3ed249..af26549104 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4791,14 +4791,16 @@ dependencies = [ "hvdef", "lazy_static", "linked_list_allocator", + "log", "memory_range", "minimal_rt", "minimal_rt_build", "serde", "serde_json", - "spin 0.10.0", + "sync_nostd", "uefi", "x86_64", + "x86defs", "zerocopy 0.8.14", ] @@ -6538,6 +6540,13 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_nostd" +version = "0.1.0" +dependencies = [ + "spin 0.10.0", +] + [[package]] name = "tap" version = "1.0.1" diff --git a/opentmk/opentmk/src/context.rs b/opentmk/opentmk/src/context.rs index da52e8ee32..80a8cd7890 100644 --- a/opentmk/opentmk/src/context.rs +++ b/opentmk/opentmk/src/context.rs @@ -4,77 +4,97 @@ use core::ops::Range; use alloc::boxed::Box; use hvdef::Vtl; +use crate::tmkdefs::TmkResult; + pub trait SecureInterceptPlatformTrait { - fn setup_secure_intercept(&mut self, interrupt_idx: u8); + fn setup_secure_intercept(&mut self, interrupt_idx: u8) -> TmkResult<()>; } pub trait InterruptPlatformTrait { - fn set_interrupt_idx(&mut self, interrupt_idx: u8, handler: fn()); - fn setup_interrupt_handler(&mut self); + fn set_interrupt_idx(&mut self, interrupt_idx: u8, handler: fn()) -> TmkResult<()>; + fn setup_interrupt_handler(&mut self) -> TmkResult<()>; } pub trait MsrPlatformTrait { - fn read_msr(&mut self, msr: u32) -> u64; - fn write_msr(&mut self, msr: u32, value: u64); + fn read_msr(&mut self, msr: u32) -> TmkResult; + fn write_msr(&mut self, msr: u32, value: u64) -> TmkResult<()>; } -pub trait VirtualProcessorlatformTrait { - fn get_register(&mut self, reg: u32) -> u128; - fn get_vp_count(&self) -> u32; - fn queue_command_vp(&mut self, cmd: VpExecutor); - fn start_on_vp(&mut self, cmd: VpExecutor); - fn start_running_vp_with_default_context(&mut self, cmd: VpExecutor); +pub trait VirtualProcessorPlatformTrait where T: VtlPlatformTrait { + fn get_register(&mut self, reg: u32) -> TmkResult; + fn get_vp_count(&self) -> TmkResult; + fn queue_command_vp(&mut self, cmd: VpExecutor) -> TmkResult<()>; + fn start_on_vp(&mut self, cmd: VpExecutor) -> TmkResult<()>; + fn start_running_vp_with_default_context(&mut self, cmd: VpExecutor) -> TmkResult<()>; } pub trait VtlPlatformTrait { - fn apply_vtl_protection_for_memory(&mut self, range: Range, vtl: Vtl); - fn enable_vp_vtl_with_default_context(&mut self, vp_index: u32, vtl: Vtl); - fn get_current_vtl(&self) -> Vtl; - fn set_default_ctx_to_vp(&mut self, vp_index: u32, vtl: Vtl); - fn setup_partition_vtl(&mut self, vtl: Vtl); - fn setup_vtl_protection(&mut self); + fn apply_vtl_protection_for_memory(&mut self, range: Range, vtl: Vtl) -> TmkResult<()>; + fn enable_vp_vtl_with_default_context(&mut self, vp_index: u32, vtl: Vtl) -> TmkResult<()>; + fn get_current_vtl(&self) -> TmkResult; + fn set_default_ctx_to_vp(&mut self, vp_index: u32, vtl: Vtl) -> TmkResult<()>; + fn setup_partition_vtl(&mut self, vtl: Vtl) -> TmkResult<()>; + fn setup_vtl_protection(&mut self) -> TmkResult<()>; fn switch_to_high_vtl(&mut self); fn switch_to_low_vtl(&mut self); } +pub trait X64PlatformTrait {} +pub trait Aarch64PlatformTrait {} - - -pub trait TestCtxTrait { - +pub trait TestCtxTrait { // partition wide Traits + /// Returns the number of virtual processors (VPs) in the partition. fn get_vp_count(&self) -> u32; - fn setup_vtl_protection(&mut self); - fn setup_partition_vtl(&mut self, vtl: Vtl); - fn setup_interrupt_handler(&mut self); - fn set_interrupt_idx(&mut self, interrupt_idx: u8, handler: fn()); - fn start_on_vp(&mut self, cmd: VpExecutor); - fn queue_command_vp(&mut self, cmd: VpExecutor); - fn setup_secure_intercept(&mut self, interrupt_idx: u8); - fn apply_vtl_protection_for_memory(&mut self, range: Range, vtl: Vtl); - fn set_default_ctx_to_vp(&mut self, vp_index: u32, vtl: Vtl); - fn start_running_vp_with_default_context(&mut self, cmd: VpExecutor); - fn enable_vp_vtl_with_default_context(&mut self, vp_index: u32, vtl: Vtl); - fn write_msr(&mut self, msr: u32, value: u64); - fn read_msr(&mut self, msr: u32) -> u64; + /// Sets up VTL (Virtualization Trust Level) protection for the partition. + fn setup_vtl_protection(&mut self) -> TmkResult<()>; + /// Sets up a specific VTL for the partition. + fn setup_partition_vtl(&mut self, vtl: Vtl) -> TmkResult<()>; + /// Sets up the interrupt handler for the partition. + fn setup_interrupt_handler(&mut self) -> TmkResult<()>; + /// Sets the interrupt handler for a specific interrupt index. + fn set_interrupt_idx(&mut self, interrupt_idx: u8, handler: fn()) -> TmkResult<()>; + /// Starts a command on a specific virtual processor. + fn start_on_vp(&mut self, cmd: VpExecutor) -> TmkResult<()>; + /// Queues a command to be executed on a virtual processor. + fn queue_command_vp(&mut self, cmd: VpExecutor) -> TmkResult<()>; + /// Sets up a secure intercept for a given interrupt index. + fn setup_secure_intercept(&mut self, interrupt_idx: u8) -> TmkResult<()>; + /// Applies VTL protection to a specified memory range. + fn apply_vtl_protection_for_memory(&mut self, range: Range, vtl: Vtl) -> TmkResult<()>; + /// Sets the default context for a specific virtual processor and VTL. + fn set_default_ctx_to_vp(&mut self, vp_index: u32, vtl: Vtl) -> TmkResult<()>; + /// Starts running a virtual processor with its default context. + fn start_running_vp_with_default_context(&mut self, cmd: VpExecutor)-> TmkResult<()>; + /// Enables VTL for a virtual processor using its default context. + fn enable_vp_vtl_with_default_context(&mut self, vp_index: u32, vtl: Vtl)-> TmkResult<()>; + /// Writes a value to a specific Model Specific Register (MSR). + fn write_msr(&mut self, msr: u32, value: u64)-> TmkResult<()>; + /// Reads a value from a specific Model Specific Register (MSR). + fn read_msr(&mut self, msr: u32) -> TmkResult; // per vp wide Traits - fn get_current_vp(&self) -> u32; - fn get_current_vtl(&self) -> Vtl; + /// Gets the index of the current virtual processor. + fn get_current_vp(&self) -> TmkResult; + /// Gets the current VTL of the virtual processor. + fn get_current_vtl(&self) -> TmkResult; + /// Switches the current virtual processor to a higher VTL. fn switch_to_high_vtl(&mut self); + /// Switches the current virtual processor to a lower VTL. fn switch_to_low_vtl(&mut self); - fn get_register(&mut self, reg: u32) -> u128; + /// Gets the value of a specific register for the current virtual processor. + fn get_register(&mut self, reg: u32) -> TmkResult; } -pub struct VpExecutor { +pub struct VpExecutor { vp_index: u32, vtl: Vtl, - cmd: Option>, + cmd: Option>, } -impl VpExecutor { +impl VpExecutor { pub fn new(vp_index: u32, vtl: Vtl) -> Self { VpExecutor { vp_index, @@ -83,12 +103,12 @@ impl VpExecutor { } } - pub fn command(mut self, cmd: impl FnOnce(&mut dyn TestCtxTrait) + 'static) -> Self { + pub fn command(mut self, cmd: impl FnOnce(&mut T) + 'static) -> Self { self.cmd = Some(Box::new(cmd)); self } - pub fn get(mut self) -> (u32, Vtl, Option>) { + pub fn get(mut self) -> (u32, Vtl, Option>) { let cmd = self.cmd.take(); (self.vp_index, self.vtl, cmd) } diff --git a/opentmk/opentmk/src/main.rs b/opentmk/opentmk/src/main.rs index dc49cd0347..f8ffeb4ab8 100644 --- a/opentmk/opentmk/src/main.rs +++ b/opentmk/opentmk/src/main.rs @@ -21,4 +21,5 @@ pub mod arch; pub mod tmk_assert; pub mod tmk_logger; pub mod hypercall; -pub mod context; \ No newline at end of file +pub mod context; +pub mod tmkdefs; \ No newline at end of file diff --git a/opentmk/opentmk/src/tmk_assert.rs b/opentmk/opentmk/src/tmk_assert.rs index dad6e46dfe..232ee7acb1 100644 --- a/opentmk/opentmk/src/tmk_assert.rs +++ b/opentmk/opentmk/src/tmk_assert.rs @@ -1,4 +1,4 @@ -use core::{any::type_name, fmt::Write}; +use core::fmt::Write; use alloc::string::{String, ToString}; use serde::Serialize; use serde_json::json; @@ -62,54 +62,54 @@ pub trait AssertOption { fn expect_assert(self, message: &str) -> T; } -impl AssertOption for Option { - fn expect_assert(self, message: &str) -> T { - match self { - Some(value) => value, - None => { - let call: &core::panic::Location<'_> = core::panic::Location::caller(); - let file_line = format!("{}:{}", call.file(), call.line()); - let expn = type_name::>(); - let js = format_assert_json_string(expn, true, file_line, false, &message); - write_str(&js); - panic!("Assertion failed: {}", message); - } - } - } -} +// impl AssertOption for Option { +// fn expect_assert(self, message: &str) -> T { +// match self { +// Some(value) => value, +// None => { +// let call: &core::panic::Location<'_> = core::panic::Location::caller(); +// let file_line = format!("{}:{}", call.file(), call.line()); +// let expn = type_name::>(); +// let js = format_assert_json_string(expn, true, file_line, false, &message); +// write_str(&js); +// panic!("Assertion failed: {}", message); +// } +// } +// } +// } -impl AssertResult for Result -where - E: core::fmt::Debug, -{ - fn unpack_assert(self) -> T { - match self { - Ok(value) => value, - Err(err) => { - let call: &core::panic::Location<'_> = core::panic::Location::caller(); - let file_line = format!("{}:{}", call.file(), call.line()); - let expn = type_name::>(); - let js = - format_assert_json_string(expn, true, file_line, false, &"ResultTest"); - write_str(&js); - panic!("Assertion failed: {:?}", err); - } - } - } - fn expect_assert(self, message: &str) -> T { - match self { - Ok(value) => { - log::info!("result is ok, condition not met for: {}", message); - value - } - Err(err) => { - let call: &core::panic::Location<'_> = core::panic::Location::caller(); - let file_line = format!("{}:{}", call.file(), call.line()); - let expn = type_name::>(); - let js = format_assert_json_string(expn, true, file_line, false, &message); - write_str(&js); - panic!("Assertion failed: {:?}", err); - } - } - } -} +// impl AssertResult for Result +// where +// E: core::fmt::Debug, +// { +// fn unpack_assert(self) -> T { +// match self { +// Ok(value) => value, +// Err(err) => { +// let call: &core::panic::Location<'_> = core::panic::Location::caller(); +// let file_line = format!("{}:{}", call.file(), call.line()); +// let expn = type_name::>(); +// let js = +// format_assert_json_string(expn, true, file_line, false, &"ResultTest"); +// write_str(&js); +// panic!("Assertion failed: {:?}", err); +// } +// } +// } +// fn expect_assert(self, message: &str) -> T { +// match self { +// Ok(value) => { +// log::info!("result is ok, condition not met for: {}", message); +// value +// } +// Err(err) => { +// let call: &core::panic::Location<'_> = core::panic::Location::caller(); +// let file_line = format!("{}:{}", call.file(), call.line()); +// let expn = type_name::>(); +// let js = format_assert_json_string(expn, true, file_line, false, &message); +// write_str(&js); +// panic!("Assertion failed: {:?}", err); +// } +// } +// } +// } diff --git a/opentmk/opentmk/src/tmkdefs.rs b/opentmk/opentmk/src/tmkdefs.rs new file mode 100644 index 0000000000..57f9faaa3f --- /dev/null +++ b/opentmk/opentmk/src/tmkdefs.rs @@ -0,0 +1,93 @@ +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum TmkErrorType { + AllocationFailed, + InvalidParameter, + EnableVtlFailed, + SetDefaultCtxFailed, + StartVpFailed, + QueueCommandFailed, + SetupVtlProtectionFailed, + SetupPartitionVtlFailed, + SetupInterruptHandlerFailed, + SetInterruptIdxFailed, + SetupSecureInterceptFailed, + ApplyVtlProtectionForMemoryFailed, + ReadMsrFailed, + WriteMsrFailed, + GetRegisterFailed, + InvalidHypercallCode, + InvalidHypercallInput, + InvalidAlignment, + AccessDenied, + InvalidPartitionState, + OperationDenied, + UnknownProperty, + PropertyValueOutOfRange, + InsufficientMemory, + PartitionTooDeep, + InvalidPartitionId, + InvalidVpIndex, + NotFound, + InvalidPortId, + InvalidConnectionId, + InsufficientBuffers, + NotAcknowledged, + InvalidVpState, + Acknowledged, + InvalidSaveRestoreState, + InvalidSynicState, + ObjectInUse, + InvalidProximityDomainInfo, + NoData, + Inactive, + NoResources, + FeatureUnavailable, + PartialPacket, + ProcessorFeatureNotSupported, + ProcessorCacheLineFlushSizeIncompatible, + InsufficientBuffer, + IncompatibleProcessor, + InsufficientDeviceDomains, + CpuidFeatureValidationError, + CpuidXsaveFeatureValidationError, + ProcessorStartupTimeout, + SmxEnabled, + InvalidLpIndex, + InvalidRegisterValue, + InvalidVtlState, + NxNotDetected, + InvalidDeviceId, + InvalidDeviceState, + PendingPageRequests, + PageRequestInvalid, + KeyAlreadyExists, + DeviceAlreadyInDomain, + InvalidCpuGroupId, + InvalidCpuGroupState, + OperationFailed, + NotAllowedWithNestedVirtActive, + InsufficientRootMemory, + EventBufferAlreadyFreed, + Timeout, + VtlAlreadyEnabled, + UnknownRegisterName, +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub struct TmkError(pub TmkErrorType); + +pub type TmkResult = Result; + +impl core::error::Error for TmkError {} + +impl core::fmt::Display for TmkError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "TmkError({:?})", self.0) + } +} + +impl From for TmkError { + fn from(e: TmkErrorType) -> Self { + TmkError(e) + } +} diff --git a/opentmk/opentmk/src/uefi/hypvctx.rs b/opentmk/opentmk/src/uefi/hypvctx.rs index bf4979568e..c72dbd0183 100644 --- a/opentmk/opentmk/src/uefi/hypvctx.rs +++ b/opentmk/opentmk/src/uefi/hypvctx.rs @@ -1,13 +1,13 @@ use crate::{ - context::{TestCtxTrait, VpExecutor}, + context::{TestCtxTrait, VpExecutor, VtlPlatformTrait}, hypercall::HvCall, + tmkdefs::{TmkError, TmkErrorType, TmkResult}, }; use crate::uefi::alloc::ALLOCATOR; -use crate::tmk_assert::AssertResult; -use crate::tmk_assert::AssertOption; -use alloc::collections::btree_map::BTreeMap; + +use alloc::collections::{btree_map::BTreeMap, btree_set::BTreeSet}; use alloc::collections::linked_list::LinkedList; -use alloc::{boxed::Box, vec::Vec}; +use alloc::boxed::Box; use core::alloc::{GlobalAlloc, Layout}; use core::arch::asm; use core::ops::Range; @@ -20,7 +20,7 @@ use sync_nostd::Mutex; const ALIGNMENT: usize = 4096; type ComandTable = - BTreeMap, Vtl)>>; + BTreeMap, Vtl)>>; static mut CMD: Mutex = Mutex::new(BTreeMap::new()); #[allow(static_mut_refs)] @@ -40,7 +40,7 @@ fn register_command_queue(vp_index: u32) { pub struct HvTestCtx { pub hvcall: HvCall, - pub vp_runing: Vec<(u32, (bool, bool))>, + pub vp_runing: BTreeSet, pub my_vp_idx: u32, pub my_vtl: Vtl, } @@ -116,25 +116,22 @@ impl Drop for HvTestCtx { /// /// - `get_current_vtl(&self) -> Vtl`: /// Returns the current Virtual Trust Level (VTL). -impl TestCtxTrait for HvTestCtx { - fn start_on_vp(&mut self, cmd: VpExecutor) { +impl TestCtxTrait for HvTestCtx { + fn start_on_vp(&mut self, cmd: VpExecutor) -> TmkResult<()> { let (vp_index, vtl, cmd) = cmd.get(); - let cmd = cmd.expect_assert("error: failed to get command as cmd is none"); + let cmd = cmd.ok_or_else(|| TmkError(TmkErrorType::InvalidParameter))?; if vtl >= Vtl::Vtl2 { panic!("error: can't run on vtl2"); } - let is_vp_running = self.vp_runing.iter_mut().find(|x| x.0 == vp_index); - + let is_vp_running = self.vp_runing.get(&vp_index); if let Some(_running_vtl) = is_vp_running { log::debug!("both vtl0 and vtl1 are running for VP: {:?}", vp_index); } else { if vp_index == 0 { let vp_context = self - .get_default_context() - .expect("error: failed to get default context"); + .get_default_context()?; self.hvcall - .enable_vp_vtl(0, Vtl::Vtl1, Some(vp_context)) - .expect("error: failed to enable vtl1"); + .enable_vp_vtl(0, Vtl::Vtl1, Some(vp_context))?; cmdt().lock().get_mut(&vp_index).unwrap().push_back(( Box::new(move |ctx| { @@ -143,20 +140,14 @@ impl TestCtxTrait for HvTestCtx { Vtl::Vtl1, )); self.switch_to_high_vtl(); - self.vp_runing.push((vp_index, (true, true))); + self.vp_runing.insert(vp_index); } else { cmdt().lock().get_mut(&self.my_vp_idx).unwrap().push_back(( Box::new(move |ctx| { - ctx.enable_vp_vtl_with_default_context(vp_index, Vtl::Vtl1); - ctx.start_running_vp_with_default_context(VpExecutor::new( + _ = ctx.enable_vp_vtl_with_default_context(vp_index, Vtl::Vtl1); + _ = ctx.start_running_vp_with_default_context(VpExecutor::new( vp_index, - Vtl::Vtl1, - )); - cmdt().lock().get_mut(&vp_index).unwrap().push_back(( - Box::new(move |ctx| { - ctx.set_default_ctx_to_vp(vp_index, Vtl::Vtl0); - }), - Vtl::Vtl1, + Vtl::Vtl0, )); ctx.switch_to_low_vtl(); }), @@ -164,7 +155,7 @@ impl TestCtxTrait for HvTestCtx { )); self.switch_to_high_vtl(); - self.vp_runing.push((vp_index, (true, true))); + self.vp_runing.insert(vp_index); } } cmdt() @@ -179,17 +170,19 @@ impl TestCtxTrait for HvTestCtx { self.switch_to_high_vtl(); } } + Ok(()) } - fn queue_command_vp(&mut self, cmd: VpExecutor) { + fn queue_command_vp(&mut self, cmd: VpExecutor) -> TmkResult<()> { let (vp_index, vtl, cmd) = cmd.get(); let cmd = - cmd.expect_assert("error: failed to get command as cmd is none with queue command vp"); + cmd.ok_or_else(|| TmkError(TmkErrorType::QueueCommandFailed))?; cmdt() .lock() .get_mut(&vp_index) .unwrap() .push_back((cmd, vtl)); + Ok(()) } fn switch_to_high_vtl(&mut self) { @@ -200,27 +193,27 @@ impl TestCtxTrait for HvTestCtx { HvCall::vtl_return(); } - fn setup_partition_vtl(&mut self, vtl: Vtl) { + fn setup_partition_vtl(&mut self, vtl: Vtl) -> TmkResult<()> { self.hvcall - .enable_partition_vtl(hvdef::HV_PARTITION_ID_SELF, vtl) - .expect_assert("Failed to enable VTL1 for the partition"); + .enable_partition_vtl(hvdef::HV_PARTITION_ID_SELF, vtl)?; log::info!("enabled vtl protections for the partition."); + Ok(()) } - fn setup_interrupt_handler(&mut self) { + fn setup_interrupt_handler(&mut self) -> TmkResult<()> { crate::arch::interrupt::init(); + Ok(()) } - fn setup_vtl_protection(&mut self) { + fn setup_vtl_protection(&mut self) -> TmkResult<()> { self.hvcall - .enable_vtl_protection(HvInputVtl::CURRENT_VTL) - .expect_assert("Failed to enable VTL protection, vtl1"); + .enable_vtl_protection(HvInputVtl::CURRENT_VTL)?; log::info!("enabled vtl protections for the partition."); + Ok(()) } - fn setup_secure_intercept(&mut self, interrupt_idx: u8) { - let layout = Layout::from_size_align(4096, ALIGNMENT) - .expect_assert("error: failed to create layout for SIMP page"); + fn setup_secure_intercept(&mut self, interrupt_idx: u8) -> TmkResult<()> { + let layout = Layout::from_size_align(4096, ALIGNMENT).or_else(|_| Err(TmkError(TmkErrorType::AllocationFailed)))?; let ptr = unsafe { ALLOCATOR.alloc(layout) }; let gpn = (ptr as u64) >> 12; @@ -235,43 +228,44 @@ impl TestCtxTrait for HvTestCtx { reg.set_masked(false); reg.set_auto_eoi(true); - self.write_msr(hvdef::HV_X64_MSR_SINT0, reg.into()); + self.write_msr(hvdef::HV_X64_MSR_SINT0, reg.into())?; log::info!("Successfuly set the SINT0 register."); + Ok(()) } - fn apply_vtl_protection_for_memory(&mut self, range: Range, vtl: Vtl) { + fn apply_vtl_protection_for_memory(&mut self, range: Range, vtl: Vtl) -> TmkResult<()> { self.hvcall - .apply_vtl_protections(MemoryRange::new(range), vtl) - .expect_assert("Failed to apply VTL protections"); + .apply_vtl_protections(MemoryRange::new(range), vtl)?; + Ok(()) } - fn write_msr(&mut self, msr: u32, value: u64) { + fn write_msr(&mut self, msr: u32, value: u64) -> TmkResult<()> { unsafe { write_msr(msr, value) }; + Ok(()) } - fn read_msr(&mut self, msr: u32) -> u64 { - unsafe { read_msr(msr) } + fn read_msr(&mut self, msr: u32) -> TmkResult { + let r = unsafe { read_msr(msr) }; + Ok(r) } - fn start_running_vp_with_default_context(&mut self, cmd: VpExecutor) { + fn start_running_vp_with_default_context(&mut self, cmd: VpExecutor) -> TmkResult<()> { let (vp_index, vtl, _cmd) = cmd.get(); let vp_ctx = self - .get_default_context() - .expect_assert("error: failed to get default context"); + .get_default_context()?; self.hvcall - .start_virtual_processor(vp_index, vtl, Some(vp_ctx)) - .expect_assert("error: failed to start vp"); + .start_virtual_processor(vp_index, vtl, Some(vp_ctx))?; + Ok(()) } - fn set_default_ctx_to_vp(&mut self, vp_index: u32, vtl: Vtl) { + fn set_default_ctx_to_vp(&mut self, vp_index: u32, vtl: Vtl) -> TmkResult<()> { let i: u8 = match vtl { Vtl::Vtl0 => 0, Vtl::Vtl1 => 1, Vtl::Vtl2 => 2, }; let vp_context = self - .get_default_context() - .expect_assert("error: failed to get default context"); + .get_default_context()?; self.hvcall .set_vp_registers( vp_index, @@ -281,22 +275,22 @@ impl TestCtxTrait for HvTestCtx { .with_use_target_vtl(true), ), Some(vp_context), - ) - .expect_assert("error: failed to set vp registers"); + )?; + Ok(()) } - fn enable_vp_vtl_with_default_context(&mut self, vp_index: u32, vtl: Vtl) { + fn enable_vp_vtl_with_default_context(&mut self, vp_index: u32, vtl: Vtl) -> TmkResult<()> { let vp_ctx = self - .get_default_context() - .expect_assert("error: failed to get default context"); + .get_default_context()?; self.hvcall - .enable_vp_vtl(vp_index, vtl, Some(vp_ctx)) - .expect_assert("error: failed to enable vp vtl"); + .enable_vp_vtl(vp_index, vtl, Some(vp_ctx))?; + Ok(()) } #[cfg(target_arch = "x86_64")] - fn set_interrupt_idx(&mut self, interrupt_idx: u8, handler: fn()) { + fn set_interrupt_idx(&mut self, interrupt_idx: u8, handler: fn()) -> TmkResult<()> { crate::arch::interrupt::set_handler(interrupt_idx, handler); + Ok(()) } #[cfg(target_arch = "x86_64")] @@ -322,33 +316,33 @@ impl TestCtxTrait for HvTestCtx { } #[cfg(target_arch = "x86_64")] - fn get_register(&mut self, reg: u32) -> u128 { + fn get_register(&mut self, reg: u32) -> TmkResult { use hvdef::HvX64RegisterName; let reg = HvX64RegisterName(reg); - self.hvcall - .get_register(reg.into(), None) - .expect_assert("error: failed to get register") - .as_u128() + let val = self.hvcall + .get_register(reg.into(), None)? + .as_u128(); + Ok(val) } #[cfg(target_arch = "aarch64")] - fn get_register(&mut self, reg: u32) -> u128 { + fn get_register(&mut self, reg: u32) -> TmkResult { use hvdef::HvAarch64RegisterName; let reg = HvAarch64RegisterName(reg); - self.hvcall - .get_register(reg.into(), None) - .expect_assert("error: failed to get register") - .as_u128() + let val = self.hvcall + .get_register(reg.into(), None)? + .as_u128(); + Ok(val) } - fn get_current_vp(&self) -> u32 { - self.my_vp_idx + fn get_current_vp(&self) -> TmkResult { + Ok(self.my_vp_idx) } - fn get_current_vtl(&self) -> Vtl { - self.my_vtl + fn get_current_vtl(&self) -> TmkResult { + Ok(self.my_vtl) } } @@ -356,7 +350,7 @@ impl HvTestCtx { pub const fn new() -> Self { HvTestCtx { hvcall: HvCall::new(), - vp_runing: Vec::new(), + vp_runing: BTreeSet::new(), my_vp_idx: 0, my_vtl: Vtl::Vtl0, } @@ -383,7 +377,7 @@ impl HvTestCtx { loop { let mut vtl: Option = None; - let mut cmd: Option> = None; + let mut cmd: Option> = None; { let mut cmdt = cmdt().lock(); @@ -417,12 +411,12 @@ impl HvTestCtx { } #[cfg(target_arch = "x86_64")] - fn get_default_context(&mut self) -> Result { + fn get_default_context(&mut self) -> Result { return self.run_fn_with_current_context(HvTestCtx::exec_handler); } #[cfg(target_arch = "x86_64")] - fn run_fn_with_current_context(&mut self, func: fn()) -> Result { + fn run_fn_with_current_context(&mut self, func: fn()) -> Result { use super::alloc::SIZE_1MB; let mut vp_context: InitialVpContextX64 = self @@ -433,7 +427,7 @@ impl HvTestCtx { .expect("Failed to create layout for stack allocation"); let allocated_stack_ptr = unsafe { ALLOCATOR.alloc(stack_layout) }; if allocated_stack_ptr.is_null() { - return Err(false); + return Err(TmkErrorType::AllocationFailed.into()); } let stack_size = stack_layout.size(); let stack_top = allocated_stack_ptr as u64 + stack_size as u64; @@ -444,3 +438,78 @@ impl HvTestCtx { Ok(vp_context) } } + +impl From for TmkError { + fn from(e: hvdef::HvError) -> Self { + log::debug!("Converting hvdef::HvError::{:?} to TmkError", e); + let tmk_error_type = match e { + hvdef::HvError::InvalidHypercallCode => TmkErrorType::InvalidHypercallCode, + hvdef::HvError::InvalidHypercallInput => TmkErrorType::InvalidHypercallInput, + hvdef::HvError::InvalidAlignment => TmkErrorType::InvalidAlignment, + hvdef::HvError::InvalidParameter => TmkErrorType::InvalidParameter, + hvdef::HvError::AccessDenied => TmkErrorType::AccessDenied, + hvdef::HvError::InvalidPartitionState => TmkErrorType::InvalidPartitionState, + hvdef::HvError::OperationDenied => TmkErrorType::OperationDenied, + hvdef::HvError::UnknownProperty => TmkErrorType::UnknownProperty, + hvdef::HvError::PropertyValueOutOfRange => TmkErrorType::PropertyValueOutOfRange, + hvdef::HvError::InsufficientMemory => TmkErrorType::InsufficientMemory, + hvdef::HvError::PartitionTooDeep => TmkErrorType::PartitionTooDeep, + hvdef::HvError::InvalidPartitionId => TmkErrorType::InvalidPartitionId, + hvdef::HvError::InvalidVpIndex => TmkErrorType::InvalidVpIndex, + hvdef::HvError::NotFound => TmkErrorType::NotFound, + hvdef::HvError::InvalidPortId => TmkErrorType::InvalidPortId, + hvdef::HvError::InvalidConnectionId => TmkErrorType::InvalidConnectionId, + hvdef::HvError::InsufficientBuffers => TmkErrorType::InsufficientBuffers, + hvdef::HvError::NotAcknowledged => TmkErrorType::NotAcknowledged, + hvdef::HvError::InvalidVpState => TmkErrorType::InvalidVpState, + hvdef::HvError::Acknowledged => TmkErrorType::Acknowledged, + hvdef::HvError::InvalidSaveRestoreState => TmkErrorType::InvalidSaveRestoreState, + hvdef::HvError::InvalidSynicState => TmkErrorType::InvalidSynicState, + hvdef::HvError::ObjectInUse => TmkErrorType::ObjectInUse, + hvdef::HvError::InvalidProximityDomainInfo => TmkErrorType::InvalidProximityDomainInfo, + hvdef::HvError::NoData => TmkErrorType::NoData, + hvdef::HvError::Inactive => TmkErrorType::Inactive, + hvdef::HvError::NoResources => TmkErrorType::NoResources, + hvdef::HvError::FeatureUnavailable => TmkErrorType::FeatureUnavailable, + hvdef::HvError::PartialPacket => TmkErrorType::PartialPacket, + hvdef::HvError::ProcessorFeatureNotSupported => TmkErrorType::ProcessorFeatureNotSupported, + hvdef::HvError::ProcessorCacheLineFlushSizeIncompatible => TmkErrorType::ProcessorCacheLineFlushSizeIncompatible, + hvdef::HvError::InsufficientBuffer => TmkErrorType::InsufficientBuffer, + hvdef::HvError::IncompatibleProcessor => TmkErrorType::IncompatibleProcessor, + hvdef::HvError::InsufficientDeviceDomains => TmkErrorType::InsufficientDeviceDomains, + hvdef::HvError::CpuidFeatureValidationError => TmkErrorType::CpuidFeatureValidationError, + hvdef::HvError::CpuidXsaveFeatureValidationError => TmkErrorType::CpuidXsaveFeatureValidationError, + hvdef::HvError::ProcessorStartupTimeout => TmkErrorType::ProcessorStartupTimeout, + hvdef::HvError::SmxEnabled => TmkErrorType::SmxEnabled, + hvdef::HvError::InvalidLpIndex => TmkErrorType::InvalidLpIndex, + hvdef::HvError::InvalidRegisterValue => TmkErrorType::InvalidRegisterValue, + hvdef::HvError::InvalidVtlState => TmkErrorType::InvalidVtlState, + hvdef::HvError::NxNotDetected => TmkErrorType::NxNotDetected, + hvdef::HvError::InvalidDeviceId => TmkErrorType::InvalidDeviceId, + hvdef::HvError::InvalidDeviceState => TmkErrorType::InvalidDeviceState, + hvdef::HvError::PendingPageRequests => TmkErrorType::PendingPageRequests, + hvdef::HvError::PageRequestInvalid => TmkErrorType::PageRequestInvalid, + hvdef::HvError::KeyAlreadyExists => TmkErrorType::KeyAlreadyExists, + hvdef::HvError::DeviceAlreadyInDomain => TmkErrorType::DeviceAlreadyInDomain, + hvdef::HvError::InvalidCpuGroupId => TmkErrorType::InvalidCpuGroupId, + hvdef::HvError::InvalidCpuGroupState => TmkErrorType::InvalidCpuGroupState, + hvdef::HvError::OperationFailed => TmkErrorType::OperationFailed, + hvdef::HvError::NotAllowedWithNestedVirtActive => TmkErrorType::NotAllowedWithNestedVirtActive, + hvdef::HvError::InsufficientRootMemory => TmkErrorType::InsufficientRootMemory, + hvdef::HvError::EventBufferAlreadyFreed => TmkErrorType::EventBufferAlreadyFreed, + hvdef::HvError::Timeout => TmkErrorType::Timeout, + hvdef::HvError::VtlAlreadyEnabled => TmkErrorType::VtlAlreadyEnabled, + hvdef::HvError::UnknownRegisterName => TmkErrorType::UnknownRegisterName, + // Add any other specific mappings here if hvdef::HvError has more variants + _ => { + log::warn!( + "Unhandled hvdef::HvError variant: {:?}. Mapping to TmkErrorType::OperationFailed.", + e + ); + TmkErrorType::OperationFailed // Generic fallback + } + }; + log::debug!("Mapped hvdef::HvError::{:?} to TmkErrorType::{:?}", e, tmk_error_type); + TmkError(tmk_error_type) + } +} \ No newline at end of file diff --git a/opentmk/opentmk/src/uefi/mod.rs b/opentmk/opentmk/src/uefi/mod.rs index 1a5e04d747..a1e677259d 100644 --- a/opentmk/opentmk/src/uefi/mod.rs +++ b/opentmk/opentmk/src/uefi/mod.rs @@ -7,14 +7,18 @@ pub mod init; mod rt; mod tests; -use crate::tmk_assert::AssertResult; +use crate::tmk_assert; use init::init; use uefi::entry; use uefi::Status; #[entry] fn uefi_main() -> Status { - init().expect_assert("Failed to initialize environment"); + let r= init(); + tmk_assert!(r.is_ok(), "init should succeed"); + + log::warn!("TEST_START"); tests::run_test(); + log::warn!("TEST_END"); Status::SUCCESS } diff --git a/opentmk/opentmk/src/uefi/tests/hv_misc.rs b/opentmk/opentmk/src/uefi/tests/hv_misc.rs index 9e4f27bdcd..681d8baa3b 100644 --- a/opentmk/opentmk/src/uefi/tests/hv_misc.rs +++ b/opentmk/opentmk/src/uefi/tests/hv_misc.rs @@ -4,6 +4,7 @@ // This test is to verify that the VTL protections are working as expected. // The stack values in VTL0 are changing after interrupt handling in VTL1. use crate::tmk_assert::{AssertOption, AssertResult}; +use crate::tmkdefs::TmkResult; use sync_nostd::{Channel, Receiver, Sender}; use crate::uefi::alloc::{ALLOCATOR, SIZE_1MB}; use crate::{context, uefi::hypvctx}; @@ -26,7 +27,7 @@ use uefi::Status; static mut HEAPX: RefCell<*mut u8> = RefCell::new(0 as *mut u8); static mut CON: AtomicI32 = AtomicI32::new(0); -pub fn exec(ctx: &mut hypvctx::HvTestCtx ) { +pub fn exec(ctx: &mut T) where T: TestCtxTrait { log::info!("ctx ptr: {:p}", &ctx as *const _); let mut vp_count = ctx.get_vp_count(); @@ -39,7 +40,7 @@ pub fn exec(ctx: &mut hypvctx::HvTestCtx ) { ctx.setup_partition_vtl(Vtl::Vtl1); ctx.start_on_vp( - VpExecutor::new(0, Vtl::Vtl1).command(move |ctx: &mut dyn TestCtxTrait| { + VpExecutor::new(0, Vtl::Vtl1).command(move |ctx: &mut T|{ log::info!("successfully started running VTL1 on vp0."); ctx.setup_secure_intercept(0x30); ctx.set_interrupt_idx(0x30, || { @@ -49,7 +50,9 @@ pub fn exec(ctx: &mut hypvctx::HvTestCtx ) { hv_test_ctx.init(); let c = hv_test_ctx.get_register(HvAllArchRegisterName::VsmVpStatus.0); - + tmk_assert!(c.is_ok(), "read should succeed"); + + let c = c.unwrap(); let cp = HvRegisterVsmVpStatus::from_bits(c as u64); log::info!("VSM VP Status: {:?}", cp); @@ -85,7 +88,7 @@ pub fn exec(ctx: &mut hypvctx::HvTestCtx ) { }), ); - ctx.queue_command_vp(VpExecutor::new(0, Vtl::Vtl1).command(move |ctx| { + ctx.queue_command_vp(VpExecutor::new(0, Vtl::Vtl1).command(move |ctx: &mut T| { log::info!("successfully started running VTL1 on vp0."); ctx.switch_to_low_vtl(); })); diff --git a/opentmk/opentmk/src/uefi/tests/hv_processor.rs b/opentmk/opentmk/src/uefi/tests/hv_processor.rs index b8fbee2ed0..7ef48d0f1f 100644 --- a/opentmk/opentmk/src/uefi/tests/hv_processor.rs +++ b/opentmk/opentmk/src/uefi/tests/hv_processor.rs @@ -5,29 +5,39 @@ use crate::{ tmk_assert, context::{TestCtxTrait, VpExecutor} }; -pub fn exec(ctx: &mut dyn TestCtxTrait) { - ctx.setup_interrupt_handler(); - ctx.setup_partition_vtl(Vtl::Vtl1); +pub fn exec(ctx: &mut T) where T: TestCtxTrait { + let r = ctx.setup_interrupt_handler(); + tmk_assert!(r.is_ok(), "setup_interrupt_handler should succeed"); + let r = ctx.setup_partition_vtl(Vtl::Vtl1); + tmk_assert!(r.is_ok(), "setup_partition_vtl should succeed"); + let vp_count = ctx.get_vp_count(); tmk_assert!(vp_count == 8, "vp count should be 8"); // Testing BSP VTL Bringup { let (tx, rx) = Channel::new().split(); - ctx.start_on_vp(VpExecutor::new(0, Vtl::Vtl1).command( - move |ctx: &mut dyn TestCtxTrait| { + let result = ctx.start_on_vp(VpExecutor::new(0, Vtl::Vtl1).command( + move |ctx: &mut T| { let vp = ctx.get_current_vp(); + tmk_assert!(vp.is_ok(), "vp should be valid"); + + let vp = vp.unwrap(); log::info!("vp: {}", vp); tmk_assert!(vp == 0, "vp should be equal to 0"); let vtl = ctx.get_current_vtl(); + tmk_assert!(vtl.is_ok(), "vtl should be valid"); + + let vtl = vtl.unwrap(); log::info!("vtl: {:?}", vtl); tmk_assert!(vtl == Vtl::Vtl1, "vtl should be Vtl1 for BSP"); - _ = tx.send(()); + tx.send(()).expect("Failed to send message through the channel"); ctx.switch_to_low_vtl(); }, )); + tmk_assert!(result.is_ok(), "start_on_vp should succeed"); _ = rx.recv(); } @@ -35,36 +45,50 @@ pub fn exec(ctx: &mut dyn TestCtxTrait) { // Testing VTL1 { let (tx, rx) = Channel::new().split(); - ctx.start_on_vp(VpExecutor::new(i, Vtl::Vtl1).command( - move |ctx: &mut dyn TestCtxTrait| { + let result = ctx.start_on_vp(VpExecutor::new(i, Vtl::Vtl1).command( + move |ctx: &mut T| { let vp = ctx.get_current_vp(); + tmk_assert!(vp.is_ok(), "vp should be valid"); + + let vp = vp.unwrap(); log::info!("vp: {}", vp); tmk_assert!(vp == i, format!("vp should be equal to {}", i)); let vtl = ctx.get_current_vtl(); + tmk_assert!(vtl.is_ok(), "vtl should be valid"); + + let vtl = vtl.unwrap(); log::info!("vtl: {:?}", vtl); tmk_assert!(vtl == Vtl::Vtl1, format!("vtl should be Vtl1 for VP {}", i)); _ = tx.send(()); }, )); + tmk_assert!(result.is_ok(), "start_on_vp should succeed"); _ = rx.recv(); } // Testing VTL0 { let (tx, rx) = Channel::new().split(); - ctx.start_on_vp(VpExecutor::new(i, Vtl::Vtl0).command( - move |ctx: &mut dyn TestCtxTrait| { + let result = ctx.start_on_vp(VpExecutor::new(i, Vtl::Vtl0).command( + move |ctx: &mut T| { let vp = ctx.get_current_vp(); + tmk_assert!(vp.is_ok(), "vp should be valid"); + + let vp = vp.unwrap(); log::info!("vp: {}", vp); tmk_assert!(vp == i, format!("vp should be equal to {}", i)); let vtl = ctx.get_current_vtl(); + tmk_assert!(vtl.is_ok(), "vtl should be valid"); + + let vtl = vtl.unwrap(); log::info!("vtl: {:?}", vtl); tmk_assert!(vtl == Vtl::Vtl0, format!("vtl should be Vtl0 for VP {}", i)); _ = tx.send(()); }, )); + tmk_assert!(result.is_ok(), "start_on_vp should succeed"); _ = rx.recv(); } } From 4cd03a596f364cc6f519685b0b8d202d75447daa Mon Sep 17 00:00:00 2001 From: Mayank Kumar Date: Wed, 21 May 2025 07:26:08 +0000 Subject: [PATCH 09/23] feat: add test for a negitive case where parition has not enabled VTL1 --- opentmk/opentmk/src/context.rs | 46 +- opentmk/opentmk/src/uefi/hypvctx.rs | 456 +++++++++--------- opentmk/opentmk/src/uefi/rt.rs | 4 +- .../src/uefi/tests/hv_error_vp_start.rs | 48 ++ opentmk/opentmk/src/uefi/tests/hv_misc.rs | 107 ++-- .../opentmk/src/uefi/tests/hv_processor.rs | 73 +-- opentmk/opentmk/src/uefi/tests/mod.rs | 6 +- 7 files changed, 373 insertions(+), 367 deletions(-) create mode 100644 opentmk/opentmk/src/uefi/tests/hv_error_vp_start.rs diff --git a/opentmk/opentmk/src/context.rs b/opentmk/opentmk/src/context.rs index 80a8cd7890..304907f221 100644 --- a/opentmk/opentmk/src/context.rs +++ b/opentmk/opentmk/src/context.rs @@ -22,6 +22,7 @@ pub trait MsrPlatformTrait { } pub trait VirtualProcessorPlatformTrait where T: VtlPlatformTrait { + fn get_current_vp(&self) -> TmkResult; fn get_register(&mut self, reg: u32) -> TmkResult; fn get_vp_count(&self) -> TmkResult; fn queue_command_vp(&mut self, cmd: VpExecutor) -> TmkResult<()>; @@ -43,51 +44,6 @@ pub trait VtlPlatformTrait { pub trait X64PlatformTrait {} pub trait Aarch64PlatformTrait {} -pub trait TestCtxTrait { - // partition wide Traits - /// Returns the number of virtual processors (VPs) in the partition. - fn get_vp_count(&self) -> u32; - /// Sets up VTL (Virtualization Trust Level) protection for the partition. - fn setup_vtl_protection(&mut self) -> TmkResult<()>; - /// Sets up a specific VTL for the partition. - fn setup_partition_vtl(&mut self, vtl: Vtl) -> TmkResult<()>; - /// Sets up the interrupt handler for the partition. - fn setup_interrupt_handler(&mut self) -> TmkResult<()>; - /// Sets the interrupt handler for a specific interrupt index. - fn set_interrupt_idx(&mut self, interrupt_idx: u8, handler: fn()) -> TmkResult<()>; - /// Starts a command on a specific virtual processor. - fn start_on_vp(&mut self, cmd: VpExecutor) -> TmkResult<()>; - /// Queues a command to be executed on a virtual processor. - fn queue_command_vp(&mut self, cmd: VpExecutor) -> TmkResult<()>; - /// Sets up a secure intercept for a given interrupt index. - fn setup_secure_intercept(&mut self, interrupt_idx: u8) -> TmkResult<()>; - /// Applies VTL protection to a specified memory range. - fn apply_vtl_protection_for_memory(&mut self, range: Range, vtl: Vtl) -> TmkResult<()>; - /// Sets the default context for a specific virtual processor and VTL. - fn set_default_ctx_to_vp(&mut self, vp_index: u32, vtl: Vtl) -> TmkResult<()>; - /// Starts running a virtual processor with its default context. - fn start_running_vp_with_default_context(&mut self, cmd: VpExecutor)-> TmkResult<()>; - /// Enables VTL for a virtual processor using its default context. - fn enable_vp_vtl_with_default_context(&mut self, vp_index: u32, vtl: Vtl)-> TmkResult<()>; - /// Writes a value to a specific Model Specific Register (MSR). - fn write_msr(&mut self, msr: u32, value: u64)-> TmkResult<()>; - /// Reads a value from a specific Model Specific Register (MSR). - fn read_msr(&mut self, msr: u32) -> TmkResult; - - // per vp wide Traits - /// Gets the index of the current virtual processor. - fn get_current_vp(&self) -> TmkResult; - /// Gets the current VTL of the virtual processor. - fn get_current_vtl(&self) -> TmkResult; - /// Switches the current virtual processor to a higher VTL. - fn switch_to_high_vtl(&mut self); - /// Switches the current virtual processor to a lower VTL. - fn switch_to_low_vtl(&mut self); - /// Gets the value of a specific register for the current virtual processor. - fn get_register(&mut self, reg: u32) -> TmkResult; - -} - pub struct VpExecutor { vp_index: u32, vtl: Vtl, diff --git a/opentmk/opentmk/src/uefi/hypvctx.rs b/opentmk/opentmk/src/uefi/hypvctx.rs index c72dbd0183..8ed85a2a3d 100644 --- a/opentmk/opentmk/src/uefi/hypvctx.rs +++ b/opentmk/opentmk/src/uefi/hypvctx.rs @@ -1,13 +1,16 @@ +use crate::uefi::alloc::ALLOCATOR; use crate::{ - context::{TestCtxTrait, VpExecutor, VtlPlatformTrait}, + context::{ + InterruptPlatformTrait, MsrPlatformTrait, SecureInterceptPlatformTrait, + VirtualProcessorPlatformTrait, VpExecutor, VtlPlatformTrait, + }, hypercall::HvCall, tmkdefs::{TmkError, TmkErrorType, TmkResult}, }; -use crate::uefi::alloc::ALLOCATOR; -use alloc::collections::{btree_map::BTreeMap, btree_set::BTreeSet}; -use alloc::collections::linked_list::LinkedList; use alloc::boxed::Box; +use alloc::collections::linked_list::LinkedList; +use alloc::collections::{btree_map::BTreeMap, btree_set::BTreeSet}; use core::alloc::{GlobalAlloc, Layout}; use core::arch::asm; use core::ops::Range; @@ -19,8 +22,7 @@ use sync_nostd::Mutex; const ALIGNMENT: usize = 4096; -type ComandTable = - BTreeMap, Vtl)>>; +type ComandTable = BTreeMap, Vtl)>>; static mut CMD: Mutex = Mutex::new(BTreeMap::new()); #[allow(static_mut_refs)] @@ -29,13 +31,13 @@ fn cmdt() -> &'static Mutex { } fn register_command_queue(vp_index: u32) { - log::debug!("registering command queue for vp: {}", vp_index); - if cmdt().lock().get(&vp_index).is_none() { - cmdt().lock().insert(vp_index, LinkedList::new()); - log::debug!("registered command queue for vp: {}", vp_index); - } else { - log::debug!("command queue already registered for vp: {}", vp_index); - } + log::debug!("registering command queue for vp: {}", vp_index); + if cmdt().lock().get(&vp_index).is_none() { + cmdt().lock().insert(vp_index, LinkedList::new()); + log::debug!("registered command queue for vp: {}", vp_index); + } else { + log::debug!("command queue already registered for vp: {}", vp_index); + } } pub struct HvTestCtx { @@ -51,87 +53,144 @@ impl Drop for HvTestCtx { } } -/// Implementation of the `TestCtxTrait` for the `HvTestCtx` structure, providing -/// various methods to manage and interact with virtual processors (VPs) and -/// Virtual Trust Levels (VTLs) in a hypervisor context. -/// -/// # Methods -/// -/// - `start_on_vp(&mut self, cmd: VpExecutor)`: -/// Starts a virtual processor (VP) on a specified VTL. Handles enabling VTLs, -/// switching between high and low VTLs, and managing VP execution contexts. -/// -/// - `queue_command_vp(&mut self, cmd: VpExecutor)`: -/// Queues a command for a specific VP and VTL. -/// -/// - `switch_to_high_vtl(&mut self)`: -/// Switches the current execution context to a high VTL. -/// -/// - `switch_to_low_vtl(&mut self)`: -/// Switches the current execution context to a low VTL. -/// -/// - `setup_partition_vtl(&mut self, vtl: Vtl)`: -/// Configures the partition to enable a specified VTL. -/// -/// - `setup_interrupt_handler(&mut self)`: -/// Sets up the interrupt handler for the architecture. -/// -/// - `setup_vtl_protection(&mut self)`: -/// Enables VTL protection for the current partition. -/// -/// - `setup_secure_intercept(&mut self, interrupt_idx: u8)`: -/// Configures secure intercept for a specified interrupt index, including -/// setting up the SIMP and SINT0 registers. -/// -/// - `apply_vtl_protection_for_memory(&mut self, range: Range, vtl: Vtl)`: -/// Applies VTL protections to a specified memory range. -/// -/// - `write_msr(&mut self, msr: u32, value: u64)`: -/// Writes a value to a specified Model-Specific Register (MSR). -/// -/// - `read_msr(&mut self, msr: u32) -> u64`: -/// Reads the value of a specified Model-Specific Register (MSR). -/// -/// - `start_running_vp_with_default_context(&mut self, cmd: VpExecutor)`: -/// Starts a VP with the default execution context. -/// -/// - `set_default_ctx_to_vp(&mut self, vp_index: u32, vtl: Vtl)`: -/// Sets the default execution context for a specified VP and VTL. -/// -/// - `enable_vp_vtl_with_default_context(&mut self, vp_index: u32, vtl: Vtl)`: -/// Enables a VTL for a specified VP using the default execution context. -/// -/// - `set_interupt_idx(&mut self, interrupt_idx: u8, handler: fn())`: -/// Sets an interrupt handler for a specified interrupt index. (x86_64 only) -/// -/// - `get_vp_count(&self) -> u32`: -/// Retrieves the number of virtual processors available on the system. -/// -/// - `get_register(&mut self, reg: u32) -> u128`: -/// Retrieves the value of a specified register. Supports both x86_64 and -/// aarch64 architectures. -/// -/// - `get_current_vp(&self) -> u32`: -/// Returns the index of the current virtual processor. -/// -/// - `get_current_vtl(&self) -> Vtl`: -/// Returns the current Virtual Trust Level (VTL). -impl TestCtxTrait for HvTestCtx { + +impl SecureInterceptPlatformTrait for HvTestCtx { + fn setup_secure_intercept(&mut self, interrupt_idx: u8) -> TmkResult<()> { + let layout = Layout::from_size_align(4096, ALIGNMENT) + .or_else(|_| Err(TmkError(TmkErrorType::AllocationFailed)))?; + + let ptr = unsafe { ALLOCATOR.alloc(layout) }; + let gpn = (ptr as u64) >> 12; + let reg = (gpn << 12) | 0x1; + + unsafe { write_msr(hvdef::HV_X64_MSR_SIMP, reg.into()) }; + log::info!("Successfuly set the SIMP register."); + + let reg = unsafe { read_msr(hvdef::HV_X64_MSR_SINT0) }; + let mut reg: hvdef::HvSynicSint = reg.into(); + reg.set_vector(interrupt_idx); + reg.set_masked(false); + reg.set_auto_eoi(true); + + self.write_msr(hvdef::HV_X64_MSR_SINT0, reg.into())?; + log::info!("Successfuly set the SINT0 register."); + Ok(()) + } +} + + + +impl InterruptPlatformTrait for HvTestCtx { + fn set_interrupt_idx(&mut self, interrupt_idx: u8, handler: fn()) -> TmkResult<()> { + #[cfg(target_arch = "x86_64")] + { + crate::arch::interrupt::set_handler(interrupt_idx, handler); + Ok(()) + } + + #[cfg(not(target_arch = "x86_64"))] + { + Err(TmkError(TmkErrorType::NotImplemented)) + } + } + + fn setup_interrupt_handler(&mut self) -> TmkResult<()> { + crate::arch::interrupt::init(); + Ok(()) + } +} + + + +impl MsrPlatformTrait for HvTestCtx { + fn read_msr(&mut self, msr: u32) -> TmkResult { + let r = unsafe { read_msr(msr) }; + Ok(r) + } + + fn write_msr(&mut self, msr: u32, value: u64) -> TmkResult<()> { + unsafe { write_msr(msr, value) }; + Ok(()) + } +} + + + +impl VirtualProcessorPlatformTrait for HvTestCtx { + fn get_register(&mut self, reg: u32) -> TmkResult { + #[cfg(target_arch = "x86_64")] + { + use hvdef::HvX64RegisterName; + let reg = HvX64RegisterName(reg); + let val = self.hvcall.get_register(reg.into(), None)?.as_u128(); + Ok(val) + } + + #[cfg(target_arch = "aarch64")] + { + use hvdef::HvAarch64RegisterName; + let reg = HvAarch64RegisterName(reg); + let val = self.hvcall.get_register(reg.into(), None)?.as_u128(); + Ok(val) + } + + #[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))] + { + Err(TmkError(TmkErrorType::NotImplemented)) + } + } + + fn get_vp_count(&self) -> TmkResult { + #[cfg(target_arch = "x86_64")] + { + let mut result: u32; + unsafe { + asm!( + "push rbx", + "cpuid", + "mov {result:r}, rbx", + "pop rbx", + in("eax") 1u32, + out("ecx") _, + out("edx") _, + result = out(reg) result, + options(nomem, nostack) + ); + } + Ok((result >> 16) & 0xFF) + } + + #[cfg(not(target_arch = "x86_64"))] + { + Err(TmkError(TmkErrorType::NotImplemented)) + } + } + + fn queue_command_vp(&mut self, cmd: VpExecutor) -> TmkResult<()> { + let (vp_index, vtl, cmd) = cmd.get(); + let cmd = cmd.ok_or_else(|| TmkError(TmkErrorType::QueueCommandFailed))?; + cmdt() + .lock() + .get_mut(&vp_index) + .unwrap() + .push_back((cmd, vtl)); + Ok(()) + } + fn start_on_vp(&mut self, cmd: VpExecutor) -> TmkResult<()> { let (vp_index, vtl, cmd) = cmd.get(); let cmd = cmd.ok_or_else(|| TmkError(TmkErrorType::InvalidParameter))?; if vtl >= Vtl::Vtl2 { - panic!("error: can't run on vtl2"); + return Err(TmkError(TmkErrorType::InvalidParameter)); } + let is_vp_running = self.vp_runing.get(&vp_index); if let Some(_running_vtl) = is_vp_running { log::debug!("both vtl0 and vtl1 are running for VP: {:?}", vp_index); } else { if vp_index == 0 { - let vp_context = self - .get_default_context()?; - self.hvcall - .enable_vp_vtl(0, Vtl::Vtl1, Some(vp_context))?; + let vp_context = self.get_default_context()?; + self.hvcall.enable_vp_vtl(0, Vtl::Vtl1, Some(vp_context))?; cmdt().lock().get_mut(&vp_index).unwrap().push_back(( Box::new(move |ctx| { @@ -142,27 +201,43 @@ impl TestCtxTrait for HvTestCtx { self.switch_to_high_vtl(); self.vp_runing.insert(vp_index); } else { + let (tx, rx) = sync_nostd::Channel::>::new().split(); cmdt().lock().get_mut(&self.my_vp_idx).unwrap().push_back(( Box::new(move |ctx| { - _ = ctx.enable_vp_vtl_with_default_context(vp_index, Vtl::Vtl1); - _ = ctx.start_running_vp_with_default_context(VpExecutor::new( + let r = ctx.enable_vp_vtl_with_default_context(vp_index, Vtl::Vtl1); + if r.is_err() { + let _ = tx.send(r); + return; + } + let r = ctx.start_running_vp_with_default_context(VpExecutor::new( vp_index, Vtl::Vtl0, )); + if r.is_err() { + let _ = tx.send(r); + return; + } + let _ = tx.send(Ok(())); ctx.switch_to_low_vtl(); }), Vtl::Vtl1, )); - self.switch_to_high_vtl(); + log::debug!("VP{} waiting for start confirmation for vp from VTL1: {}", self.my_vp_idx, vp_index); + let rx = rx.recv(); + if let Ok(r) = rx { + r?; + } self.vp_runing.insert(vp_index); } } + cmdt() .lock() .get_mut(&vp_index) .unwrap() .push_back((cmd, vtl)); + if vp_index == self.my_vp_idx && self.my_vtl != vtl { if vtl == Vtl::Vtl0 { self.switch_to_low_vtl(); @@ -173,89 +248,37 @@ impl TestCtxTrait for HvTestCtx { Ok(()) } - fn queue_command_vp(&mut self, cmd: VpExecutor) -> TmkResult<()> { - let (vp_index, vtl, cmd) = cmd.get(); - let cmd = - cmd.ok_or_else(|| TmkError(TmkErrorType::QueueCommandFailed))?; - cmdt() - .lock() - .get_mut(&vp_index) - .unwrap() - .push_back((cmd, vtl)); - Ok(()) - } - - fn switch_to_high_vtl(&mut self) { - HvCall::vtl_call(); - } - - fn switch_to_low_vtl(&mut self) { - HvCall::vtl_return(); - } - - fn setup_partition_vtl(&mut self, vtl: Vtl) -> TmkResult<()> { - self.hvcall - .enable_partition_vtl(hvdef::HV_PARTITION_ID_SELF, vtl)?; - log::info!("enabled vtl protections for the partition."); - Ok(()) - } - fn setup_interrupt_handler(&mut self) -> TmkResult<()> { - crate::arch::interrupt::init(); - Ok(()) - } - - fn setup_vtl_protection(&mut self) -> TmkResult<()> { + fn start_running_vp_with_default_context( + &mut self, + cmd: VpExecutor, + ) -> TmkResult<()> { + let (vp_index, vtl, _cmd) = cmd.get(); + let vp_ctx = self.get_default_context()?; self.hvcall - .enable_vtl_protection(HvInputVtl::CURRENT_VTL)?; - - log::info!("enabled vtl protections for the partition."); + .start_virtual_processor(vp_index, vtl, Some(vp_ctx))?; Ok(()) } - fn setup_secure_intercept(&mut self, interrupt_idx: u8) -> TmkResult<()> { - let layout = Layout::from_size_align(4096, ALIGNMENT).or_else(|_| Err(TmkError(TmkErrorType::AllocationFailed)))?; - - let ptr = unsafe { ALLOCATOR.alloc(layout) }; - let gpn = (ptr as u64) >> 12; - let reg = (gpn << 12) | 0x1; - - unsafe { write_msr(hvdef::HV_X64_MSR_SIMP, reg.into()) }; - log::info!("Successfuly set the SIMP register."); - - let reg = unsafe { read_msr(hvdef::HV_X64_MSR_SINT0) }; - let mut reg: hvdef::HvSynicSint = reg.into(); - reg.set_vector(interrupt_idx); - reg.set_masked(false); - reg.set_auto_eoi(true); - - self.write_msr(hvdef::HV_X64_MSR_SINT0, reg.into())?; - log::info!("Successfuly set the SINT0 register."); - Ok(()) + fn get_current_vp(&self) -> TmkResult { + Ok(self.my_vp_idx) } +} +impl VtlPlatformTrait for HvTestCtx { fn apply_vtl_protection_for_memory(&mut self, range: Range, vtl: Vtl) -> TmkResult<()> { self.hvcall .apply_vtl_protections(MemoryRange::new(range), vtl)?; Ok(()) } - fn write_msr(&mut self, msr: u32, value: u64) -> TmkResult<()> { - unsafe { write_msr(msr, value) }; + fn enable_vp_vtl_with_default_context(&mut self, vp_index: u32, vtl: Vtl) -> TmkResult<()> { + let vp_ctx = self.get_default_context()?; + self.hvcall.enable_vp_vtl(vp_index, vtl, Some(vp_ctx))?; Ok(()) } - fn read_msr(&mut self, msr: u32) -> TmkResult { - let r = unsafe { read_msr(msr) }; - Ok(r) - } - - fn start_running_vp_with_default_context(&mut self, cmd: VpExecutor) -> TmkResult<()> { - let (vp_index, vtl, _cmd) = cmd.get(); - let vp_ctx = self - .get_default_context()?; - self.hvcall - .start_virtual_processor(vp_index, vtl, Some(vp_ctx))?; - Ok(()) + fn get_current_vtl(&self) -> TmkResult { + Ok(self.my_vtl) } fn set_default_ctx_to_vp(&mut self, vp_index: u32, vtl: Vtl) -> TmkResult<()> { @@ -264,88 +287,40 @@ impl TestCtxTrait for HvTestCtx { Vtl::Vtl1 => 1, Vtl::Vtl2 => 2, }; - let vp_context = self - .get_default_context()?; - self.hvcall - .set_vp_registers( - vp_index, - Some( - HvInputVtl::new() - .with_target_vtl_value(i) - .with_use_target_vtl(true), - ), - Some(vp_context), - )?; + let vp_context = self.get_default_context()?; + self.hvcall.set_vp_registers( + vp_index, + Some( + HvInputVtl::new() + .with_target_vtl_value(i) + .with_use_target_vtl(true), + ), + Some(vp_context), + )?; Ok(()) } - fn enable_vp_vtl_with_default_context(&mut self, vp_index: u32, vtl: Vtl) -> TmkResult<()> { - let vp_ctx = self - .get_default_context()?; + fn setup_partition_vtl(&mut self, vtl: Vtl) -> TmkResult<()> { self.hvcall - .enable_vp_vtl(vp_index, vtl, Some(vp_ctx))?; + .enable_partition_vtl(hvdef::HV_PARTITION_ID_SELF, vtl)?; + log::info!("enabled vtl protections for the partition."); Ok(()) } - #[cfg(target_arch = "x86_64")] - fn set_interrupt_idx(&mut self, interrupt_idx: u8, handler: fn()) -> TmkResult<()> { - crate::arch::interrupt::set_handler(interrupt_idx, handler); + fn setup_vtl_protection(&mut self) -> TmkResult<()> { + self.hvcall.enable_vtl_protection(HvInputVtl::CURRENT_VTL)?; + log::info!("enabled vtl protections for the partition."); Ok(()) } - #[cfg(target_arch = "x86_64")] - fn get_vp_count(&self) -> u32 { - let mut result: u32; - unsafe { - // Call CPUID with EAX=1, but work around the rbx constraint - asm!( - "push rbx", // Save rbx - "cpuid", // Execute CPUID - "mov {result:r}, rbx", // Store ebx to our result variable - "pop rbx", // Restore rbx - in("eax") 1u32, // Input: CPUID leaf 1 - out("ecx") _, // Output registers (not used) - out("edx") _, // Output registers (not used) - result = out(reg) result, // Output: result from ebx - options(nomem, nostack) - ); - } - - // Extract logical processor count from bits [23:16] - (result >> 16) & 0xFF - } - - #[cfg(target_arch = "x86_64")] - fn get_register(&mut self, reg: u32) -> TmkResult { - use hvdef::HvX64RegisterName; - - let reg = HvX64RegisterName(reg); - let val = self.hvcall - .get_register(reg.into(), None)? - .as_u128(); - Ok(val) - } - - #[cfg(target_arch = "aarch64")] - fn get_register(&mut self, reg: u32) -> TmkResult { - use hvdef::HvAarch64RegisterName; - - let reg = HvAarch64RegisterName(reg); - let val = self.hvcall - .get_register(reg.into(), None)? - .as_u128(); - Ok(val) - } - - fn get_current_vp(&self) -> TmkResult { - Ok(self.my_vp_idx) + fn switch_to_high_vtl(&mut self) { + HvCall::vtl_call(); } - fn get_current_vtl(&self) -> TmkResult { - Ok(self.my_vtl) + fn switch_to_low_vtl(&mut self) { + HvCall::vtl_return(); } } - impl HvTestCtx { pub const fn new() -> Self { HvTestCtx { @@ -356,18 +331,19 @@ impl HvTestCtx { } } - pub fn init(&mut self) { + pub fn init(&mut self) -> TmkResult<()> { self.hvcall.initialize(); - let vp_count = self.get_vp_count(); + let vp_count = self.get_vp_count()?; for i in 0..vp_count { register_command_queue(i); } self.my_vtl = self.hvcall.vtl(); + Ok(()) } fn exec_handler() { let mut ctx = HvTestCtx::new(); - ctx.init(); + ctx.init().expect("error: failed to init on a VP"); let reg = ctx .hvcall .get_register(hvdef::HvAllArchRegisterName::VpIndex.into(), None) @@ -472,13 +448,21 @@ impl From for TmkError { hvdef::HvError::NoResources => TmkErrorType::NoResources, hvdef::HvError::FeatureUnavailable => TmkErrorType::FeatureUnavailable, hvdef::HvError::PartialPacket => TmkErrorType::PartialPacket, - hvdef::HvError::ProcessorFeatureNotSupported => TmkErrorType::ProcessorFeatureNotSupported, - hvdef::HvError::ProcessorCacheLineFlushSizeIncompatible => TmkErrorType::ProcessorCacheLineFlushSizeIncompatible, + hvdef::HvError::ProcessorFeatureNotSupported => { + TmkErrorType::ProcessorFeatureNotSupported + } + hvdef::HvError::ProcessorCacheLineFlushSizeIncompatible => { + TmkErrorType::ProcessorCacheLineFlushSizeIncompatible + } hvdef::HvError::InsufficientBuffer => TmkErrorType::InsufficientBuffer, hvdef::HvError::IncompatibleProcessor => TmkErrorType::IncompatibleProcessor, hvdef::HvError::InsufficientDeviceDomains => TmkErrorType::InsufficientDeviceDomains, - hvdef::HvError::CpuidFeatureValidationError => TmkErrorType::CpuidFeatureValidationError, - hvdef::HvError::CpuidXsaveFeatureValidationError => TmkErrorType::CpuidXsaveFeatureValidationError, + hvdef::HvError::CpuidFeatureValidationError => { + TmkErrorType::CpuidFeatureValidationError + } + hvdef::HvError::CpuidXsaveFeatureValidationError => { + TmkErrorType::CpuidXsaveFeatureValidationError + } hvdef::HvError::ProcessorStartupTimeout => TmkErrorType::ProcessorStartupTimeout, hvdef::HvError::SmxEnabled => TmkErrorType::SmxEnabled, hvdef::HvError::InvalidLpIndex => TmkErrorType::InvalidLpIndex, @@ -494,7 +478,9 @@ impl From for TmkError { hvdef::HvError::InvalidCpuGroupId => TmkErrorType::InvalidCpuGroupId, hvdef::HvError::InvalidCpuGroupState => TmkErrorType::InvalidCpuGroupState, hvdef::HvError::OperationFailed => TmkErrorType::OperationFailed, - hvdef::HvError::NotAllowedWithNestedVirtActive => TmkErrorType::NotAllowedWithNestedVirtActive, + hvdef::HvError::NotAllowedWithNestedVirtActive => { + TmkErrorType::NotAllowedWithNestedVirtActive + } hvdef::HvError::InsufficientRootMemory => TmkErrorType::InsufficientRootMemory, hvdef::HvError::EventBufferAlreadyFreed => TmkErrorType::EventBufferAlreadyFreed, hvdef::HvError::Timeout => TmkErrorType::Timeout, @@ -509,7 +495,11 @@ impl From for TmkError { TmkErrorType::OperationFailed // Generic fallback } }; - log::debug!("Mapped hvdef::HvError::{:?} to TmkErrorType::{:?}", e, tmk_error_type); + log::debug!( + "Mapped hvdef::HvError::{:?} to TmkErrorType::{:?}", + e, + tmk_error_type + ); TmkError(tmk_error_type) } -} \ No newline at end of file +} diff --git a/opentmk/opentmk/src/uefi/rt.rs b/opentmk/opentmk/src/uefi/rt.rs index d8a80b4b53..46cfc431d4 100644 --- a/opentmk/opentmk/src/uefi/rt.rs +++ b/opentmk/opentmk/src/uefi/rt.rs @@ -10,8 +10,6 @@ use core::arch::asm; #[panic_handler] fn panic_handler(panic: &core::panic::PanicInfo<'_>) -> ! { log::error!("Panic at runtime: {}", panic); - unsafe { - asm!("int 8H"); - } + log::warn!("TEST_END"); loop {} } diff --git a/opentmk/opentmk/src/uefi/tests/hv_error_vp_start.rs b/opentmk/opentmk/src/uefi/tests/hv_error_vp_start.rs new file mode 100644 index 0000000000..bda77c6322 --- /dev/null +++ b/opentmk/opentmk/src/uefi/tests/hv_error_vp_start.rs @@ -0,0 +1,48 @@ +use hvdef::Vtl; +use sync_nostd::Channel; + +use crate::{context::{VirtualProcessorPlatformTrait, VpExecutor, VtlPlatformTrait}, tmk_assert, tmkdefs::TmkErrorType}; + + +pub fn exec(ctx: &mut T) +where + T: VtlPlatformTrait + + VirtualProcessorPlatformTrait, +{ + + // Skiping VTL setup for now to test the negitive case + + let vp_count = ctx.get_vp_count(); + tmk_assert!(vp_count.is_ok(), "get_vp_count should succeed"); + + let vp_count = vp_count.unwrap(); + tmk_assert!(vp_count == 8, "vp count should be 8"); + + // Testing BSP VTL1 Bringup + { + let (tx, _rx) = Channel::new().split(); + + let result = ctx.start_on_vp(VpExecutor::new(0, Vtl::Vtl1).command(move |ctx: &mut T| { + let vp = ctx.get_current_vp(); + tmk_assert!(vp.is_ok(), "vp should be valid"); + + let vp = vp.unwrap(); + log::info!("vp: {}", vp); + tmk_assert!(vp == 0, "vp should be equal to 0"); + + let vtl = ctx.get_current_vtl(); + tmk_assert!(vtl.is_ok(), "vtl should be valid"); + + let vtl = vtl.unwrap(); + log::info!("vtl: {:?}", vtl); + tmk_assert!(vtl == Vtl::Vtl1, "vtl should be Vtl1 for BSP"); + tx.send(()) + .expect("Failed to send message through the channel"); + ctx.switch_to_low_vtl(); + })); + + tmk_assert!(result.is_err(), "start_on_vp should fail"); + tmk_assert!(result.unwrap_err() == crate::tmkdefs::TmkErrorType::InvalidVtlState.into(), "start_on_vp should fail with InvalidVtlState"); + log::info!("result on start_on_vp: {:?}", result); + } +} diff --git a/opentmk/opentmk/src/uefi/tests/hv_misc.rs b/opentmk/opentmk/src/uefi/tests/hv_misc.rs index 681d8baa3b..0621155ebd 100644 --- a/opentmk/opentmk/src/uefi/tests/hv_misc.rs +++ b/opentmk/opentmk/src/uefi/tests/hv_misc.rs @@ -1,19 +1,21 @@ #![allow(warnings)] +use crate::context::{ + InterruptPlatformTrait, SecureInterceptPlatformTrait, VirtualProcessorPlatformTrait, + VtlPlatformTrait, +}; // WIP : This test is not yet complete and is not expected to pass. // // This test is to verify that the VTL protections are working as expected. // The stack values in VTL0 are changing after interrupt handling in VTL1. +use crate::tmk_assert; use crate::tmk_assert::{AssertOption, AssertResult}; use crate::tmkdefs::TmkResult; -use sync_nostd::{Channel, Receiver, Sender}; use crate::uefi::alloc::{ALLOCATOR, SIZE_1MB}; use crate::{context, uefi::hypvctx}; -use crate::{tmk_assert}; use ::alloc::boxed::Box; -use alloc::sync::Arc; use ::alloc::vec::Vec; -use context::{TestCtxTrait, VpExecutor}; -use hypvctx::HvTestCtx; +use alloc::sync::Arc; +use context::VpExecutor; use core::alloc::{GlobalAlloc, Layout}; use core::arch::asm; use core::cell::RefCell; @@ -21,16 +23,26 @@ use core::ops::Range; use core::sync::atomic::{AtomicI32, Ordering}; use hvdef::hypercall::HvInputVtl; use hvdef::{HvAllArchRegisterName, HvRegisterVsmVpStatus, HvX64RegisterName, Vtl}; +use hypvctx::HvTestCtx; +use sync_nostd::{Channel, Receiver, Sender}; use uefi::entry; use uefi::Status; static mut HEAPX: RefCell<*mut u8> = RefCell::new(0 as *mut u8); static mut CON: AtomicI32 = AtomicI32::new(0); -pub fn exec(ctx: &mut T) where T: TestCtxTrait { +pub fn exec(ctx: &mut T) +where + T: InterruptPlatformTrait + + SecureInterceptPlatformTrait + + VtlPlatformTrait + + VirtualProcessorPlatformTrait, +{ log::info!("ctx ptr: {:p}", &ctx as *const _); - let mut vp_count = ctx.get_vp_count(); + let vp_count = ctx.get_vp_count(); + tmk_assert!(vp_count.is_ok(), "get_vp_count should succeed"); + let vp_count = vp_count.unwrap(); tmk_assert!(vp_count == 8, "vp count should be 8"); ctx.setup_interrupt_handler(); @@ -39,54 +51,51 @@ pub fn exec(ctx: &mut T) where T: TestCtxTrait { ctx.setup_partition_vtl(Vtl::Vtl1); - ctx.start_on_vp( - VpExecutor::new(0, Vtl::Vtl1).command(move |ctx: &mut T|{ - log::info!("successfully started running VTL1 on vp0."); - ctx.setup_secure_intercept(0x30); - ctx.set_interrupt_idx(0x30, || { - log::info!("interrupt fired!"); + ctx.start_on_vp(VpExecutor::new(0, Vtl::Vtl1).command(move |ctx: &mut T| { + log::info!("successfully started running VTL1 on vp0."); + ctx.setup_secure_intercept(0x30); + ctx.set_interrupt_idx(0x30, || { + log::info!("interrupt fired!"); + + let mut hv_test_ctx = HvTestCtx::new(); + hv_test_ctx.init(); - let mut hv_test_ctx = HvTestCtx::new(); - hv_test_ctx.init(); + let c = hv_test_ctx.get_register(HvAllArchRegisterName::VsmVpStatus.0); + tmk_assert!(c.is_ok(), "read should succeed"); - let c = hv_test_ctx.get_register(HvAllArchRegisterName::VsmVpStatus.0); - tmk_assert!(c.is_ok(), "read should succeed"); - - let c = c.unwrap(); - let cp = HvRegisterVsmVpStatus::from_bits(c as u64); + let c = c.unwrap(); + let cp = HvRegisterVsmVpStatus::from_bits(c as u64); - log::info!("VSM VP Status: {:?}", cp); + log::info!("VSM VP Status: {:?}", cp); - log::info!("interrupt handled!"); - }); + log::info!("interrupt handled!"); + }); - let layout = - Layout::from_size_align(SIZE_1MB, 4096).expect("msg: failed to create layout"); - let ptr = unsafe { ALLOCATOR.alloc(layout) }; - log::info!("allocated some memory in the heap from vtl1"); - unsafe { - let mut z = HEAPX.borrow_mut(); - *z = ptr; - *ptr.add(10) = 0xAA; - } + let layout = Layout::from_size_align(SIZE_1MB, 4096).expect("msg: failed to create layout"); + let ptr = unsafe { ALLOCATOR.alloc(layout) }; + log::info!("allocated some memory in the heap from vtl1"); + unsafe { + let mut z = HEAPX.borrow_mut(); + *z = ptr; + *ptr.add(10) = 0xAA; + } - let size = layout.size(); - ctx.setup_vtl_protection(); + let size = layout.size(); + ctx.setup_vtl_protection(); - log::info!("enabled vtl protections for the partition."); + log::info!("enabled vtl protections for the partition."); - let range = Range { - start: ptr as u64, - end: ptr as u64 + size as u64, - }; + let range = Range { + start: ptr as u64, + end: ptr as u64 + size as u64, + }; - ctx.apply_vtl_protection_for_memory(range, Vtl::Vtl1); + ctx.apply_vtl_protection_for_memory(range, Vtl::Vtl1); - log::info!("moving to vtl0 to attempt to read the heap memory"); + log::info!("moving to vtl0 to attempt to read the heap memory"); - ctx.switch_to_low_vtl(); - }), - ); + ctx.switch_to_low_vtl(); + })); ctx.queue_command_vp(VpExecutor::new(0, Vtl::Vtl1).command(move |ctx: &mut T| { log::info!("successfully started running VTL1 on vp0."); @@ -126,15 +135,13 @@ pub fn exec(ctx: &mut T) where T: TestCtxTrait { // } log::info!("ctx ptr: {:p}", &ctx as *const _); let c = ctx.get_vp_count(); - + tmk_assert!(c.is_ok(), "get_vp_count should succeed"); + let c = c.unwrap(); tmk_assert!(c == 8, "vp count should be 8"); // rx.recv(); log::info!("we are in vtl0 now!"); log::info!("we reached the end of the test"); - loop { - - } - -} \ No newline at end of file + loop {} +} diff --git a/opentmk/opentmk/src/uefi/tests/hv_processor.rs b/opentmk/opentmk/src/uefi/tests/hv_processor.rs index 7ef48d0f1f..a7e12f89a5 100644 --- a/opentmk/opentmk/src/uefi/tests/hv_processor.rs +++ b/opentmk/opentmk/src/uefi/tests/hv_processor.rs @@ -2,41 +2,48 @@ use hvdef::Vtl; use sync_nostd::Channel; use crate::{ - tmk_assert, context::{TestCtxTrait, VpExecutor} + context::{ + VirtualProcessorPlatformTrait, + VpExecutor, VtlPlatformTrait, + }, + tmk_assert, }; -pub fn exec(ctx: &mut T) where T: TestCtxTrait { - let r = ctx.setup_interrupt_handler(); - tmk_assert!(r.is_ok(), "setup_interrupt_handler should succeed"); - +pub fn exec(ctx: &mut T) +where + T: VtlPlatformTrait + + VirtualProcessorPlatformTrait, +{ let r = ctx.setup_partition_vtl(Vtl::Vtl1); tmk_assert!(r.is_ok(), "setup_partition_vtl should succeed"); - + let vp_count = ctx.get_vp_count(); + tmk_assert!(vp_count.is_ok(), "get_vp_count should succeed"); + + let vp_count = vp_count.unwrap(); tmk_assert!(vp_count == 8, "vp count should be 8"); // Testing BSP VTL Bringup { let (tx, rx) = Channel::new().split(); - let result = ctx.start_on_vp(VpExecutor::new(0, Vtl::Vtl1).command( - move |ctx: &mut T| { - let vp = ctx.get_current_vp(); - tmk_assert!(vp.is_ok(), "vp should be valid"); - - let vp = vp.unwrap(); - log::info!("vp: {}", vp); - tmk_assert!(vp == 0, "vp should be equal to 0"); - - let vtl = ctx.get_current_vtl(); - tmk_assert!(vtl.is_ok(), "vtl should be valid"); - - let vtl = vtl.unwrap(); - log::info!("vtl: {:?}", vtl); - tmk_assert!(vtl == Vtl::Vtl1, "vtl should be Vtl1 for BSP"); - tx.send(()).expect("Failed to send message through the channel"); - ctx.switch_to_low_vtl(); - }, - )); + let result = ctx.start_on_vp(VpExecutor::new(0, Vtl::Vtl1).command(move |ctx: &mut T| { + let vp = ctx.get_current_vp(); + tmk_assert!(vp.is_ok(), "vp should be valid"); + + let vp = vp.unwrap(); + log::info!("vp: {}", vp); + tmk_assert!(vp == 0, "vp should be equal to 0"); + + let vtl = ctx.get_current_vtl(); + tmk_assert!(vtl.is_ok(), "vtl should be valid"); + + let vtl = vtl.unwrap(); + log::info!("vtl: {:?}", vtl); + tmk_assert!(vtl == Vtl::Vtl1, "vtl should be Vtl1 for BSP"); + tx.send(()) + .expect("Failed to send message through the channel"); + ctx.switch_to_low_vtl(); + })); tmk_assert!(result.is_ok(), "start_on_vp should succeed"); _ = rx.recv(); } @@ -45,11 +52,11 @@ pub fn exec(ctx: &mut T) where T: TestCtxTrait { // Testing VTL1 { let (tx, rx) = Channel::new().split(); - let result = ctx.start_on_vp(VpExecutor::new(i, Vtl::Vtl1).command( - move |ctx: &mut T| { + let result = + ctx.start_on_vp(VpExecutor::new(i, Vtl::Vtl1).command(move |ctx: &mut T| { let vp = ctx.get_current_vp(); tmk_assert!(vp.is_ok(), "vp should be valid"); - + let vp = vp.unwrap(); log::info!("vp: {}", vp); tmk_assert!(vp == i, format!("vp should be equal to {}", i)); @@ -61,8 +68,7 @@ pub fn exec(ctx: &mut T) where T: TestCtxTrait { log::info!("vtl: {:?}", vtl); tmk_assert!(vtl == Vtl::Vtl1, format!("vtl should be Vtl1 for VP {}", i)); _ = tx.send(()); - }, - )); + })); tmk_assert!(result.is_ok(), "start_on_vp should succeed"); _ = rx.recv(); } @@ -70,8 +76,8 @@ pub fn exec(ctx: &mut T) where T: TestCtxTrait { // Testing VTL0 { let (tx, rx) = Channel::new().split(); - let result = ctx.start_on_vp(VpExecutor::new(i, Vtl::Vtl0).command( - move |ctx: &mut T| { + let result = + ctx.start_on_vp(VpExecutor::new(i, Vtl::Vtl0).command(move |ctx: &mut T| { let vp = ctx.get_current_vp(); tmk_assert!(vp.is_ok(), "vp should be valid"); @@ -86,8 +92,7 @@ pub fn exec(ctx: &mut T) where T: TestCtxTrait { log::info!("vtl: {:?}", vtl); tmk_assert!(vtl == Vtl::Vtl0, format!("vtl should be Vtl0 for VP {}", i)); _ = tx.send(()); - }, - )); + })); tmk_assert!(result.is_ok(), "start_on_vp should succeed"); _ = rx.recv(); } diff --git a/opentmk/opentmk/src/uefi/tests/mod.rs b/opentmk/opentmk/src/uefi/tests/mod.rs index 665873ac3d..6ba7df3c84 100644 --- a/opentmk/opentmk/src/uefi/tests/mod.rs +++ b/opentmk/opentmk/src/uefi/tests/mod.rs @@ -2,9 +2,11 @@ use super::hypvctx::HvTestCtx; pub mod hv_processor; pub mod hv_misc; +mod hv_error_vp_start; + pub fn run_test() { let mut ctx = HvTestCtx::new(); - ctx.init(); - hv_processor::exec(&mut ctx); + ctx.init().expect("failed to init on BSP"); + hv_error_vp_start::exec(&mut ctx); } \ No newline at end of file From bb2227037cfdcd142bdaba7d08638552d150c21b Mon Sep 17 00:00:00 2001 From: Mayank Kumar Date: Thu, 22 May 2025 13:11:09 +0000 Subject: [PATCH 10/23] docs: add documentation for structs --- opentmk/opentmk/Cargo.toml | 3 +- .../arch/x86_64/interrupt_handler_register.rs | 18 ++- opentmk/opentmk/src/context.rs | 65 +++++++- opentmk/opentmk/src/hypercall.rs | 30 ++-- opentmk/opentmk/src/main.rs | 1 + opentmk/opentmk/src/tmk_assert.rs | 142 +++++++----------- opentmk/opentmk/src/tmk_logger.rs | 39 +++-- opentmk/opentmk/src/uefi/hypvctx.rs | 69 +++++++-- .../src/uefi/tests/hv_error_vp_start.rs | 3 +- opentmk/opentmk/src/uefi/tests/hv_misc.rs | 16 +- opentmk/opentmk/src/uefi/tests/mod.rs | 7 +- 11 files changed, 238 insertions(+), 155 deletions(-) diff --git a/opentmk/opentmk/Cargo.toml b/opentmk/opentmk/Cargo.toml index 27eeee0c6c..64926f8e7c 100644 --- a/opentmk/opentmk/Cargo.toml +++ b/opentmk/opentmk/Cargo.toml @@ -16,7 +16,7 @@ linked_list_allocator.workspace = true log.workspace = true memory_range.workspace = true minimal_rt.workspace = true -serde = { version = "1.0", default-features = false} +serde = { version = "1.0", default-features = false, features = ["derive"]} serde_json = { version = "1.0", default-features = false, features = ["alloc"] } uefi = { workspace = true, features = ["alloc"] } x86_64.workspace = true @@ -33,3 +33,4 @@ minimal_rt_build.workspace = true [profile.release] debug = false strip = "debuginfo" + diff --git a/opentmk/opentmk/src/arch/x86_64/interrupt_handler_register.rs b/opentmk/opentmk/src/arch/x86_64/interrupt_handler_register.rs index e6084ec4ce..a2b799b18b 100644 --- a/opentmk/opentmk/src/arch/x86_64/interrupt_handler_register.rs +++ b/opentmk/opentmk/src/arch/x86_64/interrupt_handler_register.rs @@ -2,14 +2,20 @@ use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame, PageFaultErrorCode}; use sync_nostd::Mutex; - static mut COMMON_HANDLER: fn(InterruptStackFrame, u8) = common_handler; static COMMON_HANDLER_MUTEX: Mutex<()> = Mutex::new(()); + +#[unsafe(no_mangle)] +fn abstraction_handle(stack_frame: InterruptStackFrame, interrupt: u8) { + unsafe { (COMMON_HANDLER)(stack_frame, interrupt) }; + log::debug!("Interrupt: {}", interrupt); +} + macro_rules! create_fn { ($name:ident, $i: expr) => { extern "x86-interrupt" fn $name(stack_frame: InterruptStackFrame) { - unsafe { (COMMON_HANDLER)(stack_frame, $i) }; + abstraction_handle(stack_frame, $i); } }; } @@ -17,7 +23,7 @@ macro_rules! create_fn { macro_rules! create_fn_create_with_errorcode { ($name:ident, $i: expr) => { extern "x86-interrupt" fn $name(stack_frame: InterruptStackFrame, _error_code: u64) { - unsafe { (COMMON_HANDLER)(stack_frame, $i) }; + abstraction_handle(stack_frame, $i); } }; } @@ -25,7 +31,7 @@ macro_rules! create_fn_create_with_errorcode { macro_rules! create_fn_divergent_create_with_errorcode { ($name:ident, $i: expr) => { extern "x86-interrupt" fn $name(stack_frame: InterruptStackFrame, _error_code: u64) -> ! { - unsafe { (COMMON_HANDLER)(stack_frame, $i) }; + abstraction_handle(stack_frame, $i); loop{} } }; @@ -34,7 +40,7 @@ macro_rules! create_fn_divergent_create_with_errorcode { macro_rules! create_fn_divergent_create { ($name:ident, $i: expr) => { extern "x86-interrupt" fn $name(stack_frame: InterruptStackFrame) -> ! { - unsafe { (COMMON_HANDLER)(stack_frame, $i) }; + abstraction_handle(stack_frame, $i); loop{} } }; @@ -43,7 +49,7 @@ macro_rules! create_fn_divergent_create { macro_rules! create_page_fault_fn { ($name:ident, $i: expr) => { extern "x86-interrupt" fn $name(stack_frame:InterruptStackFrame, _error_code: PageFaultErrorCode) { - unsafe { (COMMON_HANDLER)(stack_frame, $i) }; + abstraction_handle(stack_frame, $i); } }; } diff --git a/opentmk/opentmk/src/context.rs b/opentmk/opentmk/src/context.rs index 304907f221..0962b81a99 100644 --- a/opentmk/opentmk/src/context.rs +++ b/opentmk/opentmk/src/context.rs @@ -6,38 +6,91 @@ use hvdef::Vtl; use crate::tmkdefs::TmkResult; - pub trait SecureInterceptPlatformTrait { + /// Installs a secure-world intercept for the given interrupt. + /// + /// The platform must arrange that the supplied `interrupt_idx` + /// triggers a VM-exit or any other mechanism that transfers control + /// to the TMK secure handler. + /// + /// Returns `Ok(())` on success or an error wrapped in `TmkResult`. fn setup_secure_intercept(&mut self, interrupt_idx: u8) -> TmkResult<()>; } pub trait InterruptPlatformTrait { + /// Associates an interrupt vector with a handler inside the + /// non-secure world. + /// + /// * `interrupt_idx` – IDT/GIC index to program + /// * `handler` – Function that will be executed when the interrupt + /// fires. fn set_interrupt_idx(&mut self, interrupt_idx: u8, handler: fn()) -> TmkResult<()>; + + /// Finalises platform specific interrupt setup (enables the table, + /// unmasks lines, etc.). fn setup_interrupt_handler(&mut self) -> TmkResult<()>; } pub trait MsrPlatformTrait { + /// Reads the content of `msr`. + /// + /// Returns the 64-bit value currently stored in that MSR. fn read_msr(&mut self, msr: u32) -> TmkResult; + + /// Writes `value` into `msr`. fn write_msr(&mut self, msr: u32, value: u64) -> TmkResult<()>; } -pub trait VirtualProcessorPlatformTrait where T: VtlPlatformTrait { +pub trait VirtualProcessorPlatformTrait +where + T: VtlPlatformTrait, +{ + /// Returns the index of the virtual CPU currently executing this + /// code. fn get_current_vp(&self) -> TmkResult; + + /// Reads the architecture specific register identified by `reg`. fn get_register(&mut self, reg: u32) -> TmkResult; + + /// Total number of online VPs in the partition. fn get_vp_count(&self) -> TmkResult; + + /// Queues `cmd` to run later on the VP described inside the + /// `VpExecutor`. fn queue_command_vp(&mut self, cmd: VpExecutor) -> TmkResult<()>; + + /// Synchronously executes `cmd` on its target VP. fn start_on_vp(&mut self, cmd: VpExecutor) -> TmkResult<()>; + + /// Starts the target VP (if required) and executes `cmd` with a + /// platform provided default VTL context. fn start_running_vp_with_default_context(&mut self, cmd: VpExecutor) -> TmkResult<()>; } pub trait VtlPlatformTrait { + /// Applies VTL protection to the supplied physical address range. fn apply_vtl_protection_for_memory(&mut self, range: Range, vtl: Vtl) -> TmkResult<()>; - fn enable_vp_vtl_with_default_context(&mut self, vp_index: u32, vtl: Vtl) -> TmkResult<()>; + + /// Enables the given `vtl` on `vp_index` with a default context. + fn enable_vp_vtl_with_default_context(&mut self, vp_index: u32, vtl: Vtl) -> TmkResult<()>; + + /// Returns the VTL level the caller is currently executing in. fn get_current_vtl(&self) -> TmkResult; + + /// Sets the default VTL context on `vp_index`. fn set_default_ctx_to_vp(&mut self, vp_index: u32, vtl: Vtl) -> TmkResult<()>; + + /// Performs partition wide initialisation for a given `vtl`. fn setup_partition_vtl(&mut self, vtl: Vtl) -> TmkResult<()>; + + /// Platform specific global VTL preparation (stage 2 translation, + /// EPT, etc.). fn setup_vtl_protection(&mut self) -> TmkResult<()>; + + /// Switches the current hardware thread to the higher privileged VTL. fn switch_to_high_vtl(&mut self); + + /// Switches the current hardware thread back to the lower privileged VTL. fn switch_to_low_vtl(&mut self); } @@ -51,6 +104,7 @@ pub struct VpExecutor { } impl VpExecutor { + /// Creates a new executor targeting `vp_index` running in `vtl`. pub fn new(vp_index: u32, vtl: Vtl) -> Self { VpExecutor { vp_index, @@ -59,11 +113,16 @@ impl VpExecutor { } } + /// Stores a closure `cmd` that will be executed on the target VP. + /// + /// The closure receives a mutable reference to the platform-specific + /// type `T` that implements `VtlPlatformTrait`. pub fn command(mut self, cmd: impl FnOnce(&mut T) + 'static) -> Self { self.cmd = Some(Box::new(cmd)); self } + /// Extracts the tuple `(vp_index, vtl, cmd)` consuming `self`. pub fn get(mut self) -> (u32, Vtl, Option>) { let cmd = self.cmd.take(); (self.vp_index, self.vtl, cmd) diff --git a/opentmk/opentmk/src/hypercall.rs b/opentmk/opentmk/src/hypercall.rs index 9129635272..07cc09b4b2 100644 --- a/opentmk/opentmk/src/hypercall.rs +++ b/opentmk/opentmk/src/hypercall.rs @@ -6,6 +6,8 @@ #![allow(dead_code)] use arrayvec::ArrayVec; use core::mem::size_of; +use core::sync::atomic::AtomicBool; +use core::sync::atomic::Ordering; use hvdef::hypercall::EnablePartitionVtlFlags; use hvdef::hypercall::HvInputVtl; use hvdef::hypercall::InitialVpContextX64; @@ -114,11 +116,12 @@ impl HvcallPage { /// page, and the output page, so this should not be used in any /// multi-threaded capacity (which the boot shim currently is not). pub struct HvCall { - initialized: bool, input_page: HvcallPage, output_page: HvcallPage, } +static HV_PAGE_INIT_STATUS: AtomicBool = AtomicBool::new(false); + #[expect(unsafe_code)] impl HvCall { /// Hypercall to accept vtl2 pages from address start to end with VTL 2 @@ -257,8 +260,6 @@ impl HvCall { code: hvdef::HypercallCode, rep_count: Option, ) -> hvdef::hypercall::HypercallOutput { - self.init_if_needed(); - let control: hvdef::hypercall::Control = hvdef::hypercall::Control::new() .with_code(code.0) .with_rep_count(rep_count.unwrap_or_default()); @@ -501,21 +502,17 @@ impl HvCall { Ok(()) } - /// Initializes the hypercall interface if it hasn't been already. - fn init_if_needed(&mut self) { - if !self.initialized { - self.initialize(); - } - } - /// Initializes the hypercall interface. pub fn initialize(&mut self) { - assert!(!self.initialized); - + let init = HV_PAGE_INIT_STATUS.load(Ordering::SeqCst); + if init { + return; + } // TODO: revisit os id value. For now, use 1 (which is what UEFI does) let guest_os_id = hvdef::hypercall::HvGuestOsMicrosoft::new().with_os_id(1); crate::arch::hypercall::initialize(guest_os_id.into()); - self.initialized = true; + + HV_PAGE_INIT_STATUS.swap(true, Ordering::SeqCst); } /// Returns a mutable reference to the hypercall input page. @@ -526,7 +523,6 @@ impl HvCall { /// Creates a new `HvCall` instance. pub const fn new() -> Self { HvCall { - initialized: false, input_page: HvcallPage::new(), output_page: HvcallPage::new(), } @@ -707,15 +703,15 @@ impl HvCall { /// Call before jumping to kernel. pub fn uninitialize(&mut self) { - if self.initialized { + let init = HV_PAGE_INIT_STATUS.load(Ordering::SeqCst); + if init { crate::arch::hypercall::uninitialize(); - self.initialized = false; + HV_PAGE_INIT_STATUS.swap(false, Ordering::SeqCst); } } /// Returns the environment's VTL. pub fn vtl(&mut self) -> Vtl { - assert!(self.initialized); self .get_register(hvdef::HvAllArchRegisterName::VsmVpStatus.into(), None) .map_or(Vtl::Vtl0, |status| { diff --git a/opentmk/opentmk/src/main.rs b/opentmk/opentmk/src/main.rs index f8ffeb4ab8..dc331ea99d 100644 --- a/opentmk/opentmk/src/main.rs +++ b/opentmk/opentmk/src/main.rs @@ -3,6 +3,7 @@ #![no_std] #![allow(unsafe_code)] #![feature(abi_x86_interrupt)] +#![feature(naked_functions)] #![doc = include_str!("../README.md")] diff --git a/opentmk/opentmk/src/tmk_assert.rs b/opentmk/opentmk/src/tmk_assert.rs index 232ee7acb1..d4eb9f4433 100644 --- a/opentmk/opentmk/src/tmk_assert.rs +++ b/opentmk/opentmk/src/tmk_assert.rs @@ -1,7 +1,43 @@ -use core::fmt::Write; use alloc::string::{String, ToString}; +use core::fmt::Write; use serde::Serialize; -use serde_json::json; + +#[derive(Serialize)] +struct AssertJson<'a, T> +where + T: Serialize, +{ + #[serde(rename = "type")] + type_: &'a str, + level: &'a str, + message: &'a str, + line: String, + assertion_result: bool, + testname: &'a T, +} + +impl<'a, T> AssertJson<'a, T> +where + T: Serialize, +{ + fn new( + type_: &'a str, + level: &'a str, + message: &'a str, + line: String, + assertion_result: bool, + testname: &'a T, + ) -> Self { + Self { + type_, + level, + message, + line, + assertion_result, + testname, + } + } +} pub fn format_assert_json_string( s: &str, @@ -13,14 +49,9 @@ pub fn format_assert_json_string( where T: Serialize, { - let out = json!({ - "type:": "assert", - "level": "WARN", - "message": s, - "line": line, - "assertion_result": assert_result, - "testname": testname, - }); + let assert_json = AssertJson::new("assert", "WARN", s, line, assert_result, testname); + + let out = serde_json::to_string(&assert_json).expect("Failed to serialize assert JSON"); let mut out = out.to_string(); if terminate_new_line { out.push('\n'); @@ -28,88 +59,23 @@ where return out; } - pub fn write_str(s: &str) { let _ = crate::tmk_logger::LOGGER.get_writter().write_str(s); } #[macro_export] macro_rules! tmk_assert { - ($condition:expr, $message:expr) => { - { - let file = core::file!(); - let line = line!(); - let file_line = format!("{}:{}", file, line); - let expn = stringify!($condition); - let result: bool = $condition; - let js = crate::tmk_assert::format_assert_json_string( - &expn, true, file_line, result, &$message, - ); - crate::tmk_assert::write_str(&js); - if !result { - panic!("Assertion failed: {}", $message); - } + ($condition:expr, $message:expr) => {{ + let file = core::file!(); + let line = line!(); + let file_line = format!("{}:{}", file, line); + let expn = stringify!($condition); + let result: bool = $condition; + let js = + crate::tmk_assert::format_assert_json_string(&expn, true, file_line, result, &$message); + crate::tmk_assert::write_str(&js); + if !result { + panic!("Assertion failed: {}", $message); } - }; -} - -pub trait AssertResult { - fn unpack_assert(self) -> T; - fn expect_assert(self, message: &str) -> T; -} - -pub trait AssertOption { - fn expect_assert(self, message: &str) -> T; -} - -// impl AssertOption for Option { -// fn expect_assert(self, message: &str) -> T { -// match self { -// Some(value) => value, -// None => { -// let call: &core::panic::Location<'_> = core::panic::Location::caller(); -// let file_line = format!("{}:{}", call.file(), call.line()); -// let expn = type_name::>(); -// let js = format_assert_json_string(expn, true, file_line, false, &message); -// write_str(&js); -// panic!("Assertion failed: {}", message); -// } -// } -// } -// } - -// impl AssertResult for Result -// where -// E: core::fmt::Debug, -// { -// fn unpack_assert(self) -> T { -// match self { -// Ok(value) => value, -// Err(err) => { -// let call: &core::panic::Location<'_> = core::panic::Location::caller(); -// let file_line = format!("{}:{}", call.file(), call.line()); -// let expn = type_name::>(); -// let js = -// format_assert_json_string(expn, true, file_line, false, &"ResultTest"); -// write_str(&js); -// panic!("Assertion failed: {:?}", err); -// } -// } -// } -// fn expect_assert(self, message: &str) -> T { -// match self { -// Ok(value) => { -// log::info!("result is ok, condition not met for: {}", message); -// value -// } -// Err(err) => { -// let call: &core::panic::Location<'_> = core::panic::Location::caller(); -// let file_line = format!("{}:{}", call.file(), call.line()); -// let expn = type_name::>(); -// let js = format_assert_json_string(expn, true, file_line, false, &message); -// write_str(&js); -// panic!("Assertion failed: {:?}", err); -// } -// } -// } -// } + }}; +} \ No newline at end of file diff --git a/opentmk/opentmk/src/tmk_logger.rs b/opentmk/opentmk/src/tmk_logger.rs index f594f60b63..4c974103b4 100644 --- a/opentmk/opentmk/src/tmk_logger.rs +++ b/opentmk/opentmk/src/tmk_logger.rs @@ -1,11 +1,33 @@ use core::fmt::Write; -use alloc::{fmt::format, string::{String, ToString}}; +use alloc::{ + fmt::format, + string::{String, ToString}, +}; use log::SetLoggerError; -use serde_json::json; use sync_nostd::{Mutex, MutexGuard}; use crate::arch::serial::{InstrIoAccess, Serial}; +use serde::Serialize; + +#[derive(Serialize)] +struct LogEntry { + log_type: &'static str, + level: String, + message: String, + line: String, +} + +impl LogEntry { + fn new(level: log::Level, message: &String, line: &String) -> Self { + LogEntry { + log_type: "log", + level: level.as_str().to_string(), + message: message.clone(), + line: line.clone(), + } + } +} pub fn format_log_string_to_json( message: &String, @@ -13,12 +35,8 @@ pub fn format_log_string_to_json( terminate_new_line: bool, level: log::Level, ) -> String { - let out = json!({ - "type:": "log", - "level": level.as_str(), - "message": message, - "line": line, - }); + let log_entry = LogEntry::new(level, message, line); + let out = serde_json::to_string(&log_entry).unwrap(); let mut out = out.to_string(); if terminate_new_line { out.push('\n'); @@ -40,7 +58,10 @@ where } } - pub fn get_writter(&self) -> MutexGuard<'_, T> where T: Write + Send { + pub fn get_writter(&self) -> MutexGuard<'_, T> + where + T: Write + Send, + { self.writter.lock() } } diff --git a/opentmk/opentmk/src/uefi/hypvctx.rs b/opentmk/opentmk/src/uefi/hypvctx.rs index 8ed85a2a3d..10139bb25d 100644 --- a/opentmk/opentmk/src/uefi/hypvctx.rs +++ b/opentmk/opentmk/src/uefi/hypvctx.rs @@ -47,14 +47,11 @@ pub struct HvTestCtx { pub my_vtl: Vtl, } -impl Drop for HvTestCtx { - fn drop(&mut self) { - self.hvcall.uninitialize(); - } -} - - impl SecureInterceptPlatformTrait for HvTestCtx { + /// Configure the Secure Interrupt Message Page (SIMP) and the first + /// SynIC interrupt (SINT0) so that the hypervisor can vector + /// hypervisor side notifications back to the guest. + /// Returns [`TmkResult::Err`] if the allocation of the SIMP buffer fails. fn setup_secure_intercept(&mut self, interrupt_idx: u8) -> TmkResult<()> { let layout = Layout::from_size_align(4096, ALIGNMENT) .or_else(|_| Err(TmkError(TmkErrorType::AllocationFailed)))?; @@ -78,9 +75,10 @@ impl SecureInterceptPlatformTrait for HvTestCtx { } } - - impl InterruptPlatformTrait for HvTestCtx { + /// Install an interrupt handler for the supplied vector on x86-64. + /// For non-x86-64 targets the call returns + /// [`TmkErrorType::NotImplemented`]. fn set_interrupt_idx(&mut self, interrupt_idx: u8, handler: fn()) -> TmkResult<()> { #[cfg(target_arch = "x86_64")] { @@ -94,29 +92,31 @@ impl InterruptPlatformTrait for HvTestCtx { } } + /// Initialise the minimal in-guest interrupt infrastructure + /// (IDT/GIC, etc. depending on architecture). fn setup_interrupt_handler(&mut self) -> TmkResult<()> { crate::arch::interrupt::init(); Ok(()) } } - - impl MsrPlatformTrait for HvTestCtx { + /// Read an MSR directly from the CPU and return the raw value. fn read_msr(&mut self, msr: u32) -> TmkResult { let r = unsafe { read_msr(msr) }; Ok(r) } + /// Write an MSR directly on the CPU. fn write_msr(&mut self, msr: u32, value: u64) -> TmkResult<()> { unsafe { write_msr(msr, value) }; Ok(()) } } - - impl VirtualProcessorPlatformTrait for HvTestCtx { + /// Fetch the content of the specified architectural register from + /// the current VTL for the executing VP. fn get_register(&mut self, reg: u32) -> TmkResult { #[cfg(target_arch = "x86_64")] { @@ -140,6 +140,8 @@ impl VirtualProcessorPlatformTrait for HvTestCtx { } } + /// Return the number of logical processors present in the machine + /// by issuing the `cpuid` leaf 1 call on x86-64. fn get_vp_count(&self) -> TmkResult { #[cfg(target_arch = "x86_64")] { @@ -166,6 +168,9 @@ impl VirtualProcessorPlatformTrait for HvTestCtx { } } + /// Push a command onto the per-VP linked-list so it will be executed + /// by the busy-loop running in `exec_handler`. No scheduling happens + /// here – we simply enqueue. fn queue_command_vp(&mut self, cmd: VpExecutor) -> TmkResult<()> { let (vp_index, vtl, cmd) = cmd.get(); let cmd = cmd.ok_or_else(|| TmkError(TmkErrorType::QueueCommandFailed))?; @@ -177,6 +182,15 @@ impl VirtualProcessorPlatformTrait for HvTestCtx { Ok(()) } + /// Ensure the target VP is running in the requested VTL and queue + /// the command for execution. + /// – If the VP is not yet running, it is started with a default + /// context. + /// – If the command targets a different VTL than the current one, + /// control is switched via `vtl_call` / `vtl_return` so that the + /// executor loop can pick the command up. + /// in short every VP acts as an executor engine and + /// spins in `exec_handler` waiting for work. fn start_on_vp(&mut self, cmd: VpExecutor) -> TmkResult<()> { let (vp_index, vtl, cmd) = cmd.get(); let cmd = cmd.ok_or_else(|| TmkError(TmkErrorType::InvalidParameter))?; @@ -248,6 +262,8 @@ impl VirtualProcessorPlatformTrait for HvTestCtx { Ok(()) } + /// Start the given VP in the current VTL using a freshly captured + /// context and *do not* queue any additional work. fn start_running_vp_with_default_context( &mut self, cmd: VpExecutor, @@ -259,28 +275,35 @@ impl VirtualProcessorPlatformTrait for HvTestCtx { Ok(()) } + /// Return the index of the VP that is currently executing this code. fn get_current_vp(&self) -> TmkResult { Ok(self.my_vp_idx) } } impl VtlPlatformTrait for HvTestCtx { + /// Apply VTL protections to the supplied GPA range so that only the + /// provided VTL can access it. fn apply_vtl_protection_for_memory(&mut self, range: Range, vtl: Vtl) -> TmkResult<()> { self.hvcall .apply_vtl_protections(MemoryRange::new(range), vtl)?; Ok(()) } + /// Enable the specified VTL on a VP and seed it with a default + /// context captured from the current execution environment. fn enable_vp_vtl_with_default_context(&mut self, vp_index: u32, vtl: Vtl) -> TmkResult<()> { let vp_ctx = self.get_default_context()?; self.hvcall.enable_vp_vtl(vp_index, vtl, Some(vp_ctx))?; Ok(()) } + /// Return the VTL in which the current code is running. fn get_current_vtl(&self) -> TmkResult { Ok(self.my_vtl) } + /// Inject a default context into an already existing VP/VTL pair. fn set_default_ctx_to_vp(&mut self, vp_index: u32, vtl: Vtl) -> TmkResult<()> { let i: u8 = match vtl { Vtl::Vtl0 => 0, @@ -300,6 +323,7 @@ impl VtlPlatformTrait for HvTestCtx { Ok(()) } + /// Enable VTL support for the entire partition. fn setup_partition_vtl(&mut self, vtl: Vtl) -> TmkResult<()> { self.hvcall .enable_partition_vtl(hvdef::HV_PARTITION_ID_SELF, vtl)?; @@ -307,21 +331,28 @@ impl VtlPlatformTrait for HvTestCtx { Ok(()) } + /// Turn on VTL protections for the currently running VTL. fn setup_vtl_protection(&mut self) -> TmkResult<()> { self.hvcall.enable_vtl_protection(HvInputVtl::CURRENT_VTL)?; log::info!("enabled vtl protections for the partition."); Ok(()) } + /// Switch execution from the current (low) VTL to the next higher + /// one (`vtl_call`). fn switch_to_high_vtl(&mut self) { HvCall::vtl_call(); } + /// Return from a high VTL back to the low VTL (`vtl_return`). fn switch_to_low_vtl(&mut self) { HvCall::vtl_return(); } } + impl HvTestCtx { + /// Construct an *un-initialised* test context. + /// Call [`HvTestCtx::init`] before using the value. pub const fn new() -> Self { HvTestCtx { hvcall: HvCall::new(), @@ -331,6 +362,10 @@ impl HvTestCtx { } } + /// Perform the one-time initialisation sequence: + /// – initialise the hypercall page, + /// – discover the VP count and create command queues, + /// – record the current VTL. pub fn init(&mut self) -> TmkResult<()> { self.hvcall.initialize(); let vp_count = self.get_vp_count()?; @@ -341,6 +376,9 @@ impl HvTestCtx { Ok(()) } + /// Busy-loop executor that runs on every VP. + /// Extracts commands from the per-VP queue and executes them in the + /// appropriate VTL, switching VTLs when necessary. fn exec_handler() { let mut ctx = HvTestCtx::new(); ctx.init().expect("error: failed to init on a VP"); @@ -359,6 +397,7 @@ impl HvTestCtx { let mut cmdt = cmdt().lock(); let d = cmdt.get_mut(&ctx.my_vp_idx); if d.is_some() { + log::info!("vp: {} has commands to execute", ctx.my_vp_idx); let d = d.unwrap(); if !d.is_empty() { let (_c, v) = d.front().unwrap(); @@ -387,11 +426,15 @@ impl HvTestCtx { } #[cfg(target_arch = "x86_64")] + /// Capture the current VP context, patch the entry point and stack + /// so that the new VP starts in `exec_handler`. fn get_default_context(&mut self) -> Result { return self.run_fn_with_current_context(HvTestCtx::exec_handler); } #[cfg(target_arch = "x86_64")] + /// Helper to wrap an arbitrary function inside a captured VP context + /// that can later be used to start a new VP/VTL instance. fn run_fn_with_current_context(&mut self, func: fn()) -> Result { use super::alloc::SIZE_1MB; diff --git a/opentmk/opentmk/src/uefi/tests/hv_error_vp_start.rs b/opentmk/opentmk/src/uefi/tests/hv_error_vp_start.rs index bda77c6322..45aa4b2248 100644 --- a/opentmk/opentmk/src/uefi/tests/hv_error_vp_start.rs +++ b/opentmk/opentmk/src/uefi/tests/hv_error_vp_start.rs @@ -1,8 +1,7 @@ use hvdef::Vtl; use sync_nostd::Channel; -use crate::{context::{VirtualProcessorPlatformTrait, VpExecutor, VtlPlatformTrait}, tmk_assert, tmkdefs::TmkErrorType}; - +use crate::{context::{VirtualProcessorPlatformTrait, VpExecutor, VtlPlatformTrait}, tmk_assert}; pub fn exec(ctx: &mut T) where diff --git a/opentmk/opentmk/src/uefi/tests/hv_misc.rs b/opentmk/opentmk/src/uefi/tests/hv_misc.rs index 0621155ebd..a912a94595 100644 --- a/opentmk/opentmk/src/uefi/tests/hv_misc.rs +++ b/opentmk/opentmk/src/uefi/tests/hv_misc.rs @@ -8,7 +8,6 @@ use crate::context::{ // This test is to verify that the VTL protections are working as expected. // The stack values in VTL0 are changing after interrupt handling in VTL1. use crate::tmk_assert; -use crate::tmk_assert::{AssertOption, AssertResult}; use crate::tmkdefs::TmkResult; use crate::uefi::alloc::{ALLOCATOR, SIZE_1MB}; use crate::{context, uefi::hypvctx}; @@ -54,20 +53,9 @@ where ctx.start_on_vp(VpExecutor::new(0, Vtl::Vtl1).command(move |ctx: &mut T| { log::info!("successfully started running VTL1 on vp0."); ctx.setup_secure_intercept(0x30); - ctx.set_interrupt_idx(0x30, || { + ctx.set_interrupt_idx(0x30, move || { log::info!("interrupt fired!"); - let mut hv_test_ctx = HvTestCtx::new(); - hv_test_ctx.init(); - - let c = hv_test_ctx.get_register(HvAllArchRegisterName::VsmVpStatus.0); - tmk_assert!(c.is_ok(), "read should succeed"); - - let c = c.unwrap(); - let cp = HvRegisterVsmVpStatus::from_bits(c as u64); - - log::info!("VSM VP Status: {:?}", cp); - log::info!("interrupt handled!"); }); @@ -120,6 +108,8 @@ where ); } + + log::info!("after ctx ptr: {:p}", &ctx as *const _); unsafe { asm!("mov {}, rsp", out(reg) l) }; log::info!("rsp: 0x{:x}", l); diff --git a/opentmk/opentmk/src/uefi/tests/mod.rs b/opentmk/opentmk/src/uefi/tests/mod.rs index 6ba7df3c84..75fc35f851 100644 --- a/opentmk/opentmk/src/uefi/tests/mod.rs +++ b/opentmk/opentmk/src/uefi/tests/mod.rs @@ -1,12 +1,13 @@ +#![allow(dead_code)] use super::hypvctx::HvTestCtx; -pub mod hv_processor; -pub mod hv_misc; +mod hv_processor; +mod hv_misc; mod hv_error_vp_start; pub fn run_test() { let mut ctx = HvTestCtx::new(); ctx.init().expect("failed to init on BSP"); - hv_error_vp_start::exec(&mut ctx); + hv_processor::exec(&mut ctx); } \ No newline at end of file From e9fef3ad121ae65c1762da1c10f725cf24fd7ce6 Mon Sep 17 00:00:00 2001 From: Mayank Kumar Date: Thu, 3 Jul 2025 13:48:54 +0000 Subject: [PATCH 11/23] rtc+broken misc test --- Cargo.lock | 1 + opentmk/opentmk/Cargo.toml | 12 +- opentmk/opentmk/build_deploy.sh | 6 +- opentmk/opentmk/rustfmt.toml | 2 + opentmk/opentmk/src/arch/mod.rs | 2 +- opentmk/opentmk/src/arch/x86_64/hypercall.rs | 29 +- opentmk/opentmk/src/arch/x86_64/interrupt.rs | 21 +- .../arch/x86_64/interrupt_handler_register.rs | 13 +- opentmk/opentmk/src/arch/x86_64/io.rs | 27 + opentmk/opentmk/src/arch/x86_64/mod.rs | 7 +- opentmk/opentmk/src/arch/x86_64/rtc.rs | 127 ++ opentmk/opentmk/src/arch/x86_64/serial.rs | 100 +- opentmk/opentmk/src/arch/x86_64/tpm.rs | 1021 +++++++++++++++++ opentmk/opentmk/src/context.rs | 4 +- opentmk/opentmk/src/hypercall.rs | 104 +- opentmk/opentmk/src/main.rs | 13 +- .../opentmk/src/{uefi => platform}/hypvctx.rs | 82 +- opentmk/opentmk/src/platform/mod.rs | 1 + .../src/{uefi => }/tests/hv_error_vp_start.rs | 18 +- opentmk/opentmk/src/tests/hv_misc.rs | 200 ++-- opentmk/opentmk/src/tests/hv_processor.rs | 84 +- opentmk/opentmk/src/tests/mod.rs | 12 +- opentmk/opentmk/src/tests/tpm_test.rs | 12 + opentmk/opentmk/src/tmk_assert.rs | 20 +- opentmk/opentmk/src/tmk_logger.rs | 55 +- opentmk/opentmk/src/tmkdefs.rs | 6 +- opentmk/opentmk/src/uefi/init.rs | 9 +- opentmk/opentmk/src/uefi/mod.rs | 12 +- opentmk/opentmk/src/uefi/rt.rs | 2 - opentmk/opentmk/src/uefi/tests/hv_misc.rs | 137 --- .../opentmk/src/uefi/tests/hv_processor.rs | 102 -- opentmk/opentmk/src/uefi/tests/mod.rs | 13 - opentmk/sync/src/lib.rs | 3 +- .../src/tasks/guest_test/uefi/gpt_efi_disk.rs | 2 +- 34 files changed, 1629 insertions(+), 630 deletions(-) create mode 100644 opentmk/opentmk/rustfmt.toml create mode 100644 opentmk/opentmk/src/arch/x86_64/io.rs create mode 100644 opentmk/opentmk/src/arch/x86_64/rtc.rs create mode 100644 opentmk/opentmk/src/arch/x86_64/tpm.rs rename opentmk/opentmk/src/{uefi => platform}/hypvctx.rs (93%) create mode 100644 opentmk/opentmk/src/platform/mod.rs rename opentmk/opentmk/src/{uefi => }/tests/hv_error_vp_start.rs (78%) create mode 100644 opentmk/opentmk/src/tests/tpm_test.rs delete mode 100644 opentmk/opentmk/src/uefi/tests/hv_misc.rs delete mode 100644 opentmk/opentmk/src/uefi/tests/hv_processor.rs delete mode 100644 opentmk/opentmk/src/uefi/tests/mod.rs diff --git a/Cargo.lock b/Cargo.lock index af26549104..348e235055 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4785,6 +4785,7 @@ dependencies = [ name = "opentmk" version = "0.0.0" dependencies = [ + "anyhow", "arrayvec", "bitfield-struct", "cfg-if", diff --git a/opentmk/opentmk/Cargo.toml b/opentmk/opentmk/Cargo.toml index 64926f8e7c..c5570251d2 100644 --- a/opentmk/opentmk/Cargo.toml +++ b/opentmk/opentmk/Cargo.toml @@ -7,6 +7,7 @@ edition.workspace = true rust-version.workspace = true [dependencies] +anyhow = {version = "1.0", default-features = false} arrayvec.workspace = true bitfield-struct.workspace = true cfg-if.workspace = true @@ -30,7 +31,14 @@ workspace = true [build-dependencies] minimal_rt_build.workspace = true +[profile.dev] +overflow-checks = true + [profile.release] -debug = false -strip = "debuginfo" +overflow-checks = true + + +[build] +rustflags = ["-Z", "stack-protector=all"] + diff --git a/opentmk/opentmk/build_deploy.sh b/opentmk/opentmk/build_deploy.sh index 0c68e1643e..9068f2a54d 100755 --- a/opentmk/opentmk/build_deploy.sh +++ b/opentmk/opentmk/build_deploy.sh @@ -1,3 +1,3 @@ -RUST_BACKTRACE=1 cargo build -p opentmk --target x86_64-unknown-uefi -cargo xtask guest-test uefi --bootx64 ./target/x86_64-unknown-uefi/debug/opentmk.efi -qemu-img convert -f raw -O vhdx ./target/x86_64-unknown-uefi/debug/opentmk.img ~/projects/opentmk.vhdx \ No newline at end of file +RUST_BACKTRACE=1 cargo +nightly-2025-05-09 build -p opentmk --target x86_64-unknown-uefi #--target-dir ./target/x86_64-unknown-uefi/debug +cargo xtask guest-test uefi --bootx64 ~/projects-local/openvmm/target/x86_64-unknown-uefi/debug/opentmk.efi +qemu-img convert -f raw -O vhdx ~/projects-local/openvmm/target/x86_64-unknown-uefi/debug/opentmk.img ~/projects/opentmk.vhdx \ No newline at end of file diff --git a/opentmk/opentmk/rustfmt.toml b/opentmk/opentmk/rustfmt.toml new file mode 100644 index 0000000000..8b23646db0 --- /dev/null +++ b/opentmk/opentmk/rustfmt.toml @@ -0,0 +1,2 @@ +group_imports = "StdExternalCrate" +imports_granularity = "Crate" \ No newline at end of file diff --git a/opentmk/opentmk/src/arch/mod.rs b/opentmk/opentmk/src/arch/mod.rs index 4bcf7781ee..1d578ebeb1 100644 --- a/opentmk/opentmk/src/arch/mod.rs +++ b/opentmk/opentmk/src/arch/mod.rs @@ -13,4 +13,4 @@ cfg_if::cfg_if!( } else { compile_error!("target_arch is not supported"); } -); \ No newline at end of file +); diff --git a/opentmk/opentmk/src/arch/x86_64/hypercall.rs b/opentmk/opentmk/src/arch/x86_64/hypercall.rs index 1337cbe38e..b65f905b7e 100644 --- a/opentmk/opentmk/src/arch/x86_64/hypercall.rs +++ b/opentmk/opentmk/src/arch/x86_64/hypercall.rs @@ -1,12 +1,16 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +#![expect(unsafe_code)] + use core::ptr::addr_of; + use hvdef::HV_PAGE_SIZE; -use minimal_rt::arch::hypercall::HYPERCALL_PAGE; -use minimal_rt::arch::msr::read_msr; -use minimal_rt::arch::msr::write_msr; -#[expect(unsafe_code)] +use minimal_rt::arch::{ + hypercall::HYPERCALL_PAGE, + msr::{read_msr, write_msr}, +}; + /// Writes an MSR to tell the hypervisor the OS ID for the boot shim. fn report_os_id(guest_os_id: u64) { // SAFETY: Using the contract established in the Hyper-V TLFS. @@ -15,7 +19,6 @@ fn report_os_id(guest_os_id: u64) { }; } -#[expect(unsafe_code)] /// Writes an MSR to tell the hypervisor where the hypercall page is pub fn write_hypercall_msr(enable: bool) { // SAFETY: Using the contract established in the Hyper-V TLFS. @@ -25,21 +28,19 @@ pub fn write_hypercall_msr(enable: bool) { let hypercall_page_num = addr_of!(HYPERCALL_PAGE) as u64 / HV_PAGE_SIZE; - if!(!enable || !hypercall_contents.enable()) { + if !(!enable || !hypercall_contents.enable()) { return; } - let new_hv_contents: hvdef::hypercall::MsrHypercallContents = hypercall_contents.with_enable(enable).with_gpn(if enable { - hypercall_page_num - } else { - 0 - }); + let new_hv_contents: hvdef::hypercall::MsrHypercallContents = hypercall_contents + .with_enable(enable) + .with_gpn(if enable { hypercall_page_num } else { 0 }); // SAFETY: Using the contract established in the Hyper-V TLFS. unsafe { write_msr(hvdef::HV_X64_MSR_HYPERCALL, new_hv_contents.into()) }; } /// Has to be called before using hypercalls. -pub(crate) fn initialize(guest_os_id: u64) { +pub fn initialize(guest_os_id: u64) { // We are assuming we are running under a Microsoft hypervisor, so there is // no need to check any cpuid leaves. report_os_id(guest_os_id); @@ -47,7 +48,7 @@ pub(crate) fn initialize(guest_os_id: u64) { } /// Call before jumping to kernel. -pub(crate) fn uninitialize() { +pub fn uninitialize() { write_hypercall_msr(false); report_os_id(0); -} \ No newline at end of file +} diff --git a/opentmk/opentmk/src/arch/x86_64/interrupt.rs b/opentmk/opentmk/src/arch/x86_64/interrupt.rs index 1f31036de8..faf246848d 100644 --- a/opentmk/opentmk/src/arch/x86_64/interrupt.rs +++ b/opentmk/opentmk/src/arch/x86_64/interrupt.rs @@ -1,6 +1,6 @@ -use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame}; use lazy_static::lazy_static; use sync_nostd::Mutex; +use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame}; use super::interrupt_handler_register::{register_interrupt_handler, set_common_handler}; @@ -13,25 +13,32 @@ lazy_static! { }; } -static mut HANDLERS : [fn(); 256] = [no_op; 256]; +static mut HANDLERS: [fn(); 256] = [no_op; 256]; static MUTEX: Mutex<()> = Mutex::new(()); fn no_op() {} fn common_handler(_stack_frame: InterruptStackFrame, interrupt: u8) { - unsafe { HANDLERS[interrupt as usize](); } + unsafe { + HANDLERS[interrupt as usize](); + } } pub fn set_handler(interrupt: u8, handler: fn()) { let _lock = MUTEX.lock(); - unsafe { HANDLERS[interrupt as usize] = handler; } + unsafe { + HANDLERS[interrupt as usize] = handler; + } } - extern "x86-interrupt" fn handler_double_fault( stack_frame: InterruptStackFrame, _error_code: u64, ) -> ! { - log::error!("EXCEPTION:\n\tERROR_CODE: {}\n\tDOUBLE FAULT\n{:#?}", _error_code, stack_frame); + log::error!( + "EXCEPTION:\n\tERROR_CODE: {}\n\tDOUBLE FAULT\n{:#?}", + _error_code, + stack_frame + ); loop {} } @@ -40,4 +47,4 @@ pub fn init() { IDT.load(); set_common_handler(common_handler); x86_64::instructions::interrupts::enable(); -} \ No newline at end of file +} diff --git a/opentmk/opentmk/src/arch/x86_64/interrupt_handler_register.rs b/opentmk/opentmk/src/arch/x86_64/interrupt_handler_register.rs index a2b799b18b..90d3559e6d 100644 --- a/opentmk/opentmk/src/arch/x86_64/interrupt_handler_register.rs +++ b/opentmk/opentmk/src/arch/x86_64/interrupt_handler_register.rs @@ -1,11 +1,9 @@ #![allow(dead_code)] -use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame, PageFaultErrorCode}; - use sync_nostd::Mutex; +use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame, PageFaultErrorCode}; static mut COMMON_HANDLER: fn(InterruptStackFrame, u8) = common_handler; static COMMON_HANDLER_MUTEX: Mutex<()> = Mutex::new(()); - #[unsafe(no_mangle)] fn abstraction_handle(stack_frame: InterruptStackFrame, interrupt: u8) { unsafe { (COMMON_HANDLER)(stack_frame, interrupt) }; @@ -32,7 +30,7 @@ macro_rules! create_fn_divergent_create_with_errorcode { ($name:ident, $i: expr) => { extern "x86-interrupt" fn $name(stack_frame: InterruptStackFrame, _error_code: u64) -> ! { abstraction_handle(stack_frame, $i); - loop{} + loop {} } }; } @@ -41,14 +39,17 @@ macro_rules! create_fn_divergent_create { ($name:ident, $i: expr) => { extern "x86-interrupt" fn $name(stack_frame: InterruptStackFrame) -> ! { abstraction_handle(stack_frame, $i); - loop{} + loop {} } }; } macro_rules! create_page_fault_fn { ($name:ident, $i: expr) => { - extern "x86-interrupt" fn $name(stack_frame:InterruptStackFrame, _error_code: PageFaultErrorCode) { + extern "x86-interrupt" fn $name( + stack_frame: InterruptStackFrame, + _error_code: PageFaultErrorCode, + ) { abstraction_handle(stack_frame, $i); } }; diff --git a/opentmk/opentmk/src/arch/x86_64/io.rs b/opentmk/opentmk/src/arch/x86_64/io.rs new file mode 100644 index 0000000000..979c97d273 --- /dev/null +++ b/opentmk/opentmk/src/arch/x86_64/io.rs @@ -0,0 +1,27 @@ +use core::arch::asm; + +/// Write a byte to a port. +pub fn outb(port: u16, data: u8) { + // SAFETY: The caller has assured us this is safe. + unsafe { + asm! { + "out dx, al", + in("dx") port, + in("al") data, + } + } +} + +/// Read a byte from a port. +pub fn inb(port: u16) -> u8 { + let mut data; + // SAFETY: The caller has assured us this is safe. + unsafe { + asm! { + "in al, dx", + in("dx") port, + out("al") data, + } + } + data +} \ No newline at end of file diff --git a/opentmk/opentmk/src/arch/x86_64/mod.rs b/opentmk/opentmk/src/arch/x86_64/mod.rs index 81cead476e..12103e14fe 100644 --- a/opentmk/opentmk/src/arch/x86_64/mod.rs +++ b/opentmk/opentmk/src/arch/x86_64/mod.rs @@ -1,4 +1,7 @@ pub mod hypercall; -pub mod serial; pub mod interrupt; -mod interrupt_handler_register; \ No newline at end of file +mod interrupt_handler_register; +pub mod serial; +mod io; +pub mod tpm; +pub mod rtc; \ No newline at end of file diff --git a/opentmk/opentmk/src/arch/x86_64/rtc.rs b/opentmk/opentmk/src/arch/x86_64/rtc.rs new file mode 100644 index 0000000000..322ce67f0c --- /dev/null +++ b/opentmk/opentmk/src/arch/x86_64/rtc.rs @@ -0,0 +1,127 @@ +use core::panic::PanicInfo; +use core::ptr::{read_volatile, write_volatile}; +use super::io::{inb, outb}; +// CMOS/RTC I/O ports +const CMOS_ADDRESS: u16 = 0x70; +const CMOS_DATA: u16 = 0x71; + +// RTC register addresses +const RTC_SECONDS: u8 = 0x00; +const RTC_MINUTES: u8 = 0x02; +const RTC_HOURS: u8 = 0x04; +const RTC_DAY: u8 = 0x07; +const RTC_MONTH: u8 = 0x08; +const RTC_YEAR: u8 = 0x09; +const RTC_STATUS_A: u8 = 0x0A; +const RTC_STATUS_B: u8 = 0x0B; + +#[repr(C)] +#[derive(Debug, Clone, Copy)] +pub struct DateTime { + seconds: u8, + minutes: u8, + hours: u8, + day: u8, + month: u8, + year: u8, +} + +// implement display as ISO 8601 format +impl core::fmt::Display for DateTime { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> + core::fmt::Result { + write!(f, "{:02}:{:02}:{:02} {:02}-{:02}-{:04} UTC", + self.hours, self.minutes, self.seconds, + self.day, self.month, 2000 + self.year as u64) + } +} + +// convert datetime to Unix epoch +impl DateTime { + pub fn to_unix_epoch_sec(&self) -> u64 { + let mut days = self.day as u64; + days += (self.month as u64 - 1) * 30; // Approximation, not accurate for all months + days += (self.year as u64 + 2000 - 1970) * 365; // Approximation, not accounting for leap years + let hours = self.hours as u64; + let minutes = self.minutes as u64; + let seconds = self.seconds as u64; + + (days * 24 + hours) * 3600 + (minutes * 60) + seconds + } +} + +// Read from CMOS/RTC register +fn read_cmos(reg: u8) -> u8 { + outb(CMOS_ADDRESS, reg); + inb(CMOS_DATA) +} + +// Check if RTC update is in progress +fn rtc_update_in_progress() -> bool { + read_cmos(RTC_STATUS_A) & 0x80 != 0 +} + +// Convert BCD to binary if needed +fn bcd_to_binary(bcd: u8) -> u8 { + (bcd & 0x0F) + ((bcd >> 4) * 10) +} + +// Read current date and time from RTC +pub fn read_rtc() -> DateTime { + // Wait for any update to complete + while rtc_update_in_progress() {} + + let mut datetime = DateTime { + seconds: read_cmos(RTC_SECONDS), + minutes: read_cmos(RTC_MINUTES), + hours: read_cmos(RTC_HOURS), + day: read_cmos(RTC_DAY), + month: read_cmos(RTC_MONTH), + year: read_cmos(RTC_YEAR), + }; + + // Check if we need to wait for another update cycle + while rtc_update_in_progress() {} + + // Read again to ensure consistency + let seconds_check = read_cmos(RTC_SECONDS); + if seconds_check != datetime.seconds { + datetime.seconds = seconds_check; + datetime.minutes = read_cmos(RTC_MINUTES); + datetime.hours = read_cmos(RTC_HOURS); + datetime.day = read_cmos(RTC_DAY); + datetime.month = read_cmos(RTC_MONTH); + datetime.year = read_cmos(RTC_YEAR); + } + + // Check RTC format (BCD vs binary) + let status_b = read_cmos(RTC_STATUS_B); + let is_bcd = (status_b & 0x04) == 0; + + if is_bcd { + datetime.seconds = bcd_to_binary(datetime.seconds); + datetime.minutes = bcd_to_binary(datetime.minutes); + datetime.hours = bcd_to_binary(datetime.hours); + datetime.day = bcd_to_binary(datetime.day); + datetime.month = bcd_to_binary(datetime.month); + datetime.year = bcd_to_binary(datetime.year); + } + + // Handle 12-hour format if needed + if (status_b & 0x02) == 0 && (datetime.hours & 0x80) != 0 { + datetime.hours = ((datetime.hours & 0x7F) + 12) % 24; + } + + datetime +} + +pub fn delay_sec(seconds: u64) { + let start = read_rtc().to_unix_epoch_sec(); + let end = start + seconds; + loop { + let current = read_rtc().to_unix_epoch_sec(); + if current >= end { + break; + } + } +} \ No newline at end of file diff --git a/opentmk/opentmk/src/arch/x86_64/serial.rs b/opentmk/opentmk/src/arch/x86_64/serial.rs index bb16808b60..1ad452c507 100644 --- a/opentmk/opentmk/src/arch/x86_64/serial.rs +++ b/opentmk/opentmk/src/arch/x86_64/serial.rs @@ -2,47 +2,33 @@ // Licensed under the MIT License. //! Serial output for debugging. -#![allow(static_mut_refs)] -use core::arch::asm; -use core::fmt; -use sync_nostd::Mutex; -const COM4: u16 = 0x2E8; -static mut MUTEX : Mutex<()> = Mutex::new(()); +use core::{arch::asm, fmt}; +use super::io; -/// Write a byte to a port. -/// -/// # Safety -/// -/// The caller must be sure that the given port is safe to write to, and that the -/// given value is safe for it. -unsafe fn outb(port: u16, data: u8) { - // SAFETY: The caller has assured us this is safe. - unsafe { - asm! { - "out dx, al", - in("dx") port, - in("al") data, - } - } +use sync_nostd::Mutex; + +/// Serial port addresses. +/// These are the standard COM ports used in x86 systems. +#[repr(u16)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum SerialPort { + COM1, + COM2, + COM3, + COM4, } -/// Read a byte from a port. -/// -/// # Safety -/// -/// The caller must be sure that the given port is safe to read from. -unsafe fn inb(port: u16) -> u8 { - let mut data; - // SAFETY: The caller has assured us this is safe. - unsafe { - asm! { - "in al, dx", - in("dx") port, - out("al") data, +impl SerialPort { + /// Convert the SerialPort enum to its u16 representation. + pub fn value(self) -> u16 { + match self { + SerialPort::COM1 => 0x3F8, + SerialPort::COM2 => 0x2F8, + SerialPort::COM3 => 0x3E8, + SerialPort::COM4 => 0x2E8, } } - data } /// A trait to access io ports used by the serial device. @@ -67,52 +53,54 @@ pub struct InstrIoAccess; impl IoAccess for InstrIoAccess { unsafe fn inb(&self, port: u16) -> u8 { - // SAFETY: The serial port caller has specified a valid port. - unsafe { inb(port) } + io::inb(port) } unsafe fn outb(&self, port: u16, data: u8) { - // SAFETY: The serial port caller has specified a valid port and data. - unsafe { outb(port, data) } + io::outb(port, data) } } -/// A writer for the COM3 UART. +impl Default for InstrIoAccess { + fn default() -> Self { + InstrIoAccess + } +} + +/// A writer for the UART COM Ports. pub struct Serial { io: T, + serial_port: SerialPort, + mutex: Mutex<()>, } -impl Serial { +impl Serial { /// Initialize the serial port. - pub fn init(io: T) -> Self { + pub fn new(serial_port: SerialPort) -> Self { + let io = T::default(); + // SAFETY: Writing these values to the serial device is safe. unsafe { - io.outb(COM4 + 1, 0x00); // Disable all interrupts - io.outb(COM4 + 2, 0xC7); // Enable FIFO, clear them, with 14-byte threshold - io.outb(COM4 + 4, 0x0F); + io.outb(serial_port.value() + 1, 0x00); // Disable all interrupts + io.outb(serial_port.value() + 2, 0xC7); // Enable FIFO, clear them, with 14-byte threshold + io.outb(serial_port.value() + 4, 0x0F); } - Self { io } - } - - /// Create an instance without calling init. - pub const fn new(io: T) -> Self { - Self { io } + Self { io, serial_port, mutex: Mutex::new(()) } } fn write_byte(&self, b: u8) { // SAFETY: Reading and writing text to the serial device is safe. unsafe { - while self.io.inb(COM4 + 5) & 0x20 == 0 {} - self.io.outb(COM4, b); + while self.io.inb(self.serial_port.value() + 5) & 0x20 == 0 {} + self.io.outb(self.serial_port.value(), b); } } } - -impl fmt::Write for Serial { +impl fmt::Write for Serial { fn write_str(&mut self, s: &str) -> fmt::Result { - let _guard = unsafe { MUTEX.lock() }; + let _guard = self.mutex.lock(); for &b in s.as_bytes() { if b == b'\n' { self.write_byte(b'\r'); diff --git a/opentmk/opentmk/src/arch/x86_64/tpm.rs b/opentmk/opentmk/src/arch/x86_64/tpm.rs new file mode 100644 index 0000000000..1a3ba274fa --- /dev/null +++ b/opentmk/opentmk/src/arch/x86_64/tpm.rs @@ -0,0 +1,1021 @@ +//! # Bare Metal TPM Driver +//! +//! A kernel-mode TPM 2.0 driver written in Rust. +//! This module provides direct hardware access to TPM functionality without +//! requiring user-mode components or additional dependencies. +//! +//! ## Features +//! - Direct hardware access to TPM 2.0 devices +//! - Support for both MMIO and TIS (TPM Interface Specification) interfaces +//! - Safe Rust abstractions for TPM commands +//! - Minimizes unsafe code to hardware interaction boundaries +//! - No_std compatible for kernel environments +//! - Comprehensive logging via the `log` crate +//! - Uses alloc::vec::Vec for dynamic memory allocation + +#![no_std] +#![allow(dead_code)] // Remove this in production code + +extern crate alloc; + +use core::convert::TryFrom; +use core::fmt; +use core::ptr::{read_volatile, write_volatile}; +use core::sync::atomic::{AtomicBool, Ordering}; +use log::{error, warn, info, debug, trace}; +use alloc::vec::Vec; + +use crate::arch::rtc::{self, delay_sec}; + +/// TPM command codes as defined in the TPM 2.0 specification +#[repr(u32)] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum TpmCommandCode { + Startup = 0x00000144, + SelfTest = 0x00000143, + GetRandom = 0x0000017B, + GetCapability = 0x0000017A, + PCRExtend = 0x00000182, + PCRRead = 0x0000017E, + CreatePrimary = 0x00000131, + Create = 0x00000153, + Load = 0x00000157, + Sign = 0x0000015D, + VerifySignature = 0x0000015E, + // Add more command codes as needed +} + +impl fmt::Display for TpmCommandCode { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + TpmCommandCode::Startup => write!(f, "TPM_CC_Startup"), + TpmCommandCode::SelfTest => write!(f, "TPM_CC_SelfTest"), + TpmCommandCode::GetRandom => write!(f, "TPM_CC_GetRandom"), + TpmCommandCode::GetCapability => write!(f, "TPM_CC_GetCapability"), + TpmCommandCode::PCRExtend => write!(f, "TPM_CC_PCR_Extend"), + TpmCommandCode::PCRRead => write!(f, "TPM_CC_PCR_Read"), + TpmCommandCode::CreatePrimary => write!(f, "TPM_CC_CreatePrimary"), + TpmCommandCode::Create => write!(f, "TPM_CC_Create"), + TpmCommandCode::Load => write!(f, "TPM_CC_Load"), + TpmCommandCode::Sign => write!(f, "TPM_CC_Sign"), + TpmCommandCode::VerifySignature => write!(f, "TPM_CC_VerifySignature"), + } + } +} + +/// TPM interface types supported by this driver +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum TpmInterfaceType { + /// TPM Interface Specification (TIS) - Port I/O based + Tis, + /// Memory-mapped I/O interface + Mmio, + /// Command Response Buffer Interface + Crb, + /// Firmware TPM Interface + Fifo, +} + +impl fmt::Display for TpmInterfaceType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + TpmInterfaceType::Tis => write!(f, "TIS"), + TpmInterfaceType::Mmio => write!(f, "MMIO"), + TpmInterfaceType::Crb => write!(f, "CRB"), + TpmInterfaceType::Fifo => write!(f, "FIFO"), + } + } +} + +/// TPM hardware address information +pub struct TpmAddress { + interface_type: TpmInterfaceType, + base_address: usize, +} + +/// Error types for TPM operations +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum TpmError { + Timeout, + BadParameter, + CommunicationFailure, + BufferTooSmall, + UnsupportedCommand, + HardwareFailure, + AuthFailure, + TpmResponseError(u32), + NotInitialized, + AllocationFailure, +} + +impl fmt::Display for TpmError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + TpmError::Timeout => write!(f, "TPM operation timed out"), + TpmError::BadParameter => write!(f, "Invalid parameter provided to TPM"), + TpmError::CommunicationFailure => write!(f, "Failed to communicate with TPM"), + TpmError::BufferTooSmall => write!(f, "Buffer too small for TPM operation"), + TpmError::UnsupportedCommand => write!(f, "Command not supported by TPM"), + TpmError::HardwareFailure => write!(f, "TPM hardware failure"), + TpmError::AuthFailure => write!(f, "TPM authorization failure"), + TpmError::TpmResponseError(code) => write!(f, "TPM response error: 0x{:08X}", code), + TpmError::NotInitialized => write!(f, "TPM driver not initialized"), + TpmError::AllocationFailure => write!(f, "Memory allocation failure"), + } + } +} + +/// TPM response structure for commands +#[repr(C, packed)] +pub struct TpmResponse { + pub tag: u16, + pub response_size: u32, + pub response_code: u32, + // Followed by command-specific response data +} + +/// Status of the TPM device +pub struct TpmStatus { + pub initialized: bool, + pub active: bool, + pub version_major: u8, + pub version_minor: u8, +} + +/// Memory buffer for TPM operations +pub struct TpmBuffer { + data: [u8; Self::MAX_SIZE], + len: usize, +} + +impl TpmBuffer { + const MAX_SIZE: usize = 4096; + + pub fn new() -> Self { + debug!("Creating new TpmBuffer with capacity {}", Self::MAX_SIZE); + Self { + data: [0; Self::MAX_SIZE], + len: 0, + } + } + + pub fn clear(&mut self) { + trace!("Clearing TpmBuffer (previous length: {})", self.len); + self.len = 0; + } + + pub fn as_slice(&self) -> &[u8] { + trace!("Accessing TpmBuffer as slice, length: {}", self.len); + &self.data[..self.len] + } + + pub fn as_mut_slice(&mut self) -> &mut [u8] { + trace!("Accessing TpmBuffer as mutable slice, length: {}", self.len); + &mut self.data[..self.len] + } + + pub fn write(&mut self, bytes: &[u8]) -> Result { + if self.len + bytes.len() > Self::MAX_SIZE { + error!("TpmBuffer write failed: buffer too small (current: {}, append: {}, max: {})", + self.len, bytes.len(), Self::MAX_SIZE); + return Err(TpmError::BufferTooSmall); + } + + trace!("Writing {} bytes to TpmBuffer at offset {}", bytes.len(), self.len); + self.data[self.len..self.len + bytes.len()].copy_from_slice(bytes); + self.len += bytes.len(); + + Ok(bytes.len()) + } + + pub fn read(&mut self, bytes: &mut [u8]) -> Result { + let to_read = core::cmp::min(bytes.len(), self.len); + trace!("Reading {} bytes from TpmBuffer (requested: {}, available: {})", + to_read, bytes.len(), self.len); + + bytes[..to_read].copy_from_slice(&self.data[..to_read]); + + // Shift remaining data to beginning of buffer + for i in 0..(self.len - to_read) { + self.data[i] = self.data[i + to_read]; + } + self.len -= to_read; + + Ok(to_read) + } + + pub fn len(&self) -> usize { + self.len + } + + pub fn is_empty(&self) -> bool { + self.len == 0 + } +} + +/// TIS (TPM Interface Specification) specific constants and register offsets +mod tis { + // TIS register space offsets + pub const ACCESS: usize = 0x0; + pub const STS: usize = 0x18; + pub const DATA_FIFO: usize = 0x24; + pub const INTERFACE_ID: usize = 0x30; + pub const INT_ENABLE: usize = 0x08; + pub const INT_VECTOR: usize = 0x0C; + pub const INT_STATUS: usize = 0x10; + + // TIS access register bits + pub const ACCESS_VALID: u8 = 0x80; + pub const ACCESS_ACTIVE_LOCALITY: u8 = 0x20; + pub const ACCESS_REQUEST_USE: u8 = 0x02; + pub const ACCESS_SEIZE: u8 = 0x04; + + // TIS status register bits + pub const STS_VALID: u8 = 0x80; + pub const STS_COMMAND_READY: u8 = 0x40; + pub const STS_DATA_AVAILABLE: u8 = 0x10; + pub const STS_EXPECT: u8 = 0x08; + pub const STS_GO: u8 = 0x20; + pub const STS_RESPONSE_RETRY: u8 = 0x02; +} + +/// CRB (Command Response Buffer) specific constants and register offsets +mod crb { + pub const CONTROL_AREA_REQUEST: usize = 0x40; + pub const CONTROL_AREA_STATUS: usize = 0x44; + pub const CONTROL_CANCEL: usize = 0x18; + pub const CONTROL_START: usize = 0x1C; + pub const COMMAND_BUFFER: usize = 0x80; + pub const RESPONSE_BUFFER: usize = 0x80; + + // CRB status register bits + pub const CRB_STATUS_IDLE: u32 = 0x00000001; + pub const CRB_STATUS_READY: u32 = 0x00000002; +} + +/// The main TPM driver structure +pub struct TpmDriver { + address: TpmAddress, + initialized: AtomicBool, + current_locality: u8, +} + +impl TpmDriver { + /// Create a new TPM driver instance with the specified interface + pub fn new(interface_type: TpmInterfaceType, base_address: usize) -> Self { + info!("Creating new TPM driver with {} interface at base address 0x{:X}", + interface_type, base_address); + + Self { + address: TpmAddress { + interface_type, + base_address, + }, + initialized: AtomicBool::new(false), + current_locality: 1, + } + } + + /// Initialize the TPM driver and hardware + pub fn initialize(&self) -> Result<(), TpmError> { + // Skip initialization if already done + if self.initialized.load(Ordering::SeqCst) { + debug!("TPM driver already initialized, skipping initialization"); + return Ok(()); + } + + info!("Initializing TPM driver with {} interface", self.address.interface_type); + + match self.address.interface_type { + TpmInterfaceType::Tis => { + debug!("Initializing TPM using TIS interface"); + self.initialize_tis()? + }, + TpmInterfaceType::Mmio => { + debug!("Initializing TPM using MMIO interface"); + self.initialize_mmio()? + }, + TpmInterfaceType::Crb => { + debug!("Initializing TPM using CRB interface"); + self.initialize_crb()? + }, + TpmInterfaceType::Fifo => { + debug!("Initializing TPM using FIFO interface"); + self.initialize_fifo()? + }, + } + + debug!("TPM interface initialized, sending startup command"); + + // Send TPM startup command + let mut cmd_buffer = TpmBuffer::new(); + self.build_startup_command(&mut cmd_buffer)?; + + debug!("Sending TPM_CC_Startup command, buffer size: {}", cmd_buffer.len()); + self.send_command(cmd_buffer.as_slice())?; + + info!("TPM driver successfully initialized"); + self.initialized.store(true, Ordering::SeqCst); + Ok(()) + } + + /// Initialize TPM using TIS interface + fn initialize_tis(&self) -> Result<(), TpmError> { + debug!("Requesting access to locality {}", self.current_locality); + + // Request access to locality 0 + self.write_tis_reg8(tis::ACCESS, tis::ACCESS_REQUEST_USE)?; + + // Wait for access grant + let mut timeout = 5; + debug!("Waiting for locality {} access grant", self.current_locality); + + while timeout > 0 { + let access = self.read_tis_reg8(tis::ACCESS)?; + trace!("TIS ACCESS register: 0x{:02X}", access); + + if (access & tis::ACCESS_ACTIVE_LOCALITY) != 0 { + debug!("Locality {} access granted", self.current_locality); + break; + } + timeout -= 1; + if timeout == 0 { + error!("Timeout waiting for locality {} access", self.current_locality); + return Err(TpmError::Timeout); + } + super::rtc::delay_sec(2); + } + + // Check if TPM is valid + let access = self.read_tis_reg8(tis::ACCESS)?; + trace!("TIS ACCESS register after grant: 0x{:02X}", access); + + if (access & tis::ACCESS_VALID) == 0 { + error!("TPM TIS interface not valid, ACCESS register: 0x{:02X}", access); + return Err(TpmError::HardwareFailure); + } + + debug!("Setting TPM to command ready state"); + // Set command ready + self.write_tis_reg8(tis::STS, tis::STS_COMMAND_READY)?; + + // Wait for command ready state + debug!("Waiting for command ready state"); + let mut timeout = 5; + while timeout > 0 { + let status = self.read_tis_reg8(tis::STS)?; + trace!("TIS STS register: 0x{:02X}", status); + + if (status & tis::STS_COMMAND_READY) != 0 { + debug!("TPM command ready state achieved"); + break; + } + timeout -= 1; + if timeout == 0 { + error!("Timeout waiting for command ready state"); + return Err(TpmError::Timeout); + } + // Small delay + delay_sec(2); + } + + info!("TPM TIS interface successfully initialized"); + Ok(()) + } + + /// Initialize TPM using MMIO interface + fn initialize_mmio(&self) -> Result<(), TpmError> { + debug!("Initializing TPM using MMIO interface (using TIS flow)"); + // This would be similar to TIS but with memory-mapped registers + // For simplicity, we'll use the TIS initialization flow + self.initialize_tis() + } + + /// Initialize TPM using CRB interface + fn initialize_crb(&self) -> Result<(), TpmError> { + debug!("Setting TPM CRB interface to idle state"); + // Set to idle state + self.write_crb_reg32(crb::CONTROL_AREA_REQUEST, 0x00)?; + + // Wait for TPM to become ready + debug!("Waiting for TPM CRB to enter idle state"); + let mut timeout = 1000; + while timeout > 0 { + let status = self.read_crb_reg32(crb::CONTROL_AREA_STATUS)?; + trace!("CRB status register: 0x{:08X}", status); + + if (status & crb::CRB_STATUS_IDLE) != 0 { + debug!("TPM CRB entered idle state"); + break; + } + timeout -= 1; + if timeout == 0 { + error!("Timeout waiting for CRB idle state"); + return Err(TpmError::Timeout); + } + + delay_sec(2); + } + + debug!("Requesting TPM CRB ready state"); + // Request command ready state + self.write_crb_reg32(crb::CONTROL_AREA_REQUEST, crb::CRB_STATUS_READY)?; + + // Wait for ready state + debug!("Waiting for TPM CRB to enter ready state"); + let mut timeout = 1000; + while timeout > 0 { + let status = self.read_crb_reg32(crb::CONTROL_AREA_STATUS)?; + trace!("CRB status register: 0x{:08X}", status); + + if (status & crb::CRB_STATUS_READY) != 0 { + debug!("TPM CRB entered ready state"); + break; + } + timeout -= 1; + if timeout == 0 { + error!("Timeout waiting for CRB ready state"); + return Err(TpmError::Timeout); + } + delay_sec(2); + } + + info!("TPM CRB interface successfully initialized"); + Ok(()) + } + + /// Initialize TPM using FIFO interface + fn initialize_fifo(&self) -> Result<(), TpmError> { + debug!("Initializing TPM using FIFO interface (using TIS flow)"); + // Similar to TIS for most FIFO implementations + self.initialize_tis() + } + + /// Build TPM startup command + fn build_startup_command(&self, buffer: &mut TpmBuffer) -> Result<(), TpmError> { + debug!("Building TPM_CC_Startup command"); + + // TPM 2.0 Startup command structure + // Tag: TPM_ST_NO_SESSIONS (0x8001) + // Command size: 12 bytes + // Command code: TPM_CC_Startup (0x00000144) + // Startup type: TPM_SU_CLEAR (0x0000) + + let startup_cmd = [ + 0x80, 0x01, // TPM_ST_NO_SESSIONS + 0x00, 0x00, 0x00, 0x0C, // Command size (12 bytes) + 0x00, 0x00, 0x01, 0x44, // TPM_CC_Startup + 0x00, 0x00 // TPM_SU_CLEAR + ]; + + trace!("Startup command bytes: {:02X?}", startup_cmd); + + buffer.write(&startup_cmd) + .map(|written| { + debug!("TPM_CC_Startup command built, size: {} bytes", written); + () + }) + .map_err(|err| { + error!("Failed to build TPM_CC_Startup command: {}", err); + TpmError::BufferTooSmall + }) + } + + /// Send a raw command to the TPM + pub fn send_command(&self, command: &[u8]) -> Result, TpmError> { + if !self.initialized.load(Ordering::SeqCst) && command[6..10] != [0x00, 0x00, 0x01, 0x44] { + // Skip check for Startup command, which is sent during initialization + error!("TPM driver not initialized"); + return Err(TpmError::NotInitialized); + } + + debug!("Sending TPM command, size: {} bytes", command.len()); + trace!("Command header: {:02X?}", &command[0..10]); + + match self.address.interface_type { + TpmInterfaceType::Tis | TpmInterfaceType::Fifo => { + debug!("Using TIS/FIFO protocol for command"); + self.send_command_tis(command) + }, + TpmInterfaceType::Mmio => { + debug!("Using MMIO protocol for command"); + self.send_command_mmio(command) + }, + TpmInterfaceType::Crb => { + debug!("Using CRB protocol for command"); + self.send_command_crb(command) + }, + } + } + + /// Send a command using TIS interface + fn send_command_tis(&self, command: &[u8]) -> Result, TpmError> { + // Check if TPM is ready to receive command + let status = self.read_tis_reg8(tis::STS)?; + trace!("TIS STS register before send: 0x{:02X}", status); + + if (status & tis::STS_COMMAND_READY) == 0 { + error!("TPM not ready to receive command, STS: 0x{:02X}", status); + // TPM not ready, abort + return Err(TpmError::CommunicationFailure); + } + + debug!("Sending {} bytes to TPM FIFO", command.len()); + // Send the command data byte by byte to FIFO + for (i, &byte) in command.iter().enumerate() { + trace!("Writing byte {} to FIFO: 0x{:02X}", i, byte); + self.write_tis_reg8(tis::DATA_FIFO, byte)?; + } + + debug!("Command sent, issuing GO to execute"); + // Signal to TPM that command is complete and can be executed + self.write_tis_reg8(tis::STS, tis::STS_GO)?; + + // Wait for data to become available + debug!("Waiting for TPM response"); + let mut timeout = 2000; // Longer timeout for command execution + while timeout > 0 { + let status = self.read_tis_reg8(tis::STS)?; + trace!("TIS STS register while waiting: 0x{:02X}", status); + + if (status & tis::STS_DATA_AVAILABLE) != 0 { + debug!("TPM response data available"); + break; + } + timeout -= 1; + if timeout == 0 { + error!("Timeout waiting for TPM response"); + return Err(TpmError::Timeout); + } + delay_sec(2); + } + + debug!("Reading TPM response header"); + // Read response header to determine size + let mut response_header = [0u8; 10]; // TPM response header: tag(2) + size(4) + code(4) + for i in 0..response_header.len() { + response_header[i] = self.read_tis_reg8(tis::DATA_FIFO)?; + trace!("Read header byte {}: 0x{:02X}", i, response_header[i]); + } + + // Parse response size from header (bytes 2-5, big-endian) + let response_size = u32::from_be_bytes([ + response_header[2], response_header[3], + response_header[4], response_header[5] + ]) as usize; + + debug!("TPM response size: {} bytes", response_size); + + // Validate response size + if response_size < 10 || response_size > 4096 { + error!("Invalid TPM response size: {}", response_size); + return Err(TpmError::CommunicationFailure); + } + + // Create buffer for complete response + let mut response = Vec::with_capacity(response_size); + + // Add header to response + for &byte in &response_header { + response.push(byte); + } + + debug!("Reading remaining {} bytes of TPM response", response_size - 10); + // Read the rest of the response + for i in 10..response_size { + let byte = self.read_tis_reg8(tis::DATA_FIFO)?; + trace!("Read response byte {}: 0x{:02X}", i, byte); + response.push(byte); + } + + // Check response code + let rc = u32::from_be_bytes([ + response_header[6], response_header[7], + response_header[8], response_header[9] + ]); + + if rc != 0 { + warn!("TPM returned error code: 0x{:08X}", rc); + return Err(TpmError::TpmResponseError(rc)); + } + + debug!("TPM response successfully received, signaling command ready"); + // Signal to TPM that we're done reading + self.write_tis_reg8(tis::STS, tis::STS_COMMAND_READY)?; + + debug!("Command completed successfully, response size: {}", response.len()); + Ok(response) + } + + /// Send a command using MMIO interface + fn send_command_mmio(&self, command: &[u8]) -> Result, TpmError> { + debug!("MMIO command: using TIS implementation"); + // For simplicity, reuse TIS implementation as it's similar + self.send_command_tis(command) + } + + /// Send a command using CRB interface + fn send_command_crb(&self, command: &[u8]) -> Result, TpmError> { + // Ensure TPM is in ready state + let status = self.read_crb_reg32(crb::CONTROL_AREA_STATUS)?; + trace!("CRB status before command: 0x{:08X}", status); + + if (status & crb::CRB_STATUS_READY) == 0 { + error!("TPM CRB not in ready state, status: 0x{:08X}", status); + return Err(TpmError::CommunicationFailure); + } + + debug!("Writing {} bytes to CRB command buffer", command.len()); + // Write command to command buffer + for (i, &byte) in command.iter().enumerate() { + trace!("Writing byte {} to command buffer: 0x{:02X}", i, byte); + self.write_mmio_u8(crb::COMMAND_BUFFER + i, byte)?; + } + + debug!("Starting CRB command execution"); + // Start command execution + self.write_crb_reg32(crb::CONTROL_START, 1)?; + + // Wait for command completion + debug!("Waiting for CRB command completion"); + let mut timeout = 2000; // Longer timeout for command execution + while timeout > 0 { + let status = self.read_crb_reg32(crb::CONTROL_AREA_STATUS)?; + trace!("CRB status while waiting: 0x{:08X}", status); + + if (status & crb::CRB_STATUS_IDLE) != 0 { + debug!("TPM CRB command completed"); + break; + } + timeout -= 1; + if timeout == 0 { + error!("Timeout waiting for CRB command completion"); + return Err(TpmError::Timeout); + } + delay_sec(2); + } + + debug!("Reading TPM CRB response header"); + // Read response header to determine size + let mut response_header = [0u8; 10]; // TPM response header: tag(2) + size(4) + code(4) + for i in 0..response_header.len() { + response_header[i] = self.read_mmio_u8(crb::RESPONSE_BUFFER + i)?; + trace!("Read header byte {}: 0x{:02X}", i, response_header[i]); + } + + // Parse response size from header (bytes 2-5, big-endian) + let response_size = u32::from_be_bytes([ + response_header[2], response_header[3], + response_header[4], response_header[5] + ]) as usize; + + debug!("TPM CRB response size: {} bytes", response_size); + + // Validate response size + if response_size < 10 || response_size > 4096 { + error!("Invalid TPM CRB response size: {}", response_size); + return Err(TpmError::CommunicationFailure); + } + + // Create buffer for complete response + let mut response = Vec::with_capacity(response_size); + + // Add header to response + for &byte in &response_header { + response.push(byte); + } + + debug!("Reading remaining {} bytes of TPM CRB response", response_size - 10); + // Read the rest of the response + for i in 10..response_size { + let byte = self.read_mmio_u8(crb::RESPONSE_BUFFER + i)?; + trace!("Read response byte {}: 0x{:02X}", i, byte); + response.push(byte); + } + + // Check response code + let rc = u32::from_be_bytes([ + response_header[6], response_header[7], + response_header[8], response_header[9] + ]); + + if rc != 0 { + warn!("TPM returned error code: 0x{:08X}", rc); + return Err(TpmError::TpmResponseError(rc)); + } + + debug!("Returning TPM CRB to ready state"); + // Return to ready state + self.write_crb_reg32(crb::CONTROL_AREA_REQUEST, crb::CRB_STATUS_READY)?; + + debug!("CRB command completed successfully, response size: {}", response.len()); + Ok(response) + } + + /// Execute a TPM command and parse the response + pub fn execute_command(&self, command_code: TpmCommandCode, params: &[u8]) -> Result, TpmError> { + info!("Executing TPM command: {}", command_code); + debug!("Command parameters size: {} bytes", params.len()); + + // Build the command buffer + let mut cmd_buffer = TpmBuffer::new(); + + // TPM command header: tag(2) + size(4) + command_code(4) + let tag: u16 = 0x8001; // TPM_ST_NO_SESSIONS + let cmd_size: u32 = 10 + params.len() as u32; // header + params + + debug!("Building command with tag 0x{:04X}, size {} bytes", tag, cmd_size); + + // Write tag (big-endian) + cmd_buffer.write(&tag.to_be_bytes())?; + + // Write command size (big-endian) + cmd_buffer.write(&cmd_size.to_be_bytes())?; + + // Write command code (big-endian) + let code: u32 = command_code as u32; + debug!("Command code: 0x{:08X}", code); + cmd_buffer.write(&code.to_be_bytes())?; + + // Write command parameters + cmd_buffer.write(params)?; + + debug!("Command buffer built, total size: {} bytes", cmd_buffer.len()); + trace!("Command buffer: {:02X?}", cmd_buffer.as_slice()); + + // Send the command to TPM + let result = self.send_command(cmd_buffer.as_slice()); + + match &result { + Ok(response) => { + debug!("Command {} completed successfully, response size: {} bytes", + command_code, response.len()); + trace!("Response header: {:02X?}", &response[0..10]); + }, + Err(err) => { + warn!("Command {} failed: {}", command_code, err); + } + } + + result + } + + /// Read random bytes from the TPM + pub fn get_random(&self, num_bytes: u16) -> Result, TpmError> { + info!("Getting {} random bytes from TPM", num_bytes); + + // TPM2_GetRandom command structure + // After the header: + // bytes requested (2 bytes) + + // Prepare parameter buffer + let params = num_bytes.to_be_bytes(); + debug!("GetRandom parameter: 0x{:04X}", num_bytes); + + // Execute the command + let response = self.execute_command(TpmCommandCode::GetRandom, ¶ms)?; + + // Parse response - skip header (10 bytes) and size field (2 bytes) + if response.len() < 13 { + error!("Invalid GetRandom response length: {}", response.len()); + return Err(TpmError::CommunicationFailure); + } + + // Extract random bytes from response (skipping header and size) + let random_size = u16::from_be_bytes([response[10], response[11]]) as usize; + + debug!("Received {} random bytes from TPM", random_size); + + let mut random_bytes = Vec::with_capacity(random_size); + + for i in 0..random_size { + if 12 + i >= response.len() { + warn!("Random data truncated, expected {} bytes, got {}", + random_size, response.len() - 12); + break; + } + random_bytes.push(response[12 + i]); + } + + info!("Successfully retrieved {} random bytes", random_bytes.len()); + trace!("Random bytes: {:02X?}", random_bytes.as_slice()); + + Ok(random_bytes) + } + + /// Extend a PCR with provided data + pub fn pcr_extend(&self, pcr_index: u32, digest: &[u8]) -> Result<(), TpmError> { + info!("Extending PCR {} with {} bytes of digest data", pcr_index, digest.len()); + + // TPM2_PCR_Extend command requires authorization, which complicates the example + // This is a simplified version without proper authorization + + if digest.len() != 32 { + error!("Invalid digest length for PCR extend: {} (expected 32 for SHA-256)", digest.len()); + return Err(TpmError::BadParameter); // Assuming SHA-256 (32 bytes) + } + + // Prepare parameter buffer for PCR extend + let mut params = Vec::new(); + + debug!("Building PCR_Extend parameters"); + + // PCR index (4 bytes) + params.extend_from_slice(&pcr_index.to_be_bytes()); + debug!("Added PCR index: {}", pcr_index); + + // Authorization section placeholder (simplified) + // In a real implementation, this would include proper authorization + let auth_size: u32 = 9; // Minimal auth size + params.extend_from_slice(&auth_size.to_be_bytes()); + debug!("Added auth section size: {}", auth_size); + + params.push(0); // TPM_RS_PW + params.extend_from_slice(&[0, 0]); // nonce size = 0 + params.push(0); // session attributes + params.extend_from_slice(&[0, 0]); // auth size = 0 + debug!("Added simplified auth section"); + + // Count of hashes (1 byte) + params.push(1); + debug!("Added hash count: 1"); + + // Hash algorithm (2 bytes) - SHA-256 = 0x000B + params.extend_from_slice(&[0x00, 0x0B]); + debug!("Added hash algorithm: SHA-256 (0x000B)"); + + // Hash data + params.extend_from_slice(digest); + + debug!("Added digest data, total parameter size: {} bytes", params.len()); + trace!("PCR_Extend parameters: {:02X?}", params.as_slice()); + + // Execute command + let _response = self.execute_command(TpmCommandCode::PCRExtend, ¶ms)?; + + info!("PCR {} successfully extended", pcr_index); + Ok(()) + } + + /// Read PCR value + pub fn pcr_read(&self, pcr_index: u32) -> Result, TpmError> { + info!("Reading PCR {} value", pcr_index); + + // Prepare parameter buffer for PCR read + let mut params = Vec::new(); + + debug!("Building PCR_Read parameters"); + + // Count of PCRs to read (4 bytes) - just 1 + params.extend_from_slice(&1u32.to_be_bytes()); + debug!("Added PCR count: 1"); + + // PCR index (4 bytes) + params.extend_from_slice(&pcr_index.to_be_bytes()); + debug!("Added PCR index: {}", pcr_index); + + // Hash algorithm (2 bytes) - SHA-256 = 0x000B + params.extend_from_slice(&[0x00, 0x0B]); + debug!("Added hash algorithm: SHA-256 (0x000B)"); + debug!("PCR_Read parameters built, total size: {} bytes", params.len()); + trace!("PCR_Read parameters: {:02X?}", params.as_slice()); + + // Execute command + let response = self.execute_command(TpmCommandCode::PCRRead, ¶ms)?; + + debug!("PCR_Read response received, size: {} bytes", response.len()); + trace!("PCR_Read response header: {:02X?}", &response[0..14]); + + // Parse response to extract PCR value + // For simplicity, assuming SHA-256 and skipping detailed parsing + if response.len() < 14 { + error!("Invalid PCR_Read response length: {}", response.len()); + return Err(TpmError::CommunicationFailure); + } + + // Extract PCR value (assuming SHA-256 of 32 bytes) + let mut pcr_value = Vec::with_capacity(32); + for i in 0..32 { + if 14 + i >= response.len() { + warn!("PCR value truncated, expected 32 bytes, got {}", response.len() - 14); + break; + } + pcr_value.push(response[14 + i]); + } + + info!("Successfully read PCR {} value, {} bytes", pcr_index, pcr_value.len()); + trace!("PCR value: {:02X?}", pcr_value.as_slice()); + + Ok(pcr_value) + } + + /// Read TIS register (8-bit) + fn read_tis_reg8(&self, reg_offset: usize) -> Result { + let addr = self.address.base_address + reg_offset; + trace!("Reading TIS register at offset 0x{:X} (address 0x{:X})", reg_offset, addr); + + // Use unsafe for direct hardware access + let value = unsafe { + read_volatile((addr as *const u8)) + }; + + trace!("TIS register 0x{:X} value: 0x{:02X}", reg_offset, value); + Ok(value) + } + + /// Write TIS register (8-bit) + fn write_tis_reg8(&self, reg_offset: usize, value: u8) -> Result<(), TpmError> { + let addr = self.address.base_address + reg_offset; + trace!("Writing TIS register at offset 0x{:X} (address 0x{:X}): 0x{:02X}", + reg_offset, addr, value); + + // Use unsafe for direct hardware access + unsafe { + write_volatile((addr as *mut u8), value); + } + Ok(()) + } + + /// Read CRB register (32-bit) + fn read_crb_reg32(&self, reg_offset: usize) -> Result { + let addr = self.address.base_address + reg_offset; + trace!("Reading CRB register at offset 0x{:X} (address 0x{:X})", reg_offset, addr); + + // Use unsafe for direct hardware access + let value = unsafe { + read_volatile((addr as *const u32)) + }; + + trace!("CRB register 0x{:X} value: 0x{:08X}", reg_offset, value); + Ok(value) + } + + /// Write CRB register (32-bit) + fn write_crb_reg32(&self, reg_offset: usize, value: u32) -> Result<(), TpmError> { + let addr = self.address.base_address + reg_offset; + trace!("Writing CRB register at offset 0x{:X} (address 0x{:X}): 0x{:08X}", + reg_offset, addr, value); + + // Use unsafe for direct hardware access + unsafe { + write_volatile((addr as *mut u32), value); + } + Ok(()) + } + + /// Read byte from MMIO + fn read_mmio_u8(&self, offset: usize) -> Result { + let addr = self.address.base_address + offset; + trace!("Reading MMIO byte at offset 0x{:X} (address 0x{:X})", offset, addr); + + // Use unsafe for direct hardware access + let value = unsafe { + read_volatile((addr as *const u8)) + }; + + trace!("MMIO byte 0x{:X} value: 0x{:02X}", offset, value); + Ok(value) + } + + /// Write byte to MMIO + fn write_mmio_u8(&self, offset: usize, value: u8) -> Result<(), TpmError> { + let addr = self.address.base_address + offset; + trace!("Writing MMIO byte at offset 0x{:X} (address 0x{:X}): 0x{:02X}", + offset, addr, value); + + // Use unsafe for direct hardware access + unsafe { + write_volatile((addr as *mut u8), value); + } + Ok(()) + } +} + +/// Example usage of the TPM driver +pub fn tpm_driver_example() -> Result<(), TpmError> { + info!("Starting TPM driver example"); + + // Create a new TPM driver with TIS interface + // Base address would typically be determined from hardware detection + info!("Creating TPM driver with TIS interface at base address 0xFED40000"); + let tpm = TpmDriver::new(TpmInterfaceType::Tis, 0xFED40000); + + // Initialize the TPM + info!("Initializing TPM driver"); + tpm.initialize()?; + + // Get 16 random bytes from TPM + info!("Requesting 16 random bytes from TPM"); + let random_bytes = tpm.get_random(16)?; + info!("Received {} random bytes from TPM", random_bytes.len()); + debug!("Random bytes: {:02X?}", random_bytes.as_slice()); + + // Read PCR 0 + info!("Reading PCR 0"); + let pcr_value = tpm.pcr_read(0)?; + info!("PCR 0 value retrieved, {} bytes", pcr_value.len()); + debug!("PCR 0 value: {:02X?}", pcr_value.as_slice()); + + info!("TPM driver example completed successfully"); + Ok(()) +} \ No newline at end of file diff --git a/opentmk/opentmk/src/context.rs b/opentmk/opentmk/src/context.rs index 0962b81a99..19e9e6228f 100644 --- a/opentmk/opentmk/src/context.rs +++ b/opentmk/opentmk/src/context.rs @@ -1,7 +1,7 @@ #![allow(dead_code)] +use alloc::boxed::Box; use core::ops::Range; -use alloc::boxed::Box; use hvdef::Vtl; use crate::tmkdefs::TmkResult; @@ -127,4 +127,4 @@ impl VpExecutor { let cmd = self.cmd.take(); (self.vp_index, self.vtl, cmd) } -} \ No newline at end of file +} diff --git a/opentmk/opentmk/src/hypercall.rs b/opentmk/opentmk/src/hypercall.rs index 07cc09b4b2..33021436b4 100644 --- a/opentmk/opentmk/src/hypercall.rs +++ b/opentmk/opentmk/src/hypercall.rs @@ -4,22 +4,19 @@ //! Hypercall infrastructure. #![allow(dead_code)] +use core::{ + mem::size_of, + sync::atomic::{AtomicU16, Ordering}, +}; + use arrayvec::ArrayVec; -use core::mem::size_of; -use core::sync::atomic::AtomicBool; -use core::sync::atomic::Ordering; -use hvdef::hypercall::EnablePartitionVtlFlags; -use hvdef::hypercall::HvInputVtl; -use hvdef::hypercall::InitialVpContextX64; -use hvdef::HvRegisterValue; -use hvdef::HvRegisterVsmPartitionConfig; -use hvdef::HvX64RegisterName; -use hvdef::Vtl; -use hvdef::HV_PAGE_SIZE; +use hvdef::{ + hypercall::{EnablePartitionVtlFlags, HvInputVtl, InitialVpContextX64}, + HvRegisterValue, HvRegisterVsmPartitionConfig, HvX64RegisterName, Vtl, HV_PAGE_SIZE, +}; use memory_range::MemoryRange; use minimal_rt::arch::hypercall::{invoke_hypercall, HYPERCALL_PAGE}; -use zerocopy::FromBytes; -use zerocopy::IntoBytes; +use zerocopy::{FromBytes, IntoBytes}; /// Page-aligned, page-sized buffer for use with hypercalls #[repr(C, align(4096))] @@ -120,7 +117,7 @@ pub struct HvCall { output_page: HvcallPage, } -static HV_PAGE_INIT_STATUS: AtomicBool = AtomicBool::new(false); +static HV_PAGE_INIT_STATUS: AtomicU16 = AtomicU16::new(0); #[expect(unsafe_code)] impl HvCall { @@ -302,14 +299,8 @@ impl HvCall { } /// Enables VTL protection for the specified VTL. - pub fn enable_vtl_protection( - &mut self, - vtl: HvInputVtl, - ) -> Result<(), hvdef::HvError> { - let hvreg = self.get_register( - HvX64RegisterName::VsmPartitionConfig.into(), - Some(vtl), - )?; + pub fn enable_vtl_protection(&mut self, vtl: HvInputVtl) -> Result<(), hvdef::HvError> { + let hvreg = self.get_register(HvX64RegisterName::VsmPartitionConfig.into(), Some(vtl))?; let mut hvreg: HvRegisterVsmPartitionConfig = HvRegisterVsmPartitionConfig::from_bits(hvreg.as_u64()); hvreg.set_enable_vtl_protection(true); @@ -377,8 +368,8 @@ impl HvCall { #[cfg(target_arch = "x86_64")] /// Hypercall to get the current VTL VP context pub fn get_current_vtl_vp_context(&mut self) -> Result { - use HvX64RegisterName; use zerocopy::FromZeros; + use HvX64RegisterName; let mut context: InitialVpContextX64 = FromZeros::new_zeroed(); context.cr0 = self .get_register(HvX64RegisterName::Cr0.into(), None)? @@ -478,7 +469,8 @@ impl HvCall { for hw_ids in hw_ids.chunks(MAX_PER_CALL) { let _ = header.write_to_prefix(self.input_page().buffer.as_mut_slice()); - let _ = hw_ids.write_to_prefix(&mut self.input_page().buffer[header.as_bytes().len()..]); + let _ = + hw_ids.write_to_prefix(&mut self.input_page().buffer[header.as_bytes().len()..]); // SAFETY: The input header and rep slice are the correct types for this hypercall. // The hypercall output is validated right after the hypercall is issued. @@ -504,15 +496,14 @@ impl HvCall { /// Initializes the hypercall interface. pub fn initialize(&mut self) { - let init = HV_PAGE_INIT_STATUS.load(Ordering::SeqCst); - if init { - return; - } // TODO: revisit os id value. For now, use 1 (which is what UEFI does) let guest_os_id = hvdef::hypercall::HvGuestOsMicrosoft::new().with_os_id(1); + // This is an idempotent operation, so we can call it multiple times. + // we proceed and initialize the hypercall interface because we don't know the current vtl + // This prohibit us to call this selectively for new VTLs crate::arch::hypercall::initialize(guest_os_id.into()); - - HV_PAGE_INIT_STATUS.swap(true, Ordering::SeqCst); + + HV_PAGE_INIT_STATUS.fetch_add(1, Ordering::SeqCst); } /// Returns a mutable reference to the hypercall input page. @@ -623,30 +614,12 @@ impl HvCall { HvX64RegisterName::Rflags.into(), vp_context.unwrap().rflags.into(), ); - write_reg( - HvX64RegisterName::Cs.into(), - vp_context.unwrap().cs.into(), - ); - write_reg( - HvX64RegisterName::Ss.into(), - vp_context.unwrap().ss.into(), - ); - write_reg( - HvX64RegisterName::Ds.into(), - vp_context.unwrap().ds.into(), - ); - write_reg( - HvX64RegisterName::Es.into(), - vp_context.unwrap().es.into(), - ); - write_reg( - HvX64RegisterName::Fs.into(), - vp_context.unwrap().fs.into(), - ); - write_reg( - HvX64RegisterName::Gs.into(), - vp_context.unwrap().gs.into(), - ); + write_reg(HvX64RegisterName::Cs.into(), vp_context.unwrap().cs.into()); + write_reg(HvX64RegisterName::Ss.into(), vp_context.unwrap().ss.into()); + write_reg(HvX64RegisterName::Ds.into(), vp_context.unwrap().ds.into()); + write_reg(HvX64RegisterName::Es.into(), vp_context.unwrap().es.into()); + write_reg(HvX64RegisterName::Fs.into(), vp_context.unwrap().fs.into()); + write_reg(HvX64RegisterName::Gs.into(), vp_context.unwrap().gs.into()); write_reg( HvX64RegisterName::Gdtr.into(), vp_context.unwrap().gdtr.into(), @@ -659,10 +632,7 @@ impl HvCall { HvX64RegisterName::Ldtr.into(), vp_context.unwrap().ldtr.into(), ); - write_reg( - HvX64RegisterName::Tr.into(), - vp_context.unwrap().tr.into(), - ); + write_reg(HvX64RegisterName::Tr.into(), vp_context.unwrap().tr.into()); write_reg( HvX64RegisterName::Efer.into(), vp_context.unwrap().efer.into(), @@ -703,17 +673,12 @@ impl HvCall { /// Call before jumping to kernel. pub fn uninitialize(&mut self) { - let init = HV_PAGE_INIT_STATUS.load(Ordering::SeqCst); - if init { - crate::arch::hypercall::uninitialize(); - HV_PAGE_INIT_STATUS.swap(false, Ordering::SeqCst); - } + crate::arch::hypercall::uninitialize(); } /// Returns the environment's VTL. pub fn vtl(&mut self) -> Vtl { - self - .get_register(hvdef::HvAllArchRegisterName::VsmVpStatus.into(), None) + self.get_register(hvdef::HvAllArchRegisterName::VsmVpStatus.into(), None) .map_or(Vtl::Vtl0, |status| { hvdef::HvRegisterVsmVpStatus::from(status.as_u64()) .active_vtl() @@ -748,3 +713,12 @@ pub type HwId = u32; /// MPIDR on ARM64. #[cfg(target_arch = "aarch64")] pub type HwId = u64; + +impl Drop for HvCall { + fn drop(&mut self) { + let seq = HV_PAGE_INIT_STATUS.fetch_sub(1, Ordering::SeqCst); + if seq == 0 { + self.uninitialize(); + } + } +} diff --git a/opentmk/opentmk/src/main.rs b/opentmk/opentmk/src/main.rs index dc331ea99d..3758aae71c 100644 --- a/opentmk/opentmk/src/main.rs +++ b/opentmk/opentmk/src/main.rs @@ -3,10 +3,7 @@ #![no_std] #![allow(unsafe_code)] #![feature(abi_x86_interrupt)] -#![feature(naked_functions)] - #![doc = include_str!("../README.md")] - #![cfg_attr(all(not(test), target_os = "uefi"), no_main)] #![cfg_attr(all(not(test), target_os = "uefi"), no_std)] @@ -17,10 +14,12 @@ fn main() {} #[macro_use] extern crate alloc; -mod uefi; pub mod arch; +pub mod context; +pub mod hypercall; +pub mod platform; +pub mod tests; pub mod tmk_assert; pub mod tmk_logger; -pub mod hypercall; -pub mod context; -pub mod tmkdefs; \ No newline at end of file +pub mod tmkdefs; +mod uefi; diff --git a/opentmk/opentmk/src/uefi/hypvctx.rs b/opentmk/opentmk/src/platform/hypvctx.rs similarity index 93% rename from opentmk/opentmk/src/uefi/hypvctx.rs rename to opentmk/opentmk/src/platform/hypvctx.rs index 10139bb25d..3d09751392 100644 --- a/opentmk/opentmk/src/uefi/hypvctx.rs +++ b/opentmk/opentmk/src/platform/hypvctx.rs @@ -1,4 +1,18 @@ -use crate::uefi::alloc::ALLOCATOR; +use alloc::{ + alloc::alloc, + boxed::Box, + collections::{btree_map::BTreeMap, btree_set::BTreeSet, linked_list::LinkedList}, +}; +use core::{alloc::Layout, arch::asm, ops::Range}; + +use hvdef::{ + hypercall::{HvInputVtl, InitialVpContextX64}, + Vtl, +}; +use memory_range::MemoryRange; +use minimal_rt::arch::msr::{read_msr, write_msr}; +use sync_nostd::Mutex; + use crate::{ context::{ InterruptPlatformTrait, MsrPlatformTrait, SecureInterceptPlatformTrait, @@ -8,18 +22,6 @@ use crate::{ tmkdefs::{TmkError, TmkErrorType, TmkResult}, }; -use alloc::boxed::Box; -use alloc::collections::linked_list::LinkedList; -use alloc::collections::{btree_map::BTreeMap, btree_set::BTreeSet}; -use core::alloc::{GlobalAlloc, Layout}; -use core::arch::asm; -use core::ops::Range; -use hvdef::hypercall::{HvInputVtl, InitialVpContextX64}; -use hvdef::Vtl; -use memory_range::MemoryRange; -use minimal_rt::arch::msr::{read_msr, write_msr}; -use sync_nostd::Mutex; - const ALIGNMENT: usize = 4096; type ComandTable = BTreeMap, Vtl)>>; @@ -56,7 +58,7 @@ impl SecureInterceptPlatformTrait for HvTestCtx { let layout = Layout::from_size_align(4096, ALIGNMENT) .or_else(|_| Err(TmkError(TmkErrorType::AllocationFailed)))?; - let ptr = unsafe { ALLOCATOR.alloc(layout) }; + let ptr = unsafe { alloc(layout) }; let gpn = (ptr as u64) >> 12; let reg = (gpn << 12) | 0x1; @@ -145,20 +147,8 @@ impl VirtualProcessorPlatformTrait for HvTestCtx { fn get_vp_count(&self) -> TmkResult { #[cfg(target_arch = "x86_64")] { - let mut result: u32; - unsafe { - asm!( - "push rbx", - "cpuid", - "mov {result:r}, rbx", - "pop rbx", - in("eax") 1u32, - out("ecx") _, - out("edx") _, - result = out(reg) result, - options(nomem, nostack) - ); - } + let cpuid = unsafe { core::arch::x86_64::__cpuid(1) }; + let result = cpuid.ebx; Ok((result >> 16) & 0xFF) } @@ -216,28 +206,38 @@ impl VirtualProcessorPlatformTrait for HvTestCtx { self.vp_runing.insert(vp_index); } else { let (tx, rx) = sync_nostd::Channel::>::new().split(); - cmdt().lock().get_mut(&self.my_vp_idx).unwrap().push_back(( + let self_vp_idx = self.my_vp_idx; + cmdt().lock().get_mut(&self_vp_idx).unwrap().push_back(( Box::new(move |ctx| { + log::debug!("starting VP{} in VTL1 of vp{}", vp_index, self_vp_idx); let r = ctx.enable_vp_vtl_with_default_context(vp_index, Vtl::Vtl1); if r.is_err() { + log::error!("failed to enable VTL1 for VP{}: {:?}", vp_index, r); let _ = tx.send(r); return; } + log::debug!("successfully enabled VTL1 for VP{}", vp_index); let r = ctx.start_running_vp_with_default_context(VpExecutor::new( vp_index, Vtl::Vtl0, )); if r.is_err() { + log::error!("failed to start VP{}: {:?}", vp_index, r); let _ = tx.send(r); return; } + log::debug!("successfully started VP{}", vp_index); let _ = tx.send(Ok(())); ctx.switch_to_low_vtl(); }), Vtl::Vtl1, )); self.switch_to_high_vtl(); - log::debug!("VP{} waiting for start confirmation for vp from VTL1: {}", self.my_vp_idx, vp_index); + log::debug!( + "VP{} waiting for start confirmation for vp from VTL1: {}", + self.my_vp_idx, + vp_index + ); let rx = rx.recv(); if let Ok(r) = rx { r?; @@ -373,6 +373,12 @@ impl HvTestCtx { register_command_queue(i); } self.my_vtl = self.hvcall.vtl(); + let reg = self + .hvcall + .get_register(hvdef::HvAllArchRegisterName::VpIndex.into(), None) + .expect("error: failed to get vp index"); + let reg = reg.as_u64(); + self.my_vp_idx = reg as u32; Ok(()) } @@ -382,12 +388,6 @@ impl HvTestCtx { fn exec_handler() { let mut ctx = HvTestCtx::new(); ctx.init().expect("error: failed to init on a VP"); - let reg = ctx - .hvcall - .get_register(hvdef::HvAllArchRegisterName::VpIndex.into(), None) - .expect("error: failed to get vp index"); - let reg = reg.as_u64(); - ctx.my_vp_idx = reg as u32; loop { let mut vtl: Option = None; @@ -396,9 +396,7 @@ impl HvTestCtx { { let mut cmdt = cmdt().lock(); let d = cmdt.get_mut(&ctx.my_vp_idx); - if d.is_some() { - log::info!("vp: {} has commands to execute", ctx.my_vp_idx); - let d = d.unwrap(); + if let Some(d) = d { if !d.is_empty() { let (_c, v) = d.front().unwrap(); if *v == ctx.my_vtl { @@ -436,15 +434,13 @@ impl HvTestCtx { /// Helper to wrap an arbitrary function inside a captured VP context /// that can later be used to start a new VP/VTL instance. fn run_fn_with_current_context(&mut self, func: fn()) -> Result { - use super::alloc::SIZE_1MB; - let mut vp_context: InitialVpContextX64 = self .hvcall .get_current_vtl_vp_context() .expect("Failed to get VTL1 context"); - let stack_layout = Layout::from_size_align(SIZE_1MB, 16) + let stack_layout = Layout::from_size_align(1024 * 1024, 16) .expect("Failed to create layout for stack allocation"); - let allocated_stack_ptr = unsafe { ALLOCATOR.alloc(stack_layout) }; + let allocated_stack_ptr = unsafe { alloc(stack_layout) }; if allocated_stack_ptr.is_null() { return Err(TmkErrorType::AllocationFailed.into()); } diff --git a/opentmk/opentmk/src/platform/mod.rs b/opentmk/opentmk/src/platform/mod.rs new file mode 100644 index 0000000000..6f4c0a0b95 --- /dev/null +++ b/opentmk/opentmk/src/platform/mod.rs @@ -0,0 +1 @@ +pub mod hypvctx; diff --git a/opentmk/opentmk/src/uefi/tests/hv_error_vp_start.rs b/opentmk/opentmk/src/tests/hv_error_vp_start.rs similarity index 78% rename from opentmk/opentmk/src/uefi/tests/hv_error_vp_start.rs rename to opentmk/opentmk/src/tests/hv_error_vp_start.rs index 45aa4b2248..7de00416a9 100644 --- a/opentmk/opentmk/src/uefi/tests/hv_error_vp_start.rs +++ b/opentmk/opentmk/src/tests/hv_error_vp_start.rs @@ -1,14 +1,15 @@ use hvdef::Vtl; use sync_nostd::Channel; -use crate::{context::{VirtualProcessorPlatformTrait, VpExecutor, VtlPlatformTrait}, tmk_assert}; +use crate::{ + context::{VirtualProcessorPlatformTrait, VpExecutor, VtlPlatformTrait}, + tmk_assert, +}; pub fn exec(ctx: &mut T) where - T: VtlPlatformTrait - + VirtualProcessorPlatformTrait, + T: VtlPlatformTrait + VirtualProcessorPlatformTrait, { - // Skiping VTL setup for now to test the negitive case let vp_count = ctx.get_vp_count(); @@ -20,7 +21,7 @@ where // Testing BSP VTL1 Bringup { let (tx, _rx) = Channel::new().split(); - + let result = ctx.start_on_vp(VpExecutor::new(0, Vtl::Vtl1).command(move |ctx: &mut T| { let vp = ctx.get_current_vp(); tmk_assert!(vp.is_ok(), "vp should be valid"); @@ -39,9 +40,12 @@ where .expect("Failed to send message through the channel"); ctx.switch_to_low_vtl(); })); - + tmk_assert!(result.is_err(), "start_on_vp should fail"); - tmk_assert!(result.unwrap_err() == crate::tmkdefs::TmkErrorType::InvalidVtlState.into(), "start_on_vp should fail with InvalidVtlState"); + tmk_assert!( + result.unwrap_err() == crate::tmkdefs::TmkErrorType::InvalidVtlState.into(), + "start_on_vp should fail with InvalidVtlState" + ); log::info!("result on start_on_vp: {:?}", result); } } diff --git a/opentmk/opentmk/src/tests/hv_misc.rs b/opentmk/opentmk/src/tests/hv_misc.rs index 8379c344a0..0f53f4df57 100644 --- a/opentmk/opentmk/src/tests/hv_misc.rs +++ b/opentmk/opentmk/src/tests/hv_misc.rs @@ -1,37 +1,67 @@ +#![allow(warnings)] +use alloc::{alloc::alloc, sync::Arc}; +use core::{ + alloc::{GlobalAlloc, Layout}, + arch::asm, + cell::RefCell, + ops::Range, + sync::atomic::{AtomicI32, Ordering}, +}; + +use ::alloc::{boxed::Box, vec::Vec}; +use context::VpExecutor; +use hvdef::{ + hypercall::HvInputVtl, HvAllArchRegisterName, HvRegisterVsmVpStatus, HvX64RegisterName, Vtl, +}; +use hypvctx::HvTestCtx; +use sync_nostd::{Channel, Receiver, Sender}; +use uefi::{entry, Status}; + // WIP : This test is not yet complete and is not expected to pass. // // This test is to verify that the VTL protections are working as expected. // The stack values in VTL0 are changing after interrupt handling in VTL1. -#![allow(warnings)] -use crate::slog::{AssertOption, AssertResult}; -use crate::sync::{Channel, Receiver, Sender}; -use crate::uefi::alloc::{ALLOCATOR, SIZE_1MB}; -use crate::uefi::{context, hypvctx}; -use crate::{infolog, tmk_assert}; -use ::alloc::boxed::Box; -use alloc::sync::Arc; -use ::alloc::vec::Vec; -use context::{TestCtxTrait, VpExecutor}; -use hypvctx::HvTestCtx; -use core::alloc::{GlobalAlloc, Layout}; -use core::arch::asm; -use core::cell::RefCell; -use core::ops::Range; -use core::sync::atomic::{AtomicI32, Ordering}; -use hvdef::hypercall::HvInputVtl; -use hvdef::{HvAllArchRegisterName, HvRegisterVsmVpStatus, HvX64RegisterName, Vtl}; +use crate::tmk_assert; +use crate::{ + context, + context::{ + InterruptPlatformTrait, SecureInterceptPlatformTrait, VirtualProcessorPlatformTrait, + VtlPlatformTrait, + }, + platform::hypvctx, + tmkdefs::TmkResult, +}; static mut HEAPX: RefCell<*mut u8> = RefCell::new(0 as *mut u8); static mut CON: AtomicI32 = AtomicI32::new(0); -pub fn exec(_opt: Option<()>, ctx: Arc>) { +fn call_act() { + unsafe { + let heapx = *HEAPX.borrow(); + let val = *(heapx.add(10)); + log::info!( + "reading mutated heap memory from vtl0(it should not be 0xAA): 0x{:x}", + val + ); + tmk_assert!( + val != 0xAA, + "heap memory should not be accessible from vtl0" + ); + } +} + +pub fn exec(ctx: &mut T) +where + T: InterruptPlatformTrait + + SecureInterceptPlatformTrait + + VtlPlatformTrait + + VirtualProcessorPlatformTrait, +{ log::info!("ctx ptr: {:p}", &ctx as *const _); - - let mut cpy = ctx.clone(); - - let mut ctx = ctx.borrow_mut(); - let mut vp_count = ctx.get_vp_count(); + let vp_count = ctx.get_vp_count(); + tmk_assert!(vp_count.is_ok(), "get_vp_count should succeed"); + let vp_count = vp_count.unwrap(); tmk_assert!(vp_count == 8, "vp count should be 8"); ctx.setup_interrupt_handler(); @@ -40,76 +70,79 @@ pub fn exec(_opt: Option<()>, ctx: Arc>) { ctx.setup_partition_vtl(Vtl::Vtl1); - ctx.start_on_vp( - VpExecutor::new(0, Vtl::Vtl1).command(move |ctx: &mut dyn TestCtxTrait| { - log::info!("successfully started running VTL1 on vp0."); - ctx.setup_secure_intercept(0x30); - ctx.set_interupt_idx(0x30, || { - log::info!("interrupt fired!"); - - // let mut hv_test_ctx: HvTestCtx = HvTestCtx::new(); - // hv_test_ctx.init(); - - // let c = hv_test_ctx.get_register(HvAllArchRegisterName::VsmVpStatus.0); - - // let cp: HvRegisterVsmVpStatus = HvRegisterVsmVpStatus::from_bits(c as u64); + ctx.start_on_vp(VpExecutor::new(0, Vtl::Vtl1).command(move |ctx: &mut T| { + log::info!("successfully started running VTL1 on vp0."); + ctx.setup_secure_intercept(0x30); + ctx.set_interrupt_idx(0x30, move || { + log::info!("interrupt fired!"); - // log::info!("VSM VP Status: {:?}", cp); + let hv = HvTestCtx::new(); + hv.get_current_vp(); + log::info!( + "current vp from interrupt: {}", + hv.get_current_vp().unwrap() + ); - log::info!("interrupt handled!"); - }); + log::info!("interrupt handled!"); + }); - let layout = - Layout::from_size_align(SIZE_1MB, 4096).expect("msg: failed to create layout"); - let ptr = unsafe { ALLOCATOR.alloc(layout) }; - log::info!("allocated some memory in the heap from vtl1"); - unsafe { - let mut z = HEAPX.borrow_mut(); - *z = ptr; - *ptr.add(10) = 0xAA; - } + let layout = + Layout::from_size_align(1024 * 1024, 4096).expect("msg: failed to create layout"); + let ptr = unsafe { alloc(layout) }; + log::info!("allocated some memory in the heap from vtl1"); + unsafe { + let mut z = HEAPX.borrow_mut(); + *z = ptr; + *ptr.add(10) = 0xA2; + } - let size = layout.size(); - ctx.setup_vtl_protection(); + let size = layout.size(); + ctx.setup_vtl_protection(); - log::info!("enabled vtl protections for the partition."); + log::info!("enabled vtl protections for the partition."); - let range = Range { - start: ptr as u64, - end: ptr as u64 + size as u64, - }; + let range = Range { + start: ptr as u64, + end: ptr as u64 + size as u64, + }; - ctx.apply_vtl_protection_for_memory(range, Vtl::Vtl1); + ctx.apply_vtl_protection_for_memory(range, Vtl::Vtl1); - log::info!("moving to vtl0 to attempt to read the heap memory"); + log::info!("moving to vtl0 to attempt to read the heap memory"); - ctx.switch_to_low_vtl(); - }), - ); + ctx.switch_to_low_vtl(); + })); - ctx.queue_command_vp(VpExecutor::new(0, Vtl::Vtl1).command(move |ctx| { + ctx.queue_command_vp(VpExecutor::new(0, Vtl::Vtl1).command(move |ctx: &mut T| { log::info!("successfully started running VTL1 on vp0."); ctx.switch_to_low_vtl(); })); log::info!("ctx ptr: {:p}", &ctx as *const _); - log::info!("_opt ptr: {:p}", &_opt as *const _); + let mut l = 0u64; unsafe { asm!("mov {}, rsp", out(reg) l) }; log::info!("rsp: 0x{:x}", l); - unsafe { - log::info!("Attempting to read heap memory from vtl0"); - let heapx = *HEAPX.borrow(); - let val = *(heapx.add(10)); - log::info!( - "reading mutated heap memory from vtl0(it should not be 0xAA): 0x{:x}", - val - ); - tmk_assert!( - val != 0xAA, - "heap memory should not be accessible from vtl0" - ); - } + //log::info!("Attempting to read heap memory from vtl0"); + + ctx.start_on_vp( + VpExecutor::new(0x2, Vtl::Vtl0) + .command(|ctx| unsafe { + + log::info!("successfully started running VTL0 on vp2."); + // let heapx = *HEAPX.borrow(); + // let val = *(heapx.add(10)); + // log::info!( + // "reading mutated heap memory from vtl0(it should not be 0xAA): 0x{:x}", + // val + // ); + // tmk_assert!( + // val != 0xAA, + // "heap memory should not be accessible from vtl0" + // ); + })); + + log::info!("after ctx ptr: {:p}", &ctx as *const _); unsafe { asm!("mov {}, rsp", out(reg) l) }; log::info!("rsp: 0x{:x}", l); @@ -123,23 +156,14 @@ pub fn exec(_opt: Option<()>, ctx: Arc>) { // }, // )); // } - - drop(ctx); - - let mut ctx = cpy.borrow_mut(); - // let mut ctx = cpy.borrow_mut(); log::info!("ctx ptr: {:p}", &ctx as *const _); - log::info!("opt ptr: {:p}", &_opt as *const _); let c = ctx.get_vp_count(); - + tmk_assert!(c.is_ok(), "get_vp_count should succeed"); + let c = c.unwrap(); tmk_assert!(c == 8, "vp count should be 8"); // rx.recv(); log::info!("we are in vtl0 now!"); log::info!("we reached the end of the test"); - loop { - - } - -} \ No newline at end of file +} diff --git a/opentmk/opentmk/src/tests/hv_processor.rs b/opentmk/opentmk/src/tests/hv_processor.rs index 64439039db..a201d903bd 100644 --- a/opentmk/opentmk/src/tests/hv_processor.rs +++ b/opentmk/opentmk/src/tests/hv_processor.rs @@ -1,74 +1,98 @@ use hvdef::Vtl; +use sync_nostd::Channel; use crate::{ - criticallog, infolog, + context::{VirtualProcessorPlatformTrait, VpExecutor, VtlPlatformTrait}, tmk_assert, - uefi::context::{TestCtxTrait, VpExecutor}, }; -pub fn exec(ctx: &mut dyn TestCtxTrait) { - ctx.setup_interrupt_handler(); - ctx.setup_partition_vtl(Vtl::Vtl1); +pub fn exec(ctx: &mut T) +where + T: VtlPlatformTrait + VirtualProcessorPlatformTrait, +{ + let r = ctx.setup_partition_vtl(Vtl::Vtl1); + tmk_assert!(r.is_ok(), "setup_partition_vtl should succeed"); let vp_count = ctx.get_vp_count(); + tmk_assert!(vp_count.is_ok(), "get_vp_count should succeed"); + + let vp_count = vp_count.unwrap(); tmk_assert!(vp_count == 8, "vp count should be 8"); // Testing BSP VTL Bringup { - let (tx, rx) = crate::sync::Channel::new().split(); - ctx.start_on_vp(VpExecutor::new(0, Vtl::Vtl1).command( - move |ctx: &mut dyn TestCtxTrait| { - let vp = ctx.get_current_vp(); - log::info!("vp: {}", vp); - tmk_assert!(vp == 0, "vp should be equal to 0"); - - let vtl = ctx.get_current_vtl(); - log::info!("vtl: {:?}", vtl); - tmk_assert!(vtl == Vtl::Vtl1, "vtl should be Vtl1 for BSP"); - _ = tx.send(()); - ctx.switch_to_low_vtl(); - }, - )); + let (tx, rx) = Channel::new().split(); + let result = ctx.start_on_vp(VpExecutor::new(0, Vtl::Vtl1).command(move |ctx: &mut T| { + let vp = ctx.get_current_vp(); + tmk_assert!(vp.is_ok(), "vp should be valid"); + + let vp = vp.unwrap(); + log::info!("vp: {}", vp); + tmk_assert!(vp == 0, "vp should be equal to 0"); + + let vtl = ctx.get_current_vtl(); + tmk_assert!(vtl.is_ok(), "vtl should be valid"); + + let vtl = vtl.unwrap(); + log::info!("vtl: {:?}", vtl); + tmk_assert!(vtl == Vtl::Vtl1, "vtl should be Vtl1 for BSP"); + tx.send(()) + .expect("Failed to send message through the channel"); + ctx.switch_to_low_vtl(); + })); + tmk_assert!(result.is_ok(), "start_on_vp should succeed"); _ = rx.recv(); } for i in 1..vp_count { // Testing VTL1 { - let (tx, rx) = crate::sync::Channel::new().split(); - ctx.start_on_vp(VpExecutor::new(i, Vtl::Vtl1).command( - move |ctx: &mut dyn TestCtxTrait| { + let (tx, rx) = Channel::new().split(); + let result = + ctx.start_on_vp(VpExecutor::new(i, Vtl::Vtl1).command(move |ctx: &mut T| { let vp = ctx.get_current_vp(); + tmk_assert!(vp.is_ok(), "vp should be valid"); + + let vp = vp.unwrap(); log::info!("vp: {}", vp); tmk_assert!(vp == i, format!("vp should be equal to {}", i)); let vtl = ctx.get_current_vtl(); + tmk_assert!(vtl.is_ok(), "vtl should be valid"); + + let vtl = vtl.unwrap(); log::info!("vtl: {:?}", vtl); tmk_assert!(vtl == Vtl::Vtl1, format!("vtl should be Vtl1 for VP {}", i)); _ = tx.send(()); - }, - )); + })); + tmk_assert!(result.is_ok(), "start_on_vp should succeed"); _ = rx.recv(); } // Testing VTL0 { - let (tx, rx) = crate::sync::Channel::new().split(); - ctx.start_on_vp(VpExecutor::new(i, Vtl::Vtl0).command( - move |ctx: &mut dyn TestCtxTrait| { + let (tx, rx) = Channel::new().split(); + let result = + ctx.start_on_vp(VpExecutor::new(i, Vtl::Vtl0).command(move |ctx: &mut T| { let vp = ctx.get_current_vp(); + tmk_assert!(vp.is_ok(), "vp should be valid"); + + let vp = vp.unwrap(); log::info!("vp: {}", vp); tmk_assert!(vp == i, format!("vp should be equal to {}", i)); let vtl = ctx.get_current_vtl(); + tmk_assert!(vtl.is_ok(), "vtl should be valid"); + + let vtl = vtl.unwrap(); log::info!("vtl: {:?}", vtl); tmk_assert!(vtl == Vtl::Vtl0, format!("vtl should be Vtl0 for VP {}", i)); _ = tx.send(()); - }, - )); + })); + tmk_assert!(result.is_ok(), "start_on_vp should succeed"); _ = rx.recv(); } } - log::error!("All VPs have been tested"); + log::warn!("All VPs have been tested"); } diff --git a/opentmk/opentmk/src/tests/mod.rs b/opentmk/opentmk/src/tests/mod.rs index 2457eb039e..fd74377033 100644 --- a/opentmk/opentmk/src/tests/mod.rs +++ b/opentmk/opentmk/src/tests/mod.rs @@ -1,9 +1,13 @@ -mod hv_processor; -mod hv_misc; +#![expect(dead_code)] +use crate::platform::hypvctx::HvTestCtx; -use crate::uefi::hypvctx::HvTestCtx; +mod hv_error_vp_start; +mod hv_misc; +mod hv_processor; +mod tpm_test; pub fn run_test() { let mut ctx = HvTestCtx::new(); - hv_processor::exec(&mut ctx); + ctx.init().expect("failed to init on BSP"); + hv_misc::exec(&mut ctx); } \ No newline at end of file diff --git a/opentmk/opentmk/src/tests/tpm_test.rs b/opentmk/opentmk/src/tests/tpm_test.rs new file mode 100644 index 0000000000..3aefe5533c --- /dev/null +++ b/opentmk/opentmk/src/tests/tpm_test.rs @@ -0,0 +1,12 @@ +use log::info; + +use crate::{arch::tpm, tmk_assert}; + +pub fn exec(ctx: &mut T) { + let date = crate::arch::rtc::read_rtc(); + log::info!("Current RTC: {} UNIX epoch: {}", date, date.to_unix_epoch_sec()); + + let mut tpm = tpm::tpm_driver_example(); + info!("TPM driver example started {:?}", tpm); + +} \ No newline at end of file diff --git a/opentmk/opentmk/src/tmk_assert.rs b/opentmk/opentmk/src/tmk_assert.rs index d4eb9f4433..a71c983050 100644 --- a/opentmk/opentmk/src/tmk_assert.rs +++ b/opentmk/opentmk/src/tmk_assert.rs @@ -1,5 +1,6 @@ use alloc::string::{String, ToString}; use core::fmt::Write; + use serde::Serialize; #[derive(Serialize)] @@ -39,7 +40,7 @@ where } } -pub fn format_assert_json_string( +pub(crate) fn format_assert_json_string( s: &str, terminate_new_line: bool, line: String, @@ -59,8 +60,12 @@ where return out; } -pub fn write_str(s: &str) { - let _ = crate::tmk_logger::LOGGER.get_writter().write_str(s); +pub(crate) fn write_str(s: &str) { + _ = crate::tmk_logger::LOGGER.get_writer() + .as_mut() + .map(|writer| { + writer.write_str(s) + }); } #[macro_export] @@ -71,11 +76,12 @@ macro_rules! tmk_assert { let file_line = format!("{}:{}", file, line); let expn = stringify!($condition); let result: bool = $condition; - let js = - crate::tmk_assert::format_assert_json_string(&expn, true, file_line, result, &$message); - crate::tmk_assert::write_str(&js); + let js = $crate::tmk_assert::format_assert_json_string( + &expn, true, file_line, result, &$message, + ); + $crate::tmk_assert::write_str(&js); if !result { panic!("Assertion failed: {}", $message); } }}; -} \ No newline at end of file +} diff --git a/opentmk/opentmk/src/tmk_logger.rs b/opentmk/opentmk/src/tmk_logger.rs index 4c974103b4..9daf216f00 100644 --- a/opentmk/opentmk/src/tmk_logger.rs +++ b/opentmk/opentmk/src/tmk_logger.rs @@ -1,17 +1,18 @@ -use core::fmt::Write; - use alloc::{ fmt::format, string::{String, ToString}, }; -use log::SetLoggerError; -use sync_nostd::{Mutex, MutexGuard}; +use core::fmt::Write; -use crate::arch::serial::{InstrIoAccess, Serial}; +use log::SetLoggerError; use serde::Serialize; +use sync_nostd::{Mutex, MutexGuard}; +use anyhow::Result; +use crate::arch::serial::{InstrIoAccess, Serial, SerialPort}; #[derive(Serialize)] struct LogEntry { + #[serde(rename = "type")] log_type: &'static str, level: String, message: String, @@ -29,7 +30,7 @@ impl LogEntry { } } -pub fn format_log_string_to_json( +pub(crate) fn format_log_string_to_json( message: &String, line: &String, terminate_new_line: bool, @@ -45,28 +46,40 @@ pub fn format_log_string_to_json( } pub struct TmkLogger { - pub writter: T, + pub writer: T, } -impl TmkLogger> +impl TmkLogger>> where T: Write + Send, { - pub const fn new(provider: T) -> Self { + pub fn new_provider(provider: T) -> Self { TmkLogger { - writter: Mutex::new(provider), + writer: Mutex::new(Some(provider)), } } - pub fn get_writter(&self) -> MutexGuard<'_, T> + pub const fn new() -> Self { + TmkLogger { + writer: Mutex::new(None), + } + } + + pub fn set_writer(&self, writter: T) { + self.writer + .lock() + .replace(writter); + } + + pub fn get_writer(&self) -> MutexGuard<'_, Option> where T: Write + Send, { - self.writter.lock() + self.writer.lock() } } -impl log::Log for TmkLogger> +impl log::Log for TmkLogger>> where T: Write + Send, { @@ -82,15 +95,23 @@ where record.line().unwrap_or_default() ); let str = format_log_string_to_json(&str, &line, true, record.level()); - _ = self.writter.lock().write_str(str.as_str()); + self.get_writer() + .as_mut() + .map(|writer| { + writer.write_str(&str) + }); } fn flush(&self) {} } -pub static LOGGER: TmkLogger>> = - TmkLogger::new(Serial::new(InstrIoAccess {})); +type SerialPortWriter = Serial; +pub static LOGGER: TmkLogger>> = TmkLogger::new(); pub fn init() -> Result<(), SetLoggerError> { - log::set_logger(&LOGGER).map(|()| log::set_max_level(log::LevelFilter::Debug)) + let serial = SerialPortWriter::new(SerialPort::COM2.into()); + LOGGER.set_writer(serial); + + log::set_logger(&LOGGER) + .map(|()| log::set_max_level(log::LevelFilter::Debug)) } diff --git a/opentmk/opentmk/src/tmkdefs.rs b/opentmk/opentmk/src/tmkdefs.rs index 57f9faaa3f..4bf42b1510 100644 --- a/opentmk/opentmk/src/tmkdefs.rs +++ b/opentmk/opentmk/src/tmkdefs.rs @@ -81,9 +81,9 @@ pub type TmkResult = Result; impl core::error::Error for TmkError {} impl core::fmt::Display for TmkError { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - write!(f, "TmkError({:?})", self.0) - } + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "TmkError({:?})", self.0) + } } impl From for TmkError { diff --git a/opentmk/opentmk/src/uefi/init.rs b/opentmk/opentmk/src/uefi/init.rs index aa2eac3402..3e7f2a7f84 100644 --- a/opentmk/opentmk/src/uefi/init.rs +++ b/opentmk/opentmk/src/uefi/init.rs @@ -1,9 +1,12 @@ -use uefi::{boot::{exit_boot_services, MemoryType}, guid, CStr16, Status}; +use uefi::{ + boot::{exit_boot_services, MemoryType}, + guid, CStr16, Status, +}; use super::alloc::ALLOCATOR; const EFI_GUID: uefi::Guid = guid!("610b9e98-c6f6-47f8-8b47-2d2da0d52a91"); -const OS_LOADER_INDICATIONS: &'static str = "OsLoaderIndications"; +const OS_LOADER_INDICATIONS: &str = "OsLoaderIndications"; fn enable_uefi_vtl_protection() { let mut buf = vec![0u8; 1024]; @@ -53,4 +56,4 @@ pub fn init() -> Result<(), Status> { crate::tmk_logger::init().expect("Failed to init logger"); enable_uefi_vtl_protection(); Ok(()) -} \ No newline at end of file +} diff --git a/opentmk/opentmk/src/uefi/mod.rs b/opentmk/opentmk/src/uefi/mod.rs index a1e677259d..da42ac5334 100644 --- a/opentmk/opentmk/src/uefi/mod.rs +++ b/opentmk/opentmk/src/uefi/mod.rs @@ -2,23 +2,21 @@ // Licensed under the MIT License. mod alloc; -mod hypvctx; pub mod init; mod rt; -mod tests; -use crate::tmk_assert; use init::init; -use uefi::entry; -use uefi::Status; +use uefi::{entry, Status}; + +use crate::tmk_assert; #[entry] fn uefi_main() -> Status { - let r= init(); + let r = init(); tmk_assert!(r.is_ok(), "init should succeed"); log::warn!("TEST_START"); - tests::run_test(); + crate::tests::run_test(); log::warn!("TEST_END"); Status::SUCCESS } diff --git a/opentmk/opentmk/src/uefi/rt.rs b/opentmk/opentmk/src/uefi/rt.rs index 46cfc431d4..d115f97a88 100644 --- a/opentmk/opentmk/src/uefi/rt.rs +++ b/opentmk/opentmk/src/uefi/rt.rs @@ -4,8 +4,6 @@ //! Runtime support for the UEFI application environment. #![cfg(target_os = "uefi")] -// UNSAFETY: Raw assembly needed for panic handling to abort. -use core::arch::asm; #[panic_handler] fn panic_handler(panic: &core::panic::PanicInfo<'_>) -> ! { diff --git a/opentmk/opentmk/src/uefi/tests/hv_misc.rs b/opentmk/opentmk/src/uefi/tests/hv_misc.rs deleted file mode 100644 index a912a94595..0000000000 --- a/opentmk/opentmk/src/uefi/tests/hv_misc.rs +++ /dev/null @@ -1,137 +0,0 @@ -#![allow(warnings)] -use crate::context::{ - InterruptPlatformTrait, SecureInterceptPlatformTrait, VirtualProcessorPlatformTrait, - VtlPlatformTrait, -}; -// WIP : This test is not yet complete and is not expected to pass. -// -// This test is to verify that the VTL protections are working as expected. -// The stack values in VTL0 are changing after interrupt handling in VTL1. -use crate::tmk_assert; -use crate::tmkdefs::TmkResult; -use crate::uefi::alloc::{ALLOCATOR, SIZE_1MB}; -use crate::{context, uefi::hypvctx}; -use ::alloc::boxed::Box; -use ::alloc::vec::Vec; -use alloc::sync::Arc; -use context::VpExecutor; -use core::alloc::{GlobalAlloc, Layout}; -use core::arch::asm; -use core::cell::RefCell; -use core::ops::Range; -use core::sync::atomic::{AtomicI32, Ordering}; -use hvdef::hypercall::HvInputVtl; -use hvdef::{HvAllArchRegisterName, HvRegisterVsmVpStatus, HvX64RegisterName, Vtl}; -use hypvctx::HvTestCtx; -use sync_nostd::{Channel, Receiver, Sender}; -use uefi::entry; -use uefi::Status; - -static mut HEAPX: RefCell<*mut u8> = RefCell::new(0 as *mut u8); -static mut CON: AtomicI32 = AtomicI32::new(0); - -pub fn exec(ctx: &mut T) -where - T: InterruptPlatformTrait - + SecureInterceptPlatformTrait - + VtlPlatformTrait - + VirtualProcessorPlatformTrait, -{ - log::info!("ctx ptr: {:p}", &ctx as *const _); - - let vp_count = ctx.get_vp_count(); - tmk_assert!(vp_count.is_ok(), "get_vp_count should succeed"); - let vp_count = vp_count.unwrap(); - tmk_assert!(vp_count == 8, "vp count should be 8"); - - ctx.setup_interrupt_handler(); - - log::info!("set intercept handler successfully!"); - - ctx.setup_partition_vtl(Vtl::Vtl1); - - ctx.start_on_vp(VpExecutor::new(0, Vtl::Vtl1).command(move |ctx: &mut T| { - log::info!("successfully started running VTL1 on vp0."); - ctx.setup_secure_intercept(0x30); - ctx.set_interrupt_idx(0x30, move || { - log::info!("interrupt fired!"); - - log::info!("interrupt handled!"); - }); - - let layout = Layout::from_size_align(SIZE_1MB, 4096).expect("msg: failed to create layout"); - let ptr = unsafe { ALLOCATOR.alloc(layout) }; - log::info!("allocated some memory in the heap from vtl1"); - unsafe { - let mut z = HEAPX.borrow_mut(); - *z = ptr; - *ptr.add(10) = 0xAA; - } - - let size = layout.size(); - ctx.setup_vtl_protection(); - - log::info!("enabled vtl protections for the partition."); - - let range = Range { - start: ptr as u64, - end: ptr as u64 + size as u64, - }; - - ctx.apply_vtl_protection_for_memory(range, Vtl::Vtl1); - - log::info!("moving to vtl0 to attempt to read the heap memory"); - - ctx.switch_to_low_vtl(); - })); - - ctx.queue_command_vp(VpExecutor::new(0, Vtl::Vtl1).command(move |ctx: &mut T| { - log::info!("successfully started running VTL1 on vp0."); - ctx.switch_to_low_vtl(); - })); - log::info!("ctx ptr: {:p}", &ctx as *const _); - - let mut l = 0u64; - unsafe { asm!("mov {}, rsp", out(reg) l) }; - log::info!("rsp: 0x{:x}", l); - unsafe { - log::info!("Attempting to read heap memory from vtl0"); - let heapx = *HEAPX.borrow(); - let val = *(heapx.add(10)); - log::info!( - "reading mutated heap memory from vtl0(it should not be 0xAA): 0x{:x}", - val - ); - tmk_assert!( - val != 0xAA, - "heap memory should not be accessible from vtl0" - ); - } - - - log::info!("after ctx ptr: {:p}", &ctx as *const _); - unsafe { asm!("mov {}, rsp", out(reg) l) }; - log::info!("rsp: 0x{:x}", l); - - // let (mut tx, mut rx) = Channel::new(1); - // { - // let mut tx = tx.clone(); - // ctx.start_on_vp(VpExecutor::new(2, Vtl::Vtl0).command( - // move |ctx: &mut dyn TestCtxTrait| { - // log::info!("Hello form vtl0 on vp2!"); - // tx.send(()); - // }, - // )); - // } - log::info!("ctx ptr: {:p}", &ctx as *const _); - let c = ctx.get_vp_count(); - tmk_assert!(c.is_ok(), "get_vp_count should succeed"); - let c = c.unwrap(); - tmk_assert!(c == 8, "vp count should be 8"); - - // rx.recv(); - - log::info!("we are in vtl0 now!"); - log::info!("we reached the end of the test"); - loop {} -} diff --git a/opentmk/opentmk/src/uefi/tests/hv_processor.rs b/opentmk/opentmk/src/uefi/tests/hv_processor.rs deleted file mode 100644 index a7e12f89a5..0000000000 --- a/opentmk/opentmk/src/uefi/tests/hv_processor.rs +++ /dev/null @@ -1,102 +0,0 @@ -use hvdef::Vtl; -use sync_nostd::Channel; - -use crate::{ - context::{ - VirtualProcessorPlatformTrait, - VpExecutor, VtlPlatformTrait, - }, - tmk_assert, -}; - -pub fn exec(ctx: &mut T) -where - T: VtlPlatformTrait - + VirtualProcessorPlatformTrait, -{ - let r = ctx.setup_partition_vtl(Vtl::Vtl1); - tmk_assert!(r.is_ok(), "setup_partition_vtl should succeed"); - - let vp_count = ctx.get_vp_count(); - tmk_assert!(vp_count.is_ok(), "get_vp_count should succeed"); - - let vp_count = vp_count.unwrap(); - tmk_assert!(vp_count == 8, "vp count should be 8"); - - // Testing BSP VTL Bringup - { - let (tx, rx) = Channel::new().split(); - let result = ctx.start_on_vp(VpExecutor::new(0, Vtl::Vtl1).command(move |ctx: &mut T| { - let vp = ctx.get_current_vp(); - tmk_assert!(vp.is_ok(), "vp should be valid"); - - let vp = vp.unwrap(); - log::info!("vp: {}", vp); - tmk_assert!(vp == 0, "vp should be equal to 0"); - - let vtl = ctx.get_current_vtl(); - tmk_assert!(vtl.is_ok(), "vtl should be valid"); - - let vtl = vtl.unwrap(); - log::info!("vtl: {:?}", vtl); - tmk_assert!(vtl == Vtl::Vtl1, "vtl should be Vtl1 for BSP"); - tx.send(()) - .expect("Failed to send message through the channel"); - ctx.switch_to_low_vtl(); - })); - tmk_assert!(result.is_ok(), "start_on_vp should succeed"); - _ = rx.recv(); - } - - for i in 1..vp_count { - // Testing VTL1 - { - let (tx, rx) = Channel::new().split(); - let result = - ctx.start_on_vp(VpExecutor::new(i, Vtl::Vtl1).command(move |ctx: &mut T| { - let vp = ctx.get_current_vp(); - tmk_assert!(vp.is_ok(), "vp should be valid"); - - let vp = vp.unwrap(); - log::info!("vp: {}", vp); - tmk_assert!(vp == i, format!("vp should be equal to {}", i)); - - let vtl = ctx.get_current_vtl(); - tmk_assert!(vtl.is_ok(), "vtl should be valid"); - - let vtl = vtl.unwrap(); - log::info!("vtl: {:?}", vtl); - tmk_assert!(vtl == Vtl::Vtl1, format!("vtl should be Vtl1 for VP {}", i)); - _ = tx.send(()); - })); - tmk_assert!(result.is_ok(), "start_on_vp should succeed"); - _ = rx.recv(); - } - - // Testing VTL0 - { - let (tx, rx) = Channel::new().split(); - let result = - ctx.start_on_vp(VpExecutor::new(i, Vtl::Vtl0).command(move |ctx: &mut T| { - let vp = ctx.get_current_vp(); - tmk_assert!(vp.is_ok(), "vp should be valid"); - - let vp = vp.unwrap(); - log::info!("vp: {}", vp); - tmk_assert!(vp == i, format!("vp should be equal to {}", i)); - - let vtl = ctx.get_current_vtl(); - tmk_assert!(vtl.is_ok(), "vtl should be valid"); - - let vtl = vtl.unwrap(); - log::info!("vtl: {:?}", vtl); - tmk_assert!(vtl == Vtl::Vtl0, format!("vtl should be Vtl0 for VP {}", i)); - _ = tx.send(()); - })); - tmk_assert!(result.is_ok(), "start_on_vp should succeed"); - _ = rx.recv(); - } - } - - log::warn!("All VPs have been tested"); -} diff --git a/opentmk/opentmk/src/uefi/tests/mod.rs b/opentmk/opentmk/src/uefi/tests/mod.rs deleted file mode 100644 index 75fc35f851..0000000000 --- a/opentmk/opentmk/src/uefi/tests/mod.rs +++ /dev/null @@ -1,13 +0,0 @@ -#![allow(dead_code)] -use super::hypvctx::HvTestCtx; - -mod hv_processor; -mod hv_misc; -mod hv_error_vp_start; - - -pub fn run_test() { - let mut ctx = HvTestCtx::new(); - ctx.init().expect("failed to init on BSP"); - hv_processor::exec(&mut ctx); -} \ No newline at end of file diff --git a/opentmk/sync/src/lib.rs b/opentmk/sync/src/lib.rs index e3a387c02c..bd69b046c0 100644 --- a/opentmk/sync/src/lib.rs +++ b/opentmk/sync/src/lib.rs @@ -27,7 +27,9 @@ struct ChannelInner { receivers: AtomicUsize, } +// SAFETY: ChannelInner is safe to share across threads as it uses atomic operations for senders and receivers counts unsafe impl Send for ChannelInner {} +// SAFETY: ChannelInner is safe to used across threads as it uses atomic operations for senders and receivers counts unsafe impl Sync for ChannelInner {} /// Error type for sending operations @@ -228,7 +230,6 @@ impl Receiver { Ok(value) => return Ok(value), Err(RecvError::Empty) => { // Yield to the scheduler and try again - continue; }, Err(err) => return Err(err), } diff --git a/xtask/src/tasks/guest_test/uefi/gpt_efi_disk.rs b/xtask/src/tasks/guest_test/uefi/gpt_efi_disk.rs index 862db7e55c..fb4641a93b 100644 --- a/xtask/src/tasks/guest_test/uefi/gpt_efi_disk.rs +++ b/xtask/src/tasks/guest_test/uefi/gpt_efi_disk.rs @@ -22,7 +22,7 @@ pub fn create_gpt_efi_disk(out_img: &Path, with_files: &[(&Path, &Path)]) -> Res )); } - let disk_size = 1024 * 1024 * 512; // 32MB disk should be enough for our tests + let disk_size = 1024 * 1024 * 32; // 32MB disk should be enough for our tests let num_sectors = disk_size / SECTOR_SIZE; let mut disk = vec![0; num_sectors * SECTOR_SIZE]; From 9a0ef26ad5ed67d56145cfbffe4cd140fccc2dd1 Mon Sep 17 00:00:00 2001 From: Mayank Kumar Date: Thu, 3 Jul 2025 13:50:24 +0000 Subject: [PATCH 12/23] clippy fixup --- opentmk/opentmk/src/arch/x86_64/rtc.rs | 2 -- opentmk/opentmk/src/arch/x86_64/serial.rs | 2 +- opentmk/opentmk/src/arch/x86_64/tpm.rs | 25 +++++++++++------------ opentmk/opentmk/src/hypercall.rs | 2 +- opentmk/opentmk/src/platform/hypvctx.rs | 13 ++++++------ opentmk/opentmk/src/tests/tpm_test.rs | 4 ++-- opentmk/opentmk/src/tmk_assert.rs | 2 +- opentmk/opentmk/src/tmk_logger.rs | 4 ++-- opentmk/opentmk/src/uefi/alloc.rs | 5 ++--- opentmk/opentmk/src/uefi/init.rs | 4 ++-- 10 files changed, 29 insertions(+), 34 deletions(-) diff --git a/opentmk/opentmk/src/arch/x86_64/rtc.rs b/opentmk/opentmk/src/arch/x86_64/rtc.rs index 322ce67f0c..37b73d8e59 100644 --- a/opentmk/opentmk/src/arch/x86_64/rtc.rs +++ b/opentmk/opentmk/src/arch/x86_64/rtc.rs @@ -1,5 +1,3 @@ -use core::panic::PanicInfo; -use core::ptr::{read_volatile, write_volatile}; use super::io::{inb, outb}; // CMOS/RTC I/O ports const CMOS_ADDRESS: u16 = 0x70; diff --git a/opentmk/opentmk/src/arch/x86_64/serial.rs b/opentmk/opentmk/src/arch/x86_64/serial.rs index 1ad452c507..2c2f2d8193 100644 --- a/opentmk/opentmk/src/arch/x86_64/serial.rs +++ b/opentmk/opentmk/src/arch/x86_64/serial.rs @@ -3,7 +3,7 @@ //! Serial output for debugging. -use core::{arch::asm, fmt}; +use core::fmt; use super::io; use sync_nostd::Mutex; diff --git a/opentmk/opentmk/src/arch/x86_64/tpm.rs b/opentmk/opentmk/src/arch/x86_64/tpm.rs index 1a3ba274fa..4bf442f1f3 100644 --- a/opentmk/opentmk/src/arch/x86_64/tpm.rs +++ b/opentmk/opentmk/src/arch/x86_64/tpm.rs @@ -18,14 +18,13 @@ extern crate alloc; -use core::convert::TryFrom; use core::fmt; use core::ptr::{read_volatile, write_volatile}; use core::sync::atomic::{AtomicBool, Ordering}; use log::{error, warn, info, debug, trace}; use alloc::vec::Vec; -use crate::arch::rtc::{self, delay_sec}; +use crate::arch::rtc::delay_sec; /// TPM command codes as defined in the TPM 2.0 specification #[repr(u32)] @@ -118,7 +117,7 @@ impl fmt::Display for TpmError { TpmError::UnsupportedCommand => write!(f, "Command not supported by TPM"), TpmError::HardwareFailure => write!(f, "TPM hardware failure"), TpmError::AuthFailure => write!(f, "TPM authorization failure"), - TpmError::TpmResponseError(code) => write!(f, "TPM response error: 0x{:08X}", code), + TpmError::TpmResponseError(code) => write!(f, "TPM response error: 0x{code:08X}"), TpmError::NotInitialized => write!(f, "TPM driver not initialized"), TpmError::AllocationFailure => write!(f, "Memory allocation failure"), } @@ -343,7 +342,7 @@ impl TpmDriver { error!("Timeout waiting for locality {} access", self.current_locality); return Err(TpmError::Timeout); } - super::rtc::delay_sec(2); + delay_sec(2); } // Check if TPM is valid @@ -473,7 +472,7 @@ impl TpmDriver { buffer.write(&startup_cmd) .map(|written| { debug!("TPM_CC_Startup command built, size: {} bytes", written); - () + }) .map_err(|err| { error!("Failed to build TPM_CC_Startup command: {}", err); @@ -567,7 +566,7 @@ impl TpmDriver { debug!("TPM response size: {} bytes", response_size); // Validate response size - if response_size < 10 || response_size > 4096 { + if !(10..=4096).contains(&response_size) { error!("Invalid TPM response size: {}", response_size); return Err(TpmError::CommunicationFailure); } @@ -672,7 +671,7 @@ impl TpmDriver { debug!("TPM CRB response size: {} bytes", response_size); // Validate response size - if response_size < 10 || response_size > 4096 { + if !(10..=4096).contains(&response_size) { error!("Invalid TPM CRB response size: {}", response_size); return Err(TpmError::CommunicationFailure); } @@ -916,7 +915,7 @@ impl TpmDriver { // Use unsafe for direct hardware access let value = unsafe { - read_volatile((addr as *const u8)) + read_volatile(addr as *const u8) }; trace!("TIS register 0x{:X} value: 0x{:02X}", reg_offset, value); @@ -931,7 +930,7 @@ impl TpmDriver { // Use unsafe for direct hardware access unsafe { - write_volatile((addr as *mut u8), value); + write_volatile(addr as *mut u8, value); } Ok(()) } @@ -943,7 +942,7 @@ impl TpmDriver { // Use unsafe for direct hardware access let value = unsafe { - read_volatile((addr as *const u32)) + read_volatile(addr as *const u32) }; trace!("CRB register 0x{:X} value: 0x{:08X}", reg_offset, value); @@ -958,7 +957,7 @@ impl TpmDriver { // Use unsafe for direct hardware access unsafe { - write_volatile((addr as *mut u32), value); + write_volatile(addr as *mut u32, value); } Ok(()) } @@ -970,7 +969,7 @@ impl TpmDriver { // Use unsafe for direct hardware access let value = unsafe { - read_volatile((addr as *const u8)) + read_volatile(addr as *const u8) }; trace!("MMIO byte 0x{:X} value: 0x{:02X}", offset, value); @@ -985,7 +984,7 @@ impl TpmDriver { // Use unsafe for direct hardware access unsafe { - write_volatile((addr as *mut u8), value); + write_volatile(addr as *mut u8, value); } Ok(()) } diff --git a/opentmk/opentmk/src/hypercall.rs b/opentmk/opentmk/src/hypercall.rs index 33021436b4..e4c1b02d72 100644 --- a/opentmk/opentmk/src/hypercall.rs +++ b/opentmk/opentmk/src/hypercall.rs @@ -479,7 +479,7 @@ impl HvCall { Some(hw_ids.len()), ); - let n = r.elements_processed() as usize; + let n = r.elements_processed(); output.extend( <[u32]>::ref_from_bytes(&mut self.output_page().buffer[..n * 4]) diff --git a/opentmk/opentmk/src/platform/hypvctx.rs b/opentmk/opentmk/src/platform/hypvctx.rs index 3d09751392..45075c8c82 100644 --- a/opentmk/opentmk/src/platform/hypvctx.rs +++ b/opentmk/opentmk/src/platform/hypvctx.rs @@ -3,7 +3,7 @@ use alloc::{ boxed::Box, collections::{btree_map::BTreeMap, btree_set::BTreeSet, linked_list::LinkedList}, }; -use core::{alloc::Layout, arch::asm, ops::Range}; +use core::{alloc::Layout, ops::Range}; use hvdef::{ hypercall::{HvInputVtl, InitialVpContextX64}, @@ -55,14 +55,13 @@ impl SecureInterceptPlatformTrait for HvTestCtx { /// hypervisor side notifications back to the guest. /// Returns [`TmkResult::Err`] if the allocation of the SIMP buffer fails. fn setup_secure_intercept(&mut self, interrupt_idx: u8) -> TmkResult<()> { - let layout = Layout::from_size_align(4096, ALIGNMENT) - .or_else(|_| Err(TmkError(TmkErrorType::AllocationFailed)))?; + let layout = Layout::from_size_align(4096, ALIGNMENT).map_err(|_| TmkError(TmkErrorType::AllocationFailed))?; let ptr = unsafe { alloc(layout) }; let gpn = (ptr as u64) >> 12; let reg = (gpn << 12) | 0x1; - unsafe { write_msr(hvdef::HV_X64_MSR_SIMP, reg.into()) }; + unsafe { write_msr(hvdef::HV_X64_MSR_SIMP, reg) }; log::info!("Successfuly set the SIMP register."); let reg = unsafe { read_msr(hvdef::HV_X64_MSR_SINT0) }; @@ -163,7 +162,7 @@ impl VirtualProcessorPlatformTrait for HvTestCtx { /// here – we simply enqueue. fn queue_command_vp(&mut self, cmd: VpExecutor) -> TmkResult<()> { let (vp_index, vtl, cmd) = cmd.get(); - let cmd = cmd.ok_or_else(|| TmkError(TmkErrorType::QueueCommandFailed))?; + let cmd = cmd.ok_or(TmkError(TmkErrorType::QueueCommandFailed))?; cmdt() .lock() .get_mut(&vp_index) @@ -183,7 +182,7 @@ impl VirtualProcessorPlatformTrait for HvTestCtx { /// spins in `exec_handler` waiting for work. fn start_on_vp(&mut self, cmd: VpExecutor) -> TmkResult<()> { let (vp_index, vtl, cmd) = cmd.get(); - let cmd = cmd.ok_or_else(|| TmkError(TmkErrorType::InvalidParameter))?; + let cmd = cmd.ok_or(TmkError(TmkErrorType::InvalidParameter))?; if vtl >= Vtl::Vtl2 { return Err(TmkError(TmkErrorType::InvalidParameter)); } @@ -427,7 +426,7 @@ impl HvTestCtx { /// Capture the current VP context, patch the entry point and stack /// so that the new VP starts in `exec_handler`. fn get_default_context(&mut self) -> Result { - return self.run_fn_with_current_context(HvTestCtx::exec_handler); + self.run_fn_with_current_context(HvTestCtx::exec_handler) } #[cfg(target_arch = "x86_64")] diff --git a/opentmk/opentmk/src/tests/tpm_test.rs b/opentmk/opentmk/src/tests/tpm_test.rs index 3aefe5533c..c9ecc4c06a 100644 --- a/opentmk/opentmk/src/tests/tpm_test.rs +++ b/opentmk/opentmk/src/tests/tpm_test.rs @@ -1,12 +1,12 @@ use log::info; -use crate::{arch::tpm, tmk_assert}; +use crate::arch::tpm; pub fn exec(ctx: &mut T) { let date = crate::arch::rtc::read_rtc(); log::info!("Current RTC: {} UNIX epoch: {}", date, date.to_unix_epoch_sec()); - let mut tpm = tpm::tpm_driver_example(); + let tpm = tpm::tpm_driver_example(); info!("TPM driver example started {:?}", tpm); } \ No newline at end of file diff --git a/opentmk/opentmk/src/tmk_assert.rs b/opentmk/opentmk/src/tmk_assert.rs index a71c983050..ba6f735632 100644 --- a/opentmk/opentmk/src/tmk_assert.rs +++ b/opentmk/opentmk/src/tmk_assert.rs @@ -57,7 +57,7 @@ where if terminate_new_line { out.push('\n'); } - return out; + out } pub(crate) fn write_str(s: &str) { diff --git a/opentmk/opentmk/src/tmk_logger.rs b/opentmk/opentmk/src/tmk_logger.rs index 9daf216f00..2a2e4c9b60 100644 --- a/opentmk/opentmk/src/tmk_logger.rs +++ b/opentmk/opentmk/src/tmk_logger.rs @@ -42,7 +42,7 @@ pub(crate) fn format_log_string_to_json( if terminate_new_line { out.push('\n'); } - return out; + out } pub struct TmkLogger { @@ -109,7 +109,7 @@ type SerialPortWriter = Serial; pub static LOGGER: TmkLogger>> = TmkLogger::new(); pub fn init() -> Result<(), SetLoggerError> { - let serial = SerialPortWriter::new(SerialPort::COM2.into()); + let serial = SerialPortWriter::new(SerialPort::COM2); LOGGER.set_writer(serial); log::set_logger(&LOGGER) diff --git a/opentmk/opentmk/src/uefi/alloc.rs b/opentmk/opentmk/src/uefi/alloc.rs index f6127573f7..2fa627ccae 100644 --- a/opentmk/opentmk/src/uefi/alloc.rs +++ b/opentmk/opentmk/src/uefi/alloc.rs @@ -81,7 +81,7 @@ impl MemoryAllocator { self.locked_heap.lock().init(ptr, size); } *self.use_locked_heap.lock().borrow_mut() = true; - return true; + true } #[allow(dead_code)] @@ -95,7 +95,6 @@ impl MemoryAllocator { if mem.is_err() { return core::ptr::null_mut(); } - let ptr = mem.unwrap().as_ptr(); - return ptr; + mem.unwrap().as_ptr() } } diff --git a/opentmk/opentmk/src/uefi/init.rs b/opentmk/opentmk/src/uefi/init.rs index 3e7f2a7f84..f5674d9582 100644 --- a/opentmk/opentmk/src/uefi/init.rs +++ b/opentmk/opentmk/src/uefi/init.rs @@ -30,7 +30,7 @@ fn enable_uefi_vtl_protection() { let os_loader_indications = os_loader_indications.to_le_bytes(); - let _ = uefi::runtime::set_variable( + uefi::runtime::set_variable( os_loader_indications_key, &uefi::runtime::VariableVendor(EFI_GUID), os_loader_indications_result.1, @@ -50,7 +50,7 @@ fn enable_uefi_vtl_protection() { pub fn init() -> Result<(), Status> { let r: bool = ALLOCATOR.init(2048); - if r == false { + if !r { return Err(Status::ABORTED); } crate::tmk_logger::init().expect("Failed to init logger"); From e02ee17d5f5f06180ac0c68acb007a68fb33e362 Mon Sep 17 00:00:00 2001 From: Mayank Kumar Date: Thu, 17 Jul 2025 12:06:10 +0000 Subject: [PATCH 13/23] broken --- .../minimal_rt/src/arch/x86_64/hypercall.rs | 1 + opentmk/opentmk/Cargo.toml | 18 +- opentmk/opentmk/build_deploy.sh | 8 +- opentmk/opentmk/src/arch/x86_64/mod.rs | 1 - opentmk/opentmk/src/arch/x86_64/serial.rs | 27 +- opentmk/opentmk/src/arch/x86_64/tpm.rs | 1020 ----------------- opentmk/opentmk/src/context.rs | 3 +- opentmk/opentmk/src/hypercall.rs | 3 + opentmk/opentmk/src/platform/hypvctx.rs | 127 +- .../opentmk/src/tests/hv_error_vp_start.rs | 2 +- opentmk/opentmk/src/tests/hv_misc.rs | 64 +- opentmk/opentmk/src/tests/hv_processor.rs | 13 +- opentmk/opentmk/src/tests/mod.rs | 4 +- opentmk/opentmk/src/tests/processors_valid.rs | 33 + opentmk/opentmk/src/tests/tpm_test.rs | 12 - opentmk/opentmk/src/tmk_assert.rs | 6 +- opentmk/opentmk/src/tmk_logger.rs | 37 +- opentmk/sync/src/lib.rs | 2 +- 18 files changed, 238 insertions(+), 1143 deletions(-) delete mode 100644 opentmk/opentmk/src/arch/x86_64/tpm.rs create mode 100644 opentmk/opentmk/src/tests/processors_valid.rs delete mode 100644 opentmk/opentmk/src/tests/tpm_test.rs diff --git a/openhcl/minimal_rt/src/arch/x86_64/hypercall.rs b/openhcl/minimal_rt/src/arch/x86_64/hypercall.rs index 9b87231e4f..6ad9fcfcbd 100644 --- a/openhcl/minimal_rt/src/arch/x86_64/hypercall.rs +++ b/openhcl/minimal_rt/src/arch/x86_64/hypercall.rs @@ -29,6 +29,7 @@ HYPERCALL_PAGE: /// input/output pages are not being concurrently used elsewhere. For fast /// hypercalls, the caller must ensure that there are no output words so that /// there is no register corruption. +#[inline(never)] pub unsafe fn invoke_hypercall( control: hvdef::hypercall::Control, input_gpa_or_fast1: u64, diff --git a/opentmk/opentmk/Cargo.toml b/opentmk/opentmk/Cargo.toml index c5570251d2..65ec92bfdd 100644 --- a/opentmk/opentmk/Cargo.toml +++ b/opentmk/opentmk/Cargo.toml @@ -31,14 +31,14 @@ workspace = true [build-dependencies] minimal_rt_build.workspace = true -[profile.dev] -overflow-checks = true [profile.release] -overflow-checks = true - - -[build] -rustflags = ["-Z", "stack-protector=all"] - - +opt-level = 0 # No optimizations (same as debug) +debug = true # Include debug symbols +debug-assertions = true # Enable debug assertions +overflow-checks = true # Enable integer overflow checks +lto = "off" # Disable link-time optrimization +codegen-units = 256 # Use more codegen units (faster compilation) + +[profile.release.package."*"] +opt-level = 0 diff --git a/opentmk/opentmk/build_deploy.sh b/opentmk/opentmk/build_deploy.sh index 9068f2a54d..4443e9d101 100755 --- a/opentmk/opentmk/build_deploy.sh +++ b/opentmk/opentmk/build_deploy.sh @@ -1,3 +1,5 @@ -RUST_BACKTRACE=1 cargo +nightly-2025-05-09 build -p opentmk --target x86_64-unknown-uefi #--target-dir ./target/x86_64-unknown-uefi/debug -cargo xtask guest-test uefi --bootx64 ~/projects-local/openvmm/target/x86_64-unknown-uefi/debug/opentmk.efi -qemu-img convert -f raw -O vhdx ~/projects-local/openvmm/target/x86_64-unknown-uefi/debug/opentmk.img ~/projects/opentmk.vhdx \ No newline at end of file +RUST_BACKTRACE=1 cargo +nightly-2025-05-09 build -p opentmk --target x86_64-unknown-uefi --release #--target-dir ./target/x86_64-unknown-uefi/debug +cargo xtask guest-test uefi --bootx64 ~/projects-local/openvmm/target/x86_64-unknown-uefi/release/opentmk.efi +qemu-img convert -f raw -O vhdx ~/projects-local/openvmm/target/x86_64-unknown-uefi/release/opentmk.img ~/projects/opentmk.vhdx + +# +nightly-2025-05-09 \ No newline at end of file diff --git a/opentmk/opentmk/src/arch/x86_64/mod.rs b/opentmk/opentmk/src/arch/x86_64/mod.rs index 12103e14fe..8453659c75 100644 --- a/opentmk/opentmk/src/arch/x86_64/mod.rs +++ b/opentmk/opentmk/src/arch/x86_64/mod.rs @@ -3,5 +3,4 @@ pub mod interrupt; mod interrupt_handler_register; pub mod serial; mod io; -pub mod tpm; pub mod rtc; \ No newline at end of file diff --git a/opentmk/opentmk/src/arch/x86_64/serial.rs b/opentmk/opentmk/src/arch/x86_64/serial.rs index 2c2f2d8193..ee8fbc20fc 100644 --- a/opentmk/opentmk/src/arch/x86_64/serial.rs +++ b/opentmk/opentmk/src/arch/x86_64/serial.rs @@ -61,12 +61,6 @@ impl IoAccess for InstrIoAccess { } } -impl Default for InstrIoAccess { - fn default() -> Self { - InstrIoAccess - } -} - /// A writer for the UART COM Ports. pub struct Serial { io: T, @@ -74,19 +68,18 @@ pub struct Serial { mutex: Mutex<()>, } -impl Serial { +impl Serial { /// Initialize the serial port. - pub fn new(serial_port: SerialPort) -> Self { - let io = T::default(); - - // SAFETY: Writing these values to the serial device is safe. + pub const fn new(serial_port: SerialPort, io: T) -> Self { + Self { io, serial_port, mutex: Mutex::new(()) } + } + + pub fn init(&self) { unsafe { - io.outb(serial_port.value() + 1, 0x00); // Disable all interrupts - io.outb(serial_port.value() + 2, 0xC7); // Enable FIFO, clear them, with 14-byte threshold - io.outb(serial_port.value() + 4, 0x0F); + self.io.outb(self.serial_port.value() + 1, 0x00); // Disable all interrupts + self.io.outb(self.serial_port.value() + 2, 0xC7); // Enable FIFO, clear them, with 14-byte threshold + self.io.outb(self.serial_port.value() + 4, 0x0F); } - - Self { io, serial_port, mutex: Mutex::new(()) } } fn write_byte(&self, b: u8) { @@ -98,7 +91,7 @@ impl Serial { } } -impl fmt::Write for Serial { +impl fmt::Write for Serial { fn write_str(&mut self, s: &str) -> fmt::Result { let _guard = self.mutex.lock(); for &b in s.as_bytes() { diff --git a/opentmk/opentmk/src/arch/x86_64/tpm.rs b/opentmk/opentmk/src/arch/x86_64/tpm.rs deleted file mode 100644 index 4bf442f1f3..0000000000 --- a/opentmk/opentmk/src/arch/x86_64/tpm.rs +++ /dev/null @@ -1,1020 +0,0 @@ -//! # Bare Metal TPM Driver -//! -//! A kernel-mode TPM 2.0 driver written in Rust. -//! This module provides direct hardware access to TPM functionality without -//! requiring user-mode components or additional dependencies. -//! -//! ## Features -//! - Direct hardware access to TPM 2.0 devices -//! - Support for both MMIO and TIS (TPM Interface Specification) interfaces -//! - Safe Rust abstractions for TPM commands -//! - Minimizes unsafe code to hardware interaction boundaries -//! - No_std compatible for kernel environments -//! - Comprehensive logging via the `log` crate -//! - Uses alloc::vec::Vec for dynamic memory allocation - -#![no_std] -#![allow(dead_code)] // Remove this in production code - -extern crate alloc; - -use core::fmt; -use core::ptr::{read_volatile, write_volatile}; -use core::sync::atomic::{AtomicBool, Ordering}; -use log::{error, warn, info, debug, trace}; -use alloc::vec::Vec; - -use crate::arch::rtc::delay_sec; - -/// TPM command codes as defined in the TPM 2.0 specification -#[repr(u32)] -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub enum TpmCommandCode { - Startup = 0x00000144, - SelfTest = 0x00000143, - GetRandom = 0x0000017B, - GetCapability = 0x0000017A, - PCRExtend = 0x00000182, - PCRRead = 0x0000017E, - CreatePrimary = 0x00000131, - Create = 0x00000153, - Load = 0x00000157, - Sign = 0x0000015D, - VerifySignature = 0x0000015E, - // Add more command codes as needed -} - -impl fmt::Display for TpmCommandCode { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - TpmCommandCode::Startup => write!(f, "TPM_CC_Startup"), - TpmCommandCode::SelfTest => write!(f, "TPM_CC_SelfTest"), - TpmCommandCode::GetRandom => write!(f, "TPM_CC_GetRandom"), - TpmCommandCode::GetCapability => write!(f, "TPM_CC_GetCapability"), - TpmCommandCode::PCRExtend => write!(f, "TPM_CC_PCR_Extend"), - TpmCommandCode::PCRRead => write!(f, "TPM_CC_PCR_Read"), - TpmCommandCode::CreatePrimary => write!(f, "TPM_CC_CreatePrimary"), - TpmCommandCode::Create => write!(f, "TPM_CC_Create"), - TpmCommandCode::Load => write!(f, "TPM_CC_Load"), - TpmCommandCode::Sign => write!(f, "TPM_CC_Sign"), - TpmCommandCode::VerifySignature => write!(f, "TPM_CC_VerifySignature"), - } - } -} - -/// TPM interface types supported by this driver -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub enum TpmInterfaceType { - /// TPM Interface Specification (TIS) - Port I/O based - Tis, - /// Memory-mapped I/O interface - Mmio, - /// Command Response Buffer Interface - Crb, - /// Firmware TPM Interface - Fifo, -} - -impl fmt::Display for TpmInterfaceType { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - TpmInterfaceType::Tis => write!(f, "TIS"), - TpmInterfaceType::Mmio => write!(f, "MMIO"), - TpmInterfaceType::Crb => write!(f, "CRB"), - TpmInterfaceType::Fifo => write!(f, "FIFO"), - } - } -} - -/// TPM hardware address information -pub struct TpmAddress { - interface_type: TpmInterfaceType, - base_address: usize, -} - -/// Error types for TPM operations -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub enum TpmError { - Timeout, - BadParameter, - CommunicationFailure, - BufferTooSmall, - UnsupportedCommand, - HardwareFailure, - AuthFailure, - TpmResponseError(u32), - NotInitialized, - AllocationFailure, -} - -impl fmt::Display for TpmError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - TpmError::Timeout => write!(f, "TPM operation timed out"), - TpmError::BadParameter => write!(f, "Invalid parameter provided to TPM"), - TpmError::CommunicationFailure => write!(f, "Failed to communicate with TPM"), - TpmError::BufferTooSmall => write!(f, "Buffer too small for TPM operation"), - TpmError::UnsupportedCommand => write!(f, "Command not supported by TPM"), - TpmError::HardwareFailure => write!(f, "TPM hardware failure"), - TpmError::AuthFailure => write!(f, "TPM authorization failure"), - TpmError::TpmResponseError(code) => write!(f, "TPM response error: 0x{code:08X}"), - TpmError::NotInitialized => write!(f, "TPM driver not initialized"), - TpmError::AllocationFailure => write!(f, "Memory allocation failure"), - } - } -} - -/// TPM response structure for commands -#[repr(C, packed)] -pub struct TpmResponse { - pub tag: u16, - pub response_size: u32, - pub response_code: u32, - // Followed by command-specific response data -} - -/// Status of the TPM device -pub struct TpmStatus { - pub initialized: bool, - pub active: bool, - pub version_major: u8, - pub version_minor: u8, -} - -/// Memory buffer for TPM operations -pub struct TpmBuffer { - data: [u8; Self::MAX_SIZE], - len: usize, -} - -impl TpmBuffer { - const MAX_SIZE: usize = 4096; - - pub fn new() -> Self { - debug!("Creating new TpmBuffer with capacity {}", Self::MAX_SIZE); - Self { - data: [0; Self::MAX_SIZE], - len: 0, - } - } - - pub fn clear(&mut self) { - trace!("Clearing TpmBuffer (previous length: {})", self.len); - self.len = 0; - } - - pub fn as_slice(&self) -> &[u8] { - trace!("Accessing TpmBuffer as slice, length: {}", self.len); - &self.data[..self.len] - } - - pub fn as_mut_slice(&mut self) -> &mut [u8] { - trace!("Accessing TpmBuffer as mutable slice, length: {}", self.len); - &mut self.data[..self.len] - } - - pub fn write(&mut self, bytes: &[u8]) -> Result { - if self.len + bytes.len() > Self::MAX_SIZE { - error!("TpmBuffer write failed: buffer too small (current: {}, append: {}, max: {})", - self.len, bytes.len(), Self::MAX_SIZE); - return Err(TpmError::BufferTooSmall); - } - - trace!("Writing {} bytes to TpmBuffer at offset {}", bytes.len(), self.len); - self.data[self.len..self.len + bytes.len()].copy_from_slice(bytes); - self.len += bytes.len(); - - Ok(bytes.len()) - } - - pub fn read(&mut self, bytes: &mut [u8]) -> Result { - let to_read = core::cmp::min(bytes.len(), self.len); - trace!("Reading {} bytes from TpmBuffer (requested: {}, available: {})", - to_read, bytes.len(), self.len); - - bytes[..to_read].copy_from_slice(&self.data[..to_read]); - - // Shift remaining data to beginning of buffer - for i in 0..(self.len - to_read) { - self.data[i] = self.data[i + to_read]; - } - self.len -= to_read; - - Ok(to_read) - } - - pub fn len(&self) -> usize { - self.len - } - - pub fn is_empty(&self) -> bool { - self.len == 0 - } -} - -/// TIS (TPM Interface Specification) specific constants and register offsets -mod tis { - // TIS register space offsets - pub const ACCESS: usize = 0x0; - pub const STS: usize = 0x18; - pub const DATA_FIFO: usize = 0x24; - pub const INTERFACE_ID: usize = 0x30; - pub const INT_ENABLE: usize = 0x08; - pub const INT_VECTOR: usize = 0x0C; - pub const INT_STATUS: usize = 0x10; - - // TIS access register bits - pub const ACCESS_VALID: u8 = 0x80; - pub const ACCESS_ACTIVE_LOCALITY: u8 = 0x20; - pub const ACCESS_REQUEST_USE: u8 = 0x02; - pub const ACCESS_SEIZE: u8 = 0x04; - - // TIS status register bits - pub const STS_VALID: u8 = 0x80; - pub const STS_COMMAND_READY: u8 = 0x40; - pub const STS_DATA_AVAILABLE: u8 = 0x10; - pub const STS_EXPECT: u8 = 0x08; - pub const STS_GO: u8 = 0x20; - pub const STS_RESPONSE_RETRY: u8 = 0x02; -} - -/// CRB (Command Response Buffer) specific constants and register offsets -mod crb { - pub const CONTROL_AREA_REQUEST: usize = 0x40; - pub const CONTROL_AREA_STATUS: usize = 0x44; - pub const CONTROL_CANCEL: usize = 0x18; - pub const CONTROL_START: usize = 0x1C; - pub const COMMAND_BUFFER: usize = 0x80; - pub const RESPONSE_BUFFER: usize = 0x80; - - // CRB status register bits - pub const CRB_STATUS_IDLE: u32 = 0x00000001; - pub const CRB_STATUS_READY: u32 = 0x00000002; -} - -/// The main TPM driver structure -pub struct TpmDriver { - address: TpmAddress, - initialized: AtomicBool, - current_locality: u8, -} - -impl TpmDriver { - /// Create a new TPM driver instance with the specified interface - pub fn new(interface_type: TpmInterfaceType, base_address: usize) -> Self { - info!("Creating new TPM driver with {} interface at base address 0x{:X}", - interface_type, base_address); - - Self { - address: TpmAddress { - interface_type, - base_address, - }, - initialized: AtomicBool::new(false), - current_locality: 1, - } - } - - /// Initialize the TPM driver and hardware - pub fn initialize(&self) -> Result<(), TpmError> { - // Skip initialization if already done - if self.initialized.load(Ordering::SeqCst) { - debug!("TPM driver already initialized, skipping initialization"); - return Ok(()); - } - - info!("Initializing TPM driver with {} interface", self.address.interface_type); - - match self.address.interface_type { - TpmInterfaceType::Tis => { - debug!("Initializing TPM using TIS interface"); - self.initialize_tis()? - }, - TpmInterfaceType::Mmio => { - debug!("Initializing TPM using MMIO interface"); - self.initialize_mmio()? - }, - TpmInterfaceType::Crb => { - debug!("Initializing TPM using CRB interface"); - self.initialize_crb()? - }, - TpmInterfaceType::Fifo => { - debug!("Initializing TPM using FIFO interface"); - self.initialize_fifo()? - }, - } - - debug!("TPM interface initialized, sending startup command"); - - // Send TPM startup command - let mut cmd_buffer = TpmBuffer::new(); - self.build_startup_command(&mut cmd_buffer)?; - - debug!("Sending TPM_CC_Startup command, buffer size: {}", cmd_buffer.len()); - self.send_command(cmd_buffer.as_slice())?; - - info!("TPM driver successfully initialized"); - self.initialized.store(true, Ordering::SeqCst); - Ok(()) - } - - /// Initialize TPM using TIS interface - fn initialize_tis(&self) -> Result<(), TpmError> { - debug!("Requesting access to locality {}", self.current_locality); - - // Request access to locality 0 - self.write_tis_reg8(tis::ACCESS, tis::ACCESS_REQUEST_USE)?; - - // Wait for access grant - let mut timeout = 5; - debug!("Waiting for locality {} access grant", self.current_locality); - - while timeout > 0 { - let access = self.read_tis_reg8(tis::ACCESS)?; - trace!("TIS ACCESS register: 0x{:02X}", access); - - if (access & tis::ACCESS_ACTIVE_LOCALITY) != 0 { - debug!("Locality {} access granted", self.current_locality); - break; - } - timeout -= 1; - if timeout == 0 { - error!("Timeout waiting for locality {} access", self.current_locality); - return Err(TpmError::Timeout); - } - delay_sec(2); - } - - // Check if TPM is valid - let access = self.read_tis_reg8(tis::ACCESS)?; - trace!("TIS ACCESS register after grant: 0x{:02X}", access); - - if (access & tis::ACCESS_VALID) == 0 { - error!("TPM TIS interface not valid, ACCESS register: 0x{:02X}", access); - return Err(TpmError::HardwareFailure); - } - - debug!("Setting TPM to command ready state"); - // Set command ready - self.write_tis_reg8(tis::STS, tis::STS_COMMAND_READY)?; - - // Wait for command ready state - debug!("Waiting for command ready state"); - let mut timeout = 5; - while timeout > 0 { - let status = self.read_tis_reg8(tis::STS)?; - trace!("TIS STS register: 0x{:02X}", status); - - if (status & tis::STS_COMMAND_READY) != 0 { - debug!("TPM command ready state achieved"); - break; - } - timeout -= 1; - if timeout == 0 { - error!("Timeout waiting for command ready state"); - return Err(TpmError::Timeout); - } - // Small delay - delay_sec(2); - } - - info!("TPM TIS interface successfully initialized"); - Ok(()) - } - - /// Initialize TPM using MMIO interface - fn initialize_mmio(&self) -> Result<(), TpmError> { - debug!("Initializing TPM using MMIO interface (using TIS flow)"); - // This would be similar to TIS but with memory-mapped registers - // For simplicity, we'll use the TIS initialization flow - self.initialize_tis() - } - - /// Initialize TPM using CRB interface - fn initialize_crb(&self) -> Result<(), TpmError> { - debug!("Setting TPM CRB interface to idle state"); - // Set to idle state - self.write_crb_reg32(crb::CONTROL_AREA_REQUEST, 0x00)?; - - // Wait for TPM to become ready - debug!("Waiting for TPM CRB to enter idle state"); - let mut timeout = 1000; - while timeout > 0 { - let status = self.read_crb_reg32(crb::CONTROL_AREA_STATUS)?; - trace!("CRB status register: 0x{:08X}", status); - - if (status & crb::CRB_STATUS_IDLE) != 0 { - debug!("TPM CRB entered idle state"); - break; - } - timeout -= 1; - if timeout == 0 { - error!("Timeout waiting for CRB idle state"); - return Err(TpmError::Timeout); - } - - delay_sec(2); - } - - debug!("Requesting TPM CRB ready state"); - // Request command ready state - self.write_crb_reg32(crb::CONTROL_AREA_REQUEST, crb::CRB_STATUS_READY)?; - - // Wait for ready state - debug!("Waiting for TPM CRB to enter ready state"); - let mut timeout = 1000; - while timeout > 0 { - let status = self.read_crb_reg32(crb::CONTROL_AREA_STATUS)?; - trace!("CRB status register: 0x{:08X}", status); - - if (status & crb::CRB_STATUS_READY) != 0 { - debug!("TPM CRB entered ready state"); - break; - } - timeout -= 1; - if timeout == 0 { - error!("Timeout waiting for CRB ready state"); - return Err(TpmError::Timeout); - } - delay_sec(2); - } - - info!("TPM CRB interface successfully initialized"); - Ok(()) - } - - /// Initialize TPM using FIFO interface - fn initialize_fifo(&self) -> Result<(), TpmError> { - debug!("Initializing TPM using FIFO interface (using TIS flow)"); - // Similar to TIS for most FIFO implementations - self.initialize_tis() - } - - /// Build TPM startup command - fn build_startup_command(&self, buffer: &mut TpmBuffer) -> Result<(), TpmError> { - debug!("Building TPM_CC_Startup command"); - - // TPM 2.0 Startup command structure - // Tag: TPM_ST_NO_SESSIONS (0x8001) - // Command size: 12 bytes - // Command code: TPM_CC_Startup (0x00000144) - // Startup type: TPM_SU_CLEAR (0x0000) - - let startup_cmd = [ - 0x80, 0x01, // TPM_ST_NO_SESSIONS - 0x00, 0x00, 0x00, 0x0C, // Command size (12 bytes) - 0x00, 0x00, 0x01, 0x44, // TPM_CC_Startup - 0x00, 0x00 // TPM_SU_CLEAR - ]; - - trace!("Startup command bytes: {:02X?}", startup_cmd); - - buffer.write(&startup_cmd) - .map(|written| { - debug!("TPM_CC_Startup command built, size: {} bytes", written); - - }) - .map_err(|err| { - error!("Failed to build TPM_CC_Startup command: {}", err); - TpmError::BufferTooSmall - }) - } - - /// Send a raw command to the TPM - pub fn send_command(&self, command: &[u8]) -> Result, TpmError> { - if !self.initialized.load(Ordering::SeqCst) && command[6..10] != [0x00, 0x00, 0x01, 0x44] { - // Skip check for Startup command, which is sent during initialization - error!("TPM driver not initialized"); - return Err(TpmError::NotInitialized); - } - - debug!("Sending TPM command, size: {} bytes", command.len()); - trace!("Command header: {:02X?}", &command[0..10]); - - match self.address.interface_type { - TpmInterfaceType::Tis | TpmInterfaceType::Fifo => { - debug!("Using TIS/FIFO protocol for command"); - self.send_command_tis(command) - }, - TpmInterfaceType::Mmio => { - debug!("Using MMIO protocol for command"); - self.send_command_mmio(command) - }, - TpmInterfaceType::Crb => { - debug!("Using CRB protocol for command"); - self.send_command_crb(command) - }, - } - } - - /// Send a command using TIS interface - fn send_command_tis(&self, command: &[u8]) -> Result, TpmError> { - // Check if TPM is ready to receive command - let status = self.read_tis_reg8(tis::STS)?; - trace!("TIS STS register before send: 0x{:02X}", status); - - if (status & tis::STS_COMMAND_READY) == 0 { - error!("TPM not ready to receive command, STS: 0x{:02X}", status); - // TPM not ready, abort - return Err(TpmError::CommunicationFailure); - } - - debug!("Sending {} bytes to TPM FIFO", command.len()); - // Send the command data byte by byte to FIFO - for (i, &byte) in command.iter().enumerate() { - trace!("Writing byte {} to FIFO: 0x{:02X}", i, byte); - self.write_tis_reg8(tis::DATA_FIFO, byte)?; - } - - debug!("Command sent, issuing GO to execute"); - // Signal to TPM that command is complete and can be executed - self.write_tis_reg8(tis::STS, tis::STS_GO)?; - - // Wait for data to become available - debug!("Waiting for TPM response"); - let mut timeout = 2000; // Longer timeout for command execution - while timeout > 0 { - let status = self.read_tis_reg8(tis::STS)?; - trace!("TIS STS register while waiting: 0x{:02X}", status); - - if (status & tis::STS_DATA_AVAILABLE) != 0 { - debug!("TPM response data available"); - break; - } - timeout -= 1; - if timeout == 0 { - error!("Timeout waiting for TPM response"); - return Err(TpmError::Timeout); - } - delay_sec(2); - } - - debug!("Reading TPM response header"); - // Read response header to determine size - let mut response_header = [0u8; 10]; // TPM response header: tag(2) + size(4) + code(4) - for i in 0..response_header.len() { - response_header[i] = self.read_tis_reg8(tis::DATA_FIFO)?; - trace!("Read header byte {}: 0x{:02X}", i, response_header[i]); - } - - // Parse response size from header (bytes 2-5, big-endian) - let response_size = u32::from_be_bytes([ - response_header[2], response_header[3], - response_header[4], response_header[5] - ]) as usize; - - debug!("TPM response size: {} bytes", response_size); - - // Validate response size - if !(10..=4096).contains(&response_size) { - error!("Invalid TPM response size: {}", response_size); - return Err(TpmError::CommunicationFailure); - } - - // Create buffer for complete response - let mut response = Vec::with_capacity(response_size); - - // Add header to response - for &byte in &response_header { - response.push(byte); - } - - debug!("Reading remaining {} bytes of TPM response", response_size - 10); - // Read the rest of the response - for i in 10..response_size { - let byte = self.read_tis_reg8(tis::DATA_FIFO)?; - trace!("Read response byte {}: 0x{:02X}", i, byte); - response.push(byte); - } - - // Check response code - let rc = u32::from_be_bytes([ - response_header[6], response_header[7], - response_header[8], response_header[9] - ]); - - if rc != 0 { - warn!("TPM returned error code: 0x{:08X}", rc); - return Err(TpmError::TpmResponseError(rc)); - } - - debug!("TPM response successfully received, signaling command ready"); - // Signal to TPM that we're done reading - self.write_tis_reg8(tis::STS, tis::STS_COMMAND_READY)?; - - debug!("Command completed successfully, response size: {}", response.len()); - Ok(response) - } - - /// Send a command using MMIO interface - fn send_command_mmio(&self, command: &[u8]) -> Result, TpmError> { - debug!("MMIO command: using TIS implementation"); - // For simplicity, reuse TIS implementation as it's similar - self.send_command_tis(command) - } - - /// Send a command using CRB interface - fn send_command_crb(&self, command: &[u8]) -> Result, TpmError> { - // Ensure TPM is in ready state - let status = self.read_crb_reg32(crb::CONTROL_AREA_STATUS)?; - trace!("CRB status before command: 0x{:08X}", status); - - if (status & crb::CRB_STATUS_READY) == 0 { - error!("TPM CRB not in ready state, status: 0x{:08X}", status); - return Err(TpmError::CommunicationFailure); - } - - debug!("Writing {} bytes to CRB command buffer", command.len()); - // Write command to command buffer - for (i, &byte) in command.iter().enumerate() { - trace!("Writing byte {} to command buffer: 0x{:02X}", i, byte); - self.write_mmio_u8(crb::COMMAND_BUFFER + i, byte)?; - } - - debug!("Starting CRB command execution"); - // Start command execution - self.write_crb_reg32(crb::CONTROL_START, 1)?; - - // Wait for command completion - debug!("Waiting for CRB command completion"); - let mut timeout = 2000; // Longer timeout for command execution - while timeout > 0 { - let status = self.read_crb_reg32(crb::CONTROL_AREA_STATUS)?; - trace!("CRB status while waiting: 0x{:08X}", status); - - if (status & crb::CRB_STATUS_IDLE) != 0 { - debug!("TPM CRB command completed"); - break; - } - timeout -= 1; - if timeout == 0 { - error!("Timeout waiting for CRB command completion"); - return Err(TpmError::Timeout); - } - delay_sec(2); - } - - debug!("Reading TPM CRB response header"); - // Read response header to determine size - let mut response_header = [0u8; 10]; // TPM response header: tag(2) + size(4) + code(4) - for i in 0..response_header.len() { - response_header[i] = self.read_mmio_u8(crb::RESPONSE_BUFFER + i)?; - trace!("Read header byte {}: 0x{:02X}", i, response_header[i]); - } - - // Parse response size from header (bytes 2-5, big-endian) - let response_size = u32::from_be_bytes([ - response_header[2], response_header[3], - response_header[4], response_header[5] - ]) as usize; - - debug!("TPM CRB response size: {} bytes", response_size); - - // Validate response size - if !(10..=4096).contains(&response_size) { - error!("Invalid TPM CRB response size: {}", response_size); - return Err(TpmError::CommunicationFailure); - } - - // Create buffer for complete response - let mut response = Vec::with_capacity(response_size); - - // Add header to response - for &byte in &response_header { - response.push(byte); - } - - debug!("Reading remaining {} bytes of TPM CRB response", response_size - 10); - // Read the rest of the response - for i in 10..response_size { - let byte = self.read_mmio_u8(crb::RESPONSE_BUFFER + i)?; - trace!("Read response byte {}: 0x{:02X}", i, byte); - response.push(byte); - } - - // Check response code - let rc = u32::from_be_bytes([ - response_header[6], response_header[7], - response_header[8], response_header[9] - ]); - - if rc != 0 { - warn!("TPM returned error code: 0x{:08X}", rc); - return Err(TpmError::TpmResponseError(rc)); - } - - debug!("Returning TPM CRB to ready state"); - // Return to ready state - self.write_crb_reg32(crb::CONTROL_AREA_REQUEST, crb::CRB_STATUS_READY)?; - - debug!("CRB command completed successfully, response size: {}", response.len()); - Ok(response) - } - - /// Execute a TPM command and parse the response - pub fn execute_command(&self, command_code: TpmCommandCode, params: &[u8]) -> Result, TpmError> { - info!("Executing TPM command: {}", command_code); - debug!("Command parameters size: {} bytes", params.len()); - - // Build the command buffer - let mut cmd_buffer = TpmBuffer::new(); - - // TPM command header: tag(2) + size(4) + command_code(4) - let tag: u16 = 0x8001; // TPM_ST_NO_SESSIONS - let cmd_size: u32 = 10 + params.len() as u32; // header + params - - debug!("Building command with tag 0x{:04X}, size {} bytes", tag, cmd_size); - - // Write tag (big-endian) - cmd_buffer.write(&tag.to_be_bytes())?; - - // Write command size (big-endian) - cmd_buffer.write(&cmd_size.to_be_bytes())?; - - // Write command code (big-endian) - let code: u32 = command_code as u32; - debug!("Command code: 0x{:08X}", code); - cmd_buffer.write(&code.to_be_bytes())?; - - // Write command parameters - cmd_buffer.write(params)?; - - debug!("Command buffer built, total size: {} bytes", cmd_buffer.len()); - trace!("Command buffer: {:02X?}", cmd_buffer.as_slice()); - - // Send the command to TPM - let result = self.send_command(cmd_buffer.as_slice()); - - match &result { - Ok(response) => { - debug!("Command {} completed successfully, response size: {} bytes", - command_code, response.len()); - trace!("Response header: {:02X?}", &response[0..10]); - }, - Err(err) => { - warn!("Command {} failed: {}", command_code, err); - } - } - - result - } - - /// Read random bytes from the TPM - pub fn get_random(&self, num_bytes: u16) -> Result, TpmError> { - info!("Getting {} random bytes from TPM", num_bytes); - - // TPM2_GetRandom command structure - // After the header: - // bytes requested (2 bytes) - - // Prepare parameter buffer - let params = num_bytes.to_be_bytes(); - debug!("GetRandom parameter: 0x{:04X}", num_bytes); - - // Execute the command - let response = self.execute_command(TpmCommandCode::GetRandom, ¶ms)?; - - // Parse response - skip header (10 bytes) and size field (2 bytes) - if response.len() < 13 { - error!("Invalid GetRandom response length: {}", response.len()); - return Err(TpmError::CommunicationFailure); - } - - // Extract random bytes from response (skipping header and size) - let random_size = u16::from_be_bytes([response[10], response[11]]) as usize; - - debug!("Received {} random bytes from TPM", random_size); - - let mut random_bytes = Vec::with_capacity(random_size); - - for i in 0..random_size { - if 12 + i >= response.len() { - warn!("Random data truncated, expected {} bytes, got {}", - random_size, response.len() - 12); - break; - } - random_bytes.push(response[12 + i]); - } - - info!("Successfully retrieved {} random bytes", random_bytes.len()); - trace!("Random bytes: {:02X?}", random_bytes.as_slice()); - - Ok(random_bytes) - } - - /// Extend a PCR with provided data - pub fn pcr_extend(&self, pcr_index: u32, digest: &[u8]) -> Result<(), TpmError> { - info!("Extending PCR {} with {} bytes of digest data", pcr_index, digest.len()); - - // TPM2_PCR_Extend command requires authorization, which complicates the example - // This is a simplified version without proper authorization - - if digest.len() != 32 { - error!("Invalid digest length for PCR extend: {} (expected 32 for SHA-256)", digest.len()); - return Err(TpmError::BadParameter); // Assuming SHA-256 (32 bytes) - } - - // Prepare parameter buffer for PCR extend - let mut params = Vec::new(); - - debug!("Building PCR_Extend parameters"); - - // PCR index (4 bytes) - params.extend_from_slice(&pcr_index.to_be_bytes()); - debug!("Added PCR index: {}", pcr_index); - - // Authorization section placeholder (simplified) - // In a real implementation, this would include proper authorization - let auth_size: u32 = 9; // Minimal auth size - params.extend_from_slice(&auth_size.to_be_bytes()); - debug!("Added auth section size: {}", auth_size); - - params.push(0); // TPM_RS_PW - params.extend_from_slice(&[0, 0]); // nonce size = 0 - params.push(0); // session attributes - params.extend_from_slice(&[0, 0]); // auth size = 0 - debug!("Added simplified auth section"); - - // Count of hashes (1 byte) - params.push(1); - debug!("Added hash count: 1"); - - // Hash algorithm (2 bytes) - SHA-256 = 0x000B - params.extend_from_slice(&[0x00, 0x0B]); - debug!("Added hash algorithm: SHA-256 (0x000B)"); - - // Hash data - params.extend_from_slice(digest); - - debug!("Added digest data, total parameter size: {} bytes", params.len()); - trace!("PCR_Extend parameters: {:02X?}", params.as_slice()); - - // Execute command - let _response = self.execute_command(TpmCommandCode::PCRExtend, ¶ms)?; - - info!("PCR {} successfully extended", pcr_index); - Ok(()) - } - - /// Read PCR value - pub fn pcr_read(&self, pcr_index: u32) -> Result, TpmError> { - info!("Reading PCR {} value", pcr_index); - - // Prepare parameter buffer for PCR read - let mut params = Vec::new(); - - debug!("Building PCR_Read parameters"); - - // Count of PCRs to read (4 bytes) - just 1 - params.extend_from_slice(&1u32.to_be_bytes()); - debug!("Added PCR count: 1"); - - // PCR index (4 bytes) - params.extend_from_slice(&pcr_index.to_be_bytes()); - debug!("Added PCR index: {}", pcr_index); - - // Hash algorithm (2 bytes) - SHA-256 = 0x000B - params.extend_from_slice(&[0x00, 0x0B]); - debug!("Added hash algorithm: SHA-256 (0x000B)"); - debug!("PCR_Read parameters built, total size: {} bytes", params.len()); - trace!("PCR_Read parameters: {:02X?}", params.as_slice()); - - // Execute command - let response = self.execute_command(TpmCommandCode::PCRRead, ¶ms)?; - - debug!("PCR_Read response received, size: {} bytes", response.len()); - trace!("PCR_Read response header: {:02X?}", &response[0..14]); - - // Parse response to extract PCR value - // For simplicity, assuming SHA-256 and skipping detailed parsing - if response.len() < 14 { - error!("Invalid PCR_Read response length: {}", response.len()); - return Err(TpmError::CommunicationFailure); - } - - // Extract PCR value (assuming SHA-256 of 32 bytes) - let mut pcr_value = Vec::with_capacity(32); - for i in 0..32 { - if 14 + i >= response.len() { - warn!("PCR value truncated, expected 32 bytes, got {}", response.len() - 14); - break; - } - pcr_value.push(response[14 + i]); - } - - info!("Successfully read PCR {} value, {} bytes", pcr_index, pcr_value.len()); - trace!("PCR value: {:02X?}", pcr_value.as_slice()); - - Ok(pcr_value) - } - - /// Read TIS register (8-bit) - fn read_tis_reg8(&self, reg_offset: usize) -> Result { - let addr = self.address.base_address + reg_offset; - trace!("Reading TIS register at offset 0x{:X} (address 0x{:X})", reg_offset, addr); - - // Use unsafe for direct hardware access - let value = unsafe { - read_volatile(addr as *const u8) - }; - - trace!("TIS register 0x{:X} value: 0x{:02X}", reg_offset, value); - Ok(value) - } - - /// Write TIS register (8-bit) - fn write_tis_reg8(&self, reg_offset: usize, value: u8) -> Result<(), TpmError> { - let addr = self.address.base_address + reg_offset; - trace!("Writing TIS register at offset 0x{:X} (address 0x{:X}): 0x{:02X}", - reg_offset, addr, value); - - // Use unsafe for direct hardware access - unsafe { - write_volatile(addr as *mut u8, value); - } - Ok(()) - } - - /// Read CRB register (32-bit) - fn read_crb_reg32(&self, reg_offset: usize) -> Result { - let addr = self.address.base_address + reg_offset; - trace!("Reading CRB register at offset 0x{:X} (address 0x{:X})", reg_offset, addr); - - // Use unsafe for direct hardware access - let value = unsafe { - read_volatile(addr as *const u32) - }; - - trace!("CRB register 0x{:X} value: 0x{:08X}", reg_offset, value); - Ok(value) - } - - /// Write CRB register (32-bit) - fn write_crb_reg32(&self, reg_offset: usize, value: u32) -> Result<(), TpmError> { - let addr = self.address.base_address + reg_offset; - trace!("Writing CRB register at offset 0x{:X} (address 0x{:X}): 0x{:08X}", - reg_offset, addr, value); - - // Use unsafe for direct hardware access - unsafe { - write_volatile(addr as *mut u32, value); - } - Ok(()) - } - - /// Read byte from MMIO - fn read_mmio_u8(&self, offset: usize) -> Result { - let addr = self.address.base_address + offset; - trace!("Reading MMIO byte at offset 0x{:X} (address 0x{:X})", offset, addr); - - // Use unsafe for direct hardware access - let value = unsafe { - read_volatile(addr as *const u8) - }; - - trace!("MMIO byte 0x{:X} value: 0x{:02X}", offset, value); - Ok(value) - } - - /// Write byte to MMIO - fn write_mmio_u8(&self, offset: usize, value: u8) -> Result<(), TpmError> { - let addr = self.address.base_address + offset; - trace!("Writing MMIO byte at offset 0x{:X} (address 0x{:X}): 0x{:02X}", - offset, addr, value); - - // Use unsafe for direct hardware access - unsafe { - write_volatile(addr as *mut u8, value); - } - Ok(()) - } -} - -/// Example usage of the TPM driver -pub fn tpm_driver_example() -> Result<(), TpmError> { - info!("Starting TPM driver example"); - - // Create a new TPM driver with TIS interface - // Base address would typically be determined from hardware detection - info!("Creating TPM driver with TIS interface at base address 0xFED40000"); - let tpm = TpmDriver::new(TpmInterfaceType::Tis, 0xFED40000); - - // Initialize the TPM - info!("Initializing TPM driver"); - tpm.initialize()?; - - // Get 16 random bytes from TPM - info!("Requesting 16 random bytes from TPM"); - let random_bytes = tpm.get_random(16)?; - info!("Received {} random bytes from TPM", random_bytes.len()); - debug!("Random bytes: {:02X?}", random_bytes.as_slice()); - - // Read PCR 0 - info!("Reading PCR 0"); - let pcr_value = tpm.pcr_read(0)?; - info!("PCR 0 value retrieved, {} bytes", pcr_value.len()); - debug!("PCR 0 value: {:02X?}", pcr_value.as_slice()); - - info!("TPM driver example completed successfully"); - Ok(()) -} \ No newline at end of file diff --git a/opentmk/opentmk/src/context.rs b/opentmk/opentmk/src/context.rs index 19e9e6228f..9500aebafb 100644 --- a/opentmk/opentmk/src/context.rs +++ b/opentmk/opentmk/src/context.rs @@ -117,7 +117,8 @@ impl VpExecutor { /// /// The closure receives a mutable reference to the platform-specific /// type `T` that implements `VtlPlatformTrait`. - pub fn command(mut self, cmd: impl FnOnce(&mut T) + 'static) -> Self { + pub fn command(mut self, cmd: impl FnOnce(&mut T) + 'static + Send) -> Self + { self.cmd = Some(Box::new(cmd)); self } diff --git a/opentmk/opentmk/src/hypercall.rs b/opentmk/opentmk/src/hypercall.rs index e4c1b02d72..88c75cf80c 100644 --- a/opentmk/opentmk/src/hypercall.rs +++ b/opentmk/opentmk/src/hypercall.rs @@ -24,6 +24,7 @@ struct HvcallPage { buffer: [u8; HV_PAGE_SIZE as usize], } +#[inline(never)] pub fn invoke_hypercall_vtl(control: hvdef::hypercall::Control) { // SAFETY: the caller guarantees the safety of this operation. unsafe { @@ -687,6 +688,7 @@ impl HvCall { }) } + #[inline(never)] /// Invokes the HvCallVtlCall hypercall. pub fn vtl_call() { let control: hvdef::hypercall::Control = hvdef::hypercall::Control::new() @@ -695,6 +697,7 @@ impl HvCall { invoke_hypercall_vtl(control); } + #[inline(never)] /// Invokes the HvCallVtlReturn hypercall. pub fn vtl_return() { let control: hvdef::hypercall::Control = hvdef::hypercall::Control::new() diff --git a/opentmk/opentmk/src/platform/hypvctx.rs b/opentmk/opentmk/src/platform/hypvctx.rs index 45075c8c82..61df84de13 100644 --- a/opentmk/opentmk/src/platform/hypvctx.rs +++ b/opentmk/opentmk/src/platform/hypvctx.rs @@ -3,14 +3,17 @@ use alloc::{ boxed::Box, collections::{btree_map::BTreeMap, btree_set::BTreeSet, linked_list::LinkedList}, }; -use core::{alloc::Layout, ops::Range}; +use core::{alloc::Layout, arch::asm, ops::Range}; use hvdef::{ - hypercall::{HvInputVtl, InitialVpContextX64}, - Vtl, + hypercall::{self, HvInputVtl, InitialVpContextX64}, + HvRegisterValue, Vtl, }; use memory_range::MemoryRange; -use minimal_rt::arch::msr::{read_msr, write_msr}; +use minimal_rt::arch::{ + hypercall::HYPERCALL_PAGE, + msr::{read_msr, write_msr}, +}; use sync_nostd::Mutex; use crate::{ @@ -55,7 +58,8 @@ impl SecureInterceptPlatformTrait for HvTestCtx { /// hypervisor side notifications back to the guest. /// Returns [`TmkResult::Err`] if the allocation of the SIMP buffer fails. fn setup_secure_intercept(&mut self, interrupt_idx: u8) -> TmkResult<()> { - let layout = Layout::from_size_align(4096, ALIGNMENT).map_err(|_| TmkError(TmkErrorType::AllocationFailed))?; + let layout = Layout::from_size_align(4096, ALIGNMENT) + .map_err(|_| TmkError(TmkErrorType::AllocationFailed))?; let ptr = unsafe { alloc(layout) }; let gpn = (ptr as u64) >> 12; @@ -146,9 +150,7 @@ impl VirtualProcessorPlatformTrait for HvTestCtx { fn get_vp_count(&self) -> TmkResult { #[cfg(target_arch = "x86_64")] { - let cpuid = unsafe { core::arch::x86_64::__cpuid(1) }; - let result = cpuid.ebx; - Ok((result >> 16) & 0xFF) + Ok(4) } #[cfg(not(target_arch = "x86_64"))] @@ -171,6 +173,8 @@ impl VirtualProcessorPlatformTrait for HvTestCtx { Ok(()) } + + #[inline(never)] /// Ensure the target VP is running in the requested VTL and queue /// the command for execution. /// – If the VP is not yet running, it is started with a default @@ -186,7 +190,12 @@ impl VirtualProcessorPlatformTrait for HvTestCtx { if vtl >= Vtl::Vtl2 { return Err(TmkError(TmkErrorType::InvalidParameter)); } - + log::debug!( + "VP{}: Queued command for VP{} in VTL{:?}", + self.my_vp_idx, + vp_index, + vtl + ); let is_vp_running = self.vp_runing.get(&vp_index); if let Some(_running_vtl) = is_vp_running { log::debug!("both vtl0 and vtl1 are running for VP: {:?}", vp_index); @@ -201,7 +210,15 @@ impl VirtualProcessorPlatformTrait for HvTestCtx { }), Vtl::Vtl1, )); + log::info!("self addr: {:p}", self as *const _); self.switch_to_high_vtl(); + log::info!("self addr after switch: {:p}", self as *const _); + log::debug!( + "VP{}: Queued command for VP{} in VTL{:?}", + self.my_vp_idx, + vp_index, + vtl + ); self.vp_runing.insert(vp_index); } else { let (tx, rx) = sync_nostd::Channel::>::new().split(); @@ -245,6 +262,12 @@ impl VirtualProcessorPlatformTrait for HvTestCtx { } } + log::debug!( + "VP{}: Queued command for VP{} in VTL{:?}", + self.my_vp_idx, + vp_index, + vtl + ); cmdt() .lock() .get_mut(&vp_index) @@ -340,12 +363,61 @@ impl VtlPlatformTrait for HvTestCtx { /// Switch execution from the current (low) VTL to the next higher /// one (`vtl_call`). fn switch_to_high_vtl(&mut self) { + self.print_rsp(); HvCall::vtl_call(); + let rsp: u64; + unsafe { + asm!( + "mov {}, rsp", + out(reg) rsp, + ); + } + log::debug!("switched back to low VTL"); + // let reg = self + // .get_register(hvdef::HvAllArchRegisterName::VsmCodePageOffsets.0) + // .unwrap(); + // let reg = HvRegisterValue::from(reg); + // let offset = hvdef::HvRegisterVsmCodePageOffsets::from_bits(reg.as_u64()); + + // log::debug!("call_offset: {:?}", offset); + + // let call_offset = offset.call_offset(); + // unsafe { + // let call_address = &raw const HYPERCALL_PAGE as *const u8; + // let off_addr = call_address.add(call_offset.into()) as u64; + // asm!( + // " + // call {call_address}", + // in("rcx") 0x0, + // call_address = in(reg) off_addr, + // ); + // } } /// Return from a high VTL back to the low VTL (`vtl_return`). fn switch_to_low_vtl(&mut self) { + log::debug!("switching to low VTL"); HvCall::vtl_return(); + + // let reg = self + // .get_register(hvdef::HvAllArchRegisterName::VsmCodePageOffsets.0) + // .unwrap(); + // let reg = HvRegisterValue::from(reg); + // let offset = hvdef::HvRegisterVsmCodePageOffsets::from_bits(reg.as_u64()); + + // log::debug!("ret_offset: {:?}", offset); + + // let call_offset = offset.return_offset(); + // unsafe { + // let call_address = &raw const HYPERCALL_PAGE as *const u8; + // let off_addr = call_address.add(call_offset.into()) as u64; + // asm!( + // " + // call {call_address}", + // in("rcx") 0x0, + // call_address = in(reg) off_addr, + // ); + // } } } @@ -393,32 +465,55 @@ impl HvTestCtx { let mut cmd: Option> = None; { + log::info!("pop1"); let mut cmdt = cmdt().lock(); + log::debug!("pop2"); let d = cmdt.get_mut(&ctx.my_vp_idx); + log::debug!("pop3"); if let Some(d) = d { + log::debug!("pop4"); if !d.is_empty() { let (_c, v) = d.front().unwrap(); + log::debug!("pop5: vtl={:?}", v); + log::debug!("pop5: my_vtl={:?}", ctx.my_vtl); + log::debug!("pop5: vtl_={}", *v == Vtl::Vtl1); if *v == ctx.my_vtl { let (c, _v) = d.pop_front().unwrap(); cmd = Some(c); } else { vtl = Some(*v); } + + log::debug!("pop6: cmd_is_none={:?}", cmd.is_none()); } } } + log::debug!("pop7: vtl={:?}, cmd={:?}", vtl, cmd.is_some()); if let Some(vtl) = vtl { + log::debug!("switching to vtl {:?} for vp {}", vtl, ctx.my_vp_idx); if vtl == Vtl::Vtl0 { ctx.switch_to_low_vtl(); } else { ctx.switch_to_high_vtl(); } } + log::debug!("pop8: vtl={:?}, cmd={:?}", vtl, cmd.is_some()); if let Some(cmd) = cmd { + log::debug!( + "executing command on vp {} VTL{:?}", + ctx.my_vp_idx, + ctx.my_vtl + ); cmd(&mut ctx); + log::debug!( + "executed command on vp {} VTL{:?}", + ctx.my_vp_idx, + ctx.my_vtl + ); } + log::debug!("pop9"); } } @@ -451,6 +546,20 @@ impl HvTestCtx { vp_context.rsp = stack_top; Ok(vp_context) } + + // function to print the current register states for x64 + #[cfg(target_arch = "x86_64")] + #[inline(always)] + fn print_rsp(&self) { + let rsp: u64; + unsafe { + asm!( + "mov {}, rsp", + out(reg) rsp, + ); + } + log::debug!("Current RSP: 0x{:#x}", rsp); + } } impl From for TmkError { diff --git a/opentmk/opentmk/src/tests/hv_error_vp_start.rs b/opentmk/opentmk/src/tests/hv_error_vp_start.rs index 7de00416a9..9d0908cea5 100644 --- a/opentmk/opentmk/src/tests/hv_error_vp_start.rs +++ b/opentmk/opentmk/src/tests/hv_error_vp_start.rs @@ -16,7 +16,7 @@ where tmk_assert!(vp_count.is_ok(), "get_vp_count should succeed"); let vp_count = vp_count.unwrap(); - tmk_assert!(vp_count == 8, "vp count should be 8"); + tmk_assert!(vp_count == 4, "vp count should be 4"); // Testing BSP VTL1 Bringup { diff --git a/opentmk/opentmk/src/tests/hv_misc.rs b/opentmk/opentmk/src/tests/hv_misc.rs index 0f53f4df57..e6cc89c284 100644 --- a/opentmk/opentmk/src/tests/hv_misc.rs +++ b/opentmk/opentmk/src/tests/hv_misc.rs @@ -3,9 +3,9 @@ use alloc::{alloc::alloc, sync::Arc}; use core::{ alloc::{GlobalAlloc, Layout}, arch::asm, - cell::RefCell, + cell::{RefCell, UnsafeCell}, ops::Range, - sync::atomic::{AtomicI32, Ordering}, + sync::atomic::{AtomicBool, AtomicI32, Ordering}, }; use ::alloc::{boxed::Box, vec::Vec}; @@ -62,7 +62,7 @@ where let vp_count = ctx.get_vp_count(); tmk_assert!(vp_count.is_ok(), "get_vp_count should succeed"); let vp_count = vp_count.unwrap(); - tmk_assert!(vp_count == 8, "vp count should be 8"); + tmk_assert!(vp_count == 4, "vp count should be 8"); ctx.setup_interrupt_handler(); @@ -77,7 +77,6 @@ where log::info!("interrupt fired!"); let hv = HvTestCtx::new(); - hv.get_current_vp(); log::info!( "current vp from interrupt: {}", hv.get_current_vp().unwrap() @@ -113,39 +112,47 @@ where ctx.switch_to_low_vtl(); })); - ctx.queue_command_vp(VpExecutor::new(0, Vtl::Vtl1).command(move |ctx: &mut T| { - log::info!("successfully started running VTL1 on vp0."); - ctx.switch_to_low_vtl(); - })); log::info!("ctx ptr: {:p}", &ctx as *const _); let mut l = 0u64; unsafe { asm!("mov {}, rsp", out(reg) l) }; log::info!("rsp: 0x{:x}", l); - //log::info!("Attempting to read heap memory from vtl0"); + let (tx, rx) = Channel::new().split(); + + ctx.start_on_vp(VpExecutor::new(0x2, Vtl::Vtl1).command(move|ctx: &mut T| { + ctx.setup_interrupt_handler(); + ctx.setup_secure_intercept(0x30); - ctx.start_on_vp( - VpExecutor::new(0x2, Vtl::Vtl0) - .command(|ctx| unsafe { + log::info!("successfully started running VTL1 on vp2."); + })); + + ctx.start_on_vp(VpExecutor::new(0x2, Vtl::Vtl0).command( move |ctx: &mut T| unsafe { log::info!("successfully started running VTL0 on vp2."); - // let heapx = *HEAPX.borrow(); - // let val = *(heapx.add(10)); - // log::info!( - // "reading mutated heap memory from vtl0(it should not be 0xAA): 0x{:x}", - // val - // ); - // tmk_assert!( - // val != 0xAA, - // "heap memory should not be accessible from vtl0" - // ); - })); - log::info!("after ctx ptr: {:p}", &ctx as *const _); - unsafe { asm!("mov {}, rsp", out(reg) l) }; - log::info!("rsp: 0x{:x}", l); + ctx.queue_command_vp(VpExecutor::new(2, Vtl::Vtl1).command(move |ctx: &mut T| { + log::info!("after intercept successfully started running VTL1 on vp2."); + ctx.switch_to_low_vtl(); + })); + unsafe { + let heapx = *HEAPX.borrow(); + let val = *(heapx.add(10)); + log::info!( + "reading mutated heap memory from vtl0(it should not be 0xAA): 0x{:x}", + val + ); + tmk_assert!( + val != 0xAA, + "heap memory should not be accessible from vtl0" + ); + } + + tx.send(()); + })); + + rx.recv(); // let (mut tx, mut rx) = Channel::new(1); // { // let mut tx = tx.clone(); @@ -156,11 +163,6 @@ where // }, // )); // } - log::info!("ctx ptr: {:p}", &ctx as *const _); - let c = ctx.get_vp_count(); - tmk_assert!(c.is_ok(), "get_vp_count should succeed"); - let c = c.unwrap(); - tmk_assert!(c == 8, "vp count should be 8"); // rx.recv(); diff --git a/opentmk/opentmk/src/tests/hv_processor.rs b/opentmk/opentmk/src/tests/hv_processor.rs index a201d903bd..e39a95f5e4 100644 --- a/opentmk/opentmk/src/tests/hv_processor.rs +++ b/opentmk/opentmk/src/tests/hv_processor.rs @@ -2,13 +2,14 @@ use hvdef::Vtl; use sync_nostd::Channel; use crate::{ - context::{VirtualProcessorPlatformTrait, VpExecutor, VtlPlatformTrait}, + context::{InterruptPlatformTrait, VirtualProcessorPlatformTrait, VpExecutor, VtlPlatformTrait}, tmk_assert, }; +#[inline(never)] pub fn exec(ctx: &mut T) where - T: VtlPlatformTrait + VirtualProcessorPlatformTrait, + T: VtlPlatformTrait + VirtualProcessorPlatformTrait + InterruptPlatformTrait, { let r = ctx.setup_partition_vtl(Vtl::Vtl1); tmk_assert!(r.is_ok(), "setup_partition_vtl should succeed"); @@ -17,7 +18,13 @@ where tmk_assert!(vp_count.is_ok(), "get_vp_count should succeed"); let vp_count = vp_count.unwrap(); - tmk_assert!(vp_count == 8, "vp count should be 8"); + tmk_assert!(vp_count == 4, "vp count should be 4"); + + _ = ctx.setup_interrupt_handler(); + + _ = ctx.set_interrupt_idx(0x6, || { + loop{} + }); // Testing BSP VTL Bringup { diff --git a/opentmk/opentmk/src/tests/mod.rs b/opentmk/opentmk/src/tests/mod.rs index fd74377033..a97bdfa8b3 100644 --- a/opentmk/opentmk/src/tests/mod.rs +++ b/opentmk/opentmk/src/tests/mod.rs @@ -4,10 +4,10 @@ use crate::platform::hypvctx::HvTestCtx; mod hv_error_vp_start; mod hv_misc; mod hv_processor; -mod tpm_test; +mod processors_valid; pub fn run_test() { let mut ctx = HvTestCtx::new(); ctx.init().expect("failed to init on BSP"); - hv_misc::exec(&mut ctx); + processors_valid::exec(&mut ctx); } \ No newline at end of file diff --git a/opentmk/opentmk/src/tests/processors_valid.rs b/opentmk/opentmk/src/tests/processors_valid.rs new file mode 100644 index 0000000000..1a1332f958 --- /dev/null +++ b/opentmk/opentmk/src/tests/processors_valid.rs @@ -0,0 +1,33 @@ +use hvdef::Vtl; +use sync_nostd::Channel; + +use crate::{ + context::{InterruptPlatformTrait, VirtualProcessorPlatformTrait, VpExecutor, VtlPlatformTrait}, + tmk_assert, +}; + +#[inline(never)] +pub fn exec(ctx: &mut T) +where + T: VtlPlatformTrait + VirtualProcessorPlatformTrait + InterruptPlatformTrait, +{ + let r = ctx.setup_partition_vtl(Vtl::Vtl1); + tmk_assert!(r.is_ok(), "setup_partition_vtl should succeed"); + + let vp_count = ctx.get_vp_count(); + tmk_assert!(vp_count.is_ok(), "get_vp_count should succeed"); + + let vp_count = vp_count.unwrap(); + tmk_assert!(vp_count == 4, "vp count should be 4"); + + _ = ctx.setup_interrupt_handler(); + + _ = ctx.set_interrupt_idx(0x6, || { + loop{} + }); + + ctx.start_on_vp(VpExecutor::new(0, Vtl::Vtl1).command(move |ctx: &mut T| { + log::info!("successfully started running VTL1 on vp0."); + ctx.switch_to_low_vtl(); + })).expect("Failed to start on VP 0"); +} \ No newline at end of file diff --git a/opentmk/opentmk/src/tests/tpm_test.rs b/opentmk/opentmk/src/tests/tpm_test.rs deleted file mode 100644 index c9ecc4c06a..0000000000 --- a/opentmk/opentmk/src/tests/tpm_test.rs +++ /dev/null @@ -1,12 +0,0 @@ -use log::info; - -use crate::arch::tpm; - -pub fn exec(ctx: &mut T) { - let date = crate::arch::rtc::read_rtc(); - log::info!("Current RTC: {} UNIX epoch: {}", date, date.to_unix_epoch_sec()); - - let tpm = tpm::tpm_driver_example(); - info!("TPM driver example started {:?}", tpm); - -} \ No newline at end of file diff --git a/opentmk/opentmk/src/tmk_assert.rs b/opentmk/opentmk/src/tmk_assert.rs index ba6f735632..e3a86bc28a 100644 --- a/opentmk/opentmk/src/tmk_assert.rs +++ b/opentmk/opentmk/src/tmk_assert.rs @@ -61,11 +61,7 @@ where } pub(crate) fn write_str(s: &str) { - _ = crate::tmk_logger::LOGGER.get_writer() - .as_mut() - .map(|writer| { - writer.write_str(s) - }); + _ = crate::tmk_logger::LOGGER.get_writter().write_str(s); } #[macro_export] diff --git a/opentmk/opentmk/src/tmk_logger.rs b/opentmk/opentmk/src/tmk_logger.rs index 2a2e4c9b60..0e71aac800 100644 --- a/opentmk/opentmk/src/tmk_logger.rs +++ b/opentmk/opentmk/src/tmk_logger.rs @@ -46,40 +46,28 @@ pub(crate) fn format_log_string_to_json( } pub struct TmkLogger { - pub writer: T, + pub writter: T, } -impl TmkLogger>> +impl TmkLogger> where T: Write + Send, { - pub fn new_provider(provider: T) -> Self { + pub const fn new(provider: T) -> Self { TmkLogger { - writer: Mutex::new(Some(provider)), + writter: Mutex::new(provider), } } - pub const fn new() -> Self { - TmkLogger { - writer: Mutex::new(None), - } - } - - pub fn set_writer(&self, writter: T) { - self.writer - .lock() - .replace(writter); - } - - pub fn get_writer(&self) -> MutexGuard<'_, Option> + pub fn get_writter(&self) -> MutexGuard<'_, T> where T: Write + Send, { - self.writer.lock() + self.writter.lock() } } -impl log::Log for TmkLogger>> +impl log::Log for TmkLogger> where T: Write + Send, { @@ -95,23 +83,16 @@ where record.line().unwrap_or_default() ); let str = format_log_string_to_json(&str, &line, true, record.level()); - self.get_writer() - .as_mut() - .map(|writer| { - writer.write_str(&str) - }); + _ = self.writter.lock().write_str(str.as_str()); } fn flush(&self) {} } type SerialPortWriter = Serial; -pub static LOGGER: TmkLogger>> = TmkLogger::new(); +pub static LOGGER: TmkLogger> = TmkLogger::new(SerialPortWriter::new(SerialPort::COM2, InstrIoAccess)); pub fn init() -> Result<(), SetLoggerError> { - let serial = SerialPortWriter::new(SerialPort::COM2); - LOGGER.set_writer(serial); - log::set_logger(&LOGGER) .map(|()| log::set_max_level(log::LevelFilter::Debug)) } diff --git a/opentmk/sync/src/lib.rs b/opentmk/sync/src/lib.rs index bd69b046c0..30e6ae2777 100644 --- a/opentmk/sync/src/lib.rs +++ b/opentmk/sync/src/lib.rs @@ -222,7 +222,7 @@ impl Sender { } impl Receiver { - /// Tries to receive an element from the front of the queue without blocking + /// Tries to receive an element from the front of the queue while blocking /// Returns Ok(value) if successful, Err(RecvError) otherwise pub fn recv(&self) -> Result { loop { From 48d300acbfa3947ab8ba26fe8839a95b3c4b3ffe Mon Sep 17 00:00:00 2001 From: Mayank Kumar Date: Tue, 29 Jul 2025 17:03:26 +0000 Subject: [PATCH 14/23] chore: add tpm working --- Cargo.lock | 6 +- opentmk/opentmk/Cargo.toml | 11 +- opentmk/opentmk/build_deploy.sh | 5 +- .../arch/x86_64/interrupt_handler_register.rs | 4 + opentmk/opentmk/src/arch/x86_64/io.rs | 29 +- opentmk/opentmk/src/arch/x86_64/mod.rs | 3 +- opentmk/opentmk/src/arch/x86_64/tpm.rs | 137 + opentmk/opentmk/src/context.rs | 4 + opentmk/opentmk/src/devices/mod.rs | 1 + opentmk/opentmk/src/devices/tpm/mod.rs | 1 + opentmk/opentmk/src/devices/tpm/protocol.rs | 4061 +++++++++++++++++ opentmk/opentmk/src/hypercall.rs | 297 +- opentmk/opentmk/src/main.rs | 1 + opentmk/opentmk/src/platform/hypvctx.rs | 263 +- .../opentmk/src/tests/hv_cvm_mem_protect.rs | 170 + opentmk/opentmk/src/tests/hv_misc.rs | 161 +- opentmk/opentmk/src/tests/hv_tpm.rs | 167 + opentmk/opentmk/src/tests/mod.rs | 8 +- opentmk/opentmk/src/tests/processors_valid.rs | 33 - opentmk/opentmk/src/uefi/mod.rs | 2 +- 20 files changed, 5145 insertions(+), 219 deletions(-) create mode 100644 opentmk/opentmk/src/arch/x86_64/tpm.rs create mode 100644 opentmk/opentmk/src/devices/mod.rs create mode 100644 opentmk/opentmk/src/devices/tpm/mod.rs create mode 100644 opentmk/opentmk/src/devices/tpm/protocol.rs create mode 100644 opentmk/opentmk/src/tests/hv_cvm_mem_protect.rs create mode 100644 opentmk/opentmk/src/tests/hv_tpm.rs delete mode 100644 opentmk/opentmk/src/tests/processors_valid.rs diff --git a/Cargo.lock b/Cargo.lock index 348e235055..0674d71293 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3199,9 +3199,9 @@ dependencies = [ [[package]] name = "iced-x86" -version = "1.20.0" +version = "1.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdd366a53278429c028367e0ba22a46cab6d565a57afb959f06e92c7a69e7828" +checksum = "7c447cff8c7f384a7d4f741cfcff32f75f3ad02b406432e8d6c878d56b1edf6b" dependencies = [ "lazy_static", ] @@ -4790,6 +4790,7 @@ dependencies = [ "bitfield-struct", "cfg-if", "hvdef", + "iced-x86", "lazy_static", "linked_list_allocator", "log", @@ -4799,6 +4800,7 @@ dependencies = [ "serde", "serde_json", "sync_nostd", + "thiserror 2.0.0", "uefi", "x86_64", "x86defs", diff --git a/opentmk/opentmk/Cargo.toml b/opentmk/opentmk/Cargo.toml index 65ec92bfdd..c120d08e84 100644 --- a/opentmk/opentmk/Cargo.toml +++ b/opentmk/opentmk/Cargo.toml @@ -19,12 +19,21 @@ memory_range.workspace = true minimal_rt.workspace = true serde = { version = "1.0", default-features = false, features = ["derive"]} serde_json = { version = "1.0", default-features = false, features = ["alloc"] } +thiserror.workspace = true uefi = { workspace = true, features = ["alloc"] } x86_64.workspace = true x86defs.workspace = true zerocopy.workspace = true sync_nostd.workspace = true + +[dependencies.iced-x86] +version = "1.21.0" +default-features = false +# See below for all features +features = ["decoder", "nasm", "no_std"] + + [lints] workspace = true @@ -37,7 +46,7 @@ opt-level = 0 # No optimizations (same as debug) debug = true # Include debug symbols debug-assertions = true # Enable debug assertions overflow-checks = true # Enable integer overflow checks -lto = "off" # Disable link-time optrimization +ltso = "offz" # Disable link-time optrimization codegen-units = 256 # Use more codegen units (faster compilation) [profile.release.package."*"] diff --git a/opentmk/opentmk/build_deploy.sh b/opentmk/opentmk/build_deploy.sh index 4443e9d101..8a23bf08fd 100755 --- a/opentmk/opentmk/build_deploy.sh +++ b/opentmk/opentmk/build_deploy.sh @@ -1,5 +1,4 @@ -RUST_BACKTRACE=1 cargo +nightly-2025-05-09 build -p opentmk --target x86_64-unknown-uefi --release #--target-dir ./target/x86_64-unknown-uefi/debug +RUST_BACKTRACE=1 CARGO_PROFILE_RELEASE_force_frame_pointers=yes cargo +nightly-2025-05-09 build -p opentmk --target x86_64-unknown-uefi --release #--target-dir ./target/x86_64-unknown-uefi/debug cargo xtask guest-test uefi --bootx64 ~/projects-local/openvmm/target/x86_64-unknown-uefi/release/opentmk.efi qemu-img convert -f raw -O vhdx ~/projects-local/openvmm/target/x86_64-unknown-uefi/release/opentmk.img ~/projects/opentmk.vhdx - -# +nightly-2025-05-09 \ No newline at end of file +#CARGO_PROFILE_RELEASE_OPT_LEVEL=0 \ No newline at end of file diff --git a/opentmk/opentmk/src/arch/x86_64/interrupt_handler_register.rs b/opentmk/opentmk/src/arch/x86_64/interrupt_handler_register.rs index 90d3559e6d..a07a3c9ebe 100644 --- a/opentmk/opentmk/src/arch/x86_64/interrupt_handler_register.rs +++ b/opentmk/opentmk/src/arch/x86_64/interrupt_handler_register.rs @@ -44,6 +44,10 @@ macro_rules! create_fn_divergent_create { }; } +static mut BACKUP_RSP: u64 = 0; + + + macro_rules! create_page_fault_fn { ($name:ident, $i: expr) => { extern "x86-interrupt" fn $name( diff --git a/opentmk/opentmk/src/arch/x86_64/io.rs b/opentmk/opentmk/src/arch/x86_64/io.rs index 979c97d273..56d60b616f 100644 --- a/opentmk/opentmk/src/arch/x86_64/io.rs +++ b/opentmk/opentmk/src/arch/x86_64/io.rs @@ -24,4 +24,31 @@ pub fn inb(port: u16) -> u8 { } } data -} \ No newline at end of file +} + +/// Read a double word from a port. +pub fn inl(port: u16) -> u32 { + let mut data; + // SAFETY: The caller has assured us this is safe. + unsafe { + asm! { + "in eax, dx", + in("dx") port, + out("eax") data, + } + } + data +} + +/// Write a double word to a port. +/// This is a no-op on x86. +pub fn outl(port: u16, data: u32) { + // SAFETY: The caller has assured us this is safe. + unsafe { + asm! { + "out dx, eax", + in("dx") port, + in("eax") data, + } + } +} diff --git a/opentmk/opentmk/src/arch/x86_64/mod.rs b/opentmk/opentmk/src/arch/x86_64/mod.rs index 8453659c75..69ea1d0adc 100644 --- a/opentmk/opentmk/src/arch/x86_64/mod.rs +++ b/opentmk/opentmk/src/arch/x86_64/mod.rs @@ -3,4 +3,5 @@ pub mod interrupt; mod interrupt_handler_register; pub mod serial; mod io; -pub mod rtc; \ No newline at end of file +pub mod rtc; +pub mod tpm; \ No newline at end of file diff --git a/opentmk/opentmk/src/arch/x86_64/tpm.rs b/opentmk/opentmk/src/arch/x86_64/tpm.rs new file mode 100644 index 0000000000..7fa3d39b5d --- /dev/null +++ b/opentmk/opentmk/src/arch/x86_64/tpm.rs @@ -0,0 +1,137 @@ +use zerocopy::IntoBytes; +use thiserror::Error; +use crate::devices::tpm::protocol::{protocol::{SelfTestCmd, TpmCommand}, SessionTagEnum, TpmCommandError}; + +pub const TPM_DEVICE_MMIO_REGION_BASE_ADDRESS: u64 = 0xfed40000; +pub const TPM_DEVICE_MMIO_REGION_SIZE: u64 = 0x70; + +pub const TPM_DEVICE_IO_PORT_RANGE_BEGIN: u16 = 0x1040; +pub const TPM_DEVICE_IO_PORT_RANGE_END: u16 = 0x1048; + +pub const TPM_DEVICE_IO_PORT_CONTROL_OFFSET: u16 = 0; +pub const TPM_DEVICE_IO_PORT_DATA_OFFSET: u16 = 4; + +pub const TPM_DEVICE_MMIO_PORT_REGION_BASE_ADDRESS: u64 = + TPM_DEVICE_MMIO_REGION_BASE_ADDRESS + 0x80; +pub const TPM_DEVICE_MMIO_PORT_CONTROL: u64 = + TPM_DEVICE_MMIO_PORT_REGION_BASE_ADDRESS + TPM_DEVICE_IO_PORT_CONTROL_OFFSET as u64; +pub const TPM_DEVICE_MMIO_PORT_DATA: u64 = + TPM_DEVICE_MMIO_PORT_REGION_BASE_ADDRESS + TPM_DEVICE_IO_PORT_DATA_OFFSET as u64; +pub const TPM_DEVICE_MMIO_PORT_REGION_SIZE: u64 = 0x8; + +pub struct Tpm<'a> { + command_buffer: Option<&'a mut [u8]>, + response_buffer: Option<&'a mut [u8]>, +} + +impl<'a> Tpm<'a> { + pub fn new() -> Tpm<'a> { + Tpm { + command_buffer: None, + response_buffer: None, + } + } + + pub fn set_command_buffer(&mut self, buffer: &'a mut [u8]) { + self.command_buffer = Some(buffer); + } + + pub fn set_response_buffer(&mut self, buffer: &'a mut [u8]) { + self.response_buffer = Some(buffer); + } + + #[cfg(target_arch = "x86_64")] + pub fn get_control_port(command: u32) -> u32 { + let control_port = TPM_DEVICE_IO_PORT_RANGE_BEGIN+TPM_DEVICE_IO_PORT_CONTROL_OFFSET; + let data_port = TPM_DEVICE_IO_PORT_RANGE_BEGIN+TPM_DEVICE_IO_PORT_DATA_OFFSET; + super::io::outl(control_port, command); + super::io::inl(data_port) + } + + pub fn get_tcg_protocol_version() -> u32 { + Tpm::get_control_port(64) + } + + pub fn map_shared_memory(gpa: u32) -> u32 { + let control_port = TPM_DEVICE_IO_PORT_RANGE_BEGIN+TPM_DEVICE_IO_PORT_CONTROL_OFFSET; + let data_port = TPM_DEVICE_IO_PORT_RANGE_BEGIN+TPM_DEVICE_IO_PORT_DATA_OFFSET; + super::io::outl(control_port, 0x1); + super::io::outl(data_port, gpa); + super::io::outl(control_port, 0x2); + super::io::inl(data_port) + } + + pub fn copy_to_command_buffer(&mut self, buffer: &[u8]) { + self.command_buffer + .as_mut() + .unwrap()[..buffer.len()] + .copy_from_slice(buffer); + } + + pub fn copy_from_response_buffer(&mut self, buffer: &mut [u8]) { + buffer.copy_from_slice(self.response_buffer.as_ref().unwrap()); + } + + pub fn execute_command() { + let command_exec_mmio_addr = TPM_DEVICE_MMIO_REGION_BASE_ADDRESS + 0x4c; + let command_exec_mmio_ptr = command_exec_mmio_addr as *mut u32; + + unsafe { + *command_exec_mmio_ptr = 0x1; + } + + while unsafe { *command_exec_mmio_ptr } == 0x1 { + unsafe { + core::arch::x86_64::_mm_pause(); + } + } + } + + pub fn run_command(&mut self, buffer: &[u8]) -> [u8; 4096] { + assert!(buffer.len() <= 4096); + self.copy_to_command_buffer(buffer); + + let command_exec_mmio_addr = TPM_DEVICE_MMIO_REGION_BASE_ADDRESS + 0x4c; + let command_exec_mmio_ptr = command_exec_mmio_addr as *mut u32; + + unsafe { + *command_exec_mmio_ptr = 0x1; + } + + while unsafe { *command_exec_mmio_ptr } == 0x1 { + unsafe { + core::arch::x86_64::_mm_pause(); + } + } + + let mut response = [0; 4096]; + response.copy_from_slice(self.response_buffer.as_ref().unwrap()); + response + } + + pub fn self_test(&mut self) -> Result<(), TpmCommandError> { + let session_tag = SessionTagEnum::NoSessions; + let cmd = SelfTestCmd::new(session_tag.into(), true); + let response = self.run_command(cmd.as_bytes()); + + match SelfTestCmd::base_validate_reply(&response, session_tag) { + Err(error) => Err(TpmCommandError::InvalidResponse(error)), + Ok((res, false)) => Err(TpmCommandError::TpmCommandFailed { + response_code: res.header.response_code.get(), + })?, + Ok((_res, true)) => Ok(()), + } + } + +} + +pub struct TpmUtil; +impl TpmUtil { + pub fn get_self_test_cmd() -> [u8; 4096] { + let session_tag = SessionTagEnum::NoSessions; + let cmd = SelfTestCmd::new(session_tag.into(), true); + let mut buffer = [0; 4096]; + buffer[..cmd.as_bytes().len()].copy_from_slice(cmd.as_bytes()); + buffer + } +} \ No newline at end of file diff --git a/opentmk/opentmk/src/context.rs b/opentmk/opentmk/src/context.rs index 9500aebafb..27e8ee559c 100644 --- a/opentmk/opentmk/src/context.rs +++ b/opentmk/opentmk/src/context.rs @@ -92,6 +92,10 @@ pub trait VtlPlatformTrait { /// Switches the current hardware thread back to the lower privileged VTL. fn switch_to_low_vtl(&mut self); + + fn set_vp_state_with_vtl(&mut self, register_index: u32, value: u64, vtl: Vtl) -> TmkResult<()>; + + fn get_vp_state_with_vtl(&mut self, register_index: u32, vtl: Vtl) -> TmkResult; } pub trait X64PlatformTrait {} diff --git a/opentmk/opentmk/src/devices/mod.rs b/opentmk/opentmk/src/devices/mod.rs new file mode 100644 index 0000000000..210e1097ee --- /dev/null +++ b/opentmk/opentmk/src/devices/mod.rs @@ -0,0 +1 @@ +pub mod tpm; \ No newline at end of file diff --git a/opentmk/opentmk/src/devices/tpm/mod.rs b/opentmk/opentmk/src/devices/tpm/mod.rs new file mode 100644 index 0000000000..c8b4a9f7df --- /dev/null +++ b/opentmk/opentmk/src/devices/tpm/mod.rs @@ -0,0 +1 @@ +pub mod protocol; \ No newline at end of file diff --git a/opentmk/opentmk/src/devices/tpm/protocol.rs b/opentmk/opentmk/src/devices/tpm/protocol.rs new file mode 100644 index 0000000000..c1c0b1d83b --- /dev/null +++ b/opentmk/opentmk/src/devices/tpm/protocol.rs @@ -0,0 +1,4061 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +//! TPM 2.0 Protocol types, as defined in the spec + +//! NOTE: once the `tpm-rs` project matures, this hand-rolled code should be *deleted* and +//! replaced with types from that `tpm-rs` project. + +use self::packed_nums::*; +use bitfield_struct::bitfield; +use thiserror::Error; +use zerocopy::FromBytes; +use zerocopy::FromZeros; +use zerocopy::Immutable; +use zerocopy::IntoBytes; +use zerocopy::KnownLayout; +use alloc::vec::Vec; + +#[allow(non_camel_case_types)] +mod packed_nums { + pub type u16_be = zerocopy::U16; + pub type u32_be = zerocopy::U32; + pub type u64_be = zerocopy::U64; +} + +#[derive(Debug, Error)] +pub enum InvalidInput { + #[error("input data size too large for buffer - input size > upper bound: {0} > {1}")] + BufferSizeTooLarge(usize, usize), + #[error("input list length too long - input length > upper bound: {0} > {1}")] + PcrSelectionsLengthTooLong(usize, usize), + #[error("input payload size too large - input size > upper bound: {0} > {1}")] + NvPublicPayloadTooLarge(usize, usize), +} + +#[derive(Debug, Error)] +pub enum TpmProtoError { + #[error("input user_auth to TpmsSensitiveCreate is invalid")] + TpmsSensitiveCreateUserAuth(#[source] InvalidInput), + #[error("input data to TpmsSensitiveCreate is invalid")] + TpmsSensitiveCreateData(#[source] InvalidInput), + #[error("input auth_policy to TpmtPublic is invalid")] + TpmtPublicAuthPolicy(#[source] InvalidInput), + #[error("input unique to TpmtPublic is invalid")] + TpmtPublicUnique(#[source] InvalidInput), + #[error("input auth_policy to TpmsNvPublic is invalid")] + TpmsNvPublicAuthPolicy(#[source] InvalidInput), + #[error("input outside_info to CreatePrimary is invalid")] + CreatePrimaryOutsideInfo(#[source] InvalidInput), + #[error("input creation_pcr to CreatePrimary is invalid")] + CreatePrimaryCreationPcr(#[source] InvalidInput), + #[error("input auth to NvDefineSpace is invalid")] + NvDefineSpaceAuth(#[source] InvalidInput), + #[error("input public_info to NvDefineSpace is invalid")] + NvDefineSpacePublicInfo(#[source] InvalidInput), + #[error("input data to NvWrite is invalid")] + NvWriteData(#[source] InvalidInput), + #[error("input pcr_allocation to PcrAllocate is invalid")] + PcrAllocatePcrAllocation(#[source] InvalidInput), + #[error("input data to Import is invalid")] + ImportData(#[source] InvalidInput), +} + +#[derive(Debug, Error)] +pub enum ResponseValidationError { + #[error("response size is too small to fit into the buffer")] + ResponseSizeTooSmall, + #[error( + "size {size} specified in the response header does not meet the minimal size of command type {expected_size}, command succeeded: {command_succeeded}" + )] + HeaderResponseSizeMismatch { + size: u32, + expected_size: usize, + command_succeeded: bool, + }, + #[error( + "unexpected session tag {response_session_tag} specified in the response header, expected: {expected_session_tag}, command succeeded: {command_succeeded}" + )] + HeaderSessionTagMismatch { + response_session_tag: u16, + expected_session_tag: u16, + command_succeeded: bool, + }, +} + +#[repr(transparent)] +#[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes, PartialEq)] +pub struct ReservedHandle(pub u32_be); + +impl PartialEq for u32 { + fn eq(&self, other: &ReservedHandle) -> bool { + other.0.get() == *self + } +} + +impl ReservedHandle { + pub const fn new(kind: u8, offset: u32) -> ReservedHandle { + ReservedHandle(new_u32_be((kind as u32) << 24 | offset)) + } +} + +pub const TPM20_HT_NV_INDEX: u8 = 0x01; +pub const TPM20_HT_PERMANENT: u8 = 0x40; +pub const TPM20_HT_PERSISTENT: u8 = 0x81; + +pub const TPM20_RH_OWNER: ReservedHandle = ReservedHandle::new(TPM20_HT_PERMANENT, 0x01); +pub const TPM20_RH_PLATFORM: ReservedHandle = ReservedHandle::new(TPM20_HT_PERMANENT, 0x0c); +pub const TPM20_RH_ENDORSEMENT: ReservedHandle = ReservedHandle::new(TPM20_HT_PERMANENT, 0x0b); +// `TPM_RS_PW` (not `TPM_RH_PW`) +// See Table 28, Section 7.4, "Trusted Platform Module Library Part 2: Structures", revision 1.38. +pub const TPM20_RS_PW: ReservedHandle = ReservedHandle::new(TPM20_HT_PERMANENT, 0x09); + +// Based on Section 2.2, "Registry of Reserved TPM 2.0 Handles and Localities", version 1.1. +pub const NV_INDEX_RANGE_BASE_PLATFORM_MANUFACTURER: u32 = + (TPM20_HT_NV_INDEX as u32) << 24 | 0x400000; +pub const NV_INDEX_RANGE_BASE_TCG_ASSIGNED: u32 = (TPM20_HT_NV_INDEX as u32) << 24 | 0xc00000; + +// The suggested minimal size for the buffer in `TPM2B_MAX_BUFFER`. +// See Table 79, Section 10.4.8, "Trusted Platform Module Library Part 2: Structures", revision 1.38. +pub const MAX_DIGEST_BUFFER_SIZE: usize = 1024; + +#[repr(transparent)] +#[derive(Debug, Copy, Clone, IntoBytes, Immutable, KnownLayout, FromBytes)] +pub struct SessionTag(pub u16_be); + +impl PartialEq for u16 { + fn eq(&self, other: &SessionTag) -> bool { + other.0.get() == *self + } +} + +impl SessionTag { + const fn new(val: u16) -> SessionTag { + SessionTag(new_u16_be(val)) + } +} + + +#[derive(Error, Debug)] +pub enum TpmCommandError { + #[error("failed to execute the TPM command")] + TpmExecuteCommand, + #[error("invalid response from the TPM command")] + InvalidResponse(#[source] ResponseValidationError), + #[error("invalid input parameter for the TPM command")] + InvalidInputParameter(#[source] TpmProtoError), + #[error("TPM command failed, response code: {response_code:#x}")] + TpmCommandFailed { response_code: u32 }, + #[error("failed to create the TPM command struct")] + TpmCommandCreationFailed(#[source] TpmProtoError), +} + +#[derive(Debug, Copy, Clone)] +#[repr(u16)] +pub enum SessionTagEnum { + // No structure type specified + Null = 0x8000, + + // A command/response for a command defined in this specification. The + // command/response has no attached sessions. If a command has an + // error and the command tag value is either TPM_ST_NO_SESSIONS or + // TPM_ST_SESSIONS, then this tag value is used for the response code. + NoSessions = 0x8001, + + // A command/response for a command defined in this specification. The + // command/response has one or more attached sessions and the sessionOffset + // field is present. + Sessions = 0x8002, + AttestClock = 0x8014, + AttestCommandAudit = 0x8015, + AttestSessionAudit = 0x8016, + AttestCertify = 0x8017, + AttestQuote = 0x8018, + AttestTick = 0x8019, + AttestTickstamp = 0x801A, + AttestTransport = 0x801B, + AttestCreation = 0x801C, + AttestNv = 0x801D, + // Tickets + Creation = 0x8021, + Verified = 0x8022, + Auth = 0x8023, + Hashcheck = 0x8024, + + // Structure describing a Field Upgrade Policy + FuManifest = 0x8029, +} + +impl From for SessionTag { + fn from(x: SessionTagEnum) -> Self { + SessionTag::new(x as u16) + } +} + +impl SessionTagEnum { + pub fn from_u16(val: u16) -> Option { + let ret = match val { + 0x8000 => Self::Null, + 0x8001 => Self::NoSessions, + 0x8002 => Self::Sessions, + 0x8014 => Self::AttestClock, + 0x8015 => Self::AttestCommandAudit, + 0x8016 => Self::AttestSessionAudit, + 0x8017 => Self::AttestCertify, + 0x8018 => Self::AttestQuote, + 0x8019 => Self::AttestTick, + 0x801A => Self::AttestTickstamp, + 0x801B => Self::AttestTransport, + 0x801C => Self::AttestCreation, + 0x801D => Self::AttestNv, + 0x8021 => Self::Creation, + 0x8022 => Self::Verified, + 0x8023 => Self::Auth, + 0x8024 => Self::Hashcheck, + 0x8029 => Self::FuManifest, + _ => return None, + }; + Some(ret) + } +} + +#[repr(transparent)] +#[derive(Debug, Copy, Clone, IntoBytes, Immutable, KnownLayout, FromBytes, PartialEq)] +pub struct CommandCode(pub u32_be); + +impl PartialEq for u32 { + fn eq(&self, other: &CommandCode) -> bool { + other.0.get() == *self + } +} + +impl CommandCode { + const fn new(val: u32) -> CommandCode { + CommandCode(new_u32_be(val)) + } + + pub fn into_enum(self) -> Option { + CommandCodeEnum::from_u32(self.0.get()) + } +} + +#[allow(non_camel_case_types, clippy::upper_case_acronyms)] +#[derive(Debug, Clone, Copy, PartialEq)] +#[repr(u32)] +pub enum CommandCodeEnum { + NV_UndefineSpaceSpecial = 0x0000011f, + EvictControl = 0x00000120, + HierarchyControl = 0x00000121, + NV_UndefineSpace = 0x00000122, + ChangeEPS = 0x00000124, + ChangePPS = 0x00000125, + Clear = 0x00000126, + ClearControl = 0x00000127, + ClockSet = 0x00000128, + HierarchyChangeAuth = 0x00000129, + NV_DefineSpace = 0x0000012a, + PCR_Allocate = 0x0000012b, + PCR_SetAuthPolicy = 0x0000012c, + PP_Commands = 0x0000012d, + SetPrimaryPolicy = 0x0000012e, + FieldUpgradeStart = 0x0000012f, + ClockRateAdjust = 0x00000130, + CreatePrimary = 0x00000131, + NV_GlobalWriteLock = 0x00000132, + GetCommandAuditDigest = 0x00000133, + NV_Increment = 0x00000134, + NV_SetBits = 0x00000135, + NV_Extend = 0x00000136, + NV_Write = 0x00000137, + NV_WriteLock = 0x00000138, + DictionaryAttackLockReset = 0x00000139, + DictionaryAttackParameters = 0x0000013a, + NV_ChangeAuth = 0x0000013b, + PCR_Event = 0x0000013c, + PCR_Reset = 0x0000013d, + SequenceComplete = 0x0000013e, + SetAlgorithmSet = 0x0000013f, + SetCommandCodeAuditStatus = 0x00000140, + FieldUpgradeData = 0x00000141, + IncrementalSelfTest = 0x00000142, + SelfTest = 0x00000143, + Startup = 0x00000144, + Shutdown = 0x00000145, + StirRandom = 0x00000146, + ActivateCredential = 0x00000147, + Certify = 0x00000148, + PolicyNV = 0x00000149, + CertifyCreation = 0x0000014a, + Duplicate = 0x0000014b, + GetTime = 0x0000014c, + GetSessionAuditDigest = 0x0000014d, + NV_Read = 0x0000014e, + NV_ReadLock = 0x0000014f, + ObjectChangeAuth = 0x00000150, + PolicySecret = 0x00000151, + Rewrap = 0x00000152, + Create = 0x00000153, + ECDH_ZGen = 0x00000154, + HMAC = 0x00000155, + Import = 0x00000156, + Load = 0x00000157, + Quote = 0x00000158, + RSA_Decrypt = 0x00000159, + HMAC_Start = 0x0000015b, + SequenceUpdate = 0x0000015c, + Sign = 0x0000015d, + Unseal = 0x0000015e, + PolicySigned = 0x00000160, + ContextLoad = 0x00000161, + ContextSave = 0x00000162, + ECDH_KeyGen = 0x00000163, + EncryptDecrypt = 0x00000164, + FlushContext = 0x00000165, + LoadExternal = 0x00000167, + MakeCredential = 0x00000168, + NV_ReadPublic = 0x00000169, + PolicyAuthorize = 0x0000016a, + PolicyAuthValue = 0x0000016b, + PolicyCommandCode = 0x0000016c, + PolicyCounterTimer = 0x0000016d, + PolicyCpHash = 0x0000016e, + PolicyLocality = 0x0000016f, + PolicyNameHash = 0x00000170, + PolicyOR = 0x00000171, + PolicyTicket = 0x00000172, + ReadPublic = 0x00000173, + RSA_Encrypt = 0x00000174, + StartAuthSession = 0x00000176, + VerifySignature = 0x00000177, + ECC_Parameters = 0x00000178, + FirmwareRead = 0x00000179, + GetCapability = 0x0000017a, + GetRandom = 0x0000017b, + GetTestResult = 0x0000017c, + Hash = 0x0000017d, + PCR_Read = 0x0000017e, + PolicyPCR = 0x0000017f, + PolicyRestart = 0x00000180, + ReadClock = 0x00000181, + PCR_Extend = 0x00000182, + PCR_SetAuthValue = 0x00000183, + NV_Certify = 0x00000184, + EventSequenceComplete = 0x00000185, + HashSequenceStart = 0x00000186, + PolicyPhysicalPresence = 0x00000187, + PolicyDuplicationSelect = 0x00000188, + PolicyGetDigest = 0x00000189, + TestParms = 0x0000018a, + Commit = 0x0000018b, + PolicyPassword = 0x0000018c, + ZGen_2Phase = 0x0000018d, + EC_Ephemeral = 0x0000018e, + PolicyNvWritten = 0x0000018f, + PolicyTemplate = 0x00000190, + CreateLoaded = 0x00000191, + PolicyAuthorizeNV = 0x00000192, + EncryptDecrypt2 = 0x00000193, + AC_GetCapability = 0x00000194, + AC_Send = 0x00000195, + Policy_AC_SendSelect = 0x00000196, + CertifyX509 = 0x00000197, + ACT_SetTimeout = 0x00000198, +} + +impl From for CommandCode { + fn from(x: CommandCodeEnum) -> Self { + CommandCode::new(x as u32) + } +} + +impl CommandCodeEnum { + pub fn from_u32(val: u32) -> Option { + let ret = match val { + 0x0000011f => Self::NV_UndefineSpaceSpecial, + 0x00000120 => Self::EvictControl, + 0x00000121 => Self::HierarchyControl, + 0x00000122 => Self::NV_UndefineSpace, + 0x00000124 => Self::ChangeEPS, + 0x00000125 => Self::ChangePPS, + 0x00000126 => Self::Clear, + 0x00000127 => Self::ClearControl, + 0x00000128 => Self::ClockSet, + 0x00000129 => Self::HierarchyChangeAuth, + 0x0000012a => Self::NV_DefineSpace, + 0x0000012b => Self::PCR_Allocate, + 0x0000012c => Self::PCR_SetAuthPolicy, + 0x0000012d => Self::PP_Commands, + 0x0000012e => Self::SetPrimaryPolicy, + 0x0000012f => Self::FieldUpgradeStart, + 0x00000130 => Self::ClockRateAdjust, + 0x00000131 => Self::CreatePrimary, + 0x00000132 => Self::NV_GlobalWriteLock, + 0x00000133 => Self::GetCommandAuditDigest, + 0x00000134 => Self::NV_Increment, + 0x00000135 => Self::NV_SetBits, + 0x00000136 => Self::NV_Extend, + 0x00000137 => Self::NV_Write, + 0x00000138 => Self::NV_WriteLock, + 0x00000139 => Self::DictionaryAttackLockReset, + 0x0000013a => Self::DictionaryAttackParameters, + 0x0000013b => Self::NV_ChangeAuth, + 0x0000013c => Self::PCR_Event, + 0x0000013d => Self::PCR_Reset, + 0x0000013e => Self::SequenceComplete, + 0x0000013f => Self::SetAlgorithmSet, + 0x00000140 => Self::SetCommandCodeAuditStatus, + 0x00000141 => Self::FieldUpgradeData, + 0x00000142 => Self::IncrementalSelfTest, + 0x00000143 => Self::SelfTest, + 0x00000144 => Self::Startup, + 0x00000145 => Self::Shutdown, + 0x00000146 => Self::StirRandom, + 0x00000147 => Self::ActivateCredential, + 0x00000148 => Self::Certify, + 0x00000149 => Self::PolicyNV, + 0x0000014a => Self::CertifyCreation, + 0x0000014b => Self::Duplicate, + 0x0000014c => Self::GetTime, + 0x0000014d => Self::GetSessionAuditDigest, + 0x0000014e => Self::NV_Read, + 0x0000014f => Self::NV_ReadLock, + 0x00000150 => Self::ObjectChangeAuth, + 0x00000151 => Self::PolicySecret, + 0x00000152 => Self::Rewrap, + 0x00000153 => Self::Create, + 0x00000154 => Self::ECDH_ZGen, + 0x00000155 => Self::HMAC, + 0x00000156 => Self::Import, + 0x00000157 => Self::Load, + 0x00000158 => Self::Quote, + 0x00000159 => Self::RSA_Decrypt, + 0x0000015b => Self::HMAC_Start, + 0x0000015c => Self::SequenceUpdate, + 0x0000015d => Self::Sign, + 0x0000015e => Self::Unseal, + 0x00000160 => Self::PolicySigned, + 0x00000161 => Self::ContextLoad, + 0x00000162 => Self::ContextSave, + 0x00000163 => Self::ECDH_KeyGen, + 0x00000164 => Self::EncryptDecrypt, + 0x00000165 => Self::FlushContext, + 0x00000167 => Self::LoadExternal, + 0x00000168 => Self::MakeCredential, + 0x00000169 => Self::NV_ReadPublic, + 0x0000016a => Self::PolicyAuthorize, + 0x0000016b => Self::PolicyAuthValue, + 0x0000016c => Self::PolicyCommandCode, + 0x0000016d => Self::PolicyCounterTimer, + 0x0000016e => Self::PolicyCpHash, + 0x0000016f => Self::PolicyLocality, + 0x00000170 => Self::PolicyNameHash, + 0x00000171 => Self::PolicyOR, + 0x00000172 => Self::PolicyTicket, + 0x00000173 => Self::ReadPublic, + 0x00000174 => Self::RSA_Encrypt, + 0x00000176 => Self::StartAuthSession, + 0x00000177 => Self::VerifySignature, + 0x00000178 => Self::ECC_Parameters, + 0x00000179 => Self::FirmwareRead, + 0x0000017a => Self::GetCapability, + 0x0000017b => Self::GetRandom, + 0x0000017c => Self::GetTestResult, + 0x0000017d => Self::Hash, + 0x0000017e => Self::PCR_Read, + 0x0000017f => Self::PolicyPCR, + 0x00000180 => Self::PolicyRestart, + 0x00000181 => Self::ReadClock, + 0x00000182 => Self::PCR_Extend, + 0x00000183 => Self::PCR_SetAuthValue, + 0x00000184 => Self::NV_Certify, + 0x00000185 => Self::EventSequenceComplete, + 0x00000186 => Self::HashSequenceStart, + 0x00000187 => Self::PolicyPhysicalPresence, + 0x00000188 => Self::PolicyDuplicationSelect, + 0x00000189 => Self::PolicyGetDigest, + 0x0000018a => Self::TestParms, + 0x0000018b => Self::Commit, + 0x0000018c => Self::PolicyPassword, + 0x0000018d => Self::ZGen_2Phase, + 0x0000018e => Self::EC_Ephemeral, + 0x0000018f => Self::PolicyNvWritten, + 0x00000190 => Self::PolicyTemplate, + 0x00000191 => Self::CreateLoaded, + 0x00000192 => Self::PolicyAuthorizeNV, + 0x00000193 => Self::EncryptDecrypt2, + 0x00000194 => Self::AC_GetCapability, + 0x00000195 => Self::AC_Send, + 0x00000196 => Self::Policy_AC_SendSelect, + 0x00000197 => Self::CertifyX509, + 0x00000198 => Self::ACT_SetTimeout, + _ => return None, + }; + + Some(ret) + } +} + +const FLAG_FMT1: u32 = 0x0080; +const FLAG_VER1: u32 = 0x0100; +const FLAG_WARN: u32 = 0x0800 + FLAG_VER1; + +#[repr(u32)] +pub enum ResponseCode { + Success = 0x000, + /// The given handle value is not valid or cannot be used for this + /// command. + Value = FLAG_FMT1 + 0x004, + /// Hierarchy is not enabled or is not correct for the use. + Hierarchy = FLAG_FMT1 + 0x0005, + /// The handle is not correct for the use. + Handle = FLAG_FMT1 + 0x000B, + /// The authorization HMAC check failed. + AuthFail = FLAG_FMT1 + 0x000E, + /// Structure is the wrong size. + Size = FLAG_FMT1 + 0x0015, + /// The TPM was unable to unmarshal a value because there were not + /// enough bytes in the input buffer. + Insufficient = FLAG_FMT1 + 0x001A, + /// Integrity check fail. + Integrity = FLAG_FMT1 + 0x001F, + /// TPM is in failure mode. + Failure = FLAG_VER1 + 0x0001, + /// Use of an authorization session with a context command. + AuthContext = FLAG_VER1 + 0x0045, + /// The NV index is used before being initialized or the state saved by + /// TPM20_CC_Shutdown could not be restored. + NvUninitialized = FLAG_VER1 + 0x04A, + /// ... + Sensitive = FLAG_VER1 + 0x055, + /// Gap for session context ID is too large. + ContextGap = FLAG_WARN + 0x001, + /// Out of memory for object contexts. + ObjectMemory = FLAG_WARN + 0x002, + /// Out of memory for session contexts. + SessionMemory = FLAG_WARN + 0x003, + /// Out of shared object/session memory or need space for internal + /// operations. + Memory = FLAG_WARN + 0x004, + /// Out of session handles - a session must be flushed before a new + /// session may be created. + SessionHandles = FLAG_WARN + 0x005, + /// Out of object handles - the handle space for objects is depleted and + /// a reboot is required . + /// NOTE:This cannot occur on the reference implementation. + ObjectHandles = FLAG_WARN + 0x006, + /// The TPM has suspended operation on the command. Forward progress was + /// made and the command may be retried. + Yielded = FLAG_WARN + 0x008, + /// The command was cancelled. The command may be retried. + Cancelled = FLAG_WARN + 0x009, + /// TPM is performing self tests. + Testing = FLAG_WARN + 0x00A, + /// The TPM is rate-limiting accesses to prevent wearout of NV. + NvRate = FLAG_WARN + 0x020, + /// Commands are not being accepted because the TPM is in DA lockout + /// mode. + Lockout = FLAG_WARN + 0x021, + /// The TPM was not able to start the command. Retry might work. + Retry = FLAG_WARN + 0x022, + /// The command may require writing of NV and NV is not current + /// accessible. + NvUnavailable = FLAG_WARN + 0x023, + /// This value is reserved and shall not be returned by the TPM. + NotUsed = FLAG_WARN + 0x07F, + /// Add to a parameter-, handle-, or session-related error. + Rc1 = 0x100, +} + +impl ResponseCode { + pub fn from_u32(val: u32) -> Option { + let ret = match val { + x if x == ResponseCode::Success as u32 => ResponseCode::Success, + x if x == ResponseCode::Value as u32 => ResponseCode::Value, + x if x == ResponseCode::Hierarchy as u32 => ResponseCode::Hierarchy, + x if x == ResponseCode::Handle as u32 => ResponseCode::Handle, + x if x == ResponseCode::AuthFail as u32 => ResponseCode::AuthFail, + x if x == ResponseCode::Size as u32 => ResponseCode::Size, + x if x == ResponseCode::Insufficient as u32 => ResponseCode::Insufficient, + x if x == ResponseCode::Integrity as u32 => ResponseCode::Integrity, + x if x == ResponseCode::Failure as u32 => ResponseCode::Failure, + x if x == ResponseCode::AuthContext as u32 => ResponseCode::AuthContext, + x if x == ResponseCode::NvUninitialized as u32 => ResponseCode::NvUninitialized, + x if x == ResponseCode::Sensitive as u32 => ResponseCode::Sensitive, + x if x == ResponseCode::ContextGap as u32 => ResponseCode::ContextGap, + x if x == ResponseCode::ObjectMemory as u32 => ResponseCode::ObjectMemory, + x if x == ResponseCode::SessionMemory as u32 => ResponseCode::SessionMemory, + x if x == ResponseCode::Memory as u32 => ResponseCode::Memory, + x if x == ResponseCode::SessionHandles as u32 => ResponseCode::SessionHandles, + x if x == ResponseCode::ObjectHandles as u32 => ResponseCode::ObjectHandles, + x if x == ResponseCode::Yielded as u32 => ResponseCode::Yielded, + x if x == ResponseCode::Cancelled as u32 => ResponseCode::Cancelled, + x if x == ResponseCode::Testing as u32 => ResponseCode::Testing, + x if x == ResponseCode::NvRate as u32 => ResponseCode::NvRate, + x if x == ResponseCode::Lockout as u32 => ResponseCode::Lockout, + x if x == ResponseCode::Retry as u32 => ResponseCode::Retry, + x if x == ResponseCode::NvUnavailable as u32 => ResponseCode::NvUnavailable, + x if x == ResponseCode::NotUsed as u32 => ResponseCode::NotUsed, + _ => return None, + }; + Some(ret) + } +} + +#[repr(transparent)] +#[derive(Debug, Copy, Clone, IntoBytes, Immutable, KnownLayout, FromBytes, PartialEq)] +pub struct AlgId(pub u16_be); + +impl PartialEq for u16 { + fn eq(&self, other: &AlgId) -> bool { + other.0.get() == *self + } +} + +impl AlgId { + const fn new(val: u16) -> AlgId { + AlgId(new_u16_be(val)) + } +} + +#[allow(non_camel_case_types, clippy::upper_case_acronyms)] +#[derive(Debug)] +#[repr(u16)] +pub enum AlgIdEnum { + RSA = 0x0001, + SHA = 0x0004, + AES = 0x0006, + SHA256 = 0x000b, + SHA384 = 0x000c, + SHA512 = 0x000d, + NULL = 0x0010, + SM3_256 = 0x0012, + RSASSA = 0x0014, + CFB = 0x0043, +} + +impl From for AlgId { + fn from(x: AlgIdEnum) -> Self { + AlgId::new(x as u16) + } +} + +impl AlgIdEnum { + pub fn from_u16(val: u16) -> Option { + let ret = match val { + 0x0004 => Self::SHA, + 0x000b => Self::SHA256, + 0x000c => Self::SHA384, + 0x000d => Self::SHA512, + 0x0012 => Self::SM3_256, + _ => return None, + }; + + Some(ret) + } +} + +/// `TPMA_OBJECT` +#[repr(transparent)] +#[derive(Debug, Copy, Clone, IntoBytes, Immutable, KnownLayout, FromBytes, PartialEq)] +pub struct TpmaObject(pub u32_be); + +impl TpmaObject { + const fn new(val: u32) -> Self { + Self(new_u32_be(val)) + } +} + +impl From for TpmaObject { + fn from(x: TpmaObjectBits) -> Self { + let val: u32 = x.into(); + Self::new(val) + } +} + +impl From for TpmaObject { + fn from(x: u32) -> Self { + Self::new(x) + } +} + +#[bitfield(u32)] +pub struct TpmaObjectBits { + _reserved0: bool, + pub fixed_tpm: bool, + pub st_clear: bool, + _reserved1: bool, + pub fixed_parent: bool, + pub sensitive_data_origin: bool, + pub user_with_auth: bool, + pub admin_with_policy: bool, + #[bits(2)] + _reserved2: u8, + pub no_da: bool, + pub encrypted_duplication: bool, + #[bits(4)] + _reserved3: u8, + pub restricted: bool, + pub decrypt: bool, + pub sign_encrypt: bool, + #[bits(13)] + _reserved4: u16, +} + +/// `TPMA_NV` +#[repr(transparent)] +#[derive(Debug, Copy, Clone, IntoBytes, Immutable, KnownLayout, FromBytes, PartialEq)] +pub struct TpmaNv(pub u32_be); + +impl TpmaNv { + const fn new(val: u32) -> Self { + Self(new_u32_be(val)) + } +} + +impl From for TpmaNv { + fn from(x: TpmaNvBits) -> Self { + let val: u32 = x.into(); + Self::new(val) + } +} + +impl From for TpmaNv { + fn from(x: u32) -> Self { + Self::new(x) + } +} + +#[bitfield(u32)] +pub struct TpmaNvBits { + pub nv_ppwrite: bool, + pub nv_ownerwrite: bool, + pub nv_authwrite: bool, + pub nv_policywrite: bool, + // bits 7:4: `TPM_NT` + // 0001 - `tpm_nt_counter` + pub nt_counter: bool, + // 0010 - `tpm_nt_bits` + pub nt_bits: bool, + // 0100 - `tpm_nt_extend` + pub nt_extend: bool, + _unused0: bool, + // bits 9:8 are reserved + #[bits(2)] + _reserved1: u8, + pub nv_policy_delete: bool, + pub nv_writelocked: bool, + pub nv_writeall: bool, + pub nv_writedefine: bool, + pub nv_write_stclear: bool, + pub nv_globallock: bool, + pub nv_ppread: bool, + pub nv_ownerread: bool, + pub nv_authread: bool, + pub nv_policyread: bool, + // bits 24:20 are reserved + #[bits(5)] + _reserved2: u8, + pub nv_no_da: bool, + pub nv_orderly: bool, + pub nv_clear_stclear: bool, + pub nv_readlocked: bool, + pub nv_written: bool, + pub nv_platformcreate: bool, + pub nv_read_stclear: bool, +} + +/// Workaround to allow constructing a zerocopy U64 in a const context. +const fn new_u64_be(val: u64) -> u64_be { + u64_be::from_bytes(val.to_be_bytes()) +} + +/// Workaround to allow constructing a zerocopy U32 in a const context. +const fn new_u32_be(val: u32) -> u32_be { + u32_be::from_bytes(val.to_be_bytes()) +} + +/// Workaround to allow constructing a zerocopy U16 in a const context. +const fn new_u16_be(val: u16) -> u16_be { + u16_be::from_bytes(val.to_be_bytes()) +} + +/// TPM command / response definitions +pub mod protocol { + use super::*; + + /// Common structs shared between multiple command / response structs + pub mod common { + use super::*; + + #[repr(C)] + #[derive(Debug, IntoBytes, Immutable, KnownLayout, FromBytes)] + pub struct CmdHeader { + pub session_tag: SessionTag, + pub size: u32_be, + pub command_code: CommandCode, + } + + impl CmdHeader { + /// Construct a header for a fixed-size command + pub fn new( + session_tag: SessionTag, + command_code: CommandCode, + ) -> CmdHeader { + CmdHeader { + session_tag, + size: (size_of::() as u32).into(), + command_code, + } + } + } + + #[repr(C)] + #[derive(Debug, IntoBytes, Immutable, KnownLayout, FromBytes)] + pub struct ReplyHeader { + pub session_tag: u16_be, + pub size: u32_be, + pub response_code: u32_be, + } + + impl ReplyHeader { + /// Performs a few command-agnostic validation checks: + /// - Ensures the size matches the size_of the provided `FullReply` type + /// - Compares provided session_tag + /// + /// Returns Ok(bool) if the validation passes. The bool value indicates whether + /// the response_code is [`ResponseCode::Success`] or not. + /// Returns Err(ResponseValidationError) otherwise. + pub fn base_validation( + &self, + session_tag: SessionTag, + expected_size: u32, + ) -> Result { + // Response code other than Success indicates that the command fails + // See Section 6.2, "Trusted Platform Module Library Part 3: Commands", revision 1.38. + let command_succeeded = ResponseCode::from_u32(self.response_code.get()) + .map(|c| matches!(c, ResponseCode::Success)) + .unwrap_or(false); + + let (expected_tag, expected_size) = if command_succeeded { + (session_tag, expected_size as usize) + } else { + // If the command fails, the expected tag should be NoSessions and the minimal size + // of the response should be the size of the header. + // See Section 6.1, "Trusted Platform Module Library Part 3: Commands", revision 1.38. + // + // DEVNOTE: we do not handle the special case caused by sending unsupported commands where + // the session tag will be `TPM_RC_BAD_TAG` instead. + (SessionTagEnum::NoSessions.into(), size_of::()) + }; + + if self.session_tag.get() != expected_tag { + Err(ResponseValidationError::HeaderSessionTagMismatch { + response_session_tag: self.session_tag.get(), + expected_session_tag: session_tag.0.get(), + command_succeeded, + })? + } + + // Allow the size specified in the header to be equal to or larger than the expected size in case + // that the expected size does not take the authorization area into account. + if (self.size.get() as usize) < expected_size { + Err(ResponseValidationError::HeaderResponseSizeMismatch { + size: self.size.get(), + expected_size, + command_succeeded, + })? + } + + Ok(command_succeeded) + } + } + + #[repr(C)] + #[derive(Debug, IntoBytes, Immutable, KnownLayout, FromBytes)] + pub struct CmdAuth { + handle: ReservedHandle, + nonce_2b: u16_be, + session: u8, + auth_2b: u16_be, + } + + impl CmdAuth { + pub fn new(handle: ReservedHandle, nonce_2b: u16, session: u8, auth_2b: u16) -> Self { + CmdAuth { + handle, + nonce_2b: nonce_2b.into(), + session, + auth_2b: auth_2b.into(), + } + } + } + + #[repr(C)] + #[derive(Debug, IntoBytes, Immutable, KnownLayout, FromBytes)] + pub struct ReplyAuth { + pub nonce_2b: u16_be, + pub session: u8, + pub auth_2b: u16_be, + } + } + + use common::CmdHeader; + use common::ReplyHeader; + + /// Marker trait for a struct that corresponds to a TPM Command + pub trait TpmCommand: IntoBytes + FromBytes + Sized + Immutable + KnownLayout { + type Reply: TpmReply; + + fn base_validate_reply( + reply_buf: &[u8], + session_tag: impl Into, + ) -> Result<(Self::Reply, bool), ResponseValidationError> { + let res = Self::Reply::deserialize(reply_buf) + .ok_or(ResponseValidationError::ResponseSizeTooSmall)?; + let succeeded = res.base_validation(session_tag.into())?; + + Ok((res, succeeded)) + } + } + + /// Marker trait for a struct that corresponds to a TPM Reply + pub trait TpmReply: IntoBytes + FromBytes + Sized + Immutable + KnownLayout { + type Command: TpmCommand; + + fn base_validation( + &self, + session_tag: SessionTag, + ) -> Result { + // `Reply::deserialize` guarantees this should not fail + let header = ReplyHeader::ref_from_prefix(self.as_bytes()) + .expect("unexpected response size") + .0; // TODO: zerocopy: error (https://github.com/microsoft/openvmm/issues/759) + header.base_validation(session_tag, self.payload_size() as u32) + } + fn deserialize(bytes: &[u8]) -> Option; + fn payload_size(&self) -> usize; + } + + /// General type for TPM 2.0 sized buffers. + #[repr(C)] + #[derive(Debug, Copy, Clone, FromBytes, IntoBytes, Immutable, KnownLayout)] + pub struct Tpm2bBuffer { + pub size: u16_be, + // Use value that is large enough as the buffer size so that we + // only need to define one struct. + pub buffer: [u8; MAX_DIGEST_BUFFER_SIZE], + } + + impl Tpm2bBuffer { + /// Create a `Tpm2bBuffer` from a slice. + pub fn new(data: &[u8]) -> Result { + let size = data.len(); + if size > MAX_DIGEST_BUFFER_SIZE { + Err(InvalidInput::BufferSizeTooLarge( + size, + MAX_DIGEST_BUFFER_SIZE, + ))? + } + + let mut buffer = [0u8; MAX_DIGEST_BUFFER_SIZE]; + buffer[..size].copy_from_slice(data); + + Ok(Self { + size: new_u16_be(size as u16), + buffer, + }) + } + + pub fn serialize(self) -> Vec { + let mut buffer = Vec::new(); + + buffer.extend_from_slice(self.size.as_bytes()); + buffer.extend_from_slice(&self.buffer[..self.size.get() as usize]); + + buffer + } + + pub fn deserialize(bytes: &[u8]) -> Option { + let mut start = 0; + let mut end = size_of::(); + if bytes.len() < end { + return None; + } + + let size: u16 = u16_be::read_from_bytes(&bytes[start..end]).ok()?.into(); // TODO: zerocopy: simplify (https://github.com/microsoft/openvmm/issues/759) + if size as usize > MAX_DIGEST_BUFFER_SIZE { + return None; + } + + start = end; + end += size as usize; + if bytes.len() < end { + return None; + } + let mut buffer = [0u8; MAX_DIGEST_BUFFER_SIZE]; + buffer[..size as usize].copy_from_slice(&bytes[start..end]); + + Some(Self { + size: size.into(), + buffer, + }) + } + + pub fn payload_size(&self) -> usize { + let mut payload_size = 0; + + payload_size += size_of_val(&self.size); + payload_size += self.size.get() as usize; + + payload_size + } + } + + /// `TPML_PCR_SELECTION` + #[repr(C)] + #[derive(Debug, Copy, Clone, FromBytes, IntoBytes, Immutable, KnownLayout)] + pub struct TpmlPcrSelection { + pub count: u32_be, + pub pcr_selections: [PcrSelection; 5], + } + + impl TpmlPcrSelection { + pub fn new(pcr_selections: &[PcrSelection]) -> Result { + let count = pcr_selections.len(); + if count > 5 { + Err(InvalidInput::PcrSelectionsLengthTooLong(count, 5))? + } + + let mut base = [PcrSelection::new_zeroed(); 5]; + base[..count].copy_from_slice(pcr_selections); + + Ok(Self { + count: new_u32_be(count as u32), + pcr_selections: base, + }) + } + + pub fn serialize(self) -> Vec { + let mut buffer = Vec::new(); + + buffer.extend_from_slice(self.count.as_bytes()); + for i in 0..self.count.get() { + buffer.extend_from_slice(&self.pcr_selections[i as usize].serialize()); + } + + buffer + } + + pub fn deserialize(bytes: &[u8]) -> Option { + let mut start = 0; + let mut end = size_of::(); + + if bytes.len() < end { + return None; + } + + let count: u32 = u32_be::read_from_bytes(&bytes[start..end]).ok()?.into(); // TODO: zerocopy: simplify (https://github.com/microsoft/openvmm/issues/759) + if count > 5 { + return None; + } + + let mut pcr_selections = [PcrSelection::new_zeroed(); 5]; + for i in 0..count { + start = end; + pcr_selections[i as usize] = PcrSelection::deserialize(&bytes[start..])?; + end += pcr_selections[i as usize].payload_size(); + } + + Some(Self { + count: count.into(), + pcr_selections, + }) + } + + pub fn payload_size(&self) -> usize { + let mut payload_size = 0; + let count = self.count; + + payload_size += size_of_val(&count); + for i in 0..count.get() { + payload_size += self.pcr_selections[i as usize].payload_size(); + } + + payload_size + } + } + + /// `TPMS_SENSITIVE_CREATE` + #[repr(C)] + #[derive(Debug, Copy, Clone, FromBytes, IntoBytes, Immutable, KnownLayout)] + pub struct TpmsSensitiveCreate { + user_auth: Tpm2bBuffer, + data: Tpm2bBuffer, + } + + impl TpmsSensitiveCreate { + pub fn new(user_auth: &[u8], data: &[u8]) -> Result { + let user_auth = + Tpm2bBuffer::new(user_auth).map_err(TpmProtoError::TpmsSensitiveCreateUserAuth)?; + let data = Tpm2bBuffer::new(data).map_err(TpmProtoError::TpmsSensitiveCreateData)?; + Ok(Self { user_auth, data }) + } + + pub fn serialize(self) -> Vec { + let mut buffer = Vec::new(); + + buffer.extend_from_slice(&self.user_auth.serialize()); + buffer.extend_from_slice(&self.data.serialize()); + + buffer + } + + pub fn payload_size(&self) -> usize { + let mut payload_size = 0; + + payload_size += self.user_auth.payload_size(); + payload_size += self.data.payload_size(); + + payload_size + } + } + + /// `TPM2B_SENSITIVE_CREATE` + #[repr(C)] + #[derive(Debug, Copy, Clone, FromBytes, IntoBytes, Immutable, KnownLayout)] + pub struct Tpm2bSensitiveCreate { + size: u16_be, + sensitive: TpmsSensitiveCreate, + } + + impl Tpm2bSensitiveCreate { + pub fn new(sensitive: TpmsSensitiveCreate) -> Self { + let size = sensitive.payload_size() as u16; + Self { + size: size.into(), + sensitive, + } + } + + pub fn serialize(self) -> Vec { + let mut buffer = Vec::new(); + + buffer.extend_from_slice(self.size.as_bytes()); + buffer.extend_from_slice(&self.sensitive.serialize()); + + buffer + } + + pub fn payload_size(&self) -> usize { + let mut payload_size = 0; + let size = self.size; + + payload_size += size_of_val(&size); + payload_size += self.sensitive.payload_size(); + + payload_size + } + } + + /// `TPMT_RSA_SCHEME` + #[repr(C)] + #[derive(Debug, Copy, Clone, FromBytes, IntoBytes, Immutable, KnownLayout, PartialEq)] + pub struct TpmtRsaScheme { + scheme: AlgId, + hash_alg: AlgId, + } + + impl TpmtRsaScheme { + pub fn new(scheme: AlgId, hash_alg: Option) -> Self { + let hash_alg = hash_alg.map_or_else(|| AlgId::new(0), |v| v); + + Self { scheme, hash_alg } + } + + pub fn serialize(&self) -> Vec { + let mut buffer = Vec::new(); + + buffer.extend_from_slice(self.scheme.as_bytes()); + + // No parameters when algorithm is NULL + if self.scheme != AlgIdEnum::NULL.into() { + // Only support scheme with hash (e.g., RSASSA) for now + buffer.extend_from_slice(self.hash_alg.as_bytes()); + } + + buffer + } + + pub fn deserialize(bytes: &[u8]) -> Option { + let mut start = 0; + let mut end = size_of::(); + + if bytes.len() < end { + return None; + } + + let scheme = AlgId::read_from_prefix(&bytes[start..end]).ok()?.0; // TODO: zerocopy: use-rest-of-range, option-to-error (https://github.com/microsoft/openvmm/issues/759) + + let hash_alg = if scheme != AlgIdEnum::NULL.into() { + start = end; + end += size_of::(); + AlgId::read_from_prefix(&bytes[start..end]).ok()?.0 // TODO: zerocopy: use-rest-of-range, option-to-error (https://github.com/microsoft/openvmm/issues/759) + } else { + AlgId::new(0) + }; + + Some(Self { scheme, hash_alg }) + } + + pub fn payload_size(&self) -> usize { + let mut payload_size = 0; + + payload_size += size_of_val(&self.scheme); + + if self.scheme != AlgIdEnum::NULL.into() { + payload_size += size_of_val(&self.hash_alg); + } + + payload_size + } + } + + /// `TPMT_SYM_DEF_OBJECT` + #[repr(C)] + #[derive(Debug, Copy, Clone, FromBytes, IntoBytes, Immutable, KnownLayout, PartialEq)] + pub struct TpmtSymDefObject { + algorithm: AlgId, + key_bits: u16_be, + mode: AlgId, + } + + impl TpmtSymDefObject { + pub fn new(algorithm: AlgId, key_bits: Option, mode: Option) -> Self { + let key_bits = key_bits.map_or_else(|| new_u16_be(0), |v| v.into()); + let mode = mode.map_or_else(|| AlgId::new(0), |v| v); + + Self { + algorithm, + key_bits, + mode, + } + } + + pub fn serialize(&self) -> Vec { + let mut buffer = Vec::new(); + + buffer.extend_from_slice(self.algorithm.as_bytes()); + + // No parameters when algorithm is NULL + if self.algorithm != AlgIdEnum::NULL.into() { + buffer.extend_from_slice(self.key_bits.as_bytes()); + buffer.extend_from_slice(self.mode.as_bytes()); + } + + buffer + } + + pub fn deserialize(bytes: &[u8]) -> Option { + let mut start = 0; + let mut end = size_of::(); + + if bytes.len() < end { + return None; + } + + let algorithm = AlgId::read_from_prefix(&bytes[start..end]).ok()?.0; // TODO: zerocopy: use-rest-of-range, option-to-error (https://github.com/microsoft/openvmm/issues/759) + + let (key_bits, mode) = if algorithm != AlgIdEnum::NULL.into() { + start = end; + end += size_of::(); + let key_bits = u16_be::read_from_bytes(&bytes[start..end]).ok()?; // TODO: zerocopy: simplify (https://github.com/microsoft/openvmm/issues/759) + + start = end; + end += size_of::(); + let mode = AlgId::read_from_prefix(&bytes[start..end]).ok()?.0; // TODO: zerocopy: use-rest-of-range, option-to-error (https://github.com/microsoft/openvmm/issues/759) + + (key_bits, mode) + } else { + (new_u16_be(0), AlgId::new(0)) + }; + + Some(Self { + algorithm, + key_bits, + mode, + }) + } + + pub fn payload_size(&self) -> usize { + let mut payload_size = 0; + + payload_size += size_of_val(&self.algorithm); + + if self.algorithm != AlgIdEnum::NULL.into() { + payload_size += size_of_val(&self.key_bits); + payload_size += size_of_val(&self.mode); + } + + payload_size + } + } + + /// `TPMS_RSA_PARMS` + #[repr(C)] + #[derive(Debug, Copy, Clone, FromBytes, IntoBytes, Immutable, KnownLayout, PartialEq)] + pub struct TpmsRsaParams { + symmetric: TpmtSymDefObject, + scheme: TpmtRsaScheme, + key_bits: u16_be, + pub exponent: u32_be, + } + + impl TpmsRsaParams { + pub fn new( + symmetric: TpmtSymDefObject, + scheme: TpmtRsaScheme, + key_bits: u16, + exponent: u32, + ) -> Self { + Self { + symmetric, + scheme, + key_bits: key_bits.into(), + exponent: exponent.into(), + } + } + + pub fn serialize(&self) -> Vec { + let mut buffer = Vec::new(); + + buffer.extend_from_slice(&self.symmetric.serialize()); + buffer.extend_from_slice(&self.scheme.serialize()); + buffer.extend_from_slice(self.key_bits.as_bytes()); + buffer.extend_from_slice(self.exponent.as_bytes()); + + buffer + } + + pub fn deserialize(bytes: &[u8]) -> Option { + let mut start = 0; + let mut end = 0; + + let symmetric = TpmtSymDefObject::deserialize(&bytes[start..])?; + end += symmetric.payload_size(); + + start = end; + let scheme = TpmtRsaScheme::deserialize(&bytes[start..])?; + end += scheme.payload_size(); + + // TODO: zerocopy: as of zerocopy 0.8 this can be simplified with `read_from_bytes`....ok()?, to avoid (https://github.com/microsoft/openvmm/issues/759) + // manual size checks. Leaving this code as-is to reduce risk of the 0.7 -> 0.8 move. + start = end; + end += size_of::(); + if bytes.len() < end { + return None; + } + let key_bits = u16_be::read_from_bytes(&bytes[start..end]).ok()?; + + // TODO: zerocopy: as of zerocopy 0.8 this can be simplified with `read_from_bytes`....ok()?, to avoid (https://github.com/microsoft/openvmm/issues/759) + // manual size checks. Leaving this code as-is to reduce risk of the 0.7 -> 0.8 move. + start = end; + end += size_of::(); + if bytes.len() < end { + return None; + } + let exponent = u32_be::read_from_bytes(&bytes[start..end]).ok()?; + + Some(Self { + symmetric, + scheme, + key_bits, + exponent, + }) + } + + pub fn payload_size(&self) -> usize { + let mut payload_size = 0; + + payload_size += self.symmetric.payload_size(); + payload_size += self.scheme.payload_size(); + payload_size += size_of_val(&self.key_bits); + payload_size += size_of_val(&self.exponent); + + payload_size + } + } + + /// `TPMT_PUBLIC` + #[repr(C)] + #[derive(Debug, Copy, Clone, FromBytes, IntoBytes, Immutable, KnownLayout)] + pub struct TpmtPublic { + my_type: AlgId, + name_alg: AlgId, + object_attributes: TpmaObject, + auth_policy: Tpm2bBuffer, + // `TPMS_RSA_PARAMS` + pub parameters: TpmsRsaParams, + // `TPM2B_PUBLIC_KEY_RSA` + pub unique: Tpm2bBuffer, + } + + impl TpmtPublic { + pub fn new( + my_type: AlgId, + name_alg: AlgId, + object_attributes: TpmaObjectBits, + auth_policy: &[u8], + parameters: TpmsRsaParams, + unique: &[u8], + ) -> Result { + let auth_policy = + Tpm2bBuffer::new(auth_policy).map_err(TpmProtoError::TpmtPublicAuthPolicy)?; + let unique = Tpm2bBuffer::new(unique).map_err(TpmProtoError::TpmtPublicUnique)?; + Ok(Self { + my_type, + name_alg, + object_attributes: object_attributes.into(), + auth_policy, + parameters, + unique, + }) + } + + pub fn serialize(self) -> Vec { + let mut buffer = Vec::new(); + + buffer.extend_from_slice(self.my_type.as_bytes()); + buffer.extend_from_slice(self.name_alg.as_bytes()); + buffer.extend_from_slice(self.object_attributes.as_bytes()); + buffer.extend_from_slice(&self.auth_policy.serialize()); + buffer.extend_from_slice(&self.parameters.serialize()); + buffer.extend_from_slice(&self.unique.serialize()); + + buffer + } + + pub fn deserialize(bytes: &[u8]) -> Option { + let mut start = 0; + let mut end = size_of::(); + if bytes.len() < end { + return None; + } + let r#type = AlgId::read_from_prefix(&bytes[start..end]).ok()?.0; // TODO: zerocopy: use-rest-of-range, option-to-error (https://github.com/microsoft/openvmm/issues/759) + + start = end; + end += size_of::(); + if bytes.len() < end { + return None; + } + let name_alg = AlgId::read_from_prefix(&bytes[start..end]).ok()?.0; // TODO: zerocopy: use-rest-of-range, option-to-error (https://github.com/microsoft/openvmm/issues/759) + + start = end; + end += size_of::(); + if bytes.len() < end { + return None; + } + let object_attributes: u32 = u32_be::read_from_bytes(&bytes[start..end]).ok()?.into(); // TODO: zerocopy: simplify (https://github.com/microsoft/openvmm/issues/759) + + start = end; + let auth_policy = Tpm2bBuffer::deserialize(&bytes[start..])?; + end += auth_policy.payload_size(); + if bytes.len() < end { + return None; + } + + start = end; + let parameters = TpmsRsaParams::deserialize(&bytes[start..])?; + end += parameters.payload_size(); + + start = end; + let unique = Tpm2bBuffer::deserialize(&bytes[start..])?; + + Some(Self { + my_type: r#type, + name_alg, + object_attributes: object_attributes.into(), + auth_policy, + parameters, + unique, + }) + } + + pub fn payload_size(&self) -> usize { + let mut payload_size = 0; + + payload_size += size_of_val(&self.my_type); + payload_size += size_of_val(&self.name_alg); + payload_size += size_of_val(&self.object_attributes); + payload_size += self.auth_policy.payload_size(); + payload_size += self.parameters.payload_size(); + payload_size += self.unique.payload_size(); + + payload_size + } + } + + /// `TPM2B_PUBLIC` + #[repr(C)] + #[derive(Debug, Copy, Clone, FromBytes, IntoBytes, Immutable, KnownLayout)] + pub struct Tpm2bPublic { + pub size: u16_be, + pub public_area: TpmtPublic, + } + + impl Tpm2bPublic { + pub fn new(public_area: TpmtPublic) -> Self { + let size = public_area.payload_size() as u16; + Self { + size: size.into(), + public_area, + } + } + + pub fn serialize(self) -> Vec { + let mut buffer = Vec::new(); + + buffer.extend_from_slice(self.size.as_bytes()); + buffer.extend_from_slice(&self.public_area.serialize()); + + buffer + } + + pub fn deserialize(bytes: &[u8]) -> Option { + let mut start = 0; + let end = size_of::(); + + if bytes.len() < end { + return None; + } + + let size = u16_be::read_from_bytes(&bytes[start..end]).ok()?; // TODO: zerocopy: simplify (https://github.com/microsoft/openvmm/issues/759) + + start = end; + let public_area = TpmtPublic::deserialize(&bytes[start..])?; + + Some(Self { size, public_area }) + } + + pub fn payload_size(&self) -> usize { + let mut payload_size = 0; + + payload_size += size_of_val(&self.size); + payload_size += self.public_area.payload_size(); + + payload_size + } + } + + /// `TPMS_CREATION_DATA` + #[repr(C)] + #[derive(Debug, FromBytes, IntoBytes, Immutable, KnownLayout)] + pub struct TpmsCreationData { + pcr_select: TpmlPcrSelection, + pcr_digest: Tpm2bBuffer, + locality: u8, + parent_name_alg: AlgId, + parent_name: Tpm2bBuffer, + parent_qualified_name: Tpm2bBuffer, + outside_info: Tpm2bBuffer, + } + + impl TpmsCreationData { + pub fn deserialize(bytes: &[u8]) -> Option { + let mut start = 0; + let mut end = 0; + + let pcr_select = TpmlPcrSelection::deserialize(&bytes[start..])?; + end += pcr_select.payload_size(); + + start = end; + let pcr_digest = Tpm2bBuffer::deserialize(&bytes[start..])?; + end += pcr_digest.payload_size(); + + start = end; + end += size_of::(); + if bytes.len() < end { + return None; + } + let locality = bytes[start]; + + start = end; + end += size_of::(); + if bytes.len() < end { + return None; + } + let parent_name_alg = AlgId::read_from_prefix(&bytes[start..end]).ok()?.0; // TODO: zerocopy: use-rest-of-range, option-to-error (https://github.com/microsoft/openvmm/issues/759) + + start = end; + let parent_name = Tpm2bBuffer::deserialize(&bytes[start..])?; + end += parent_name.payload_size(); + + start = end; + let parent_qualified_name = Tpm2bBuffer::deserialize(&bytes[start..])?; + end += parent_qualified_name.payload_size(); + + start = end; + let outside_info = Tpm2bBuffer::deserialize(&bytes[start..])?; + + Some(Self { + pcr_select, + pcr_digest, + locality, + parent_name_alg, + parent_name, + parent_qualified_name, + outside_info, + }) + } + + pub fn payload_size(&self) -> usize { + let mut payload_size = 0; + + payload_size += self.pcr_select.payload_size(); + payload_size += self.pcr_digest.payload_size(); + payload_size += size_of_val(&self.locality); + payload_size += size_of_val(&self.parent_name_alg); + payload_size += self.parent_name.payload_size(); + payload_size += self.parent_qualified_name.payload_size(); + payload_size += self.outside_info.payload_size(); + + payload_size + } + } + + /// `TPM2B_CREATION_DATA` + #[derive(Debug, FromBytes, IntoBytes, Immutable, KnownLayout)] + #[repr(C)] + pub struct Tpm2bCreationData { + size: u16_be, + creation_data: TpmsCreationData, + } + + impl Tpm2bCreationData { + pub fn deserialize(bytes: &[u8]) -> Option { + let mut start = 0; + let end = size_of::(); + + if bytes.len() < end { + return None; + } + + let size = u16_be::read_from_bytes(&bytes[start..end]).ok()?; // TODO: zerocopy: simplify (https://github.com/microsoft/openvmm/issues/759) + + start = end; + let creation_data = TpmsCreationData::deserialize(&bytes[start..])?; + + Some(Self { + size, + creation_data, + }) + } + + pub fn payload_size(&self) -> usize { + let mut payload_size = 0; + + payload_size += size_of_val(&self.size); + payload_size += self.creation_data.payload_size(); + + payload_size + } + } + + /// `TPMT_TK_CREATION` + #[repr(C)] + #[derive(Debug, FromBytes, IntoBytes, Immutable, KnownLayout)] + pub struct TpmtTkCreation { + tag: SessionTag, + hierarchy: ReservedHandle, + digest: Tpm2bBuffer, + } + + impl TpmtTkCreation { + pub fn deserialize(bytes: &[u8]) -> Option { + let mut start = 0; + let mut end = size_of::(); + if bytes.len() < end { + return None; + } + let tag = SessionTag::read_from_prefix(&bytes[start..end]).ok()?.0; // TODO: zerocopy: use-rest-of-range, option-to-error (https://github.com/microsoft/openvmm/issues/759) + + start = end; + end += size_of::(); + if bytes.len() < end { + return None; + } + let hierarchy = ReservedHandle::read_from_prefix(&bytes[start..end]).ok()?.0; // TODO: zerocopy: use-rest-of-range, option-to-error (https://github.com/microsoft/openvmm/issues/759) + + start = end; + let digest = Tpm2bBuffer::deserialize(&bytes[start..])?; + + Some(Self { + tag, + hierarchy, + digest, + }) + } + + pub fn payload_size(&self) -> usize { + let mut payload_size = 0; + + payload_size += size_of_val(&self.tag); + payload_size += size_of_val(&self.hierarchy); + payload_size += self.digest.payload_size(); + + payload_size + } + } + + /// `TPMS_NV_PUBLIC` + #[repr(C)] + #[derive(Debug, Copy, Clone, FromBytes, IntoBytes, Immutable, KnownLayout)] + pub struct TpmsNvPublic { + nv_index: u32_be, + name_alg: AlgId, + pub attributes: TpmaNv, + auth_policy: Tpm2bBuffer, + pub data_size: u16_be, + } + + impl TpmsNvPublic { + pub fn new( + nv_index: u32, + name_alg: AlgId, + attributes: TpmaNvBits, + auth_policy: &[u8], + data_size: u16, + ) -> Result { + let auth_policy = + Tpm2bBuffer::new(auth_policy).map_err(TpmProtoError::TpmsNvPublicAuthPolicy)?; + + Ok(Self { + nv_index: nv_index.into(), + name_alg, + attributes: attributes.into(), + auth_policy, + data_size: data_size.into(), + }) + } + + pub fn serialize(self) -> Vec { + let mut buffer = Vec::new(); + + buffer.extend_from_slice(self.nv_index.as_bytes()); + buffer.extend_from_slice(self.name_alg.as_bytes()); + buffer.extend_from_slice(self.attributes.as_bytes()); + buffer.extend_from_slice(&self.auth_policy.serialize()); + buffer.extend_from_slice(self.data_size.as_bytes()); + + buffer + } + + pub fn deserialize(bytes: &[u8]) -> Option { + let mut start = 0; + let mut end = size_of::(); + if bytes.len() < end { + return None; + } + let nv_index: u32 = u32_be::read_from_bytes(&bytes[start..end]).ok()?.into(); // TODO: zerocopy: simplify (https://github.com/microsoft/openvmm/issues/759) + + start = end; + end += size_of::(); + if bytes.len() < end { + return None; + } + let name_alg = AlgId::read_from_prefix(&bytes[start..end]).ok()?.0; // TODO: zerocopy: use-rest-of-range, option-to-error (https://github.com/microsoft/openvmm/issues/759) + + start = end; + end += size_of::(); + if bytes.len() < end { + return None; + } + let attributes: u32 = u32_be::read_from_bytes(&bytes[start..end]).ok()?.into(); // TODO: zerocopy: simplify (https://github.com/microsoft/openvmm/issues/759) + + start = end; + let auth_policy = Tpm2bBuffer::deserialize(&bytes[start..])?; + end += auth_policy.payload_size(); + + start = end; + end += size_of::(); + if bytes.len() < end { + return None; + } + let data_size = u16_be::read_from_bytes(&bytes[start..end]).ok()?; // TODO: zerocopy: simplify (https://github.com/microsoft/openvmm/issues/759) + + Some(Self { + nv_index: nv_index.into(), + name_alg, + attributes: attributes.into(), + auth_policy, + data_size, + }) + } + + pub fn payload_size(&self) -> usize { + let mut payload_size = 0; + + payload_size += size_of_val(&self.nv_index); + payload_size += size_of_val(&self.name_alg); + payload_size += size_of_val(&self.attributes); + payload_size += self.auth_policy.payload_size(); + payload_size += size_of_val(&self.data_size); + + payload_size + } + } + + /// `TPM2B_NV_PUBLIC` + #[repr(C)] + #[derive(Debug, Copy, Clone, FromBytes, IntoBytes, Immutable, KnownLayout)] + pub struct Tpm2bNvPublic { + size: u16_be, + pub nv_public: TpmsNvPublic, + } + + impl Tpm2bNvPublic { + pub fn new(nv_public: TpmsNvPublic) -> Result { + let size = nv_public.payload_size(); + if size > u16::MAX.into() { + Err(InvalidInput::NvPublicPayloadTooLarge(size, u16::MAX.into()))? + } + + Ok(Self { + size: (size as u16).into(), + nv_public, + }) + } + + pub fn serialize(self) -> Vec { + let mut buffer = Vec::new(); + + buffer.extend_from_slice(self.size.as_bytes()); + buffer.extend_from_slice(&self.nv_public.serialize()); + + buffer + } + + pub fn deserialize(bytes: &[u8]) -> Option { + let mut start = 0; + let end = size_of::(); + + if bytes.len() < end { + return None; + } + + let size = u16_be::read_from_bytes(&bytes[start..end]).ok()?; // TODO: zerocopy: simplify (https://github.com/microsoft/openvmm/issues/759) + + start = end; + let nv_public = TpmsNvPublic::deserialize(&bytes[start..])?; + + Some(Self { size, nv_public }) + } + + pub fn payload_size(&self) -> usize { + let mut payload_size = 0; + + payload_size += size_of_val(&self.size); + payload_size += self.nv_public.payload_size(); + + payload_size + } + } + + // === ClearControl === // + + #[repr(C)] + #[derive(IntoBytes, Immutable, KnownLayout, FromBytes)] + pub struct ClearControlCmd { + header: CmdHeader, + auth_handle: ReservedHandle, + auth_size: u32_be, + auth: common::CmdAuth, + disable: u8, + } + + impl ClearControlCmd { + pub fn new( + session: SessionTag, + auth_handle: ReservedHandle, + auth: common::CmdAuth, + disable: bool, + ) -> Self { + Self { + header: CmdHeader::new::(session, CommandCodeEnum::ClearControl.into()), + auth_handle, + auth_size: (size_of::() as u32).into(), + auth, + disable: disable as u8, + } + } + } + + #[repr(C)] + #[derive(Debug, IntoBytes, Immutable, KnownLayout, FromBytes)] + pub struct ClearControlReply { + pub header: ReplyHeader, + pub param_size: u32_be, + pub auth: common::ReplyAuth, + } + + impl TpmCommand for ClearControlCmd { + type Reply = ClearControlReply; + } + + impl TpmReply for ClearControlReply { + type Command = ClearControlCmd; + + fn deserialize(bytes: &[u8]) -> Option { + Some(Self::read_from_prefix(bytes).ok()?.0) // TODO: zerocopy: tpm better error? (https://github.com/microsoft/openvmm/issues/759) + } + + fn payload_size(&self) -> usize { + size_of::() + } + } + + // === Clear === // + + #[repr(C)] + #[derive(IntoBytes, Immutable, KnownLayout, FromBytes)] + pub struct ClearCmd { + header: CmdHeader, + + auth_handle: ReservedHandle, + auth_size: u32_be, + auth: common::CmdAuth, + } + + impl ClearCmd { + pub fn new( + session: SessionTag, + auth_handle: ReservedHandle, + auth: common::CmdAuth, + ) -> Self { + Self { + header: CmdHeader::new::(session, CommandCodeEnum::Clear.into()), + auth_handle, + auth_size: (size_of::() as u32).into(), + auth, + } + } + } + + #[repr(C)] + #[derive(Debug, IntoBytes, Immutable, KnownLayout, FromBytes)] + pub struct ClearReply { + pub header: ReplyHeader, + pub param_size: u32_be, + pub auth: common::ReplyAuth, + } + + impl TpmCommand for ClearCmd { + type Reply = ClearReply; + } + + impl TpmReply for ClearReply { + type Command = ClearCmd; + + fn deserialize(bytes: &[u8]) -> Option { + Some(Self::read_from_prefix(bytes).ok()?.0) // TODO: zerocopy: tpm better error? (https://github.com/microsoft/openvmm/issues/759) + } + + fn payload_size(&self) -> usize { + size_of::() + } + } + + // === Startup === // + + #[expect(dead_code)] + pub enum StartupType { + Clear, + State, + } + + #[repr(C)] + #[derive(IntoBytes, Immutable, KnownLayout, FromBytes)] + pub struct StartupCmd { + header: CmdHeader, + startup_type: u16_be, + } + + impl StartupCmd { + pub fn new(session_tag: SessionTag, startup_type: StartupType) -> StartupCmd { + StartupCmd { + header: CmdHeader::new::(session_tag, CommandCodeEnum::Startup.into()), + startup_type: match startup_type { + StartupType::Clear => 0, + StartupType::State => 1, + } + .into(), + } + } + } + + #[repr(C)] + #[derive(Debug, IntoBytes, Immutable, KnownLayout, FromBytes)] + pub struct StartupReply { + pub header: ReplyHeader, + } + + impl TpmCommand for StartupCmd { + type Reply = StartupReply; + } + + impl TpmReply for StartupReply { + type Command = StartupCmd; + + fn deserialize(bytes: &[u8]) -> Option { + Some(Self::read_from_prefix(bytes).ok()?.0) // TODO: zerocopy: tpm better error? (https://github.com/microsoft/openvmm/issues/759) + } + + fn payload_size(&self) -> usize { + size_of::() + } + } + + // === Self Test === // + + #[repr(C)] + #[derive(IntoBytes, Immutable, KnownLayout, FromBytes)] + pub struct SelfTestCmd { + header: CmdHeader, + full_test: u8, + } + + impl SelfTestCmd { + pub fn new(session_tag: SessionTag, full_test: bool) -> SelfTestCmd { + SelfTestCmd { + header: CmdHeader::new::(session_tag, CommandCodeEnum::SelfTest.into()), + full_test: full_test as u8, + } + } + } + + #[repr(C)] + #[derive(Debug, IntoBytes, Immutable, KnownLayout, FromBytes)] + pub struct SelfTestReply { + pub header: ReplyHeader, + } + + impl TpmCommand for SelfTestCmd { + type Reply = SelfTestReply; + } + + impl TpmReply for SelfTestReply { + type Command = SelfTestCmd; + + fn deserialize(bytes: &[u8]) -> Option { + Some(Self::read_from_prefix(bytes).ok()?.0) // TODO: zerocopy: tpm better error? (https://github.com/microsoft/openvmm/issues/759) + } + + fn payload_size(&self) -> usize { + size_of::() + } + } + + // === Hierarchy Control === // + + #[repr(C)] + #[derive(IntoBytes, Immutable, KnownLayout, FromBytes)] + pub struct HierarchyControlCmd { + header: CmdHeader, + + auth_handle: ReservedHandle, + auth_size: u32_be, + auth: common::CmdAuth, + + hierarchy: ReservedHandle, + state: u8, + } + + impl HierarchyControlCmd { + pub fn new( + session: SessionTag, + auth_handle: ReservedHandle, + auth: common::CmdAuth, + hierarchy: ReservedHandle, + state: bool, + ) -> Self { + Self { + header: CmdHeader::new::(session, CommandCodeEnum::HierarchyControl.into()), + auth_handle, + auth_size: (size_of::() as u32).into(), + auth, + hierarchy, + state: state as u8, + } + } + } + + #[repr(C)] + #[derive(Debug, IntoBytes, Immutable, KnownLayout, FromBytes)] + pub struct HierarchyControlReply { + pub header: ReplyHeader, + pub param_size: u32_be, + pub auth: common::ReplyAuth, + } + + impl TpmCommand for HierarchyControlCmd { + type Reply = HierarchyControlReply; + } + + impl TpmReply for HierarchyControlReply { + type Command = HierarchyControlCmd; + + fn deserialize(bytes: &[u8]) -> Option { + Some(Self::read_from_prefix(bytes).ok()?.0) // TODO: zerocopy: tpm better error? (https://github.com/microsoft/openvmm/issues/759) + } + + fn payload_size(&self) -> usize { + size_of::() + } + } + + // === Pcr Allocate === // + + #[repr(C)] + #[derive(Debug, Copy, Clone, IntoBytes, Immutable, KnownLayout, FromBytes)] + pub struct PcrSelection { + pub hash: AlgId, + pub size_of_select: u8, + pub bitmap: [u8; 3], + } + + impl PcrSelection { + pub fn serialize(self) -> Vec { + let mut buffer = Vec::new(); + + buffer.extend_from_slice(self.hash.as_bytes()); + buffer.extend_from_slice(self.size_of_select.as_bytes()); + buffer.extend_from_slice(&self.bitmap[..self.size_of_select as usize]); + + buffer + } + + pub fn deserialize(bytes: &[u8]) -> Option { + let mut start = 0; + let mut end = size_of::(); + if bytes.len() < end { + return None; + } + let hash = AlgId::read_from_prefix(&bytes[start..end]).ok()?.0; // TODO: zerocopy: use-rest-of-range, option-to-error (https://github.com/microsoft/openvmm/issues/759) + + start = end; + end += size_of::(); + if bytes.len() < end { + return None; + } + let size_of_select = bytes[start]; + if size_of_select > 3 { + return None; + } + + start = end; + end += size_of_select as usize; + if bytes.len() < end { + return None; + } + let mut bitmap = [0u8; 3]; + bitmap[..size_of_select as usize].copy_from_slice(&bytes[start..end]); + + Some(Self { + hash, + size_of_select, + bitmap, + }) + } + + pub fn payload_size(&self) -> usize { + let mut payload_size = 0; + + payload_size += size_of_val(&self.hash); + payload_size += size_of_val(&self.size_of_select); + payload_size += self.size_of_select as usize; + + payload_size + } + } + + #[repr(C)] + #[derive(Debug, IntoBytes, Immutable, KnownLayout, FromBytes)] + pub struct PcrAllocateCmd { + header: CmdHeader, + auth_handle: ReservedHandle, + // Authorization area + auth_size: u32_be, + auth: common::CmdAuth, + // Parameters + pcr_allocation: TpmlPcrSelection, + } + + impl PcrAllocateCmd { + pub const HASH_ALG_TO_ID: [(u32, AlgId); 5] = [ + (1 << 0, AlgId::new(AlgIdEnum::SHA as u16)), + (1 << 1, AlgId::new(AlgIdEnum::SHA256 as u16)), + (1 << 2, AlgId::new(AlgIdEnum::SHA384 as u16)), + (1 << 3, AlgId::new(AlgIdEnum::SHA512 as u16)), + (1 << 4, AlgId::new(AlgIdEnum::SM3_256 as u16)), + ]; + + /// # Panics + /// + /// `pcr_selections` must be have a len less than `TCG_BOOT_HASH_COUNT` + pub fn new( + session: SessionTag, + auth_handle: ReservedHandle, + auth: common::CmdAuth, + pcr_selections: &[PcrSelection], + ) -> Result { + let pcr_allocation = TpmlPcrSelection::new(pcr_selections) + .map_err(TpmProtoError::PcrAllocatePcrAllocation)?; + + let mut cmd = Self { + header: CmdHeader::new::(session, CommandCodeEnum::PCR_Allocate.into()), + auth_handle, + auth_size: (size_of::() as u32).into(), + auth, + pcr_allocation, + }; + + cmd.header.size = new_u32_be(cmd.payload_size() as u32); + + Ok(cmd) + } + + pub fn serialize(&self) -> Vec { + let mut buffer = Vec::new(); + + buffer.extend_from_slice(self.header.as_bytes()); + buffer.extend_from_slice(self.auth_handle.as_bytes()); + buffer.extend_from_slice(self.auth_size.as_bytes()); + buffer.extend_from_slice(self.auth.as_bytes()); + buffer.extend_from_slice(&self.pcr_allocation.serialize()); + + buffer + } + + pub fn payload_size(&self) -> usize { + let mut payload_size = 0; + + payload_size += size_of_val(&self.header); + payload_size += size_of_val(&self.auth_handle); + payload_size += size_of_val(&self.auth_size); + payload_size += size_of_val(&self.auth); + payload_size += self.pcr_allocation.payload_size(); + + payload_size + } + } + + #[repr(C)] + #[derive(Debug, IntoBytes, Immutable, KnownLayout, FromBytes)] + pub struct PcrAllocateReply { + pub header: ReplyHeader, + pub auth_size: u32_be, + pub allocation_success: u8, + pub max_pcr: u32_be, + pub size_needed: u32_be, + pub size_available: u32_be, + + pub auth: common::ReplyAuth, + } + + impl TpmCommand for PcrAllocateCmd { + type Reply = PcrAllocateReply; + } + + impl TpmReply for PcrAllocateReply { + type Command = PcrAllocateCmd; + + fn deserialize(bytes: &[u8]) -> Option { + Some(Self::read_from_prefix(bytes).ok()?.0) // TODO: zerocopy: tpm better error? (https://github.com/microsoft/openvmm/issues/759) + } + + fn payload_size(&self) -> usize { + size_of::() + } + } + + // === ChangeSeed === // + + #[repr(C)] + #[derive(IntoBytes, Immutable, KnownLayout, FromBytes)] + pub struct ChangeSeedCmd { + header: CmdHeader, + auth_handle: ReservedHandle, + auth_size: u32_be, + auth: common::CmdAuth, + } + + impl ChangeSeedCmd { + pub fn new( + session: SessionTag, + auth_handle: ReservedHandle, + auth: common::CmdAuth, + command_code: CommandCodeEnum, + ) -> Self { + Self { + header: CmdHeader::new::(session, command_code.into()), + auth_handle, + auth_size: (size_of::() as u32).into(), + auth, + } + } + } + + #[repr(C)] + #[derive(Debug, IntoBytes, Immutable, KnownLayout, FromBytes)] + pub struct ChangeSeedReply { + pub header: ReplyHeader, + pub param_size: u32_be, + + pub auth: common::ReplyAuth, + } + + impl TpmCommand for ChangeSeedCmd { + type Reply = ChangeSeedReply; + } + + impl TpmReply for ChangeSeedReply { + type Command = ChangeSeedCmd; + + fn deserialize(bytes: &[u8]) -> Option { + Some(Self::read_from_prefix(bytes).ok()?.0) // TODO: zerocopy: option-to-error (https://github.com/microsoft/openvmm/issues/759) + } + + fn payload_size(&self) -> usize { + size_of::() + } + } + + // === CreatePrimary === // + + #[repr(C)] + #[derive(IntoBytes, Immutable, KnownLayout, FromBytes)] + pub struct CreatePrimaryCmd { + pub header: CmdHeader, + primary_handle: ReservedHandle, + // Authorization area + auth_size: u32_be, + auth: common::CmdAuth, + // Parameters + in_sensitive: Tpm2bSensitiveCreate, + in_public: Tpm2bPublic, + outside_info: Tpm2bBuffer, + creation_pcr: TpmlPcrSelection, + } + + impl CreatePrimaryCmd { + pub fn new( + session: SessionTag, + primary_handle: ReservedHandle, + auth: common::CmdAuth, + in_sensitive_user_auth: &[u8], + in_sensitive_data: &[u8], + in_public: TpmtPublic, + outside_info: &[u8], + creation_pcr: &[PcrSelection], + ) -> Result { + let sensitive_create = + TpmsSensitiveCreate::new(in_sensitive_user_auth, in_sensitive_data)?; + let in_sensitive = Tpm2bSensitiveCreate::new(sensitive_create); + let in_public = Tpm2bPublic::new(in_public); + let outside_info = + Tpm2bBuffer::new(outside_info).map_err(TpmProtoError::CreatePrimaryOutsideInfo)?; + let creation_pcr = TpmlPcrSelection::new(creation_pcr) + .map_err(TpmProtoError::CreatePrimaryCreationPcr)?; + + let mut cmd = Self { + header: CmdHeader::new::(session, CommandCodeEnum::CreatePrimary.into()), + primary_handle, + auth_size: (size_of::() as u32).into(), + auth, + in_sensitive, + in_public, + outside_info, + creation_pcr, + }; + + cmd.header.size = new_u32_be(cmd.payload_size() as u32); + + Ok(cmd) + } + + pub fn serialize(&self) -> Vec { + let mut buffer = Vec::new(); + + buffer.extend_from_slice(self.header.as_bytes()); + buffer.extend_from_slice(self.primary_handle.as_bytes()); + buffer.extend_from_slice(self.auth_size.as_bytes()); + buffer.extend_from_slice(self.auth.as_bytes()); + buffer.extend_from_slice(&self.in_sensitive.serialize()); + buffer.extend_from_slice(&self.in_public.serialize()); + buffer.extend_from_slice(&self.outside_info.serialize()); + buffer.extend_from_slice(&self.creation_pcr.serialize()); + + buffer + } + + pub fn payload_size(&self) -> usize { + let mut payload_size = 0; + + payload_size += size_of_val(&self.header); + payload_size += size_of_val(&self.primary_handle); + payload_size += size_of_val(&self.auth_size); + payload_size += size_of_val(&self.auth); + payload_size += self.in_sensitive.payload_size(); + payload_size += self.in_public.payload_size(); + payload_size += self.outside_info.payload_size(); + payload_size += self.creation_pcr.payload_size(); + + payload_size + } + } + + #[repr(C)] + #[derive(Debug, FromBytes, IntoBytes, Immutable, KnownLayout)] + pub struct CreatePrimaryReply { + pub header: ReplyHeader, + pub object_handle: ReservedHandle, + // Parameter size + param_size: u32_be, + // Parameters + pub out_public: Tpm2bPublic, + creation_data: Tpm2bCreationData, + creation_hash: Tpm2bBuffer, + creation_ticket: TpmtTkCreation, + name: Tpm2bBuffer, + // Authorization area + auth: common::ReplyAuth, + } + + impl TpmCommand for CreatePrimaryCmd { + type Reply = CreatePrimaryReply; + } + + impl TpmReply for CreatePrimaryReply { + type Command = CreatePrimaryCmd; + + fn deserialize(bytes: &[u8]) -> Option { + let mut start = 0; + let mut end = size_of::(); + let header = ReplyHeader::read_from_prefix(&bytes[start..end]).ok()?.0; // TODO: zerocopy: use-rest-of-range, option-to-error (https://github.com/microsoft/openvmm/issues/759) + + // Handle the command failure. + if header.size.get() as usize == end { + let mut cmd = CreatePrimaryReply::new_zeroed(); + cmd.header = header; + return Some(cmd); + } + + start = end; + end += size_of::(); + let object_handle = ReservedHandle::read_from_prefix(&bytes[start..end]).ok()?.0; // TODO: zerocopy: use-rest-of-range, option-to-error (https://github.com/microsoft/openvmm/issues/759) + + start = end; + end += size_of::(); + let param_size = u32_be::read_from_bytes(&bytes[start..end]).ok()?; // TODO: zerocopy: simplify (https://github.com/microsoft/openvmm/issues/759) + + start = end; + let out_public = Tpm2bPublic::deserialize(&bytes[start..])?; + end += out_public.payload_size(); + + start = end; + let creation_data = Tpm2bCreationData::deserialize(&bytes[start..])?; + end += creation_data.payload_size(); + + start = end; + let creation_hash = Tpm2bBuffer::deserialize(&bytes[start..])?; + end += creation_hash.payload_size(); + + start = end; + let creation_ticket = TpmtTkCreation::deserialize(&bytes[start..])?; + end += creation_ticket.payload_size(); + + start = end; + let name = Tpm2bBuffer::deserialize(&bytes[start..])?; + end += name.payload_size(); + + start = end; + end += size_of::(); + let auth = common::ReplyAuth::read_from_prefix(&bytes[start..end]) + .ok()? + .0; // TODO: zerocopy: use-rest-of-range, option-to-error (https://github.com/microsoft/openvmm/issues/759) + + if header.size.get() as usize != end { + return None; + } + + Some(Self { + header, + object_handle, + param_size, + out_public, + creation_data, + creation_hash, + creation_ticket, + name, + auth, + }) + } + + fn payload_size(&self) -> usize { + let mut payload_size = 0; + + payload_size += size_of_val(&self.header); + payload_size += size_of_val(&self.object_handle); + payload_size += size_of_val(&self.param_size); + payload_size += self.out_public.payload_size(); + payload_size += self.creation_data.payload_size(); + payload_size += self.creation_hash.payload_size(); + payload_size += self.creation_ticket.payload_size(); + payload_size += self.name.payload_size(); + payload_size += size_of_val(&self.auth); + + payload_size + } + } + + // === FlushContext === // + + #[repr(C)] + #[derive(IntoBytes, Immutable, KnownLayout, FromBytes)] + pub struct FlushContextCmd { + pub header: CmdHeader, + // Parameter + flush_handle: ReservedHandle, + } + + impl FlushContextCmd { + pub fn new(flush_handle: ReservedHandle) -> Self { + Self { + header: CmdHeader::new::( + SessionTagEnum::NoSessions.into(), + CommandCodeEnum::FlushContext.into(), + ), + flush_handle, + } + } + } + + #[repr(C)] + #[derive(Debug, FromBytes, IntoBytes, Immutable, KnownLayout)] + pub struct FlushContextReply { + pub header: ReplyHeader, + } + + impl TpmCommand for FlushContextCmd { + type Reply = FlushContextReply; + } + + impl TpmReply for FlushContextReply { + type Command = FlushContextCmd; + + fn deserialize(bytes: &[u8]) -> Option { + Some(Self::read_from_prefix(bytes).ok()?.0) // TODO: zerocopy: tpm better error? (https://github.com/microsoft/openvmm/issues/759) + } + + fn payload_size(&self) -> usize { + size_of::() + } + } + + // === EvictControl === // + + #[repr(C)] + #[derive(IntoBytes, Immutable, KnownLayout, FromBytes)] + pub struct EvictControlCmd { + header: CmdHeader, + auth_handle: ReservedHandle, + object_handle: ReservedHandle, + // Authorization area + auth_size: u32_be, + auth: common::CmdAuth, + // Parameter + persistent_handle: ReservedHandle, + } + + impl EvictControlCmd { + pub fn new( + session: SessionTag, + auth_handle: ReservedHandle, + object_handle: ReservedHandle, + auth: common::CmdAuth, + persistent_handle: ReservedHandle, + ) -> Self { + Self { + header: CmdHeader::new::(session, CommandCodeEnum::EvictControl.into()), + auth_handle, + object_handle, + auth_size: (size_of::() as u32).into(), + auth, + persistent_handle, + } + } + } + + #[repr(C)] + #[derive(Debug, FromBytes, IntoBytes, Immutable, KnownLayout)] + pub struct EvictControlReply { + pub header: ReplyHeader, + } + + impl TpmCommand for EvictControlCmd { + type Reply = EvictControlReply; + } + + impl TpmReply for EvictControlReply { + type Command = EvictControlCmd; + + fn deserialize(bytes: &[u8]) -> Option { + Some(Self::read_from_prefix(bytes).ok()?.0) // TODO: zerocopy: error-to-option (https://github.com/microsoft/openvmm/issues/759) + } + + fn payload_size(&self) -> usize { + size_of::() + } + } + + // === ReadPublic === // + + #[repr(C)] + #[derive(IntoBytes, Immutable, KnownLayout, FromBytes)] + pub struct ReadPublicCmd { + header: CmdHeader, + object_handle: ReservedHandle, + } + + impl ReadPublicCmd { + pub fn new(session: SessionTag, object_handle: ReservedHandle) -> Self { + Self { + header: CmdHeader::new::(session, CommandCodeEnum::ReadPublic.into()), + object_handle, + } + } + } + + #[repr(C)] + #[derive(Debug, FromBytes, IntoBytes, Immutable, KnownLayout)] + pub struct ReadPublicReply { + pub header: ReplyHeader, + pub out_public: Tpm2bPublic, + name: Tpm2bBuffer, + qualified_name: Tpm2bBuffer, + } + + impl TpmCommand for ReadPublicCmd { + type Reply = ReadPublicReply; + } + + impl TpmReply for ReadPublicReply { + type Command = ReadPublicCmd; + + fn deserialize(bytes: &[u8]) -> Option { + let mut start = 0; + let mut end = size_of::(); + + let header = ReplyHeader::read_from_prefix(&bytes[start..end]).ok()?.0; // TODO: zerocopy: use-rest-of-range, option-to-error (https://github.com/microsoft/openvmm/issues/759) + + // Handle the command failure. + if header.size.get() as usize == end { + return Some(Self { + header, + out_public: Tpm2bPublic::new_zeroed(), + name: Tpm2bBuffer::new_zeroed(), + qualified_name: Tpm2bBuffer::new_zeroed(), + }); + } + + start = end; + let out_public = Tpm2bPublic::deserialize(&bytes[start..])?; + end += out_public.payload_size(); + + start = end; + let name = Tpm2bBuffer::deserialize(&bytes[start..])?; + end += name.payload_size(); + + start = end; + let qualified_name = Tpm2bBuffer::deserialize(&bytes[start..])?; + end += qualified_name.payload_size(); + + if header.size.get() as usize != end { + return None; + } + + Some(Self { + header, + out_public, + name, + qualified_name, + }) + } + + fn payload_size(&self) -> usize { + let mut payload_size = 0; + + payload_size += size_of::(); + payload_size += self.out_public.payload_size(); + payload_size += self.name.payload_size(); + payload_size += self.qualified_name.payload_size(); + + payload_size + } + } + + // === Nv DefineSpace === // + + #[repr(C)] + #[derive(FromBytes, IntoBytes, Immutable, KnownLayout)] + pub struct NvDefineSpaceCmd { + header: CmdHeader, + auth_handle: ReservedHandle, + // Authorization area + auth_size: u32_be, + auth_cmd: common::CmdAuth, + // Parameters + auth: Tpm2bBuffer, + public_info: Tpm2bNvPublic, + } + + impl NvDefineSpaceCmd { + pub fn new( + session: SessionTag, + auth_handle: ReservedHandle, + auth_cmd: common::CmdAuth, + auth: u64, + public_info: TpmsNvPublic, + ) -> Result { + let auth = new_u64_be(auth); + let auth = + Tpm2bBuffer::new(auth.as_bytes()).map_err(TpmProtoError::NvDefineSpaceAuth)?; + let public_info = + Tpm2bNvPublic::new(public_info).map_err(TpmProtoError::NvDefineSpacePublicInfo)?; + + let mut cmd = Self { + header: CmdHeader::new::(session, CommandCodeEnum::NV_DefineSpace.into()), + auth_handle, + auth_size: (size_of::() as u32).into(), + auth_cmd, + auth, + public_info, + }; + + cmd.header.size = new_u32_be(cmd.payload_size() as u32); + + Ok(cmd) + } + + pub fn serialize(&self) -> Vec { + let mut buffer = Vec::new(); + + buffer.extend_from_slice(self.header.as_bytes()); + buffer.extend_from_slice(self.auth_handle.as_bytes()); + buffer.extend_from_slice(self.auth_size.as_bytes()); + buffer.extend_from_slice(self.auth_cmd.as_bytes()); + buffer.extend_from_slice(&self.auth.serialize()); + buffer.extend_from_slice(&self.public_info.serialize()); + + buffer + } + + pub fn payload_size(&self) -> usize { + let mut payload_size = 0; + + payload_size += size_of_val(&self.header); + payload_size += size_of_val(&self.auth_handle); + payload_size += size_of_val(&self.auth_size); + payload_size += size_of_val(&self.auth_cmd); + payload_size += self.auth.payload_size(); + payload_size += self.public_info.payload_size(); + + payload_size + } + } + + #[repr(C)] + #[derive(Debug, FromBytes, IntoBytes, Immutable, KnownLayout)] + pub struct NvDefineSpaceReply { + pub header: ReplyHeader, + } + + impl TpmCommand for NvDefineSpaceCmd { + type Reply = NvDefineSpaceReply; + } + + impl TpmReply for NvDefineSpaceReply { + type Command = NvDefineSpaceCmd; + + fn deserialize(bytes: &[u8]) -> Option { + Some(Self::read_from_prefix(bytes).ok()?.0) // TODO: zerocopy: tpm better error? (https://github.com/microsoft/openvmm/issues/759) + } + + fn payload_size(&self) -> usize { + size_of::() + } + } + + // === Nv UndefineSpace === // + + #[repr(C)] + #[derive(FromBytes, IntoBytes, Immutable, KnownLayout)] + pub struct NvUndefineSpaceCmd { + header: CmdHeader, + auth_handle: ReservedHandle, + nv_index: u32_be, + // Authorization area + auth_size: u32_be, + auth: common::CmdAuth, + } + + impl NvUndefineSpaceCmd { + pub fn new( + session: SessionTag, + auth_handle: ReservedHandle, + auth: common::CmdAuth, + nv_index: u32, + ) -> Self { + Self { + header: CmdHeader::new::(session, CommandCodeEnum::NV_UndefineSpace.into()), + auth_handle, + nv_index: nv_index.into(), + auth_size: (size_of::() as u32).into(), + auth, + } + } + } + + #[repr(C)] + #[derive(Debug, FromBytes, IntoBytes, Immutable, KnownLayout)] + pub struct NvUndefineSpaceReply { + pub header: ReplyHeader, + } + + impl TpmCommand for NvUndefineSpaceCmd { + type Reply = NvUndefineSpaceReply; + } + + impl TpmReply for NvUndefineSpaceReply { + type Command = NvUndefineSpaceCmd; + + fn deserialize(bytes: &[u8]) -> Option { + Some(Self::read_from_prefix(bytes).ok()?.0) // TODO: zerocopy: tpm better error? (https://github.com/microsoft/openvmm/issues/759) + } + + fn payload_size(&self) -> usize { + size_of::() + } + } + + // === Nv ReadPublic === // + + #[repr(C)] + #[derive(IntoBytes, Immutable, KnownLayout, FromBytes)] + pub struct NvReadPublicCmd { + header: CmdHeader, + nv_index: u32_be, + } + + impl NvReadPublicCmd { + pub fn new(session: SessionTag, nv_index: u32) -> Self { + Self { + header: CmdHeader::new::(session, CommandCodeEnum::NV_ReadPublic.into()), + nv_index: nv_index.into(), + } + } + } + + #[repr(C)] + #[derive(Debug, FromBytes, IntoBytes, Immutable, KnownLayout)] + pub struct NvReadPublicReply { + pub header: ReplyHeader, + // Parameters + pub nv_public: Tpm2bNvPublic, + nv_name: Tpm2bBuffer, + } + + impl TpmCommand for NvReadPublicCmd { + type Reply = NvReadPublicReply; + } + + impl TpmReply for NvReadPublicReply { + type Command = NvReadPublicCmd; + + fn deserialize(bytes: &[u8]) -> Option { + let mut start = 0; + let mut end = size_of::(); + + let header = ReplyHeader::read_from_prefix(&bytes[start..end]).ok()?.0; // TODO: zerocopy: use-rest-of-range, option-to-error (https://github.com/microsoft/openvmm/issues/759) + + // Handle the command failure. + if header.size.get() as usize == end { + return Some(Self { + header, + nv_public: Tpm2bNvPublic::new_zeroed(), + nv_name: Tpm2bBuffer::new_zeroed(), + }); + } + + start = end; + let nv_public = Tpm2bNvPublic::deserialize(&bytes[start..])?; + end += nv_public.payload_size(); + + start = end; + let nv_name = Tpm2bBuffer::deserialize(&bytes[start..])?; + end += nv_name.payload_size(); + + if header.size.get() as usize != end { + return None; + } + + Some(Self { + header, + nv_public, + nv_name, + }) + } + + fn payload_size(&self) -> usize { + let mut size = 0; + + size += size_of::(); + size += self.nv_public.payload_size(); + size += self.nv_name.payload_size(); + + size + } + } + + // === Nv Write === // + + #[repr(C)] + #[derive(FromBytes, IntoBytes, Immutable, KnownLayout)] + pub struct NvWriteCmd { + header: CmdHeader, + auth_handle: ReservedHandle, + pub nv_index: u32_be, + // Authorization area + auth_size: u32_be, + auth: common::CmdAuth, + auth_value: u64_be, + // Parameters + pub data: Tpm2bBuffer, + pub offset: u16_be, + } + + impl NvWriteCmd { + pub fn new( + session: SessionTag, + auth_handle: ReservedHandle, + auth: common::CmdAuth, + auth_value: u64, + nv_index: u32, + data: &[u8], + offset: u16, + ) -> Result { + let data = Tpm2bBuffer::new(data).map_err(TpmProtoError::NvWriteData)?; + // If `auth_handle` is not the owner, assuming password-based authorization is used. + let auth_value_size = if auth_handle != TPM20_RH_OWNER { + size_of::() as u32 + } else { + 0 + }; + + let mut cmd = Self { + header: CmdHeader::new::(session, CommandCodeEnum::NV_Write.into()), + auth_handle, + nv_index: nv_index.into(), + auth_size: (size_of::() as u32 + auth_value_size).into(), + auth, + auth_value: auth_value.into(), + data, + offset: offset.into(), + }; + + cmd.header.size = new_u32_be(cmd.payload_size() as u32); + + Ok(cmd) + } + + pub fn update_write_data(&mut self, data: &[u8], offset: u16) -> Result<(), TpmProtoError> { + let data = Tpm2bBuffer::new(data).map_err(TpmProtoError::NvWriteData)?; + + self.data = data; + self.offset = offset.into(); + self.header.size = new_u32_be(self.payload_size() as u32); + + Ok(()) + } + + pub fn serialize(&self) -> Vec { + let mut buffer = Vec::new(); + + buffer.extend_from_slice(self.header.as_bytes()); + buffer.extend_from_slice(self.auth_handle.as_bytes()); + buffer.extend_from_slice(self.nv_index.as_bytes()); + buffer.extend_from_slice(self.auth_size.as_bytes()); + buffer.extend_from_slice(self.auth.as_bytes()); + if self.auth_handle != TPM20_RH_OWNER { + buffer.extend_from_slice(self.auth_value.as_bytes()); + } + buffer.extend_from_slice(&self.data.serialize()); + buffer.extend_from_slice(self.offset.as_bytes()); + + buffer + } + + pub fn payload_size(&self) -> usize { + let mut payload_size = 0; + + payload_size += size_of_val(&self.header); + payload_size += size_of_val(&self.auth_handle); + payload_size += size_of_val(&self.nv_index); + payload_size += size_of_val(&self.auth_size); + payload_size += size_of_val(&self.auth); + if self.auth_handle != TPM20_RH_OWNER { + payload_size += size_of_val(&self.auth_value); + } + payload_size += self.data.payload_size(); + payload_size += size_of_val(&self.offset); + + payload_size + } + } + + #[repr(C)] + #[derive(Debug, FromBytes, IntoBytes, Immutable, KnownLayout)] + pub struct NvWriteReply { + pub header: ReplyHeader, + } + + impl TpmCommand for NvWriteCmd { + type Reply = NvWriteReply; + } + + impl TpmReply for NvWriteReply { + type Command = NvWriteCmd; + + fn deserialize(bytes: &[u8]) -> Option { + Some(Self::read_from_prefix(bytes).ok()?.0) // TODO: zerocopy: tpm better error? (https://github.com/microsoft/openvmm/issues/759) + } + + fn payload_size(&self) -> usize { + size_of::() + } + } + + // === Nv Read === // + + #[repr(C)] + #[derive(IntoBytes, Immutable, KnownLayout, FromBytes)] + pub struct NvReadCmd { + header: CmdHeader, + auth_handle: ReservedHandle, + pub nv_index: u32_be, + // Authorization area + auth_size: u32_be, + auth: common::CmdAuth, + // Parameters + size: u16_be, + pub offset: u16_be, + } + + impl NvReadCmd { + pub fn new( + session: SessionTag, + auth_handle: ReservedHandle, + nv_index: u32, + auth: common::CmdAuth, + size: u16, + offset: u16, + ) -> Self { + Self { + header: CmdHeader::new::(session, CommandCodeEnum::NV_Read.into()), + auth_handle, + nv_index: nv_index.into(), + auth_size: (size_of::() as u32).into(), + auth, + size: size.into(), + offset: offset.into(), + } + } + + pub fn update_read_parameters(&mut self, size: u16, offset: u16) { + self.size = size.into(); + self.offset = offset.into(); + } + + pub fn deserialize(bytes: &[u8]) -> Option { + let mut start = 0; + let mut end = size_of::(); + if bytes.len() < end { + return None; + } + let header = CmdHeader::read_from_prefix(&bytes[start..end]).ok()?.0; // TODO: zerocopy: use-rest-of-range, option-to-error (https://github.com/microsoft/openvmm/issues/759) + + if header.command_code != CommandCodeEnum::NV_Read.into() { + return None; + } + + start = end; + end += size_of::(); + if bytes.len() < end { + return None; + } + let auth_handle = ReservedHandle::read_from_prefix(&bytes[start..end]).ok()?.0; // TODO: zerocopy: use-rest-of-range, option-to-error (https://github.com/microsoft/openvmm/issues/759) + + start = end; + end += size_of::(); + if bytes.len() < end { + return None; + } + let nv_index = u32_be::read_from_prefix(&bytes[start..end]).ok()?.0; // TODO: zerocopy: use-rest-of-range, option-to-error (https://github.com/microsoft/openvmm/issues/759) + + start = end; + end += size_of::(); + if bytes.len() < end { + return None; + } + let auth_size = u32_be::read_from_prefix(&bytes[start..end]).ok()?.0; // TODO: zerocopy: use-rest-of-range, option-to-error (https://github.com/microsoft/openvmm/issues/759) + + // Skip authorization area + end += auth_size.get() as usize; + + start = end; + end += size_of::(); + if bytes.len() < end { + return None; + } + let size = u16_be::read_from_prefix(&bytes[start..end]).ok()?.0; // TODO: zerocopy: use-rest-of-range, option-to-error (https://github.com/microsoft/openvmm/issues/759) + + start = end; + end += size_of::(); + if bytes.len() < end { + return None; + } + let offset = u16_be::read_from_prefix(&bytes[start..end]).ok()?.0; // TODO: zerocopy: use-rest-of-range, option-to-error (https://github.com/microsoft/openvmm/issues/759) + + Some(Self { + header, + auth_handle, + nv_index, + auth_size, + auth: common::CmdAuth::new(ReservedHandle(0.into()), 0, 0, 0), + size, + offset, + }) + } + } + + #[repr(C)] + #[derive(Debug, FromBytes, IntoBytes, Immutable, KnownLayout)] + pub struct NvReadReply { + pub header: ReplyHeader, + pub parameter_size: u32_be, + // Parameter + pub data: Tpm2bBuffer, + // Authorization area + pub auth: common::ReplyAuth, + } + + impl TpmCommand for NvReadCmd { + type Reply = NvReadReply; + } + + impl TpmReply for NvReadReply { + type Command = NvReadCmd; + + fn deserialize(bytes: &[u8]) -> Option { + let mut start = 0; + let mut end = size_of::(); + + let header = ReplyHeader::read_from_prefix(&bytes[start..end]).ok()?.0; // TODO: zerocopy: use-rest-of-range, option-to-error (https://github.com/microsoft/openvmm/issues/759) + + // Handle the command failure. + if header.size.get() as usize == end { + return Some(Self { + header, + parameter_size: 0.into(), + data: Tpm2bBuffer::new_zeroed(), + auth: common::ReplyAuth::new_zeroed(), + }); + } + + start = end; + end += size_of::(); + if bytes.len() < end { + return None; + } + let parameter_size = u32_be::read_from_prefix(&bytes[start..end]).ok()?.0; // TODO: zerocopy: use-rest-of-range, option-to-error (https://github.com/microsoft/openvmm/issues/759) + + start = end; + let data = Tpm2bBuffer::deserialize(&bytes[start..])?; + end += data.payload_size(); + + start = end; + end += size_of::(); + if bytes.len() < end { + return None; + } + let auth = common::ReplyAuth::read_from_prefix(&bytes[start..end]) + .ok()? + .0; // TODO: zerocopy: use-rest-of-range, option-to-error (https://github.com/microsoft/openvmm/issues/759) + + if header.size.get() as usize != end { + return None; + } + + Some(Self { + header, + parameter_size, + data, + auth, + }) + } + + fn payload_size(&self) -> usize { + let mut size = 0; + + size += size_of::(); + size += self.data.payload_size(); + + size + } + } + + // === Import === // + + #[repr(C)] + #[derive(IntoBytes, Immutable, KnownLayout, FromBytes)] + pub struct ImportCmd { + pub header: CmdHeader, + pub auth_handle: ReservedHandle, + // Authorization area + pub auth_size: u32_be, + pub auth: common::CmdAuth, + // Parameters + // `TPM2B_DATA` + pub encryption_key: Tpm2bBuffer, + // `TPM2B_PUBLIC` + pub object_public: Tpm2bPublic, + // `TPM2B_PRIVATE` + pub duplicate: Tpm2bBuffer, + // `TPM2B_ENCRYPTED_SECRET` + pub in_sym_seed: Tpm2bBuffer, + // `TPMT_SYM_DEF_OBJECT` + pub symmetric_alg: TpmtSymDefObject, + } + + impl ImportCmd { + pub fn new( + session: SessionTag, + auth_handle: ReservedHandle, + auth: common::CmdAuth, + encryption_key: &Tpm2bBuffer, + object_public: &Tpm2bPublic, + duplicate: &Tpm2bBuffer, + in_sym_seed: &Tpm2bBuffer, + symmetric_alg: &TpmtSymDefObject, + ) -> Self { + let mut cmd = Self { + header: CmdHeader::new::(session, CommandCodeEnum::Import.into()), + auth_handle, + auth_size: (size_of::() as u32).into(), + auth, + encryption_key: *encryption_key, + object_public: *object_public, + duplicate: *duplicate, + in_sym_seed: *in_sym_seed, + symmetric_alg: *symmetric_alg, + }; + + cmd.header.size = new_u32_be(cmd.payload_size() as u32); + + cmd + } + + /// Deserialize the command payload assuming no inner wrapping key + pub fn deserialize_no_wrapping_key(bytes: &[u8]) -> Option { + let mut start = 0; + let mut end = 0; + + // When there is no inner wrapper for `duplicate`, `encryption_key` + // should be an empty buffer and `symmetric_alg` should be `TPM_ALG_NULL`. + // See Table 42, Section 13.3.2, "Trusted Platform Module Library Part 3: Commands", revision 1.38. + let encryption_key = Tpm2bBuffer::new_zeroed(); + let symmetric_alg = TpmtSymDefObject::new(AlgIdEnum::NULL.into(), None, None); + + let object_public = Tpm2bPublic::deserialize(&bytes[start..])?; + end += object_public.payload_size(); + + start = end; + let duplicate = Tpm2bBuffer::deserialize(&bytes[start..])?; + end += duplicate.payload_size(); + + start = end; + let in_sym_seed = Tpm2bBuffer::deserialize(&bytes[start..])?; + end += in_sym_seed.payload_size(); + + // Handle zero paddings applied to valid payload + if bytes.len() < end { + return None; + } + + Some(Self { + header: CmdHeader::new_zeroed(), + auth_handle: ReservedHandle(0.into()), + auth_size: 0.into(), + auth: common::CmdAuth::new_zeroed(), + encryption_key, + object_public, + duplicate, + in_sym_seed, + symmetric_alg, + }) + } + + pub fn serialize(&self) -> Vec { + let mut buffer = Vec::new(); + + buffer.extend_from_slice(self.header.as_bytes()); + buffer.extend_from_slice(self.auth_handle.as_bytes()); + buffer.extend_from_slice(self.auth_size.as_bytes()); + buffer.extend_from_slice(self.auth.as_bytes()); + buffer.extend_from_slice(&self.encryption_key.serialize()); + buffer.extend_from_slice(&self.object_public.serialize()); + buffer.extend_from_slice(&self.duplicate.serialize()); + buffer.extend_from_slice(&self.in_sym_seed.serialize()); + buffer.extend_from_slice(&self.symmetric_alg.serialize()); + + buffer + } + + pub fn payload_size(&self) -> usize { + let mut payload_size = 0; + + payload_size += size_of_val(&self.header); + payload_size += size_of_val(&self.auth_handle); + payload_size += size_of_val(&self.auth_size); + payload_size += size_of_val(&self.auth); + payload_size += self.encryption_key.payload_size(); + payload_size += self.object_public.payload_size(); + payload_size += self.duplicate.payload_size(); + payload_size += self.in_sym_seed.payload_size(); + payload_size += self.symmetric_alg.payload_size(); + + payload_size + } + } + + #[repr(C)] + #[derive(Debug, FromBytes, IntoBytes, Immutable, KnownLayout)] + pub struct ImportReply { + pub header: ReplyHeader, + pub parameter_size: u32_be, + // Parameter + // `TPM2B_PRIVATE` + pub out_private: Tpm2bBuffer, + // Authorization area + pub auth: common::ReplyAuth, + } + + impl TpmCommand for ImportCmd { + type Reply = ImportReply; + } + + impl TpmReply for ImportReply { + type Command = ImportCmd; + + fn deserialize(bytes: &[u8]) -> Option { + let mut start = 0; + let mut end = size_of::(); + + let header = ReplyHeader::read_from_prefix(&bytes[start..end]).ok()?.0; // TODO: zerocopy: use-rest-of-range, option-to-error (https://github.com/microsoft/openvmm/issues/759) + + // Handle the command failure. + if header.size.get() as usize == end { + return Some(Self { + header, + parameter_size: 0.into(), + out_private: Tpm2bBuffer::new_zeroed(), + auth: common::ReplyAuth::new_zeroed(), + }); + } + + start = end; + end += size_of::(); + if bytes.len() < end { + return None; + } + let parameter_size = u32_be::read_from_prefix(&bytes[start..end]).ok()?.0; // TODO: zerocopy: use-rest-of-range, option-to-error (https://github.com/microsoft/openvmm/issues/759) + let expected_auth_start = end + parameter_size.get() as usize; + + start = end; + let out_private = Tpm2bBuffer::deserialize(&bytes[start..])?; + end += out_private.payload_size(); + + start = end; + if start != expected_auth_start { + return None; + } + end += size_of::(); + if bytes.len() < end { + return None; + } + let auth = common::ReplyAuth::read_from_prefix(&bytes[start..end]) + .ok()? + .0; // TODO: zerocopy: use-rest-of-range, option-to-error (https://github.com/microsoft/openvmm/issues/759) + + if header.size.get() as usize != end { + return None; + } + + Some(Self { + header, + parameter_size, + out_private, + auth, + }) + } + + fn payload_size(&self) -> usize { + let mut size = 0; + + size += size_of::(); + size += self.out_private.payload_size(); + + size + } + } + + // === Load === // + + #[repr(C)] + #[derive(IntoBytes, Immutable, KnownLayout, FromBytes)] + pub struct LoadCmd { + header: CmdHeader, + auth_handle: ReservedHandle, + // Authorization area + auth_size: u32_be, + auth: common::CmdAuth, + // Parameters + // `TPM2B_PRIVATE` + in_private: Tpm2bBuffer, + // `TPM2B_PUBLIC` + in_public: Tpm2bPublic, + } + + impl LoadCmd { + pub fn new( + session: SessionTag, + auth_handle: ReservedHandle, + auth: common::CmdAuth, + in_private: &Tpm2bBuffer, + in_public: &Tpm2bPublic, + ) -> Self { + let mut cmd = Self { + header: CmdHeader::new::(session, CommandCodeEnum::Load.into()), + auth_handle, + auth_size: (size_of::() as u32).into(), + auth, + in_private: *in_private, + in_public: *in_public, + }; + + cmd.header.size = new_u32_be(cmd.payload_size() as u32); + + cmd + } + + pub fn serialize(&self) -> Vec { + let mut buffer = Vec::new(); + + buffer.extend_from_slice(self.header.as_bytes()); + buffer.extend_from_slice(self.auth_handle.as_bytes()); + buffer.extend_from_slice(self.auth_size.as_bytes()); + buffer.extend_from_slice(self.auth.as_bytes()); + buffer.extend_from_slice(&self.in_private.serialize()); + buffer.extend_from_slice(&self.in_public.serialize()); + + buffer + } + + pub fn payload_size(&self) -> usize { + let mut payload_size = 0; + + payload_size += size_of_val(&self.header); + payload_size += size_of_val(&self.auth_handle); + payload_size += size_of_val(&self.auth_size); + payload_size += size_of_val(&self.auth); + payload_size += self.in_private.payload_size(); + payload_size += self.in_public.payload_size(); + + payload_size + } + } + + #[repr(C)] + #[derive(Debug, FromBytes, IntoBytes, Immutable, KnownLayout)] + pub struct LoadReply { + pub header: ReplyHeader, + pub object_handle: ReservedHandle, + pub parameter_size: u32_be, + // Parameter + // `TPM2B_NAME` + pub name: Tpm2bBuffer, + // Authorization area + pub auth: common::ReplyAuth, + } + + impl TpmCommand for LoadCmd { + type Reply = LoadReply; + } + + impl TpmReply for LoadReply { + type Command = LoadCmd; + + fn deserialize(bytes: &[u8]) -> Option { + let mut start = 0; + let mut end = size_of::(); + + let header = ReplyHeader::read_from_prefix(&bytes[start..end]).ok()?.0; // TODO: zerocopy: use-rest-of-range, option-to-error (https://github.com/microsoft/openvmm/issues/759) + + // Handle the command failure. + if header.size.get() as usize == end { + return Some(Self { + header, + object_handle: ReservedHandle::new_zeroed(), + parameter_size: 0.into(), + name: Tpm2bBuffer::new_zeroed(), + auth: common::ReplyAuth::new_zeroed(), + }); + } + + start = end; + end += size_of::(); + if bytes.len() < end { + return None; + } + let object_handle = ReservedHandle::read_from_prefix(&bytes[start..end]).ok()?.0; // TODO: zerocopy: use-rest-of-range, option-to-error (https://github.com/microsoft/openvmm/issues/759) + + start = end; + end += size_of::(); + if bytes.len() < end { + return None; + } + let parameter_size = u32_be::read_from_prefix(&bytes[start..end]).ok()?.0; // TODO: zerocopy: use-rest-of-range, option-to-error (https://github.com/microsoft/openvmm/issues/759) + let expected_auth_start = end + parameter_size.get() as usize; + + start = end; + let name = Tpm2bBuffer::deserialize(&bytes[start..])?; + end += name.payload_size(); + + start = end; + if start != expected_auth_start { + return None; + } + end += size_of::(); + if bytes.len() < end { + return None; + } + let auth = common::ReplyAuth::read_from_prefix(&bytes[start..end]) + .ok()? + .0; // TODO: zerocopy: use-rest-of-range, option-to-error (https://github.com/microsoft/openvmm/issues/759) + + if header.size.get() as usize != end { + return None; + } + + Some(Self { + header, + object_handle, + parameter_size, + name, + auth, + }) + } + + fn payload_size(&self) -> usize { + let mut size = 0; + + size += size_of::(); + size += size_of::(); + size += self.name.payload_size(); + + size + } + } +} + +#[cfg(test)] +mod tests { + use super::protocol::common::*; + use super::protocol::*; + use super::*; + + #[test] + fn test_create_primary() { + const AK_PUB_EXPECTED_CMD: [u8; 321] = [ + 0x80, 0x02, 0x00, 0x00, 0x01, 0x41, 0x00, 0x00, 0x01, 0x31, 0x40, 0x00, 0x00, 0x0b, + 0x00, 0x00, 0x00, 0x09, 0x40, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0x18, 0x00, 0x01, 0x00, 0x0b, 0x00, 0x05, 0x04, + 0x72, 0x00, 0x00, 0x00, 0x10, 0x00, 0x14, 0x00, 0x0b, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ]; + + const AK_PUB_REPLY_SUCCEED: [u8; 488] = [ + 0x80, 0x02, 0x00, 0x00, 0x01, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0xd1, 0x01, 0x18, 0x00, 0x01, 0x00, 0x0b, 0x00, 0x05, 0x04, 0x72, + 0x00, 0x00, 0x00, 0x10, 0x00, 0x14, 0x00, 0x0b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0xc8, 0x38, 0xd1, 0x52, 0x00, 0x00, 0xe9, 0x3c, 0x89, 0x4c, 0x52, 0xfb, + 0x79, 0x7b, 0xc4, 0x14, 0x28, 0x5f, 0xaa, 0x50, 0x78, 0x9a, 0x31, 0x2b, 0x4d, 0xfe, + 0xad, 0xad, 0x97, 0x28, 0x49, 0xb2, 0x39, 0x77, 0x5e, 0x06, 0x49, 0xb7, 0x93, 0xf5, + 0x2f, 0x84, 0x85, 0x2e, 0x17, 0x87, 0x52, 0x96, 0x36, 0x74, 0x76, 0x21, 0x5f, 0xc2, + 0x90, 0x81, 0xf7, 0xe9, 0xd8, 0xac, 0x07, 0x60, 0xaf, 0x83, 0xa2, 0x08, 0xda, 0x94, + 0x77, 0x2c, 0x73, 0x9c, 0xd4, 0x80, 0x47, 0x43, 0xa6, 0x4e, 0x36, 0xc3, 0x7e, 0xe2, + 0x9c, 0xfb, 0xf1, 0x7e, 0x36, 0x8e, 0x7a, 0x86, 0xde, 0x3d, 0x4e, 0x8a, 0x3a, 0xce, + 0x7a, 0xa1, 0x58, 0xf6, 0xdb, 0x49, 0x3e, 0xc2, 0x2e, 0xcb, 0x4a, 0xbc, 0x19, 0x81, + 0xd5, 0x5d, 0x4f, 0x57, 0x39, 0xf5, 0x9e, 0x02, 0x56, 0x91, 0x37, 0xc2, 0x87, 0x96, + 0x26, 0xd8, 0x4a, 0x45, 0x16, 0x01, 0xe0, 0x2e, 0x20, 0x95, 0x75, 0xb8, 0x20, 0x6d, + 0x83, 0x54, 0x65, 0x3d, 0x66, 0xf4, 0x8a, 0x43, 0x84, 0x9f, 0xa6, 0xc5, 0x2c, 0x08, + 0xe7, 0x59, 0x8e, 0x1f, 0x6d, 0xea, 0x32, 0x5b, 0x36, 0x8e, 0xd1, 0xf3, 0x09, 0x60, + 0x86, 0xdb, 0x55, 0xc9, 0xf0, 0xf9, 0x79, 0x87, 0x71, 0x1c, 0x7c, 0x98, 0xa4, 0xc8, + 0x91, 0x77, 0xa7, 0x95, 0x82, 0x19, 0xcc, 0x9d, 0xde, 0x4d, 0x7b, 0xf7, 0xc1, 0x31, + 0x5b, 0xae, 0x45, 0x6e, 0x6b, 0xf1, 0xaf, 0x89, 0x07, 0x91, 0x80, 0x9d, 0xe5, 0x49, + 0xfc, 0x5e, 0xb2, 0x15, 0x67, 0xcf, 0x05, 0xbb, 0xb3, 0x98, 0x54, 0x34, 0x45, 0x2c, + 0xc3, 0x3d, 0x09, 0x8e, 0x8d, 0x60, 0xba, 0x67, 0xd9, 0xbe, 0x1c, 0x2a, 0x2c, 0x2a, + 0xfa, 0xed, 0x26, 0x81, 0x96, 0x48, 0x17, 0xb3, 0xa6, 0x90, 0x9a, 0x78, 0xa5, 0xac, + 0x80, 0xb2, 0xbe, 0xff, 0x3d, 0x35, 0x00, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, + 0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, + 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, + 0x78, 0x52, 0xb8, 0x55, 0x01, 0x00, 0x10, 0x00, 0x04, 0x40, 0x00, 0x00, 0x0b, 0x00, + 0x04, 0x40, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x20, 0x28, 0xd0, 0x26, 0xfa, 0xfd, + 0x74, 0x91, 0x06, 0x74, 0x3e, 0x27, 0xc4, 0x28, 0x05, 0x51, 0x58, 0x5e, 0x5d, 0x17, + 0x66, 0x8e, 0xb5, 0x21, 0x83, 0x5e, 0xd6, 0x01, 0x27, 0xef, 0xfc, 0x05, 0xd4, 0x80, + 0x21, 0x40, 0x00, 0x00, 0x0b, 0x00, 0x30, 0xfb, 0xfe, 0xd4, 0xe7, 0x9f, 0xc5, 0x2f, + 0xfd, 0x7c, 0xe0, 0x4a, 0x97, 0xb5, 0xec, 0x61, 0x59, 0x4d, 0x43, 0x19, 0x29, 0xc0, + 0x4f, 0xef, 0xda, 0xdc, 0xe1, 0x48, 0x4d, 0xbd, 0x3d, 0x47, 0x0e, 0xe3, 0x2f, 0xd4, + 0xf9, 0x57, 0x4f, 0x77, 0x0f, 0x58, 0x5c, 0x73, 0x58, 0xc2, 0x2d, 0xd7, 0x4a, 0x00, + 0x22, 0x00, 0x0b, 0x92, 0x57, 0x64, 0x38, 0x21, 0xf9, 0x68, 0xe9, 0xfc, 0x47, 0xfa, + 0xbf, 0x9c, 0x56, 0x49, 0x7a, 0x63, 0xc2, 0xc0, 0x8a, 0x12, 0x80, 0x49, 0x73, 0xc3, + 0x8b, 0x00, 0x06, 0x99, 0xe9, 0xfc, 0x22, 0x00, 0x00, 0x01, 0x00, 0x00, + ]; + + const EK_PUB_EXPECTED_CMD: [u8; 355] = [ + 0x80, 0x02, 0x00, 0x00, 0x01, 0x63, 0x00, 0x00, 0x01, 0x31, 0x40, 0x00, 0x00, 0x0b, + 0x00, 0x00, 0x00, 0x09, 0x40, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0x3a, 0x00, 0x01, 0x00, 0x0b, 0x00, 0x03, 0x00, + 0xb2, 0x00, 0x20, 0x83, 0x71, 0x97, 0x67, 0x44, 0x84, 0xb3, 0xf8, 0x1a, 0x90, 0xcc, + 0x8d, 0x46, 0xa5, 0xd7, 0x24, 0xfd, 0x52, 0xd7, 0x6e, 0x06, 0x52, 0x0b, 0x64, 0xf2, + 0xa1, 0xda, 0x1b, 0x33, 0x14, 0x69, 0xaa, 0x00, 0x06, 0x00, 0x80, 0x00, 0x43, 0x00, + 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + ]; + + const EK_PUB_REPLY_SUCCEED: [u8; 522] = [ + 0x80, 0x02, 0x00, 0x00, 0x02, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0xf3, 0x01, 0x3a, 0x00, 0x01, 0x00, 0x0b, 0x00, 0x03, 0x00, 0xb2, + 0x00, 0x20, 0x83, 0x71, 0x97, 0x67, 0x44, 0x84, 0xb3, 0xf8, 0x1a, 0x90, 0xcc, 0x8d, + 0x46, 0xa5, 0xd7, 0x24, 0xfd, 0x52, 0xd7, 0x6e, 0x06, 0x52, 0x0b, 0x64, 0xf2, 0xa1, + 0xda, 0x1b, 0x33, 0x14, 0x69, 0xaa, 0x00, 0x06, 0x00, 0x80, 0x00, 0x43, 0x00, 0x10, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x9e, 0x9c, 0x1b, 0x40, 0x00, 0x00, + 0xea, 0x2f, 0xd5, 0xd7, 0xde, 0x9b, 0x18, 0x83, 0x55, 0x00, 0x09, 0x53, 0x13, 0xa8, + 0x88, 0x10, 0x24, 0x46, 0x44, 0xa8, 0x2d, 0x62, 0xd3, 0x24, 0xe5, 0xf9, 0xcd, 0xca, + 0x61, 0xb7, 0xd8, 0x15, 0x98, 0xf8, 0x56, 0x64, 0x14, 0x7b, 0x40, 0x5a, 0x47, 0xbd, + 0xd1, 0xc8, 0x7d, 0x1f, 0x93, 0x72, 0x3f, 0x03, 0xe0, 0x29, 0x38, 0x08, 0x03, 0xae, + 0x62, 0x13, 0x10, 0xf5, 0x88, 0x5f, 0x86, 0x84, 0x82, 0xfb, 0xda, 0xd8, 0x78, 0xfd, + 0x02, 0x9e, 0x88, 0x5c, 0xaf, 0x30, 0xd4, 0x3d, 0x41, 0xb2, 0xb7, 0x7a, 0x36, 0xa5, + 0x95, 0x37, 0x08, 0x44, 0x20, 0x10, 0xb3, 0x6c, 0xd0, 0x6d, 0xe9, 0xab, 0xce, 0x35, + 0xc0, 0x82, 0x52, 0x06, 0x41, 0x4c, 0xc5, 0x48, 0x5b, 0xe6, 0x22, 0x00, 0x7e, 0x1d, + 0x4b, 0x68, 0x80, 0x34, 0xe9, 0xea, 0x6e, 0xf9, 0xf7, 0xf7, 0x84, 0xbe, 0x56, 0xdf, + 0xea, 0x85, 0x97, 0x1b, 0x03, 0x5c, 0x5c, 0x9f, 0xf4, 0x72, 0xef, 0xe7, 0xfe, 0x5e, + 0x73, 0x2f, 0xf1, 0xdd, 0x40, 0x80, 0x16, 0x8d, 0x1b, 0x95, 0xee, 0xec, 0x21, 0x1c, + 0x30, 0x84, 0x25, 0x08, 0x8d, 0x0e, 0xda, 0x5b, 0x00, 0x9c, 0x49, 0x8b, 0xc8, 0xb3, + 0x48, 0x9a, 0xc9, 0x19, 0x0f, 0x68, 0xc7, 0x0a, 0x7a, 0x65, 0x35, 0xa0, 0x09, 0x23, + 0x88, 0x3f, 0x97, 0x53, 0x4e, 0xbc, 0x08, 0xc0, 0x5b, 0x69, 0x94, 0xcc, 0xd9, 0xb9, + 0xea, 0x8c, 0x20, 0x9e, 0x1a, 0xf9, 0x57, 0x08, 0x1a, 0xe0, 0x2d, 0x88, 0x56, 0x1f, + 0x9f, 0x50, 0x2e, 0x12, 0xf2, 0x69, 0x9a, 0xdf, 0x30, 0x56, 0xc1, 0xf0, 0x31, 0xef, + 0x64, 0xd5, 0x34, 0x02, 0x15, 0xf4, 0xd7, 0x7b, 0x76, 0xd9, 0x99, 0x24, 0x83, 0x99, + 0xa5, 0x05, 0xc1, 0xcd, 0xa6, 0xbd, 0xc3, 0x3d, 0x7c, 0x1e, 0x94, 0xdd, 0x00, 0x37, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, + 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, + 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55, 0x01, 0x00, 0x10, 0x00, + 0x04, 0x40, 0x00, 0x00, 0x0b, 0x00, 0x04, 0x40, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x20, 0x28, 0xd0, 0x26, 0xfa, 0xfd, 0x74, 0x91, 0x06, 0x74, 0x3e, 0x27, 0xc4, 0x28, + 0x05, 0x51, 0x58, 0x5e, 0x5d, 0x17, 0x66, 0x8e, 0xb5, 0x21, 0x83, 0x5e, 0xd6, 0x01, + 0x27, 0xef, 0xfc, 0x05, 0xd4, 0x80, 0x21, 0x40, 0x00, 0x00, 0x0b, 0x00, 0x30, 0xe2, + 0xf2, 0x64, 0xc3, 0xd7, 0x9e, 0xc1, 0x07, 0xbb, 0x49, 0x74, 0x67, 0xd3, 0xc7, 0xf6, + 0xb7, 0x8c, 0xe3, 0x2e, 0x28, 0x36, 0xa6, 0x1f, 0x6f, 0x0b, 0xbd, 0xe3, 0x8e, 0x77, + 0xa1, 0x8c, 0x50, 0xe4, 0xaa, 0xa4, 0x01, 0x61, 0xb4, 0x7a, 0x4a, 0x3b, 0x5d, 0xac, + 0xe1, 0xd1, 0x65, 0x69, 0x1e, 0x00, 0x22, 0x00, 0x0b, 0xe5, 0x6f, 0x0f, 0xae, 0x8d, + 0x0f, 0x91, 0xb9, 0x84, 0x17, 0xc3, 0x86, 0x13, 0xa6, 0x12, 0xbe, 0xec, 0x85, 0xf9, + 0x0b, 0xd3, 0xfe, 0x4f, 0x3d, 0x79, 0x7d, 0x6d, 0x3c, 0xc5, 0xcc, 0xb1, 0x5b, 0x00, + 0x00, 0x01, 0x00, 0x00, + ]; + + const REPLY_FAIL: [u8; 10] = [0x80, 0x01, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x02, 0xda]; + + // Create AK pub + let symmetric = TpmtSymDefObject::new(AlgIdEnum::NULL.into(), None, None); + let scheme = TpmtRsaScheme::new(AlgIdEnum::RSASSA.into(), Some(AlgIdEnum::SHA256.into())); + let rsa_params = TpmsRsaParams::new(symmetric, scheme, 2048, 0); + + let object_attributes = TpmaObjectBits::new() + .with_fixed_tpm(true) + .with_fixed_parent(true) + .with_sensitive_data_origin(true) + .with_user_with_auth(true) + .with_no_da(true) + .with_restricted(true) + .with_sign_encrypt(true); + + let result = TpmtPublic::new( + AlgIdEnum::RSA.into(), + AlgIdEnum::SHA256.into(), + object_attributes, + &[], + rsa_params, + &[0u8; 256], + ); + assert!(result.is_ok()); + let in_public = result.unwrap(); + + let result = CreatePrimaryCmd::new( + SessionTagEnum::Sessions.into(), + TPM20_RH_ENDORSEMENT, + CmdAuth::new(TPM20_RS_PW, 0, 0, 0), + &[], + &[], + in_public, + &[], + &[], + ); + assert!(result.is_ok()); + let cmd = result.unwrap(); + + let bytes = cmd.serialize(); + + assert_eq!(bytes, AK_PUB_EXPECTED_CMD); + + let mut reply = [0u8; 4096]; + reply[..AK_PUB_REPLY_SUCCEED.len()].copy_from_slice(&AK_PUB_REPLY_SUCCEED); + + let response = CreatePrimaryReply::deserialize(&reply); + assert!(response.is_some()); + let response = response.unwrap(); + assert_eq!(response.header.response_code.get(), 0x0); + assert_eq!(response.object_handle.0.get(), 0x80000000); + + reply[..REPLY_FAIL.len()].copy_from_slice(&REPLY_FAIL); + + let response = CreatePrimaryReply::deserialize(&reply); + assert!(response.is_some()); + let response = response.unwrap(); + assert_eq!(response.header.response_code.get(), 0x2da); + + // Create EK pub + const AUTH_POLICY_A_SHA_256: [u8; 32] = [ + 0x83, 0x71, 0x97, 0x67, 0x44, 0x84, 0xB3, 0xF8, 0x1A, 0x90, 0xCC, 0x8D, 0x46, 0xA5, + 0xD7, 0x24, 0xFD, 0x52, 0xD7, 0x6E, 0x06, 0x52, 0x0B, 0x64, 0xF2, 0xA1, 0xDA, 0x1B, + 0x33, 0x14, 0x69, 0xAA, + ]; + let symmetric = TpmtSymDefObject::new( + AlgIdEnum::AES.into(), + Some(128), + Some(AlgIdEnum::CFB.into()), + ); + let scheme = TpmtRsaScheme::new(AlgIdEnum::NULL.into(), None); + let rsa_params = TpmsRsaParams::new(symmetric, scheme, 2048, 0); + + let object_attributes = TpmaObjectBits::new() + .with_fixed_tpm(true) + .with_fixed_parent(true) + .with_sensitive_data_origin(true) + .with_admin_with_policy(true) + .with_restricted(true) + .with_decrypt(true); + + let result = TpmtPublic::new( + AlgIdEnum::RSA.into(), + AlgIdEnum::SHA256.into(), + object_attributes, + &AUTH_POLICY_A_SHA_256, + rsa_params, + &[0u8; 256], + ); + assert!(result.is_ok()); + let in_public = result.unwrap(); + + let result = CreatePrimaryCmd::new( + SessionTagEnum::Sessions.into(), + TPM20_RH_ENDORSEMENT, + CmdAuth::new(TPM20_RS_PW, 0, 0, 0), + &[], + &[], + in_public, + &[], + &[], + ); + assert!(result.is_ok()); + let cmd = result.unwrap(); + + let bytes = cmd.serialize(); + + assert_eq!(bytes, EK_PUB_EXPECTED_CMD); + + reply[..EK_PUB_REPLY_SUCCEED.len()].copy_from_slice(&EK_PUB_REPLY_SUCCEED); + + let response = CreatePrimaryReply::deserialize(&reply); + assert!(response.is_some()); + let response = response.unwrap(); + assert_eq!(response.header.response_code.get(), 0x0); + assert_eq!(response.object_handle.0.get(), 0x80000000); + } + + #[test] + fn test_read_public() { + const REPLY_SUCCEED: [u8; 364] = [ + 0x80, 0x01, 0x00, 0x00, 0x01, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x01, 0x18, 0x00, 0x01, + 0x00, 0x0b, 0x00, 0x05, 0x04, 0x72, 0x00, 0x00, 0x00, 0x10, 0x00, 0x14, 0x00, 0x0b, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xa6, 0xaf, 0x71, 0xec, 0x00, 0x00, + 0xe0, 0x69, 0xa5, 0xc5, 0xcd, 0x94, 0x59, 0x3b, 0x79, 0xe6, 0xee, 0x14, 0xd3, 0x50, + 0xfb, 0x0b, 0xa9, 0x03, 0x51, 0xbf, 0x23, 0xc5, 0x15, 0xdc, 0xbc, 0x4a, 0x3b, 0xaa, + 0xef, 0x12, 0x3c, 0x24, 0x47, 0xf2, 0x81, 0xf6, 0x85, 0xf4, 0x8c, 0x16, 0x14, 0x10, + 0x3c, 0x3b, 0x2e, 0x7b, 0x04, 0x5e, 0x25, 0x66, 0xcd, 0x8d, 0x86, 0x0b, 0x8c, 0x2b, + 0x5f, 0xca, 0x36, 0x1d, 0x5f, 0xff, 0xbf, 0x70, 0x63, 0x79, 0x5b, 0x7f, 0x93, 0x94, + 0x6d, 0xbd, 0x6e, 0x4f, 0x22, 0x94, 0x93, 0x87, 0xe1, 0x63, 0x4d, 0xa4, 0x9a, 0x2f, + 0xad, 0x90, 0x4c, 0xc9, 0x37, 0x14, 0x59, 0xd3, 0x03, 0x6d, 0x37, 0x98, 0xd4, 0x85, + 0x19, 0x9b, 0x93, 0x7e, 0x61, 0x93, 0x6d, 0x1c, 0xe0, 0xe6, 0x72, 0x71, 0x81, 0x45, + 0xe0, 0xea, 0x5f, 0xb4, 0x6a, 0x9a, 0x3e, 0x86, 0x60, 0x86, 0xaf, 0xfc, 0x86, 0x0f, + 0x0d, 0xe8, 0x81, 0x46, 0x59, 0xad, 0xeb, 0x6f, 0xef, 0x38, 0x5e, 0x53, 0xea, 0x91, + 0xcb, 0xa9, 0xf8, 0x31, 0xcd, 0x52, 0x85, 0x55, 0xa8, 0x91, 0x68, 0xd8, 0xdd, 0x20, + 0x67, 0x21, 0x30, 0x03, 0xcd, 0x48, 0x3b, 0xb0, 0x33, 0x16, 0xb4, 0xf0, 0x06, 0x55, + 0xdf, 0x15, 0xd2, 0x65, 0x55, 0x2f, 0xec, 0xec, 0xc5, 0x74, 0xea, 0xd8, 0x0f, 0x29, + 0xac, 0x24, 0x38, 0x32, 0x34, 0x1f, 0xb3, 0x20, 0x28, 0xf6, 0x55, 0xfb, 0x51, 0xf1, + 0x22, 0xa3, 0x5e, 0x38, 0xc6, 0xa5, 0xa4, 0xe0, 0xc2, 0xa3, 0x50, 0x27, 0xf6, 0x1d, + 0x55, 0x8e, 0x95, 0xe9, 0x95, 0x26, 0x8e, 0x70, 0x35, 0x7b, 0x73, 0xbb, 0x8e, 0xf2, + 0xdc, 0x37, 0x30, 0x99, 0x20, 0x2e, 0x1f, 0x09, 0xbd, 0x85, 0x24, 0x44, 0x05, 0x8f, + 0x11, 0xc4, 0xb5, 0x71, 0xc1, 0x2e, 0x52, 0xf6, 0x2e, 0x6f, 0x9a, 0x11, 0x00, 0x22, + 0x00, 0x0b, 0x61, 0xca, 0x8b, 0xec, 0x0f, 0x9e, 0xc1, 0x38, 0x35, 0xd3, 0x43, 0x58, + 0x77, 0xdf, 0x53, 0x82, 0xe7, 0xb2, 0xff, 0x7b, 0xe4, 0x6c, 0xfb, 0x34, 0xa4, 0x28, + 0xdd, 0xda, 0xcb, 0xe9, 0x50, 0x50, 0x00, 0x22, 0x00, 0x0b, 0x51, 0xfa, 0x43, 0xbd, + 0x35, 0x01, 0xd6, 0x66, 0xa0, 0x4d, 0xc8, 0x03, 0x4f, 0xa1, 0x64, 0xa0, 0x91, 0x63, + 0x3c, 0x27, 0xd5, 0x90, 0xa3, 0x7a, 0xae, 0xbc, 0x52, 0xcc, 0x4e, 0x9a, 0xa3, 0x66, + ]; + + const REPLY_FAIL: [u8; 10] = [0x80, 0x01, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x01, 0x8b]; + + let mut reply = [0u8; 4096]; + reply[..REPLY_SUCCEED.len()].copy_from_slice(&REPLY_SUCCEED); + + let response: Option = ReadPublicReply::deserialize(&reply); + assert!(response.is_some()); + let response = response.unwrap(); + assert_eq!(response.header.response_code.get(), 0x0); + + reply[..REPLY_FAIL.len()].copy_from_slice(&REPLY_FAIL); + + let response = ReadPublicReply::deserialize(&reply); + assert!(response.is_some()); + let response = response.unwrap(); + assert_eq!(response.header.response_code.get(), 0x18b); + } + + #[test] + fn test_nv_read_public() { + const REPLY_SUCCEED: [u8; 62] = [ + 0x80, 0x01, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x01, 0x40, + 0x00, 0x01, 0x00, 0x0b, 0x42, 0x06, 0x00, 0x04, 0x00, 0x00, 0x10, 0x00, 0x00, 0x22, + 0x00, 0x0b, 0xc1, 0x0f, 0x8d, 0x61, 0x77, 0xea, 0xd0, 0x29, 0x52, 0xa6, 0x2d, 0x3a, + 0x39, 0xc7, 0x22, 0x0b, 0xb9, 0xa1, 0xe1, 0xfe, 0x08, 0x68, 0xa8, 0x6f, 0x5f, 0x10, + 0xd6, 0x86, 0x83, 0x28, 0x79, 0x3e, + ]; + + const REPLY_FAIL: [u8; 10] = [0x80, 0x01, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x01, 0x8b]; + + let mut reply = [0u8; 4096]; + reply[..REPLY_SUCCEED.len()].copy_from_slice(&REPLY_SUCCEED); + + let response = NvReadPublicReply::deserialize(&reply); + assert!(response.is_some()); + let response = response.unwrap(); + assert_eq!(response.header.response_code.get(), 0x0); + + reply[..REPLY_FAIL.len()].copy_from_slice(&REPLY_FAIL); + + let response = NvReadPublicReply::deserialize(&reply); + assert!(response.is_some()); + let response = response.unwrap(); + assert_eq!(response.header.response_code.get(), 0x18b); + } + + #[test] + fn test_define_space() { + const EXPECTED_CMD: [u8; 53] = [ + 0x80, 0x02, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x01, 0x2a, 0x40, 0x00, 0x00, 0x0c, + 0x00, 0x00, 0x00, 0x09, 0x40, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00, 0x00, 0x0e, 0x01, 0xc1, 0x01, + 0xd0, 0x00, 0x0b, 0x42, 0x06, 0x00, 0x04, 0x00, 0x00, 0x10, 0x00, + ]; + + let auth_value: u64 = 0x7766554433221100; + + let attributes = TpmaNvBits::new() + .with_nv_authread(true) + .with_nv_authwrite(true) + .with_nv_ownerread(true) + .with_nv_platformcreate(true) + .with_nv_no_da(true); + + let result = TpmsNvPublic::new(0x1c101d0, AlgIdEnum::SHA256.into(), attributes, &[], 4096); + assert!(result.is_ok()); + let nv_public = result.unwrap(); + + let result = NvDefineSpaceCmd::new( + SessionTagEnum::Sessions.into(), + TPM20_RH_PLATFORM, + CmdAuth::new(TPM20_RS_PW, 0, 0, 0), + auth_value, + nv_public, + ); + assert!(result.is_ok()); + let cmd = result.unwrap(); + + let bytes = cmd.serialize(); + assert_eq!(bytes, EXPECTED_CMD); + } + + #[test] + fn test_nv_write_authwrite() { + const EXPECTED_CMD: [u8; 171] = [ + 0x80, 0x02, 0x00, 0x00, 0x00, 0xab, 0x00, 0x00, 0x01, 0x37, 0x01, 0xc1, 0x01, 0xd0, + 0x01, 0xc1, 0x01, 0xd0, 0x00, 0x00, 0x00, 0x11, 0x40, 0x00, 0x00, 0x09, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00, 0x00, 0x80, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, + ]; + let auth_value: u64 = 0x7766554433221100; + + let result = NvWriteCmd::new( + SessionTagEnum::Sessions.into(), + ReservedHandle(0x1c101d0.into()), + CmdAuth::new(TPM20_RS_PW, 0, 0, size_of_val(&auth_value) as u16), + auth_value, + 0x1c101d0, + &[1u8; 128], + 0, + ); + assert!(result.is_ok()); + let cmd = result.unwrap(); + + let bytes = cmd.serialize(); + assert_eq!(bytes, EXPECTED_CMD); + } + + #[test] + fn test_nv_write_ownerwrite() { + const EXPECTED_CMD: [u8; 163] = [ + 0x80, 0x02, 0x00, 0x00, 0x00, 0xa3, 0x00, 0x00, 0x01, 0x37, 0x40, 0x00, 0x00, 0x01, + 0x01, 0xc1, 0x01, 0xd0, 0x00, 0x00, 0x00, 0x09, 0x40, 0x00, 0x00, 0x09, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, + ]; + + let result = NvWriteCmd::new( + SessionTagEnum::Sessions.into(), + TPM20_RH_OWNER, + CmdAuth::new(TPM20_RS_PW, 0, 0, 0), + 0, + 0x1c101d0, + &[1u8; 128], + 0, + ); + assert!(result.is_ok()); + let cmd = result.unwrap(); + + let bytes = cmd.serialize(); + assert_eq!(bytes, EXPECTED_CMD); + } + + #[test] + fn test_nv_read() { + const REPLY_SUCCEED: [u8; 85] = [ + 0x80, 0x02, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, + 0x00, 0x40, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, + 0xdd, 0xee, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, + ]; + + const EXPECTED_DATA: [u8; 64] = [ + 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ]; + + let mut reply = [0u8; 4096]; + reply[..REPLY_SUCCEED.len()].copy_from_slice(&REPLY_SUCCEED); + + let response = NvReadReply::deserialize(&reply); + assert!(response.is_some()); + let response = response.unwrap(); + assert_eq!(response.header.response_code.get(), 0x0); + assert_eq!(response.data.buffer[..EXPECTED_DATA.len()], EXPECTED_DATA); + } +} diff --git a/opentmk/opentmk/src/hypercall.rs b/opentmk/opentmk/src/hypercall.rs index 88c75cf80c..b84849fa88 100644 --- a/opentmk/opentmk/src/hypercall.rs +++ b/opentmk/opentmk/src/hypercall.rs @@ -5,6 +5,7 @@ #![allow(dead_code)] use core::{ + arch::asm, mem::size_of, sync::atomic::{AtomicU16, Ordering}, }; @@ -12,7 +13,8 @@ use core::{ use arrayvec::ArrayVec; use hvdef::{ hypercall::{EnablePartitionVtlFlags, HvInputVtl, InitialVpContextX64}, - HvRegisterValue, HvRegisterVsmPartitionConfig, HvX64RegisterName, Vtl, HV_PAGE_SIZE, + HvRegisterValue, HvRegisterVsmPartitionConfig, HvX64RegisterName, HvX64SegmentRegister, Vtl, + HV_PAGE_SIZE, }; use memory_range::MemoryRange; use minimal_rt::arch::hypercall::{invoke_hypercall, HYPERCALL_PAGE}; @@ -301,12 +303,11 @@ impl HvCall { /// Enables VTL protection for the specified VTL. pub fn enable_vtl_protection(&mut self, vtl: HvInputVtl) -> Result<(), hvdef::HvError> { - let hvreg = self.get_register(HvX64RegisterName::VsmPartitionConfig.into(), Some(vtl))?; - let mut hvreg: HvRegisterVsmPartitionConfig = - HvRegisterVsmPartitionConfig::from_bits(hvreg.as_u64()); + // let hvreg = self.get_register(HvX64RegisterName::VsmPartitionConfig.into(), Some(vtl))?; + let mut hvreg: HvRegisterVsmPartitionConfig = HvRegisterVsmPartitionConfig::new(); hvreg.set_enable_vtl_protection(true); // hvreg.set_intercept_page(true); - // hvreg.set_default_vtl_protection_mask(0b11); + hvreg.set_default_vtl_protection_mask(0xF); // hvreg.set_intercept_enable_vtl_protection(true); let bits = hvreg.into_bits(); let hvre: HvRegisterValue = HvRegisterValue::from(bits); @@ -366,60 +367,248 @@ impl HvCall { } } + fn get_segment_descriptor(segment_reg: &str) -> HvX64SegmentRegister { + unsafe { + use core::arch::asm; + let mut descriptor = HvX64SegmentRegister { + base: 0, + limit: 0, + selector: 0, + attributes: 0, + }; + match segment_reg { + "cs" => { + asm!("mov {0:x}, cs", out(reg) descriptor.selector, options(nomem, nostack)) + } + "ds" => { + asm!("mov {0:x}, ds", out(reg) descriptor.selector, options(nomem, nostack)) + } + "es" => { + asm!("mov {0:x}, es", out(reg) descriptor.selector, options(nomem, nostack)) + } + "ss" => { + asm!("mov {0:x}, ss", out(reg) descriptor.selector, options(nomem, nostack)) + } + "fs" => { + asm!("mov {0:x}, fs", out(reg) descriptor.selector, options(nomem, nostack)) + } + "gs" => { + asm!("mov {0:x}, gs", out(reg) descriptor.selector, options(nomem, nostack)) + } + "tr" => asm!("str {0:x}", out(reg) descriptor.selector, options(nomem, nostack)), + _ => panic!("Invalid segment register"), + } + + // For FS and GS in 64-bit mode, we can get the base directly via MSRs + if segment_reg == "fs" { + let mut base_low: u32; + let mut base_high: u32; + asm!( + "mov ecx, 0xC0000100", // FS_BASE MSR + "rdmsr", + out("eax") base_low, + out("edx") base_high, + options(nomem, nostack) + ); + descriptor.base = ((base_high as u64) << 32) | (base_low as u64); + } else if segment_reg == "gs" { + let mut base_low: u32; + let mut base_high: u32; + asm!( + "mov ecx, 0xC0000101", // GS_BASE MSR + "rdmsr", + out("eax") base_low, + out("edx") base_high, + options(nomem, nostack) + ); + descriptor.base = ((base_high as u64) << 32) | (base_low as u64); + } else { + // For other segments, need to look up in GDT/LDT + // Allocate 10 bytes for storing GDTR/LDTR content + let mut descriptor_table = [0u8; 10]; + + // Determine if selector is in GDT or LDT + let table_indicator = descriptor.selector & 0x04; + + if table_indicator == 0 { + // Get GDT base + asm!("sgdt [{}]", in(reg) descriptor_table.as_mut_ptr(), options(nostack)); + } else { + // Get LDT base + asm!("sldt [{}]", in(reg) descriptor_table.as_mut_ptr(), options(nostack)); + } + + // Extract GDT/LDT base (bytes 2-9 of descriptor_table) + let table_base = u64::from_ne_bytes([ + descriptor_table[2], + descriptor_table[3], + descriptor_table[4], + descriptor_table[5], + descriptor_table[6], + descriptor_table[7], + descriptor_table[8], + descriptor_table[9], + ]); + + // Calculate descriptor entry address + let index = (descriptor.selector & 0xFFF8) as u64; // Clear RPL and TI bits + let desc_addr = table_base + index; + + // Read the 8-byte descriptor + let desc_bytes = alloc::slice::from_raw_parts(desc_addr as *const u8, 8); + let desc_low = u32::from_ne_bytes([ + desc_bytes[0], + desc_bytes[1], + desc_bytes[2], + desc_bytes[3], + ]); + let desc_high = u32::from_ne_bytes([ + desc_bytes[4], + desc_bytes[5], + desc_bytes[6], + desc_bytes[7], + ]); + + // Extract base (bits 16-39 and 56-63) + let base_low = ((desc_low >> 16) & 0xFFFF) as u64; + let base_mid = (desc_high & 0xFF) as u64; + let base_high = ((desc_high >> 24) & 0xFF) as u64; + descriptor.base = base_low | (base_mid << 16) | (base_high << 24); + + // Extract limit (bits 0-15 and 48-51) + let limit_low = desc_low & 0xFFFF; + let limit_high = (desc_high >> 16) & 0x0F; + descriptor.limit = limit_low | (limit_high << 16); + + // Extract attributes (bits 40-47 and 52-55) + let attr_low = (desc_high >> 8) & 0xFF; + let attr_high = (desc_high >> 20) & 0x0F; + descriptor.attributes = (attr_low as u16) | ((attr_high as u16) << 8); + + // If G bit is set (bit 55), the limit is in 4K pages + if (desc_high & 0x00800000) != 0 { + descriptor.limit = (descriptor.limit << 12) | 0xFFF; + } + + // For TR, which is a system segment in 64-bit mode, read the second 8 bytes to get the high 32 bits of base + if segment_reg == "tr" { + // Check if it's a system descriptor (bit 4 of attributes is 0) + if (descriptor.attributes & 0x10) == 0 { + // Read the next 8 bytes of the descriptor (high part of 16-byte descriptor) + let high_desc_bytes = + alloc::slice::from_raw_parts((desc_addr + 8) as *const u8, 8); + let high_base = u32::from_ne_bytes([ + high_desc_bytes[0], + high_desc_bytes[1], + high_desc_bytes[2], + high_desc_bytes[3], + ]) as u64; + + // Combine with existing base to get full 64-bit base + descriptor.base |= high_base << 32; + } + } + } + + descriptor + } + } + #[cfg(target_arch = "x86_64")] /// Hypercall to get the current VTL VP context pub fn get_current_vtl_vp_context(&mut self) -> Result { + use minimal_rt::arch::msr::read_msr; use zerocopy::FromZeros; - use HvX64RegisterName; let mut context: InitialVpContextX64 = FromZeros::new_zeroed(); - context.cr0 = self - .get_register(HvX64RegisterName::Cr0.into(), None)? - .as_u64(); - context.cr3 = self - .get_register(HvX64RegisterName::Cr3.into(), None)? - .as_u64(); - context.cr4 = self - .get_register(HvX64RegisterName::Cr4.into(), None)? - .as_u64(); - context.rip = self - .get_register(HvX64RegisterName::Rip.into(), None)? - .as_u64(); - context.rsp = self - .get_register(HvX64RegisterName::Rsp.into(), None)? - .as_u64(); - context.rflags = self - .get_register(HvX64RegisterName::Rflags.into(), None)? - .as_u64(); - context.cs = self - .get_register(HvX64RegisterName::Cs.into(), None)? - .as_segment(); - context.ss = self - .get_register(HvX64RegisterName::Ss.into(), None)? - .as_segment(); - context.ds = self - .get_register(HvX64RegisterName::Ds.into(), None)? - .as_segment(); - context.es = self - .get_register(HvX64RegisterName::Es.into(), None)? - .as_segment(); - context.fs = self - .get_register(HvX64RegisterName::Fs.into(), None)? - .as_segment(); - context.gs = self - .get_register(HvX64RegisterName::Gs.into(), None)? - .as_segment(); - context.gdtr = self - .get_register(HvX64RegisterName::Gdtr.into(), None)? - .as_table(); - context.idtr = self - .get_register(HvX64RegisterName::Idtr.into(), None)? - .as_table(); - context.tr = self - .get_register(HvX64RegisterName::Tr.into(), None)? - .as_segment(); - context.efer = self - .get_register(HvX64RegisterName::Efer.into(), None)? - .as_u64(); + + let rsp: u64; + unsafe { asm!("mov {0:r}, rsp", out(reg) rsp, options(nomem, nostack)) }; + + let cr0; + unsafe { asm!("mov {0:r}, cr0", out(reg) cr0, options(nomem, nostack)) }; + let cr3; + unsafe { asm!("mov {0:r}, cr3", out(reg) cr3, options(nomem, nostack)) }; + let cr4; + unsafe { asm!("mov {0:r}, cr4", out(reg) cr4, options(nomem, nostack)) }; + + let rflags: u64; + unsafe { + asm!( + "pushfq", + "pop {0}", + out(reg) rflags, + ); + } + + context.cr0 = cr0; + context.cr3 = cr3; + context.cr4 = cr4; + + context.rsp = rsp; + context.rip = 0; + + context.rflags = rflags; + + // load segment registers + + let cs: u16; + let ss: u16; + let ds: u16; + let es: u16; + let fs: u16; + let gs: u16; + + unsafe { + asm!(" + mov {0:x}, cs + mov {1:x}, ss + mov {2:x}, ds + mov {3:x}, es + mov {4:x}, fs + mov {5:x}, gs + ", out(reg) cs, out(reg) ss, out(reg) ds, out(reg) es, out(reg) fs, out(reg) gs, options(nomem, nostack)) + } + + context.cs.selector = cs; + context.cs.attributes = 0xA09B; + context.cs.limit = 0xFFFFFFFF; + + context.ss.selector = ss; + context.ss.attributes = 0xC093; + context.ss.limit = 0xFFFFFFFF; + + context.ds.selector = ds; + context.ds.attributes = 0xC093; + context.ds.limit = 0xFFFFFFFF; + + context.es.selector = es; + context.es.attributes = 0xC093; + context.es.limit = 0xFFFFFFFF; + + context.fs.selector = fs; + context.fs.attributes = 0xC093; + context.fs.limit = 0xFFFFFFFF; + + context.gs.selector = gs; + context.gs.attributes = 0xC093; + context.gs.limit = 0xFFFFFFFF; + + context.tr.selector = 0; + context.tr.attributes = 0x8B; + context.tr.limit = 0xFFFF; + + let idt = x86_64::instructions::tables::sidt(); + context.idtr.base = idt.base.as_u64(); + context.idtr.limit = idt.limit; + + let gdtr = x86_64::instructions::tables::sgdt(); + context.gdtr.base = gdtr.base.as_u64(); + context.gdtr.limit = gdtr.limit; + + let efer = unsafe { read_msr(0xC0000080) }; + context.efer = efer; + + log::info!("Current VTL VP context: {:?}", context); Ok(context) } diff --git a/opentmk/opentmk/src/main.rs b/opentmk/opentmk/src/main.rs index 3758aae71c..9445d689b2 100644 --- a/opentmk/opentmk/src/main.rs +++ b/opentmk/opentmk/src/main.rs @@ -23,3 +23,4 @@ pub mod tmk_assert; pub mod tmk_logger; pub mod tmkdefs; mod uefi; +pub mod devices; \ No newline at end of file diff --git a/opentmk/opentmk/src/platform/hypvctx.rs b/opentmk/opentmk/src/platform/hypvctx.rs index 61df84de13..f3a4607d03 100644 --- a/opentmk/opentmk/src/platform/hypvctx.rs +++ b/opentmk/opentmk/src/platform/hypvctx.rs @@ -3,15 +3,14 @@ use alloc::{ boxed::Box, collections::{btree_map::BTreeMap, btree_set::BTreeSet, linked_list::LinkedList}, }; -use core::{alloc::Layout, arch::asm, ops::Range}; +use core::{alloc::Layout, arch::asm, fmt::Display, ops::Range}; use hvdef::{ - hypercall::{self, HvInputVtl, InitialVpContextX64}, - HvRegisterValue, Vtl, + hypercall::{HvInputVtl, InitialVpContextX64}, + AlignedU128, Vtl, }; use memory_range::MemoryRange; use minimal_rt::arch::{ - hypercall::HYPERCALL_PAGE, msr::{read_msr, write_msr}, }; use sync_nostd::Mutex; @@ -47,11 +46,22 @@ fn register_command_queue(vp_index: u32) { pub struct HvTestCtx { pub hvcall: HvCall, + // BUG: make this static pub vp_runing: BTreeSet, pub my_vp_idx: u32, pub my_vtl: Vtl, } +impl Display for HvTestCtx { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!( + f, + "HvTestCtx {{ vp_idx: {}, vtl: {:?} }}", + self.my_vp_idx, self.my_vtl + ) + } +} + impl SecureInterceptPlatformTrait for HvTestCtx { /// Configure the Secure Interrupt Message Page (SIMP) and the first /// SynIC interrupt (SINT0) so that the hypervisor can vector @@ -173,7 +183,6 @@ impl VirtualProcessorPlatformTrait for HvTestCtx { Ok(()) } - #[inline(never)] /// Ensure the target VP is running in the requested VTL and queue /// the command for execution. @@ -190,18 +199,12 @@ impl VirtualProcessorPlatformTrait for HvTestCtx { if vtl >= Vtl::Vtl2 { return Err(TmkError(TmkErrorType::InvalidParameter)); } - log::debug!( - "VP{}: Queued command for VP{} in VTL{:?}", - self.my_vp_idx, - vp_index, - vtl - ); let is_vp_running = self.vp_runing.get(&vp_index); if let Some(_running_vtl) = is_vp_running { log::debug!("both vtl0 and vtl1 are running for VP: {:?}", vp_index); } else { if vp_index == 0 { - let vp_context = self.get_default_context()?; + let vp_context = self.get_default_context(Vtl::Vtl1)?; self.hvcall.enable_vp_vtl(0, Vtl::Vtl1, Some(vp_context))?; cmdt().lock().get_mut(&vp_index).unwrap().push_back(( @@ -213,12 +216,6 @@ impl VirtualProcessorPlatformTrait for HvTestCtx { log::info!("self addr: {:p}", self as *const _); self.switch_to_high_vtl(); log::info!("self addr after switch: {:p}", self as *const _); - log::debug!( - "VP{}: Queued command for VP{} in VTL{:?}", - self.my_vp_idx, - vp_index, - vtl - ); self.vp_runing.insert(vp_index); } else { let (tx, rx) = sync_nostd::Channel::>::new().split(); @@ -249,11 +246,6 @@ impl VirtualProcessorPlatformTrait for HvTestCtx { Vtl::Vtl1, )); self.switch_to_high_vtl(); - log::debug!( - "VP{} waiting for start confirmation for vp from VTL1: {}", - self.my_vp_idx, - vp_index - ); let rx = rx.recv(); if let Ok(r) = rx { r?; @@ -261,13 +253,6 @@ impl VirtualProcessorPlatformTrait for HvTestCtx { self.vp_runing.insert(vp_index); } } - - log::debug!( - "VP{}: Queued command for VP{} in VTL{:?}", - self.my_vp_idx, - vp_index, - vtl - ); cmdt() .lock() .get_mut(&vp_index) @@ -291,7 +276,7 @@ impl VirtualProcessorPlatformTrait for HvTestCtx { cmd: VpExecutor, ) -> TmkResult<()> { let (vp_index, vtl, _cmd) = cmd.get(); - let vp_ctx = self.get_default_context()?; + let vp_ctx: InitialVpContextX64 = self.get_default_context(vtl)?; self.hvcall .start_virtual_processor(vp_index, vtl, Some(vp_ctx))?; Ok(()) @@ -315,7 +300,7 @@ impl VtlPlatformTrait for HvTestCtx { /// Enable the specified VTL on a VP and seed it with a default /// context captured from the current execution environment. fn enable_vp_vtl_with_default_context(&mut self, vp_index: u32, vtl: Vtl) -> TmkResult<()> { - let vp_ctx = self.get_default_context()?; + let vp_ctx = self.get_default_context(vtl)?; self.hvcall.enable_vp_vtl(vp_index, vtl, Some(vp_ctx))?; Ok(()) } @@ -332,7 +317,7 @@ impl VtlPlatformTrait for HvTestCtx { Vtl::Vtl1 => 1, Vtl::Vtl2 => 2, }; - let vp_context = self.get_default_context()?; + let vp_context = self.get_default_context(vtl)?; self.hvcall.set_vp_registers( vp_index, Some( @@ -362,17 +347,46 @@ impl VtlPlatformTrait for HvTestCtx { /// Switch execution from the current (low) VTL to the next higher /// one (`vtl_call`). + #[inline(never)] fn switch_to_high_vtl(&mut self) { - self.print_rsp(); - HvCall::vtl_call(); - let rsp: u64; unsafe { asm!( - "mov {}, rsp", - out(reg) rsp, + " + push rax + push rbx + push rcx + push rdx + push rdi + push rsi + push rbp + push r8 + push r9 + push r10 + push r11 + push r12 + push r13 + push r14 + push r15 + call {call_address} + pop r15 + pop r14 + pop r13 + pop r12 + pop r11 + pop r10 + pop r9 + pop r8 + pop rbp + pop rsi + pop rdi + pop rdx + pop rcx + pop rbx + pop rax", + call_address = sym HvCall::vtl_call, ); } - log::debug!("switched back to low VTL"); + // let reg = self // .get_register(hvdef::HvAllArchRegisterName::VsmCodePageOffsets.0) // .unwrap(); @@ -395,18 +409,52 @@ impl VtlPlatformTrait for HvTestCtx { } /// Return from a high VTL back to the low VTL (`vtl_return`). + #[inline(never)] fn switch_to_low_vtl(&mut self) { - log::debug!("switching to low VTL"); - HvCall::vtl_return(); - + // HvCall::vtl_return(); + unsafe { + asm!( + " + push rax + push rbx + push rcx + push rdx + push rdi + push rsi + push rbp + push r8 + push r9 + push r10 + push r11 + push r12 + push r13 + push r14 + push r15 + call {call_address} + pop r15 + pop r14 + pop r13 + pop r12 + pop r11 + pop r10 + pop r9 + pop r8 + pop rbp + pop rsi + pop rdi + pop rdx + pop rcx + pop rbx + pop rax", + call_address = sym HvCall::vtl_return, + ); + } // let reg = self // .get_register(hvdef::HvAllArchRegisterName::VsmCodePageOffsets.0) // .unwrap(); // let reg = HvRegisterValue::from(reg); // let offset = hvdef::HvRegisterVsmCodePageOffsets::from_bits(reg.as_u64()); - // log::debug!("ret_offset: {:?}", offset); - // let call_offset = offset.return_offset(); // unsafe { // let call_address = &raw const HYPERCALL_PAGE as *const u8; @@ -419,6 +467,39 @@ impl VtlPlatformTrait for HvTestCtx { // ); // } } + + fn set_vp_state_with_vtl( + &mut self, + register_index: u32, + value: u64, + vtl: Vtl, + ) -> TmkResult<()> { + let vtl = vtl_transform(vtl); + let value = AlignedU128::from(value); + let reg_value = hvdef::HvRegisterValue(value); + self.hvcall + .set_register(hvdef::HvRegisterName(register_index), reg_value, Some(vtl)) + .map_err(|e| e.into()) + } + + fn get_vp_state_with_vtl(&mut self, register_index: u32, vtl: Vtl) -> TmkResult { + let vtl = vtl_transform(vtl); + self.hvcall + .get_register(hvdef::HvRegisterName(register_index), Some(vtl)) + .map(|v| v.as_u64()) + .map_err(|e| e.into()) + } +} + +fn vtl_transform(vtl: Vtl) -> HvInputVtl { + let vtl = match vtl { + Vtl::Vtl0 => 0, + Vtl::Vtl1 => 1, + Vtl::Vtl2 => 2, + }; + HvInputVtl::new() + .with_target_vtl_value(vtl) + .with_use_target_vtl(true) } impl HvTestCtx { @@ -437,91 +518,88 @@ impl HvTestCtx { /// – initialise the hypercall page, /// – discover the VP count and create command queues, /// – record the current VTL. - pub fn init(&mut self) -> TmkResult<()> { + pub fn init(&mut self, vtl: Vtl) -> TmkResult<()> { self.hvcall.initialize(); let vp_count = self.get_vp_count()?; for i in 0..vp_count { register_command_queue(i); } - self.my_vtl = self.hvcall.vtl(); - let reg = self - .hvcall - .get_register(hvdef::HvAllArchRegisterName::VpIndex.into(), None) - .expect("error: failed to get vp index"); - let reg = reg.as_u64(); - self.my_vp_idx = reg as u32; + self.my_vtl = vtl; + // let reg = self + // .hvcall + // .get_register(hvdef::HvAllArchRegisterName::VpIndex.into(), None) + // .expect("error: failed to get vp index"); + // let reg = reg.as_u64(); + // self.my_vp_idx = reg as u32; + + let result = unsafe { core::arch::x86_64::__cpuid(0x1) }; + self.my_vp_idx = (result.ebx >> 24) & 0xFF; Ok(()) } + fn secure_exec_handler() { + HvTestCtx::exec_handler(Vtl::Vtl1); + } + + fn general_exec_handler() { + HvTestCtx::exec_handler(Vtl::Vtl0); + } + /// Busy-loop executor that runs on every VP. /// Extracts commands from the per-VP queue and executes them in the /// appropriate VTL, switching VTLs when necessary. - fn exec_handler() { + fn exec_handler(vtl: Vtl) { let mut ctx = HvTestCtx::new(); - ctx.init().expect("error: failed to init on a VP"); + ctx.init(vtl).expect("error: failed to init on a VP"); + + ctx.print_rbp(); + ctx.print_rsp(); loop { let mut vtl: Option = None; let mut cmd: Option> = None; { - log::info!("pop1"); let mut cmdt = cmdt().lock(); - log::debug!("pop2"); let d = cmdt.get_mut(&ctx.my_vp_idx); - log::debug!("pop3"); if let Some(d) = d { - log::debug!("pop4"); if !d.is_empty() { let (_c, v) = d.front().unwrap(); - log::debug!("pop5: vtl={:?}", v); - log::debug!("pop5: my_vtl={:?}", ctx.my_vtl); - log::debug!("pop5: vtl_={}", *v == Vtl::Vtl1); if *v == ctx.my_vtl { let (c, _v) = d.pop_front().unwrap(); cmd = Some(c); } else { vtl = Some(*v); } - - log::debug!("pop6: cmd_is_none={:?}", cmd.is_none()); } } } - log::debug!("pop7: vtl={:?}, cmd={:?}", vtl, cmd.is_some()); if let Some(vtl) = vtl { - log::debug!("switching to vtl {:?} for vp {}", vtl, ctx.my_vp_idx); if vtl == Vtl::Vtl0 { ctx.switch_to_low_vtl(); } else { ctx.switch_to_high_vtl(); } } - log::debug!("pop8: vtl={:?}, cmd={:?}", vtl, cmd.is_some()); if let Some(cmd) = cmd { - log::debug!( - "executing command on vp {} VTL{:?}", - ctx.my_vp_idx, - ctx.my_vtl - ); cmd(&mut ctx); - log::debug!( - "executed command on vp {} VTL{:?}", - ctx.my_vp_idx, - ctx.my_vtl - ); } - log::debug!("pop9"); } } #[cfg(target_arch = "x86_64")] /// Capture the current VP context, patch the entry point and stack /// so that the new VP starts in `exec_handler`. - fn get_default_context(&mut self) -> Result { - self.run_fn_with_current_context(HvTestCtx::exec_handler) + fn get_default_context(&mut self, vtl: Vtl) -> Result { + let handler = match vtl { + Vtl::Vtl0 => HvTestCtx::general_exec_handler, + Vtl::Vtl1 => HvTestCtx::secure_exec_handler, + _ => return Err(TmkErrorType::InvalidParameter.into()), + + }; + self.run_fn_with_current_context(handler) } #[cfg(target_arch = "x86_64")] @@ -550,7 +628,25 @@ impl HvTestCtx { // function to print the current register states for x64 #[cfg(target_arch = "x86_64")] #[inline(always)] - fn print_rsp(&self) { + pub fn print_rbp(&self) { + let rbp: u64; + unsafe { + asm!( + "mov {}, rbp", + out(reg) rbp, + ); + } + log::debug!( + "Current RBP: 0x{:#x}, VP:{} VTL:{:?}", + rbp, + self.my_vp_idx, + self.my_vtl + ); + } + + #[cfg(target_arch = "x86_64")] + #[inline(always)] + pub fn print_rsp(&self) { let rsp: u64; unsafe { asm!( @@ -558,7 +654,12 @@ impl HvTestCtx { out(reg) rsp, ); } - log::debug!("Current RSP: 0x{:#x}", rsp); + log::debug!( + "Current RSP: 0x{:#x}, VP:{} VTL:{:?}", + rsp, + self.my_vp_idx, + self.my_vtl + ); } } diff --git a/opentmk/opentmk/src/tests/hv_cvm_mem_protect.rs b/opentmk/opentmk/src/tests/hv_cvm_mem_protect.rs new file mode 100644 index 0000000000..3556429864 --- /dev/null +++ b/opentmk/opentmk/src/tests/hv_cvm_mem_protect.rs @@ -0,0 +1,170 @@ +#![allow(warnings)] +use alloc::{alloc::alloc, sync::Arc}; +use core::{ + alloc::{GlobalAlloc, Layout}, arch::asm, cell::{RefCell, UnsafeCell}, fmt::Write, ops::Range, sync::atomic::{AtomicBool, AtomicI32, Ordering} +}; + +use ::alloc::{boxed::Box, vec::Vec}; +use context::VpExecutor; +use hvdef::{ + hypercall::HvInputVtl, HvAllArchRegisterName, HvRegisterVsmVpStatus, HvX64RegisterName, Vtl, +}; +use hypvctx::HvTestCtx; +use sync_nostd::{Channel, Receiver, Sender}; +use uefi::{entry, Status}; + +use crate::{tmk_assert, tmk_logger}; +use crate::{ + context, + context::{ + InterruptPlatformTrait, SecureInterceptPlatformTrait, VirtualProcessorPlatformTrait, + VtlPlatformTrait, + }, + platform::hypvctx, + tmkdefs::TmkResult, +}; + +static mut HEAPX: RefCell<*mut u8> = RefCell::new(0 as *mut u8); +static mut RETURN_VALUE: u8 = 0; +#[inline(never)] +fn violate_heap() { + unsafe { + let heapx = *HEAPX.borrow(); + RETURN_VALUE = *(heapx.add(10)); + } +} + +#[inline(never)] +fn backup_and_restore() { + use core::arch::asm; + unsafe { + asm!(" + push rax + push rbx + push rcx + push rdx + push rsi + push rdi + push rbp + push r8 + push r9 + push r10 + push r11 + push r12 + push r13 + push r14 + push r15 + call {} + pop r15 + pop r14 + pop r13 + pop r12 + pop r11 + pop r10 + pop r9 + pop r8 + pop rbp + pop rdi + pop rsi + pop rdx + pop rcx + pop rbx + pop rax + ", sym violate_heap); + } +} + + +pub fn exec(ctx: &mut T) +where + T: InterruptPlatformTrait + + VtlPlatformTrait + + VirtualProcessorPlatformTrait, +{ + log::info!("ctx ptr: {:p}", &ctx as *const _); + + let vp_count = ctx.get_vp_count(); + tmk_assert!(vp_count.is_ok(), "get_vp_count should succeed"); + let vp_count = vp_count.unwrap(); + tmk_assert!(vp_count == 4, "vp count should be 8"); + + ctx.setup_interrupt_handler(); + log::info!("successfully setup interrupt handler"); + + ctx.setup_partition_vtl(Vtl::Vtl1); + log::info!("successfully setup partition vtl1"); + + ctx.start_on_vp(VpExecutor::new(0, Vtl::Vtl1).command(move |ctx: &mut T| { + log::info!("successfully started running VTL1 on vp0."); + + let layout = + Layout::from_size_align(1024 * 1024, 4096).expect("msg: failed to create layout"); + let ptr = unsafe { alloc(layout) }; + + log::info!("allocated some memory in the heap from vtl1"); + + unsafe { + let mut z = HEAPX.borrow_mut(); + *z = ptr; + *ptr.add(10) = 0xA2; + } + + let size = layout.size(); + + ctx.setup_vtl_protection(); + + log::info!("enabled vtl protections for the partition."); + + let range = Range { + start: ptr as u64, + end: ptr as u64 + size as u64, + }; + + let result = ctx.apply_vtl_protection_for_memory(range, Vtl::Vtl1); + tmk_assert!(result.is_ok(), "apply_vtl_protection_for_memory should succeed"); + + log::info!("moving to vtl0 to attempt to read the heap memory"); + + ctx.switch_to_low_vtl(); + })); + + log::info!("BACK to vtl0"); + ctx.set_interrupt_idx(18, || { + tmk_assert!(true, "we reached to MC handler"); + panic!("MC causes the test to end"); + }); + + let (tx, rx) = Channel::new().split(); + + ctx.start_on_vp(VpExecutor::new(0x2, Vtl::Vtl1).command(move|ctx: &mut T| { + ctx.setup_interrupt_handler(); + log::info!("successfully started running VTL1 on vp2."); + })); + + ctx.start_on_vp(VpExecutor::new(0x2, Vtl::Vtl0).command( move |ctx: &mut T| unsafe { + log::info!("successfully started running VTL0 on vp2."); + unsafe { + let heapx = *HEAPX.borrow(); + + let read_protected_memory = || { *(heapx.add(10)) }; + + let read_result = read_protected_memory(); + log::info!( + "reading mutated heap memory from vtl0(it should not be 0xA2): 0x{:x}", + read_result + ); + tmk_assert!( + + + read_result != 0xA2, + "heap memory should not be accessible from vtl0" + ); + } + + tx.send(()); + })); + + rx.recv(); + + tmk_assert!(false, "we should not reach here injecting MC should terminate the test"); +} diff --git a/opentmk/opentmk/src/tests/hv_misc.rs b/opentmk/opentmk/src/tests/hv_misc.rs index e6cc89c284..6db4d07de1 100644 --- a/opentmk/opentmk/src/tests/hv_misc.rs +++ b/opentmk/opentmk/src/tests/hv_misc.rs @@ -1,9 +1,10 @@ #![allow(warnings)] -use alloc::{alloc::alloc, sync::Arc}; +use alloc::{alloc::alloc, string::String, sync::Arc}; use core::{ alloc::{GlobalAlloc, Layout}, arch::asm, cell::{RefCell, UnsafeCell}, + fmt::Write, ops::Range, sync::atomic::{AtomicBool, AtomicI32, Ordering}, }; @@ -14,14 +15,10 @@ use hvdef::{ hypercall::HvInputVtl, HvAllArchRegisterName, HvRegisterVsmVpStatus, HvX64RegisterName, Vtl, }; use hypvctx::HvTestCtx; +use iced_x86::{DecoderOptions, Formatter, NasmFormatter}; use sync_nostd::{Channel, Receiver, Sender}; use uefi::{entry, Status}; -// WIP : This test is not yet complete and is not expected to pass. -// -// This test is to verify that the VTL protections are working as expected. -// The stack values in VTL0 are changing after interrupt handling in VTL1. -use crate::tmk_assert; use crate::{ context, context::{ @@ -32,21 +29,80 @@ use crate::{ tmkdefs::TmkResult, }; +use crate::{tmk_assert, tmk_logger}; + static mut HEAPX: RefCell<*mut u8> = RefCell::new(0 as *mut u8); -static mut CON: AtomicI32 = AtomicI32::new(0); -fn call_act() { +static mut RETURN_VALUE: u8 = 0; +#[inline(never)] +fn violate_heap() { unsafe { let heapx = *HEAPX.borrow(); - let val = *(heapx.add(10)); - log::info!( - "reading mutated heap memory from vtl0(it should not be 0xAA): 0x{:x}", - val - ); - tmk_assert!( - val != 0xAA, - "heap memory should not be accessible from vtl0" - ); + RETURN_VALUE = *(heapx.add(10)); + } +} + +#[inline(never)] +fn backup_and_restore() { + unsafe { + asm!(" + push rax + push rbx + push rcx + push rdx + push rsi + push rdi + push rbp + push r8 + push r9 + push r10 + push r11 + push r12 + push r13 + push r14 + push r15 + call {} + pop r15 + pop r14 + pop r13 + pop r12 + pop r11 + pop r10 + pop r9 + pop r8 + pop rbp + pop rdi + pop rsi + pop rdx + pop rcx + pop rbx + pop rax + ", sym violate_heap); + } +} + +pub fn read_assembly_output(target: u64) -> usize { + unsafe { + let target_ptr = target as *const u8; + let code_bytes = core::slice::from_raw_parts(target_ptr, 0x100); + let mut decoder = iced_x86::Decoder::with_ip(64, code_bytes, target, DecoderOptions::NONE); + + let mut formatter = NasmFormatter::new(); + let mut output = String::new(); + let mut first_ip_len = 0; + let mut set = false; + while decoder.can_decode() { + let instr = decoder.decode(); + if !set { + first_ip_len = instr.len(); + set = true; + } + formatter.format(&instr, &mut output); + log::info!("{}:{}", instr.ip(), output); + output.clear(); + } + + first_ip_len } } @@ -75,14 +131,46 @@ where ctx.setup_secure_intercept(0x30); ctx.set_interrupt_idx(0x30, move || { log::info!("interrupt fired!"); - - let hv = HvTestCtx::new(); + let mut hv = HvTestCtx::new(); + // expected to get interrupt in VTL1. + // CVMs dont support hypercalls to get the current VTL from VTL1/0. + hv.init(Vtl::Vtl1); log::info!( "current vp from interrupt: {}", hv.get_current_vp().unwrap() ); + let rip = hvdef::HvX64RegisterName::Rip.0; + + let reg = hv.get_vp_state_with_vtl(rip, Vtl::Vtl0); + tmk_assert!(reg.is_ok(), "get_vp_state_with_vtl should succeed"); + + let reg = reg.unwrap(); + log::info!("rip from vtl0: 0x{:x}", reg); + + log::info!("pring assembly for the current RIP:"); + let size = read_assembly_output(reg); + + let new_rip_value = reg + size as u64; + + log::info!("pring assembly for the updated RIP:"); + read_assembly_output(new_rip_value); + + let r = hv.set_vp_state_with_vtl(HvX64RegisterName::Rip.0, new_rip_value, Vtl::Vtl0); + tmk_assert!(r.is_ok(), "set_vp_state_with_vtl should succeed"); + + let reg = hv.get_vp_state_with_vtl(rip, Vtl::Vtl0); + tmk_assert!(reg.is_ok(), "get_vp_state_with_vtl should succeed"); + + let reg = reg.unwrap(); + log::info!("rip from vtl0 after modification: 0x{:x}", reg); + tmk_assert!(reg == new_rip_value, "rip should be modified"); + + log::info!("pring assembly for the updated RIP after fetch:"); + read_assembly_output(reg); + log::info!("interrupt handled!"); + hv.print_rbp(); }); let layout = @@ -119,38 +207,35 @@ where log::info!("rsp: 0x{:x}", l); let (tx, rx) = Channel::new().split(); - - ctx.start_on_vp(VpExecutor::new(0x2, Vtl::Vtl1).command(move|ctx: &mut T| { + + ctx.start_on_vp(VpExecutor::new(0x2, Vtl::Vtl1).command(move |ctx: &mut T| { ctx.setup_interrupt_handler(); ctx.setup_secure_intercept(0x30); log::info!("successfully started running VTL1 on vp2."); })); - ctx.start_on_vp(VpExecutor::new(0x2, Vtl::Vtl0).command( move |ctx: &mut T| unsafe { - - log::info!("successfully started running VTL0 on vp2."); + ctx.start_on_vp( + VpExecutor::new(0x2, Vtl::Vtl0).command(move |ctx: &mut T| unsafe { + log::info!("successfully started running VTL0 on vp2."); - ctx.queue_command_vp(VpExecutor::new(2, Vtl::Vtl1).command(move |ctx: &mut T| { - log::info!("after intercept successfully started running VTL1 on vp2."); - ctx.switch_to_low_vtl(); - })); + ctx.queue_command_vp(VpExecutor::new(2, Vtl::Vtl1).command(move |ctx: &mut T| { + log::info!("after intercept successfully started running VTL1 on vp2."); + ctx.switch_to_low_vtl(); + })); - unsafe { - let heapx = *HEAPX.borrow(); - let val = *(heapx.add(10)); + backup_and_restore(); log::info!( - "reading mutated heap memory from vtl0(it should not be 0xAA): 0x{:x}", - val + "reading mutated heap memory from vtl0(it should not be 0xA2): 0x{:x}", + RETURN_VALUE ); tmk_assert!( - val != 0xAA, + RETURN_VALUE != 0xA2, "heap memory should not be accessible from vtl0" ); - } - - tx.send(()); - })); + tx.send(()); + }), + ); rx.recv(); // let (mut tx, mut rx) = Channel::new(1); diff --git a/opentmk/opentmk/src/tests/hv_tpm.rs b/opentmk/opentmk/src/tests/hv_tpm.rs new file mode 100644 index 0000000000..a2573a68fc --- /dev/null +++ b/opentmk/opentmk/src/tests/hv_tpm.rs @@ -0,0 +1,167 @@ +use alloc::string::String; +use core::{alloc::Layout, ops::Range}; + +use ::alloc::alloc::alloc; +use hvdef::{HvX64RegisterName, Vtl}; +use iced_x86::{DecoderOptions, Formatter, NasmFormatter}; + +use crate::{ + arch::tpm::Tpm, + context::{ + InterruptPlatformTrait, SecureInterceptPlatformTrait, VirtualProcessorPlatformTrait, + VpExecutor, VtlPlatformTrait, + }, + platform::hypvctx::HvTestCtx, + tmk_assert, +}; + +pub fn read_assembly_output(target: u64) -> usize { + unsafe { + let target_ptr = target as *const u8; + let code_bytes = core::slice::from_raw_parts(target_ptr, 0x100); + let mut decoder = iced_x86::Decoder::with_ip(64, code_bytes, target, DecoderOptions::NONE); + + let mut formatter = NasmFormatter::new(); + let mut output = String::new(); + let mut first_ip_len = 0; + let mut set = false; + while decoder.can_decode() { + let instr = decoder.decode(); + if !set { + first_ip_len = instr.len(); + set = true; + } + formatter.format(&instr, &mut output); + log::info!("{}:{}", instr.ip(), output); + output.clear(); + } + + first_ip_len + } +} + +pub fn exec(ctx: &mut T) +where + T: InterruptPlatformTrait + + SecureInterceptPlatformTrait + + VtlPlatformTrait + + VirtualProcessorPlatformTrait, +{ + let mut _tpm = Tpm::new(); + let protocol_version = Tpm::get_tcg_protocol_version(); + log::warn!("TPM protocol version: 0x{:x}", protocol_version); + // SAFETY: asuming that memory range is limited to 4GB (addressable by 32-bit) + let tpm_layout = Layout::from_size_align(4096 * 2, 4096); + tmk_assert!(tpm_layout.is_ok(), "TPM layout is allocated as expected"); + let tpm_layout = tpm_layout.unwrap(); + let tpm_ptr = unsafe { alloc(tpm_layout) }; + + let tpm_gpa = tpm_ptr as u64; + tmk_assert!( + tpm_gpa >> 32 == 0, + "TPM layout is allocated in the first 4GB" + ); + + let tpm_gpa = tpm_gpa as u32; + + let set_tpm_gpa = Tpm::map_shared_memory(tpm_gpa); + tmk_assert!( + set_tpm_gpa == tpm_gpa, + format!( + "TPM layout is mapped as expected, tpm_gpa: 0x{:x}, set_tpm_gpa: 0x{:x}", + tpm_gpa, set_tpm_gpa + ) + ); + + // build slice from pointer + let tpm_command = unsafe { core::slice::from_raw_parts_mut(tpm_ptr, 4096) }; + let tpm_response = unsafe { core::slice::from_raw_parts_mut(tpm_ptr.add(4096), 4096) }; + + _tpm.set_command_buffer(tpm_command); + _tpm.set_response_buffer(tpm_response); + + let result = _tpm.self_test(); + + log::warn!("TPM self test result: {:?}", result); + tmk_assert!(result.is_ok(), "TPM self test is successful"); + + let vp_count = ctx.get_vp_count(); + tmk_assert!(vp_count.is_ok(), "get_vp_count should succeed"); + let vp_count = vp_count.unwrap(); + tmk_assert!(vp_count == 4, "vp count should be 8"); + let r = ctx.setup_interrupt_handler(); + tmk_assert!(r.is_ok(), "setup_interrupt_handler should succeed"); + log::info!("set intercept handler successfully!"); + let r = ctx.setup_partition_vtl(Vtl::Vtl1); + tmk_assert!(r.is_ok(), "setup_partition_vtl should succeed"); + + let response_rage = Range { + start: tpm_gpa as u64 + 4096, + end: tpm_gpa as u64 + 4096 * 2, + }; + + let r= ctx.start_on_vp(VpExecutor::new(0, Vtl::Vtl1).command(move |ctx: &mut T| { + log::info!("successfully started running VTL1 on vp0."); + let r = ctx.setup_secure_intercept(0x30); + tmk_assert!(r.is_ok(), "setup_secure_intercept should succeed"); + + let r = ctx.set_interrupt_idx(0x30, move || { + log::info!("interrupt fired!"); + let mut hv = HvTestCtx::new(); + // expected to get interrupt in VTL1. + // CVMs dont support hypercalls to get the current VTL from VTL1/0. + hv.init(Vtl::Vtl1); + log::info!( + "current vp from interrupt: {}", + hv.get_current_vp().unwrap() + ); + + let rip = hvdef::HvX64RegisterName::Rip.0; + + let reg = hv.get_vp_state_with_vtl(rip, Vtl::Vtl0); + tmk_assert!(reg.is_ok(), "get_vp_state_with_vtl should succeed"); + + let reg = reg.unwrap(); + log::info!("rip from vtl0: 0x{:x}", reg); + + log::info!("pring assembly for the current RIP:"); + let size = read_assembly_output(reg); + + let new_rip_value = reg + size as u64; + + log::info!("pring assembly for the updated RIP:"); + read_assembly_output(new_rip_value); + + let r = hv.set_vp_state_with_vtl(HvX64RegisterName::Rip.0, new_rip_value, Vtl::Vtl0); + tmk_assert!(r.is_ok(), "set_vp_state_with_vtl should succeed"); + + let reg = hv.get_vp_state_with_vtl(rip, Vtl::Vtl0); + tmk_assert!(reg.is_ok(), "get_vp_state_with_vtl should succeed"); + + let reg = reg.unwrap(); + log::info!("rip from vtl0 after modification: 0x{:x}", reg); + tmk_assert!(reg == new_rip_value, "rip should be modified"); + + log::info!("pring assembly for the updated RIP after fetch:"); + read_assembly_output(reg); + + log::info!("interrupt handled!"); + hv.print_rbp(); + }); + tmk_assert!(r.is_ok(), "set_interrupt_idx should succeed"); + + let r= ctx.setup_vtl_protection(); + tmk_assert!(r.is_ok(), "setup_vtl_protection should succeed"); + + log::info!("enabled vtl protections for the partition."); + + let r = ctx.apply_vtl_protection_for_memory(response_rage, Vtl::Vtl1); + tmk_assert!(r.is_ok(), "apply_vtl_protection_for_memory should succeed"); + + log::info!("moving to vtl0 to attempt to read the heap memory"); + + ctx.switch_to_low_vtl(); + })); + + loop {} +} diff --git a/opentmk/opentmk/src/tests/mod.rs b/opentmk/opentmk/src/tests/mod.rs index a97bdfa8b3..8b6129bab7 100644 --- a/opentmk/opentmk/src/tests/mod.rs +++ b/opentmk/opentmk/src/tests/mod.rs @@ -4,10 +4,10 @@ use crate::platform::hypvctx::HvTestCtx; mod hv_error_vp_start; mod hv_misc; mod hv_processor; -mod processors_valid; - +mod hv_cvm_mem_protect; +mod hv_tpm; pub fn run_test() { let mut ctx = HvTestCtx::new(); - ctx.init().expect("failed to init on BSP"); - processors_valid::exec(&mut ctx); + ctx.init(hvdef::Vtl::Vtl0).expect("failed to init on BSP"); + hv_tpm::exec(&mut ctx); } \ No newline at end of file diff --git a/opentmk/opentmk/src/tests/processors_valid.rs b/opentmk/opentmk/src/tests/processors_valid.rs deleted file mode 100644 index 1a1332f958..0000000000 --- a/opentmk/opentmk/src/tests/processors_valid.rs +++ /dev/null @@ -1,33 +0,0 @@ -use hvdef::Vtl; -use sync_nostd::Channel; - -use crate::{ - context::{InterruptPlatformTrait, VirtualProcessorPlatformTrait, VpExecutor, VtlPlatformTrait}, - tmk_assert, -}; - -#[inline(never)] -pub fn exec(ctx: &mut T) -where - T: VtlPlatformTrait + VirtualProcessorPlatformTrait + InterruptPlatformTrait, -{ - let r = ctx.setup_partition_vtl(Vtl::Vtl1); - tmk_assert!(r.is_ok(), "setup_partition_vtl should succeed"); - - let vp_count = ctx.get_vp_count(); - tmk_assert!(vp_count.is_ok(), "get_vp_count should succeed"); - - let vp_count = vp_count.unwrap(); - tmk_assert!(vp_count == 4, "vp count should be 4"); - - _ = ctx.setup_interrupt_handler(); - - _ = ctx.set_interrupt_idx(0x6, || { - loop{} - }); - - ctx.start_on_vp(VpExecutor::new(0, Vtl::Vtl1).command(move |ctx: &mut T| { - log::info!("successfully started running VTL1 on vp0."); - ctx.switch_to_low_vtl(); - })).expect("Failed to start on VP 0"); -} \ No newline at end of file diff --git a/opentmk/opentmk/src/uefi/mod.rs b/opentmk/opentmk/src/uefi/mod.rs index da42ac5334..7441a56f06 100644 --- a/opentmk/opentmk/src/uefi/mod.rs +++ b/opentmk/opentmk/src/uefi/mod.rs @@ -18,5 +18,5 @@ fn uefi_main() -> Status { log::warn!("TEST_START"); crate::tests::run_test(); log::warn!("TEST_END"); - Status::SUCCESS + loop{} } From 7bdf91fb2ceec5f901dcbdf1cfc269ff70d98679 Mon Sep 17 00:00:00 2001 From: Mayank Kumar Date: Tue, 12 Aug 2025 07:03:25 +0000 Subject: [PATCH 15/23] feat: add new cvm tests --- opentmk/opentmk/src/arch/x86_64/tpm.rs | 21 ++--- opentmk/opentmk/src/tests/hv_tpm.rs | 58 +++++++----- opentmk/opentmk/src/tests/hv_tpm_read_cvm.rs | 93 +++++++++++++++++++ opentmk/opentmk/src/tests/hv_tpm_write_cvm.rs | 90 ++++++++++++++++++ opentmk/opentmk/src/tests/mod.rs | 5 +- 5 files changed, 230 insertions(+), 37 deletions(-) create mode 100644 opentmk/opentmk/src/tests/hv_tpm_read_cvm.rs create mode 100644 opentmk/opentmk/src/tests/hv_tpm_write_cvm.rs diff --git a/opentmk/opentmk/src/arch/x86_64/tpm.rs b/opentmk/opentmk/src/arch/x86_64/tpm.rs index 7fa3d39b5d..8cb73613f3 100644 --- a/opentmk/opentmk/src/arch/x86_64/tpm.rs +++ b/opentmk/opentmk/src/arch/x86_64/tpm.rs @@ -61,6 +61,12 @@ impl<'a> Tpm<'a> { super::io::inl(data_port) } + pub fn get_mapped_shared_memory() -> u32 { + let data_port = TPM_DEVICE_IO_PORT_RANGE_BEGIN+TPM_DEVICE_IO_PORT_DATA_OFFSET; + Tpm::get_control_port(0x2); + super::io::inl(data_port) + } + pub fn copy_to_command_buffer(&mut self, buffer: &[u8]) { self.command_buffer .as_mut() @@ -91,21 +97,10 @@ impl<'a> Tpm<'a> { assert!(buffer.len() <= 4096); self.copy_to_command_buffer(buffer); - let command_exec_mmio_addr = TPM_DEVICE_MMIO_REGION_BASE_ADDRESS + 0x4c; - let command_exec_mmio_ptr = command_exec_mmio_addr as *mut u32; - - unsafe { - *command_exec_mmio_ptr = 0x1; - } - - while unsafe { *command_exec_mmio_ptr } == 0x1 { - unsafe { - core::arch::x86_64::_mm_pause(); - } - } + Tpm::execute_command(); let mut response = [0; 4096]; - response.copy_from_slice(self.response_buffer.as_ref().unwrap()); + self.copy_from_response_buffer(&mut response); response } diff --git a/opentmk/opentmk/src/tests/hv_tpm.rs b/opentmk/opentmk/src/tests/hv_tpm.rs index a2573a68fc..d3ff19551e 100644 --- a/opentmk/opentmk/src/tests/hv_tpm.rs +++ b/opentmk/opentmk/src/tests/hv_tpm.rs @@ -6,7 +6,7 @@ use hvdef::{HvX64RegisterName, Vtl}; use iced_x86::{DecoderOptions, Formatter, NasmFormatter}; use crate::{ - arch::tpm::Tpm, + arch::tpm::{Tpm, TpmUtil}, context::{ InterruptPlatformTrait, SecureInterceptPlatformTrait, VirtualProcessorPlatformTrait, VpExecutor, VtlPlatformTrait, @@ -51,27 +51,31 @@ where let protocol_version = Tpm::get_tcg_protocol_version(); log::warn!("TPM protocol version: 0x{:x}", protocol_version); // SAFETY: asuming that memory range is limited to 4GB (addressable by 32-bit) - let tpm_layout = Layout::from_size_align(4096 * 2, 4096); - tmk_assert!(tpm_layout.is_ok(), "TPM layout is allocated as expected"); - let tpm_layout = tpm_layout.unwrap(); - let tpm_ptr = unsafe { alloc(tpm_layout) }; - - let tpm_gpa = tpm_ptr as u64; - tmk_assert!( - tpm_gpa >> 32 == 0, - "TPM layout is allocated in the first 4GB" - ); - - let tpm_gpa = tpm_gpa as u32; - - let set_tpm_gpa = Tpm::map_shared_memory(tpm_gpa); - tmk_assert!( - set_tpm_gpa == tpm_gpa, - format!( - "TPM layout is mapped as expected, tpm_gpa: 0x{:x}, set_tpm_gpa: 0x{:x}", - tpm_gpa, set_tpm_gpa - ) - ); + // let tpm_layout = Layout::from_size_align(4096 * 2, 4096); + // tmk_assert!(tpm_layout.is_ok(), "TPM layout is allocated as expected"); + // let tpm_layout = tpm_layout.unwrap(); + // let tpm_ptr = unsafe { alloc(tpm_layout) }; + + // let tpm_gpa = tpm_ptr as u64; + // tmk_assert!( + // tpm_gpa >> 32 == 0, + // "TPM layout is allocated in the first 4GB" + // ); + + // let tpm_gpa = tpm_gpa as u32; + + // let set_tpm_gpa = Tpm::map_shared_memory(tpm_gpa); + // tmk_assert!( + // set_tpm_gpa == tpm_gpa, + // format!( + // "TPM layout is mapped as expected, tpm_gpa: 0x{:x}, set_tpm_gpa: 0x{:x}", + // tpm_gpa, set_tpm_gpa + // ) + // ); + + let tpm_gpa = Tpm::get_mapped_shared_memory(); + log::warn!("TPM CMD buffer from vTPM Device: 0x{:x}", tpm_gpa); + let tpm_ptr = (tpm_gpa as u64) as *mut u8; // build slice from pointer let tpm_command = unsafe { core::slice::from_raw_parts_mut(tpm_ptr, 4096) }; @@ -157,11 +161,19 @@ where let r = ctx.apply_vtl_protection_for_memory(response_rage, Vtl::Vtl1); tmk_assert!(r.is_ok(), "apply_vtl_protection_for_memory should succeed"); - + log::info!("moving to vtl0 to attempt to read the heap memory"); ctx.switch_to_low_vtl(); })); + let cmd = TpmUtil::get_self_test_cmd(); + _tpm.copy_to_command_buffer(&cmd); + log::warn!("TPM self test command copied to buffer"); + log::warn!("about to execute TPM self test command.."); + Tpm::execute_command(); + log::warn!("TPM self test command executed"); + + loop {} } diff --git a/opentmk/opentmk/src/tests/hv_tpm_read_cvm.rs b/opentmk/opentmk/src/tests/hv_tpm_read_cvm.rs new file mode 100644 index 0000000000..b28d8f8d0f --- /dev/null +++ b/opentmk/opentmk/src/tests/hv_tpm_read_cvm.rs @@ -0,0 +1,93 @@ +use alloc::string::String; +use core::{alloc::Layout, ops::Range}; + +use ::alloc::alloc::alloc; +use hvdef::{HvX64RegisterName, Vtl}; +use iced_x86::{DecoderOptions, Formatter, NasmFormatter}; + +use crate::{ + arch::tpm::{Tpm, TpmUtil}, + context::{ + InterruptPlatformTrait, SecureInterceptPlatformTrait, VirtualProcessorPlatformTrait, + VpExecutor, VtlPlatformTrait, + }, + platform::hypvctx::HvTestCtx, + tmk_assert, +}; + +pub fn exec(ctx: &mut T) +where + T: InterruptPlatformTrait + + SecureInterceptPlatformTrait + + VtlPlatformTrait + + VirtualProcessorPlatformTrait, +{ + let mut _tpm = Tpm::new(); + let protocol_version = Tpm::get_tcg_protocol_version(); + log::warn!("TPM protocol version: 0x{:x}", protocol_version); + + let tpm_gpa = Tpm::get_mapped_shared_memory(); + log::warn!("TPM CMD buffer from vTPM Device: 0x{:x}", tpm_gpa); + let tpm_ptr = (tpm_gpa as u64) as *mut u8; + + // build slice from pointer + let tpm_command = unsafe { core::slice::from_raw_parts_mut(tpm_ptr, 4096) }; + let tpm_response = unsafe { core::slice::from_raw_parts_mut(tpm_ptr.add(4096), 4096) }; + + _tpm.set_command_buffer(tpm_command); + _tpm.set_response_buffer(tpm_response); + + let result = _tpm.self_test(); + + log::warn!("TPM self test result: {:?}", result); + tmk_assert!(result.is_ok(), "TPM self test is successful"); + + let vp_count = ctx.get_vp_count(); + tmk_assert!(vp_count.is_ok(), "get_vp_count should succeed"); + let vp_count = vp_count.unwrap(); + tmk_assert!(vp_count == 4, "vp count should be 8"); + let r = ctx.setup_interrupt_handler(); + tmk_assert!(r.is_ok(), "setup_interrupt_handler should succeed"); + log::info!("set intercept handler successfully!"); + let r = ctx.setup_partition_vtl(Vtl::Vtl1); + tmk_assert!(r.is_ok(), "setup_partition_vtl should succeed"); + + let command_range = Range { + start: tpm_gpa as u64, + end: tpm_gpa as u64 + 4096, + }; + + let r= ctx.start_on_vp(VpExecutor::new(0, Vtl::Vtl1).command(move |ctx: &mut T| { + log::info!("successfully started running VTL1 on vp0."); + let r = ctx.setup_secure_intercept(0x30); + tmk_assert!(r.is_ok(), "setup_secure_intercept should succeed"); + + let r= ctx.setup_vtl_protection(); + tmk_assert!(r.is_ok(), "setup_vtl_protection should succeed"); + log::info!("enabled vtl protections for the partition."); + ctx.switch_to_low_vtl(); + })); + + let r = ctx.set_interrupt_idx(18, || { + log::warn!("successfully intercepted interrupt 18"); + panic!("MC should cause a system abort"); + }); + tmk_assert!(r.is_ok(), "set_interrupt_idx should succeed"); + + let cmd = TpmUtil::get_self_test_cmd(); + _tpm.copy_to_command_buffer(&cmd); + log::warn!("TPM self test command copied to buffer"); + + let r = ctx.start_on_vp(VpExecutor::new(0, Vtl::Vtl1).command(move |ctx: &mut T| { + + let r = ctx.apply_vtl_protection_for_memory(command_range, Vtl::Vtl1); + tmk_assert!(r.is_ok(), "apply_vtl_protection_for_memory should succeed"); + + ctx.switch_to_low_vtl(); + })); + tmk_assert!(r.is_ok(), "start_on_vp should succeed"); + + log::warn!("about to execute TPM self test command.."); + Tpm::execute_command(); + log::warn!("TPM self test command executed"); +} diff --git a/opentmk/opentmk/src/tests/hv_tpm_write_cvm.rs b/opentmk/opentmk/src/tests/hv_tpm_write_cvm.rs new file mode 100644 index 0000000000..1640100a1c --- /dev/null +++ b/opentmk/opentmk/src/tests/hv_tpm_write_cvm.rs @@ -0,0 +1,90 @@ +use alloc::string::String; +use core::{alloc::Layout, ops::Range}; + +use ::alloc::alloc::alloc; +use hvdef::{HvX64RegisterName, Vtl}; +use iced_x86::{DecoderOptions, Formatter, NasmFormatter}; + +use crate::{ + arch::tpm::{Tpm, TpmUtil}, + context::{ + InterruptPlatformTrait, SecureInterceptPlatformTrait, VirtualProcessorPlatformTrait, + VpExecutor, VtlPlatformTrait, + }, + platform::hypvctx::HvTestCtx, + tmk_assert, +}; + +pub fn exec(ctx: &mut T) +where + T: InterruptPlatformTrait + + SecureInterceptPlatformTrait + + VtlPlatformTrait + + VirtualProcessorPlatformTrait, +{ + let mut _tpm = Tpm::new(); + let protocol_version = Tpm::get_tcg_protocol_version(); + log::warn!("TPM protocol version: 0x{:x}", protocol_version); + + let tpm_gpa = Tpm::get_mapped_shared_memory(); + log::warn!("TPM CMD buffer from vTPM Device: 0x{:x}", tpm_gpa); + let tpm_ptr = (tpm_gpa as u64) as *mut u8; + + // build slice from pointer + let tpm_command = unsafe { core::slice::from_raw_parts_mut(tpm_ptr, 4096) }; + let tpm_response = unsafe { core::slice::from_raw_parts_mut(tpm_ptr.add(4096), 4096) }; + + _tpm.set_command_buffer(tpm_command); + _tpm.set_response_buffer(tpm_response); + + let result = _tpm.self_test(); + + log::warn!("TPM self test result: {:?}", result); + tmk_assert!(result.is_ok(), "TPM self test is successful"); + + let vp_count = ctx.get_vp_count(); + tmk_assert!(vp_count.is_ok(), "get_vp_count should succeed"); + let vp_count = vp_count.unwrap(); + tmk_assert!(vp_count == 4, "vp count should be 8"); + let r = ctx.setup_interrupt_handler(); + tmk_assert!(r.is_ok(), "setup_interrupt_handler should succeed"); + log::info!("set intercept handler successfully!"); + let r = ctx.setup_partition_vtl(Vtl::Vtl1); + tmk_assert!(r.is_ok(), "setup_partition_vtl should succeed"); + + let response_rage = Range { + start: tpm_gpa as u64 + 4096, + end: tpm_gpa as u64 + 4096 * 2, + }; + + let r= ctx.start_on_vp(VpExecutor::new(0, Vtl::Vtl1).command(move |ctx: &mut T| { + log::info!("successfully started running VTL1 on vp0."); + let r = ctx.setup_secure_intercept(0x30); + tmk_assert!(r.is_ok(), "setup_secure_intercept should succeed"); + + let r= ctx.setup_vtl_protection(); + tmk_assert!(r.is_ok(), "setup_vtl_protection should succeed"); + + log::info!("enabled vtl protections for the partition."); + + let r = ctx.apply_vtl_protection_for_memory(response_rage, Vtl::Vtl1); + tmk_assert!(r.is_ok(), "apply_vtl_protection_for_memory should succeed"); + + log::info!("moving to vtl0 to attempt to read the heap memory"); + + ctx.switch_to_low_vtl(); + })); + + let r = ctx.set_interrupt_idx(18, || { + log::warn!("successfully intercepted interrupt 18"); + panic!("MC should cause a system abort"); + }); + tmk_assert!(r.is_ok(), "set_interrupt_idx should succeed"); + + let cmd = TpmUtil::get_self_test_cmd(); + _tpm.copy_to_command_buffer(&cmd); + log::warn!("TPM self test command copied to buffer"); + log::warn!("about to execute TPM self test command.."); + Tpm::execute_command(); + log::warn!("TPM self test command executed"); +} diff --git a/opentmk/opentmk/src/tests/mod.rs b/opentmk/opentmk/src/tests/mod.rs index 8b6129bab7..46ba3f5c12 100644 --- a/opentmk/opentmk/src/tests/mod.rs +++ b/opentmk/opentmk/src/tests/mod.rs @@ -6,8 +6,11 @@ mod hv_misc; mod hv_processor; mod hv_cvm_mem_protect; mod hv_tpm; +mod hv_tpm_write_cvm; +mod hv_tpm_read_cvm; + pub fn run_test() { let mut ctx = HvTestCtx::new(); ctx.init(hvdef::Vtl::Vtl0).expect("failed to init on BSP"); - hv_tpm::exec(&mut ctx); + hv_tpm_read_cvm::exec(&mut ctx); } \ No newline at end of file From 1104901251e3a50f8699c97367744741e985f8c3 Mon Sep 17 00:00:00 2001 From: Mayank Kumar Date: Tue, 12 Aug 2025 07:32:57 +0000 Subject: [PATCH 16/23] lint: add lints for imports --- opentmk/opentmk/rustfmt.toml | 6 +- opentmk/opentmk/src/arch/x86_64/hypercall.rs | 7 +- opentmk/opentmk/src/arch/x86_64/interrupt.rs | 6 +- .../arch/x86_64/interrupt_handler_register.rs | 6 +- opentmk/opentmk/src/arch/x86_64/mod.rs | 4 +- opentmk/opentmk/src/arch/x86_64/rtc.rs | 37 +++--- opentmk/opentmk/src/arch/x86_64/serial.rs | 11 +- opentmk/opentmk/src/arch/x86_64/tpm.rs | 36 ++--- opentmk/opentmk/src/context.rs | 6 +- opentmk/opentmk/src/devices/mod.rs | 2 +- opentmk/opentmk/src/devices/tpm/mod.rs | 15 ++- opentmk/opentmk/src/devices/tpm/protocol.rs | 8 +- opentmk/opentmk/src/hypercall.rs | 29 ++-- opentmk/opentmk/src/main.rs | 2 +- opentmk/opentmk/src/platform/hypvctx.rs | 49 +++---- .../opentmk/src/tests/hv_cvm_mem_protect.rs | 124 ++++++++++-------- .../opentmk/src/tests/hv_error_vp_start.rs | 8 +- opentmk/opentmk/src/tests/hv_misc.rs | 68 ++++++---- opentmk/opentmk/src/tests/hv_processor.rs | 13 +- opentmk/opentmk/src/tests/hv_tpm.rs | 43 +++--- opentmk/opentmk/src/tests/hv_tpm_read_cvm.rs | 39 +++--- opentmk/opentmk/src/tests/hv_tpm_write_cvm.rs | 34 +++-- opentmk/opentmk/src/tests/mod.rs | 6 +- opentmk/opentmk/src/tmk_assert.rs | 3 +- opentmk/opentmk/src/tmk_logger.rs | 23 ++-- opentmk/opentmk/src/uefi/alloc.rs | 11 +- opentmk/opentmk/src/uefi/init.rs | 9 +- opentmk/opentmk/src/uefi/mod.rs | 5 +- 28 files changed, 329 insertions(+), 281 deletions(-) diff --git a/opentmk/opentmk/rustfmt.toml b/opentmk/opentmk/rustfmt.toml index 8b23646db0..a451c50644 100644 --- a/opentmk/opentmk/rustfmt.toml +++ b/opentmk/opentmk/rustfmt.toml @@ -1,2 +1,4 @@ -group_imports = "StdExternalCrate" -imports_granularity = "Crate" \ No newline at end of file +imports_granularity = "Item" # Expands `use foo::{bar, baz};` into separate `use` lines +reorder_imports = true # Optional: sort imports +normalize_imports = true # Optional: standardize style (e.g., remove leading ::) +group_imports = "StdExternalCrate" # Optional: group std/external/local with blank lines diff --git a/opentmk/opentmk/src/arch/x86_64/hypercall.rs b/opentmk/opentmk/src/arch/x86_64/hypercall.rs index b65f905b7e..3fee1370cf 100644 --- a/opentmk/opentmk/src/arch/x86_64/hypercall.rs +++ b/opentmk/opentmk/src/arch/x86_64/hypercall.rs @@ -6,10 +6,9 @@ use core::ptr::addr_of; use hvdef::HV_PAGE_SIZE; -use minimal_rt::arch::{ - hypercall::HYPERCALL_PAGE, - msr::{read_msr, write_msr}, -}; +use minimal_rt::arch::hypercall::HYPERCALL_PAGE; +use minimal_rt::arch::msr::read_msr; +use minimal_rt::arch::msr::write_msr; /// Writes an MSR to tell the hypervisor the OS ID for the boot shim. fn report_os_id(guest_os_id: u64) { diff --git a/opentmk/opentmk/src/arch/x86_64/interrupt.rs b/opentmk/opentmk/src/arch/x86_64/interrupt.rs index faf246848d..800c13ad5d 100644 --- a/opentmk/opentmk/src/arch/x86_64/interrupt.rs +++ b/opentmk/opentmk/src/arch/x86_64/interrupt.rs @@ -1,8 +1,10 @@ use lazy_static::lazy_static; use sync_nostd::Mutex; -use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame}; +use x86_64::structures::idt::InterruptDescriptorTable; +use x86_64::structures::idt::InterruptStackFrame; -use super::interrupt_handler_register::{register_interrupt_handler, set_common_handler}; +use super::interrupt_handler_register::register_interrupt_handler; +use super::interrupt_handler_register::set_common_handler; lazy_static! { static ref IDT: InterruptDescriptorTable = { diff --git a/opentmk/opentmk/src/arch/x86_64/interrupt_handler_register.rs b/opentmk/opentmk/src/arch/x86_64/interrupt_handler_register.rs index a07a3c9ebe..6f37c3f6d6 100644 --- a/opentmk/opentmk/src/arch/x86_64/interrupt_handler_register.rs +++ b/opentmk/opentmk/src/arch/x86_64/interrupt_handler_register.rs @@ -1,6 +1,8 @@ #![allow(dead_code)] use sync_nostd::Mutex; -use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame, PageFaultErrorCode}; +use x86_64::structures::idt::InterruptDescriptorTable; +use x86_64::structures::idt::InterruptStackFrame; +use x86_64::structures::idt::PageFaultErrorCode; static mut COMMON_HANDLER: fn(InterruptStackFrame, u8) = common_handler; static COMMON_HANDLER_MUTEX: Mutex<()> = Mutex::new(()); @@ -46,8 +48,6 @@ macro_rules! create_fn_divergent_create { static mut BACKUP_RSP: u64 = 0; - - macro_rules! create_page_fault_fn { ($name:ident, $i: expr) => { extern "x86-interrupt" fn $name( diff --git a/opentmk/opentmk/src/arch/x86_64/mod.rs b/opentmk/opentmk/src/arch/x86_64/mod.rs index 69ea1d0adc..2da8e40fb0 100644 --- a/opentmk/opentmk/src/arch/x86_64/mod.rs +++ b/opentmk/opentmk/src/arch/x86_64/mod.rs @@ -1,7 +1,7 @@ pub mod hypercall; pub mod interrupt; mod interrupt_handler_register; -pub mod serial; mod io; pub mod rtc; -pub mod tpm; \ No newline at end of file +pub mod serial; +pub mod tpm; diff --git a/opentmk/opentmk/src/arch/x86_64/rtc.rs b/opentmk/opentmk/src/arch/x86_64/rtc.rs index 37b73d8e59..afdcaaa9ee 100644 --- a/opentmk/opentmk/src/arch/x86_64/rtc.rs +++ b/opentmk/opentmk/src/arch/x86_64/rtc.rs @@ -1,4 +1,5 @@ -use super::io::{inb, outb}; +use super::io::inb; +use super::io::outb; // CMOS/RTC I/O ports const CMOS_ADDRESS: u16 = 0x70; const CMOS_DATA: u16 = 0x71; @@ -26,11 +27,17 @@ pub struct DateTime { // implement display as ISO 8601 format impl core::fmt::Display for DateTime { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> - core::fmt::Result { - write!(f, "{:02}:{:02}:{:02} {:02}-{:02}-{:04} UTC", - self.hours, self.minutes, self.seconds, - self.day, self.month, 2000 + self.year as u64) + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!( + f, + "{:02}:{:02}:{:02} {:02}-{:02}-{:04} UTC", + self.hours, + self.minutes, + self.seconds, + self.day, + self.month, + 2000 + self.year as u64 + ) } } @@ -43,7 +50,7 @@ impl DateTime { let hours = self.hours as u64; let minutes = self.minutes as u64; let seconds = self.seconds as u64; - + (days * 24 + hours) * 3600 + (minutes * 60) + seconds } } @@ -68,7 +75,7 @@ fn bcd_to_binary(bcd: u8) -> u8 { pub fn read_rtc() -> DateTime { // Wait for any update to complete while rtc_update_in_progress() {} - + let mut datetime = DateTime { seconds: read_cmos(RTC_SECONDS), minutes: read_cmos(RTC_MINUTES), @@ -77,10 +84,10 @@ pub fn read_rtc() -> DateTime { month: read_cmos(RTC_MONTH), year: read_cmos(RTC_YEAR), }; - + // Check if we need to wait for another update cycle while rtc_update_in_progress() {} - + // Read again to ensure consistency let seconds_check = read_cmos(RTC_SECONDS); if seconds_check != datetime.seconds { @@ -91,11 +98,11 @@ pub fn read_rtc() -> DateTime { datetime.month = read_cmos(RTC_MONTH); datetime.year = read_cmos(RTC_YEAR); } - + // Check RTC format (BCD vs binary) let status_b = read_cmos(RTC_STATUS_B); let is_bcd = (status_b & 0x04) == 0; - + if is_bcd { datetime.seconds = bcd_to_binary(datetime.seconds); datetime.minutes = bcd_to_binary(datetime.minutes); @@ -104,12 +111,12 @@ pub fn read_rtc() -> DateTime { datetime.month = bcd_to_binary(datetime.month); datetime.year = bcd_to_binary(datetime.year); } - + // Handle 12-hour format if needed if (status_b & 0x02) == 0 && (datetime.hours & 0x80) != 0 { datetime.hours = ((datetime.hours & 0x7F) + 12) % 24; } - + datetime } @@ -122,4 +129,4 @@ pub fn delay_sec(seconds: u64) { break; } } -} \ No newline at end of file +} diff --git a/opentmk/opentmk/src/arch/x86_64/serial.rs b/opentmk/opentmk/src/arch/x86_64/serial.rs index ee8fbc20fc..2124c7b673 100644 --- a/opentmk/opentmk/src/arch/x86_64/serial.rs +++ b/opentmk/opentmk/src/arch/x86_64/serial.rs @@ -4,10 +4,11 @@ //! Serial output for debugging. use core::fmt; -use super::io; use sync_nostd::Mutex; +use super::io; + /// Serial port addresses. /// These are the standard COM ports used in x86 systems. #[repr(u16)] @@ -70,8 +71,12 @@ pub struct Serial { impl Serial { /// Initialize the serial port. - pub const fn new(serial_port: SerialPort, io: T) -> Self { - Self { io, serial_port, mutex: Mutex::new(()) } + pub const fn new(serial_port: SerialPort, io: T) -> Self { + Self { + io, + serial_port, + mutex: Mutex::new(()), + } } pub fn init(&self) { diff --git a/opentmk/opentmk/src/arch/x86_64/tpm.rs b/opentmk/opentmk/src/arch/x86_64/tpm.rs index 8cb73613f3..f837d62653 100644 --- a/opentmk/opentmk/src/arch/x86_64/tpm.rs +++ b/opentmk/opentmk/src/arch/x86_64/tpm.rs @@ -1,6 +1,9 @@ use zerocopy::IntoBytes; -use thiserror::Error; -use crate::devices::tpm::protocol::{protocol::{SelfTestCmd, TpmCommand}, SessionTagEnum, TpmCommandError}; + +use crate::devices::tpm::protocol::protocol::SelfTestCmd; +use crate::devices::tpm::protocol::protocol::TpmCommand; +use crate::devices::tpm::protocol::SessionTagEnum; +use crate::devices::tpm::protocol::TpmCommandError; pub const TPM_DEVICE_MMIO_REGION_BASE_ADDRESS: u64 = 0xfed40000; pub const TPM_DEVICE_MMIO_REGION_SIZE: u64 = 0x70; @@ -42,8 +45,8 @@ impl<'a> Tpm<'a> { #[cfg(target_arch = "x86_64")] pub fn get_control_port(command: u32) -> u32 { - let control_port = TPM_DEVICE_IO_PORT_RANGE_BEGIN+TPM_DEVICE_IO_PORT_CONTROL_OFFSET; - let data_port = TPM_DEVICE_IO_PORT_RANGE_BEGIN+TPM_DEVICE_IO_PORT_DATA_OFFSET; + let control_port = TPM_DEVICE_IO_PORT_RANGE_BEGIN + TPM_DEVICE_IO_PORT_CONTROL_OFFSET; + let data_port = TPM_DEVICE_IO_PORT_RANGE_BEGIN + TPM_DEVICE_IO_PORT_DATA_OFFSET; super::io::outl(control_port, command); super::io::inl(data_port) } @@ -53,8 +56,8 @@ impl<'a> Tpm<'a> { } pub fn map_shared_memory(gpa: u32) -> u32 { - let control_port = TPM_DEVICE_IO_PORT_RANGE_BEGIN+TPM_DEVICE_IO_PORT_CONTROL_OFFSET; - let data_port = TPM_DEVICE_IO_PORT_RANGE_BEGIN+TPM_DEVICE_IO_PORT_DATA_OFFSET; + let control_port = TPM_DEVICE_IO_PORT_RANGE_BEGIN + TPM_DEVICE_IO_PORT_CONTROL_OFFSET; + let data_port = TPM_DEVICE_IO_PORT_RANGE_BEGIN + TPM_DEVICE_IO_PORT_DATA_OFFSET; super::io::outl(control_port, 0x1); super::io::outl(data_port, gpa); super::io::outl(control_port, 0x2); @@ -62,16 +65,13 @@ impl<'a> Tpm<'a> { } pub fn get_mapped_shared_memory() -> u32 { - let data_port = TPM_DEVICE_IO_PORT_RANGE_BEGIN+TPM_DEVICE_IO_PORT_DATA_OFFSET; + let data_port = TPM_DEVICE_IO_PORT_RANGE_BEGIN + TPM_DEVICE_IO_PORT_DATA_OFFSET; Tpm::get_control_port(0x2); super::io::inl(data_port) } pub fn copy_to_command_buffer(&mut self, buffer: &[u8]) { - self.command_buffer - .as_mut() - .unwrap()[..buffer.len()] - .copy_from_slice(buffer); + self.command_buffer.as_mut().unwrap()[..buffer.len()].copy_from_slice(buffer); } pub fn copy_from_response_buffer(&mut self, buffer: &mut [u8]) { @@ -108,7 +108,7 @@ impl<'a> Tpm<'a> { let session_tag = SessionTagEnum::NoSessions; let cmd = SelfTestCmd::new(session_tag.into(), true); let response = self.run_command(cmd.as_bytes()); - + match SelfTestCmd::base_validate_reply(&response, session_tag) { Err(error) => Err(TpmCommandError::InvalidResponse(error)), Ok((res, false)) => Err(TpmCommandError::TpmCommandFailed { @@ -117,16 +117,4 @@ impl<'a> Tpm<'a> { Ok((_res, true)) => Ok(()), } } - } - -pub struct TpmUtil; -impl TpmUtil { - pub fn get_self_test_cmd() -> [u8; 4096] { - let session_tag = SessionTagEnum::NoSessions; - let cmd = SelfTestCmd::new(session_tag.into(), true); - let mut buffer = [0; 4096]; - buffer[..cmd.as_bytes().len()].copy_from_slice(cmd.as_bytes()); - buffer - } -} \ No newline at end of file diff --git a/opentmk/opentmk/src/context.rs b/opentmk/opentmk/src/context.rs index 27e8ee559c..8e8f122938 100644 --- a/opentmk/opentmk/src/context.rs +++ b/opentmk/opentmk/src/context.rs @@ -93,7 +93,8 @@ pub trait VtlPlatformTrait { /// Switches the current hardware thread back to the lower privileged VTL. fn switch_to_low_vtl(&mut self); - fn set_vp_state_with_vtl(&mut self, register_index: u32, value: u64, vtl: Vtl) -> TmkResult<()>; + fn set_vp_state_with_vtl(&mut self, register_index: u32, value: u64, vtl: Vtl) + -> TmkResult<()>; fn get_vp_state_with_vtl(&mut self, register_index: u32, vtl: Vtl) -> TmkResult; } @@ -121,8 +122,7 @@ impl VpExecutor { /// /// The closure receives a mutable reference to the platform-specific /// type `T` that implements `VtlPlatformTrait`. - pub fn command(mut self, cmd: impl FnOnce(&mut T) + 'static + Send) -> Self - { + pub fn command(mut self, cmd: impl FnOnce(&mut T) + 'static + Send) -> Self { self.cmd = Some(Box::new(cmd)); self } diff --git a/opentmk/opentmk/src/devices/mod.rs b/opentmk/opentmk/src/devices/mod.rs index 210e1097ee..1e7e6110dc 100644 --- a/opentmk/opentmk/src/devices/mod.rs +++ b/opentmk/opentmk/src/devices/mod.rs @@ -1 +1 @@ -pub mod tpm; \ No newline at end of file +pub mod tpm; diff --git a/opentmk/opentmk/src/devices/tpm/mod.rs b/opentmk/opentmk/src/devices/tpm/mod.rs index c8b4a9f7df..19c290fe42 100644 --- a/opentmk/opentmk/src/devices/tpm/mod.rs +++ b/opentmk/opentmk/src/devices/tpm/mod.rs @@ -1 +1,14 @@ -pub mod protocol; \ No newline at end of file +use zerocopy::IntoBytes; + +pub mod protocol; + +pub struct TpmUtil; +impl TpmUtil { + pub fn get_self_test_cmd() -> [u8; 4096] { + let session_tag = protocol::SessionTagEnum::NoSessions; + let cmd = protocol::protocol::SelfTestCmd::new(session_tag.into(), true); + let mut buffer = [0; 4096]; + buffer[..cmd.as_bytes().len()].copy_from_slice(cmd.as_bytes()); + buffer + } +} diff --git a/opentmk/opentmk/src/devices/tpm/protocol.rs b/opentmk/opentmk/src/devices/tpm/protocol.rs index c1c0b1d83b..90e72adb54 100644 --- a/opentmk/opentmk/src/devices/tpm/protocol.rs +++ b/opentmk/opentmk/src/devices/tpm/protocol.rs @@ -6,7 +6,8 @@ //! NOTE: once the `tpm-rs` project matures, this hand-rolled code should be *deleted* and //! replaced with types from that `tpm-rs` project. -use self::packed_nums::*; +use alloc::vec::Vec; + use bitfield_struct::bitfield; use thiserror::Error; use zerocopy::FromBytes; @@ -14,7 +15,8 @@ use zerocopy::FromZeros; use zerocopy::Immutable; use zerocopy::IntoBytes; use zerocopy::KnownLayout; -use alloc::vec::Vec; + +use self::packed_nums::*; #[allow(non_camel_case_types)] mod packed_nums { @@ -135,7 +137,6 @@ impl SessionTag { } } - #[derive(Error, Debug)] pub enum TpmCommandError { #[error("failed to execute the TPM command")] @@ -1966,7 +1967,6 @@ pub mod protocol { // === Startup === // - #[expect(dead_code)] pub enum StartupType { Clear, State, diff --git a/opentmk/opentmk/src/hypercall.rs b/opentmk/opentmk/src/hypercall.rs index b84849fa88..c5ff0f471f 100644 --- a/opentmk/opentmk/src/hypercall.rs +++ b/opentmk/opentmk/src/hypercall.rs @@ -4,21 +4,26 @@ //! Hypercall infrastructure. #![allow(dead_code)] -use core::{ - arch::asm, - mem::size_of, - sync::atomic::{AtomicU16, Ordering}, -}; +use core::arch::asm; +use core::mem::size_of; +use core::sync::atomic::AtomicU16; +use core::sync::atomic::Ordering; use arrayvec::ArrayVec; -use hvdef::{ - hypercall::{EnablePartitionVtlFlags, HvInputVtl, InitialVpContextX64}, - HvRegisterValue, HvRegisterVsmPartitionConfig, HvX64RegisterName, HvX64SegmentRegister, Vtl, - HV_PAGE_SIZE, -}; +use hvdef::hypercall::EnablePartitionVtlFlags; +use hvdef::hypercall::HvInputVtl; +use hvdef::hypercall::InitialVpContextX64; +use hvdef::HvRegisterValue; +use hvdef::HvRegisterVsmPartitionConfig; +use hvdef::HvX64RegisterName; +use hvdef::HvX64SegmentRegister; +use hvdef::Vtl; +use hvdef::HV_PAGE_SIZE; use memory_range::MemoryRange; -use minimal_rt::arch::hypercall::{invoke_hypercall, HYPERCALL_PAGE}; -use zerocopy::{FromBytes, IntoBytes}; +use minimal_rt::arch::hypercall::invoke_hypercall; +use minimal_rt::arch::hypercall::HYPERCALL_PAGE; +use zerocopy::FromBytes; +use zerocopy::IntoBytes; /// Page-aligned, page-sized buffer for use with hypercalls #[repr(C, align(4096))] diff --git a/opentmk/opentmk/src/main.rs b/opentmk/opentmk/src/main.rs index 9445d689b2..0a0f6f0e9a 100644 --- a/opentmk/opentmk/src/main.rs +++ b/opentmk/opentmk/src/main.rs @@ -16,6 +16,7 @@ extern crate alloc; pub mod arch; pub mod context; +pub mod devices; pub mod hypercall; pub mod platform; pub mod tests; @@ -23,4 +24,3 @@ pub mod tmk_assert; pub mod tmk_logger; pub mod tmkdefs; mod uefi; -pub mod devices; \ No newline at end of file diff --git a/opentmk/opentmk/src/platform/hypvctx.rs b/opentmk/opentmk/src/platform/hypvctx.rs index f3a4607d03..0c1114c957 100644 --- a/opentmk/opentmk/src/platform/hypvctx.rs +++ b/opentmk/opentmk/src/platform/hypvctx.rs @@ -1,28 +1,32 @@ -use alloc::{ - alloc::alloc, - boxed::Box, - collections::{btree_map::BTreeMap, btree_set::BTreeSet, linked_list::LinkedList}, -}; -use core::{alloc::Layout, arch::asm, fmt::Display, ops::Range}; - -use hvdef::{ - hypercall::{HvInputVtl, InitialVpContextX64}, - AlignedU128, Vtl, -}; +use alloc::alloc::alloc; +use alloc::boxed::Box; +use alloc::collections::btree_map::BTreeMap; +use alloc::collections::btree_set::BTreeSet; +use alloc::collections::linked_list::LinkedList; +use core::alloc::Layout; +use core::arch::asm; +use core::fmt::Display; +use core::ops::Range; + +use hvdef::hypercall::HvInputVtl; +use hvdef::hypercall::InitialVpContextX64; +use hvdef::AlignedU128; +use hvdef::Vtl; use memory_range::MemoryRange; -use minimal_rt::arch::{ - msr::{read_msr, write_msr}, -}; +use minimal_rt::arch::msr::read_msr; +use minimal_rt::arch::msr::write_msr; use sync_nostd::Mutex; -use crate::{ - context::{ - InterruptPlatformTrait, MsrPlatformTrait, SecureInterceptPlatformTrait, - VirtualProcessorPlatformTrait, VpExecutor, VtlPlatformTrait, - }, - hypercall::HvCall, - tmkdefs::{TmkError, TmkErrorType, TmkResult}, -}; +use crate::context::InterruptPlatformTrait; +use crate::context::MsrPlatformTrait; +use crate::context::SecureInterceptPlatformTrait; +use crate::context::VirtualProcessorPlatformTrait; +use crate::context::VpExecutor; +use crate::context::VtlPlatformTrait; +use crate::hypercall::HvCall; +use crate::tmkdefs::TmkError; +use crate::tmkdefs::TmkErrorType; +use crate::tmkdefs::TmkResult; const ALIGNMENT: usize = 4096; @@ -597,7 +601,6 @@ impl HvTestCtx { Vtl::Vtl0 => HvTestCtx::general_exec_handler, Vtl::Vtl1 => HvTestCtx::secure_exec_handler, _ => return Err(TmkErrorType::InvalidParameter.into()), - }; self.run_fn_with_current_context(handler) } diff --git a/opentmk/opentmk/src/tests/hv_cvm_mem_protect.rs b/opentmk/opentmk/src/tests/hv_cvm_mem_protect.rs index 3556429864..d17639bd0f 100644 --- a/opentmk/opentmk/src/tests/hv_cvm_mem_protect.rs +++ b/opentmk/opentmk/src/tests/hv_cvm_mem_protect.rs @@ -1,28 +1,41 @@ #![allow(warnings)] -use alloc::{alloc::alloc, sync::Arc}; -use core::{ - alloc::{GlobalAlloc, Layout}, arch::asm, cell::{RefCell, UnsafeCell}, fmt::Write, ops::Range, sync::atomic::{AtomicBool, AtomicI32, Ordering} -}; - -use ::alloc::{boxed::Box, vec::Vec}; +use alloc::alloc::alloc; +use alloc::sync::Arc; +use core::alloc::GlobalAlloc; +use core::alloc::Layout; +use core::arch::asm; +use core::cell::RefCell; +use core::cell::UnsafeCell; +use core::fmt::Write; +use core::ops::Range; +use core::sync::atomic::AtomicBool; +use core::sync::atomic::AtomicI32; +use core::sync::atomic::Ordering; + +use ::alloc::boxed::Box; +use ::alloc::vec::Vec; use context::VpExecutor; -use hvdef::{ - hypercall::HvInputVtl, HvAllArchRegisterName, HvRegisterVsmVpStatus, HvX64RegisterName, Vtl, -}; +use hvdef::hypercall::HvInputVtl; +use hvdef::HvAllArchRegisterName; +use hvdef::HvRegisterVsmVpStatus; +use hvdef::HvX64RegisterName; +use hvdef::Vtl; use hypvctx::HvTestCtx; -use sync_nostd::{Channel, Receiver, Sender}; -use uefi::{entry, Status}; - -use crate::{tmk_assert, tmk_logger}; -use crate::{ - context, - context::{ - InterruptPlatformTrait, SecureInterceptPlatformTrait, VirtualProcessorPlatformTrait, - VtlPlatformTrait, - }, - platform::hypvctx, - tmkdefs::TmkResult, -}; +use sync_nostd::Channel; +use sync_nostd::Receiver; +use sync_nostd::Sender; +use uefi::entry; +use uefi::Status; + +use crate::context; +use crate::context::InterruptPlatformTrait; +use crate::context::SecureInterceptPlatformTrait; +use crate::context::VirtualProcessorPlatformTrait; +use crate::context::VtlPlatformTrait; +use crate::platform::hypvctx; +use crate::tmk_assert; +use crate::tmk_logger; +use crate::tmkdefs::TmkResult; static mut HEAPX: RefCell<*mut u8> = RefCell::new(0 as *mut u8); static mut RETURN_VALUE: u8 = 0; @@ -74,12 +87,9 @@ fn backup_and_restore() { } } - pub fn exec(ctx: &mut T) where - T: InterruptPlatformTrait - + VtlPlatformTrait - + VirtualProcessorPlatformTrait, + T: InterruptPlatformTrait + VtlPlatformTrait + VirtualProcessorPlatformTrait, { log::info!("ctx ptr: {:p}", &ctx as *const _); @@ -93,7 +103,7 @@ where ctx.setup_partition_vtl(Vtl::Vtl1); log::info!("successfully setup partition vtl1"); - + ctx.start_on_vp(VpExecutor::new(0, Vtl::Vtl1).command(move |ctx: &mut T| { log::info!("successfully started running VTL1 on vp0."); @@ -102,7 +112,7 @@ where let ptr = unsafe { alloc(layout) }; log::info!("allocated some memory in the heap from vtl1"); - + unsafe { let mut z = HEAPX.borrow_mut(); *z = ptr; @@ -121,7 +131,10 @@ where }; let result = ctx.apply_vtl_protection_for_memory(range, Vtl::Vtl1); - tmk_assert!(result.is_ok(), "apply_vtl_protection_for_memory should succeed"); + tmk_assert!( + result.is_ok(), + "apply_vtl_protection_for_memory should succeed" + ); log::info!("moving to vtl0 to attempt to read the heap memory"); @@ -135,36 +148,39 @@ where }); let (tx, rx) = Channel::new().split(); - - ctx.start_on_vp(VpExecutor::new(0x2, Vtl::Vtl1).command(move|ctx: &mut T| { + + ctx.start_on_vp(VpExecutor::new(0x2, Vtl::Vtl1).command(move |ctx: &mut T| { ctx.setup_interrupt_handler(); log::info!("successfully started running VTL1 on vp2."); })); - ctx.start_on_vp(VpExecutor::new(0x2, Vtl::Vtl0).command( move |ctx: &mut T| unsafe { - log::info!("successfully started running VTL0 on vp2."); - unsafe { - let heapx = *HEAPX.borrow(); - - let read_protected_memory = || { *(heapx.add(10)) }; - - let read_result = read_protected_memory(); - log::info!( - "reading mutated heap memory from vtl0(it should not be 0xA2): 0x{:x}", - read_result - ); - tmk_assert!( - - - read_result != 0xA2, - "heap memory should not be accessible from vtl0" - ); - } - - tx.send(()); - })); + ctx.start_on_vp( + VpExecutor::new(0x2, Vtl::Vtl0).command(move |ctx: &mut T| unsafe { + log::info!("successfully started running VTL0 on vp2."); + unsafe { + let heapx = *HEAPX.borrow(); + + let read_protected_memory = || *(heapx.add(10)); + + let read_result = read_protected_memory(); + log::info!( + "reading mutated heap memory from vtl0(it should not be 0xA2): 0x{:x}", + read_result + ); + tmk_assert!( + read_result != 0xA2, + "heap memory should not be accessible from vtl0" + ); + } + + tx.send(()); + }), + ); rx.recv(); - tmk_assert!(false, "we should not reach here injecting MC should terminate the test"); + tmk_assert!( + false, + "we should not reach here injecting MC should terminate the test" + ); } diff --git a/opentmk/opentmk/src/tests/hv_error_vp_start.rs b/opentmk/opentmk/src/tests/hv_error_vp_start.rs index 9d0908cea5..fe98b49bfd 100644 --- a/opentmk/opentmk/src/tests/hv_error_vp_start.rs +++ b/opentmk/opentmk/src/tests/hv_error_vp_start.rs @@ -1,10 +1,10 @@ use hvdef::Vtl; use sync_nostd::Channel; -use crate::{ - context::{VirtualProcessorPlatformTrait, VpExecutor, VtlPlatformTrait}, - tmk_assert, -}; +use crate::context::VirtualProcessorPlatformTrait; +use crate::context::VpExecutor; +use crate::context::VtlPlatformTrait; +use crate::tmk_assert; pub fn exec(ctx: &mut T) where diff --git a/opentmk/opentmk/src/tests/hv_misc.rs b/opentmk/opentmk/src/tests/hv_misc.rs index 6db4d07de1..bea9f22ad7 100644 --- a/opentmk/opentmk/src/tests/hv_misc.rs +++ b/opentmk/opentmk/src/tests/hv_misc.rs @@ -1,35 +1,45 @@ #![allow(warnings)] -use alloc::{alloc::alloc, string::String, sync::Arc}; -use core::{ - alloc::{GlobalAlloc, Layout}, - arch::asm, - cell::{RefCell, UnsafeCell}, - fmt::Write, - ops::Range, - sync::atomic::{AtomicBool, AtomicI32, Ordering}, -}; - -use ::alloc::{boxed::Box, vec::Vec}; +use alloc::alloc::alloc; +use alloc::string::String; +use alloc::sync::Arc; +use core::alloc::GlobalAlloc; +use core::alloc::Layout; +use core::arch::asm; +use core::cell::RefCell; +use core::cell::UnsafeCell; +use core::fmt::Write; +use core::ops::Range; +use core::sync::atomic::AtomicBool; +use core::sync::atomic::AtomicI32; +use core::sync::atomic::Ordering; + +use ::alloc::boxed::Box; +use ::alloc::vec::Vec; use context::VpExecutor; -use hvdef::{ - hypercall::HvInputVtl, HvAllArchRegisterName, HvRegisterVsmVpStatus, HvX64RegisterName, Vtl, -}; +use hvdef::hypercall::HvInputVtl; +use hvdef::HvAllArchRegisterName; +use hvdef::HvRegisterVsmVpStatus; +use hvdef::HvX64RegisterName; +use hvdef::Vtl; use hypvctx::HvTestCtx; -use iced_x86::{DecoderOptions, Formatter, NasmFormatter}; -use sync_nostd::{Channel, Receiver, Sender}; -use uefi::{entry, Status}; - -use crate::{ - context, - context::{ - InterruptPlatformTrait, SecureInterceptPlatformTrait, VirtualProcessorPlatformTrait, - VtlPlatformTrait, - }, - platform::hypvctx, - tmkdefs::TmkResult, -}; - -use crate::{tmk_assert, tmk_logger}; +use iced_x86::DecoderOptions; +use iced_x86::Formatter; +use iced_x86::NasmFormatter; +use sync_nostd::Channel; +use sync_nostd::Receiver; +use sync_nostd::Sender; +use uefi::entry; +use uefi::Status; + +use crate::context; +use crate::context::InterruptPlatformTrait; +use crate::context::SecureInterceptPlatformTrait; +use crate::context::VirtualProcessorPlatformTrait; +use crate::context::VtlPlatformTrait; +use crate::platform::hypvctx; +use crate::tmk_assert; +use crate::tmk_logger; +use crate::tmkdefs::TmkResult; static mut HEAPX: RefCell<*mut u8> = RefCell::new(0 as *mut u8); diff --git a/opentmk/opentmk/src/tests/hv_processor.rs b/opentmk/opentmk/src/tests/hv_processor.rs index e39a95f5e4..3ce93018c6 100644 --- a/opentmk/opentmk/src/tests/hv_processor.rs +++ b/opentmk/opentmk/src/tests/hv_processor.rs @@ -1,10 +1,11 @@ use hvdef::Vtl; use sync_nostd::Channel; -use crate::{ - context::{InterruptPlatformTrait, VirtualProcessorPlatformTrait, VpExecutor, VtlPlatformTrait}, - tmk_assert, -}; +use crate::context::InterruptPlatformTrait; +use crate::context::VirtualProcessorPlatformTrait; +use crate::context::VpExecutor; +use crate::context::VtlPlatformTrait; +use crate::tmk_assert; #[inline(never)] pub fn exec(ctx: &mut T) @@ -22,9 +23,7 @@ where _ = ctx.setup_interrupt_handler(); - _ = ctx.set_interrupt_idx(0x6, || { - loop{} - }); + _ = ctx.set_interrupt_idx(0x6, || loop {}); // Testing BSP VTL Bringup { diff --git a/opentmk/opentmk/src/tests/hv_tpm.rs b/opentmk/opentmk/src/tests/hv_tpm.rs index d3ff19551e..a866cf41fa 100644 --- a/opentmk/opentmk/src/tests/hv_tpm.rs +++ b/opentmk/opentmk/src/tests/hv_tpm.rs @@ -1,19 +1,21 @@ use alloc::string::String; -use core::{alloc::Layout, ops::Range}; - -use ::alloc::alloc::alloc; -use hvdef::{HvX64RegisterName, Vtl}; -use iced_x86::{DecoderOptions, Formatter, NasmFormatter}; - -use crate::{ - arch::tpm::{Tpm, TpmUtil}, - context::{ - InterruptPlatformTrait, SecureInterceptPlatformTrait, VirtualProcessorPlatformTrait, - VpExecutor, VtlPlatformTrait, - }, - platform::hypvctx::HvTestCtx, - tmk_assert, -}; +use core::ops::Range; + +use hvdef::HvX64RegisterName; +use hvdef::Vtl; +use iced_x86::DecoderOptions; +use iced_x86::Formatter; +use iced_x86::NasmFormatter; + +use crate::arch::tpm::Tpm; +use crate::context::InterruptPlatformTrait; +use crate::context::SecureInterceptPlatformTrait; +use crate::context::VirtualProcessorPlatformTrait; +use crate::context::VpExecutor; +use crate::context::VtlPlatformTrait; +use crate::devices::tpm::TpmUtil; +use crate::platform::hypvctx::HvTestCtx; +use crate::tmk_assert; pub fn read_assembly_output(target: u64) -> usize { unsafe { @@ -98,13 +100,13 @@ where log::info!("set intercept handler successfully!"); let r = ctx.setup_partition_vtl(Vtl::Vtl1); tmk_assert!(r.is_ok(), "setup_partition_vtl should succeed"); - + let response_rage = Range { start: tpm_gpa as u64 + 4096, end: tpm_gpa as u64 + 4096 * 2, }; - let r= ctx.start_on_vp(VpExecutor::new(0, Vtl::Vtl1).command(move |ctx: &mut T| { + let _r = ctx.start_on_vp(VpExecutor::new(0, Vtl::Vtl1).command(move |ctx: &mut T| { log::info!("successfully started running VTL1 on vp0."); let r = ctx.setup_secure_intercept(0x30); tmk_assert!(r.is_ok(), "setup_secure_intercept should succeed"); @@ -114,13 +116,13 @@ where let mut hv = HvTestCtx::new(); // expected to get interrupt in VTL1. // CVMs dont support hypercalls to get the current VTL from VTL1/0. - hv.init(Vtl::Vtl1); + _ = hv.init(Vtl::Vtl1); log::info!( "current vp from interrupt: {}", hv.get_current_vp().unwrap() ); - let rip = hvdef::HvX64RegisterName::Rip.0; + let rip = HvX64RegisterName::Rip.0; let reg = hv.get_vp_state_with_vtl(rip, Vtl::Vtl0); tmk_assert!(reg.is_ok(), "get_vp_state_with_vtl should succeed"); @@ -154,7 +156,7 @@ where }); tmk_assert!(r.is_ok(), "set_interrupt_idx should succeed"); - let r= ctx.setup_vtl_protection(); + let r = ctx.setup_vtl_protection(); tmk_assert!(r.is_ok(), "setup_vtl_protection should succeed"); log::info!("enabled vtl protections for the partition."); @@ -174,6 +176,5 @@ where Tpm::execute_command(); log::warn!("TPM self test command executed"); - loop {} } diff --git a/opentmk/opentmk/src/tests/hv_tpm_read_cvm.rs b/opentmk/opentmk/src/tests/hv_tpm_read_cvm.rs index b28d8f8d0f..f8c27baff3 100644 --- a/opentmk/opentmk/src/tests/hv_tpm_read_cvm.rs +++ b/opentmk/opentmk/src/tests/hv_tpm_read_cvm.rs @@ -1,19 +1,15 @@ -use alloc::string::String; -use core::{alloc::Layout, ops::Range}; - -use ::alloc::alloc::alloc; -use hvdef::{HvX64RegisterName, Vtl}; -use iced_x86::{DecoderOptions, Formatter, NasmFormatter}; - -use crate::{ - arch::tpm::{Tpm, TpmUtil}, - context::{ - InterruptPlatformTrait, SecureInterceptPlatformTrait, VirtualProcessorPlatformTrait, - VpExecutor, VtlPlatformTrait, - }, - platform::hypvctx::HvTestCtx, - tmk_assert, -}; +use core::ops::Range; + +use hvdef::Vtl; + +use crate::arch::tpm::Tpm; +use crate::context::InterruptPlatformTrait; +use crate::context::SecureInterceptPlatformTrait; +use crate::context::VirtualProcessorPlatformTrait; +use crate::context::VpExecutor; +use crate::context::VtlPlatformTrait; +use crate::devices::tpm::TpmUtil; +use crate::tmk_assert; pub fn exec(ctx: &mut T) where @@ -51,18 +47,18 @@ where log::info!("set intercept handler successfully!"); let r = ctx.setup_partition_vtl(Vtl::Vtl1); tmk_assert!(r.is_ok(), "setup_partition_vtl should succeed"); - + let command_range = Range { start: tpm_gpa as u64, end: tpm_gpa as u64 + 4096, }; - let r= ctx.start_on_vp(VpExecutor::new(0, Vtl::Vtl1).command(move |ctx: &mut T| { + let _r = ctx.start_on_vp(VpExecutor::new(0, Vtl::Vtl1).command(move |ctx: &mut T| { log::info!("successfully started running VTL1 on vp0."); let r = ctx.setup_secure_intercept(0x30); tmk_assert!(r.is_ok(), "setup_secure_intercept should succeed"); - let r= ctx.setup_vtl_protection(); + let r = ctx.setup_vtl_protection(); tmk_assert!(r.is_ok(), "setup_vtl_protection should succeed"); log::info!("enabled vtl protections for the partition."); ctx.switch_to_low_vtl(); @@ -77,16 +73,15 @@ where let cmd = TpmUtil::get_self_test_cmd(); _tpm.copy_to_command_buffer(&cmd); log::warn!("TPM self test command copied to buffer"); - - let r = ctx.start_on_vp(VpExecutor::new(0, Vtl::Vtl1).command(move |ctx: &mut T| { + let r = ctx.start_on_vp(VpExecutor::new(0, Vtl::Vtl1).command(move |ctx: &mut T| { let r = ctx.apply_vtl_protection_for_memory(command_range, Vtl::Vtl1); tmk_assert!(r.is_ok(), "apply_vtl_protection_for_memory should succeed"); ctx.switch_to_low_vtl(); })); tmk_assert!(r.is_ok(), "start_on_vp should succeed"); - + log::warn!("about to execute TPM self test command.."); Tpm::execute_command(); log::warn!("TPM self test command executed"); diff --git a/opentmk/opentmk/src/tests/hv_tpm_write_cvm.rs b/opentmk/opentmk/src/tests/hv_tpm_write_cvm.rs index 1640100a1c..c659c2c0b8 100644 --- a/opentmk/opentmk/src/tests/hv_tpm_write_cvm.rs +++ b/opentmk/opentmk/src/tests/hv_tpm_write_cvm.rs @@ -1,19 +1,15 @@ -use alloc::string::String; -use core::{alloc::Layout, ops::Range}; - -use ::alloc::alloc::alloc; -use hvdef::{HvX64RegisterName, Vtl}; -use iced_x86::{DecoderOptions, Formatter, NasmFormatter}; - -use crate::{ - arch::tpm::{Tpm, TpmUtil}, - context::{ - InterruptPlatformTrait, SecureInterceptPlatformTrait, VirtualProcessorPlatformTrait, - VpExecutor, VtlPlatformTrait, - }, - platform::hypvctx::HvTestCtx, - tmk_assert, -}; +use core::ops::Range; + +use hvdef::Vtl; + +use crate::arch::tpm::Tpm; +use crate::context::InterruptPlatformTrait; +use crate::context::SecureInterceptPlatformTrait; +use crate::context::VirtualProcessorPlatformTrait; +use crate::context::VpExecutor; +use crate::context::VtlPlatformTrait; +use crate::devices::tpm::TpmUtil; +use crate::tmk_assert; pub fn exec(ctx: &mut T) where @@ -51,18 +47,18 @@ where log::info!("set intercept handler successfully!"); let r = ctx.setup_partition_vtl(Vtl::Vtl1); tmk_assert!(r.is_ok(), "setup_partition_vtl should succeed"); - + let response_rage = Range { start: tpm_gpa as u64 + 4096, end: tpm_gpa as u64 + 4096 * 2, }; - let r= ctx.start_on_vp(VpExecutor::new(0, Vtl::Vtl1).command(move |ctx: &mut T| { + let _r = ctx.start_on_vp(VpExecutor::new(0, Vtl::Vtl1).command(move |ctx: &mut T| { log::info!("successfully started running VTL1 on vp0."); let r = ctx.setup_secure_intercept(0x30); tmk_assert!(r.is_ok(), "setup_secure_intercept should succeed"); - let r= ctx.setup_vtl_protection(); + let r = ctx.setup_vtl_protection(); tmk_assert!(r.is_ok(), "setup_vtl_protection should succeed"); log::info!("enabled vtl protections for the partition."); diff --git a/opentmk/opentmk/src/tests/mod.rs b/opentmk/opentmk/src/tests/mod.rs index 46ba3f5c12..b4b70472c8 100644 --- a/opentmk/opentmk/src/tests/mod.rs +++ b/opentmk/opentmk/src/tests/mod.rs @@ -1,16 +1,16 @@ #![expect(dead_code)] use crate::platform::hypvctx::HvTestCtx; +mod hv_cvm_mem_protect; mod hv_error_vp_start; mod hv_misc; mod hv_processor; -mod hv_cvm_mem_protect; mod hv_tpm; -mod hv_tpm_write_cvm; mod hv_tpm_read_cvm; +mod hv_tpm_write_cvm; pub fn run_test() { let mut ctx = HvTestCtx::new(); ctx.init(hvdef::Vtl::Vtl0).expect("failed to init on BSP"); hv_tpm_read_cvm::exec(&mut ctx); -} \ No newline at end of file +} diff --git a/opentmk/opentmk/src/tmk_assert.rs b/opentmk/opentmk/src/tmk_assert.rs index e3a86bc28a..04cf9544f6 100644 --- a/opentmk/opentmk/src/tmk_assert.rs +++ b/opentmk/opentmk/src/tmk_assert.rs @@ -1,4 +1,5 @@ -use alloc::string::{String, ToString}; +use alloc::string::String; +use alloc::string::ToString; use core::fmt::Write; use serde::Serialize; diff --git a/opentmk/opentmk/src/tmk_logger.rs b/opentmk/opentmk/src/tmk_logger.rs index 0e71aac800..e9c662fc8f 100644 --- a/opentmk/opentmk/src/tmk_logger.rs +++ b/opentmk/opentmk/src/tmk_logger.rs @@ -1,14 +1,17 @@ -use alloc::{ - fmt::format, - string::{String, ToString}, -}; +use alloc::fmt::format; +use alloc::string::String; +use alloc::string::ToString; use core::fmt::Write; +use anyhow::Result; use log::SetLoggerError; use serde::Serialize; -use sync_nostd::{Mutex, MutexGuard}; -use anyhow::Result; -use crate::arch::serial::{InstrIoAccess, Serial, SerialPort}; +use sync_nostd::Mutex; +use sync_nostd::MutexGuard; + +use crate::arch::serial::InstrIoAccess; +use crate::arch::serial::Serial; +use crate::arch::serial::SerialPort; #[derive(Serialize)] struct LogEntry { @@ -90,9 +93,9 @@ where } type SerialPortWriter = Serial; -pub static LOGGER: TmkLogger> = TmkLogger::new(SerialPortWriter::new(SerialPort::COM2, InstrIoAccess)); +pub static LOGGER: TmkLogger> = + TmkLogger::new(SerialPortWriter::new(SerialPort::COM2, InstrIoAccess)); pub fn init() -> Result<(), SetLoggerError> { - log::set_logger(&LOGGER) - .map(|()| log::set_max_level(log::LevelFilter::Debug)) + log::set_logger(&LOGGER).map(|()| log::set_max_level(log::LevelFilter::Debug)) } diff --git a/opentmk/opentmk/src/uefi/alloc.rs b/opentmk/opentmk/src/uefi/alloc.rs index 2fa627ccae..356b390278 100644 --- a/opentmk/opentmk/src/uefi/alloc.rs +++ b/opentmk/opentmk/src/uefi/alloc.rs @@ -1,11 +1,12 @@ -use core::{alloc::GlobalAlloc, cell::RefCell}; +use core::alloc::GlobalAlloc; +use core::cell::RefCell; use linked_list_allocator::LockedHeap; use sync_nostd::Mutex; -use uefi::{ - allocator::Allocator, - boot::{self, AllocateType, MemoryType}, -}; +use uefi::allocator::Allocator; +use uefi::boot::AllocateType; +use uefi::boot::MemoryType; +use uefi::boot::{self}; pub const SIZE_1MB: usize = 1024 * 1024; const PAGE_SIZE: usize = 4096; diff --git a/opentmk/opentmk/src/uefi/init.rs b/opentmk/opentmk/src/uefi/init.rs index f5674d9582..292dacd159 100644 --- a/opentmk/opentmk/src/uefi/init.rs +++ b/opentmk/opentmk/src/uefi/init.rs @@ -1,7 +1,8 @@ -use uefi::{ - boot::{exit_boot_services, MemoryType}, - guid, CStr16, Status, -}; +use uefi::boot::exit_boot_services; +use uefi::boot::MemoryType; +use uefi::guid; +use uefi::CStr16; +use uefi::Status; use super::alloc::ALLOCATOR; diff --git a/opentmk/opentmk/src/uefi/mod.rs b/opentmk/opentmk/src/uefi/mod.rs index 7441a56f06..cbd6b96d92 100644 --- a/opentmk/opentmk/src/uefi/mod.rs +++ b/opentmk/opentmk/src/uefi/mod.rs @@ -6,7 +6,8 @@ pub mod init; mod rt; use init::init; -use uefi::{entry, Status}; +use uefi::entry; +use uefi::Status; use crate::tmk_assert; @@ -18,5 +19,5 @@ fn uefi_main() -> Status { log::warn!("TEST_START"); crate::tests::run_test(); log::warn!("TEST_END"); - loop{} + loop {} } From c0f3e0a51f8d289dc079200ccd42a31cbd69c35d Mon Sep 17 00:00:00 2001 From: Mayank Kumar Date: Tue, 26 Aug 2025 14:46:53 +0000 Subject: [PATCH 17/23] chore: resolve PR comments --- Cargo.lock | 18 +- Cargo.toml | 9 +- opentmk/{opentmk => }/Cargo.toml | 15 +- opentmk/{opentmk => }/README.md | 0 opentmk/{opentmk => }/build_deploy.sh | 0 opentmk/opentmk/src/context.rs | 135 --------- .../opentmk/src/tests/hv_cvm_mem_protect.rs | 186 ------------ opentmk/opentmk/src/tests/hv_misc.rs | 266 ------------------ opentmk/opentmk/src/tests/hv_tpm.rs | 180 ------------ opentmk/{opentmk => }/rustfmt.toml | 0 .../src/arch/aarch64/hypercall.rs | 2 +- opentmk/{opentmk => }/src/arch/aarch64/mod.rs | 0 opentmk/{opentmk => }/src/arch/mod.rs | 0 .../src/arch/x86_64/hypercall.rs | 2 +- .../src/arch/x86_64/interrupt.rs | 2 +- .../arch/x86_64/interrupt_handler_register.rs | 4 +- opentmk/{opentmk => }/src/arch/x86_64/io.rs | 0 opentmk/{opentmk => }/src/arch/x86_64/mod.rs | 0 opentmk/{opentmk => }/src/arch/x86_64/rtc.rs | 43 ++- .../{opentmk => }/src/arch/x86_64/serial.rs | 2 +- opentmk/{opentmk => }/src/arch/x86_64/tpm.rs | 0 opentmk/src/context.rs | 131 +++++++++ opentmk/{opentmk => }/src/devices/mod.rs | 0 opentmk/{opentmk => }/src/devices/tpm/mod.rs | 0 .../{opentmk => }/src/devices/tpm/protocol.rs | 6 +- opentmk/{opentmk => }/src/hypercall.rs | 24 +- opentmk/{opentmk => }/src/main.rs | 2 +- opentmk/{opentmk => }/src/platform/hypvctx.rs | 153 +++++----- opentmk/{opentmk => }/src/platform/mod.rs | 0 .../tests/hyperv}/hv_error_vp_start.rs | 4 +- .../tests/hyperv/hv_memory_protect_read.rs | 141 ++++++++++ .../tests/hyperv/hv_memory_protect_write.rs | 132 +++++++++ .../tests/hyperv}/hv_processor.rs | 2 +- .../tests/hyperv}/hv_tpm_read_cvm.rs | 0 .../tests/hyperv}/hv_tpm_write_cvm.rs | 0 opentmk/src/tests/hyperv/mod.rs | 7 + opentmk/src/tests/hyperv/test_helpers.rs | 43 +++ opentmk/{opentmk => }/src/tests/mod.rs | 11 +- opentmk/{opentmk => }/src/tmk_assert.rs | 0 opentmk/{opentmk => }/src/tmk_logger.rs | 5 +- opentmk/{opentmk => }/src/tmkdefs.rs | 26 +- opentmk/{opentmk => }/src/uefi/alloc.rs | 47 ++-- opentmk/{opentmk => }/src/uefi/init.rs | 4 +- opentmk/{opentmk => }/src/uefi/mod.rs | 0 opentmk/{opentmk => }/src/uefi/rt.rs | 2 - .../nostd_spin_channel}/Cargo.toml | 2 +- .../nostd_spin_channel}/src/lib.rs | 2 +- 47 files changed, 646 insertions(+), 962 deletions(-) rename opentmk/{opentmk => }/Cargo.toml (82%) rename opentmk/{opentmk => }/README.md (100%) rename opentmk/{opentmk => }/build_deploy.sh (100%) delete mode 100644 opentmk/opentmk/src/tests/hv_cvm_mem_protect.rs delete mode 100644 opentmk/opentmk/src/tests/hv_misc.rs delete mode 100644 opentmk/opentmk/src/tests/hv_tpm.rs rename opentmk/{opentmk => }/rustfmt.toml (100%) rename opentmk/{opentmk => }/src/arch/aarch64/hypercall.rs (98%) rename opentmk/{opentmk => }/src/arch/aarch64/mod.rs (100%) rename opentmk/{opentmk => }/src/arch/mod.rs (100%) rename opentmk/{opentmk => }/src/arch/x86_64/hypercall.rs (95%) rename opentmk/{opentmk => }/src/arch/x86_64/interrupt.rs (98%) rename opentmk/{opentmk => }/src/arch/x86_64/interrupt_handler_register.rs (99%) rename opentmk/{opentmk => }/src/arch/x86_64/io.rs (100%) rename opentmk/{opentmk => }/src/arch/x86_64/mod.rs (100%) rename opentmk/{opentmk => }/src/arch/x86_64/rtc.rs (73%) rename opentmk/{opentmk => }/src/arch/x86_64/serial.rs (99%) rename opentmk/{opentmk => }/src/arch/x86_64/tpm.rs (100%) create mode 100644 opentmk/src/context.rs rename opentmk/{opentmk => }/src/devices/mod.rs (100%) rename opentmk/{opentmk => }/src/devices/tpm/mod.rs (100%) rename opentmk/{opentmk => }/src/devices/tpm/protocol.rs (99%) rename opentmk/{opentmk => }/src/hypercall.rs (97%) rename opentmk/{opentmk => }/src/main.rs (96%) rename opentmk/{opentmk => }/src/platform/hypvctx.rs (80%) rename opentmk/{opentmk => }/src/platform/mod.rs (100%) rename opentmk/{opentmk/src/tests => src/tests/hyperv}/hv_error_vp_start.rs (93%) create mode 100644 opentmk/src/tests/hyperv/hv_memory_protect_read.rs create mode 100644 opentmk/src/tests/hyperv/hv_memory_protect_write.rs rename opentmk/{opentmk/src/tests => src/tests/hyperv}/hv_processor.rs (99%) rename opentmk/{opentmk/src/tests => src/tests/hyperv}/hv_tpm_read_cvm.rs (100%) rename opentmk/{opentmk/src/tests => src/tests/hyperv}/hv_tpm_write_cvm.rs (100%) create mode 100644 opentmk/src/tests/hyperv/mod.rs create mode 100644 opentmk/src/tests/hyperv/test_helpers.rs rename opentmk/{opentmk => }/src/tests/mod.rs (52%) rename opentmk/{opentmk => }/src/tmk_assert.rs (100%) rename opentmk/{opentmk => }/src/tmk_logger.rs (96%) rename opentmk/{opentmk => }/src/tmkdefs.rs (80%) rename opentmk/{opentmk => }/src/uefi/alloc.rs (66%) rename opentmk/{opentmk => }/src/uefi/init.rs (93%) rename opentmk/{opentmk => }/src/uefi/mod.rs (100%) rename opentmk/{opentmk => }/src/uefi/rt.rs (82%) rename {opentmk/sync => support/nostd_spin_channel}/Cargo.toml (84%) rename {opentmk/sync => support/nostd_spin_channel}/src/lib.rs (99%) diff --git a/Cargo.lock b/Cargo.lock index 0674d71293..e2f566aa3f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4436,6 +4436,13 @@ dependencies = [ "libc", ] +[[package]] +name = "nostd_spin_channel" +version = "0.1.0" +dependencies = [ + "spin 0.10.0", +] + [[package]] name = "ntapi" version = "0.4.1" @@ -4785,7 +4792,6 @@ dependencies = [ name = "opentmk" version = "0.0.0" dependencies = [ - "anyhow", "arrayvec", "bitfield-struct", "cfg-if", @@ -4797,9 +4803,10 @@ dependencies = [ "memory_range", "minimal_rt", "minimal_rt_build", + "nostd_spin_channel", "serde", "serde_json", - "sync_nostd", + "spin 0.10.0", "thiserror 2.0.0", "uefi", "x86_64", @@ -6543,13 +6550,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "sync_nostd" -version = "0.1.0" -dependencies = [ - "spin 0.10.0", -] - [[package]] name = "tap" version = "1.0.1" diff --git a/Cargo.toml b/Cargo.toml index ed1823f711..fbefe739da 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,8 @@ members = [ "support/sparse_mmap/fuzz", "support/ucs2/fuzz", "vm/devices/chipset/fuzz", + #opentmk support + "support/nostd_spin_channel", "vm/devices/firmware/firmware_uefi/fuzz", "vm/devices/storage/disk_nvme/nvme_driver/fuzz", "vm/devices/storage/ide/fuzz", @@ -43,8 +45,7 @@ members = [ "vm/vmgs/vmgs_lib", "vm/vmgs/vmgstool", # opentmk - "opentmk/opentmk", - "opentmk/sync" + "opentmk/", ] exclude = [ "xsync", @@ -378,7 +379,7 @@ vnc = { path = "workers/vnc_worker/vnc" } profiler_worker = { path = "openhcl/profiler_worker" } # opentmk -sync_nostd = { path = "opentmk/sync"} +nostd_spin_channel = { path = "support/nostd_spin_channel"} # crates.io anyhow = "1.0" @@ -451,7 +452,7 @@ jiff = "0.1" kvm-bindings = "0.7" # Use of these specific REPO will go away when changes are taken upstream. landlock = "0.3.1" -lazy_static = { version = "1.4.0", features = ["spin_no_std"] } +lazy_static = "1.4.0" libc = "0.2" libfuzzer-sys = "0.4" libtest-mimic = "0.8" diff --git a/opentmk/opentmk/Cargo.toml b/opentmk/Cargo.toml similarity index 82% rename from opentmk/opentmk/Cargo.toml rename to opentmk/Cargo.toml index c120d08e84..b11cced3fb 100644 --- a/opentmk/opentmk/Cargo.toml +++ b/opentmk/Cargo.toml @@ -7,16 +7,17 @@ edition.workspace = true rust-version.workspace = true [dependencies] -anyhow = {version = "1.0", default-features = false} arrayvec.workspace = true bitfield-struct.workspace = true cfg-if.workspace = true hvdef = {workspace = true} -lazy_static.workspace = true +iced-x86 = { workspace = true, features = ["decoder", "nasm", "no_std"]} +lazy_static = { workspace = true, features = ["spin_no_std"] } linked_list_allocator.workspace = true log.workspace = true memory_range.workspace = true minimal_rt.workspace = true +spin.workspace = true serde = { version = "1.0", default-features = false, features = ["derive"]} serde_json = { version = "1.0", default-features = false, features = ["alloc"] } thiserror.workspace = true @@ -24,15 +25,7 @@ uefi = { workspace = true, features = ["alloc"] } x86_64.workspace = true x86defs.workspace = true zerocopy.workspace = true -sync_nostd.workspace = true - - -[dependencies.iced-x86] -version = "1.21.0" -default-features = false -# See below for all features -features = ["decoder", "nasm", "no_std"] - +nostd_spin_channel.workspace = true [lints] workspace = true diff --git a/opentmk/opentmk/README.md b/opentmk/README.md similarity index 100% rename from opentmk/opentmk/README.md rename to opentmk/README.md diff --git a/opentmk/opentmk/build_deploy.sh b/opentmk/build_deploy.sh similarity index 100% rename from opentmk/opentmk/build_deploy.sh rename to opentmk/build_deploy.sh diff --git a/opentmk/opentmk/src/context.rs b/opentmk/opentmk/src/context.rs index 8e8f122938..e69de29bb2 100644 --- a/opentmk/opentmk/src/context.rs +++ b/opentmk/opentmk/src/context.rs @@ -1,135 +0,0 @@ -#![allow(dead_code)] -use alloc::boxed::Box; -use core::ops::Range; - -use hvdef::Vtl; - -use crate::tmkdefs::TmkResult; - -pub trait SecureInterceptPlatformTrait { - /// Installs a secure-world intercept for the given interrupt. - /// - /// The platform must arrange that the supplied `interrupt_idx` - /// triggers a VM-exit or any other mechanism that transfers control - /// to the TMK secure handler. - /// - /// Returns `Ok(())` on success or an error wrapped in `TmkResult`. - fn setup_secure_intercept(&mut self, interrupt_idx: u8) -> TmkResult<()>; -} - -pub trait InterruptPlatformTrait { - /// Associates an interrupt vector with a handler inside the - /// non-secure world. - /// - /// * `interrupt_idx` – IDT/GIC index to program - /// * `handler` – Function that will be executed when the interrupt - /// fires. - fn set_interrupt_idx(&mut self, interrupt_idx: u8, handler: fn()) -> TmkResult<()>; - - /// Finalises platform specific interrupt setup (enables the table, - /// unmasks lines, etc.). - fn setup_interrupt_handler(&mut self) -> TmkResult<()>; -} - -pub trait MsrPlatformTrait { - /// Reads the content of `msr`. - /// - /// Returns the 64-bit value currently stored in that MSR. - fn read_msr(&mut self, msr: u32) -> TmkResult; - - /// Writes `value` into `msr`. - fn write_msr(&mut self, msr: u32, value: u64) -> TmkResult<()>; -} - -pub trait VirtualProcessorPlatformTrait -where - T: VtlPlatformTrait, -{ - /// Returns the index of the virtual CPU currently executing this - /// code. - fn get_current_vp(&self) -> TmkResult; - - /// Reads the architecture specific register identified by `reg`. - fn get_register(&mut self, reg: u32) -> TmkResult; - - /// Total number of online VPs in the partition. - fn get_vp_count(&self) -> TmkResult; - - /// Queues `cmd` to run later on the VP described inside the - /// `VpExecutor`. - fn queue_command_vp(&mut self, cmd: VpExecutor) -> TmkResult<()>; - - /// Synchronously executes `cmd` on its target VP. - fn start_on_vp(&mut self, cmd: VpExecutor) -> TmkResult<()>; - - /// Starts the target VP (if required) and executes `cmd` with a - /// platform provided default VTL context. - fn start_running_vp_with_default_context(&mut self, cmd: VpExecutor) -> TmkResult<()>; -} - -pub trait VtlPlatformTrait { - /// Applies VTL protection to the supplied physical address range. - fn apply_vtl_protection_for_memory(&mut self, range: Range, vtl: Vtl) -> TmkResult<()>; - - /// Enables the given `vtl` on `vp_index` with a default context. - fn enable_vp_vtl_with_default_context(&mut self, vp_index: u32, vtl: Vtl) -> TmkResult<()>; - - /// Returns the VTL level the caller is currently executing in. - fn get_current_vtl(&self) -> TmkResult; - - /// Sets the default VTL context on `vp_index`. - fn set_default_ctx_to_vp(&mut self, vp_index: u32, vtl: Vtl) -> TmkResult<()>; - - /// Performs partition wide initialisation for a given `vtl`. - fn setup_partition_vtl(&mut self, vtl: Vtl) -> TmkResult<()>; - - /// Platform specific global VTL preparation (stage 2 translation, - /// EPT, etc.). - fn setup_vtl_protection(&mut self) -> TmkResult<()>; - - /// Switches the current hardware thread to the higher privileged VTL. - fn switch_to_high_vtl(&mut self); - - /// Switches the current hardware thread back to the lower privileged VTL. - fn switch_to_low_vtl(&mut self); - - fn set_vp_state_with_vtl(&mut self, register_index: u32, value: u64, vtl: Vtl) - -> TmkResult<()>; - - fn get_vp_state_with_vtl(&mut self, register_index: u32, vtl: Vtl) -> TmkResult; -} - -pub trait X64PlatformTrait {} -pub trait Aarch64PlatformTrait {} - -pub struct VpExecutor { - vp_index: u32, - vtl: Vtl, - cmd: Option>, -} - -impl VpExecutor { - /// Creates a new executor targeting `vp_index` running in `vtl`. - pub fn new(vp_index: u32, vtl: Vtl) -> Self { - VpExecutor { - vp_index, - vtl, - cmd: None, - } - } - - /// Stores a closure `cmd` that will be executed on the target VP. - /// - /// The closure receives a mutable reference to the platform-specific - /// type `T` that implements `VtlPlatformTrait`. - pub fn command(mut self, cmd: impl FnOnce(&mut T) + 'static + Send) -> Self { - self.cmd = Some(Box::new(cmd)); - self - } - - /// Extracts the tuple `(vp_index, vtl, cmd)` consuming `self`. - pub fn get(mut self) -> (u32, Vtl, Option>) { - let cmd = self.cmd.take(); - (self.vp_index, self.vtl, cmd) - } -} diff --git a/opentmk/opentmk/src/tests/hv_cvm_mem_protect.rs b/opentmk/opentmk/src/tests/hv_cvm_mem_protect.rs deleted file mode 100644 index d17639bd0f..0000000000 --- a/opentmk/opentmk/src/tests/hv_cvm_mem_protect.rs +++ /dev/null @@ -1,186 +0,0 @@ -#![allow(warnings)] -use alloc::alloc::alloc; -use alloc::sync::Arc; -use core::alloc::GlobalAlloc; -use core::alloc::Layout; -use core::arch::asm; -use core::cell::RefCell; -use core::cell::UnsafeCell; -use core::fmt::Write; -use core::ops::Range; -use core::sync::atomic::AtomicBool; -use core::sync::atomic::AtomicI32; -use core::sync::atomic::Ordering; - -use ::alloc::boxed::Box; -use ::alloc::vec::Vec; -use context::VpExecutor; -use hvdef::hypercall::HvInputVtl; -use hvdef::HvAllArchRegisterName; -use hvdef::HvRegisterVsmVpStatus; -use hvdef::HvX64RegisterName; -use hvdef::Vtl; -use hypvctx::HvTestCtx; -use sync_nostd::Channel; -use sync_nostd::Receiver; -use sync_nostd::Sender; -use uefi::entry; -use uefi::Status; - -use crate::context; -use crate::context::InterruptPlatformTrait; -use crate::context::SecureInterceptPlatformTrait; -use crate::context::VirtualProcessorPlatformTrait; -use crate::context::VtlPlatformTrait; -use crate::platform::hypvctx; -use crate::tmk_assert; -use crate::tmk_logger; -use crate::tmkdefs::TmkResult; - -static mut HEAPX: RefCell<*mut u8> = RefCell::new(0 as *mut u8); -static mut RETURN_VALUE: u8 = 0; -#[inline(never)] -fn violate_heap() { - unsafe { - let heapx = *HEAPX.borrow(); - RETURN_VALUE = *(heapx.add(10)); - } -} - -#[inline(never)] -fn backup_and_restore() { - use core::arch::asm; - unsafe { - asm!(" - push rax - push rbx - push rcx - push rdx - push rsi - push rdi - push rbp - push r8 - push r9 - push r10 - push r11 - push r12 - push r13 - push r14 - push r15 - call {} - pop r15 - pop r14 - pop r13 - pop r12 - pop r11 - pop r10 - pop r9 - pop r8 - pop rbp - pop rdi - pop rsi - pop rdx - pop rcx - pop rbx - pop rax - ", sym violate_heap); - } -} - -pub fn exec(ctx: &mut T) -where - T: InterruptPlatformTrait + VtlPlatformTrait + VirtualProcessorPlatformTrait, -{ - log::info!("ctx ptr: {:p}", &ctx as *const _); - - let vp_count = ctx.get_vp_count(); - tmk_assert!(vp_count.is_ok(), "get_vp_count should succeed"); - let vp_count = vp_count.unwrap(); - tmk_assert!(vp_count == 4, "vp count should be 8"); - - ctx.setup_interrupt_handler(); - log::info!("successfully setup interrupt handler"); - - ctx.setup_partition_vtl(Vtl::Vtl1); - log::info!("successfully setup partition vtl1"); - - ctx.start_on_vp(VpExecutor::new(0, Vtl::Vtl1).command(move |ctx: &mut T| { - log::info!("successfully started running VTL1 on vp0."); - - let layout = - Layout::from_size_align(1024 * 1024, 4096).expect("msg: failed to create layout"); - let ptr = unsafe { alloc(layout) }; - - log::info!("allocated some memory in the heap from vtl1"); - - unsafe { - let mut z = HEAPX.borrow_mut(); - *z = ptr; - *ptr.add(10) = 0xA2; - } - - let size = layout.size(); - - ctx.setup_vtl_protection(); - - log::info!("enabled vtl protections for the partition."); - - let range = Range { - start: ptr as u64, - end: ptr as u64 + size as u64, - }; - - let result = ctx.apply_vtl_protection_for_memory(range, Vtl::Vtl1); - tmk_assert!( - result.is_ok(), - "apply_vtl_protection_for_memory should succeed" - ); - - log::info!("moving to vtl0 to attempt to read the heap memory"); - - ctx.switch_to_low_vtl(); - })); - - log::info!("BACK to vtl0"); - ctx.set_interrupt_idx(18, || { - tmk_assert!(true, "we reached to MC handler"); - panic!("MC causes the test to end"); - }); - - let (tx, rx) = Channel::new().split(); - - ctx.start_on_vp(VpExecutor::new(0x2, Vtl::Vtl1).command(move |ctx: &mut T| { - ctx.setup_interrupt_handler(); - log::info!("successfully started running VTL1 on vp2."); - })); - - ctx.start_on_vp( - VpExecutor::new(0x2, Vtl::Vtl0).command(move |ctx: &mut T| unsafe { - log::info!("successfully started running VTL0 on vp2."); - unsafe { - let heapx = *HEAPX.borrow(); - - let read_protected_memory = || *(heapx.add(10)); - - let read_result = read_protected_memory(); - log::info!( - "reading mutated heap memory from vtl0(it should not be 0xA2): 0x{:x}", - read_result - ); - tmk_assert!( - read_result != 0xA2, - "heap memory should not be accessible from vtl0" - ); - } - - tx.send(()); - }), - ); - - rx.recv(); - - tmk_assert!( - false, - "we should not reach here injecting MC should terminate the test" - ); -} diff --git a/opentmk/opentmk/src/tests/hv_misc.rs b/opentmk/opentmk/src/tests/hv_misc.rs deleted file mode 100644 index bea9f22ad7..0000000000 --- a/opentmk/opentmk/src/tests/hv_misc.rs +++ /dev/null @@ -1,266 +0,0 @@ -#![allow(warnings)] -use alloc::alloc::alloc; -use alloc::string::String; -use alloc::sync::Arc; -use core::alloc::GlobalAlloc; -use core::alloc::Layout; -use core::arch::asm; -use core::cell::RefCell; -use core::cell::UnsafeCell; -use core::fmt::Write; -use core::ops::Range; -use core::sync::atomic::AtomicBool; -use core::sync::atomic::AtomicI32; -use core::sync::atomic::Ordering; - -use ::alloc::boxed::Box; -use ::alloc::vec::Vec; -use context::VpExecutor; -use hvdef::hypercall::HvInputVtl; -use hvdef::HvAllArchRegisterName; -use hvdef::HvRegisterVsmVpStatus; -use hvdef::HvX64RegisterName; -use hvdef::Vtl; -use hypvctx::HvTestCtx; -use iced_x86::DecoderOptions; -use iced_x86::Formatter; -use iced_x86::NasmFormatter; -use sync_nostd::Channel; -use sync_nostd::Receiver; -use sync_nostd::Sender; -use uefi::entry; -use uefi::Status; - -use crate::context; -use crate::context::InterruptPlatformTrait; -use crate::context::SecureInterceptPlatformTrait; -use crate::context::VirtualProcessorPlatformTrait; -use crate::context::VtlPlatformTrait; -use crate::platform::hypvctx; -use crate::tmk_assert; -use crate::tmk_logger; -use crate::tmkdefs::TmkResult; - -static mut HEAPX: RefCell<*mut u8> = RefCell::new(0 as *mut u8); - -static mut RETURN_VALUE: u8 = 0; -#[inline(never)] -fn violate_heap() { - unsafe { - let heapx = *HEAPX.borrow(); - RETURN_VALUE = *(heapx.add(10)); - } -} - -#[inline(never)] -fn backup_and_restore() { - unsafe { - asm!(" - push rax - push rbx - push rcx - push rdx - push rsi - push rdi - push rbp - push r8 - push r9 - push r10 - push r11 - push r12 - push r13 - push r14 - push r15 - call {} - pop r15 - pop r14 - pop r13 - pop r12 - pop r11 - pop r10 - pop r9 - pop r8 - pop rbp - pop rdi - pop rsi - pop rdx - pop rcx - pop rbx - pop rax - ", sym violate_heap); - } -} - -pub fn read_assembly_output(target: u64) -> usize { - unsafe { - let target_ptr = target as *const u8; - let code_bytes = core::slice::from_raw_parts(target_ptr, 0x100); - let mut decoder = iced_x86::Decoder::with_ip(64, code_bytes, target, DecoderOptions::NONE); - - let mut formatter = NasmFormatter::new(); - let mut output = String::new(); - let mut first_ip_len = 0; - let mut set = false; - while decoder.can_decode() { - let instr = decoder.decode(); - if !set { - first_ip_len = instr.len(); - set = true; - } - formatter.format(&instr, &mut output); - log::info!("{}:{}", instr.ip(), output); - output.clear(); - } - - first_ip_len - } -} - -pub fn exec(ctx: &mut T) -where - T: InterruptPlatformTrait - + SecureInterceptPlatformTrait - + VtlPlatformTrait - + VirtualProcessorPlatformTrait, -{ - log::info!("ctx ptr: {:p}", &ctx as *const _); - - let vp_count = ctx.get_vp_count(); - tmk_assert!(vp_count.is_ok(), "get_vp_count should succeed"); - let vp_count = vp_count.unwrap(); - tmk_assert!(vp_count == 4, "vp count should be 8"); - - ctx.setup_interrupt_handler(); - - log::info!("set intercept handler successfully!"); - - ctx.setup_partition_vtl(Vtl::Vtl1); - - ctx.start_on_vp(VpExecutor::new(0, Vtl::Vtl1).command(move |ctx: &mut T| { - log::info!("successfully started running VTL1 on vp0."); - ctx.setup_secure_intercept(0x30); - ctx.set_interrupt_idx(0x30, move || { - log::info!("interrupt fired!"); - let mut hv = HvTestCtx::new(); - // expected to get interrupt in VTL1. - // CVMs dont support hypercalls to get the current VTL from VTL1/0. - hv.init(Vtl::Vtl1); - log::info!( - "current vp from interrupt: {}", - hv.get_current_vp().unwrap() - ); - - let rip = hvdef::HvX64RegisterName::Rip.0; - - let reg = hv.get_vp_state_with_vtl(rip, Vtl::Vtl0); - tmk_assert!(reg.is_ok(), "get_vp_state_with_vtl should succeed"); - - let reg = reg.unwrap(); - log::info!("rip from vtl0: 0x{:x}", reg); - - log::info!("pring assembly for the current RIP:"); - let size = read_assembly_output(reg); - - let new_rip_value = reg + size as u64; - - log::info!("pring assembly for the updated RIP:"); - read_assembly_output(new_rip_value); - - let r = hv.set_vp_state_with_vtl(HvX64RegisterName::Rip.0, new_rip_value, Vtl::Vtl0); - tmk_assert!(r.is_ok(), "set_vp_state_with_vtl should succeed"); - - let reg = hv.get_vp_state_with_vtl(rip, Vtl::Vtl0); - tmk_assert!(reg.is_ok(), "get_vp_state_with_vtl should succeed"); - - let reg = reg.unwrap(); - log::info!("rip from vtl0 after modification: 0x{:x}", reg); - tmk_assert!(reg == new_rip_value, "rip should be modified"); - - log::info!("pring assembly for the updated RIP after fetch:"); - read_assembly_output(reg); - - log::info!("interrupt handled!"); - hv.print_rbp(); - }); - - let layout = - Layout::from_size_align(1024 * 1024, 4096).expect("msg: failed to create layout"); - let ptr = unsafe { alloc(layout) }; - log::info!("allocated some memory in the heap from vtl1"); - unsafe { - let mut z = HEAPX.borrow_mut(); - *z = ptr; - *ptr.add(10) = 0xA2; - } - - let size = layout.size(); - ctx.setup_vtl_protection(); - - log::info!("enabled vtl protections for the partition."); - - let range = Range { - start: ptr as u64, - end: ptr as u64 + size as u64, - }; - - ctx.apply_vtl_protection_for_memory(range, Vtl::Vtl1); - - log::info!("moving to vtl0 to attempt to read the heap memory"); - - ctx.switch_to_low_vtl(); - })); - - log::info!("ctx ptr: {:p}", &ctx as *const _); - - let mut l = 0u64; - unsafe { asm!("mov {}, rsp", out(reg) l) }; - log::info!("rsp: 0x{:x}", l); - - let (tx, rx) = Channel::new().split(); - - ctx.start_on_vp(VpExecutor::new(0x2, Vtl::Vtl1).command(move |ctx: &mut T| { - ctx.setup_interrupt_handler(); - ctx.setup_secure_intercept(0x30); - - log::info!("successfully started running VTL1 on vp2."); - })); - - ctx.start_on_vp( - VpExecutor::new(0x2, Vtl::Vtl0).command(move |ctx: &mut T| unsafe { - log::info!("successfully started running VTL0 on vp2."); - - ctx.queue_command_vp(VpExecutor::new(2, Vtl::Vtl1).command(move |ctx: &mut T| { - log::info!("after intercept successfully started running VTL1 on vp2."); - ctx.switch_to_low_vtl(); - })); - - backup_and_restore(); - log::info!( - "reading mutated heap memory from vtl0(it should not be 0xA2): 0x{:x}", - RETURN_VALUE - ); - tmk_assert!( - RETURN_VALUE != 0xA2, - "heap memory should not be accessible from vtl0" - ); - tx.send(()); - }), - ); - - rx.recv(); - // let (mut tx, mut rx) = Channel::new(1); - // { - // let mut tx = tx.clone(); - // ctx.start_on_vp(VpExecutor::new(2, Vtl::Vtl0).command( - // move |ctx: &mut dyn TestCtxTrait| { - // log::info!("Hello form vtl0 on vp2!"); - // tx.send(()); - // }, - // )); - // } - - // rx.recv(); - - log::info!("we are in vtl0 now!"); - log::info!("we reached the end of the test"); -} diff --git a/opentmk/opentmk/src/tests/hv_tpm.rs b/opentmk/opentmk/src/tests/hv_tpm.rs deleted file mode 100644 index a866cf41fa..0000000000 --- a/opentmk/opentmk/src/tests/hv_tpm.rs +++ /dev/null @@ -1,180 +0,0 @@ -use alloc::string::String; -use core::ops::Range; - -use hvdef::HvX64RegisterName; -use hvdef::Vtl; -use iced_x86::DecoderOptions; -use iced_x86::Formatter; -use iced_x86::NasmFormatter; - -use crate::arch::tpm::Tpm; -use crate::context::InterruptPlatformTrait; -use crate::context::SecureInterceptPlatformTrait; -use crate::context::VirtualProcessorPlatformTrait; -use crate::context::VpExecutor; -use crate::context::VtlPlatformTrait; -use crate::devices::tpm::TpmUtil; -use crate::platform::hypvctx::HvTestCtx; -use crate::tmk_assert; - -pub fn read_assembly_output(target: u64) -> usize { - unsafe { - let target_ptr = target as *const u8; - let code_bytes = core::slice::from_raw_parts(target_ptr, 0x100); - let mut decoder = iced_x86::Decoder::with_ip(64, code_bytes, target, DecoderOptions::NONE); - - let mut formatter = NasmFormatter::new(); - let mut output = String::new(); - let mut first_ip_len = 0; - let mut set = false; - while decoder.can_decode() { - let instr = decoder.decode(); - if !set { - first_ip_len = instr.len(); - set = true; - } - formatter.format(&instr, &mut output); - log::info!("{}:{}", instr.ip(), output); - output.clear(); - } - - first_ip_len - } -} - -pub fn exec(ctx: &mut T) -where - T: InterruptPlatformTrait - + SecureInterceptPlatformTrait - + VtlPlatformTrait - + VirtualProcessorPlatformTrait, -{ - let mut _tpm = Tpm::new(); - let protocol_version = Tpm::get_tcg_protocol_version(); - log::warn!("TPM protocol version: 0x{:x}", protocol_version); - // SAFETY: asuming that memory range is limited to 4GB (addressable by 32-bit) - // let tpm_layout = Layout::from_size_align(4096 * 2, 4096); - // tmk_assert!(tpm_layout.is_ok(), "TPM layout is allocated as expected"); - // let tpm_layout = tpm_layout.unwrap(); - // let tpm_ptr = unsafe { alloc(tpm_layout) }; - - // let tpm_gpa = tpm_ptr as u64; - // tmk_assert!( - // tpm_gpa >> 32 == 0, - // "TPM layout is allocated in the first 4GB" - // ); - - // let tpm_gpa = tpm_gpa as u32; - - // let set_tpm_gpa = Tpm::map_shared_memory(tpm_gpa); - // tmk_assert!( - // set_tpm_gpa == tpm_gpa, - // format!( - // "TPM layout is mapped as expected, tpm_gpa: 0x{:x}, set_tpm_gpa: 0x{:x}", - // tpm_gpa, set_tpm_gpa - // ) - // ); - - let tpm_gpa = Tpm::get_mapped_shared_memory(); - log::warn!("TPM CMD buffer from vTPM Device: 0x{:x}", tpm_gpa); - let tpm_ptr = (tpm_gpa as u64) as *mut u8; - - // build slice from pointer - let tpm_command = unsafe { core::slice::from_raw_parts_mut(tpm_ptr, 4096) }; - let tpm_response = unsafe { core::slice::from_raw_parts_mut(tpm_ptr.add(4096), 4096) }; - - _tpm.set_command_buffer(tpm_command); - _tpm.set_response_buffer(tpm_response); - - let result = _tpm.self_test(); - - log::warn!("TPM self test result: {:?}", result); - tmk_assert!(result.is_ok(), "TPM self test is successful"); - - let vp_count = ctx.get_vp_count(); - tmk_assert!(vp_count.is_ok(), "get_vp_count should succeed"); - let vp_count = vp_count.unwrap(); - tmk_assert!(vp_count == 4, "vp count should be 8"); - let r = ctx.setup_interrupt_handler(); - tmk_assert!(r.is_ok(), "setup_interrupt_handler should succeed"); - log::info!("set intercept handler successfully!"); - let r = ctx.setup_partition_vtl(Vtl::Vtl1); - tmk_assert!(r.is_ok(), "setup_partition_vtl should succeed"); - - let response_rage = Range { - start: tpm_gpa as u64 + 4096, - end: tpm_gpa as u64 + 4096 * 2, - }; - - let _r = ctx.start_on_vp(VpExecutor::new(0, Vtl::Vtl1).command(move |ctx: &mut T| { - log::info!("successfully started running VTL1 on vp0."); - let r = ctx.setup_secure_intercept(0x30); - tmk_assert!(r.is_ok(), "setup_secure_intercept should succeed"); - - let r = ctx.set_interrupt_idx(0x30, move || { - log::info!("interrupt fired!"); - let mut hv = HvTestCtx::new(); - // expected to get interrupt in VTL1. - // CVMs dont support hypercalls to get the current VTL from VTL1/0. - _ = hv.init(Vtl::Vtl1); - log::info!( - "current vp from interrupt: {}", - hv.get_current_vp().unwrap() - ); - - let rip = HvX64RegisterName::Rip.0; - - let reg = hv.get_vp_state_with_vtl(rip, Vtl::Vtl0); - tmk_assert!(reg.is_ok(), "get_vp_state_with_vtl should succeed"); - - let reg = reg.unwrap(); - log::info!("rip from vtl0: 0x{:x}", reg); - - log::info!("pring assembly for the current RIP:"); - let size = read_assembly_output(reg); - - let new_rip_value = reg + size as u64; - - log::info!("pring assembly for the updated RIP:"); - read_assembly_output(new_rip_value); - - let r = hv.set_vp_state_with_vtl(HvX64RegisterName::Rip.0, new_rip_value, Vtl::Vtl0); - tmk_assert!(r.is_ok(), "set_vp_state_with_vtl should succeed"); - - let reg = hv.get_vp_state_with_vtl(rip, Vtl::Vtl0); - tmk_assert!(reg.is_ok(), "get_vp_state_with_vtl should succeed"); - - let reg = reg.unwrap(); - log::info!("rip from vtl0 after modification: 0x{:x}", reg); - tmk_assert!(reg == new_rip_value, "rip should be modified"); - - log::info!("pring assembly for the updated RIP after fetch:"); - read_assembly_output(reg); - - log::info!("interrupt handled!"); - hv.print_rbp(); - }); - tmk_assert!(r.is_ok(), "set_interrupt_idx should succeed"); - - let r = ctx.setup_vtl_protection(); - tmk_assert!(r.is_ok(), "setup_vtl_protection should succeed"); - - log::info!("enabled vtl protections for the partition."); - - let r = ctx.apply_vtl_protection_for_memory(response_rage, Vtl::Vtl1); - tmk_assert!(r.is_ok(), "apply_vtl_protection_for_memory should succeed"); - - log::info!("moving to vtl0 to attempt to read the heap memory"); - - ctx.switch_to_low_vtl(); - })); - - let cmd = TpmUtil::get_self_test_cmd(); - _tpm.copy_to_command_buffer(&cmd); - log::warn!("TPM self test command copied to buffer"); - log::warn!("about to execute TPM self test command.."); - Tpm::execute_command(); - log::warn!("TPM self test command executed"); - - loop {} -} diff --git a/opentmk/opentmk/rustfmt.toml b/opentmk/rustfmt.toml similarity index 100% rename from opentmk/opentmk/rustfmt.toml rename to opentmk/rustfmt.toml diff --git a/opentmk/opentmk/src/arch/aarch64/hypercall.rs b/opentmk/src/arch/aarch64/hypercall.rs similarity index 98% rename from opentmk/opentmk/src/arch/aarch64/hypercall.rs rename to opentmk/src/arch/aarch64/hypercall.rs index 35011e089a..9d48029c21 100644 --- a/opentmk/opentmk/src/arch/aarch64/hypercall.rs +++ b/opentmk/src/arch/aarch64/hypercall.rs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -/// Writes a synthehtic register to tell the hypervisor the OS ID for the boot shim. +/// Writes a synthehtic register to tell the hypervisor the OS ID. fn report_os_id(guest_os_id: u64) { // On ARM64, to be able to make hypercalls, one needs first to set the Guest OS ID // synthetic register using a hypercall. Can't use `Hvcall::set_register` at that will diff --git a/opentmk/opentmk/src/arch/aarch64/mod.rs b/opentmk/src/arch/aarch64/mod.rs similarity index 100% rename from opentmk/opentmk/src/arch/aarch64/mod.rs rename to opentmk/src/arch/aarch64/mod.rs diff --git a/opentmk/opentmk/src/arch/mod.rs b/opentmk/src/arch/mod.rs similarity index 100% rename from opentmk/opentmk/src/arch/mod.rs rename to opentmk/src/arch/mod.rs diff --git a/opentmk/opentmk/src/arch/x86_64/hypercall.rs b/opentmk/src/arch/x86_64/hypercall.rs similarity index 95% rename from opentmk/opentmk/src/arch/x86_64/hypercall.rs rename to opentmk/src/arch/x86_64/hypercall.rs index 3fee1370cf..5f625423df 100644 --- a/opentmk/opentmk/src/arch/x86_64/hypercall.rs +++ b/opentmk/src/arch/x86_64/hypercall.rs @@ -10,7 +10,7 @@ use minimal_rt::arch::hypercall::HYPERCALL_PAGE; use minimal_rt::arch::msr::read_msr; use minimal_rt::arch::msr::write_msr; -/// Writes an MSR to tell the hypervisor the OS ID for the boot shim. +/// Writes an MSR to tell the hypervisor the OS ID. fn report_os_id(guest_os_id: u64) { // SAFETY: Using the contract established in the Hyper-V TLFS. unsafe { diff --git a/opentmk/opentmk/src/arch/x86_64/interrupt.rs b/opentmk/src/arch/x86_64/interrupt.rs similarity index 98% rename from opentmk/opentmk/src/arch/x86_64/interrupt.rs rename to opentmk/src/arch/x86_64/interrupt.rs index 800c13ad5d..b7db1e0af3 100644 --- a/opentmk/opentmk/src/arch/x86_64/interrupt.rs +++ b/opentmk/src/arch/x86_64/interrupt.rs @@ -1,5 +1,5 @@ use lazy_static::lazy_static; -use sync_nostd::Mutex; +use spin::Mutex; use x86_64::structures::idt::InterruptDescriptorTable; use x86_64::structures::idt::InterruptStackFrame; diff --git a/opentmk/opentmk/src/arch/x86_64/interrupt_handler_register.rs b/opentmk/src/arch/x86_64/interrupt_handler_register.rs similarity index 99% rename from opentmk/opentmk/src/arch/x86_64/interrupt_handler_register.rs rename to opentmk/src/arch/x86_64/interrupt_handler_register.rs index 6f37c3f6d6..eb760e758e 100644 --- a/opentmk/opentmk/src/arch/x86_64/interrupt_handler_register.rs +++ b/opentmk/src/arch/x86_64/interrupt_handler_register.rs @@ -1,5 +1,5 @@ -#![allow(dead_code)] -use sync_nostd::Mutex; +#![expect(dead_code)] +use spin::Mutex; use x86_64::structures::idt::InterruptDescriptorTable; use x86_64::structures::idt::InterruptStackFrame; use x86_64::structures::idt::PageFaultErrorCode; diff --git a/opentmk/opentmk/src/arch/x86_64/io.rs b/opentmk/src/arch/x86_64/io.rs similarity index 100% rename from opentmk/opentmk/src/arch/x86_64/io.rs rename to opentmk/src/arch/x86_64/io.rs diff --git a/opentmk/opentmk/src/arch/x86_64/mod.rs b/opentmk/src/arch/x86_64/mod.rs similarity index 100% rename from opentmk/opentmk/src/arch/x86_64/mod.rs rename to opentmk/src/arch/x86_64/mod.rs diff --git a/opentmk/opentmk/src/arch/x86_64/rtc.rs b/opentmk/src/arch/x86_64/rtc.rs similarity index 73% rename from opentmk/opentmk/src/arch/x86_64/rtc.rs rename to opentmk/src/arch/x86_64/rtc.rs index afdcaaa9ee..5241f6daac 100644 --- a/opentmk/opentmk/src/arch/x86_64/rtc.rs +++ b/opentmk/src/arch/x86_64/rtc.rs @@ -30,13 +30,13 @@ impl core::fmt::Display for DateTime { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!( f, - "{:02}:{:02}:{:02} {:02}-{:02}-{:04} UTC", + "{:04}-{:02}-{:02}T{:02}:{:02}:{:02}Z", + 2000 + self.year as u64, + self.month, + self.day, self.hours, self.minutes, - self.seconds, - self.day, - self.month, - 2000 + self.year as u64 + self.seconds ) } } @@ -44,9 +44,36 @@ impl core::fmt::Display for DateTime { // convert datetime to Unix epoch impl DateTime { pub fn to_unix_epoch_sec(&self) -> u64 { - let mut days = self.day as u64; - days += (self.month as u64 - 1) * 30; // Approximation, not accurate for all months - days += (self.year as u64 + 2000 - 1970) * 365; // Approximation, not accounting for leap years + // Check if a year is a leap year + let is_leap_year = |year: u64| -> bool { + (year % 4 == 0 && year % 100 != 0) || year % 400 == 0 + }; + + // Define days in each month (0-indexed array) + let days_in_month = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; + + // Calculate days since Unix epoch (1970-01-01) + let year = 2000 + self.year as u64; + let month = self.month as u64; + let day = self.day as u64; + + // Days from years + let mut days = 0u64; + for y in 1970..year { + days += 365 + if is_leap_year(y) { 1 } else { 0 }; + } + + // Add days from months in current year + for m in 1..month { + days += days_in_month[m as usize - 1] as u64; + // Add leap day if February and leap year + if m == 2 && is_leap_year(year) { + days += 1; + } + } + + // Add days of current month + days += day - 1; // -1 because we want elapsed days let hours = self.hours as u64; let minutes = self.minutes as u64; let seconds = self.seconds as u64; diff --git a/opentmk/opentmk/src/arch/x86_64/serial.rs b/opentmk/src/arch/x86_64/serial.rs similarity index 99% rename from opentmk/opentmk/src/arch/x86_64/serial.rs rename to opentmk/src/arch/x86_64/serial.rs index 2124c7b673..f388525652 100644 --- a/opentmk/opentmk/src/arch/x86_64/serial.rs +++ b/opentmk/src/arch/x86_64/serial.rs @@ -5,7 +5,7 @@ use core::fmt; -use sync_nostd::Mutex; +use spin::Mutex; use super::io; diff --git a/opentmk/opentmk/src/arch/x86_64/tpm.rs b/opentmk/src/arch/x86_64/tpm.rs similarity index 100% rename from opentmk/opentmk/src/arch/x86_64/tpm.rs rename to opentmk/src/arch/x86_64/tpm.rs diff --git a/opentmk/src/context.rs b/opentmk/src/context.rs new file mode 100644 index 0000000000..e00d7b1189 --- /dev/null +++ b/opentmk/src/context.rs @@ -0,0 +1,131 @@ +use alloc::boxed::Box; +use core::ops::Range; + +use hvdef::Vtl; + +use crate::tmkdefs::TmkResult; + +pub trait SecureInterceptPlatformTrait { + /// Installs a secure-world intercept for the given interrupt. + /// + /// The platform must arrange that the supplied `interrupt_idx` + /// triggers a VM-exit or any other mechanism that transfers control + /// to the TMK secure handler. + /// + /// Returns `Ok(())` on success or an error wrapped in `TmkResult`. + fn setup_secure_intercept(&mut self, interrupt_idx: u8) -> TmkResult<()>; +} + +pub trait InterruptPlatformTrait { + /// Associates an interrupt vector with a handler inside the + /// non-secure world. + /// + /// * `interrupt_idx` – IDT/GIC index to program + /// * `handler` – Function that will be executed when the interrupt + /// fires. + fn set_interrupt_idx(&mut self, interrupt_idx: u8, handler: fn()) -> TmkResult<()>; + + /// Finalises platform specific interrupt setup (enables the table, + /// unmasks lines, etc.). + fn setup_interrupt_handler(&mut self) -> TmkResult<()>; +} + +pub trait MsrPlatformTrait { + /// Reads the content of `msr`. + /// + /// Returns the 64-bit value currently stored in that MSR. + fn read_msr(&mut self, msr: u32) -> TmkResult; + + /// Writes `value` into `msr`. + fn write_msr(&mut self, msr: u32, value: u64) -> TmkResult<()>; +} + +pub trait VirtualProcessorPlatformTrait +where + T: VtlPlatformTrait, +{ + /// Returns the index of the virtual CPU currently executing this + /// code. + fn get_current_vp(&self) -> TmkResult; + + /// Reads the architecture specific register identified by `reg`. + fn get_register(&mut self, reg: u32) -> TmkResult; + + /// Total number of online VPs in the partition. + fn get_vp_count(&self) -> TmkResult; + + /// Queues `cmd` to run later on the VP described inside the + /// `VpExecutor`. + fn queue_command_vp(&mut self, cmd: VpExecutor) -> TmkResult<()>; + + /// Synchronously executes `cmd` on its target VP. + fn start_on_vp(&mut self, cmd: VpExecutor) -> TmkResult<()>; + + /// Starts the target VP (if required) and executes `cmd` with a + /// platform provided default VTL context. + fn start_running_vp_with_default_context(&mut self, cmd: VpExecutor) -> TmkResult<()>; +} + +pub trait VtlPlatformTrait { + /// Applies VTL protection to the supplied physical address range. + fn apply_vtl_protection_for_memory(&mut self, range: Range, vtl: Vtl) -> TmkResult<()>; + + /// Enables the given `vtl` on `vp_index` with a default context. + fn enable_vp_vtl_with_default_context(&mut self, vp_index: u32, vtl: Vtl) -> TmkResult<()>; + + /// Returns the VTL level the caller is currently executing in. + fn get_current_vtl(&self) -> TmkResult; + + /// Sets the default VTL context on `vp_index`. + fn set_default_ctx_to_vp(&mut self, vp_index: u32, vtl: Vtl) -> TmkResult<()>; + + /// Performs partition wide initialisation for a given `vtl`. + fn setup_partition_vtl(&mut self, vtl: Vtl) -> TmkResult<()>; + + /// Platform specific global VTL preparation (stage 2 translation, + /// EPT, etc.). + fn setup_vtl_protection(&mut self) -> TmkResult<()>; + + /// Switches the current hardware thread to the higher privileged VTL. + fn switch_to_high_vtl(&mut self); + + /// Switches the current hardware thread back to the lower privileged VTL. + fn switch_to_low_vtl(&mut self); + + fn set_vp_state_with_vtl(&mut self, register_index: u32, value: u64, vtl: Vtl) + -> TmkResult<()>; + + fn get_vp_state_with_vtl(&mut self, register_index: u32, vtl: Vtl) -> TmkResult; +} + +pub struct VpExecutor { + vp_index: u32, + vtl: Vtl, + cmd: Option>, +} + +impl VpExecutor { + /// Creates a new executor targeting `vp_index` running in `vtl`. + pub fn new(vp_index: u32, vtl: Vtl) -> Self { + VpExecutor { + vp_index, + vtl, + cmd: None, + } + } + + /// Stores a closure `cmd` that will be executed on the target VP. + /// + /// The closure receives a mutable reference to the platform-specific + /// type `T` that implements `VtlPlatformTrait`. + pub fn command(mut self, cmd: impl FnOnce(&mut T) + 'static + Send) -> Self { + self.cmd = Some(Box::new(cmd)); + self + } + + /// Extracts the tuple `(vp_index, vtl, cmd)` consuming `self`. + pub fn get(mut self) -> (u32, Vtl, Option>) { + let cmd = self.cmd.take(); + (self.vp_index, self.vtl, cmd) + } +} diff --git a/opentmk/opentmk/src/devices/mod.rs b/opentmk/src/devices/mod.rs similarity index 100% rename from opentmk/opentmk/src/devices/mod.rs rename to opentmk/src/devices/mod.rs diff --git a/opentmk/opentmk/src/devices/tpm/mod.rs b/opentmk/src/devices/tpm/mod.rs similarity index 100% rename from opentmk/opentmk/src/devices/tpm/mod.rs rename to opentmk/src/devices/tpm/mod.rs diff --git a/opentmk/opentmk/src/devices/tpm/protocol.rs b/opentmk/src/devices/tpm/protocol.rs similarity index 99% rename from opentmk/opentmk/src/devices/tpm/protocol.rs rename to opentmk/src/devices/tpm/protocol.rs index 90e72adb54..a73bf89ceb 100644 --- a/opentmk/opentmk/src/devices/tpm/protocol.rs +++ b/opentmk/src/devices/tpm/protocol.rs @@ -18,7 +18,7 @@ use zerocopy::KnownLayout; use self::packed_nums::*; -#[allow(non_camel_case_types)] +#[expect(non_camel_case_types)] mod packed_nums { pub type u16_be = zerocopy::U16; pub type u32_be = zerocopy::U32; @@ -240,7 +240,7 @@ impl CommandCode { } } -#[allow(non_camel_case_types, clippy::upper_case_acronyms)] +#[expect(non_camel_case_types, clippy::upper_case_acronyms)] #[derive(Debug, Clone, Copy, PartialEq)] #[repr(u32)] pub enum CommandCodeEnum { @@ -618,7 +618,7 @@ impl AlgId { } } -#[allow(non_camel_case_types, clippy::upper_case_acronyms)] +#[expect(clippy::upper_case_acronyms)] #[derive(Debug)] #[repr(u16)] pub enum AlgIdEnum { diff --git a/opentmk/opentmk/src/hypercall.rs b/opentmk/src/hypercall.rs similarity index 97% rename from opentmk/opentmk/src/hypercall.rs rename to opentmk/src/hypercall.rs index c5ff0f471f..152611e6d5 100644 --- a/opentmk/opentmk/src/hypercall.rs +++ b/opentmk/src/hypercall.rs @@ -3,7 +3,7 @@ //! Hypercall infrastructure. -#![allow(dead_code)] +#![expect(dead_code)] use core::arch::asm; use core::mem::size_of; use core::sync::atomic::AtomicU16; @@ -32,7 +32,7 @@ struct HvcallPage { } #[inline(never)] -pub fn invoke_hypercall_vtl(control: hvdef::hypercall::Control) { +pub unsafe fn invoke_hypercall_vtl(control: hvdef::hypercall::Control) { // SAFETY: the caller guarantees the safety of this operation. unsafe { core::arch::asm! { @@ -63,7 +63,7 @@ impl HvcallPage { } } -/// Provides mechanisms to invoke hypercalls within the boot shim. +/// Provides mechanisms to invoke hypercalls. /// /// This module defines the `HvCall` struct and associated methods to interact with /// hypervisor functionalities through hypercalls. It includes utilities for managing @@ -116,10 +116,10 @@ impl HvcallPage { /// /// - This module assumes the presence of a hypervisor that supports the required /// hypercalls. -/// - The boot shim must ensure that hypercalls are invoked in a valid context. +/// - The caller must ensure that hypercalls are invoked in a valid context. /// Internally uses static buffers for the hypercall page, the input /// page, and the output page, so this should not be used in any -/// multi-threaded capacity (which the boot shim currently is not). +/// multi-threaded capacity (which the caller currently is not). pub struct HvCall { input_page: HvcallPage, output_page: HvcallPage, @@ -131,7 +131,7 @@ static HV_PAGE_INIT_STATUS: AtomicU16 = AtomicU16::new(0); impl HvCall { /// Hypercall to accept vtl2 pages from address start to end with VTL 2 /// protections and no host visibility - #[cfg_attr(target_arch = "aarch64", allow(dead_code))] + #[cfg_attr(target_arch = "aarch64", expect(dead_code))] pub fn accept_vtl2_pages( &mut self, range: MemoryRange, @@ -173,7 +173,7 @@ impl HvCall { } /// Hypercall to apply vtl protections to the pages from address start to end - #[cfg_attr(target_arch = "aarch64", allow(dead_code))] + #[cfg_attr(target_arch = "aarch64", expect(dead_code))] pub fn apply_vtl2_protections(&mut self, range: MemoryRange) -> Result<(), hvdef::HvError> { const HEADER_SIZE: usize = size_of::(); const MAX_INPUT_ELEMENTS: usize = (HV_PAGE_SIZE as usize - HEADER_SIZE) / size_of::(); @@ -212,8 +212,8 @@ impl HvCall { Ok(()) } - /// Hypercall to apply vtl protections to the pages from address start to end - #[cfg_attr(target_arch = "x86_64", allow(dead_code))] + /// Hypercall to apply vtl protections (NO ACCESS) to the pages from address start to end + #[cfg(target_arch = "x86_64")] pub fn apply_vtl_protections( &mut self, range: MemoryRange, @@ -888,7 +888,8 @@ impl HvCall { let control: hvdef::hypercall::Control = hvdef::hypercall::Control::new() .with_code(hvdef::HypercallCode::HvCallVtlCall.0) .with_rep_count(0); - invoke_hypercall_vtl(control); + // SAFETY: This is safe because we are calling a hypercall with a valid control structure. + unsafe { invoke_hypercall_vtl(control) }; } #[inline(never)] @@ -897,7 +898,8 @@ impl HvCall { let control: hvdef::hypercall::Control = hvdef::hypercall::Control::new() .with_code(hvdef::HypercallCode::HvCallVtlReturn.0) .with_rep_count(0); - invoke_hypercall_vtl(control); + // SAFETY: This is safe because we are calling a hypercall with a valid control structure. + unsafe { invoke_hypercall_vtl(control) }; } } diff --git a/opentmk/opentmk/src/main.rs b/opentmk/src/main.rs similarity index 96% rename from opentmk/opentmk/src/main.rs rename to opentmk/src/main.rs index 0a0f6f0e9a..1877d09057 100644 --- a/opentmk/opentmk/src/main.rs +++ b/opentmk/src/main.rs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #![no_std] -#![allow(unsafe_code)] +#![expect(unsafe_code)] #![feature(abi_x86_interrupt)] #![doc = include_str!("../README.md")] #![cfg_attr(all(not(test), target_os = "uefi"), no_main)] diff --git a/opentmk/opentmk/src/platform/hypvctx.rs b/opentmk/src/platform/hypvctx.rs similarity index 80% rename from opentmk/opentmk/src/platform/hypvctx.rs rename to opentmk/src/platform/hypvctx.rs index 0c1114c957..c2b6c65239 100644 --- a/opentmk/opentmk/src/platform/hypvctx.rs +++ b/opentmk/src/platform/hypvctx.rs @@ -15,7 +15,7 @@ use hvdef::Vtl; use memory_range::MemoryRange; use minimal_rt::arch::msr::read_msr; use minimal_rt::arch::msr::write_msr; -use sync_nostd::Mutex; +use spin::Mutex; use crate::context::InterruptPlatformTrait; use crate::context::MsrPlatformTrait; @@ -25,7 +25,6 @@ use crate::context::VpExecutor; use crate::context::VtlPlatformTrait; use crate::hypercall::HvCall; use crate::tmkdefs::TmkError; -use crate::tmkdefs::TmkErrorType; use crate::tmkdefs::TmkResult; const ALIGNMENT: usize = 4096; @@ -33,7 +32,7 @@ const ALIGNMENT: usize = 4096; type ComandTable = BTreeMap, Vtl)>>; static mut CMD: Mutex = Mutex::new(BTreeMap::new()); -#[allow(static_mut_refs)] +#[expect(static_mut_refs)] fn cmdt() -> &'static Mutex { unsafe { &CMD } } @@ -73,16 +72,16 @@ impl SecureInterceptPlatformTrait for HvTestCtx { /// Returns [`TmkResult::Err`] if the allocation of the SIMP buffer fails. fn setup_secure_intercept(&mut self, interrupt_idx: u8) -> TmkResult<()> { let layout = Layout::from_size_align(4096, ALIGNMENT) - .map_err(|_| TmkError(TmkErrorType::AllocationFailed))?; + .map_err(|_| TmkError::AllocationFailed)?; let ptr = unsafe { alloc(layout) }; let gpn = (ptr as u64) >> 12; let reg = (gpn << 12) | 0x1; - unsafe { write_msr(hvdef::HV_X64_MSR_SIMP, reg) }; + self.write_msr(hvdef::HV_X64_MSR_SIMP, reg)?; log::info!("Successfuly set the SIMP register."); - let reg = unsafe { read_msr(hvdef::HV_X64_MSR_SINT0) }; + let reg = self.read_msr(hvdef::HV_X64_MSR_SINT0)?; let mut reg: hvdef::HvSynicSint = reg.into(); reg.set_vector(interrupt_idx); reg.set_masked(false); @@ -97,7 +96,7 @@ impl SecureInterceptPlatformTrait for HvTestCtx { impl InterruptPlatformTrait for HvTestCtx { /// Install an interrupt handler for the supplied vector on x86-64. /// For non-x86-64 targets the call returns - /// [`TmkErrorType::NotImplemented`]. + /// [`TmkError::NotImplemented`]. fn set_interrupt_idx(&mut self, interrupt_idx: u8, handler: fn()) -> TmkResult<()> { #[cfg(target_arch = "x86_64")] { @@ -107,7 +106,7 @@ impl InterruptPlatformTrait for HvTestCtx { #[cfg(not(target_arch = "x86_64"))] { - Err(TmkError(TmkErrorType::NotImplemented)) + Err(TmkError(TmkError::NotImplemented)) } } @@ -155,7 +154,7 @@ impl VirtualProcessorPlatformTrait for HvTestCtx { #[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))] { - Err(TmkError(TmkErrorType::NotImplemented)) + Err(TmkError(TmkError::NotImplemented)) } } @@ -169,7 +168,7 @@ impl VirtualProcessorPlatformTrait for HvTestCtx { #[cfg(not(target_arch = "x86_64"))] { - Err(TmkError(TmkErrorType::NotImplemented)) + Err(TmkError(TmkError::NotImplemented)) } } @@ -178,7 +177,7 @@ impl VirtualProcessorPlatformTrait for HvTestCtx { /// here – we simply enqueue. fn queue_command_vp(&mut self, cmd: VpExecutor) -> TmkResult<()> { let (vp_index, vtl, cmd) = cmd.get(); - let cmd = cmd.ok_or(TmkError(TmkErrorType::QueueCommandFailed))?; + let cmd = cmd.ok_or(TmkError::QueueCommandFailed)?; cmdt() .lock() .get_mut(&vp_index) @@ -199,9 +198,9 @@ impl VirtualProcessorPlatformTrait for HvTestCtx { /// spins in `exec_handler` waiting for work. fn start_on_vp(&mut self, cmd: VpExecutor) -> TmkResult<()> { let (vp_index, vtl, cmd) = cmd.get(); - let cmd = cmd.ok_or(TmkError(TmkErrorType::InvalidParameter))?; + let cmd = cmd.ok_or(TmkError::InvalidParameter)?; if vtl >= Vtl::Vtl2 { - return Err(TmkError(TmkErrorType::InvalidParameter)); + return Err(TmkError::InvalidParameter); } let is_vp_running = self.vp_runing.get(&vp_index); if let Some(_running_vtl) = is_vp_running { @@ -222,7 +221,7 @@ impl VirtualProcessorPlatformTrait for HvTestCtx { log::info!("self addr after switch: {:p}", self as *const _); self.vp_runing.insert(vp_index); } else { - let (tx, rx) = sync_nostd::Channel::>::new().split(); + let (tx, rx) = nostd_spin_channel::Channel::>::new().split(); let self_vp_idx = self.my_vp_idx; cmdt().lock().get_mut(&self_vp_idx).unwrap().push_back(( Box::new(move |ctx| { @@ -600,7 +599,7 @@ impl HvTestCtx { let handler = match vtl { Vtl::Vtl0 => HvTestCtx::general_exec_handler, Vtl::Vtl1 => HvTestCtx::secure_exec_handler, - _ => return Err(TmkErrorType::InvalidParameter.into()), + _ => return Err(TmkError::InvalidParameter.into()), }; self.run_fn_with_current_context(handler) } @@ -617,7 +616,7 @@ impl HvTestCtx { .expect("Failed to create layout for stack allocation"); let allocated_stack_ptr = unsafe { alloc(stack_layout) }; if allocated_stack_ptr.is_null() { - return Err(TmkErrorType::AllocationFailed.into()); + return Err(TmkError::AllocationFailed.into()); } let stack_size = stack_layout.size(); let stack_top = allocated_stack_ptr as u64 + stack_size as u64; @@ -670,87 +669,87 @@ impl From for TmkError { fn from(e: hvdef::HvError) -> Self { log::debug!("Converting hvdef::HvError::{:?} to TmkError", e); let tmk_error_type = match e { - hvdef::HvError::InvalidHypercallCode => TmkErrorType::InvalidHypercallCode, - hvdef::HvError::InvalidHypercallInput => TmkErrorType::InvalidHypercallInput, - hvdef::HvError::InvalidAlignment => TmkErrorType::InvalidAlignment, - hvdef::HvError::InvalidParameter => TmkErrorType::InvalidParameter, - hvdef::HvError::AccessDenied => TmkErrorType::AccessDenied, - hvdef::HvError::InvalidPartitionState => TmkErrorType::InvalidPartitionState, - hvdef::HvError::OperationDenied => TmkErrorType::OperationDenied, - hvdef::HvError::UnknownProperty => TmkErrorType::UnknownProperty, - hvdef::HvError::PropertyValueOutOfRange => TmkErrorType::PropertyValueOutOfRange, - hvdef::HvError::InsufficientMemory => TmkErrorType::InsufficientMemory, - hvdef::HvError::PartitionTooDeep => TmkErrorType::PartitionTooDeep, - hvdef::HvError::InvalidPartitionId => TmkErrorType::InvalidPartitionId, - hvdef::HvError::InvalidVpIndex => TmkErrorType::InvalidVpIndex, - hvdef::HvError::NotFound => TmkErrorType::NotFound, - hvdef::HvError::InvalidPortId => TmkErrorType::InvalidPortId, - hvdef::HvError::InvalidConnectionId => TmkErrorType::InvalidConnectionId, - hvdef::HvError::InsufficientBuffers => TmkErrorType::InsufficientBuffers, - hvdef::HvError::NotAcknowledged => TmkErrorType::NotAcknowledged, - hvdef::HvError::InvalidVpState => TmkErrorType::InvalidVpState, - hvdef::HvError::Acknowledged => TmkErrorType::Acknowledged, - hvdef::HvError::InvalidSaveRestoreState => TmkErrorType::InvalidSaveRestoreState, - hvdef::HvError::InvalidSynicState => TmkErrorType::InvalidSynicState, - hvdef::HvError::ObjectInUse => TmkErrorType::ObjectInUse, - hvdef::HvError::InvalidProximityDomainInfo => TmkErrorType::InvalidProximityDomainInfo, - hvdef::HvError::NoData => TmkErrorType::NoData, - hvdef::HvError::Inactive => TmkErrorType::Inactive, - hvdef::HvError::NoResources => TmkErrorType::NoResources, - hvdef::HvError::FeatureUnavailable => TmkErrorType::FeatureUnavailable, - hvdef::HvError::PartialPacket => TmkErrorType::PartialPacket, + hvdef::HvError::InvalidHypercallCode => TmkError::InvalidHypercallCode, + hvdef::HvError::InvalidHypercallInput => TmkError::InvalidHypercallInput, + hvdef::HvError::InvalidAlignment => TmkError::InvalidAlignment, + hvdef::HvError::InvalidParameter => TmkError::InvalidParameter, + hvdef::HvError::AccessDenied => TmkError::AccessDenied, + hvdef::HvError::InvalidPartitionState => TmkError::InvalidPartitionState, + hvdef::HvError::OperationDenied => TmkError::OperationDenied, + hvdef::HvError::UnknownProperty => TmkError::UnknownProperty, + hvdef::HvError::PropertyValueOutOfRange => TmkError::PropertyValueOutOfRange, + hvdef::HvError::InsufficientMemory => TmkError::InsufficientMemory, + hvdef::HvError::PartitionTooDeep => TmkError::PartitionTooDeep, + hvdef::HvError::InvalidPartitionId => TmkError::InvalidPartitionId, + hvdef::HvError::InvalidVpIndex => TmkError::InvalidVpIndex, + hvdef::HvError::NotFound => TmkError::NotFound, + hvdef::HvError::InvalidPortId => TmkError::InvalidPortId, + hvdef::HvError::InvalidConnectionId => TmkError::InvalidConnectionId, + hvdef::HvError::InsufficientBuffers => TmkError::InsufficientBuffers, + hvdef::HvError::NotAcknowledged => TmkError::NotAcknowledged, + hvdef::HvError::InvalidVpState => TmkError::InvalidVpState, + hvdef::HvError::Acknowledged => TmkError::Acknowledged, + hvdef::HvError::InvalidSaveRestoreState => TmkError::InvalidSaveRestoreState, + hvdef::HvError::InvalidSynicState => TmkError::InvalidSynicState, + hvdef::HvError::ObjectInUse => TmkError::ObjectInUse, + hvdef::HvError::InvalidProximityDomainInfo => TmkError::InvalidProximityDomainInfo, + hvdef::HvError::NoData => TmkError::NoData, + hvdef::HvError::Inactive => TmkError::Inactive, + hvdef::HvError::NoResources => TmkError::NoResources, + hvdef::HvError::FeatureUnavailable => TmkError::FeatureUnavailable, + hvdef::HvError::PartialPacket => TmkError::PartialPacket, hvdef::HvError::ProcessorFeatureNotSupported => { - TmkErrorType::ProcessorFeatureNotSupported + TmkError::ProcessorFeatureNotSupported } hvdef::HvError::ProcessorCacheLineFlushSizeIncompatible => { - TmkErrorType::ProcessorCacheLineFlushSizeIncompatible + TmkError::ProcessorCacheLineFlushSizeIncompatible } - hvdef::HvError::InsufficientBuffer => TmkErrorType::InsufficientBuffer, - hvdef::HvError::IncompatibleProcessor => TmkErrorType::IncompatibleProcessor, - hvdef::HvError::InsufficientDeviceDomains => TmkErrorType::InsufficientDeviceDomains, + hvdef::HvError::InsufficientBuffer => TmkError::InsufficientBuffer, + hvdef::HvError::IncompatibleProcessor => TmkError::IncompatibleProcessor, + hvdef::HvError::InsufficientDeviceDomains => TmkError::InsufficientDeviceDomains, hvdef::HvError::CpuidFeatureValidationError => { - TmkErrorType::CpuidFeatureValidationError + TmkError::CpuidFeatureValidationError } hvdef::HvError::CpuidXsaveFeatureValidationError => { - TmkErrorType::CpuidXsaveFeatureValidationError + TmkError::CpuidXsaveFeatureValidationError } - hvdef::HvError::ProcessorStartupTimeout => TmkErrorType::ProcessorStartupTimeout, - hvdef::HvError::SmxEnabled => TmkErrorType::SmxEnabled, - hvdef::HvError::InvalidLpIndex => TmkErrorType::InvalidLpIndex, - hvdef::HvError::InvalidRegisterValue => TmkErrorType::InvalidRegisterValue, - hvdef::HvError::InvalidVtlState => TmkErrorType::InvalidVtlState, - hvdef::HvError::NxNotDetected => TmkErrorType::NxNotDetected, - hvdef::HvError::InvalidDeviceId => TmkErrorType::InvalidDeviceId, - hvdef::HvError::InvalidDeviceState => TmkErrorType::InvalidDeviceState, - hvdef::HvError::PendingPageRequests => TmkErrorType::PendingPageRequests, - hvdef::HvError::PageRequestInvalid => TmkErrorType::PageRequestInvalid, - hvdef::HvError::KeyAlreadyExists => TmkErrorType::KeyAlreadyExists, - hvdef::HvError::DeviceAlreadyInDomain => TmkErrorType::DeviceAlreadyInDomain, - hvdef::HvError::InvalidCpuGroupId => TmkErrorType::InvalidCpuGroupId, - hvdef::HvError::InvalidCpuGroupState => TmkErrorType::InvalidCpuGroupState, - hvdef::HvError::OperationFailed => TmkErrorType::OperationFailed, + hvdef::HvError::ProcessorStartupTimeout => TmkError::ProcessorStartupTimeout, + hvdef::HvError::SmxEnabled => TmkError::SmxEnabled, + hvdef::HvError::InvalidLpIndex => TmkError::InvalidLpIndex, + hvdef::HvError::InvalidRegisterValue => TmkError::InvalidRegisterValue, + hvdef::HvError::InvalidVtlState => TmkError::InvalidVtlState, + hvdef::HvError::NxNotDetected => TmkError::NxNotDetected, + hvdef::HvError::InvalidDeviceId => TmkError::InvalidDeviceId, + hvdef::HvError::InvalidDeviceState => TmkError::InvalidDeviceState, + hvdef::HvError::PendingPageRequests => TmkError::PendingPageRequests, + hvdef::HvError::PageRequestInvalid => TmkError::PageRequestInvalid, + hvdef::HvError::KeyAlreadyExists => TmkError::KeyAlreadyExists, + hvdef::HvError::DeviceAlreadyInDomain => TmkError::DeviceAlreadyInDomain, + hvdef::HvError::InvalidCpuGroupId => TmkError::InvalidCpuGroupId, + hvdef::HvError::InvalidCpuGroupState => TmkError::InvalidCpuGroupState, + hvdef::HvError::OperationFailed => TmkError::OperationFailed, hvdef::HvError::NotAllowedWithNestedVirtActive => { - TmkErrorType::NotAllowedWithNestedVirtActive + TmkError::NotAllowedWithNestedVirtActive } - hvdef::HvError::InsufficientRootMemory => TmkErrorType::InsufficientRootMemory, - hvdef::HvError::EventBufferAlreadyFreed => TmkErrorType::EventBufferAlreadyFreed, - hvdef::HvError::Timeout => TmkErrorType::Timeout, - hvdef::HvError::VtlAlreadyEnabled => TmkErrorType::VtlAlreadyEnabled, - hvdef::HvError::UnknownRegisterName => TmkErrorType::UnknownRegisterName, + hvdef::HvError::InsufficientRootMemory => TmkError::InsufficientRootMemory, + hvdef::HvError::EventBufferAlreadyFreed => TmkError::EventBufferAlreadyFreed, + hvdef::HvError::Timeout => TmkError::Timeout, + hvdef::HvError::VtlAlreadyEnabled => TmkError::VtlAlreadyEnabled, + hvdef::HvError::UnknownRegisterName => TmkError::UnknownRegisterName, // Add any other specific mappings here if hvdef::HvError has more variants _ => { log::warn!( - "Unhandled hvdef::HvError variant: {:?}. Mapping to TmkErrorType::OperationFailed.", + "Unhandled hvdef::HvError variant: {:?}. Mapping to TmkError::OperationFailed.", e ); - TmkErrorType::OperationFailed // Generic fallback + TmkError::OperationFailed // Generic fallback } }; log::debug!( - "Mapped hvdef::HvError::{:?} to TmkErrorType::{:?}", + "Mapped hvdef::HvError::{:?} to TmkError::{:?}", e, tmk_error_type ); - TmkError(tmk_error_type) + tmk_error_type } } diff --git a/opentmk/opentmk/src/platform/mod.rs b/opentmk/src/platform/mod.rs similarity index 100% rename from opentmk/opentmk/src/platform/mod.rs rename to opentmk/src/platform/mod.rs diff --git a/opentmk/opentmk/src/tests/hv_error_vp_start.rs b/opentmk/src/tests/hyperv/hv_error_vp_start.rs similarity index 93% rename from opentmk/opentmk/src/tests/hv_error_vp_start.rs rename to opentmk/src/tests/hyperv/hv_error_vp_start.rs index fe98b49bfd..4d1e8da489 100644 --- a/opentmk/opentmk/src/tests/hv_error_vp_start.rs +++ b/opentmk/src/tests/hyperv/hv_error_vp_start.rs @@ -1,5 +1,5 @@ use hvdef::Vtl; -use sync_nostd::Channel; +use nostd_spin_channel::Channel; use crate::context::VirtualProcessorPlatformTrait; use crate::context::VpExecutor; @@ -43,7 +43,7 @@ where tmk_assert!(result.is_err(), "start_on_vp should fail"); tmk_assert!( - result.unwrap_err() == crate::tmkdefs::TmkErrorType::InvalidVtlState.into(), + result.unwrap_err() == crate::tmkdefs::TmkError::InvalidVtlState, "start_on_vp should fail with InvalidVtlState" ); log::info!("result on start_on_vp: {:?}", result); diff --git a/opentmk/src/tests/hyperv/hv_memory_protect_read.rs b/opentmk/src/tests/hyperv/hv_memory_protect_read.rs new file mode 100644 index 0000000000..fc6aac1419 --- /dev/null +++ b/opentmk/src/tests/hyperv/hv_memory_protect_read.rs @@ -0,0 +1,141 @@ +use alloc::alloc::alloc; +use core::alloc::Layout; +use core::arch::asm; +use core::cell::RefCell; +use core::ops::Range; + +use hvdef::Vtl; +use nostd_spin_channel::Channel; + +use crate::context::InterruptPlatformTrait; +use crate::context::SecureInterceptPlatformTrait; +use crate::context::VirtualProcessorPlatformTrait; +use crate::context::VpExecutor; +use crate::context::VtlPlatformTrait; +use crate::create_function_with_restore; +use crate::tmk_assert; + +static mut HEAPX: RefCell<*mut u8> = RefCell::new(0 as *mut u8); + +static mut RETURN_VALUE: u8 = 0; + +#[inline(never)] +#[expect(warnings)] +fn violate_heap() { + unsafe { + let heapx = *HEAPX.borrow(); + // after a VTL switch we can't trust the value returned by eax + RETURN_VALUE = *(heapx.add(10)); + } +} +create_function_with_restore!(f_violate_heap, violate_heap); + + +pub fn exec(ctx: &mut T) +where + T: InterruptPlatformTrait + + SecureInterceptPlatformTrait + + VtlPlatformTrait + + VirtualProcessorPlatformTrait, +{ + let vp_count = ctx.get_vp_count(); + tmk_assert!(vp_count.is_ok(), "get_vp_count should succeed"); + let vp_count = vp_count.unwrap(); + tmk_assert!(vp_count == 4, "vp count should be 8"); + + let r = ctx.setup_interrupt_handler(); + tmk_assert!(r.is_ok(), "setup_interrupt_handler should succeed"); + log::info!("set intercept handler successfully!"); + + let r = ctx.setup_partition_vtl(Vtl::Vtl1); + tmk_assert!(r.is_ok(), "setup_partition_vtl should succeed"); + + let r = ctx.start_on_vp(VpExecutor::new(0, Vtl::Vtl1).command(move |ctx: &mut T| { + log::info!("successfully started running VTL1 on vp0."); + let r = ctx.setup_secure_intercept(0x30); + tmk_assert!(r.is_ok(), "setup_secure_intercept should succeed"); + + let r = ctx.set_interrupt_idx(0x30, move || { + log::info!("interrupt handled for 0x30!"); + }); + tmk_assert!(r.is_ok(), "set_interrupt_idx should succeed"); + + let layout = + Layout::from_size_align(1024 * 1024, 4096).expect("msg: failed to create layout"); + let ptr = unsafe { alloc(layout) }; + log::info!("allocated some memory in the heap from vtl1"); + + #[expect(warnings)] + unsafe { + let mut z = HEAPX.borrow_mut(); + *z = ptr; + *ptr.add(10) = 0xA2; + } + + let size = layout.size(); + let r = ctx.setup_vtl_protection(); + tmk_assert!(r.is_ok(), "setup_vtl_protection should succeed"); + log::info!("enabled vtl protections for the partition."); + + let range = Range { + start: ptr as u64, + end: ptr as u64 + size as u64, + }; + + let r = ctx.apply_vtl_protection_for_memory(range, Vtl::Vtl1); + tmk_assert!(r.is_ok(), "apply_vtl_protection_for_memory should succeed"); + + log::info!("moving to vtl0 to attempt to read the heap memory"); + + ctx.switch_to_low_vtl(); + })); + tmk_assert!(r.is_ok(), "start_on_vp should succeed"); + + let (tx, rx) = Channel::new().split(); + + let r = ctx.start_on_vp(VpExecutor::new(0x2, Vtl::Vtl1).command(move |ctx: &mut T| { + let r = ctx.setup_interrupt_handler(); + tmk_assert!(r.is_ok(), "setup_interrupt_handler should succeed"); + + let r = ctx.setup_secure_intercept(0x30); + tmk_assert!(r.is_ok(), "setup_secure_intercept should succeed"); + + log::info!("successfully started running VTL1 on vp2."); + })); + tmk_assert!(r.is_ok(), "start_on_vp should succeed"); + + let r = ctx.start_on_vp( + VpExecutor::new(0x2, Vtl::Vtl0).command(move |ctx: &mut T| unsafe { + log::info!("successfully started running VTL0 on vp2."); + + let r = + ctx.queue_command_vp(VpExecutor::new(2, Vtl::Vtl1).command(move |ctx: &mut T| { + log::info!("after intercept successfully started running VTL1 on vp2."); + ctx.switch_to_low_vtl(); + })); + tmk_assert!(r.is_ok(), "queue_command_vp should succeed"); + + f_violate_heap(); + + #[expect(warnings)] + { + log::info!( + "reading mutated heap memory from vtl0(it should not be 0xA2): 0x{:x}", + RETURN_VALUE + ); + tmk_assert!( + RETURN_VALUE != 0xA2, + "heap memory should not be accessible from vtl0" + ); + } + + _ = tx.send(()); + }), + ); + tmk_assert!(r.is_ok(), "start_on_vp should succeed"); + + _ = rx.recv(); + + log::info!("we are in vtl0 now!"); + log::info!("we reached the end of the test"); +} diff --git a/opentmk/src/tests/hyperv/hv_memory_protect_write.rs b/opentmk/src/tests/hyperv/hv_memory_protect_write.rs new file mode 100644 index 0000000000..70124aed5b --- /dev/null +++ b/opentmk/src/tests/hyperv/hv_memory_protect_write.rs @@ -0,0 +1,132 @@ +use alloc::alloc::alloc; +use spin::Mutex; +use core::alloc::Layout; +use core::arch::asm; +use core::cell::RefCell; +use core::ops::Range; + +use hvdef::Vtl; +use nostd_spin_channel::Channel; + +use crate::context::InterruptPlatformTrait; +use crate::context::SecureInterceptPlatformTrait; +use crate::context::VirtualProcessorPlatformTrait; +use crate::context::VpExecutor; +use crate::context::VtlPlatformTrait; +use crate::create_function_with_restore; +use crate::tmk_assert; + +static mut HEAPX: RefCell<*mut u8> = RefCell::new(0 as *mut u8); +static FAULT_CALLED: Mutex = Mutex::new(false); + +#[inline(never)] +#[expect(warnings)] +fn violate_heap() { + unsafe { + let heapx = *HEAPX.borrow(); + *(heapx.add(10)) = 0x56; + } +} +create_function_with_restore!(f_violate_heap, violate_heap); + + +pub fn exec(ctx: &mut T) +where + T: InterruptPlatformTrait + + SecureInterceptPlatformTrait + + VtlPlatformTrait + + VirtualProcessorPlatformTrait, +{ + let vp_count = ctx.get_vp_count(); + tmk_assert!(vp_count.is_ok(), "get_vp_count should succeed"); + let vp_count = vp_count.unwrap(); + tmk_assert!(vp_count == 4, "vp count should be 8"); + + let r = ctx.setup_interrupt_handler(); + tmk_assert!(r.is_ok(), "setup_interrupt_handler should succeed"); + log::info!("set intercept handler successfully!"); + + let r = ctx.setup_partition_vtl(Vtl::Vtl1); + tmk_assert!(r.is_ok(), "setup_partition_vtl should succeed"); + + let r = ctx.start_on_vp(VpExecutor::new(0, Vtl::Vtl1).command(move |ctx: &mut T| { + log::info!("successfully started running VTL1 on vp0."); + let r = ctx.setup_secure_intercept(0x30); + tmk_assert!(r.is_ok(), "setup_secure_intercept should succeed"); + + let r = ctx.set_interrupt_idx(0x30, move || { + log::info!("interrupt handled for 0x30!"); + let mut status = FAULT_CALLED.lock(); + *status = true; + }); + tmk_assert!(r.is_ok(), "set_interrupt_idx should succeed"); + + let layout = + Layout::from_size_align(1024 * 1024, 4096).expect("msg: failed to create layout"); + let ptr = unsafe { alloc(layout) }; + log::info!("allocated some memory in the heap from vtl1"); + + #[expect(warnings)] + unsafe { + let mut z = HEAPX.borrow_mut(); + *z = ptr; + *ptr.add(10) = 0xA2; + } + + let size = layout.size(); + let r = ctx.setup_vtl_protection(); + tmk_assert!(r.is_ok(), "setup_vtl_protection should succeed"); + log::info!("enabled vtl protections for the partition."); + + let range = Range { + start: ptr as u64, + end: ptr as u64 + size as u64, + }; + + let r = ctx.apply_vtl_protection_for_memory(range, Vtl::Vtl1); + tmk_assert!(r.is_ok(), "apply_vtl_protection_for_memory should succeed"); + + log::info!("moving to vtl0 to attempt to read the heap memory"); + + ctx.switch_to_low_vtl(); + })); + tmk_assert!(r.is_ok(), "start_on_vp should succeed"); + + let (tx, rx) = Channel::new().split(); + + let r = ctx.start_on_vp(VpExecutor::new(0x2, Vtl::Vtl1).command(move |ctx: &mut T| { + let r = ctx.setup_interrupt_handler(); + tmk_assert!(r.is_ok(), "setup_interrupt_handler should succeed"); + + let r = ctx.setup_secure_intercept(0x30); + tmk_assert!(r.is_ok(), "setup_secure_intercept should succeed"); + + log::info!("successfully started running VTL1 on vp2."); + })); + tmk_assert!(r.is_ok(), "start_on_vp should succeed"); + + let r = ctx.start_on_vp( + VpExecutor::new(0x2, Vtl::Vtl0).command(move |ctx: &mut T| { + log::info!("successfully started running VTL0 on vp2."); + + let r = + ctx.queue_command_vp(VpExecutor::new(2, Vtl::Vtl1).command(move |ctx: &mut T| { + log::info!("after intercept successfully started running VTL1 on vp2."); + ctx.switch_to_low_vtl(); + })); + tmk_assert!(r.is_ok(), "queue_command_vp should succeed"); + + f_violate_heap(); + _ = tx.send(()); + }), + ); + tmk_assert!(r.is_ok(), "start_on_vp should succeed"); + + _ = rx.recv(); + + let fault_called = *FAULT_CALLED.lock(); + tmk_assert!(fault_called, "Secure intercept should be received"); + + log::info!("we are in vtl0 now!"); + log::info!("we reached the end of the test"); +} diff --git a/opentmk/opentmk/src/tests/hv_processor.rs b/opentmk/src/tests/hyperv/hv_processor.rs similarity index 99% rename from opentmk/opentmk/src/tests/hv_processor.rs rename to opentmk/src/tests/hyperv/hv_processor.rs index 3ce93018c6..226a7c9743 100644 --- a/opentmk/opentmk/src/tests/hv_processor.rs +++ b/opentmk/src/tests/hyperv/hv_processor.rs @@ -1,5 +1,5 @@ use hvdef::Vtl; -use sync_nostd::Channel; +use nostd_spin_channel::Channel; use crate::context::InterruptPlatformTrait; use crate::context::VirtualProcessorPlatformTrait; diff --git a/opentmk/opentmk/src/tests/hv_tpm_read_cvm.rs b/opentmk/src/tests/hyperv/hv_tpm_read_cvm.rs similarity index 100% rename from opentmk/opentmk/src/tests/hv_tpm_read_cvm.rs rename to opentmk/src/tests/hyperv/hv_tpm_read_cvm.rs diff --git a/opentmk/opentmk/src/tests/hv_tpm_write_cvm.rs b/opentmk/src/tests/hyperv/hv_tpm_write_cvm.rs similarity index 100% rename from opentmk/opentmk/src/tests/hv_tpm_write_cvm.rs rename to opentmk/src/tests/hyperv/hv_tpm_write_cvm.rs diff --git a/opentmk/src/tests/hyperv/mod.rs b/opentmk/src/tests/hyperv/mod.rs new file mode 100644 index 0000000000..1e9ee0e2a1 --- /dev/null +++ b/opentmk/src/tests/hyperv/mod.rs @@ -0,0 +1,7 @@ +pub mod hv_error_vp_start; +pub mod hv_memory_protect_read; +pub mod hv_memory_protect_write; +pub mod hv_processor; +pub mod hv_tpm_read_cvm; +pub mod hv_tpm_write_cvm; +pub mod test_helpers; diff --git a/opentmk/src/tests/hyperv/test_helpers.rs b/opentmk/src/tests/hyperv/test_helpers.rs new file mode 100644 index 0000000000..d68a002826 --- /dev/null +++ b/opentmk/src/tests/hyperv/test_helpers.rs @@ -0,0 +1,43 @@ +#[macro_export] +macro_rules! create_function_with_restore { + ($func_name:ident, $symbol:ident) => { + #[inline(never)] + fn $func_name() { + unsafe { + asm!(" + push rax + push rbx + push rcx + push rdx + push rsi + push rdi + push rbp + push r8 + push r9 + push r10 + push r11 + push r12 + push r13 + push r14 + push r15 + call {} + pop r15 + pop r14 + pop r13 + pop r12 + pop r11 + pop r10 + pop r9 + pop r8 + pop rbp + pop rdi + pop rsi + pop rdx + pop rcx + pop rbx + pop rax + ", sym $symbol); + } + } + }; +} \ No newline at end of file diff --git a/opentmk/opentmk/src/tests/mod.rs b/opentmk/src/tests/mod.rs similarity index 52% rename from opentmk/opentmk/src/tests/mod.rs rename to opentmk/src/tests/mod.rs index b4b70472c8..84e83cad87 100644 --- a/opentmk/opentmk/src/tests/mod.rs +++ b/opentmk/src/tests/mod.rs @@ -1,16 +1,9 @@ #![expect(dead_code)] use crate::platform::hypvctx::HvTestCtx; - -mod hv_cvm_mem_protect; -mod hv_error_vp_start; -mod hv_misc; -mod hv_processor; -mod hv_tpm; -mod hv_tpm_read_cvm; -mod hv_tpm_write_cvm; +mod hyperv; pub fn run_test() { let mut ctx = HvTestCtx::new(); ctx.init(hvdef::Vtl::Vtl0).expect("failed to init on BSP"); - hv_tpm_read_cvm::exec(&mut ctx); + hyperv::hv_tpm_read_cvm::exec(&mut ctx); } diff --git a/opentmk/opentmk/src/tmk_assert.rs b/opentmk/src/tmk_assert.rs similarity index 100% rename from opentmk/opentmk/src/tmk_assert.rs rename to opentmk/src/tmk_assert.rs diff --git a/opentmk/opentmk/src/tmk_logger.rs b/opentmk/src/tmk_logger.rs similarity index 96% rename from opentmk/opentmk/src/tmk_logger.rs rename to opentmk/src/tmk_logger.rs index e9c662fc8f..2705b715f6 100644 --- a/opentmk/opentmk/src/tmk_logger.rs +++ b/opentmk/src/tmk_logger.rs @@ -3,11 +3,10 @@ use alloc::string::String; use alloc::string::ToString; use core::fmt::Write; -use anyhow::Result; use log::SetLoggerError; use serde::Serialize; -use sync_nostd::Mutex; -use sync_nostd::MutexGuard; +use spin::Mutex; +use spin::MutexGuard; use crate::arch::serial::InstrIoAccess; use crate::arch::serial::Serial; diff --git a/opentmk/opentmk/src/tmkdefs.rs b/opentmk/src/tmkdefs.rs similarity index 80% rename from opentmk/opentmk/src/tmkdefs.rs rename to opentmk/src/tmkdefs.rs index 4bf42b1510..5d5381be05 100644 --- a/opentmk/opentmk/src/tmkdefs.rs +++ b/opentmk/src/tmkdefs.rs @@ -1,5 +1,10 @@ -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub enum TmkErrorType { +use core::fmt::Display; + +use thiserror::Error; + + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Error)] +pub enum TmkError { AllocationFailed, InvalidParameter, EnableVtlFailed, @@ -73,21 +78,10 @@ pub enum TmkErrorType { UnknownRegisterName, } -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub struct TmkError(pub TmkErrorType); - -pub type TmkResult = Result; - -impl core::error::Error for TmkError {} - -impl core::fmt::Display for TmkError { +impl Display for TmkError { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - write!(f, "TmkError({:?})", self.0) + write!(f, "{:?}", self) } } -impl From for TmkError { - fn from(e: TmkErrorType) -> Self { - TmkError(e) - } -} +pub type TmkResult = Result; \ No newline at end of file diff --git a/opentmk/opentmk/src/uefi/alloc.rs b/opentmk/src/uefi/alloc.rs similarity index 66% rename from opentmk/opentmk/src/uefi/alloc.rs rename to opentmk/src/uefi/alloc.rs index 356b390278..c8a7f61d43 100644 --- a/opentmk/opentmk/src/uefi/alloc.rs +++ b/opentmk/src/uefi/alloc.rs @@ -2,7 +2,7 @@ use core::alloc::GlobalAlloc; use core::cell::RefCell; use linked_list_allocator::LockedHeap; -use sync_nostd::Mutex; +use spin::Mutex; use uefi::allocator::Allocator; use uefi::boot::AllocateType; use uefi::boot::MemoryType; @@ -26,29 +26,16 @@ pub struct MemoryAllocator { #[expect(unsafe_code)] unsafe impl GlobalAlloc for MemoryAllocator { - #[allow(unsafe_code)] unsafe fn alloc(&self, layout: core::alloc::Layout) -> *mut u8 { - if *self.use_locked_heap.lock().borrow() { - unsafe { self.locked_heap.alloc(layout) } - } else { - unsafe { self.uefi_allocator.alloc(layout) } - } + unsafe { self.get_allocator().alloc(layout) } } unsafe fn dealloc(&self, ptr: *mut u8, layout: core::alloc::Layout) { - if *self.use_locked_heap.lock().borrow() { - unsafe { self.locked_heap.dealloc(ptr, layout) } - } else { - unsafe { self.uefi_allocator.dealloc(ptr, layout) } - } + unsafe { self.get_allocator().dealloc(ptr, layout) }; } unsafe fn alloc_zeroed(&self, layout: core::alloc::Layout) -> *mut u8 { - if *self.use_locked_heap.lock().borrow() { - unsafe { self.locked_heap.alloc_zeroed(layout) } - } else { - unsafe { self.uefi_allocator.alloc_zeroed(layout) } - } + unsafe { self.get_allocator().alloc_zeroed(layout) } } unsafe fn realloc( @@ -57,16 +44,12 @@ unsafe impl GlobalAlloc for MemoryAllocator { layout: core::alloc::Layout, new_size: usize, ) -> *mut u8 { - if *self.use_locked_heap.lock().borrow() { - unsafe { self.locked_heap.realloc(ptr, layout, new_size) } - } else { - unsafe { self.uefi_allocator.realloc(ptr, layout, new_size) } - } + unsafe { self.get_allocator().realloc(ptr, layout, new_size) } } } impl MemoryAllocator { - pub fn init(&self, size: usize) -> bool { + pub fn switch_to_capped_heap(&self, size: usize) -> bool { let pages = ((SIZE_1MB * size) / 4096) + 1; let size = pages * 4096; let mem: Result, uefi::Error> = boot::allocate_pages( @@ -78,15 +61,13 @@ impl MemoryAllocator { return false; } let ptr = mem.unwrap().as_ptr(); - unsafe { - self.locked_heap.lock().init(ptr, size); - } + unsafe { self.locked_heap.lock().init(ptr, size) }; *self.use_locked_heap.lock().borrow_mut() = true; true } - #[allow(dead_code)] - pub fn get_page_alligned_memory(&self, size: usize) -> *mut u8 { + #[expect(dead_code)] + pub fn get_page_aligned_memory(&self, size: usize) -> *mut u8 { let pages = ((SIZE_1MB * size) / PAGE_SIZE) + 1; let mem: Result, uefi::Error> = boot::allocate_pages( AllocateType::AnyPages, @@ -98,4 +79,14 @@ impl MemoryAllocator { } mem.unwrap().as_ptr() } + + + fn get_allocator(&self) -> &dyn GlobalAlloc { + if *self.use_locked_heap.lock().borrow() { + &self.locked_heap + } else { + &self.uefi_allocator + } + } + } diff --git a/opentmk/opentmk/src/uefi/init.rs b/opentmk/src/uefi/init.rs similarity index 93% rename from opentmk/opentmk/src/uefi/init.rs rename to opentmk/src/uefi/init.rs index 292dacd159..33fc3c717a 100644 --- a/opentmk/opentmk/src/uefi/init.rs +++ b/opentmk/src/uefi/init.rs @@ -50,11 +50,11 @@ fn enable_uefi_vtl_protection() { } pub fn init() -> Result<(), Status> { - let r: bool = ALLOCATOR.init(2048); + let r: bool = ALLOCATOR.switch_to_capped_heap(512); if !r { return Err(Status::ABORTED); } - crate::tmk_logger::init().expect("Failed to init logger"); + crate::tmk_logger::init().map_err(|_| Status::NOT_READY)?; enable_uefi_vtl_protection(); Ok(()) } diff --git a/opentmk/opentmk/src/uefi/mod.rs b/opentmk/src/uefi/mod.rs similarity index 100% rename from opentmk/opentmk/src/uefi/mod.rs rename to opentmk/src/uefi/mod.rs diff --git a/opentmk/opentmk/src/uefi/rt.rs b/opentmk/src/uefi/rt.rs similarity index 82% rename from opentmk/opentmk/src/uefi/rt.rs rename to opentmk/src/uefi/rt.rs index d115f97a88..f065229d72 100644 --- a/opentmk/opentmk/src/uefi/rt.rs +++ b/opentmk/src/uefi/rt.rs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -//! Runtime support for the UEFI application environment. - #![cfg(target_os = "uefi")] #[panic_handler] diff --git a/opentmk/sync/Cargo.toml b/support/nostd_spin_channel/Cargo.toml similarity index 84% rename from opentmk/sync/Cargo.toml rename to support/nostd_spin_channel/Cargo.toml index 53f9ba2ad6..52926e1b7d 100644 --- a/opentmk/sync/Cargo.toml +++ b/support/nostd_spin_channel/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "sync_nostd" +name = "nostd_spin_channel" version = "0.1.0" rust-version.workspace = true edition.workspace = true diff --git a/opentmk/sync/src/lib.rs b/support/nostd_spin_channel/src/lib.rs similarity index 99% rename from opentmk/sync/src/lib.rs rename to support/nostd_spin_channel/src/lib.rs index 30e6ae2777..68d6d76034 100644 --- a/opentmk/sync/src/lib.rs +++ b/support/nostd_spin_channel/src/lib.rs @@ -2,7 +2,7 @@ #![allow(unsafe_code)] extern crate alloc; use core::sync::atomic::{AtomicUsize, Ordering}; -pub use spin::{Mutex, MutexGuard}; +use spin::Mutex; use alloc::{sync::Arc, vec::Vec}; use alloc::collections::VecDeque; use core::error::Error; From 7f33bbda296bcda4c28ea5b297919501599053b5 Mon Sep 17 00:00:00 2001 From: Mayank Kumar Date: Wed, 27 Aug 2025 17:23:45 +0000 Subject: [PATCH 18/23] chore: resolve PR comments --- Cargo.lock | 1 + opentmk/Cargo.toml | 5 + opentmk/build_deploy.sh | 5 +- opentmk/src/arch/aarch64/mod.rs | 2 +- opentmk/src/arch/aarch64/serial.rs | 255 +++++++++++++++++++++++ opentmk/src/arch/mod.rs | 4 +- opentmk/src/arch/x86_64/mod.rs | 2 + opentmk/src/arch/x86_64/serial.rs | 1 - opentmk/src/context.rs | 1 + opentmk/src/hypercall.rs | 1 - opentmk/src/main.rs | 4 +- opentmk/src/platform/hypvctx.rs | 2 + opentmk/src/tests/hyperv/hv_processor.rs | 7 +- opentmk/src/tests/hyperv/mod.rs | 4 + opentmk/src/tests/mod.rs | 2 +- support/nostd_spin_channel/Cargo.toml | 2 +- support/nostd_spin_channel/src/lib.rs | 32 +-- 17 files changed, 288 insertions(+), 42 deletions(-) create mode 100644 opentmk/src/arch/aarch64/serial.rs diff --git a/Cargo.lock b/Cargo.lock index e2f566aa3f..c97acbb05e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4441,6 +4441,7 @@ name = "nostd_spin_channel" version = "0.1.0" dependencies = [ "spin 0.10.0", + "thiserror 2.0.0", ] [[package]] diff --git a/opentmk/Cargo.toml b/opentmk/Cargo.toml index b11cced3fb..3997b98a93 100644 --- a/opentmk/Cargo.toml +++ b/opentmk/Cargo.toml @@ -6,6 +6,11 @@ name = "opentmk" edition.workspace = true rust-version.workspace = true +[features] +default = [] +nightly = [] + + [dependencies] arrayvec.workspace = true bitfield-struct.workspace = true diff --git a/opentmk/build_deploy.sh b/opentmk/build_deploy.sh index 8a23bf08fd..fc522e8a09 100755 --- a/opentmk/build_deploy.sh +++ b/opentmk/build_deploy.sh @@ -1,4 +1,5 @@ -RUST_BACKTRACE=1 CARGO_PROFILE_RELEASE_force_frame_pointers=yes cargo +nightly-2025-05-09 build -p opentmk --target x86_64-unknown-uefi --release #--target-dir ./target/x86_64-unknown-uefi/debug +RUST_BACKTRACE=1 CARGO_PROFILE_RELEASE_force_frame_pointers=yes cargo build -p opentmk --target x86_64-unknown-uefi --release #--target-dir ./target/x86_64-unknown-uefi/debug cargo xtask guest-test uefi --bootx64 ~/projects-local/openvmm/target/x86_64-unknown-uefi/release/opentmk.efi qemu-img convert -f raw -O vhdx ~/projects-local/openvmm/target/x86_64-unknown-uefi/release/opentmk.img ~/projects/opentmk.vhdx -#CARGO_PROFILE_RELEASE_OPT_LEVEL=0 \ No newline at end of file +#CARGO_PROFILE_RELEASE_OPT_LEVEL=0 +#+nightly-2025-05-09 \ No newline at end of file diff --git a/opentmk/src/arch/aarch64/mod.rs b/opentmk/src/arch/aarch64/mod.rs index c9ab11a58c..85dde0c546 100644 --- a/opentmk/src/arch/aarch64/mod.rs +++ b/opentmk/src/arch/aarch64/mod.rs @@ -1,3 +1,3 @@ -pub use minimal_rt::arch::aarch64::serial; +pub mod serial; pub mod hypercall; diff --git a/opentmk/src/arch/aarch64/serial.rs b/opentmk/src/arch/aarch64/serial.rs new file mode 100644 index 0000000000..f11e97257e --- /dev/null +++ b/opentmk/src/arch/aarch64/serial.rs @@ -0,0 +1,255 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +//! aarch64 MMIO-based serial port, UART PL011. +//! +//! Used for debug output. Follows +//! [PrimeCell UART (PL011) Technical Reference Manual](https://developer.arm.com/documentation/ddi0183/g/) +//! +//! PL011 Registers: +//! +//! Offset Name Type Reset Bits Description +//! ---------------------------------------------------------------------- +//! 0x000 UARTDR RW 0x--- 12/8 Data Register +//! 0x004 UARTRSR/UARTECR RW 0x0 4/0 Receive Status Register/Error Clear Register +//! 0x018 UARTFR RO 0b-10010--- 9 Flag Register +//! 0x020 UARTILPR RW 0x00 8 IrDA Low-Power Counter Register +//! 0x024 UARTIBRD RW 0x0000 16 Integer Baud Rate Register +//! 0x028 UARTFBRD RW 0x00 6 Fractional Baud Rate Register +//! 0x02C UARTLCR_H RW 0x00 8 Line Control Register +//! 0x030 UARTCR RW 0x0300 16 Control Register +//! 0x034 UARTIFLS RW 0x12 6 Interrupt FIFO Level Select Register +//! 0x038 UARTIMSC RW 0x000 11 Interrupt Mask Set/Clear Register +//! 0x03C UARTRIS RO 0x00- 11 Raw Interrupt Status Register +//! 0x040 UARTMIS RO 0x00- 11 Masked Interrupt Status Register +//! 0x044 UARTICR WO - 11 Interrupt Clear Register +//! 0x048 UARTDMACR RW 0x00 3 DMA Control Register +//! 0xFE0 UARTPeriphID0 RO 0x11 8 UARTPeriphID0 Register +//! 0xFE4 UARTPeriphID1 RO 0x10 8 UARTPeriphID1 Register +//! 0xFE8 UARTPeriphID2 RO 0x_4a 8 UARTPeriphID2 Register +//! 0xFEC UARTPeriphID3 RO 0x00 8 UARTPeriphID3 Register +//! 0xFF0 UARTPCellID0 RO 0x0D 8 UARTPCellID0 Register +//! 0xFF4 UARTPCellID1 RO 0xF0 8 UARTPCellID1 Register +//! 0xFF8 UARTPCellID2 RO 0x05 8 UARTPCellID2 Register +//! 0xFFC UARTPCellID3 RO 0xB1 8 UARTPCellID3 Register + +#![allow(dead_code)] + +use core::hint::spin_loop; +use core::sync::atomic::AtomicBool; +use core::sync::atomic::Ordering; + +#[derive(Debug, Clone, Copy)] +#[repr(u16)] +enum Pl011Register { + /// Data Register + Dr = 0x000, + /// Receive Status Register/Error Clear Register + RsrOrEcr = 0x004, + /// Flag register + Fr = 0x018, + /// Integer Baud Rate Register + Ibrd = 0x024, + /// Fractional Baud Rate Register + Fbrd = 0x028, + /// Line Control Register + LcrHigh = 0x02c, + /// Control Register + Cr = 0x030, + /// Masked Interrupt Status Register + Imsc = 0x038, + /// Interrupt Clear Register + Icr = 0x044, + /// DMA Control Register + DmaCr = 0x048, + /// UARTPeriphID0 Register + PeriphID0 = 0xFE0, + /// UARTPeriphID1 Register + PeriphID1 = 0xFE4, + /// UARTPeriphID2 Register + PeriphID2 = 0xFE8, + /// UARTPeriphID3 Register + PeriphID3 = 0xFEC, + /// UARTPCellID0 Register + PCellID0 = 0xFF0, + /// UARTPCellID1 Register + PCellID1 = 0xFF4, + /// UARTPCellID2 Register + PCellID2 = 0xFF8, + /// UARTPCellID3 Register + PCellID3 = 0xFFC, +} + +const CR_RX_ENABLE: u32 = 0x200; +const CR_TX_ENABLE: u32 = 0x100; +const CR_UART_ENABLE: u32 = 1; +const LCR_H_FIFO_EN: u32 = 0x10; +const LCR_H_8BITS: u32 = 0x60; + +const _FR_TX_EMPTY: u32 = 0x080; +const _FR_RX_FULL: u32 = 0x040; +const FR_TX_FULL: u32 = 0x020; +const _FR_RX_EMPTY: u32 = 0x010; +const FR_BUSY: u32 = 0x008; + +/// The Hyper-V PL011 host emulated PL011's are found at these +/// base addresses. Should come from ACPI or DT of course yet +/// due to having been hardcoded in some products makes that +/// virtually constants. +const PL011_HYPER_V_BASE_1: u64 = 0xeffec000; +const _PL011_HYPER_V_BASE_2: u64 = 0xeffeb000; +const PL011_BASE: u64 = PL011_HYPER_V_BASE_1; + +fn read_register(reg: Pl011Register) -> u32 { + // SAFETY: using the PL011 MMIO address. + unsafe { core::ptr::read_volatile((PL011_BASE + reg as u64) as *const u32) } +} + +fn write_register(reg: Pl011Register, val: u32) { + // SAFETY: using the PL011 MMIO address. + unsafe { + core::ptr::write_volatile((PL011_BASE + reg as u64) as *mut u32, val); + } +} + +fn cell_id() -> u32 { + // This can easily be rewritten employing + // bare arithmetic yet the compiler does a very good job + // so using the domain abstractions. + [ + Pl011Register::PCellID3, + Pl011Register::PCellID2, + Pl011Register::PCellID1, + Pl011Register::PCellID0, + ] + .iter() + .fold(0, |id_running, &r| { + id_running.wrapping_shl(8) | (read_register(r) as u8 as u32) + }) +} + +fn periph_id() -> u32 { + // This can easily be rewritten employing + // bare arithmetic yet the compiler does a very good job + // so using the domain abstractions. + [ + Pl011Register::PeriphID3, + Pl011Register::PeriphID2, + Pl011Register::PeriphID1, + Pl011Register::PeriphID0, + ] + .iter() + .fold(0, |id_running, &r| { + id_running.wrapping_shl(8) | (read_register(r) as u8 as u32) + }) +} + +fn poll_tx_not_full() { + while read_register(Pl011Register::Fr) & FR_TX_FULL != 0 { + spin_loop(); + } +} + +fn poll_not_busy() { + while read_register(Pl011Register::Fr) & FR_BUSY != 0 { + spin_loop(); + } +} + +/// Disables the functional parts of the UART, drains FIFOs, +/// sets baud rate and enables the UART in the polling mode. +/// Might be geared towards the real hardware more than the virtual one. +/// Works with qemu and Hyper-V. +fn reset_and_init() { + // Mask interrupts (lower 11 bits) + write_register(Pl011Register::Imsc, 0x7ff); + // Clear interrupts (lower 11 bits) + write_register(Pl011Register::Icr, 0x7ff); + // Disable DMA on Rx and Tx + write_register(Pl011Register::DmaCr, 0x0); + + // Leave Rx and Tx enabled to drain FIFOs. + write_register(Pl011Register::Cr, CR_RX_ENABLE | CR_TX_ENABLE); + read_register(Pl011Register::Cr); // wait + read_register(Pl011Register::Cr); // wait + poll_not_busy(); + + // Disable Rx, Tx, and UART. + write_register(Pl011Register::Cr, 0x00000000); + + // Set integer and fractional parts of the baud rate, + // hardcoded for now + write_register(Pl011Register::Fbrd, 0x00000004); + write_register(Pl011Register::Ibrd, 0x00000027); + // The UARTLCR_H, UARTIBRD, and UARTFBRD registers form the single 30-bit + // wide UARTLCR Register that is updated on a single write strobe generated by a + // UARTLCR_H write + write_register(Pl011Register::LcrHigh, LCR_H_FIFO_EN | LCR_H_8BITS); + + // Clear the errors + write_register(Pl011Register::RsrOrEcr, 0); + + // Enable Tx and Rx + write_register(Pl011Register::Cr, CR_RX_ENABLE | CR_TX_ENABLE); + read_register(Pl011Register::Cr); // wait + read_register(Pl011Register::Cr); // wait + poll_not_busy(); + + // Enable UART + write_register( + Pl011Register::Cr, + CR_RX_ENABLE | CR_TX_ENABLE | CR_UART_ENABLE, + ); + poll_not_busy(); +} + +/// A PL011 serial port. +pub struct Serial; + +static SUPPORTED: AtomicBool = AtomicBool::new(false); + +/// Serial port addresses. +/// These are the standard COM ports used in x86 systems. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum SerialPort { + COM1, + COM2, + COM3, + COM4, +} + + +impl Serial { + + pub fn new(serial: SerialPort, io: T) -> Self { + } + + /// Initializes the serial port. + pub fn init() -> Serial { + const SUPPORTED_PL011_CELLS: &[u32] = &[0xB105_F00D]; + + let cell_id = cell_id(); + let supported = SUPPORTED_PL011_CELLS.contains(&cell_id); + if supported { + reset_and_init(); + } + SUPPORTED.store(supported, Ordering::Relaxed); + + Self + } +} + +impl core::fmt::Write for Serial { + fn write_str(&mut self, s: &str) -> core::fmt::Result { + if !SUPPORTED.load(Ordering::Relaxed) { + return Ok(()); + } + + for byte in s.bytes() { + poll_tx_not_full(); + write_register(Pl011Register::Dr, byte.into()); + } + + Ok(()) + } +} diff --git a/opentmk/src/arch/mod.rs b/opentmk/src/arch/mod.rs index 1d578ebeb1..3f64d7432b 100644 --- a/opentmk/src/arch/mod.rs +++ b/opentmk/src/arch/mod.rs @@ -3,12 +3,12 @@ //! Imports and re-exports architecture-specific implementations. -mod x86_64; - cfg_if::cfg_if!( if #[cfg(target_arch = "x86_64")] { + mod x86_64; pub use x86_64::*; } else if #[cfg(target_arch = "aarch64")] { + mod aarch64; pub use aarch64::*; } else { compile_error!("target_arch is not supported"); diff --git a/opentmk/src/arch/x86_64/mod.rs b/opentmk/src/arch/x86_64/mod.rs index 2da8e40fb0..a6d65a4025 100644 --- a/opentmk/src/arch/x86_64/mod.rs +++ b/opentmk/src/arch/x86_64/mod.rs @@ -1,5 +1,7 @@ pub mod hypercall; +#[cfg(feature = "nightly")] pub mod interrupt; +#[cfg(feature = "nightly")] mod interrupt_handler_register; mod io; pub mod rtc; diff --git a/opentmk/src/arch/x86_64/serial.rs b/opentmk/src/arch/x86_64/serial.rs index f388525652..e37b855f6a 100644 --- a/opentmk/src/arch/x86_64/serial.rs +++ b/opentmk/src/arch/x86_64/serial.rs @@ -11,7 +11,6 @@ use super::io; /// Serial port addresses. /// These are the standard COM ports used in x86 systems. -#[repr(u16)] #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum SerialPort { COM1, diff --git a/opentmk/src/context.rs b/opentmk/src/context.rs index e00d7b1189..3822b2eec7 100644 --- a/opentmk/src/context.rs +++ b/opentmk/src/context.rs @@ -16,6 +16,7 @@ pub trait SecureInterceptPlatformTrait { fn setup_secure_intercept(&mut self, interrupt_idx: u8) -> TmkResult<()>; } +#[cfg(feature = "nightly")] pub trait InterruptPlatformTrait { /// Associates an interrupt vector with a handler inside the /// non-secure world. diff --git a/opentmk/src/hypercall.rs b/opentmk/src/hypercall.rs index 152611e6d5..4b629612e3 100644 --- a/opentmk/src/hypercall.rs +++ b/opentmk/src/hypercall.rs @@ -613,7 +613,6 @@ impl HvCall { let efer = unsafe { read_msr(0xC0000080) }; context.efer = efer; - log::info!("Current VTL VP context: {:?}", context); Ok(context) } diff --git a/opentmk/src/main.rs b/opentmk/src/main.rs index 1877d09057..279ff3b951 100644 --- a/opentmk/src/main.rs +++ b/opentmk/src/main.rs @@ -2,7 +2,8 @@ // Licensed under the MIT License. #![no_std] #![expect(unsafe_code)] -#![feature(abi_x86_interrupt)] +#![cfg_attr(feature = "nightly", feature(abi_x86_interrupt))] + #![doc = include_str!("../README.md")] #![cfg_attr(all(not(test), target_os = "uefi"), no_main)] #![cfg_attr(all(not(test), target_os = "uefi"), no_std)] @@ -11,6 +12,7 @@ #[cfg(any(test, not(target_os = "uefi")))] fn main() {} + #[macro_use] extern crate alloc; diff --git a/opentmk/src/platform/hypvctx.rs b/opentmk/src/platform/hypvctx.rs index c2b6c65239..703a0e9803 100644 --- a/opentmk/src/platform/hypvctx.rs +++ b/opentmk/src/platform/hypvctx.rs @@ -17,6 +17,7 @@ use minimal_rt::arch::msr::read_msr; use minimal_rt::arch::msr::write_msr; use spin::Mutex; +#[cfg(feature = "nightly")] use crate::context::InterruptPlatformTrait; use crate::context::MsrPlatformTrait; use crate::context::SecureInterceptPlatformTrait; @@ -93,6 +94,7 @@ impl SecureInterceptPlatformTrait for HvTestCtx { } } +#[cfg(feature = "nightly")] impl InterruptPlatformTrait for HvTestCtx { /// Install an interrupt handler for the supplied vector on x86-64. /// For non-x86-64 targets the call returns diff --git a/opentmk/src/tests/hyperv/hv_processor.rs b/opentmk/src/tests/hyperv/hv_processor.rs index 226a7c9743..415142d417 100644 --- a/opentmk/src/tests/hyperv/hv_processor.rs +++ b/opentmk/src/tests/hyperv/hv_processor.rs @@ -1,7 +1,6 @@ use hvdef::Vtl; use nostd_spin_channel::Channel; -use crate::context::InterruptPlatformTrait; use crate::context::VirtualProcessorPlatformTrait; use crate::context::VpExecutor; use crate::context::VtlPlatformTrait; @@ -10,7 +9,7 @@ use crate::tmk_assert; #[inline(never)] pub fn exec(ctx: &mut T) where - T: VtlPlatformTrait + VirtualProcessorPlatformTrait + InterruptPlatformTrait, + T: VtlPlatformTrait + VirtualProcessorPlatformTrait, { let r = ctx.setup_partition_vtl(Vtl::Vtl1); tmk_assert!(r.is_ok(), "setup_partition_vtl should succeed"); @@ -21,10 +20,6 @@ where let vp_count = vp_count.unwrap(); tmk_assert!(vp_count == 4, "vp count should be 4"); - _ = ctx.setup_interrupt_handler(); - - _ = ctx.set_interrupt_idx(0x6, || loop {}); - // Testing BSP VTL Bringup { let (tx, rx) = Channel::new().split(); diff --git a/opentmk/src/tests/hyperv/mod.rs b/opentmk/src/tests/hyperv/mod.rs index 1e9ee0e2a1..f609c4774c 100644 --- a/opentmk/src/tests/hyperv/mod.rs +++ b/opentmk/src/tests/hyperv/mod.rs @@ -1,7 +1,11 @@ pub mod hv_error_vp_start; +#[cfg(feature = "nightly")] pub mod hv_memory_protect_read; +#[cfg(feature = "nightly")] pub mod hv_memory_protect_write; pub mod hv_processor; +#[cfg(feature = "nightly")] pub mod hv_tpm_read_cvm; +#[cfg(feature = "nightly")] pub mod hv_tpm_write_cvm; pub mod test_helpers; diff --git a/opentmk/src/tests/mod.rs b/opentmk/src/tests/mod.rs index 84e83cad87..6034f31ca7 100644 --- a/opentmk/src/tests/mod.rs +++ b/opentmk/src/tests/mod.rs @@ -5,5 +5,5 @@ mod hyperv; pub fn run_test() { let mut ctx = HvTestCtx::new(); ctx.init(hvdef::Vtl::Vtl0).expect("failed to init on BSP"); - hyperv::hv_tpm_read_cvm::exec(&mut ctx); + hyperv::hv_processor::exec(&mut ctx); } diff --git a/support/nostd_spin_channel/Cargo.toml b/support/nostd_spin_channel/Cargo.toml index 52926e1b7d..d381c97d49 100644 --- a/support/nostd_spin_channel/Cargo.toml +++ b/support/nostd_spin_channel/Cargo.toml @@ -6,7 +6,7 @@ edition.workspace = true [dependencies] spin.workspace = true - +thiserror.workspace = true [lints] workspace = true diff --git a/support/nostd_spin_channel/src/lib.rs b/support/nostd_spin_channel/src/lib.rs index 68d6d76034..63f82860f8 100644 --- a/support/nostd_spin_channel/src/lib.rs +++ b/support/nostd_spin_channel/src/lib.rs @@ -5,7 +5,7 @@ use core::sync::atomic::{AtomicUsize, Ordering}; use spin::Mutex; use alloc::{sync::Arc, vec::Vec}; use alloc::collections::VecDeque; -use core::error::Error; +use thiserror::Error; use core::fmt; /// An unbounded channel implementation with priority send capability. @@ -33,42 +33,22 @@ unsafe impl Send for ChannelInner {} unsafe impl Sync for ChannelInner {} /// Error type for sending operations -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug, Eq, PartialEq, Error)] pub enum SendError { /// All receivers have been dropped + #[error("send failed because receiver is disconnected")] Disconnected(T), } -impl fmt::Display for SendError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - SendError::Disconnected(_) => write!(f, "send failed because receiver is disconnected"), - } - } -} - -impl Error for SendError {} - /// Error type for receiving operations -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug, Eq, PartialEq, Error)] pub enum RecvError { - /// Channel is empty + #[error("receive failed because channel is empty")] Empty, - /// All senders have been dropped + #[error("receive failed because all senders are disconnected")] Disconnected, } -impl fmt::Display for RecvError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - RecvError::Empty => write!(f, "receive failed because channel is empty"), - RecvError::Disconnected => write!(f, "receive failed because sender is disconnected"), - } - } -} - -impl Error for RecvError {} - /// Sender half of the channel pub struct Sender { inner: Arc>, From a49da94c76c6a4f68aff2f0403052991f32ca0bd Mon Sep 17 00:00:00 2001 From: Mayank Kumar Date: Wed, 27 Aug 2025 18:40:14 +0000 Subject: [PATCH 19/23] feat: compiles on Arm64 --- opentmk/build_deploy.sh | 2 +- opentmk/src/arch/aarch64/serial.rs | 6 +- opentmk/src/context.rs | 3 + opentmk/src/hypercall.rs | 350 +++++++++----------------- opentmk/src/platform/hypvctx.rs | 60 +++-- opentmk/src/tmk_logger.rs | 10 +- opentmk/src/tmkdefs.rs | 1 + opentmk/src/uefi/rt.rs | 1 + support/nostd_spin_channel/src/lib.rs | 7 +- 9 files changed, 176 insertions(+), 264 deletions(-) diff --git a/opentmk/build_deploy.sh b/opentmk/build_deploy.sh index fc522e8a09..08e4f9a5b6 100755 --- a/opentmk/build_deploy.sh +++ b/opentmk/build_deploy.sh @@ -1,4 +1,4 @@ -RUST_BACKTRACE=1 CARGO_PROFILE_RELEASE_force_frame_pointers=yes cargo build -p opentmk --target x86_64-unknown-uefi --release #--target-dir ./target/x86_64-unknown-uefi/debug +RUST_BACKTRACE=1 CARGO_PROFILE_RELEASE_force_frame_pointers=yes cargo build -p opentmk --target aarch64-unknown-uefi --release #--target-dir ./target/x86_64-unknown-uefi/debug cargo xtask guest-test uefi --bootx64 ~/projects-local/openvmm/target/x86_64-unknown-uefi/release/opentmk.efi qemu-img convert -f raw -O vhdx ~/projects-local/openvmm/target/x86_64-unknown-uefi/release/opentmk.img ~/projects/opentmk.vhdx #CARGO_PROFILE_RELEASE_OPT_LEVEL=0 diff --git a/opentmk/src/arch/aarch64/serial.rs b/opentmk/src/arch/aarch64/serial.rs index f11e97257e..bc2d094507 100644 --- a/opentmk/src/arch/aarch64/serial.rs +++ b/opentmk/src/arch/aarch64/serial.rs @@ -219,11 +219,7 @@ pub enum SerialPort { } -impl Serial { - - pub fn new(serial: SerialPort, io: T) -> Self { - } - +impl Serial { /// Initializes the serial port. pub fn init() -> Serial { const SUPPORTED_PL011_CELLS: &[u32] = &[0xB105_F00D]; diff --git a/opentmk/src/context.rs b/opentmk/src/context.rs index 3822b2eec7..ef38856e83 100644 --- a/opentmk/src/context.rs +++ b/opentmk/src/context.rs @@ -5,6 +5,8 @@ use hvdef::Vtl; use crate::tmkdefs::TmkResult; +#[cfg(feature = "nightly")] +#[cfg(target_arch = "x86_64")] pub trait SecureInterceptPlatformTrait { /// Installs a secure-world intercept for the given interrupt. /// @@ -31,6 +33,7 @@ pub trait InterruptPlatformTrait { fn setup_interrupt_handler(&mut self) -> TmkResult<()>; } +#[cfg(target_arch = "x86_64")] pub trait MsrPlatformTrait { /// Reads the content of `msr`. /// diff --git a/opentmk/src/hypercall.rs b/opentmk/src/hypercall.rs index 4b629612e3..0554f6a7ac 100644 --- a/opentmk/src/hypercall.rs +++ b/opentmk/src/hypercall.rs @@ -3,7 +3,8 @@ //! Hypercall infrastructure. -#![expect(dead_code)] +#![cfg_attr(target_arch = "aarch64", expect(unused_imports))] + use core::arch::asm; use core::mem::size_of; use core::sync::atomic::AtomicU16; @@ -12,15 +13,20 @@ use core::sync::atomic::Ordering; use arrayvec::ArrayVec; use hvdef::hypercall::EnablePartitionVtlFlags; use hvdef::hypercall::HvInputVtl; +#[cfg(target_arch = "aarch64")] +use hvdef::hypercall::InitialVpContextArm64; +#[cfg(target_arch = "x86_64")] use hvdef::hypercall::InitialVpContextX64; +#[cfg(target_arch = "aarch64")] +use hvdef::HvArm64RegisterName; use hvdef::HvRegisterValue; use hvdef::HvRegisterVsmPartitionConfig; use hvdef::HvX64RegisterName; -use hvdef::HvX64SegmentRegister; use hvdef::Vtl; use hvdef::HV_PAGE_SIZE; use memory_range::MemoryRange; use minimal_rt::arch::hypercall::invoke_hypercall; +#[cfg(target_arch = "x86_64")] use minimal_rt::arch::hypercall::HYPERCALL_PAGE; use zerocopy::FromBytes; use zerocopy::IntoBytes; @@ -32,6 +38,7 @@ struct HvcallPage { } #[inline(never)] +#[cfg(target_arch = "x86_64")] pub unsafe fn invoke_hypercall_vtl(control: hvdef::hypercall::Control) { // SAFETY: the caller guarantees the safety of this operation. unsafe { @@ -45,6 +52,11 @@ pub unsafe fn invoke_hypercall_vtl(control: hvdef::hypercall::Control) { } } +#[cfg(target_arch = "aarch64")] +pub unsafe fn invoke_hypercall_vtl(_control: hvdef::hypercall::Control) { + unimplemented!(); +} + impl HvcallPage { pub const fn new() -> Self { HvcallPage { @@ -129,91 +141,7 @@ static HV_PAGE_INIT_STATUS: AtomicU16 = AtomicU16::new(0); #[expect(unsafe_code)] impl HvCall { - /// Hypercall to accept vtl2 pages from address start to end with VTL 2 - /// protections and no host visibility - #[cfg_attr(target_arch = "aarch64", expect(dead_code))] - pub fn accept_vtl2_pages( - &mut self, - range: MemoryRange, - memory_type: hvdef::hypercall::AcceptMemoryType, - ) -> Result<(), hvdef::HvError> { - const HEADER_SIZE: usize = size_of::(); - const MAX_INPUT_ELEMENTS: usize = (HV_PAGE_SIZE as usize - HEADER_SIZE) / size_of::(); - - let mut current_page = range.start_4k_gpn(); - while current_page < range.end_4k_gpn() { - let header = hvdef::hypercall::AcceptGpaPages { - partition_id: hvdef::HV_PARTITION_ID_SELF, - page_attributes: hvdef::hypercall::AcceptPagesAttributes::new() - .with_memory_type(memory_type.0) - .with_host_visibility(hvdef::hypercall::HostVisibilityType::PRIVATE) // no host visibility - .with_vtl_set(1 << 2), // applies vtl permissions for vtl 2 - vtl_permission_set: hvdef::hypercall::VtlPermissionSet { - vtl_permission_from_1: [0; hvdef::hypercall::HV_VTL_PERMISSION_SET_SIZE], - }, - gpa_page_base: current_page, - }; - - let remaining_pages = range.end_4k_gpn() - current_page; - let count = remaining_pages.min(MAX_INPUT_ELEMENTS as u64); - - let _ = header.write_to_prefix(self.input_page().buffer.as_mut_slice()); - - let output = self.dispatch_hvcall( - hvdef::HypercallCode::HvCallAcceptGpaPages, - Some(count as usize), - ); - - output.result()?; - - current_page += count; - } - - Ok(()) - } - - /// Hypercall to apply vtl protections to the pages from address start to end - #[cfg_attr(target_arch = "aarch64", expect(dead_code))] - pub fn apply_vtl2_protections(&mut self, range: MemoryRange) -> Result<(), hvdef::HvError> { - const HEADER_SIZE: usize = size_of::(); - const MAX_INPUT_ELEMENTS: usize = (HV_PAGE_SIZE as usize - HEADER_SIZE) / size_of::(); - - let header = hvdef::hypercall::ModifyVtlProtectionMask { - partition_id: hvdef::HV_PARTITION_ID_SELF, - map_flags: hvdef::HV_MAP_GPA_PERMISSIONS_NONE, - target_vtl: HvInputVtl::CURRENT_VTL, - reserved: [0; 3], - }; - - let mut current_page = range.start_4k_gpn(); - while current_page < range.end_4k_gpn() { - let remaining_pages = range.end_4k_gpn() - current_page; - let count = remaining_pages.min(MAX_INPUT_ELEMENTS as u64); - - let _ = header.write_to_prefix(self.input_page().buffer.as_mut_slice()); - - let mut input_offset = HEADER_SIZE; - for i in 0..count { - let page_num = current_page + i; - let _ = page_num.write_to_prefix(&mut self.input_page().buffer[input_offset..]); - input_offset += size_of::(); - } - - let output = self.dispatch_hvcall( - hvdef::HypercallCode::HvCallModifyVtlProtectionMask, - Some(count as usize), - ); - - output.result()?; - - current_page += count; - } - - Ok(()) - } - /// Hypercall to apply vtl protections (NO ACCESS) to the pages from address start to end - #[cfg(target_arch = "x86_64")] pub fn apply_vtl_protections( &mut self, range: MemoryRange, @@ -352,18 +280,23 @@ impl HvCall { /// Hypercall to enable VP VTL #[cfg(target_arch = "aarch64")] - pub fn enable_vp_vtl(&mut self, vp_index: u32) -> Result<(), hvdef::HvError> { + pub fn enable_vp_vtl( + &mut self, + vp_index: u32, + target_vtl: Vtl, + vp_context: Option, + ) -> Result<(), hvdef::HvError> { let header = hvdef::hypercall::EnableVpVtlArm64 { partition_id: hvdef::HV_PARTITION_ID_SELF, vp_index, // The VTL value here is just a u8 and not the otherwise usual // HvInputVtl value. - target_vtl: Vtl::Vtl2.into(), + target_vtl: target_vtl.into(), reserved: [0; 3], - vp_vtl_context: zerocopy::FromZeroes::new_zeroed(), + vp_vtl_context: vp_context.unwrap_or(zerocopy::FromZeros::new_zeroed()), }; - header.write_to_prefix(self.input_page().buffer.as_mut_slice()); + _ = header.write_to_prefix(self.input_page().buffer.as_mut_slice()); let output = self.dispatch_hvcall(hvdef::HypercallCode::HvCallEnableVpVtl, None); match output.result() { @@ -372,153 +305,6 @@ impl HvCall { } } - fn get_segment_descriptor(segment_reg: &str) -> HvX64SegmentRegister { - unsafe { - use core::arch::asm; - let mut descriptor = HvX64SegmentRegister { - base: 0, - limit: 0, - selector: 0, - attributes: 0, - }; - match segment_reg { - "cs" => { - asm!("mov {0:x}, cs", out(reg) descriptor.selector, options(nomem, nostack)) - } - "ds" => { - asm!("mov {0:x}, ds", out(reg) descriptor.selector, options(nomem, nostack)) - } - "es" => { - asm!("mov {0:x}, es", out(reg) descriptor.selector, options(nomem, nostack)) - } - "ss" => { - asm!("mov {0:x}, ss", out(reg) descriptor.selector, options(nomem, nostack)) - } - "fs" => { - asm!("mov {0:x}, fs", out(reg) descriptor.selector, options(nomem, nostack)) - } - "gs" => { - asm!("mov {0:x}, gs", out(reg) descriptor.selector, options(nomem, nostack)) - } - "tr" => asm!("str {0:x}", out(reg) descriptor.selector, options(nomem, nostack)), - _ => panic!("Invalid segment register"), - } - - // For FS and GS in 64-bit mode, we can get the base directly via MSRs - if segment_reg == "fs" { - let mut base_low: u32; - let mut base_high: u32; - asm!( - "mov ecx, 0xC0000100", // FS_BASE MSR - "rdmsr", - out("eax") base_low, - out("edx") base_high, - options(nomem, nostack) - ); - descriptor.base = ((base_high as u64) << 32) | (base_low as u64); - } else if segment_reg == "gs" { - let mut base_low: u32; - let mut base_high: u32; - asm!( - "mov ecx, 0xC0000101", // GS_BASE MSR - "rdmsr", - out("eax") base_low, - out("edx") base_high, - options(nomem, nostack) - ); - descriptor.base = ((base_high as u64) << 32) | (base_low as u64); - } else { - // For other segments, need to look up in GDT/LDT - // Allocate 10 bytes for storing GDTR/LDTR content - let mut descriptor_table = [0u8; 10]; - - // Determine if selector is in GDT or LDT - let table_indicator = descriptor.selector & 0x04; - - if table_indicator == 0 { - // Get GDT base - asm!("sgdt [{}]", in(reg) descriptor_table.as_mut_ptr(), options(nostack)); - } else { - // Get LDT base - asm!("sldt [{}]", in(reg) descriptor_table.as_mut_ptr(), options(nostack)); - } - - // Extract GDT/LDT base (bytes 2-9 of descriptor_table) - let table_base = u64::from_ne_bytes([ - descriptor_table[2], - descriptor_table[3], - descriptor_table[4], - descriptor_table[5], - descriptor_table[6], - descriptor_table[7], - descriptor_table[8], - descriptor_table[9], - ]); - - // Calculate descriptor entry address - let index = (descriptor.selector & 0xFFF8) as u64; // Clear RPL and TI bits - let desc_addr = table_base + index; - - // Read the 8-byte descriptor - let desc_bytes = alloc::slice::from_raw_parts(desc_addr as *const u8, 8); - let desc_low = u32::from_ne_bytes([ - desc_bytes[0], - desc_bytes[1], - desc_bytes[2], - desc_bytes[3], - ]); - let desc_high = u32::from_ne_bytes([ - desc_bytes[4], - desc_bytes[5], - desc_bytes[6], - desc_bytes[7], - ]); - - // Extract base (bits 16-39 and 56-63) - let base_low = ((desc_low >> 16) & 0xFFFF) as u64; - let base_mid = (desc_high & 0xFF) as u64; - let base_high = ((desc_high >> 24) & 0xFF) as u64; - descriptor.base = base_low | (base_mid << 16) | (base_high << 24); - - // Extract limit (bits 0-15 and 48-51) - let limit_low = desc_low & 0xFFFF; - let limit_high = (desc_high >> 16) & 0x0F; - descriptor.limit = limit_low | (limit_high << 16); - - // Extract attributes (bits 40-47 and 52-55) - let attr_low = (desc_high >> 8) & 0xFF; - let attr_high = (desc_high >> 20) & 0x0F; - descriptor.attributes = (attr_low as u16) | ((attr_high as u16) << 8); - - // If G bit is set (bit 55), the limit is in 4K pages - if (desc_high & 0x00800000) != 0 { - descriptor.limit = (descriptor.limit << 12) | 0xFFF; - } - - // For TR, which is a system segment in 64-bit mode, read the second 8 bytes to get the high 32 bits of base - if segment_reg == "tr" { - // Check if it's a system descriptor (bit 4 of attributes is 0) - if (descriptor.attributes & 0x10) == 0 { - // Read the next 8 bytes of the descriptor (high part of 16-byte descriptor) - let high_desc_bytes = - alloc::slice::from_raw_parts((desc_addr + 8) as *const u8, 8); - let high_base = u32::from_ne_bytes([ - high_desc_bytes[0], - high_desc_bytes[1], - high_desc_bytes[2], - high_desc_bytes[3], - ]) as u64; - - // Combine with existing base to get full 64-bit base - descriptor.base |= high_base << 32; - } - } - } - - descriptor - } - } - #[cfg(target_arch = "x86_64")] /// Hypercall to get the current VTL VP context pub fn get_current_vtl_vp_context(&mut self) -> Result { @@ -749,6 +535,7 @@ impl HvCall { output.result() } + #[cfg(target_arch = "x86_64")] /// Sets multiple virtual processor (VP) registers for a given VP and VTL. pub fn set_vp_registers( &mut self, @@ -837,6 +624,67 @@ impl HvCall { output.result() } + #[cfg(target_arch = "aarch64")] + /// Sets multiple virtual processor (VP) registers for a given VP and VTL. + pub fn set_vp_registers( + &mut self, + vp: u32, + vtl: Option, + vp_context: Option, + ) -> Result<(), hvdef::HvError> { + const HEADER_SIZE: usize = size_of::(); + + let header = hvdef::hypercall::GetSetVpRegisters { + partition_id: hvdef::HV_PARTITION_ID_SELF, + vp_index: vp, + target_vtl: vtl.unwrap_or(HvInputVtl::CURRENT_VTL), + rsvd: [0; 3], + }; + + let _ = header.write_to_prefix(self.input_page().buffer.as_mut_slice()); + + let mut input_offset = HEADER_SIZE; + + let mut count = 0; + let mut write_reg = |reg_name: hvdef::HvRegisterName, reg_value: HvRegisterValue| { + let reg = hvdef::hypercall::HvRegisterAssoc { + name: reg_name, + pad: Default::default(), + value: reg_value, + }; + + let _ = reg.write_to_prefix(&mut self.input_page().buffer[input_offset..]); + + input_offset += size_of::(); + count += 1; + }; + // pub msr_cr_pat: u64, + + write_reg( + HvArm64RegisterName::MairEl1.into(), + vp_context.unwrap().mair_el1.into(), + ); + write_reg( + HvArm64RegisterName::XPc.into(), + vp_context.unwrap().pc.into(), + ); + write_reg( + HvArm64RegisterName::SctlrEl1.into(), + vp_context.unwrap().sctlr_el1.into(), + ); + write_reg( + HvArm64RegisterName::TcrEl1.into(), + vp_context.unwrap().tcr_el1.into(), + ); + write_reg( + HvArm64RegisterName::VbarEl1.into(), + vp_context.unwrap().vbar_el1.into(), + ); + let output = self.dispatch_hvcall(hvdef::HypercallCode::HvCallSetVpRegisters, Some(count)); + + output.result() + } + #[cfg(target_arch = "x86_64")] /// Starts a virtual processor (VP) with the specified VTL and context on x86_64. pub fn start_virtual_processor( @@ -865,6 +713,34 @@ impl HvCall { } } + #[cfg(target_arch = "aarch64")] + /// Starts a virtual processor (VP) with the specified VTL and context on aarch64. + pub fn start_virtual_processor( + &mut self, + vp_index: u32, + target_vtl: Vtl, + vp_context: Option, + ) -> Result<(), hvdef::HvError> { + let header = hvdef::hypercall::StartVirtualProcessorArm64 { + partition_id: hvdef::HV_PARTITION_ID_SELF, + vp_index, + target_vtl: target_vtl.into(), + vp_context: vp_context.unwrap_or(zerocopy::FromZeros::new_zeroed()), + rsvd0: 0u8, + rsvd1: 0u16, + }; + + header + .write_to_prefix(self.input_page().buffer.as_mut_slice()) + .expect("size of start_virtual_processor header is not correct"); + + let output = self.dispatch_hvcall(hvdef::HypercallCode::HvCallStartVirtualProcessor, None); + match output.result() { + Ok(()) => Ok(()), + err => panic!("Failed to start virtual processor: {:?}", err), + } + } + /// Call before jumping to kernel. pub fn uninitialize(&mut self) { crate::arch::hypercall::uninitialize(); diff --git a/opentmk/src/platform/hypvctx.rs b/opentmk/src/platform/hypvctx.rs index 703a0e9803..2eb342a47b 100644 --- a/opentmk/src/platform/hypvctx.rs +++ b/opentmk/src/platform/hypvctx.rs @@ -1,25 +1,35 @@ +#![cfg_attr(target_arch = "aarch64", expect(unused_imports))] use alloc::alloc::alloc; use alloc::boxed::Box; use alloc::collections::btree_map::BTreeMap; use alloc::collections::btree_set::BTreeSet; use alloc::collections::linked_list::LinkedList; +#[cfg(target_arch = "aarch64")] +use hvdef::hypercall::InitialVpContextArm64; use core::alloc::Layout; use core::arch::asm; use core::fmt::Display; use core::ops::Range; use hvdef::hypercall::HvInputVtl; +#[cfg(target_arch = "x86_64")] use hvdef::hypercall::InitialVpContextX64; use hvdef::AlignedU128; use hvdef::Vtl; use memory_range::MemoryRange; +#[cfg(target_arch = "x86_64")] use minimal_rt::arch::msr::read_msr; +#[cfg(target_arch = "x86_64")] use minimal_rt::arch::msr::write_msr; use spin::Mutex; #[cfg(feature = "nightly")] +#[cfg(target_arch = "x86_64")] use crate::context::InterruptPlatformTrait; +#[cfg(target_arch = "x86_64")] use crate::context::MsrPlatformTrait; +#[cfg(feature = "nightly")] +#[cfg(target_arch = "x86_64")] use crate::context::SecureInterceptPlatformTrait; use crate::context::VirtualProcessorPlatformTrait; use crate::context::VpExecutor; @@ -28,13 +38,11 @@ use crate::hypercall::HvCall; use crate::tmkdefs::TmkError; use crate::tmkdefs::TmkResult; -const ALIGNMENT: usize = 4096; - -type ComandTable = BTreeMap, Vtl)>>; -static mut CMD: Mutex = Mutex::new(BTreeMap::new()); +type CommandTable = BTreeMap, Vtl)>>; +static mut CMD: Mutex = Mutex::new(BTreeMap::new()); #[expect(static_mut_refs)] -fn cmdt() -> &'static Mutex { +fn cmdt() -> &'static Mutex { unsafe { &CMD } } @@ -66,11 +74,13 @@ impl Display for HvTestCtx { } } +#[cfg(feature = "nightly")] +#[cfg(target_arch = "x86_64")] impl SecureInterceptPlatformTrait for HvTestCtx { /// Configure the Secure Interrupt Message Page (SIMP) and the first /// SynIC interrupt (SINT0) so that the hypervisor can vector /// hypervisor side notifications back to the guest. - /// Returns [`TmkResult::Err`] if the allocation of the SIMP buffer fails. + /// Returns [`TmkError`] if the allocation of the SIMP buffer fails. fn setup_secure_intercept(&mut self, interrupt_idx: u8) -> TmkResult<()> { let layout = Layout::from_size_align(4096, ALIGNMENT) .map_err(|_| TmkError::AllocationFailed)?; @@ -95,6 +105,7 @@ impl SecureInterceptPlatformTrait for HvTestCtx { } #[cfg(feature = "nightly")] +#[cfg(target_arch = "x86_64")] impl InterruptPlatformTrait for HvTestCtx { /// Install an interrupt handler for the supplied vector on x86-64. /// For non-x86-64 targets the call returns @@ -108,7 +119,7 @@ impl InterruptPlatformTrait for HvTestCtx { #[cfg(not(target_arch = "x86_64"))] { - Err(TmkError(TmkError::NotImplemented)) + Err(TmkError::NotImplemented) } } @@ -120,6 +131,7 @@ impl InterruptPlatformTrait for HvTestCtx { } } +#[cfg(target_arch = "x86_64")] impl MsrPlatformTrait for HvTestCtx { /// Read an MSR directly from the CPU and return the raw value. fn read_msr(&mut self, msr: u32) -> TmkResult { @@ -148,8 +160,7 @@ impl VirtualProcessorPlatformTrait for HvTestCtx { #[cfg(target_arch = "aarch64")] { - use hvdef::HvAarch64RegisterName; - let reg = HvAarch64RegisterName(reg); + let reg = hvdef::HvArm64RegisterName(reg); let val = self.hvcall.get_register(reg.into(), None)?.as_u128(); Ok(val) } @@ -170,7 +181,7 @@ impl VirtualProcessorPlatformTrait for HvTestCtx { #[cfg(not(target_arch = "x86_64"))] { - Err(TmkError(TmkError::NotImplemented)) + Err(TmkError::NotImplemented) } } @@ -281,7 +292,7 @@ impl VirtualProcessorPlatformTrait for HvTestCtx { cmd: VpExecutor, ) -> TmkResult<()> { let (vp_index, vtl, _cmd) = cmd.get(); - let vp_ctx: InitialVpContextX64 = self.get_default_context(vtl)?; + let vp_ctx = self.get_default_context(vtl)?; self.hvcall .start_virtual_processor(vp_index, vtl, Some(vp_ctx))?; Ok(()) @@ -507,6 +518,7 @@ fn vtl_transform(vtl: Vtl) -> HvInputVtl { .with_use_target_vtl(true) } +#[cfg_attr(target_arch = "aarch64", expect(dead_code))] impl HvTestCtx { /// Construct an *un-initialised* test context. /// Call [`HvTestCtx::init`] before using the value. @@ -537,11 +549,21 @@ impl HvTestCtx { // let reg = reg.as_u64(); // self.my_vp_idx = reg as u32; - let result = unsafe { core::arch::x86_64::__cpuid(0x1) }; - self.my_vp_idx = (result.ebx >> 24) & 0xFF; + self.my_vp_idx = Self::get_vp_idx(); Ok(()) } + #[cfg(target_arch = "x86_64")] + fn get_vp_idx() -> u32 { + let result = unsafe { core::arch::x86_64::__cpuid(0x1) }; + (result.ebx >> 24) & 0xFF + } + + #[cfg(target_arch = "aarch64")] + fn get_vp_idx() -> u32 { + unimplemented!() + } + fn secure_exec_handler() { HvTestCtx::exec_handler(Vtl::Vtl1); } @@ -556,10 +578,6 @@ impl HvTestCtx { fn exec_handler(vtl: Vtl) { let mut ctx = HvTestCtx::new(); ctx.init(vtl).expect("error: failed to init on a VP"); - - ctx.print_rbp(); - ctx.print_rsp(); - loop { let mut vtl: Option = None; let mut cmd: Option> = None; @@ -606,6 +624,14 @@ impl HvTestCtx { self.run_fn_with_current_context(handler) } + #[cfg(target_arch = "aarch64")] + /// Capture the current VP context, patch the entry point and stack + /// so that the new VP starts in `exec_handler`. + fn get_default_context(&mut self, _vtl: Vtl) -> Result { + use core::panic; + panic!("aarch64 not implemented"); + } + #[cfg(target_arch = "x86_64")] /// Helper to wrap an arbitrary function inside a captured VP context /// that can later be used to start a new VP/VTL instance. diff --git a/opentmk/src/tmk_logger.rs b/opentmk/src/tmk_logger.rs index 2705b715f6..41f4d02b0d 100644 --- a/opentmk/src/tmk_logger.rs +++ b/opentmk/src/tmk_logger.rs @@ -8,8 +8,10 @@ use serde::Serialize; use spin::Mutex; use spin::MutexGuard; +#[cfg(target_arch = "x86_64")] use crate::arch::serial::InstrIoAccess; use crate::arch::serial::Serial; +#[cfg(target_arch = "x86_64")] use crate::arch::serial::SerialPort; #[derive(Serialize)] @@ -91,10 +93,16 @@ where fn flush(&self) {} } +#[cfg(target_arch = "x86_64")] type SerialPortWriter = Serial; +#[cfg(target_arch = "x86_64")] pub static LOGGER: TmkLogger> = TmkLogger::new(SerialPortWriter::new(SerialPort::COM2, InstrIoAccess)); -pub fn init() -> Result<(), SetLoggerError> { +#[cfg(target_arch = "aarch64")] +pub static LOGGER: TmkLogger> = + TmkLogger::new(Serial{}); + +pub fn init() -> Result<(), SetLoggerError> { log::set_logger(&LOGGER).map(|()| log::set_max_level(log::LevelFilter::Debug)) } diff --git a/opentmk/src/tmkdefs.rs b/opentmk/src/tmkdefs.rs index 5d5381be05..c7cff72626 100644 --- a/opentmk/src/tmkdefs.rs +++ b/opentmk/src/tmkdefs.rs @@ -76,6 +76,7 @@ pub enum TmkError { Timeout, VtlAlreadyEnabled, UnknownRegisterName, + NotImplemented, } impl Display for TmkError { diff --git a/opentmk/src/uefi/rt.rs b/opentmk/src/uefi/rt.rs index f065229d72..8712adeff3 100644 --- a/opentmk/src/uefi/rt.rs +++ b/opentmk/src/uefi/rt.rs @@ -6,6 +6,7 @@ #[panic_handler] fn panic_handler(panic: &core::panic::PanicInfo<'_>) -> ! { log::error!("Panic at runtime: {}", panic); + crate::tmk_assert!(false, "panics are not expected at runtime"); log::warn!("TEST_END"); loop {} } diff --git a/support/nostd_spin_channel/src/lib.rs b/support/nostd_spin_channel/src/lib.rs index 63f82860f8..545e5c5fc8 100644 --- a/support/nostd_spin_channel/src/lib.rs +++ b/support/nostd_spin_channel/src/lib.rs @@ -1,12 +1,13 @@ #![no_std] #![allow(unsafe_code)] extern crate alloc; -use core::sync::atomic::{AtomicUsize, Ordering}; +use core::sync::atomic::AtomicUsize; +use core::sync::atomic::Ordering; use spin::Mutex; -use alloc::{sync::Arc, vec::Vec}; +use alloc::sync::Arc; +use alloc::vec::Vec; use alloc::collections::VecDeque; use thiserror::Error; -use core::fmt; /// An unbounded channel implementation with priority send capability. /// This implementation works in no_std environments using spin-rs. From 9bdf302639bb864ce6320ebd7f9af15ff38d3172 Mon Sep 17 00:00:00 2001 From: Mayank Kumar Date: Wed, 27 Aug 2025 19:05:29 +0000 Subject: [PATCH 20/23] chore: resolve copilot comments --- opentmk/src/platform/hypvctx.rs | 14 +++++++------- opentmk/src/tests/hyperv/hv_memory_protect_read.rs | 2 +- .../src/tests/hyperv/hv_memory_protect_write.rs | 2 +- opentmk/src/tests/hyperv/hv_tpm_read_cvm.rs | 2 +- opentmk/src/tests/hyperv/hv_tpm_write_cvm.rs | 2 +- opentmk/src/tmk_assert.rs | 2 +- opentmk/src/tmk_logger.rs | 10 +++++----- support/nostd_spin_channel/src/lib.rs | 2 +- 8 files changed, 18 insertions(+), 18 deletions(-) diff --git a/opentmk/src/platform/hypvctx.rs b/opentmk/src/platform/hypvctx.rs index 2eb342a47b..b38f233cbd 100644 --- a/opentmk/src/platform/hypvctx.rs +++ b/opentmk/src/platform/hypvctx.rs @@ -58,8 +58,8 @@ fn register_command_queue(vp_index: u32) { pub struct HvTestCtx { pub hvcall: HvCall, - // BUG: make this static - pub vp_runing: BTreeSet, + // TODO: make this static, this could lead to bugs when init a VP from AP + pub vp_running: BTreeSet, pub my_vp_idx: u32, pub my_vtl: Vtl, } @@ -82,7 +82,7 @@ impl SecureInterceptPlatformTrait for HvTestCtx { /// hypervisor side notifications back to the guest. /// Returns [`TmkError`] if the allocation of the SIMP buffer fails. fn setup_secure_intercept(&mut self, interrupt_idx: u8) -> TmkResult<()> { - let layout = Layout::from_size_align(4096, ALIGNMENT) + let layout = Layout::from_size_align(4096, 4096) .map_err(|_| TmkError::AllocationFailed)?; let ptr = unsafe { alloc(layout) }; @@ -215,7 +215,7 @@ impl VirtualProcessorPlatformTrait for HvTestCtx { if vtl >= Vtl::Vtl2 { return Err(TmkError::InvalidParameter); } - let is_vp_running = self.vp_runing.get(&vp_index); + let is_vp_running = self.vp_running.get(&vp_index); if let Some(_running_vtl) = is_vp_running { log::debug!("both vtl0 and vtl1 are running for VP: {:?}", vp_index); } else { @@ -232,7 +232,7 @@ impl VirtualProcessorPlatformTrait for HvTestCtx { log::info!("self addr: {:p}", self as *const _); self.switch_to_high_vtl(); log::info!("self addr after switch: {:p}", self as *const _); - self.vp_runing.insert(vp_index); + self.vp_running.insert(vp_index); } else { let (tx, rx) = nostd_spin_channel::Channel::>::new().split(); let self_vp_idx = self.my_vp_idx; @@ -266,7 +266,7 @@ impl VirtualProcessorPlatformTrait for HvTestCtx { if let Ok(r) = rx { r?; } - self.vp_runing.insert(vp_index); + self.vp_running.insert(vp_index); } } cmdt() @@ -525,7 +525,7 @@ impl HvTestCtx { pub const fn new() -> Self { HvTestCtx { hvcall: HvCall::new(), - vp_runing: BTreeSet::new(), + vp_running: BTreeSet::new(), my_vp_idx: 0, my_vtl: Vtl::Vtl0, } diff --git a/opentmk/src/tests/hyperv/hv_memory_protect_read.rs b/opentmk/src/tests/hyperv/hv_memory_protect_read.rs index fc6aac1419..23bce2877d 100644 --- a/opentmk/src/tests/hyperv/hv_memory_protect_read.rs +++ b/opentmk/src/tests/hyperv/hv_memory_protect_read.rs @@ -41,7 +41,7 @@ where let vp_count = ctx.get_vp_count(); tmk_assert!(vp_count.is_ok(), "get_vp_count should succeed"); let vp_count = vp_count.unwrap(); - tmk_assert!(vp_count == 4, "vp count should be 8"); + tmk_assert!(vp_count == 4, "vp count should be 4"); let r = ctx.setup_interrupt_handler(); tmk_assert!(r.is_ok(), "setup_interrupt_handler should succeed"); diff --git a/opentmk/src/tests/hyperv/hv_memory_protect_write.rs b/opentmk/src/tests/hyperv/hv_memory_protect_write.rs index 70124aed5b..6c3ded2849 100644 --- a/opentmk/src/tests/hyperv/hv_memory_protect_write.rs +++ b/opentmk/src/tests/hyperv/hv_memory_protect_write.rs @@ -40,7 +40,7 @@ where let vp_count = ctx.get_vp_count(); tmk_assert!(vp_count.is_ok(), "get_vp_count should succeed"); let vp_count = vp_count.unwrap(); - tmk_assert!(vp_count == 4, "vp count should be 8"); + tmk_assert!(vp_count == 4, "vp count should be 4"); let r = ctx.setup_interrupt_handler(); tmk_assert!(r.is_ok(), "setup_interrupt_handler should succeed"); diff --git a/opentmk/src/tests/hyperv/hv_tpm_read_cvm.rs b/opentmk/src/tests/hyperv/hv_tpm_read_cvm.rs index f8c27baff3..1e78f14a02 100644 --- a/opentmk/src/tests/hyperv/hv_tpm_read_cvm.rs +++ b/opentmk/src/tests/hyperv/hv_tpm_read_cvm.rs @@ -41,7 +41,7 @@ where let vp_count = ctx.get_vp_count(); tmk_assert!(vp_count.is_ok(), "get_vp_count should succeed"); let vp_count = vp_count.unwrap(); - tmk_assert!(vp_count == 4, "vp count should be 8"); + tmk_assert!(vp_count == 4, "vp count should be 4"); let r = ctx.setup_interrupt_handler(); tmk_assert!(r.is_ok(), "setup_interrupt_handler should succeed"); log::info!("set intercept handler successfully!"); diff --git a/opentmk/src/tests/hyperv/hv_tpm_write_cvm.rs b/opentmk/src/tests/hyperv/hv_tpm_write_cvm.rs index c659c2c0b8..6545a0938a 100644 --- a/opentmk/src/tests/hyperv/hv_tpm_write_cvm.rs +++ b/opentmk/src/tests/hyperv/hv_tpm_write_cvm.rs @@ -41,7 +41,7 @@ where let vp_count = ctx.get_vp_count(); tmk_assert!(vp_count.is_ok(), "get_vp_count should succeed"); let vp_count = vp_count.unwrap(); - tmk_assert!(vp_count == 4, "vp count should be 8"); + tmk_assert!(vp_count == 4, "vp count should be 4"); let r = ctx.setup_interrupt_handler(); tmk_assert!(r.is_ok(), "setup_interrupt_handler should succeed"); log::info!("set intercept handler successfully!"); diff --git a/opentmk/src/tmk_assert.rs b/opentmk/src/tmk_assert.rs index 04cf9544f6..d4561008f8 100644 --- a/opentmk/src/tmk_assert.rs +++ b/opentmk/src/tmk_assert.rs @@ -62,7 +62,7 @@ where } pub(crate) fn write_str(s: &str) { - _ = crate::tmk_logger::LOGGER.get_writter().write_str(s); + _ = crate::tmk_logger::LOGGER.get_writer().write_str(s); } #[macro_export] diff --git a/opentmk/src/tmk_logger.rs b/opentmk/src/tmk_logger.rs index 41f4d02b0d..4385bb14cc 100644 --- a/opentmk/src/tmk_logger.rs +++ b/opentmk/src/tmk_logger.rs @@ -50,7 +50,7 @@ pub(crate) fn format_log_string_to_json( } pub struct TmkLogger { - pub writter: T, + pub writer: T, } impl TmkLogger> @@ -59,15 +59,15 @@ where { pub const fn new(provider: T) -> Self { TmkLogger { - writter: Mutex::new(provider), + writer: Mutex::new(provider), } } - pub fn get_writter(&self) -> MutexGuard<'_, T> + pub fn get_writer(&self) -> MutexGuard<'_, T> where T: Write + Send, { - self.writter.lock() + self.writer.lock() } } @@ -87,7 +87,7 @@ where record.line().unwrap_or_default() ); let str = format_log_string_to_json(&str, &line, true, record.level()); - _ = self.writter.lock().write_str(str.as_str()); + _ = self.writer.lock().write_str(str.as_str()); } fn flush(&self) {} diff --git a/support/nostd_spin_channel/src/lib.rs b/support/nostd_spin_channel/src/lib.rs index 545e5c5fc8..d0051a3f96 100644 --- a/support/nostd_spin_channel/src/lib.rs +++ b/support/nostd_spin_channel/src/lib.rs @@ -30,7 +30,7 @@ struct ChannelInner { // SAFETY: ChannelInner is safe to share across threads as it uses atomic operations for senders and receivers counts unsafe impl Send for ChannelInner {} -// SAFETY: ChannelInner is safe to used across threads as it uses atomic operations for senders and receivers counts +// SAFETY: ChannelInner is safe to use across threads as it uses atomic operations for senders and receivers counts unsafe impl Sync for ChannelInner {} /// Error type for sending operations From fc0b866cbcb74ec868b72c80f85c6005117db335 Mon Sep 17 00:00:00 2001 From: Mayank Kumar Date: Wed, 27 Aug 2025 19:19:09 +0000 Subject: [PATCH 21/23] chore: remove default impl for channel --- support/nostd_spin_channel/src/lib.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/support/nostd_spin_channel/src/lib.rs b/support/nostd_spin_channel/src/lib.rs index d0051a3f96..11c0c59ea0 100644 --- a/support/nostd_spin_channel/src/lib.rs +++ b/support/nostd_spin_channel/src/lib.rs @@ -305,10 +305,4 @@ impl Drop for Receiver { fn drop(&mut self) { self.inner.receivers.fetch_sub(1, Ordering::SeqCst); } -} - -impl Default for Channel { - fn default() -> Self { - Self::new() - } } \ No newline at end of file From 7019671e42bf6d9f2c4c478856bccd6f755011a4 Mon Sep 17 00:00:00 2001 From: Mayank Kumar Date: Wed, 27 Aug 2025 19:30:59 +0000 Subject: [PATCH 22/23] cleanup --- opentmk/opentmk/src/context.rs | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 opentmk/opentmk/src/context.rs diff --git a/opentmk/opentmk/src/context.rs b/opentmk/opentmk/src/context.rs deleted file mode 100644 index e69de29bb2..0000000000 From 7d70c67e60b821b38ab273941b5d9f4d3b8daf70 Mon Sep 17 00:00:00 2001 From: Mayank Kumar Date: Thu, 28 Aug 2025 09:02:04 +0000 Subject: [PATCH 23/23] fix: compiler error --- Cargo.toml | 2 +- opentmk/Cargo.toml | 20 ++++---------------- opentmk/build_deploy.sh | 2 +- 3 files changed, 6 insertions(+), 18 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index fbefe739da..77ca623586 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -530,7 +530,7 @@ winapi = "0.3" windows = "0.59" windows-service = "0.7" windows-sys = "0.52" -x86_64 = "0.15.2" +x86_64 = { version = "0.15.2", default-features = false } xshell = "=0.2.2" # pin to 0.2.2 to work around https://github.com/matklad/xshell/issues/63 xshell-macros = "0.2" # We add the derive feature here since the vast majority of our crates use it. diff --git a/opentmk/Cargo.toml b/opentmk/Cargo.toml index 3997b98a93..9521bc2a74 100644 --- a/opentmk/Cargo.toml +++ b/opentmk/Cargo.toml @@ -8,15 +8,15 @@ rust-version.workspace = true [features] default = [] -nightly = [] - +nightly = ["x86_64-nightly"] +x86_64-nightly = ["x86_64/nightly"] [dependencies] arrayvec.workspace = true bitfield-struct.workspace = true cfg-if.workspace = true hvdef = {workspace = true} -iced-x86 = { workspace = true, features = ["decoder", "nasm", "no_std"]} +iced-x86 = { workspace = true, features = ["no_std"] } lazy_static = { workspace = true, features = ["spin_no_std"] } linked_list_allocator.workspace = true log.workspace = true @@ -27,7 +27,7 @@ serde = { version = "1.0", default-features = false, features = ["derive"]} serde_json = { version = "1.0", default-features = false, features = ["alloc"] } thiserror.workspace = true uefi = { workspace = true, features = ["alloc"] } -x86_64.workspace = true +x86_64 = { workspace = true, features = ["instructions"] } x86defs.workspace = true zerocopy.workspace = true nostd_spin_channel.workspace = true @@ -37,15 +37,3 @@ workspace = true [build-dependencies] minimal_rt_build.workspace = true - - -[profile.release] -opt-level = 0 # No optimizations (same as debug) -debug = true # Include debug symbols -debug-assertions = true # Enable debug assertions -overflow-checks = true # Enable integer overflow checks -ltso = "offz" # Disable link-time optrimization -codegen-units = 256 # Use more codegen units (faster compilation) - -[profile.release.package."*"] -opt-level = 0 diff --git a/opentmk/build_deploy.sh b/opentmk/build_deploy.sh index 08e4f9a5b6..8ac648d74c 100755 --- a/opentmk/build_deploy.sh +++ b/opentmk/build_deploy.sh @@ -1,4 +1,4 @@ -RUST_BACKTRACE=1 CARGO_PROFILE_RELEASE_force_frame_pointers=yes cargo build -p opentmk --target aarch64-unknown-uefi --release #--target-dir ./target/x86_64-unknown-uefi/debug +RUST_BACKTRACE=1 CARGO_PROFILE_RELEASE_force_frame_pointers=yes cargo +stable build -p opentmk --target x86_64-unknown-uefi --release #--target-dir ./target/x86_64-unknown-uefi/debug cargo xtask guest-test uefi --bootx64 ~/projects-local/openvmm/target/x86_64-unknown-uefi/release/opentmk.efi qemu-img convert -f raw -O vhdx ~/projects-local/openvmm/target/x86_64-unknown-uefi/release/opentmk.img ~/projects/opentmk.vhdx #CARGO_PROFILE_RELEASE_OPT_LEVEL=0