diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c4893ad3c9..3a6b926987 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -254,12 +254,6 @@ jobs: # - `litebox_platform_lvbs` has a custom target (`no_std`), so it does # not work with the current no_std checker. # - # - `litebox_platform_multiplex` is allowed to have `std` access (in - # its default feature set) because `litebox_platform_linux_userland` - # has access, and this is just a multiplexer. Ideally, we'd do a - # more precise check, but as long as we are tracking the underlying - # platforms, we are unlikely to hit any significant issues here. - # # - `litebox_runner_linux_on_windows_userland` is allowed to have `std` # access since it needs to actually access the file-system, pull in # relevant files, and then actually trigger LiteBox itself. @@ -303,7 +297,6 @@ jobs: -not -path './litebox_platform_windows_userland/Cargo.toml' \ -not -path './litebox_runner_linux_on_windows_userland/Cargo.toml' \ -not -path './litebox_platform_lvbs/Cargo.toml' \ - -not -path './litebox_platform_multiplex/Cargo.toml' \ -not -path './litebox_runner_linux_userland/Cargo.toml' \ -not -path './litebox_runner_lvbs/Cargo.toml' \ -not -path './litebox_runner_optee_on_linux_userland/Cargo.toml' \ diff --git a/Cargo.lock b/Cargo.lock index 5c60bf4d6b..9f312d91ba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1646,19 +1646,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "litebox_platform_multiplex" -version = "0.1.0" -dependencies = [ - "cfg-if", - "litebox", - "litebox_platform_linux_kernel", - "litebox_platform_linux_userland", - "litebox_platform_lvbs", - "litebox_platform_windows_userland", - "once_cell", -] - [[package]] name = "litebox_platform_windows_userland" version = "0.1.0" @@ -1714,11 +1701,11 @@ dependencies = [ "litebox_common_lvbs", "litebox_common_optee", "litebox_platform_lvbs", - "litebox_platform_multiplex", "litebox_service_heki", "litebox_shim_optee", "litebox_util_log", "log", + "once_cell", "spin 0.10.0", "x86_64", ] @@ -1728,14 +1715,12 @@ name = "litebox_runner_optee_on_linux_userland" version = "0.1.0" dependencies = [ "anyhow", - "arrayvec", "base64", "clap", "litebox", "litebox_common_linux", "litebox_common_optee", "litebox_platform_linux_userland", - "litebox_platform_multiplex", "litebox_shim_optee", "litebox_syscall_rewriter", "litebox_util_log", @@ -1814,7 +1799,6 @@ name = "litebox_shim_optee" version = "0.1.0" dependencies = [ "aes", - "arrayvec", "ctr", "elf", "hashbrown", @@ -1822,7 +1806,7 @@ dependencies = [ "litebox", "litebox_common_linux", "litebox_common_optee", - "litebox_platform_multiplex", + "litebox_platform_linux_userland", "litebox_util_log", "num_enum", "once_cell", diff --git a/Cargo.toml b/Cargo.toml index 9715547c3a..420e16337c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,6 @@ members = [ "litebox_platform_linux_userland", "litebox_platform_windows_userland", "litebox_platform_lvbs", - "litebox_platform_multiplex", "litebox_runner_linux_userland", "litebox_runner_linux_on_windows_userland", "litebox_runner_lvbs", @@ -35,7 +34,6 @@ default-members = [ "litebox_platform_linux_userland", "litebox_platform_windows_userland", "litebox_platform_lvbs", - "litebox_platform_multiplex", "litebox_runner_linux_userland", "litebox_runner_linux_on_windows_userland", "litebox_shim_linux", diff --git a/dev_tests/src/ratchet.rs b/dev_tests/src/ratchet.rs index 54a30a964e..6338e825dc 100644 --- a/dev_tests/src/ratchet.rs +++ b/dev_tests/src/ratchet.rs @@ -37,10 +37,9 @@ fn ratchet_globals() -> Result<()> { ("litebox/", 9), ("litebox_platform_linux_kernel/", 6), ("litebox_platform_linux_userland/", 5), - ("litebox_platform_lvbs/", 23), - ("litebox_platform_multiplex/", 1), + ("litebox_platform_lvbs/", 22), ("litebox_platform_windows_userland/", 8), - ("litebox_runner_lvbs/", 6), + ("litebox_runner_lvbs/", 8), ("litebox_runner_snp/", 2), ("litebox_shim_linux/", 1), ("litebox_shim_optee/", 5), diff --git a/litebox_common_linux/src/physical_pointers.rs b/litebox_common_linux/src/physical_pointers.rs index 1bde38e6e5..300a901e68 100644 --- a/litebox_common_linux/src/physical_pointers.rs +++ b/litebox_common_linux/src/physical_pointers.rs @@ -34,16 +34,11 @@ //! mapping cannot alias any Rust reference. use crate::vmap::{ - GlobalVmapManager, PhysPageAddr, PhysPageMapInfo, PhysPageMapPermissions, PhysPointerError, - VmapManager, + PhysPageAddr, PhysPageMapInfo, PhysPageMapPermissions, PhysPointerError, VmapManager, }; use core::marker::PhantomData; use zerocopy::{FromBytes, IntoBytes}; -/// The concrete [`PhysPageMapInfo`] produced by the `VmapManager` behind a [`GlobalVmapManager`]. -type MapInfoOf = - <>::Manager as VmapManager>::MapInfo; - /// Allocate a zeroed `Box` on the heap. /// /// # Panics @@ -80,24 +75,24 @@ fn align_down(address: usize, align: usize) -> usize { /// Write methods require `T: IntoBytes` because values are written by copying their byte /// representation. /// +/// - `vmgr`: A *borrowed* [`VmapManager`] used to map and unmap `pages`. /// - `pages`: An array of page-aligned physical addresses. We expect physical addresses in this array are /// virtually contiguous. /// - `offset`: The offset within `pages[0]` where the object starts. It should be smaller than `ALIGN`. /// - `count`: The number of objects of type `T` that can be accessed from this pointer. /// - `T`: The type of the object being pointed to. `pages` with respect to `offset` should cover enough /// memory for an object of type `T`. -#[repr(C)] -pub struct PhysMutPtr> { +pub struct PhysMutPtr<'v, V: VmapManager, T, const ALIGN: usize> { + vmgr: &'v V, pages: alloc::boxed::Box<[PhysPageAddr]>, offset: usize, count: usize, _type: PhantomData, - _vmap: PhantomData, } -impl PhysMutPtr +impl<'v, V, T, const ALIGN: usize> PhysMutPtr<'v, V, T, ALIGN> where - V: GlobalVmapManager, + V: VmapManager, { /// Compile-time guard rejecting zero-sized types. /// @@ -119,12 +114,17 @@ where /// memory. This is sound because the foreign `T` is never dereferenced as a Rust reference or /// via a typed load/store: all access goes through `copy_in`/`copy_out`, which cast the /// mapped pointer to `*mut u8` and perform a byte-granular, unaligned-safe `memcpy_fallible`. - pub fn new(pages: &[PhysPageAddr], offset: usize) -> Result { - Self::from_boxed(pages.into(), offset) + pub fn new( + vmgr: &'v V, + pages: &[PhysPageAddr], + offset: usize, + ) -> Result { + Self::from_boxed(vmgr, pages.into(), offset) } /// Create a new `PhysMutPtr` from an owned page list, consuming it without copying. fn from_boxed( + vmgr: &'v V, pages: alloc::boxed::Box<[PhysPageAddr]>, offset: usize, ) -> Result { @@ -148,13 +148,13 @@ where core::mem::size_of::(), )); } - V::manager().validate_unowned(&pages)?; + vmgr.validate_unowned(&pages)?; Ok(Self { + vmgr, offset, count: size / core::mem::size_of::(), pages, _type: PhantomData, - _vmap: PhantomData, }) } @@ -162,7 +162,11 @@ where /// /// This is a shortcut for /// `PhysMutPtr::new([align_down(pa), align_down(pa) + ALIGN, ..., align_up(pa + bytes) - ALIGN], pa % ALIGN)`. - pub fn with_contiguous_pages(pa: usize, bytes: usize) -> Result { + pub fn with_contiguous_pages( + vmgr: &'v V, + pa: usize, + bytes: usize, + ) -> Result { if bytes < core::mem::size_of::() { return Err(PhysPointerError::InsufficientPhysicalPages( bytes, @@ -189,7 +193,7 @@ where .ok_or(PhysPointerError::Overflow)?; } // reuse the allocation - Self::from_boxed(pages.into_boxed_slice(), pa - start_page) + Self::from_boxed(vmgr, pages.into_boxed_slice(), pa - start_page) } /// Create a new `PhysMutPtr` from the given physical address for a single object. @@ -197,8 +201,8 @@ where /// This is a shortcut for `PhysMutPtr::with_contiguous_pages(pa, size_of::())`. /// /// Note: This module doesn't provide `as_usize` because LiteBox should not dereference physical addresses directly. - pub fn with_usize(pa: usize) -> Result { - Self::with_contiguous_pages(pa, core::mem::size_of::()) + pub fn with_usize(vmgr: &'v V, pa: usize) -> Result { + Self::with_contiguous_pages(vmgr, pa, core::mem::size_of::()) } /// Read the value at the given offset from the physical pointer. @@ -327,7 +331,7 @@ where count: usize, size: usize, perms: PhysPageMapPermissions, - ) -> Result, PhysPointerError> { + ) -> Result, PhysPointerError> { let skip = self .offset .checked_add( @@ -344,10 +348,10 @@ where let map_info = self.map_range(start, end, perms)?; let ptr = map_info.base().wrapping_add(skip % ALIGN).cast::(); Ok(MappedGuard { + vmgr: self.vmgr, map_info: Some(map_info), ptr, size, - _owner: PhantomData, }) } @@ -357,7 +361,7 @@ where start: usize, end: usize, perms: PhysPageMapPermissions, - ) -> Result, PhysPointerError> { + ) -> Result { if start >= end || end > self.pages.len() { return Err(PhysPointerError::IndexOutOfBounds(end, self.pages.len())); } @@ -370,14 +374,14 @@ where // The platform `VmapManager` must map them only in a foreign-memory VA range, disjoint // from LiteBox-owned Rust objects. This caller never creates Rust references from the // returned pointer; `MappedGuard` uses it only for fault-tolerant raw byte copies. - unsafe { V::manager().vmap(sub_pages, perms) } + unsafe { self.vmgr.vmap(sub_pages, perms) } } } /// RAII guard that unmaps physical pages when dropped. /// -/// Created by `map_and_get_ptr_guard`. Its lifetime is tied to the parent -/// `PhysMutPtr`, and it owns the map info for the duration of the temporary mapping. +/// Created by `map_and_get_ptr_guard`. Its lifetime is tied to the guard to a borrow of +/// the parent `PhysMutPtr`. /// /// # Invariant /// @@ -385,14 +389,14 @@ where /// at `ptr` lie within that mapping. The mapping refers to foreign (non-Rust) physical /// memory that another core may unmap concurrently, so `ptr` must only ever be accessed /// through [`Self::copy_in`]/[`Self::copy_out`], which perform fault-tolerant copies. -struct MappedGuard<'a, T, const ALIGN: usize, V: GlobalVmapManager> { - map_info: Option>, +struct MappedGuard<'v, V: VmapManager, T, const ALIGN: usize> { + vmgr: &'v V, + map_info: Option, ptr: *mut T, size: usize, - _owner: PhantomData<&'a PhysMutPtr>, } -impl> MappedGuard<'_, T, ALIGN, V> { +impl, T, const ALIGN: usize> MappedGuard<'_, V, T, ALIGN> { /// Copy the `self.size` mapped bytes out into `dst`. /// /// This is the only path through which the raw mapped pointer is dereferenced. @@ -426,20 +430,20 @@ impl> MappedGuard<'_, T, ALIG } } -impl> Drop for MappedGuard<'_, T, ALIGN, V> { +impl, T, const ALIGN: usize> Drop for MappedGuard<'_, V, T, ALIGN> { fn drop(&mut self) { // SAFETY: The platform is expected to handle unmapping safely. Drop cannot // report errors. If unmapping fails, drop the returned private map_info; // platform-specific resources that cannot be reclaimed are handled by the // platform `vunmap` implementation. if let Some(map_info) = self.map_info.take() { - let _ = unsafe { V::manager().vunmap(map_info) }; + let _ = unsafe { self.vmgr.vunmap(map_info) }; } } } -impl> core::fmt::Debug - for PhysMutPtr +impl, T, const ALIGN: usize> core::fmt::Debug + for PhysMutPtr<'_, V, T, ALIGN> { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("PhysMutPtr") @@ -451,14 +455,13 @@ impl> core::fmt::Debug /// Represent a physical pointer to a read-only object. This wraps around [`PhysMutPtr`] and /// exposes only copy-out access. -#[repr(C)] -pub struct PhysConstPtr> { - inner: PhysMutPtr, +pub struct PhysConstPtr<'v, V: VmapManager, T, const ALIGN: usize> { + inner: PhysMutPtr<'v, V, T, ALIGN>, } -impl PhysConstPtr +impl<'v, V, T: FromBytes, const ALIGN: usize> PhysConstPtr<'v, V, T, ALIGN> where - V: GlobalVmapManager, + V: VmapManager, { /// Create a new `PhysConstPtr` from the given physical page array and offset. /// @@ -466,9 +469,13 @@ where /// than `ALIGN`. Also, `pages` should contain enough pages to cover at least one object of /// type `T` starting from `offset`. If these conditions are not met, this function returns /// `Err(PhysPointerError)`. - pub fn new(pages: &[PhysPageAddr], offset: usize) -> Result { + pub fn new( + vmgr: &'v V, + pages: &[PhysPageAddr], + offset: usize, + ) -> Result { Ok(Self { - inner: PhysMutPtr::new(pages, offset)?, + inner: PhysMutPtr::new(vmgr, pages, offset)?, }) } @@ -476,9 +483,13 @@ where /// /// This is a shortcut for /// `PhysConstPtr::new([align_down(pa), align_down(pa) + ALIGN, ..., align_up(pa + bytes) - ALIGN], pa % ALIGN)`. - pub fn with_contiguous_pages(pa: usize, bytes: usize) -> Result { + pub fn with_contiguous_pages( + vmgr: &'v V, + pa: usize, + bytes: usize, + ) -> Result { Ok(Self { - inner: PhysMutPtr::with_contiguous_pages(pa, bytes)?, + inner: PhysMutPtr::with_contiguous_pages(vmgr, pa, bytes)?, }) } @@ -487,9 +498,9 @@ where /// This is a shortcut for `PhysConstPtr::with_contiguous_pages(pa, size_of::())`. /// /// Note: This module doesn't provide `as_usize` because LiteBox should not dereference physical addresses directly. - pub fn with_usize(pa: usize) -> Result { + pub fn with_usize(vmgr: &'v V, pa: usize) -> Result { Ok(Self { - inner: PhysMutPtr::with_usize(pa)?, + inner: PhysMutPtr::with_usize(vmgr, pa)?, }) } @@ -518,8 +529,8 @@ where } } -impl> core::fmt::Debug - for PhysConstPtr +impl, T, const ALIGN: usize> core::fmt::Debug + for PhysConstPtr<'_, V, T, ALIGN> { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("PhysConstPtr") diff --git a/litebox_common_linux/src/vmap.rs b/litebox_common_linux/src/vmap.rs index 4218ce6b7b..d2a0fd9d49 100644 --- a/litebox_common_linux/src/vmap.rs +++ b/litebox_common_linux/src/vmap.rs @@ -9,8 +9,9 @@ use thiserror::Error; /// `ALIGN`: The page frame size. /// /// This provider exists to service [`crate::physical_pointers::PhysMutPtr`] and -/// [`crate::physical_pointers::PhysConstPtr`]. It can benefit other modules which need -/// Linux kernel's `vmap()` and `vunmap()` functionalities (e.g., HVCI/HEKI, drivers). +/// [`crate::physical_pointers::PhysConstPtr`], which borrow an implementor. It +/// can benefit other modules which need Linux kernel's `vmap()` and `vunmap()` +/// functionalities (e.g., HVCI/HEKI, drivers). /// /// # Safety /// @@ -115,22 +116,6 @@ pub unsafe trait VmapManager { ) -> Result<(), PhysPointerError>; } -/// A type-level handle to a platform-global [`VmapManager`]. -/// -/// `PhysMutPtr` and `PhysConstPtr` carry their provider as a type parameter -/// (`PhantomData

`), so they cannot hold a live `&VmapManager`. This trait -/// is the minimum surface that lets such a `PhantomData`-only carrier reach -/// the live manager: each platform implements this on a small unit struct -/// (e.g., `Vmap`) and points `manager()` at its global -/// platform singleton. -pub trait GlobalVmapManager: 'static { - /// The concrete `VmapManager` this marker resolves to. - type Manager: VmapManager + 'static; - - /// Return the global manager instance for this platform. - fn manager() -> &'static Self::Manager; -} - /// Data structure representing a physical address with page alignment. /// /// Currently, this is an alias to `crate::mm::linux::NonZeroAddress`. This might change if diff --git a/litebox_platform_lvbs/src/lib.rs b/litebox_platform_lvbs/src/lib.rs index c90cd86efc..31549abe6f 100644 --- a/litebox_platform_lvbs/src/lib.rs +++ b/litebox_platform_lvbs/src/lib.rs @@ -23,8 +23,7 @@ use litebox::{ }; use litebox_common_linux::errno::Errno; use litebox_common_linux::vmap::{ - GlobalVmapManager, PhysPageAddrArray, PhysPageMapInfo, PhysPageMapPermissions, - PhysPointerError, VmapManager, + PhysPageAddrArray, PhysPageMapInfo, PhysPageMapPermissions, PhysPointerError, VmapManager, }; use x86_64::{ VirtAddr, @@ -451,16 +450,6 @@ type UserConstPtr = type UserMutPtr = litebox::platform::common_providers::userspace_pointers::UserMutPtr; -/// Type-level marker for the VTL0 physical-pointer provider. -pub enum Vmap {} - -impl GlobalVmapManager for Vmap { - type Manager = crate::host::LvbsLinuxKernel; - fn manager() -> &'static Self::Manager { - crate::platform_low() - } -} - impl RawPointerProvider for LinuxKernel { type RawConstPointer = UserConstPtr; type RawMutPointer = UserMutPtr; @@ -1118,7 +1107,8 @@ impl litebox::platform::SystemInfoProvider for LinuxKernel< } } -unsafe impl VmapManager for LinuxKernel { +// Concrete: the VTL0 protection hypercalls exist only for the LVBS host. +unsafe impl VmapManager for crate::host::LvbsLinuxKernel { type MapInfo = LvbsPhysPageMapInfo; unsafe fn vmap( @@ -1310,7 +1300,7 @@ unsafe impl VmapManager for Linu PhysFrame::::containing_address(x86_64::PhysAddr::new(range.start)), PhysFrame::::containing_address(x86_64::PhysAddr::new(range.end)), ); - crate::mshv::vsm::protect_physical_memory_range(frame_range, page_prot) + crate::mshv::vsm::protect_physical_memory_range(self, frame_range, page_prot) .map_err(|_| PhysPointerError::UnsupportedPermissions(perms.bits()))?; } @@ -1997,29 +1987,3 @@ unsafe extern "C" fn switch_to_user(_ctx: &litebox_common_linux::PtRegs) -> ! { vtl1_user_xsaved_off = const { PerCpuVariablesAsm::vtl1_user_xsaved_offset() }, ); } - -// NOTE: The below code is a naive workaround to let LVBS code to access the platform. -// Rather than doing this, we should implement LVBS interface/provider for the platform. - -pub type Platform = crate::host::LvbsLinuxKernel; - -static PLATFORM_LOW: once_cell::race::OnceRef<'static, Platform> = once_cell::race::OnceRef::new(); - -/// # Panics -/// -/// Panics if invoked more than once -pub fn set_platform_low(platform: &'static Platform) { - match PLATFORM_LOW.set(platform) { - Ok(()) => {} - Err(()) => panic!("set_platform should only be called once per crate"), - } -} - -/// # Panics -/// -/// Panics if [`set_platform_low`] has not been invoked before this -pub fn platform_low() -> &'static Platform { - PLATFORM_LOW - .get() - .expect("set_platform_low should have already been called before this point") -} diff --git a/litebox_platform_lvbs/src/mshv/mod.rs b/litebox_platform_lvbs/src/mshv/mod.rs index cbb849f6bd..36d3b5a80d 100644 --- a/litebox_platform_lvbs/src/mshv/mod.rs +++ b/litebox_platform_lvbs/src/mshv/mod.rs @@ -12,23 +12,34 @@ pub mod vsm_intercept; pub mod vtl1_mem_layout; pub mod vtl_switch; +use crate::mshv::vtl1_mem_layout::PAGE_SIZE; use litebox_common_linux::vmap::{ - GlobalVmapManager, PhysPageAddrArray, PhysPageMapPermissions, PhysPointerError, VmapManager, + PhysPageAddrArray, PhysPageMapPermissions, PhysPointerError, VmapManager, }; +use litebox_common_lvbs::MAX_CORES; +use modular_bitfield::prelude::*; +use modular_bitfield::specifiers::{B3, B4, B7, B8, B16, B31, B32, B45, B51, B62}; +use num_enum::{IntoPrimitive, TryFromPrimitive}; -/// Provider for MSHV operations authorized to modify protected VTL0 frames. -struct PrivilegedVmap; - -impl GlobalVmapManager for PrivilegedVmap { - type Manager = PrivilegedVmap; - - fn manager() -> &'static Self::Manager { - &PrivilegedVmap +/// Adapter for mapping and modifying protected VTL0 frames. +/// +/// Wrap a borrowed manager to bound lifetime: it cannot outlive the platform borrow +/// it was built from, and it can only be obtained by having a platform in hand. +/// Note that this is just for auditing: any code in this module can still construct it. +struct PrivilegedVmap<'a, P>(&'a P); + +impl<'a, P> PrivilegedVmap<'a, P> { + /// Mint the protection-bypassing mapper. Every privileged VTL0 mapping starts here, + /// so `PrivilegedVmap::mint` is the grep target for auditing them like `LvbsVtl0Gate`. + fn mint(platform: &'a P) -> Self { + Self(platform) } } -unsafe impl VmapManager for PrivilegedVmap { - type MapInfo = crate::LvbsPhysPageMapInfo; +unsafe impl> VmapManager + for PrivilegedVmap<'_, P> +{ + type MapInfo = P::MapInfo; unsafe fn vmap( &self, @@ -37,7 +48,7 @@ unsafe impl VmapManager for PrivilegedVmap { ) -> Result { // SAFETY: callers uphold the raw mapping contract. This provider is used only for // writes whose destination the caller has independently authorized. - unsafe { crate::platform_low().vmap_privileged(pages, perms) } + unsafe { self.0.vmap_privileged(pages, perms) } } unsafe fn vunmap( @@ -46,16 +57,11 @@ unsafe impl VmapManager for PrivilegedVmap { ) -> Result<(), (PhysPointerError, Self::MapInfo)> { // SAFETY: `map_info` came from the same LVBS mapper and has no outstanding uses beyond the // physical-pointer guard that is dropping it. - unsafe { - >::vunmap( - crate::platform_low(), - map_info, - ) - } + unsafe { self.0.vunmap(map_info) } } fn validate_unowned(&self, pages: &PhysPageAddrArray) -> Result<(), PhysPointerError> { - crate::platform_low().validate_unowned(pages) + self.0.validate_unowned(pages) } unsafe fn protect( @@ -64,24 +70,28 @@ unsafe impl VmapManager for PrivilegedVmap { perms: PhysPageMapPermissions, ) -> Result<(), PhysPointerError> { // SAFETY: callers uphold `VmapManager::protect`; this forwards unchanged to LVBS. - unsafe { crate::platform_low().protect(pages, perms) } + unsafe { self.0.protect(pages, perms) } } } -type Vtl0PhysConstPtr = - litebox_common_linux::physical_pointers::PhysConstPtr; +type Vtl0PhysConstPtr<'a, T, const ALIGN: usize> = + litebox_common_linux::physical_pointers::PhysConstPtr< + 'a, + crate::host::LvbsLinuxKernel, + T, + ALIGN, + >; /// Mutable VTL0 pointer reserved for callers that have independently validated the destination. /// It bypasses ordinary protected-frame access checks and synchronization. Do not use it for other /// VTL0 destinations that could enable confused-deputy writes. -type PrivilegedVtl0PhysMutPtr = - litebox_common_linux::physical_pointers::PhysMutPtr; - -use crate::mshv::vtl1_mem_layout::PAGE_SIZE; -use litebox_common_lvbs::MAX_CORES; -use modular_bitfield::prelude::*; -use modular_bitfield::specifiers::{B3, B4, B7, B8, B16, B31, B32, B45, B51, B62}; -use num_enum::{IntoPrimitive, TryFromPrimitive}; +type PrivilegedVtl0PhysMutPtr<'a, T, const ALIGN: usize> = + litebox_common_linux::physical_pointers::PhysMutPtr< + 'a, + PrivilegedVmap<'a, crate::host::LvbsLinuxKernel>, + T, + ALIGN, + >; pub const HV_HYPERCALL_REP_COMP_MASK: u64 = 0xfff_0000_0000; pub const HV_HYPERCALL_REP_COMP_OFFSET: u32 = 32; diff --git a/litebox_platform_lvbs/src/mshv/ringbuffer.rs b/litebox_platform_lvbs/src/mshv/ringbuffer.rs index 573bde849c..2958ba988e 100644 --- a/litebox_platform_lvbs/src/mshv/ringbuffer.rs +++ b/litebox_platform_lvbs/src/mshv/ringbuffer.rs @@ -3,7 +3,7 @@ //! RingBuffer implementation and functions -use super::PrivilegedVtl0PhysMutPtr; +use super::{PrivilegedVmap, PrivilegedVtl0PhysMutPtr}; use core::fmt; use litebox::mm::linux::PAGE_SIZE; use litebox::utils::TruncateExt; @@ -12,6 +12,7 @@ use spin::{Mutex, Once}; use x86_64::PhysAddr; pub struct RingBuffer { + pvmap: PrivilegedVmap<'static, crate::host::LvbsLinuxKernel>, rb_pa: PhysAddr, write_offset: usize, size: usize, @@ -23,12 +24,17 @@ pub struct RingBuffer { } impl RingBuffer { - pub fn new(phys_addr: PhysAddr, requested_size: usize) -> Self { + pub fn new( + platform: &'static crate::host::LvbsLinuxKernel, + phys_addr: PhysAddr, + requested_size: usize, + ) -> Self { let pa: usize = phys_addr.as_u64().trunc(); let fast_path_eligible = requested_size > 0 && requested_size.is_multiple_of(PAGE_SIZE) && pa.is_multiple_of(PAGE_SIZE); RingBuffer { + pvmap: PrivilegedVmap::mint(platform), rb_pa: phys_addr, write_offset: 0, size: requested_size, @@ -41,9 +47,9 @@ impl RingBuffer { return; } self.write_offset = if self.fast_path_eligible { - write_fast(self.rb_pa, self.size, self.write_offset, buf) + write_fast(&self.pvmap, self.rb_pa, self.size, self.write_offset, buf) } else { - write_slow(self.rb_pa, self.size, self.write_offset, buf) + write_slow(&self.pvmap, self.rb_pa, self.size, self.write_offset, buf) }; } } @@ -61,7 +67,13 @@ fn advance_offset(size: usize, write_offset: usize, len: usize) -> usize { /// single virtually-contiguous, physically non-contiguous mapping by emitting /// the wrap span as `[rb_pa + (start_page + i) % page_count * PAGE_SIZE]`. /// Returns the new write offset after attempting the write. -fn write_fast(rb_pa: PhysAddr, size: usize, write_offset: usize, buf: &[u8]) -> usize { +fn write_fast( + pvmap: &PrivilegedVmap<'static, crate::host::LvbsLinuxKernel>, + rb_pa: PhysAddr, + size: usize, + write_offset: usize, + buf: &[u8], +) -> usize { const MAX_SPAN_PAGES: usize = 16; // Inputs longer than the buffer overwrite the whole ring with the trailing bytes. @@ -79,7 +91,7 @@ fn write_fast(rb_pa: PhysAddr, size: usize, write_offset: usize, buf: &[u8]) -> // would map the same physical page twice and vmap rejects the duplicate. // `span_pages > MAX_SPAN_PAGES`: the span is too long for `span` below. if span_pages > page_count || span_pages > MAX_SPAN_PAGES { - return write_slow(rb_pa, size, write_offset, buf); + return write_slow(pvmap, rb_pa, size, write_offset, buf); } let rb_pa: usize = rb_pa.as_u64().trunc(); let mut span: arrayvec::ArrayVec, MAX_SPAN_PAGES> = @@ -96,7 +108,8 @@ fn write_fast(rb_pa: PhysAddr, size: usize, write_offset: usize, buf: &[u8]) -> span.push(addr); } - let Ok(ptr) = PrivilegedVtl0PhysMutPtr::::new(&span, in_page_offset) else { + let Ok(ptr) = PrivilegedVtl0PhysMutPtr::::new(pvmap, &span, in_page_offset) + else { return advance_offset(size, write_offset, buf.len()); }; let _ = ptr.write_slice_at_offset(0, buf); @@ -106,9 +119,16 @@ fn write_fast(rb_pa: PhysAddr, size: usize, write_offset: usize, buf: &[u8]) -> /// Slow path used when `rb_pa` or `size` is not page-aligned/page-multiple. /// Wraparound issues two map/unmap cycles. Returns the new write offset /// after attempting the write. -fn write_slow(rb_pa: PhysAddr, size: usize, write_offset: usize, buf: &[u8]) -> usize { +fn write_slow( + pvmap: &PrivilegedVmap<'static, crate::host::LvbsLinuxKernel>, + rb_pa: PhysAddr, + size: usize, + write_offset: usize, + buf: &[u8], +) -> usize { let write_slice = |pa: PhysAddr, slice: &[u8]| -> bool { PrivilegedVtl0PhysMutPtr::::with_contiguous_pages( + pvmap, pa.as_u64().trunc(), slice.len(), ) @@ -135,9 +155,13 @@ fn write_slow(rb_pa: PhysAddr, size: usize, write_offset: usize, buf: &[u8]) -> } static RINGBUFFER_ONCE: Once> = Once::new(); -pub(crate) fn set_ringbuffer(pa: PhysAddr, size: usize) -> &'static Mutex { +pub(crate) fn set_ringbuffer( + platform: &'static crate::host::LvbsLinuxKernel, + pa: PhysAddr, + size: usize, +) -> &'static Mutex { RINGBUFFER_ONCE.call_once(|| { - let ring_buffer = RingBuffer::new(pa, size); + let ring_buffer = RingBuffer::new(platform, pa, size); Mutex::new(ring_buffer) }) } diff --git a/litebox_platform_lvbs/src/mshv/vsm.rs b/litebox_platform_lvbs/src/mshv/vsm.rs index 396413224d..951ae734a4 100644 --- a/litebox_platform_lvbs/src/mshv/vsm.rs +++ b/litebox_platform_lvbs/src/mshv/vsm.rs @@ -116,10 +116,10 @@ pub(crate) fn mshv_vsm_configure_partition() -> Result { /// VSM function for locking VTL0's control registers, snapshotting their /// current values into VTL1 per-CPU state. -pub(crate) fn mshv_vsm_lock_regs() -> Result { +pub(crate) fn mshv_vsm_lock_regs(platform: &crate::host::LvbsLinuxKernel) -> Result { debug_serial_println!("VSM: Lock control registers"); - if crate::platform_low().end_of_boot_reached() { + if platform.end_of_boot_reached() { return Err(VsmError::OperationAfterEndOfBoot( "control register locking", )); @@ -281,15 +281,17 @@ pub(crate) fn protect_vtl1_physical_memory_range( /// VTL1 has to record this itself because there is no Hyper-V hypercall to get /// a frame's current VTL protection mask. The reservation remembers which ranges /// it changed, enabling reliable rollback. -pub(crate) struct FrameReservation { +pub(crate) struct FrameReservation<'a> { + platform: &'a crate::host::LvbsLinuxKernel, owned_ranges: Vec>, owned_frames: RangeSet, committed: bool, } -impl FrameReservation { - pub(crate) fn new() -> Self { +impl<'a> FrameReservation<'a> { + pub(crate) fn new(platform: &'a crate::host::LvbsLinuxKernel) -> Self { Self { + platform, owned_ranges: Vec::new(), owned_frames: RangeSet::new(), committed: false, @@ -321,7 +323,7 @@ impl FrameReservation { &mut self, frames: impl IntoIterator>, ) -> Result, VsmError> { - let vtl1 = crate::platform_low().vtl1_phys_frame_range(); + let vtl1 = self.platform.vtl1_phys_frame_range(); let vtl1_start = vtl1.start.start_address().as_u64(); let vtl1_end = vtl1.end.start_address().as_u64(); @@ -380,7 +382,7 @@ impl FrameReservation { } } -impl Drop for FrameReservation { +impl Drop for FrameReservation<'_> { fn drop(&mut self) { if self.committed { return; @@ -388,7 +390,7 @@ impl Drop for FrameReservation { // Rollback: restore every newly reserved range to VTL0 read/write, non-executable access. // Drop cannot report failure, so debug builds assert it. for &phys_frame_range in &self.owned_ranges { - let result = unprotect_physical_memory_range(phys_frame_range); + let result = unprotect_physical_memory_range(self.platform, phys_frame_range); debug_assert!( result.is_ok(), "Failed to restore VTL0 read/write access for reserved frames" @@ -498,11 +500,12 @@ pub(crate) fn protected_frame_registry() -> &'static ProtectedFrameRegistry { /// portions are ignored. /// `page_prot` specifies the hypervisor page-protection flags (VTL0's allowed access) to apply. pub(crate) fn protect_physical_memory_range( + platform: &crate::host::LvbsLinuxKernel, phys_frame_range: PhysFrameRange, page_prot: HvPageProtFlags, ) -> Result<(), VsmError> { let protect = !page_prot.contains(HvPageProtFlags::HV_PAGE_WRITABLE); - let vtl1_range = crate::platform_low().vtl1_phys_frame_range(); + let vtl1_range = platform.vtl1_phys_frame_range(); // Range fully within VTL1 — nothing to protect for VTL0. if phys_frame_range.start >= vtl1_range.start && phys_frame_range.end <= vtl1_range.end { @@ -556,9 +559,11 @@ pub(crate) fn protect_physical_memory_range( /// it also restores user-mode execute — see [`mem_attr_to_hv_page_prot_flags`] /// for why that rides along with read. pub(crate) fn unprotect_physical_memory_range( + platform: &crate::host::LvbsLinuxKernel, phys_frame_range: PhysFrameRange, ) -> Result<(), VsmError> { protect_physical_memory_range( + platform, phys_frame_range, HvPageProtFlags::HV_PAGE_READABLE | HvPageProtFlags::HV_PAGE_USER_EXECUTABLE @@ -568,41 +573,43 @@ pub(crate) fn unprotect_physical_memory_range( // --- The gates: platform implementation of the capability traits ----------- -/// Zero-sized capability implementing [`Vtl0Gate`]: mediated access to the -/// untrusted VTL0. Held by the HEKI service. +/// Capability implementing [`Vtl0Gate`]: mediated access to the untrusted VTL0. +/// Held by the HEKI service. pub struct LvbsVtl0Gate { /// Private, so the capability is built only via [`LvbsVtl0Gate::mint`], /// never a bare literal. - _private: (), + /// `'static` because it is long lived. + platform: &'static crate::host::LvbsLinuxKernel, } -/// Zero-sized capability implementing [`Vtl1Gate`]: the VTL1 setup steps VTL0 -/// may request. Held by the runner. +/// Capability implementing [`Vtl1Gate`]: the VTL1 setup steps VTL0 may request. +/// Held by the runner. pub struct LvbsVtl1Gate { /// Private, so the capability is built only via [`LvbsVtl1Gate::mint`], /// never a bare literal. - _private: (), + /// `'static` because it is long lived. + platform: &'static crate::host::LvbsLinuxKernel, } -/// Zero-sized capability implementing [`Vtl0PrivilegedWrite`]: VTL0 writes with -/// the protection masks bypassed. +/// Capability implementing [`Vtl0PrivilegedWrite`]: VTL0 writes with the +/// protection masks bypassed. /// /// Deliberately its own type rather than a method on [`LvbsVtl0Gate`], so this /// authority is granted per-operation and nothing holds it incidentally. Like a /// `PunchthroughToken`, it is an auditability aid rather than a boundary: it /// funnels every protection-bypassing write through one greppable mint point. -pub struct LvbsVtl0PrivilegedWriter { +pub struct LvbsVtl0PrivilegedWriter<'a> { /// Private, so the capability is built only via /// [`LvbsVtl0PrivilegedWriter::mint`], never a bare literal. - _private: (), + platform: &'a crate::host::LvbsLinuxKernel, } impl LvbsVtl0Gate { /// Mint the VTL0 mediation capability. Reserved for VTL1-trusted /// composition-root code (the runner). #[must_use] - pub fn mint() -> Self { - Self { _private: () } + pub fn mint(platform: &'static crate::host::LvbsLinuxKernel) -> Self { + Self { platform } } } @@ -610,17 +617,17 @@ impl LvbsVtl1Gate { /// Mint the VTL1 setup capability. Reserved for VTL1-trusted /// composition-root code (the runner). #[must_use] - pub fn mint() -> Self { - Self { _private: () } + pub fn mint(platform: &'static crate::host::LvbsLinuxKernel) -> Self { + Self { platform } } } -impl LvbsVtl0PrivilegedWriter { +impl<'a> LvbsVtl0PrivilegedWriter<'a> { /// Mint the protection-mask-bypassing write capability. The audit point for /// every privileged VTL0 write. #[must_use] - pub fn mint() -> Self { - Self { _private: () } + pub fn mint(platform: &'a crate::host::LvbsLinuxKernel) -> Self { + Self { platform } } } @@ -650,11 +657,11 @@ pub(crate) fn mem_attr_to_hv_page_prot_flags(attr: MemAttr) -> HvPageProtFlags { /// Restricted transaction handle for a `protect_frames_transactionally` closure. /// Wraps the private platform [`FrameReservation`] guard so the service can /// never hold or leak a reservation across the trait boundary. -struct PlatformFrameTxn<'a> { - guard: &'a mut FrameReservation, +struct PlatformFrameTxn<'a, 'r> { + guard: &'a mut FrameReservation<'r>, } -impl FrameTxn for PlatformFrameTxn<'_> { +impl FrameTxn for PlatformFrameTxn<'_, '_> { fn reserve( &mut self, ranges: &[PhysFrameRange], @@ -663,7 +670,11 @@ impl FrameTxn for PlatformFrameTxn<'_> { } fn protect(&mut self, range: PhysFrameRange, attr: MemAttr) -> Result<(), VsmError> { - protect_physical_memory_range(range, mem_attr_to_hv_page_prot_flags(attr)) + protect_physical_memory_range( + self.guard.platform, + range, + mem_attr_to_hv_page_prot_flags(attr), + ) } } @@ -674,7 +685,7 @@ impl Vtl0Gate for LvbsVtl0Gate { offset: usize, out: &mut [u8], ) -> Result<(), VsmError> { - let ptr = Vtl0PhysConstPtr::::new(pages, offset) + let ptr = Vtl0PhysConstPtr::::new(self.platform, pages, offset) .map_err(|_| VsmError::Vtl0CopyFailed)?; ptr.read_slice_at_offset(0, out) .map_err(|_| VsmError::Vtl0CopyFailed) @@ -685,11 +696,11 @@ impl Vtl0Gate for LvbsVtl0Gate { range: PhysFrameRange, attr: MemAttr, ) -> Result<(), VsmError> { - protect_physical_memory_range(range, mem_attr_to_hv_page_prot_flags(attr)) + protect_physical_memory_range(self.platform, range, mem_attr_to_hv_page_prot_flags(attr)) } fn unprotect_frames(&self, range: PhysFrameRange) -> Result<(), VsmError> { - unprotect_physical_memory_range(range) + unprotect_physical_memory_range(self.platform, range) } fn protect_frames_transactionally( @@ -697,7 +708,7 @@ impl Vtl0Gate for LvbsVtl0Gate { initial: &[PhysFrameRange], f: &mut dyn FnMut(&mut dyn FrameTxn) -> Result<(), VsmError>, ) -> Result<(), VsmError> { - let mut guard = FrameReservation::new(); + let mut guard = FrameReservation::new(self.platform); guard.reserve(initial.iter().copied())?; let mut txn = PlatformFrameTxn { guard: &mut guard }; let result = f(&mut txn); @@ -709,26 +720,28 @@ impl Vtl0Gate for LvbsVtl0Gate { } fn install_ringbuffer(&self, pa: u64, size: u64) { - let _ = crate::mshv::ringbuffer::set_ringbuffer(PhysAddr::new(pa), size.trunc()); + let _ = + crate::mshv::ringbuffer::set_ringbuffer(self.platform, PhysAddr::new(pa), size.trunc()); } fn end_of_boot_reached(&self) -> bool { - crate::platform_low().end_of_boot_reached() + self.platform.end_of_boot_reached() } fn lock_control_registers(&self) -> Result<(), VsmError> { - mshv_vsm_lock_regs().map(|_| ()) + mshv_vsm_lock_regs(self.platform).map(|_| ()) } } -impl Vtl0PrivilegedWrite for LvbsVtl0PrivilegedWriter { +impl Vtl0PrivilegedWrite for LvbsVtl0PrivilegedWriter<'_> { fn write_vtl0_pages( &self, pages: &[PhysPageAddr], offset: usize, bytes: &[u8], ) -> Result<(), VsmError> { - let ptr = PrivilegedVtl0PhysMutPtr::::new(pages, offset) + let privileged = super::PrivilegedVmap::mint(self.platform); + let ptr = PrivilegedVtl0PhysMutPtr::::new(&privileged, pages, offset) .map_err(|_| VsmError::Vtl0CopyFailed)?; ptr.write_slice_at_offset(0, bytes) .map_err(|_| VsmError::Vtl0CopyFailed) @@ -753,7 +766,7 @@ impl Vtl1Gate for LvbsVtl1Gate { let mut mask_bytes = [0u8; core::mem::size_of::()]; // Reading the argument out of VTL0 needs the VTL0 gate; the platform // implements both capabilities, so it mints its own. - LvbsVtl0Gate::mint() + LvbsVtl0Gate::mint(self.platform) .read_vtl0_contiguous(mask_pa.as_u64(), &mut mask_bytes) .map_err(|_| VsmError::CpuOnlineMaskCopyFailed)?; let cpu_online_mask = @@ -774,17 +787,17 @@ impl Vtl1Gate for LvbsVtl1Gate { fn signal_end_of_boot(&self) { debug_serial_println!("VSM: End of boot; VTL0 is no longer trusted"); - crate::platform_low().signal_end_of_boot(); + self.platform.signal_end_of_boot(); } fn set_platform_root_key(&self, key_pa: u64) -> Result<(), VsmError> { - if crate::platform_low().end_of_boot_reached() { + if self.platform.end_of_boot_reached() { return Err(VsmError::OperationAfterEndOfBoot("set platform root key")); } let key_pa = PhysAddr::try_new(key_pa).map_err(|_| VsmError::InvalidPhysicalAddress)?; let mut keybuf = Zeroizing::new([0u8; PRK_LEN]); - LvbsVtl0Gate::mint() + LvbsVtl0Gate::mint(self.platform) .read_vtl0_contiguous(key_pa.as_u64(), &mut *keybuf) .map_err(|_| VsmError::Vtl0CopyFailed)?; crate::host::set_platform_root_key(&keybuf); diff --git a/litebox_platform_lvbs/src/mshv/vtl_switch.rs b/litebox_platform_lvbs/src/mshv/vtl_switch.rs index 1d7c163703..4d43c8c942 100644 --- a/litebox_platform_lvbs/src/mshv/vtl_switch.rs +++ b/litebox_platform_lvbs/src/mshv/vtl_switch.rs @@ -236,13 +236,8 @@ impl VtlState { /// Initialize VTL switch for the current CPU. /// -/// This function sets the platform reference for the current CPU. /// It should be called once before entering the VTL switch loop. -pub fn vtl_switch_init(platform: Option<&'static crate::Platform>) { - if let Some(platform) = platform { - crate::set_platform_low(platform); - } - +pub fn vtl_switch_init() { // The VP is already in VTL1 when the runner calls this; register it // in the mask so TLB flushes during the first VTL call dispatch // target this VP. diff --git a/litebox_platform_multiplex/Cargo.toml b/litebox_platform_multiplex/Cargo.toml deleted file mode 100644 index 1099d334e2..0000000000 --- a/litebox_platform_multiplex/Cargo.toml +++ /dev/null @@ -1,27 +0,0 @@ -[package] -name = "litebox_platform_multiplex" -version = "0.1.0" -edition = "2024" - -[dependencies] -litebox = { path = "../litebox/", version = "0.1.0" } -litebox_platform_linux_userland = { path = "../litebox_platform_linux_userland/", version = "0.1.0", default-features = false, optional = true } -litebox_platform_linux_kernel = { path = "../litebox_platform_linux_kernel/", version = "0.1.0", default-features = false, optional = true } -litebox_platform_windows_userland = { path = "../litebox_platform_windows_userland/", version = "0.1.0", default-features = false, optional = true } -litebox_platform_lvbs = { path = "../litebox_platform_lvbs/", version = "0.1.0", default-features = false, optional = true } -once_cell = { version = "1.20.2", default-features = false, features = ["alloc", "race"] } -cfg-if = "1.0.0" - -[features] -default = ["platform_linux_userland_with_linux_syscall"] -platform_linux_userland = ["dep:litebox_platform_linux_userland"] -platform_windows_userland = ["dep:litebox_platform_windows_userland"] -platform_lvbs = ["dep:litebox_platform_lvbs"] -platform_linux_snp = ["dep:litebox_platform_linux_kernel"] -platform_linux_userland_with_linux_syscall = ["platform_linux_userland", "litebox_platform_linux_userland/linux_syscall"] -platform_linux_userland_with_optee_syscall = ["platform_linux_userland", "litebox_platform_linux_userland/optee_syscall"] -platform_lvbs_with_linux_syscall = ["platform_lvbs", "litebox_platform_lvbs/linux_syscall"] -platform_lvbs_with_optee_syscall = ["platform_lvbs"] - -[lints] -workspace = true diff --git a/litebox_platform_multiplex/src/lib.rs b/litebox_platform_multiplex/src/lib.rs deleted file mode 100644 index 57edcab067..0000000000 --- a/litebox_platform_multiplex/src/lib.rs +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -//! A multiplexer for [LiteBox platforms](../litebox/platform/index.html), to simplify access to a -//! global platform for shims "above" LiteBox. -//! -//! At a high level, due to Rust language design decisions, supporting a **global** -//! runtime-parametric platform is not quite directly feasible. In particular, either -//! platform-dependent functionality either needs to be aware of the platform, or each function -//! would need a parametric platform, neither of which is ideal. This crate side-steps that by using -//! conditional compilation (at this single crate) to provide the necessary switching between -//! platforms. -//! -//! Specifically, a platform MUST be selected via one of the features provided by this crate (and -//! cannot be provided dynamically at run-time). However, crates above it can then work with a -//! global platform _without_ needing to deal with any such switching. If a LiteBox platform exists -//! that does not have a corresponding feature in this crate, support for it is easy to add. -//! -//! By default, this crate picks the Linux userland platform. - -#![no_std] - -extern crate alloc; - -// Checking if more than one of the platforms has been specified. If so, compiler error. -// -// NOTE: Currently, we only support one platform, thus this is a trivial no-op. However, once we -// have more, we must account for each of the possible pairs. -cfg_if::cfg_if! { - if #[cfg(all(feature = "platform_linux_userland", target_os = "linux"))] { - pub type Platform = litebox_platform_linux_userland::LinuxUserland; - } else if #[cfg(all(feature = "platform_windows_userland", target_os = "windows"))] { - pub type Platform = litebox_platform_windows_userland::WindowsUserland; - } else if #[cfg(feature = "platform_lvbs")] { - pub type Platform = litebox_platform_lvbs::host::LvbsLinuxKernel; - } else if #[cfg(feature = "platform_linux_snp")] { - pub type Platform = litebox_platform_linux_kernel::host::snp::snp_impl::SnpLinuxKernel; - } else { - compile_error!( - r##"Hint: you might have forgotten to mark 'default-features = false'."## - ); - } -} - -static PLATFORM: once_cell::race::OnceRef<'static, Platform> = once_cell::race::OnceRef::new(); - -/// Initialize the shim by providing a [LiteBox platform](../litebox/platform/index.html). -/// -/// **Must** be invoked prior to any of the other functionality provided by this crate; all other -/// functionality is prone to panics if this has not been invoked first. -/// -/// # Panics -/// -/// Panics if invoked more than once -pub fn set_platform(platform: &'static Platform) { - match PLATFORM.set(platform) { - Ok(()) => {} - Err(()) => panic!("set_platform should only be called once per crate"), - } -} - -/// Get the global platform, or panic if [`set_platform`] has not yet been invoked. -/// -/// # Panics -/// -/// Panics if [`set_platform`] has not been invoked before this -pub fn platform() -> &'static Platform { - PLATFORM - .get() - .expect("set_platform should have already been called before this point") -} diff --git a/litebox_runner_lvbs/Cargo.toml b/litebox_runner_lvbs/Cargo.toml index a612b73860..5ad183ec92 100644 --- a/litebox_runner_lvbs/Cargo.toml +++ b/litebox_runner_lvbs/Cargo.toml @@ -7,7 +7,6 @@ edition = "2024" arrayvec = { version = "0.7.6", default-features = false } litebox = { version = "0.1.0", path = "../litebox" } litebox_platform_lvbs = { version = "0.1.0", path = "../litebox_platform_lvbs", default-features = false } -litebox_platform_multiplex = { version = "0.1.0", path = "../litebox_platform_multiplex", default-features = false, features = ["platform_lvbs"] } litebox_common_optee = { path = "../litebox_common_optee/", version = "0.1.0" } litebox_common_linux = { path = "../litebox_common_linux/", version = "0.1.0" } litebox_common_lvbs = { path = "../litebox_common_lvbs/", version = "0.1.0" } @@ -15,6 +14,7 @@ litebox_service_heki = { path = "../litebox_service_heki/", version = "0.1.0" } litebox_shim_optee = { path = "../litebox_shim_optee/", version = "0.1.0" } litebox_util_log = { version = "0.1.0", path = "../litebox_util_log" } log = { version = "0.4", default-features = false } +once_cell = { version = "1.20.2", default-features = false, features = ["alloc", "race"] } spin = { version = "0.10.0", default-features = false, features = ["spin_mutex"] } [target.'cfg(target_arch = "x86_64")'.dependencies] diff --git a/litebox_runner_lvbs/src/lib.rs b/litebox_runner_lvbs/src/lib.rs index 8e5350bdc5..64b604aeb0 100644 --- a/litebox_runner_lvbs/src/lib.rs +++ b/litebox_runner_lvbs/src/lib.rs @@ -18,6 +18,7 @@ use litebox_common_optee::{ OpteeMessageCommand, OpteeMsgArgs, OpteeRpcArgs, OpteeSmcArgs, OpteeSmcResult, OpteeSmcReturnCode, TeeOrigin, TeeResult, UteeEntryFunc, UteeParams, optee_msg_args_total_size, }; +use litebox_platform_lvbs::host::LvbsLinuxKernel as Platform; use litebox_platform_lvbs::mshv::vsm::{LvbsVtl0Gate, LvbsVtl0PrivilegedWriter, LvbsVtl1Gate}; use litebox_platform_lvbs::{ arch::{gdt, instrs::hlt_loop, interrupts, timer}, @@ -38,13 +39,23 @@ use litebox_platform_lvbs::{ }, serial_println, }; -use litebox_platform_multiplex::Platform; use litebox_shim_optee::msg_handler::{ decode_ta_request, handle_optee_msg_args, handle_optee_smc_args, update_optee_msg_args, }; -use litebox_shim_optee::session::{OpenSessionTarget, TaInstance, session_manager}; +use litebox_shim_optee::session::{OpenSessionTarget, SessionManager, TaInstance}; use litebox_shim_optee::{NormalWorldConstPtr, NormalWorldMutPtr, UserConstPtr}; +/// The session registry for this runner. +/// +/// The shim is generic over its platform, so it cannot own this: a `static` cannot +/// name a generic parameter. The composition root names the concrete platform, so +/// the singleton lives here. +fn session_manager() -> &'static SessionManager { + static SESSION_MANAGER: once_cell::race::OnceBox> = + once_cell::race::OnceBox::new(); + SESSION_MANAGER.get_or_init(|| alloc::boxed::Box::new(SessionManager::new())) +} + /// Seed the initial heap regions so the global allocator has enough memory /// for slab-backed allocations (the slab needs >= 2 MB backing pages). pub fn seed_initial_heap() { @@ -80,19 +91,30 @@ pub fn seed_initial_heap() { ); } -/// Initialize the current core. +/// Initialize the current core and yield the platform it should run against. /// /// When `is_bsp` is `true`, creates the platform, sets up page tables, and /// reclaims early memory. /// All cores then initialize hypercalls, GDT, IDT, interrupts, and syscall /// support. /// +/// The BSP constructs the platform; every AP recovers that same instance here. This +/// is the AP's arrival point, and from here the reference is passed by argument. +/// /// # Panics /// /// Panics if VTL1 memory info is unavailable (BSP) or if hypercall /// initialization fails. -pub fn init(is_bsp: bool) -> Option<&'static Platform> { - let ret = if is_bsp { +pub fn init(is_bsp: bool) -> &'static Platform { + // Bootstrap-only handle, existing solely to satisfy the AP entry ABI: APs are + // entered by Hyper-V at `_ap_start`, a naked trampoline that can pass nothing but + // `is_bsp`, so the BSP publishes here and each AP recovers on arrival. Scoped to + // this function so no other code can reach for it: everything below `init` takes + // `&'static Platform` as an argument instead. + static BOOT_PLATFORM: once_cell::race::OnceRef<'static, Platform> = + once_cell::race::OnceRef::new(); + + if is_bsp { let (start, size) = get_vtl1_memory_info().expect("Failed to get memory info"); let min_vtl1_size = ((VTL1_REMAP_PDE_PAGE + 1) * PAGE_SIZE) as u64; assert!( @@ -141,7 +163,10 @@ pub fn init(is_bsp: bool) -> Option<&'static Platform> { } let platform = Platform::new(vtl1_start, vtl1_end, text_phys_start, text_phys_end); - litebox_platform_multiplex::set_platform(platform); + assert!( + BOOT_PLATFORM.set(platform).is_ok(), + "the BSP must publish the platform exactly once" + ); // Reclaim Phase 1 / VTL0 page table frames now that Platform::new() // has loaded a fresh base page table covering all VTL1 memory. @@ -202,11 +227,7 @@ pub fn init(is_bsp: bool) -> Option<&'static Platform> { mem_fill_start, mem_fill_size ); - - Some(platform) - } else { - None - }; + } // Allocate XSAVE areas now that we are on the kernel stack (the CPUID // queries and aligned-vec allocations need a lot of stack space). @@ -224,16 +245,18 @@ pub fn init(is_bsp: bool) -> Option<&'static Platform> { // Per-CPU; safe to call on BSP and APs. timer::init(); - ret + BOOT_PLATFORM + .get() + .expect("init must publish the platform before any core uses it") } -pub fn run(platform: Option<&'static Platform>) -> ! { - vtl_switch_init(platform); +pub fn run(platform: &'static Platform) -> ! { + vtl_switch_init(); let mut return_value: Option = None; loop { let params = vtl_switch(return_value); - return_value = Some(vtlcall_dispatch(¶ms)); + return_value = Some(vtlcall_dispatch(platform, ¶ms)); } } @@ -246,7 +269,7 @@ pub fn run(platform: Option<&'static Platform>) -> ! { /// TODO: Consider unified interface signature and naming /// VTL call is Hyper-V specific. However, in general, there is no fundamental difference /// between VTL call and TrustZone SMC call, TDX TDCALL, etc. -fn vtlcall_dispatch(params: &[u64; NUM_VTLCALL_PARAMS]) -> i64 { +fn vtlcall_dispatch(platform: &'static Platform, params: &[u64; NUM_VTLCALL_PARAMS]) -> i64 { let func_id: u32 = params[0].trunc(); let Ok(func_id) = VsmFunction::try_from(func_id) else { return Errno::EINVAL.as_neg().into(); @@ -254,14 +277,14 @@ fn vtlcall_dispatch(params: &[u64; NUM_VTLCALL_PARAMS]) -> i64 { match func_id { VsmFunction::OpteeMessage => { let smc_args_pfn = params[1]; - optee_smc_handler_entry(smc_args_pfn) + optee_smc_handler_entry(platform, smc_args_pfn) } VsmFunction::GenerateIdentitySigningKey => { let public_key_pa = params[1]; let key_alg = params[2]; - litebox_shim_optee::idk::generate_identity_signing_key(public_key_pa, key_alg) + litebox_shim_optee::idk::generate_identity_signing_key(platform, public_key_pa, key_alg) } - _ => vsm_dispatch(func_id, ¶ms[1..]), + _ => vsm_dispatch(platform, func_id, ¶ms[1..]), } } @@ -270,9 +293,9 @@ fn vtlcall_dispatch(params: &[u64; NUM_VTLCALL_PARAMS]) -> i64 { /// /// This is where the abstract service is bound to the concrete platform gate; /// the service holds it for its lifetime, so handlers need no gate argument. -fn heki() -> &'static litebox_service_heki::Heki { +fn heki(platform: &'static Platform) -> &'static litebox_service_heki::Heki { static HEKI: spin::Once> = spin::Once::new(); - HEKI.call_once(|| litebox_service_heki::Heki::new(LvbsVtl0Gate::mint())) + HEKI.call_once(|| litebox_service_heki::Heki::new(LvbsVtl0Gate::mint(platform))) } /// Dispatch a VSM function to its handler and return the result. @@ -282,11 +305,11 @@ fn heki() -> &'static litebox_service_heki::Heki { /// The Hyper-V mechanics behind both stay inside the platform, so nothing /// here talks to the hypervisor. As the VSM composition root, the runner /// mints the gate and owns the HEKI service. -fn vsm_dispatch(func_id: VsmFunction, params: &[u64]) -> i64 { +fn vsm_dispatch(platform: &'static Platform, func_id: VsmFunction, params: &[u64]) -> i64 { use litebox_common_lvbs::Vtl1Gate as _; - let vtl1 = LvbsVtl1Gate::mint(); - let heki = heki(); + let vtl1 = LvbsVtl1Gate::mint(platform); + let heki = heki(platform); let result: Result = match func_id { VsmFunction::EnableAPsVtl => vtl1.enable_aps_vtl(params[0]).map(|()| 0), VsmFunction::BootAPs => vtl1.boot_aps(params[0]).map(|()| 0), @@ -304,9 +327,11 @@ fn vsm_dispatch(func_id: VsmFunction, params: &[u64]) -> i64 { VsmFunction::UnloadModule => heki.unload_guest_module(params[0].reinterpret_as_signed()), VsmFunction::CopySecondaryKey => heki.copy_secondary_key(params[0], params[1]), VsmFunction::KexecValidate => heki.kexec_validate(params[0], params[1], params[2]), - VsmFunction::PatchText => { - heki.patch_text(&LvbsVtl0PrivilegedWriter::mint(), params[0], params[1]) - } + VsmFunction::PatchText => heki.patch_text( + &LvbsVtl0PrivilegedWriter::mint(platform), + params[0], + params[1], + ), VsmFunction::AllocateRingbufferMemory => { heki.allocate_ringbuffer_memory(params[0], params[1]) } @@ -323,26 +348,28 @@ fn vsm_dispatch(func_id: VsmFunction, params: &[u64]) -> i64 { } /// An entry point function to handle OP-TEE SMC call. -fn optee_smc_handler_entry(smc_args_pfn: u64) -> i64 { - match optee_smc_handler_entry_inner(smc_args_pfn) { +fn optee_smc_handler_entry(platform: &'static Platform, smc_args_pfn: u64) -> i64 { + match optee_smc_handler_entry_inner(platform, smc_args_pfn) { Ok(res) => res, Err(e) => e.as_neg().into(), } } fn optee_smc_handler_entry_inner( + platform: &'static Platform, smc_args_pfn: u64, ) -> Result { let smc_args_pfn: usize = smc_args_pfn.trunc(); let smc_args_addr = smc_args_pfn .checked_mul(1usize << litebox_platform_lvbs::mshv::vtl1_mem_layout::PAGE_SHIFT) .ok_or(litebox_common_linux::errno::Errno::EINVAL)?; - let smc_args_updated = optee_smc_handler(smc_args_addr); + let smc_args_updated = optee_smc_handler(platform, smc_args_addr); // Write back the SMC arguments page to normal world memory. // All OP-TEE return codes (success or error) are delivered via smc_args.args[0]. - let smc_args_ptr = NormalWorldMutPtr::::with_usize(smc_args_addr) - .map_err(|_| litebox_common_linux::errno::Errno::EINVAL)?; + let smc_args_ptr = + NormalWorldMutPtr::::with_usize(platform, smc_args_addr) + .map_err(|_| litebox_common_linux::errno::Errno::EINVAL)?; smc_args_ptr .write_at_offset(0, smc_args_updated) .map_err(|_| litebox_common_linux::errno::Errno::EFAULT)?; @@ -359,8 +386,7 @@ fn optee_smc_handler_entry_inner( /// The caller must ensure that no references to user-space memory are held /// after the switch. #[inline] -unsafe fn switch_to_base_page_table() { - let platform = litebox_platform_multiplex::platform(); +unsafe fn switch_to_base_page_table(platform: &'static Platform) { // Safety: We're switching to base page table which contains valid mappings // for all kernel memory that will be accessed after the switch. unsafe { @@ -370,8 +396,7 @@ unsafe fn switch_to_base_page_table() { /// Creates a new task-specific page table. #[inline] -fn create_task_page_table() -> Result { - let platform = litebox_platform_multiplex::platform(); +fn create_task_page_table(platform: &'static Platform) -> Result { platform .create_task_page_table() .map_err(|_| OpteeSmcReturnCode::ENomem) @@ -384,8 +409,10 @@ fn create_task_page_table() -> Result { /// The caller must ensure that no references to user-space memory from a different /// task's address space are held after the switch. #[inline] -unsafe fn switch_to_task_page_table(task_pt_id: usize) -> Result<(), OpteeSmcReturnCode> { - let platform = litebox_platform_multiplex::platform(); +unsafe fn switch_to_task_page_table( + platform: &'static Platform, + task_pt_id: usize, +) -> Result<(), OpteeSmcReturnCode> { // Safety: We're switching to a task page table which contains valid mappings // for both kernel memory and the specific task's user-space memory. unsafe { @@ -403,8 +430,10 @@ unsafe fn switch_to_task_page_table(task_pt_id: usize) -> Result<(), OpteeSmcRet /// The caller must ensure that no references or pointers to memory mapped /// by this page table are held after deletion. #[inline] -unsafe fn delete_task_page_table(task_pt_id: usize) -> Result<(), OpteeSmcReturnCode> { - let platform = litebox_platform_multiplex::platform(); +unsafe fn delete_task_page_table( + platform: &'static Platform, + task_pt_id: usize, +) -> Result<(), OpteeSmcReturnCode> { // Safety: caller guarantees no dangling references unsafe { platform @@ -422,18 +451,22 @@ unsafe fn delete_task_page_table(task_pt_id: usize) -> Result<(), OpteeSmcReturn /// paths that switch to base internally before deleting the task page /// table can run before this guard's `Drop` — the redundant write at /// drop time is benign. -struct TaskPageTableGuard; +struct TaskPageTableGuard { + /// Needed by `Drop`, which cannot take arguments and so has nowhere to receive + /// the platform from. + platform: &'static Platform, +} impl TaskPageTableGuard { - fn enter(task_pt_id: usize) -> Result { - unsafe { switch_to_task_page_table(task_pt_id)? }; - Ok(Self) + fn enter(platform: &'static Platform, task_pt_id: usize) -> Result { + unsafe { switch_to_task_page_table(platform, task_pt_id)? }; + Ok(Self { platform }) } } impl Drop for TaskPageTableGuard { fn drop(&mut self) { - unsafe { switch_to_base_page_table() }; + unsafe { switch_to_base_page_table(self.platform) }; } } @@ -448,14 +481,18 @@ impl Drop for TaskPageTableGuard { /// /// The caller must ensure that no references to user-space memory mapped by /// this task's page table are held after this call. -unsafe fn teardown_ta_page_table(shim: &litebox_shim_optee::OpteeShim, task_pt_id: usize) { +unsafe fn teardown_ta_page_table( + platform: &'static Platform, + shim: &litebox_shim_optee::OpteeShim, + task_pt_id: usize, +) { unsafe { // this function unmaps/deallocates user pages in the **active** page table, so we must // still be on the TA's page table. shim.release_user_mappings(); - switch_to_base_page_table(); + switch_to_base_page_table(platform); // Now delete the TA's page table without memory leak. - let _ = delete_task_page_table(task_pt_id); + let _ = delete_task_page_table(platform, task_pt_id); } } @@ -491,7 +528,7 @@ unsafe fn teardown_ta_page_table(shim: &litebox_shim_optee::OpteeShim, task_pt_i /// This function always returns `OpteeSmcArgs` with the result code in `args[0]`. /// The OP-TEE driver expects all return codes (success or error) to be delivered via /// `smc_args.args[0]`. -fn optee_smc_handler(smc_args_addr: usize) -> OpteeSmcArgs { +fn optee_smc_handler(platform: &'static Platform, smc_args_addr: usize) -> OpteeSmcArgs { use OpteeMessageCommand::{CloseSession, InvokeCommand, OpenSession}; // Helper to create error response when we don't read smc_args from the normal world yet @@ -501,15 +538,16 @@ fn optee_smc_handler(smc_args_addr: usize) -> OpteeSmcArgs { args }; - let Ok(smc_args_ptr) = - NormalWorldConstPtr::::with_usize(smc_args_addr) - else { + let Ok(smc_args_ptr) = NormalWorldConstPtr::::with_usize( + platform, + smc_args_addr, + ) else { return make_error_response(OpteeSmcReturnCode::EBadAddr); }; let Ok(mut smc_args) = smc_args_ptr.read_at_offset(0) else { return make_error_response(OpteeSmcReturnCode::EBadAddr); }; - let Ok(smc_result) = handle_optee_smc_args(&mut smc_args) else { + let Ok(smc_result) = handle_optee_smc_args(platform, &mut smc_args) else { smc_args.set_return_code(OpteeSmcReturnCode::EBadCmd); return *smc_args; }; @@ -522,25 +560,26 @@ fn optee_smc_handler(smc_args_addr: usize) -> OpteeSmcArgs { let mut msg_args = *msg_args; debug_serial_println!("OP-TEE SMC with MsgArgs Command: {:?}", msg_args.cmd); let result = match msg_args.cmd { - OpenSession => handle_open_session(&mut msg_args, msg_args_phys_addr), - InvokeCommand => handle_invoke_command(&mut msg_args, msg_args_phys_addr), - CloseSession => handle_close_session(&mut msg_args, msg_args_phys_addr), + OpenSession => handle_open_session(platform, &mut msg_args, msg_args_phys_addr), + InvokeCommand => handle_invoke_command(platform, &mut msg_args, msg_args_phys_addr), + CloseSession => handle_close_session(platform, &mut msg_args, msg_args_phys_addr), _ => { - let r = handle_optee_msg_args(&msg_args); + let r = handle_optee_msg_args(platform, &msg_args); if r.is_ok() { msg_args.ret = TeeResult::Success; } else { msg_args.ret = TeeResult::BadParameters; } msg_args.ret_origin = TeeOrigin::Tee; - let _ = write_non_ta_msg_args_to_normal_world(&msg_args, msg_args_phys_addr); + let _ = + write_non_ta_msg_args_to_normal_world(platform, &msg_args, msg_args_phys_addr); r } }; // Always switch back to base page table before returning to VTL0 // Safety: No user-space memory references are held after this point - unsafe { switch_to_base_page_table() }; + unsafe { switch_to_base_page_table(platform) }; if let Err(e) = result { smc_args.set_return_code(e); @@ -563,10 +602,12 @@ fn optee_smc_handler(smc_args_addr: usize) -> OpteeSmcArgs { /// and appropriate cleanup is performed (page table teardown for new instances, /// instance cleanup for TARGET_DEAD on single-instance TAs). fn handle_open_session( + platform: &'static Platform, msg_args: &mut OpteeMsgArgs, msg_args_phys_addr: u64, ) -> Result<(), OpteeSmcReturnCode> { - let ta_req_info = decode_ta_request(msg_args).map_err(|_| OpteeSmcReturnCode::EBadCmd)?; + let ta_req_info = + decode_ta_request(platform, msg_args).map_err(|_| OpteeSmcReturnCode::EBadCmd)?; if ta_req_info.entry_func != UteeEntryFunc::OpenSession { return Err(OpteeSmcReturnCode::EBadCmd); } @@ -577,6 +618,7 @@ fn handle_open_session( session_manager().with_ta(&ta_uuid, |target| match target { OpenSessionTarget::Sibling(instance) => open_session_single_instance( + platform, msg_args, msg_args_phys_addr, instance, @@ -585,6 +627,7 @@ fn handle_open_session( &ta_req_info, ), OpenSessionTarget::NewInstance => open_session_new_instance( + platform, msg_args, msg_args_phys_addr, params, @@ -598,7 +641,7 @@ fn handle_open_session( // return TEE_ERROR_BUSY with origin TEE via msg_args. msg_args.ret = TeeResult::Busy; msg_args.ret_origin = TeeOrigin::Tee; - write_non_ta_msg_args_to_normal_world(msg_args, msg_args_phys_addr)?; + write_non_ta_msg_args_to_normal_world(platform, msg_args, msg_args_phys_addr)?; Ok(()) } }) @@ -611,9 +654,10 @@ fn handle_open_session( /// single-instance cache entry is evicted, and the TA instance is torn down. /// For cleanup semantics, see OP-TEE OS `tee_ta_open_session()` in `tee_ta_manager.c`. fn open_session_single_instance( + platform: &'static Platform, msg_args: &mut OpteeMsgArgs, msg_args_phys_addr: u64, - instance: &TaInstance, + instance: &TaInstance, params: &[litebox_common_optee::UteeParamOwned], client_identity: Option, ta_req_info: &litebox_shim_optee::msg_handler::TaRequestInfo, @@ -637,7 +681,7 @@ fn open_session_single_instance( ); // Switch to the existing TA's page table - let _task_pt_guard = TaskPageTableGuard::enter(task_pt_id)?; + let _task_pt_guard = TaskPageTableGuard::enter(platform, task_pt_id)?; // Load TA context with parameters for OpenSession - pass actual session_id instance @@ -667,7 +711,7 @@ fn open_session_single_instance( .loaded_program() .params_address .ok_or(OpteeSmcReturnCode::EBadAddr)?; - let ta_params = UserConstPtr::::from_usize(params_address) + let ta_params = UserConstPtr::::from_usize(params_address) .read_at_offset(0) .ok_or(OpteeSmcReturnCode::EBadAddr)?; @@ -687,6 +731,7 @@ fn open_session_single_instance( // `with_ta`'s serialization keeps the instance alive so another core cannot // tear down the active page table while this core is copying TA outputs. let write_result = write_msg_args_to_normal_world( + platform, msg_args, msg_args_phys_addr, return_code, @@ -705,7 +750,7 @@ fn open_session_single_instance( // Safety: We are about to tear down this TA instance; // no references to user-space memory will be held afterwards. unsafe { - teardown_ta_page_table(instance.shim(), task_pt_id); + teardown_ta_page_table(platform, instance.shim(), task_pt_id); }; // TODO: Per OP-TEE OS semantics, if the TA has INSTANCE_KEEP_ALIVE but not @@ -719,6 +764,7 @@ fn open_session_single_instance( // Treat write-back failure as OpenSession failure: do not publish the session. let write_result = write_msg_args_to_normal_world( + platform, msg_args, msg_args_phys_addr, return_code, @@ -742,7 +788,7 @@ fn open_session_single_instance( // Safety: We are about to tear down this TA instance; // no references to user-space memory will be held afterwards. unsafe { - teardown_ta_page_table(instance.shim(), task_pt_id); + teardown_ta_page_table(platform, instance.shim(), task_pt_id); }; } else { // The session id is forgotten (never recycled), so the token's drop @@ -771,6 +817,7 @@ fn open_session_single_instance( /// If ldelf loading or OpenSession entry point fails, the page table is torn down. /// Per OP-TEE OS semantics: if OpenSession returns non-success, cleanup happens. fn open_session_new_instance( + platform: &'static Platform, msg_args: &mut OpteeMsgArgs, msg_args_phys_addr: u64, params: &[litebox_common_optee::UteeParamOwned], @@ -782,7 +829,7 @@ fn open_session_new_instance( msg_args.session = 0; msg_args.ret = TeeResult::ItemNotFound; msg_args.ret_origin = TeeOrigin::Tee; - write_non_ta_msg_args_to_normal_world(msg_args, msg_args_phys_addr)?; + write_non_ta_msg_args_to_normal_world(platform, msg_args, msg_args_phys_addr)?; return Ok(()); }; @@ -792,22 +839,22 @@ fn open_session_new_instance( let mut session_token = session_manager().try_acquire_open_session_token()?; let runner_session_id = session_token.session_id().unwrap(); - let task_pt_id = create_task_page_table()?; + let task_pt_id = create_task_page_table(platform)?; debug_serial_println!("Created task page table ID: {}", task_pt_id); - let _task_pt_guard = TaskPageTableGuard::enter(task_pt_id).inspect_err(|_| { + let _task_pt_guard = TaskPageTableGuard::enter(platform, task_pt_id).inspect_err(|_| { // Safety: switch_to_task_page_table failed, so task page table is not active. - let _ = unsafe { delete_task_page_table(task_pt_id) }; + let _ = unsafe { delete_task_page_table(platform, task_pt_id) }; })?; // Load ldelf and TA - Box immediately to keep at fixed heap address - let shim = litebox_shim_optee::OpteeShimBuilder::new().build(); + let shim = litebox_shim_optee::OpteeShimBuilder::new(platform, session_manager()).build(); let loaded_program = Box::new( shim.load_ldelf(LDELF_BINARY, ta_uuid, Some(ta_bin)) .map_err(|_| { // Safety: We are about to tear down this TA instance; // no references to user-space memory will be held afterwards. - unsafe { teardown_ta_page_table(&shim, task_pt_id) }; + unsafe { teardown_ta_page_table(platform, &shim, task_pt_id) }; OpteeSmcReturnCode::ENomem })?, ); @@ -841,6 +888,7 @@ fn open_session_new_instance( // Write error response back to normal world let write_result = write_msg_args_to_normal_world( + platform, msg_args, msg_args_phys_addr, ldelf_return_code, @@ -851,7 +899,7 @@ fn open_session_new_instance( // Safety: We are about to tear down this TA instance; // no references to user-space memory will be held afterwards. - unsafe { teardown_ta_page_table(&shim, task_pt_id) }; + unsafe { teardown_ta_page_table(platform, &shim, task_pt_id) }; write_result?; return Ok(()); @@ -864,7 +912,7 @@ fn open_session_new_instance( loaded_program.entrypoints.as_ref().ok_or_else(|| { // Safety: We are about to tear down this TA instance; // no references to user-space memory will be held afterwards. - unsafe { teardown_ta_page_table(&shim, task_pt_id) }; + unsafe { teardown_ta_page_table(platform, &shim, task_pt_id) }; OpteeSmcReturnCode::EBadCmd })?; loaded_program @@ -880,7 +928,7 @@ fn open_session_new_instance( .map_err(|_| { // Safety: We are about to tear down this TA instance; // no references to user-space memory will be held afterwards. - unsafe { teardown_ta_page_table(&shim, task_pt_id) }; + unsafe { teardown_ta_page_table(platform, &shim, task_pt_id) }; OpteeSmcReturnCode::EBadCmd })?; @@ -897,15 +945,15 @@ fn open_session_new_instance( let params_address = loaded_program.params_address.ok_or_else(|| { // Safety: We are about to tear down this TA instance; // no references to user-space memory will be held afterwards. - unsafe { teardown_ta_page_table(&shim, task_pt_id) }; + unsafe { teardown_ta_page_table(platform, &shim, task_pt_id) }; OpteeSmcReturnCode::EBadAddr })?; - let ta_params = UserConstPtr::::from_usize(params_address) + let ta_params = UserConstPtr::::from_usize(params_address) .read_at_offset(0) .ok_or_else(|| { // Safety: We are about to tear down this TA instance; // no references to user-space memory will be held afterwards. - unsafe { teardown_ta_page_table(&shim, task_pt_id) }; + unsafe { teardown_ta_page_table(platform, &shim, task_pt_id) }; OpteeSmcReturnCode::EBadAddr })?; @@ -923,6 +971,7 @@ fn open_session_new_instance( // Write error response back to normal world let write_result = write_msg_args_to_normal_world( + platform, msg_args, msg_args_phys_addr, return_code, @@ -933,7 +982,7 @@ fn open_session_new_instance( // Safety: We are about to tear down this TA instance; // no references to user-space memory will be held afterwards. - unsafe { teardown_ta_page_table(&shim, task_pt_id) }; + unsafe { teardown_ta_page_table(platform, &shim, task_pt_id) }; write_result?; return Ok(()); @@ -943,6 +992,7 @@ fn open_session_new_instance( // session is neither registered nor cached, so we just tear down the // local resources and let `session_token` recycle the ID on drop. write_msg_args_to_normal_world( + platform, msg_args, msg_args_phys_addr, return_code, @@ -953,7 +1003,7 @@ fn open_session_new_instance( .inspect_err(|_| { // Safety: We are about to tear down this TA instance; // no references to user-space memory will be held afterwards. - unsafe { teardown_ta_page_table(&shim, task_pt_id) }; + unsafe { teardown_ta_page_table(platform, &shim, task_pt_id) }; })?; // Success: register the new session with the manager. @@ -980,6 +1030,7 @@ fn open_session_new_instance( /// Must be called from within a `with_session` closure so its serialization /// covers the cleanup. fn finalize_dead_session( + platform: &'static Platform, session_id: u32, msg_args: &mut OpteeMsgArgs, msg_args_phys_addr: u64, @@ -989,7 +1040,7 @@ fn finalize_dead_session( session_manager().unregister_session(session_id); msg_args.ret = return_code; msg_args.ret_origin = TeeOrigin::Tee; - write_non_ta_msg_args_to_normal_world(msg_args, msg_args_phys_addr)?; + write_non_ta_msg_args_to_normal_world(platform, msg_args, msg_args_phys_addr)?; debug_serial_println!( "{}: session_id={} on dead TA session", log_prefix, @@ -1005,10 +1056,12 @@ fn finalize_dead_session( /// Per OP-TEE OS semantics: if the TA panics (returns TARGET_DEAD), the session /// should be cleaned up. For single-instance TAs, the entire instance is destroyed. fn handle_invoke_command( + platform: &'static Platform, msg_args: &mut OpteeMsgArgs, msg_args_phys_addr: u64, ) -> Result<(), OpteeSmcReturnCode> { - let ta_req_info = decode_ta_request(msg_args).map_err(|_| OpteeSmcReturnCode::EBadCmd)?; + let ta_req_info = + decode_ta_request(platform, msg_args).map_err(|_| OpteeSmcReturnCode::EBadCmd)?; if ta_req_info.entry_func != UteeEntryFunc::InvokeCommand { return Err(OpteeSmcReturnCode::EBadCmd); } @@ -1019,6 +1072,7 @@ fn handle_invoke_command( session_manager().with_session(session_id, |instance| { let Some(instance) = instance else { return finalize_dead_session( + platform, session_id, msg_args, msg_args_phys_addr, @@ -1028,7 +1082,7 @@ fn handle_invoke_command( }; let task_pt_id = instance.task_page_table_id(); - let _task_pt_guard = TaskPageTableGuard::enter(task_pt_id)?; + let _task_pt_guard = TaskPageTableGuard::enter(platform, task_pt_id)?; debug_serial_println!( "InvokeCommand: session_id={}, task_pt_id={}, cmd_id={}", @@ -1061,7 +1115,7 @@ fn handle_invoke_command( .loaded_program() .params_address .ok_or(OpteeSmcReturnCode::EBadAddr)?; - let ta_params = UserConstPtr::::from_usize(params_address) + let ta_params = UserConstPtr::::from_usize(params_address) .read_at_offset(0) .ok_or(OpteeSmcReturnCode::EBadAddr)?; @@ -1072,6 +1126,7 @@ fn handle_invoke_command( // `with_session`'s serialization keeps the entry stable so another core cannot // tear down the active page table while this core is copying TA outputs. let write_result = write_msg_args_to_normal_world( + platform, msg_args, msg_args_phys_addr, return_code, @@ -1098,7 +1153,7 @@ fn handle_invoke_command( // Safety: We are about to tear down this TA instance; // no references to user-space memory will be held afterwards. unsafe { - teardown_ta_page_table(instance.shim(), task_pt_id); + teardown_ta_page_table(platform, instance.shim(), task_pt_id); }; debug_serial_println!( @@ -1121,10 +1176,12 @@ fn handle_invoke_command( /// then removes the session from the map. For single-instance TAs, the TA /// is only destroyed when the last session closes. fn handle_close_session( + platform: &'static Platform, msg_args: &mut OpteeMsgArgs, msg_args_phys_addr: u64, ) -> Result<(), OpteeSmcReturnCode> { - let ta_req_info = decode_ta_request(msg_args).map_err(|_| OpteeSmcReturnCode::EBadCmd)?; + let ta_req_info = + decode_ta_request(platform, msg_args).map_err(|_| OpteeSmcReturnCode::EBadCmd)?; if ta_req_info.entry_func != UteeEntryFunc::CloseSession { return Err(OpteeSmcReturnCode::EBadCmd); } @@ -1135,6 +1192,7 @@ fn handle_close_session( session_manager().with_session(session_id, |instance| { let Some(instance) = instance else { return finalize_dead_session( + platform, session_id, msg_args, msg_args_phys_addr, @@ -1144,7 +1202,7 @@ fn handle_close_session( }; let task_pt_id = instance.task_page_table_id(); - let _task_pt_guard = TaskPageTableGuard::enter(task_pt_id)?; + let _task_pt_guard = TaskPageTableGuard::enter(platform, task_pt_id)?; // Set up the entry-point parameters for CloseSession. instance @@ -1171,6 +1229,7 @@ fn handle_close_session( // CloseSession always succeeds (TA_CloseSessionEntryPoint returns void) let write_result = write_msg_args_to_normal_world( + platform, msg_args, msg_args_phys_addr, TeeResult::Success, @@ -1205,7 +1264,7 @@ fn handle_close_session( // Safety: We are about to tear down this TA instance; // no references to user-space memory will be held afterwards. unsafe { - teardown_ta_page_table(instance.shim(), task_pt_id); + teardown_ta_page_table(platform, instance.shim(), task_pt_id); }; debug_serial_println!( @@ -1243,6 +1302,7 @@ fn handle_close_session( /// Panics if called while the base page table is active (i.e., not in a TA context). #[inline] fn write_msg_args_to_normal_world( + platform: &'static Platform, msg_args: &mut OpteeMsgArgs, msg_args_phys_addr: u64, return_code: TeeResult, @@ -1253,9 +1313,7 @@ fn write_msg_args_to_normal_world( // Ensure we're on a task page table, not the base page table. // Accessing TA userspace memory requires the TA's page table to be active. debug_assert!( - !litebox_platform_multiplex::platform() - .page_table_manager() - .is_base_page_table_active(), + !platform.page_table_manager().is_base_page_table_active(), "write_msg_args_to_normal_world called on base page table" ); @@ -1266,6 +1324,7 @@ fn write_msg_args_to_normal_world( TeeOrigin::TrustedApp }; update_optee_msg_args( + platform, return_code, origin, session_id, @@ -1278,7 +1337,8 @@ fn write_msg_args_to_normal_world( let mut blob = vec![0u8; msg_args_size]; msg_args.serialize(&mut blob)?; - let ptr = NormalWorldMutPtr::::with_contiguous_pages( + let ptr = NormalWorldMutPtr::::with_contiguous_pages( + platform, msg_args_phys_addr.trunc(), msg_args_size, )?; @@ -1295,6 +1355,7 @@ fn write_msg_args_to_normal_world( /// the normal world physical address. #[inline] fn write_non_ta_msg_args_to_normal_world( + platform: &'static Platform, msg_args: &OpteeMsgArgs, msg_args_phys_addr: u64, ) -> Result<(), OpteeSmcReturnCode> { @@ -1302,7 +1363,8 @@ fn write_non_ta_msg_args_to_normal_world( let mut blob = vec![0u8; msg_args_size]; msg_args.serialize(&mut blob)?; - let ptr = NormalWorldMutPtr::::with_contiguous_pages( + let ptr = NormalWorldMutPtr::::with_contiguous_pages( + platform, msg_args_phys_addr.trunc(), msg_args_size, )?; @@ -1319,6 +1381,7 @@ fn write_non_ta_msg_args_to_normal_world( #[expect(dead_code)] #[inline] fn write_rpc_args_to_normal_world( + platform: &'static Platform, msg_args: &OpteeMsgArgs, msg_args_phys_addr: u64, rpc_args: &OpteeRpcArgs, @@ -1332,7 +1395,11 @@ fn write_rpc_args_to_normal_world( let rpc_pa: usize = >::trunc(msg_args_phys_addr) .checked_add(msg_args_size) .ok_or(OpteeSmcReturnCode::EBadAddr)?; // RPC args are placed right after the main msg_args blob - let ptr = NormalWorldMutPtr::::with_contiguous_pages(rpc_pa, rpc_args_size)?; + let ptr = NormalWorldMutPtr::::with_contiguous_pages( + platform, + rpc_pa, + rpc_args_size, + )?; ptr.write_slice_at_offset(0, &blob)?; Ok(()) } diff --git a/litebox_runner_optee_on_linux_userland/Cargo.toml b/litebox_runner_optee_on_linux_userland/Cargo.toml index 87949ad380..b6cc9796a6 100644 --- a/litebox_runner_optee_on_linux_userland/Cargo.toml +++ b/litebox_runner_optee_on_linux_userland/Cargo.toml @@ -5,15 +5,13 @@ edition = "2024" [dependencies] anyhow = "1.0.97" -arrayvec = { version = "0.7.6", default-features = false } base64 = "0.22.1" clap = { version = "4.5.33", features = ["derive"] } litebox = { version = "0.1.0", path = "../litebox" } litebox_common_linux = { version = "0.1.0", path = "../litebox_common_linux" } litebox_common_optee = { version = "0.1.0", path = "../litebox_common_optee" } litebox_platform_linux_userland = { version = "0.1.0", path = "../litebox_platform_linux_userland", default-features = false, features = ["optee_syscall"] } -litebox_platform_multiplex = { version = "0.1.0", path = "../litebox_platform_multiplex", default-features = false, features = ["platform_linux_userland_with_optee_syscall"] } -litebox_shim_optee = { version = "0.1.0", path = "../litebox_shim_optee", default-features = false, features = ["platform_linux_userland"] } +litebox_shim_optee = { version = "0.1.0", path = "../litebox_shim_optee", default-features = false } litebox_syscall_rewriter = { version = "0.1.0", path = "../litebox_syscall_rewriter" } litebox_util_log = { version = "0.1.0", path = "../litebox_util_log", features = ["backend_tracing"] } tracing-subscriber = { version = "0.3.22", features = ["env-filter"] } diff --git a/litebox_runner_optee_on_linux_userland/src/lib.rs b/litebox_runner_optee_on_linux_userland/src/lib.rs index e2f0ea7d0b..1b56d1f574 100644 --- a/litebox_runner_optee_on_linux_userland/src/lib.rs +++ b/litebox_runner_optee_on_linux_userland/src/lib.rs @@ -4,8 +4,8 @@ use anyhow::{Context as _, Result}; use clap::Parser; use litebox_common_optee::{TeeUuid, UteeEntryFunc, UteeParamOwned}; -use litebox_platform_multiplex::Platform; -use litebox_shim_optee::session::session_manager; +use litebox_platform_linux_userland::LinuxUserland as Platform; +use litebox_shim_optee::session::SessionManager; use std::path::PathBuf; mod tests; @@ -80,8 +80,12 @@ pub fn run(cli_args: CliArgs) -> Result<()> { // TODO(jb): Clean up platform initialization once we have https://github.com/MSRSSP/litebox/issues/24 let platform = Platform::new(None); - litebox_platform_multiplex::set_platform(platform); - let shim_builder = litebox_shim_optee::OpteeShimBuilder::new(); + // One registry per `run`, minted beside the platform it is paired with, so + // `OpteeShimBuilder::new`'s "one registry per image" invariant holds by + // construction. Leaked because the shim stores it as `&'static`. + let session_manager: &'static SessionManager = + Box::leak(Box::new(SessionManager::new())); + let shim_builder = litebox_shim_optee::OpteeShimBuilder::new(platform, session_manager); let _litebox = shim_builder.litebox(); let shim = shim_builder.build(); @@ -105,7 +109,7 @@ pub fn run(cli_args: CliArgs) -> Result<()> { /// it can be loaded and run. Note that an OP-TEE TA does nothing without /// a client invoking commands on it. fn run_ta_with_default_commands( - shim: &litebox_shim_optee::OpteeShim, + shim: &litebox_shim_optee::OpteeShim, ldelf_bin: &[u8], ta_bin: &[u8], ) { @@ -113,7 +117,10 @@ fn run_ta_with_default_commands( let params = [const { UteeParamOwned::None }; UteeParamOwned::TEE_NUM_PARAMS]; if func_id == UteeEntryFunc::OpenSession { - let session_token = session_manager().try_acquire_open_session_token().unwrap(); + let session_token = shim + .session_manager() + .try_acquire_open_session_token() + .unwrap(); let session_id = session_token.session_id().unwrap(); let loaded_program = shim .load_ldelf(ldelf_bin, TeeUuid::default(), Some(ta_bin)) diff --git a/litebox_runner_optee_on_linux_userland/src/tests.rs b/litebox_runner_optee_on_linux_userland/src/tests.rs index 645055431e..cd7a9e6759 100644 --- a/litebox_runner_optee_on_linux_userland/src/tests.rs +++ b/litebox_runner_optee_on_linux_userland/src/tests.rs @@ -10,14 +10,14 @@ use litebox::utils::TruncateExt; use litebox_common_optee::{ TeeIdentity, TeeLogin, TeeParamType, TeeUuid, UteeEntryFunc, UteeParamOwned, UteeParams, }; -use litebox_shim_optee::session::session_manager; +use litebox_platform_linux_userland::LinuxUserland as Platform; use litebox_shim_optee::{LoadedProgram, UserConstPtr}; use serde::Deserialize; use std::path::PathBuf; /// Run the loaded TA with a sequence of test commands pub fn run_ta_with_test_commands( - shim: &litebox_shim_optee::OpteeShim, + shim: &litebox_shim_optee::OpteeShim, ldelf_bin: &[u8], ta_bin: &[u8], _prog_name: &str, @@ -27,7 +27,7 @@ pub fn run_ta_with_test_commands( let json_str = std::fs::read_to_string(json_path).unwrap(); serde_json::from_str(&json_str).unwrap() }; - let mut ta_info: Option = None; + let mut ta_info: Option> = None; // The active session id for the TA. Set at OpenSession and reused for the // subsequent InvokeCommand entries on the same persistent session. let mut session_id: Option = None; @@ -54,7 +54,10 @@ pub fn run_ta_with_test_commands( if func_id == UteeEntryFunc::OpenSession { let ta_head = litebox_common_optee::parse_ta_head(ta_bin) .expect("Failed to parse TA header from ta_bin"); - let mut session_token = session_manager().try_acquire_open_session_token().unwrap(); + let mut session_token = shim + .session_manager() + .try_acquire_open_session_token() + .unwrap(); let open_session_id = session_token.session_id().unwrap(); session_id = Some(open_session_id); // Emulate the client identity a real REE client would present. @@ -65,7 +68,8 @@ pub fn run_ta_with_test_commands( }, ClientIdentityJson::to_tee_identity, ); - session_manager().set_session_client_identity(open_session_id, Some(client_identity)); + shim.session_manager() + .set_session_client_identity(open_session_id, Some(client_identity)); let loaded = shim .load_ldelf(ldelf_bin, ta_head.uuid, Some(ta_bin)) .map_err(|_| { @@ -125,7 +129,7 @@ pub fn run_ta_with_test_commands( ); // TA stores results in the `UteeParams` structure and/or buffers it refers to. if let Some(params_address) = info.params_address { - let ptr = UserConstPtr::::from_usize(params_address); + let ptr = UserConstPtr::::from_usize(params_address); let params = ptr.read_at_offset(0).expect("Failed to read UteeParams"); handle_ta_command_output(¶ms); } @@ -152,7 +156,8 @@ fn handle_ta_command_output(params: &UteeParams) { TeeParamType::MemrefOutput | TeeParamType::MemrefInout => { if let Ok(Some((addr, len))) = params.get_values(idx) { let len: usize = len.trunc(); - let ptr: UserConstPtr = UserConstPtr::from_ptr(addr as *const u8); + let ptr: UserConstPtr = + UserConstPtr::::from_ptr(addr as *const u8); let slice = ptr.to_owned_slice(len).unwrap_or_default(); if slice.is_empty() { litebox_util_log::info!( diff --git a/litebox_service_heki/src/lib.rs b/litebox_service_heki/src/lib.rs index 9b9667d266..25add83d6c 100644 --- a/litebox_service_heki/src/lib.rs +++ b/litebox_service_heki/src/lib.rs @@ -43,8 +43,7 @@ use x509_cert::Certificate; /// policy decisions cannot be raced by VTL0 mutating the data behind them. pub struct Heki { /// The VTL0 capability every handler acts through. Owned rather than - /// borrowed: gate types are zero-sized, so this costs nothing and keeps the - /// gate out of every handler signature. + /// borrowed, which keeps the gate out of every handler signature. pub(crate) gate: P, pub(crate) module_memory_metadata: ModuleMemoryMetadataMap, system_certs: once_cell::race::OnceBox>, diff --git a/litebox_shim_optee/Cargo.toml b/litebox_shim_optee/Cargo.toml index 057a59ef7a..ac5fc774e5 100644 --- a/litebox_shim_optee/Cargo.toml +++ b/litebox_shim_optee/Cargo.toml @@ -5,14 +5,12 @@ edition = "2024" [dependencies] aes = { version = "0.7", default-features = false } -arrayvec = { version = "0.7.6", default-features = false } ctr = { version = "0.8", default-features = false } elf = { version = "0.8.0", default-features = false } hashbrown = "0.15.2" litebox = { path = "../litebox/", version = "0.1.0" } litebox_common_linux = { path = "../litebox_common_linux/", version = "0.1.0" } litebox_common_optee = { path = "../litebox_common_optee/", version = "0.1.0" } -litebox_platform_multiplex = { path = "../litebox_platform_multiplex/", version = "0.1.0", default-features = false } litebox_util_log = { version = "0.1.0", path = "../litebox_util_log" } hmac = { version = "0.12", default-features = false } num_enum = { version = "0.7.3", default-features = false } @@ -25,13 +23,8 @@ zerocopy = { version = "0.8", default-features = false, features = ["derive"] } zeroize = { version = "1.8", default-features = false, features = ["alloc"] } p384 = { version = "0.13.1", default-features = false, features = ["arithmetic", "ecdsa"] } -[features] -default = ["platform_lvbs"] -platform_linux_userland = ["litebox_platform_multiplex/platform_linux_userland_with_optee_syscall"] -platform_lvbs = ["litebox_platform_multiplex/platform_lvbs_with_optee_syscall"] - [lints] workspace = true [dev-dependencies] -litebox_platform_multiplex = { path = "../litebox_platform_multiplex/", version = "0.1.0", default-features = false, features = ["platform_linux_userland_with_optee_syscall"] } +litebox_platform_linux_userland = { path = "../litebox_platform_linux_userland/", version = "0.1.0", default-features = false, features = ["optee_syscall"] } diff --git a/litebox_shim_optee/src/idk.rs b/litebox_shim_optee/src/idk.rs index 282aea63b1..745b1e51fb 100644 --- a/litebox_shim_optee/src/idk.rs +++ b/litebox_shim_optee/src/idk.rs @@ -2,7 +2,7 @@ // Licensed under the MIT license. use crate::NormalWorldMutPtr; -use litebox::{mm::linux::PAGE_SIZE, platform::CrngProvider, utils::TruncateExt}; +use litebox::{mm::linux::PAGE_SIZE, utils::TruncateExt}; use litebox_common_linux::errno::Errno; use num_enum::TryFromPrimitive; use p384::{NonZeroScalar, elliptic_curve::sec1::ToEncodedPoint}; @@ -40,8 +40,12 @@ enum EcdsaCurve { P521 = 0x03, } -pub fn generate_identity_signing_key(public_key_pa: u64, key_alg: u64) -> i64 { - match generate_identity_signing_key_inner(public_key_pa, key_alg) { +pub fn generate_identity_signing_key( + platform: &Platform, + public_key_pa: u64, + key_alg: u64, +) -> i64 { + match generate_identity_signing_key_inner(platform, public_key_pa, key_alg) { Ok(res) => res, Err(e) => e.as_neg().into(), } @@ -61,16 +65,21 @@ pub fn generate_identity_signing_key(public_key_pa: u64, key_alg: u64) -> i64 { /// This function assumes that the caller prepares a buffer at the given physical /// address (in a single or contiguous physical memory page(s)) whose length is equal to /// or greater than `IDENTITY_SIGNING_PUBLIC_KEY_LEN`. -fn generate_identity_signing_key_inner(public_key_pa: u64, key_alg: u64) -> Result { +fn generate_identity_signing_key_inner( + platform: &Platform, + public_key_pa: u64, + key_alg: u64, +) -> Result { validate_key_algorithm(key_alg)?; let pubkey_ptr = - NormalWorldMutPtr::<[u8; IDENTITY_SIGNING_PUBLIC_KEY_LEN], PAGE_SIZE>::with_usize( + NormalWorldMutPtr::::with_usize( + platform, public_key_pa.trunc(), ) .map_err(|_| Errno::EINVAL)?; - let key_pair = get_identity_signing_key_pair()?; + let key_pair = get_identity_signing_key_pair(platform)?; pubkey_ptr .write_at_offset(0, key_pair.public_key) .map_err(|_| Errno::EFAULT)?; @@ -100,9 +109,11 @@ fn validate_key_algorithm(key_alg: u64) -> Result<(), Errno> { } } -fn get_identity_signing_key_pair() -> Result<&'static IdentitySigningKeyPair, Errno> { +fn get_identity_signing_key_pair( + platform: &Platform, +) -> Result<&'static IdentitySigningKeyPair, Errno> { IDENTITY_SIGNING_KEY_PAIR.try_call_once(|| { - let private_key = generate_identity_signing_private_key()?; + let private_key = generate_identity_signing_private_key(platform)?; let public_key = identity_signing_public_key_from_private_key(&private_key)?; Ok(IdentitySigningKeyPair { private_key, @@ -111,12 +122,13 @@ fn get_identity_signing_key_pair() -> Result<&'static IdentitySigningKeyPair, Er }) } -fn generate_identity_signing_private_key() --> Result, Errno> { +fn generate_identity_signing_private_key( + platform: &Platform, +) -> Result, Errno> { let mut private_key_bytes = Zeroizing::new([0u8; IDENTITY_SIGNING_PRIVATE_KEY_LEN]); for _ in 0..MAX_KEYGEN_ATTEMPT { - litebox_platform_multiplex::platform().fill_bytes_crng(&mut *private_key_bytes); + platform.fill_bytes_crng(&mut *private_key_bytes); if is_valid_identity_signing_private_key(&private_key_bytes) { return Ok(private_key_bytes); } @@ -160,7 +172,8 @@ mod tests { let message = b"IDK_S signing test message"; let _task = init_platform(); - let private_key = generate_identity_signing_private_key().unwrap(); + let shim = crate::syscalls::tests::shim_builder().build(); + let private_key = generate_identity_signing_private_key(shim.platform()).unwrap(); assert!(is_valid_identity_signing_private_key(&private_key)); let signing_key = SigningKey::from_slice(&private_key[..]).unwrap(); let public_key = identity_signing_public_key_from_private_key(&private_key).unwrap(); diff --git a/litebox_shim_optee/src/lib.rs b/litebox_shim_optee/src/lib.rs index f4bd367c28..2e9950c139 100644 --- a/litebox_shim_optee/src/lib.rs +++ b/litebox_shim_optee/src/lib.rs @@ -22,13 +22,12 @@ use litebox::{ shim::ContinueOperation, utils::TruncateExt, }; -use litebox_common_linux::{MapFlags, ProtFlags, errno::Errno, vmap::GlobalVmapManager}; +use litebox_common_linux::{MapFlags, ProtFlags, errno::Errno}; use litebox_common_optee::{ LdelfArg, LdelfSyscallRequest, SyscallRequest, TaFlags, TeeAlgorithm, TeeAlgorithmClass, TeeAttributeType, TeeCrypStateHandle, TeeHandleFlag, TeeIdentity, TeeLogin, TeeObjHandle, TeeObjectInfo, TeeObjectType, TeeOperationMode, TeeResult, TeeUuid, UteeAttribute, }; -use litebox_platform_multiplex::Platform; pub mod loader; pub mod session; @@ -36,7 +35,6 @@ pub(crate) mod syscalls; pub mod msg_handler; -#[cfg(feature = "platform_lvbs")] pub mod idk; // Re-export session management types for convenience @@ -44,14 +42,53 @@ pub use session::{OpenSessionTarget, SessionManager, SessionToken, TaInstance}; const MAX_KERNEL_BUF_SIZE: usize = 0x80_000; -pub struct OpteeShimEntrypoints { - task: Task, +/// Aggregate bound capturing everything the OP-TEE shim requires of a platform. +/// +/// This exists so that the (many) `impl` blocks throughout the shim can be written +/// as `impl` rather than repeating a large `where` clause. +/// +/// `VmapManager` is part of the contract because the shim reads and writes normal-world +/// physical memory through [`NormalWorldConstPtr`]/[`NormalWorldMutPtr`]. +pub trait OpteeShimPlatform: + litebox::platform::RawPointerProvider + + litebox::platform::TimeProvider + + litebox::platform::PageManagementProvider<{ PAGE_SIZE }> + + litebox::mm::linux::VmemPageFaultHandler + + litebox::platform::RawMutexProvider + + litebox::sync::RawSyncPrimitivesProvider + + litebox::platform::CrngProvider + + litebox::platform::SystemInfoProvider + + litebox::platform::ArchSpecificProvider + + litebox::platform::DerivedKeyProvider + + litebox_common_linux::vmap::VmapManager<{ PAGE_SIZE }> + + 'static +{ +} + +impl OpteeShimPlatform for T where + T: litebox::platform::RawPointerProvider + + litebox::platform::TimeProvider + + litebox::platform::PageManagementProvider<{ PAGE_SIZE }> + + litebox::mm::linux::VmemPageFaultHandler + + litebox::platform::RawMutexProvider + + litebox::sync::RawSyncPrimitivesProvider + + litebox::platform::CrngProvider + + litebox::platform::SystemInfoProvider + + litebox::platform::ArchSpecificProvider + + litebox::platform::DerivedKeyProvider + + litebox_common_linux::vmap::VmapManager<{ PAGE_SIZE }> + + 'static +{ +} + +pub struct OpteeShimEntrypoints { + task: Task, // The task should not be moved once it's bound to a platform thread so that // we preserve the ability to use TLS in the future. _not_send: core::marker::PhantomData<*const ()>, } -impl litebox::shim::EnterShim for OpteeShimEntrypoints { +impl litebox::shim::EnterShim for OpteeShimEntrypoints { type ExecutionContext = litebox_common_linux::PtRegs; fn init(&self, ctx: &mut Self::ExecutionContext) -> ContinueOperation { @@ -105,35 +142,41 @@ impl litebox::shim::EnterShim for OpteeShimEntrypoints { } } -impl OpteeShimEntrypoints { +impl OpteeShimEntrypoints { fn enter_shim( &self, _is_init: bool, ctx: &mut litebox_common_linux::PtRegs, - f: impl FnOnce(&Task, &mut litebox_common_linux::PtRegs) -> ContinueOperation, + f: impl FnOnce(&Task, &mut litebox_common_linux::PtRegs) -> ContinueOperation, ) -> ContinueOperation { f(&self.task, ctx) } } /// The shim entry point structure. -pub struct OpteeShimBuilder { +pub struct OpteeShimBuilder { platform: &'static Platform, + session_manager: &'static session::SessionManager, litebox: LiteBox, } -impl Default for OpteeShimBuilder { - fn default() -> Self { - Self::new() - } -} - -impl OpteeShimBuilder { - /// Returns a new shim builder. - pub fn new() -> Self { - let platform = litebox_platform_multiplex::platform(); +impl OpteeShimBuilder { + /// Returns a new shim builder for `platform`. + /// + /// # Invariant + /// + /// `session_manager` must be the one session registry of the LiteBox image this + /// shim runs in — per process for a userland runner, per VTL1 kernel image for + /// LVBS, where all VPs share it. Shims are built per session, so passing a second + /// registry splits session state across two views. Reach the registry via + /// [`OpteeShim::session_manager`] where a shim is already in hand. + pub fn new( + platform: &'static Platform, + session_manager: &'static session::SessionManager, + ) -> Self { Self { platform, + session_manager, litebox: LiteBox::new(platform), } } @@ -144,9 +187,10 @@ impl OpteeShimBuilder { } /// Build the shim. - pub fn build(self) -> OpteeShim { + pub fn build(self) -> OpteeShim { let global = Arc::new(GlobalState { platform: self.platform, + session_manager: self.session_manager, boot_instant: TimeProvider::now(self.platform), pm: PageManager::new(&self.litebox), _litebox: self.litebox, @@ -158,9 +202,11 @@ impl OpteeShimBuilder { } /// Global shim state, shared across all tasks. -struct GlobalState { +struct GlobalState { /// The platform instance used throughout the shim. platform: &'static Platform, + /// The session registry, owned by the composition root (the runner). + session_manager: &'static session::SessionManager, /// Monotonic baseline captured when this instance was created; the /// arbitrary origin for GP "system time" (`TEE_GetSystemTime`). /// See [`GlobalState::system_time`]. @@ -181,7 +227,7 @@ struct GlobalState { pta_busy: spin::mutex::SpinMutex>, } -impl GlobalState { +impl GlobalState { /// Store the TA binary associated with the given TA UUID. /// /// Returns `true` if the binary was successfully stored, `false` if the binary's @@ -236,15 +282,22 @@ impl GlobalState { } } -type UserMutPtr = ::RawMutPointer; -pub type UserConstPtr = ::RawConstPointer; +type UserMutPtr = + ::RawMutPointer; +pub type UserConstPtr = + ::RawConstPointer; -type MutPtr = ::RawMutPointer; +type MutPtr = ::RawMutPointer; -#[derive(Clone)] -pub struct OpteeShim(Arc); +pub struct OpteeShim(Arc>); + +impl Clone for OpteeShim { + fn clone(&self) -> Self { + Self(self.0.clone()) + } +} -impl OpteeShim { +impl OpteeShim { /// Load the given `ldelf` binary into memory while making it ready to load the TA binary specified /// by `ta_uuid` (and optionally `ta_bin`). /// @@ -258,7 +311,7 @@ impl OpteeShim { ldelf_bin: &[u8], ta_uuid: TeeUuid, ta_bin: Option<&[u8]>, - ) -> Result { + ) -> Result, loader::elf::ElfLoaderError> { let entrypoints = crate::OpteeShimEntrypoints { _not_send: core::marker::PhantomData, task: Task { @@ -305,6 +358,20 @@ impl OpteeShim { }) } + /// The platform this shim was built against. + #[must_use] + pub fn platform(&self) -> &'static Platform { + self.0.platform + } + + /// The session registry this shim was built against. + /// + /// See the invariant on [`OpteeShimBuilder::new`]. + #[must_use] + pub fn session_manager(&self) -> &'static session::SessionManager { + self.0.session_manager + } + /// Get the global page manager pub fn page_manager(&self) -> &PageManager { &self.0.pm @@ -327,7 +394,7 @@ impl OpteeShim { } } -impl OpteeShimEntrypoints { +impl OpteeShimEntrypoints { /// Load the CPU context to (re)enter the loaded TA. pub fn load_ta_context( &self, @@ -345,9 +412,9 @@ impl OpteeShimEntrypoints { } /// Information about a loaded TA program. -pub struct LoadedProgram { +pub struct LoadedProgram { /// The entrypoints for the TA (syscall handling, context loading, etc.) - pub entrypoints: Option, + pub entrypoints: Option>, /// Address where TA parameters (`UteeParams`) are stored on the stack. /// /// This address is constant for the lifetime of the TA instance because: @@ -363,7 +430,7 @@ pub struct LoadedProgram { pub ta_flags: TaFlags, } -impl Task { +impl Task { /// Handle OP-TEE syscalls /// /// It dispatches the syscall handling based on the current thread initialization state (ldelf or TA). @@ -449,7 +516,9 @@ impl Task { name_len, index, } => match name.to_owned_slice(name_len) { - Some(name) => Task::sys_get_property_name_to_index(prop_set, &name, index), + Some(name) => { + Task::::sys_get_property_name_to_index(prop_set, &name, index) + } None => Err(TeeResult::BadParameters), }, SyscallRequest::OpenTaSession { @@ -774,7 +843,7 @@ impl Task { /// Load `ldelf` and prepare the stack and CPU context for it with the given TA UUID. fn load_ldelf( &self, - mut loader: crate::loader::elf::ElfLoader<'_>, + mut loader: crate::loader::elf::ElfLoader<'_, Platform>, ta_uuid: TeeUuid, ) -> Result<(), ElfLoaderError> { let ldelf_arg = LdelfArg::new(ta_uuid); @@ -844,7 +913,7 @@ impl Task { login: TeeLogin::Public, uuid: TeeUuid::NIL, }, - |session_id| crate::session::session_manager().client_identity(session_id), + |session_id| self.global.session_manager.client_identity(session_id), ) } @@ -893,12 +962,12 @@ impl Task { /// every TA entry. #[cfg(target_arch = "x86_64")] fn restore_guest_tls(&self) { - use litebox::platform::ArchSpecificProvider as _; let addr = self.tls_base_addr.get(); if addr == 0 { return; // TLS not allocated yet } - litebox_platform_multiplex::platform() + self.global + .platform .set_arch_specific_register(&litebox::platform::ArchSpecificRegister::FsBase, addr) .expect("requires guaranteed platform support for FsBase"); } @@ -912,7 +981,7 @@ impl Task { _ => None, }; if let Some(ldelf_arg_address) = ldelf_arg_address { - let ldelf_arg_ptr = UserConstPtr::::from_usize(ldelf_arg_address); + let ldelf_arg_ptr = UserConstPtr::::from_usize(ldelf_arg_address); if let Some(ldef_arg) = ldelf_arg_ptr.read_at_offset(0) { let entry_func = ldef_arg.entry_func.trunc(); // If `ldelf` has been successfully executed, it loads the given TA and stores the TA's entry @@ -943,7 +1012,7 @@ impl Task { /// Since the TA entry point is provided by `ldelf` which is untrusted, we checks whether /// the given `addr` is within the user space. pub(crate) fn set_ta_entry_point(&self, addr: usize) { - let ptr = UserConstPtr::::from_usize(addr); + let ptr = UserConstPtr::::from_usize(addr); if ptr.read_at_offset(0).is_some() { self.ta_entry_point.set(addr); } @@ -956,17 +1025,23 @@ impl Task { } #[inline] -fn handle_cipher_update_or_final( - task: &Task, +fn handle_cipher_update_or_final( + task: &Task, state: TeeCrypStateHandle, - src: UserConstPtr, + src: UserConstPtr, src_len: usize, - dst: UserMutPtr, - dst_len: UserMutPtr, + dst: UserMutPtr, + dst_len: UserMutPtr, syscall_fn: F, ) -> Result<(), TeeResult> where - F: Fn(&Task, TeeCrypStateHandle, &[u8], &mut [u8], &mut usize) -> Result<(), TeeResult>, + F: Fn( + &Task, + TeeCrypStateHandle, + &[u8], + &mut [u8], + &mut usize, + ) -> Result<(), TeeResult>, { if let Some(src_slice) = src.to_owned_slice(src_len) && let Some(length) = dst_len.read_at_offset(0) @@ -1079,7 +1154,7 @@ impl TeeObjMap { inner.insert(handle, tee_obj.clone()); } - pub fn populate( + pub fn populate( &self, handle: TeeObjHandle, user_attrs: &[UteeAttribute], @@ -1099,7 +1174,7 @@ impl TeeObjMap { if key_len > MAX_KERNEL_BUF_SIZE { return Err(TeeResult::BadParameters); } - let key_ptr = UserConstPtr::::from_usize(key_addr); + let key_ptr = UserConstPtr::::from_usize(key_addr); let Some(key_box) = key_ptr.to_owned_slice(key_len) else { return Err(TeeResult::BadParameters); }; @@ -1372,8 +1447,8 @@ impl TaUuidMap { /// Per-instance TA state which can be shared between sessions if it is /// a single-instance multi-session TA. The active session id is carried /// per entry (see [`Task::current_session_id`]). -struct Task { - global: Arc, +struct Task { + global: Arc>, thread: ThreadState, /// TA UUID ta_app_id: TeeUuid, @@ -1415,7 +1490,7 @@ impl ThreadState { } } -impl Drop for Task { +impl Drop for Task { fn drop(&mut self) { self.close_all_pta_sessions(); } @@ -1527,28 +1602,18 @@ impl SessionIdPool { } } -/// Type-level marker for the normal-world physical-pointer provider. -pub enum Vmap {} - -impl GlobalVmapManager for Vmap { - type Manager = litebox_platform_multiplex::Platform; - fn manager() -> &'static Self::Manager { - litebox_platform_multiplex::platform() - } -} - -pub type NormalWorldConstPtr = - litebox_common_linux::physical_pointers::PhysConstPtr; -pub type NormalWorldMutPtr = - litebox_common_linux::physical_pointers::PhysMutPtr; +pub type NormalWorldConstPtr<'a, Platform, T, const ALIGN: usize> = + litebox_common_linux::physical_pointers::PhysConstPtr<'a, Platform, T, ALIGN>; +pub type NormalWorldMutPtr<'a, Platform, T, const ALIGN: usize> = + litebox_common_linux::physical_pointers::PhysMutPtr<'a, Platform, T, ALIGN>; #[cfg(test)] mod test_utils { use super::*; - impl GlobalState { + impl GlobalState { /// Make a new task with default values for testing. - pub(crate) fn new_test_task(self: Arc) -> Task { + pub(crate) fn new_test_task(self: Arc) -> Task { Task { global: self.clone(), thread: ThreadState::new(), diff --git a/litebox_shim_optee/src/loader/elf.rs b/litebox_shim_optee/src/loader/elf.rs index b9c0187a0c..2579a4e543 100644 --- a/litebox_shim_optee/src/loader/elf.rs +++ b/litebox_shim_optee/src/loader/elf.rs @@ -20,7 +20,7 @@ use crate::{MutPtr, Task, ThreadInitState, UserMutPtr}; use litebox::{ mm::linux::{MappingError, PAGE_SIZE}, - platform::{RawConstPointer as _, RawMutPointer as _, SystemInfoProvider as _}, + platform::{RawConstPointer as _, RawMutPointer as _}, utils::TruncateExt, }; use litebox_common_linux::{ @@ -32,12 +32,16 @@ use litebox_common_optee::{LdelfArg, TeeUuid}; use thiserror::Error; /// An ELF file loaded in memory -struct ElfFileInMemory<'a> { - task: &'a Task, +struct ElfFileInMemory<'a, Platform: crate::OpteeShimPlatform> { + task: &'a Task, buffer: alloc::boxed::Box<[u8]>, } -fn read_at(elf: &ElfFileInMemory, offset: u64, buf: &mut [u8]) -> Result<(), Errno> { +fn read_at( + elf: &ElfFileInMemory, + offset: u64, + buf: &mut [u8], +) -> Result<(), Errno> { if buf.is_empty() { return Ok(()); } @@ -52,8 +56,8 @@ fn read_at(elf: &ElfFileInMemory, offset: u64, buf: &mut [u8]) -> Result<(), Err Ok(()) } -impl<'a> ElfFileInMemory<'a> { - fn new(task: &'a Task, elf_buf: &[u8]) -> Self { +impl<'a, Platform: crate::OpteeShimPlatform> ElfFileInMemory<'a, Platform> { + fn new(task: &'a Task, elf_buf: &[u8]) -> Self { Self { task, buffer: elf_buf.into(), @@ -61,7 +65,9 @@ impl<'a> ElfFileInMemory<'a> { } } -impl litebox_common_linux::loader::ReadAt for &'_ ElfFileInMemory<'_> { +impl litebox_common_linux::loader::ReadAt + for &'_ ElfFileInMemory<'_, Platform> +{ type Error = Errno; fn read_at(&mut self, offset: u64, buf: &mut [u8]) -> Result<(), Self::Error> { @@ -73,7 +79,9 @@ impl litebox_common_linux::loader::ReadAt for &'_ ElfFileInMemory<'_> { } } -impl litebox_common_linux::loader::MapMemory for ElfFileInMemory<'_> { +impl litebox_common_linux::loader::MapMemory + for ElfFileInMemory<'_, Platform> +{ type Error = Errno; fn reserve(&mut self, len: usize, align: usize) -> Result { @@ -105,10 +113,12 @@ impl litebox_common_linux::loader::MapMemory for ElfFileInMemory<'_> { align, ); if let Some((addr, size)) = regions.head_unmap { - self.task.sys_munmap(MutPtr::from_usize(addr), size)?; + self.task + .sys_munmap(MutPtr::::from_usize(addr), size)?; } if let Some((addr, size)) = regions.tail_unmap { - self.task.sys_munmap(MutPtr::from_usize(addr), size)?; + self.task + .sys_munmap(MutPtr::::from_usize(addr), size)?; } Ok(regions.aligned_ptr) } @@ -147,14 +157,18 @@ impl litebox_common_linux::loader::MapMemory for ElfFileInMemory<'_> { let available = self.buffer.len() - offset; let end = offset + core::cmp::min(len, available); let src = &self.buffer[offset..end]; - let user_ptr = UserMutPtr::::from_usize(mapped_addr); + let user_ptr = UserMutPtr::::from_usize(mapped_addr); user_ptr .copy_from_slice(0, src) .ok_or(ElfLoaderError::MappingError(MappingError::OutOfMemory))?; } self.task - .sys_mprotect(UserMutPtr::from_usize(mapped_addr), len, prot.flags()) + .sys_mprotect( + UserMutPtr::::from_usize(mapped_addr), + len, + prot.flags(), + ) .map_err(ElfLoaderError::ProtectError)?; Ok(()) } @@ -187,24 +201,24 @@ impl litebox_common_linux::loader::MapMemory for ElfFileInMemory<'_> { len: usize, prot: &litebox_common_linux::loader::Protection, ) -> Result<(), Self::Error> { - let addr = crate::MutPtr::::from_usize(address); + let addr = crate::MutPtr::::from_usize(address); self.task.sys_mprotect(addr, len, prot.flags()) } } /// Loader for ELF files -pub(crate) struct ElfLoader<'a> { - main: FileAndParsed<'a>, +pub(crate) struct ElfLoader<'a, Platform: crate::OpteeShimPlatform> { + main: FileAndParsed<'a, Platform>, is_ldelf: bool, } -struct FileAndParsed<'a> { - file: ElfFileInMemory<'a>, +struct FileAndParsed<'a, Platform: crate::OpteeShimPlatform> { + file: ElfFileInMemory<'a, Platform>, parsed: ElfParsedFile, } -impl<'a> FileAndParsed<'a> { - fn new(task: &'a Task, elf_buf: &[u8]) -> Result { +impl<'a, Platform: crate::OpteeShimPlatform> FileAndParsed<'a, Platform> { + fn new(task: &'a Task, elf_buf: &[u8]) -> Result { let file = ElfFileInMemory::new(task, elf_buf); let mut parsed = litebox_common_linux::loader::ElfParsedFile::parse(&mut &file) .map_err(ElfLoaderError::ParseError)?; @@ -216,9 +230,13 @@ impl<'a> FileAndParsed<'a> { } } -impl<'a> ElfLoader<'a> { +impl<'a, Platform: crate::OpteeShimPlatform> ElfLoader<'a, Platform> { /// Parse a given ELF binary in memory. - pub fn new(task: &'a Task, elf_bin: &[u8], is_ldelf: bool) -> Result { + pub fn new( + task: &'a Task, + elf_bin: &[u8], + is_ldelf: bool, + ) -> Result { let main = FileAndParsed::new(task, elf_bin)?; Ok(Self { main, is_ldelf }) } @@ -231,7 +249,7 @@ impl<'a> ElfLoader<'a> { /// `entry_point - e_entry`. The two agree as long as the first `PT_LOAD` /// starts at vaddr 0, which `ldelf` assumes too. pub(crate) fn ta_trampoline_relative_page_range( - task: &'a Task, + task: &'a Task, ta_uuid: &TeeUuid, ) -> Result>, ElfLoaderError> { let ta_bin = task diff --git a/litebox_shim_optee/src/loader/ta_stack.rs b/litebox_shim_optee/src/loader/ta_stack.rs index a0057a78b7..365a30b248 100644 --- a/litebox_shim_optee/src/loader/ta_stack.rs +++ b/litebox_shim_optee/src/loader/ta_stack.rs @@ -10,7 +10,7 @@ use litebox::{ use litebox_common_optee::{LdelfArg, TeeParamType, UteeParamOwned, UteeParams}; use zerocopy::IntoBytes; -use crate::{Platform, UserMutPtr}; +use crate::UserMutPtr; #[inline] fn align_down(addr: usize, align: usize) -> usize { @@ -49,9 +49,9 @@ fn align_down(addr: usize, align: usize) -> usize { /// - rcx: command ID /// /// NOTE: The above layout diagram is for 64-bit processes. -pub struct TaStack { +pub struct TaStack { /// The top of the stack (base address) - stack_top: UserMutPtr, + stack_top: UserMutPtr, /// The length of the stack len: usize, /// The current position of the stack pointer @@ -64,14 +64,14 @@ pub struct TaStack { ldelf_arg_pos: Option, } -impl TaStack { +impl TaStack { /// Stack alignment required by libc ABI (not for TAs but for compatibility) const STACK_ALIGNMENT: usize = 16; /// Create a new stack for the user process. /// /// `stack_top` and `len` must be aligned to [`Self::STACK_ALIGNMENT`] - pub(super) fn new(stack_top: UserMutPtr, len: usize) -> Option { + pub(super) fn new(stack_top: UserMutPtr, len: usize) -> Option { if !stack_top.as_usize().is_multiple_of(Self::STACK_ALIGNMENT) || !len.is_multiple_of(Self::STACK_ALIGNMENT) { @@ -289,9 +289,12 @@ impl TaStack { /// # Safety /// The caller must ensure that `sp` is a valid stack pointer and is not currently used. /// Normally, `sp` should be the return value of this function's previous call (with `None`). -pub(crate) fn allocate_stack(task: &crate::Task, stack_base: Option) -> Option { +pub(crate) fn allocate_stack( + task: &crate::Task, + stack_base: Option, +) -> Option> { let sp = if let Some(stack_base) = stack_base { - UserMutPtr::from_usize(stack_base) + UserMutPtr::::from_usize(stack_base) } else { let length = litebox::mm::linux::NonZeroPageSize::new(super::DEFAULT_STACK_SIZE) .expect("DEFAULT_STACK_SIZE is not page-aligned"); diff --git a/litebox_shim_optee/src/msg_handler.rs b/litebox_shim_optee/src/msg_handler.rs index 3f73043b5b..ea67a9e4f5 100644 --- a/litebox_shim_optee/src/msg_handler.rs +++ b/litebox_shim_optee/src/msg_handler.rs @@ -179,7 +179,8 @@ fn parse_optee_msg_args( /// ``` /// /// Returns `(main_args, Option)`. -pub fn read_optee_msg_args_from_phys( +pub fn read_optee_msg_args_from_phys( + platform: &Platform, phys_addr: usize, has_rpc_arg: bool, ) -> Result<(Box, Option>), OpteeSmcReturnCode> { @@ -193,9 +194,10 @@ pub fn read_optee_msg_args_from_phys( let mut blob = alloc::vec![0u8; copy_size]; - let blob_ptr = - NormalWorldConstPtr::::with_contiguous_pages(phys_addr, copy_size) - .map_err(|_| OpteeSmcReturnCode::EBadAddr)?; + let blob_ptr = NormalWorldConstPtr::::with_contiguous_pages( + platform, phys_addr, copy_size, + ) + .map_err(|_| OpteeSmcReturnCode::EBadAddr)?; blob_ptr .read_slice_at_offset(0, &mut blob) .map_err(|_| OpteeSmcReturnCode::EBadAddr)?; @@ -207,9 +209,10 @@ pub fn read_optee_msg_args_from_phys( /// It returns an `OpteeSmcResult` representing the result of the SMC call or `OpteeMsgArgs` it contains /// if the SMC call involves with an OP-TEE message which should be handled by /// `handle_optee_msg_args` or `handle_ta_request`. -pub fn handle_optee_smc_args( - smc: &mut OpteeSmcArgs, -) -> Result, OpteeSmcReturnCode> { +pub fn handle_optee_smc_args<'a, Platform: crate::OpteeShimPlatform>( + platform: &Platform, + smc: &'a mut OpteeSmcArgs, +) -> Result, OpteeSmcReturnCode> { let func_id = smc.func_id()?; #[cfg(debug_assertions)] litebox_util_log::debug!( @@ -220,7 +223,7 @@ pub fn handle_optee_smc_args( OpteeSmcFunction::CallWithArg => { let msg_args_addr = smc.optee_msg_args_phys_addr()?; let msg_args_addr: usize = msg_args_addr.trunc(); - let (msg_args, _) = read_optee_msg_args_from_phys(msg_args_addr, false)?; + let (msg_args, _) = read_optee_msg_args_from_phys(platform, msg_args_addr, false)?; Ok(OpteeSmcResult::CallWithArg { msg_args, rpc_args: None, @@ -230,7 +233,8 @@ pub fn handle_optee_smc_args( OpteeSmcFunction::CallWithRpcArg => { let msg_args_addr = smc.optee_msg_args_phys_addr()?; let msg_args_addr: usize = msg_args_addr.trunc(); - let (msg_args, rpc_args) = read_optee_msg_args_from_phys(msg_args_addr, true)?; + let (msg_args, rpc_args) = + read_optee_msg_args_from_phys(platform, msg_args_addr, true)?; Ok(OpteeSmcResult::CallWithArg { msg_args, rpc_args, @@ -250,7 +254,7 @@ pub fn handle_optee_smc_args( main_max + optee_msg_args_total_size(OpteeRpcArgs::MAX_RPC_ARG_PARAM_COUNT.trunc()); let mut blob = alloc::vec![0u8; copy_size]; - shm_info.read_at(offset, &mut blob)?; + shm_info.read_at(platform, offset, &mut blob)?; let (msg_args, rpc_args) = parse_optee_msg_args(&blob, true)?; // Compute the physical address of `OpteeMsgArgs` @@ -328,7 +332,10 @@ pub fn handle_optee_smc_args( /// If an OP-TEE message involves with a TA request, it simply returns /// `Err(OpteeSmcReturnCode::Ok)` while expecting that the caller will handle /// the message with `handle_ta_request`. -pub fn handle_optee_msg_args(msg_args: &OpteeMsgArgs) -> Result<(), OpteeSmcReturnCode> { +pub fn handle_optee_msg_args( + platform: &Platform, + msg_args: &OpteeMsgArgs, +) -> Result<(), OpteeSmcReturnCode> { msg_args.validate()?; match msg_args.cmd { OpteeMessageCommand::RegisterShm => { @@ -349,6 +356,7 @@ pub fn handle_optee_msg_args(msg_args: &OpteeMsgArgs) -> Result<(), OpteeSmcRetu .ok_or(OpteeSmcReturnCode::ENomem)?; let aligned_size = page_align_up(size).ok_or(OpteeSmcReturnCode::ENomem)?; shm_ref_map().register_shm( + platform, shm_ref_pages_data_phys_addr, page_offset, tmem.size, @@ -401,7 +409,8 @@ pub struct TaRequestInfo { /// It copies the entire parameter data from the normal world shared memory into the secure world's /// memory to create `UteeParamOwned` structures to avoid potential data corruption during TA /// execution. -pub fn decode_ta_request( +pub fn decode_ta_request( + platform: &Platform, msg_args: &OpteeMsgArgs, ) -> Result, OpteeSmcReturnCode> { let ta_entry_func: UteeEntryFunc = msg_args.cmd.try_into()?; @@ -511,13 +520,13 @@ pub fn decode_ta_request( let tmem = param.get_param_tmem().ok_or(OpteeSmcReturnCode::EBadCmd)?; let data_size = checked_memref_size(tmem.size)?; let shm_info = get_shm_info_from_optee_msg_param_tmem(tmem)?; - build_memref_input(&shm_info, data_size)? + build_memref_input(platform, &shm_info, data_size)? } OpteeMsgAttrType::RmemInput => { let rmem = param.get_param_rmem().ok_or(OpteeSmcReturnCode::EBadCmd)?; let data_size = checked_memref_size(rmem.size)?; let shm_info = get_shm_info_from_optee_msg_param_rmem(rmem)?; - build_memref_input(&shm_info, data_size)? + build_memref_input(platform, &shm_info, data_size)? } OpteeMsgAttrType::TmemOutput => { let tmem = param.get_param_tmem().ok_or(OpteeSmcReturnCode::EBadCmd)?; @@ -541,7 +550,7 @@ pub fn decode_ta_request( let shm_info = get_shm_info_from_optee_msg_param_tmem(tmem)?; ta_req_info.out_shm_info[i] = Some(shm_info.clone()); - build_memref_inout(&shm_info, buffer_size)? + build_memref_inout(platform, &shm_info, buffer_size)? } OpteeMsgAttrType::RmemInout => { let rmem = param.get_param_rmem().ok_or(OpteeSmcReturnCode::EBadCmd)?; @@ -549,7 +558,7 @@ pub fn decode_ta_request( let shm_info = get_shm_info_from_optee_msg_param_rmem(rmem)?; ta_req_info.out_shm_info[i] = Some(shm_info.clone()); - build_memref_inout(&shm_info, buffer_size)? + build_memref_inout(platform, &shm_info, buffer_size)? } _ => return Err(OpteeSmcReturnCode::EBadCmd), }; @@ -559,22 +568,24 @@ pub fn decode_ta_request( } #[inline] -fn build_memref_input( +fn build_memref_input( + platform: &Platform, shm_info: &ShmInfo, data_size: usize, ) -> Result { let mut data = alloc::vec![0u8; data_size]; - shm_info.read_at(0, &mut data)?; + shm_info.read_at(platform, 0, &mut data)?; Ok(UteeParamOwned::MemrefInput { data: data.into() }) } #[inline] -fn build_memref_inout( +fn build_memref_inout( + platform: &Platform, shm_info: &ShmInfo, buffer_size: usize, ) -> Result { let mut buffer = alloc::vec![0u8; buffer_size]; - shm_info.read_at(0, &mut buffer)?; + shm_info.read_at(platform, 0, &mut buffer)?; Ok(UteeParamOwned::MemrefInout { data: buffer.into(), buffer_size, @@ -590,7 +601,8 @@ fn build_memref_inout( /// `ta_params` is a reference to `UteeParams` structure that stores TA's output within its memory. /// `ta_req_info` refers to the decoded TA request information including the normal world /// shared memory addresses to write back output data. -pub fn update_optee_msg_args( +pub fn update_optee_msg_args( + platform: &Platform, return_code: TeeResult, return_origin: TeeOrigin, session_id: Option, @@ -646,7 +658,7 @@ pub fn update_optee_msg_args( // SAFETY // `addr` is expected to be a valid address of a TA and `addr + len` does not // exceed the TA's memory region. - let ptr = crate::UserConstPtr::::from_usize(addr.trunc()); + let ptr = crate::UserConstPtr::::from_usize(addr.trunc()); let slice = ptr .to_owned_slice(len) .ok_or(OpteeSmcReturnCode::EBadAddr)?; @@ -654,7 +666,7 @@ pub fn update_optee_msg_args( if slice.is_empty() { continue; } - out_shm_info.write(slice.as_ref())?; + out_shm_info.write(platform, slice.as_ref())?; } } _ => {} @@ -725,14 +737,23 @@ impl ShmInfo { /// Read into `buffer` from the normal-world shared memory pages referenced by `self`, /// starting at byte `offset` within the view. /// Returns `EBadAddr` if the requested range is not entirely within the view. - fn read_at(&self, offset: usize, buffer: &mut [u8]) -> Result<(), OpteeSmcReturnCode> { + fn read_at>( + &self, + platform: &Platform, + offset: usize, + buffer: &mut [u8], + ) -> Result<(), OpteeSmcReturnCode> { if offset .checked_add(buffer.len()) .is_none_or(|end| end > self.len) { return Err(OpteeSmcReturnCode::EBadAddr); } - let ptr = NormalWorldConstPtr::::new(&self.page_addrs, self.page_offset)?; + let ptr = NormalWorldConstPtr::::new( + platform, + &self.page_addrs, + self.page_offset, + )?; ptr.read_slice_at_offset(offset, buffer)?; Ok(()) } @@ -740,11 +761,19 @@ impl ShmInfo { /// Write `buffer` to the normal-world shared memory pages referenced by `self`, /// starting at the beginning of the view. /// Returns `EBadAddr` if `buffer` does not fit within the view. - fn write(&self, buffer: &[u8]) -> Result<(), OpteeSmcReturnCode> { + fn write>( + &self, + platform: &Platform, + buffer: &[u8], + ) -> Result<(), OpteeSmcReturnCode> { if buffer.len() > self.len { return Err(OpteeSmcReturnCode::EBadAddr); } - let ptr = NormalWorldMutPtr::::new(&self.page_addrs, self.page_offset)?; + let ptr = NormalWorldMutPtr::::new( + platform, + &self.page_addrs, + self.page_offset, + )?; ptr.write_slice_at_offset(0, buffer)?; Ok(()) } @@ -797,8 +826,9 @@ impl ShmRefMap { /// `aligned_size` indicates the page-aligned size of the shared memory region to register /// (i.e., `page_align_up(page_offset + size)`) and determines how many physical pages are /// walked from the [`ShmRefPagesData`] list. - pub fn register_shm( + pub fn register_shm>( &self, + platform: &Platform, shm_ref_pages_data_phys_addr: u64, page_offset: u64, size: u64, @@ -822,8 +852,10 @@ impl ShmRefMap { return Err(OpteeSmcReturnCode::EBadAddr); } visited_pages_data.insert(cur_addr); - let cur_ptr = NormalWorldConstPtr::::with_usize(cur_addr) - .map_err(|_| OpteeSmcReturnCode::EBadAddr)?; + let cur_ptr = NormalWorldConstPtr::::with_usize( + platform, cur_addr, + ) + .map_err(|_| OpteeSmcReturnCode::EBadAddr)?; let pages_data = cur_ptr .read_at_offset(0) .map_err(|_| OpteeSmcReturnCode::EBadAddr)?; diff --git a/litebox_shim_optee/src/session.rs b/litebox_shim_optee/src/session.rs index 17fdc4edeb..817a7dffc4 100644 --- a/litebox_shim_optee/src/session.rs +++ b/litebox_shim_optee/src/session.rs @@ -10,8 +10,8 @@ //! //! ## Concurrency Model //! -//! TA execution is serialized externally; [`TaInstance`] is shared without -//! an inner mutex. The exclusivity invariant lives in [`SessionManager`] +//! TA execution is serialized externally; [`TaInstance`] is shared without +//! an inner mutex. The exclusivity invariant lives in [`SessionManager`] //! and is acquired through an internal RAII `SessionToken` that bundles //! whichever locks the current operation requires — see `SessionToken`'s //! doc for the per-case breakdown. @@ -125,13 +125,13 @@ const ANONYMOUS_CLIENT_IDENTITY: TeeIdentity = TeeIdentity { /// TA stays in memory until the last session closes (if it does not have the /// `TA_FLAG_INSTANCE_KEEP_ALIVE` flag). Each instance has its own task page /// table that provides memory isolation from other TAs. -pub struct TaInstance { +pub struct TaInstance { /// The shim must be kept alive to keep the loaded program's memory mappings valid. - shim: OpteeShim, + shim: OpteeShim, /// The loaded TA program state including entrypoints. /// Boxed to keep it at a fixed heap address - the Task inside must not be moved /// after initialization because it contains internal state that may not survive moves. - loaded_program: alloc::boxed::Box, + loaded_program: alloc::boxed::Box>, /// The task page table ID associated with this TA instance. /// /// Also serves as the instance's identity for sibling-tracking @@ -141,16 +141,16 @@ pub struct TaInstance { ta_uuid: TeeUuid, } -impl TaInstance { +impl TaInstance { pub fn task_page_table_id(&self) -> usize { self.task_page_table_id } - pub fn shim(&self) -> &OpteeShim { + pub fn shim(&self) -> &OpteeShim { &self.shim } - pub fn loaded_program(&self) -> &LoadedProgram { + pub fn loaded_program(&self) -> &LoadedProgram { &self.loaded_program } @@ -159,25 +159,30 @@ impl TaInstance { } } -// SAFETY: `TaInstance`'s interior (`shim`, `loaded_program`) is not -// auto-`Send`/`Sync`, but every access goes through a `SessionToken` that -// serializes execution on the per-UUID lock (single-instance TAs) or the -// per-`session_id` marker (multi-instance TAs), so at most one core is -// ever inside a given instance. See the module-level "Concurrency Model". -unsafe impl Send for TaInstance {} -unsafe impl Sync for TaInstance {} +// SAFETY: `TaInstance` is not auto-`Send`/`Sync` only because of the `_not_send` marker in +// `OpteeShimEntrypoints` and the `Cell`s in `Task`. Sharing those is sound because every +// access goes through a `SessionToken` serializing on the per-UUID lock or the +// per-`session_id` marker, so at most one core is ever inside an instance. See the +// module-level "Concurrency Model". These impls vouch for that discipline and nothing else. +// +// Nothing reached through `Platform` needs vouching for: `OpteeShimPlatform` already +// implies `Platform: Sync` and everything `GlobalState` holds (`LiteBox`, `PageManager`, +// the handle maps) is auto-`Send + Sync`. Also, nothing under `TaInstance` stores +// associated pointer types: `Task` keeps addresses as `Cell`. +unsafe impl Send for TaInstance {} +unsafe impl Sync for TaInstance {} /// What an OpenSession should do given the current cache state for a /// `uuid`, as decided by [`SessionManager::with_ta`] under its /// serialization. The closure dispatches on the variant. -pub enum OpenSessionTarget<'a> { +pub enum OpenSessionTarget<'a, Platform: crate::OpteeShimPlatform> { /// No cached single-instance instance for this UUID (either it's /// not single-instance, or the cache is empty). Closure should load /// a fresh TA and call `register_new_session`. NewInstance, /// A cached single-instance TA is available for sharing. Closure /// should reuse it for a sibling session via `register_sibling_session`. - Sibling(&'a TaInstance), + Sibling(&'a TaInstance), /// A cached single-instance TA exists but it lacks `TA_FLAG_MULTI_SESSION` /// and already has at least one live session. Per OP-TEE OS /// `tee_ta_init_session_with_context`, reject with @@ -188,13 +193,25 @@ pub enum OpenSessionTarget<'a> { /// Per-session entry in the session map. The `Dead` variant retains /// `(ta_uuid, ta_flags)` so cleanup paths and `try_acquire_for_session`'s /// snapshot still have them after the instance is gone. -#[derive(Clone)] -enum SessionEntry { - Live(Arc), +enum SessionEntry { + Live(Arc>), Dead { ta_uuid: TeeUuid, ta_flags: TaFlags }, } -impl SessionEntry { +// Hand-written: `#[derive(Clone)]` would add a spurious `Platform: Clone` bound. +impl Clone for SessionEntry { + fn clone(&self) -> Self { + match self { + Self::Live(instance) => Self::Live(Arc::clone(instance)), + Self::Dead { ta_uuid, ta_flags } => Self::Dead { + ta_uuid: *ta_uuid, + ta_flags: *ta_flags, + }, + } + } +} + +impl SessionEntry { fn ta_uuid(&self) -> TeeUuid { match self { SessionEntry::Live(arc) => arc.ta_uuid, @@ -213,11 +230,11 @@ impl SessionEntry { /// Session map for tracking active sessions. /// /// Maps runner-allocated session IDs to session entries. -struct SessionMap { - inner: SpinMutex>, +struct SessionMap { + inner: SpinMutex>>, } -impl SessionMap { +impl SessionMap { /// Create a new empty session map. fn new() -> Self { Self { @@ -226,19 +243,19 @@ impl SessionMap { } /// Get full session entry by session ID. - fn get_entry(&self, session_id: u32) -> Option { + fn get_entry(&self, session_id: u32) -> Option> { self.inner.lock().get(&session_id).cloned() } /// Insert a live session into the map. - fn insert_live(&self, session_id: u32, instance: Arc) { + fn insert_live(&self, session_id: u32, instance: Arc>) { self.inner .lock() .insert(session_id, SessionEntry::Live(instance)); } /// Remove a session from the map. - fn remove(&self, session_id: u32) -> Option { + fn remove(&self, session_id: u32) -> Option> { self.inner.lock().remove(&session_id) } @@ -272,7 +289,7 @@ impl SessionMap { } } -impl Default for SessionMap { +impl Default for SessionMap { fn default() -> Self { Self::new() } @@ -282,11 +299,11 @@ impl Default for SessionMap { /// /// Single-instance TAs (with `TA_FLAG_SINGLE_INSTANCE`) share a single TA instance /// across all sessions. This cache stores instances by UUID for fast reuse lookup. -struct SingleInstanceCache { - inner: SpinMutex>>, +struct SingleInstanceCache { + inner: SpinMutex>>>, } -impl SingleInstanceCache { +impl SingleInstanceCache { /// Create a new empty cache. fn new() -> Self { Self { @@ -295,12 +312,12 @@ impl SingleInstanceCache { } /// Get a cached single-instance TA by UUID. - fn get(&self, uuid: &TeeUuid) -> Option> { + fn get(&self, uuid: &TeeUuid) -> Option>> { self.inner.lock().get(uuid).cloned() } /// Cache a single-instance TA by UUID. - fn insert(&self, uuid: TeeUuid, instance: Arc) { + fn insert(&self, uuid: TeeUuid, instance: Arc>) { self.inner.lock().insert(uuid, instance); } @@ -324,7 +341,7 @@ impl SingleInstanceCache { } } -impl Default for SingleInstanceCache { +impl Default for SingleInstanceCache { fn default() -> Self { Self::new() } @@ -376,10 +393,10 @@ enum HeldUuidLock { /// On drop the held UUID-level lock is released first (whether per-UUID /// or the global load lock), then the per-session-id marker, then /// (if still owned) the session id is recycled. -pub struct SessionToken<'a> { - manager: &'a SessionManager, +pub struct SessionToken<'a, Platform: crate::OpteeShimPlatform> { + manager: &'a SessionManager, /// Logical UUID-level lock owned by this token. The actual lock state - /// lives in `SessionManager`; `Drop` releases it (clears the held flag). + /// lives in `SessionManager`; `Drop` releases it (clears the held flag). uuid_lock: Option, /// `Some(id)` while the token holds the active-session marker for `id` /// in [`SessionManager::active_sessions`]. Drop releases the marker. @@ -394,7 +411,7 @@ pub struct SessionToken<'a> { owns_id_recycling: bool, } -impl SessionToken<'_> { +impl SessionToken<'_, Platform> { /// Session id this token reserves the active-session marker for, if any. /// Set for tokens minted by /// [`SessionManager::try_acquire_open_session_token`] or @@ -413,7 +430,7 @@ impl SessionToken<'_> { } } -impl Drop for SessionToken<'_> { +impl Drop for SessionToken<'_, Platform> { fn drop(&mut self) { if let Some(lock) = self.uuid_lock.take() { self.manager.release_uuid_lock(lock); @@ -438,11 +455,11 @@ impl Drop for SessionToken<'_> { /// run the caller's closure under an internal `SessionToken`. State /// mutations the closure performs on the manager (registration, /// sibling-marking, cache eviction) are serialized by that token. -pub struct SessionManager { +pub struct SessionManager { /// Active sessions mapped by session ID. - sessions: SessionMap, + sessions: SessionMap, /// Cache of single-instance TAs by UUID. - single_instance_cache: SingleInstanceCache, + single_instance_cache: SingleInstanceCache, /// Number of instances currently being created (not yet registered). /// Added to [`SessionManager::instance_count`] for the capacity check /// in [`SessionManager::with_ta`] so two concurrent loads cannot both @@ -481,14 +498,13 @@ pub struct SessionManager { session_client_identities: SpinMutex>, } -/// Get the global session manager. -pub fn session_manager() -> &'static SessionManager { - static SESSION_MANAGER: once_cell::race::OnceBox = - once_cell::race::OnceBox::new(); - SESSION_MANAGER.get_or_init(|| alloc::boxed::Box::new(SessionManager::new())) -} +// NOTE: the session manager singleton lives in the composition root (each +// runner), not here. A `static` cannot name a generic parameter, and a shim +// instance is built per session, so the shim has nowhere to put it. The runner +// knows its concrete platform, so it can hold the `static` and hand out +// `&'static SessionManager`. -impl SessionManager { +impl SessionManager { pub fn new() -> Self { Self { sessions: SessionMap::new(), @@ -518,7 +534,9 @@ impl SessionManager { /// /// # Errors /// - `EBusy` if the id pool is exhausted. - pub fn try_acquire_open_session_token(&self) -> Result, OpteeSmcReturnCode> { + pub fn try_acquire_open_session_token( + &self, + ) -> Result, OpteeSmcReturnCode> { let session_id = allocate_session_id().ok_or(OpteeSmcReturnCode::EBusy)?; // The id pool's hint+wrap allocator defers reuse of recycled ids, // so a freshly-allocated id can never collide with a marker slot @@ -544,7 +562,7 @@ impl SessionManager { /// Marks every session currently pointing at `instance` as `Dead` and /// evicts the matching entry from the single-instance cache. Use when /// tearing down a *failed* TA that may still have sibling sessions. - pub fn mark_sessions_dead_for_instance(&self, instance: &TaInstance) { + pub fn mark_sessions_dead_for_instance(&self, instance: &TaInstance) { self.sessions .mark_sessions_dead_for_pt(instance.task_page_table_id); let _ = self.evict_cached_instance(instance); @@ -553,7 +571,7 @@ impl SessionManager { /// Count live sessions currently pointing at `instance` (`Dead` entries /// are skipped). Used by the last-close path to detect whether teardown /// is appropriate. - pub fn count_sessions_for_instance(&self, instance: &TaInstance) -> usize { + pub fn count_sessions_for_instance(&self, instance: &TaInstance) -> usize { self.sessions .count_sessions_for_pt(instance.task_page_table_id) } @@ -614,7 +632,10 @@ impl SessionManager { /// `single_instance_locks`. /// /// Returns `Err(EThreadLimit)` on contention. - fn try_acquire_for_open(&self, uuid: TeeUuid) -> Result, OpteeSmcReturnCode> { + fn try_acquire_for_open( + &self, + uuid: TeeUuid, + ) -> Result, OpteeSmcReturnCode> { let uuid_lock = match self.get_known_flags(&uuid) { Some(flags) if flags.is_single_instance() => Some( self.try_acquire_uuid_lock(uuid) @@ -667,7 +688,7 @@ impl SessionManager { fn try_acquire_for_session( &self, session_id: u32, - ) -> Result<(SessionToken<'_>, SessionEntry), OpteeSmcReturnCode> { + ) -> Result<(SessionToken<'_, Platform>, SessionEntry), OpteeSmcReturnCode> { let entry = self .sessions .get_entry(session_id) @@ -712,7 +733,7 @@ impl SessionManager { /// Drive an Invoke/Close to completion under the right serialization /// (see [`SessionToken`] for the locks held). Passes - /// `Some(&TaInstance)` to `f` for live sessions, `None` for dead + /// `Some(&TaInstance)` to `f` for live sessions, `None` for dead /// ones. State mutations `f` performs on the manager /// (`unregister_session`, `mark_sessions_dead_for_instance`, /// `evict_cached_instance`) are serialized against concurrent @@ -723,7 +744,7 @@ impl SessionManager { /// transparently). pub fn with_session(&self, session_id: u32, f: F) -> Result<(), OpteeSmcReturnCode> where - F: for<'a> FnOnce(Option<&'a TaInstance>) -> Result<(), OpteeSmcReturnCode>, + F: for<'a> FnOnce(Option<&'a TaInstance>) -> Result<(), OpteeSmcReturnCode>, { let (_token, entry) = self.try_acquire_for_session(session_id)?; let instance = match &entry { @@ -766,8 +787,8 @@ impl SessionManager { pub fn register_new_session( &self, session_id: u32, - shim: OpteeShim, - loaded_program: alloc::boxed::Box, + shim: OpteeShim, + loaded_program: alloc::boxed::Box>, task_page_table_id: usize, ta_uuid: TeeUuid, ) { @@ -801,7 +822,7 @@ impl SessionManager { pub fn register_sibling_session( &self, session_id: u32, - instance: &TaInstance, + instance: &TaInstance, ) -> Result<(), OpteeSmcReturnCode> { let arc = self .single_instance_cache @@ -867,7 +888,7 @@ impl SessionManager { /// cached instance. /// Callers on the last-session-close path may skip the mark step — by /// that point there are no sibling sessions to fence out. - pub fn evict_cached_instance(&self, instance: &TaInstance) -> bool { + pub fn evict_cached_instance(&self, instance: &TaInstance) -> bool { self.single_instance_cache .remove_matching_instance(&instance.ta_uuid, instance.task_page_table_id) } @@ -915,7 +936,7 @@ impl SessionManager { /// serialized by the UUID-level lock itself. pub fn with_ta(&self, uuid: &TeeUuid, f: F) -> Result<(), OpteeSmcReturnCode> where - F: for<'a> FnOnce(OpenSessionTarget<'a>) -> Result<(), OpteeSmcReturnCode>, + F: for<'a> FnOnce(OpenSessionTarget<'a, Platform>) -> Result<(), OpteeSmcReturnCode>, { let mut token = self.try_acquire_for_open(*uuid)?; // Captured before `f` runs so we know whether to perform the @@ -977,7 +998,7 @@ impl SessionManager { } } -impl Default for SessionManager { +impl Default for SessionManager { fn default() -> Self { Self::new() } @@ -986,14 +1007,14 @@ impl Default for SessionManager { #[cfg(test)] mod tests { use super::*; - use crate::syscalls::tests::init_platform; + use crate::syscalls::tests::shim_builder; + use litebox_platform_linux_userland::LinuxUserland as Platform; - fn make_shim() -> OpteeShim { - let _ = init_platform(); - crate::OpteeShimBuilder::new().build() + fn make_shim() -> OpteeShim { + shim_builder().build() } - fn make_loaded_program(ta_flags: TaFlags) -> alloc::boxed::Box { + fn make_loaded_program(ta_flags: TaFlags) -> alloc::boxed::Box> { alloc::boxed::Box::new(LoadedProgram { entrypoints: None, params_address: None, @@ -1013,7 +1034,7 @@ mod tests { /// pre-held per-UUID lock state the way `with_ta` would, so subsequent /// operations (Invoke/Close, evict, count, etc.) aren't blocked. fn register_for_test( - manager: &SessionManager, + manager: &SessionManager, session_id: u32, ta_flags: TaFlags, task_page_table_id: usize, @@ -1038,7 +1059,7 @@ mod tests { /// the stale handle must not evict the new one. #[test] fn evict_cached_instance_distinguishes_stale_handle() { - let manager = SessionManager::new(); + let manager = SessionManager::::new(); let uuid = make_uuid(0xA4); register_for_test(&manager, 105, single_instance_flags(), 10, uuid); @@ -1056,7 +1077,7 @@ mod tests { /// and new opens cannot reuse the dead cached instance. #[test] fn mark_dead_makes_with_session_observe_none() { - let manager = SessionManager::new(); + let manager = SessionManager::::new(); let uuid = make_uuid(0xA6); register_for_test(&manager, 108, single_instance_flags(), 55, uuid); let arc = manager.single_instance_cache.get(&uuid).unwrap(); @@ -1080,7 +1101,7 @@ mod tests { /// turns out to be multi-instance. #[test] fn with_ta_does_not_mint_lock_entry_for_failed_unknown_load() { - let manager = SessionManager::new(); + let manager = SessionManager::::new(); let uuid = make_uuid(0xA9); assert!(manager.get_known_flags(&uuid).is_none()); @@ -1094,7 +1115,7 @@ mod tests { /// success or failure — across multiple calls it must return to zero. #[test] fn pending_count_returns_to_zero_across_paths() { - let manager = SessionManager::new(); + let manager = SessionManager::::new(); let uuid_multi = make_uuid(0xC0); let uuid_single = make_uuid(0xC1); @@ -1129,7 +1150,7 @@ mod tests { /// the same UUID must succeed. #[test] fn with_ta_releases_per_uuid_lock_after_unknown_load() { - let manager = SessionManager::new(); + let manager = SessionManager::::new(); let uuid = make_uuid(0xD0); manager @@ -1160,7 +1181,7 @@ mod tests { /// untouched. #[test] fn unrelated_with_ta_does_not_adopt_other_uuids_lock() { - let manager = SessionManager::new(); + let manager = SessionManager::::new(); let uuid_locked = make_uuid(0xE1); let uuid_other = make_uuid(0xE2); diff --git a/litebox_shim_optee/src/syscalls/cryp.rs b/litebox_shim_optee/src/syscalls/cryp.rs index 60394b0eae..8dbc256319 100644 --- a/litebox_shim_optee/src/syscalls/cryp.rs +++ b/litebox_shim_optee/src/syscalls/cryp.rs @@ -17,14 +17,14 @@ use litebox_common_optee::{ use crate::{Cipher, TeeCrypState, TeeObj, UserMutPtr}; -impl Task { +impl Task { pub(crate) fn sys_cryp_state_alloc( &self, algo: TeeAlgorithm, mode: TeeOperationMode, key1: TeeObjHandle, key2: TeeObjHandle, - state: UserMutPtr, + state: UserMutPtr, ) -> Result<(), TeeResult> { let tee_cryp_state_map = &self.tee_cryp_state_map; let tee_obj_map = &self.tee_obj_map; @@ -210,7 +210,7 @@ impl Task { pub(crate) fn sys_cryp_obj_get_info( &self, obj: TeeObjHandle, - info: UserMutPtr, + info: UserMutPtr, ) -> Result<(), TeeResult> { let tee_obj_map = &self.tee_obj_map; if tee_obj_map.exists(obj) { @@ -226,7 +226,7 @@ impl Task { &self, typ: TeeObjectType, max_size: u32, - obj: UserMutPtr, + obj: UserMutPtr, ) -> Result<(), TeeResult> { let tee_obj_map = &self.tee_obj_map; let tee_obj = TeeObj::new(typ, max_size); @@ -273,7 +273,7 @@ impl Task { if !tee_obj_map.exists(obj) { return Err(TeeResult::BadState); } - tee_obj_map.populate(obj, attrs) + tee_obj_map.populate::(obj, attrs) } pub(crate) fn sys_cryp_obj_copy( @@ -307,7 +307,7 @@ impl Task { #[allow(clippy::unnecessary_wraps)] pub(crate) fn sys_cryp_random_number_generate(&self, buf: &mut [u8]) -> Result<(), TeeResult> { if !buf.is_empty() { - ::fill_bytes_crng( + ::fill_bytes_crng( self.global.platform, buf, ); diff --git a/litebox_shim_optee/src/syscalls/ldelf.rs b/litebox_shim_optee/src/syscalls/ldelf.rs index 5f46d67c90..e79ceba61b 100644 --- a/litebox_shim_optee/src/syscalls/ldelf.rs +++ b/litebox_shim_optee/src/syscalls/ldelf.rs @@ -2,7 +2,7 @@ // Licensed under the MIT license. use crate::syscalls::Cleanup; -use crate::{Platform, Task, UserMutPtr}; +use crate::{Task, UserMutPtr}; use litebox::mm::linux::PAGE_SIZE; use litebox::platform::page_mgmt::PageManagementProvider; use litebox::platform::{RawConstPointer, RawMutPointer}; @@ -22,14 +22,14 @@ fn align_down(addr: usize, align: usize) -> usize { /// and ownership of the mapping has been transferred to the caller, call /// `disarm()` to suppress the unmap. #[must_use = "MmapGuard unmaps on drop unless disarm() is called; bind it"] -struct MmapGuard<'a> { - task: &'a Task, - addr: UserMutPtr, +struct MmapGuard<'a, Platform: crate::OpteeShimPlatform> { + task: &'a Task, + addr: UserMutPtr, len: usize, } -impl<'a> MmapGuard<'a> { - fn new(task: &'a Task, addr: UserMutPtr, len: usize) -> Self { +impl<'a, Platform: crate::OpteeShimPlatform> MmapGuard<'a, Platform> { + fn new(task: &'a Task, addr: UserMutPtr, len: usize) -> Self { Self { task, addr, len } } @@ -38,13 +38,13 @@ impl<'a> MmapGuard<'a> { } } -impl Drop for MmapGuard<'_> { +impl Drop for MmapGuard<'_, Platform> { fn drop(&mut self) { let _ = self.task.sys_munmap(self.addr, self.len); } } -impl Task { +impl Task { #[inline] fn checked_map_len( num_bytes: usize, @@ -230,7 +230,7 @@ impl Task { .ok_or(TeeResult::BadParameters)?; if pad_end_start_addr < map_end_addr { let _ = self.sys_munmap( - UserMutPtr::from_usize(pad_end_start_addr), + UserMutPtr::::from_usize(pad_end_start_addr), map_end_addr - pad_end_start_addr, ); } @@ -244,7 +244,11 @@ impl Task { } /// OP-TEE's syscall to open a TA binary. - pub fn sys_open_bin(&self, ta_uuid: TeeUuid, handle: UserMutPtr) -> Result<(), TeeResult> { + pub fn sys_open_bin( + &self, + ta_uuid: TeeUuid, + handle: UserMutPtr, + ) -> Result<(), TeeResult> { #[cfg(debug_assertions)] litebox_util_log::debug!( ta_uuid:? = ta_uuid, @@ -278,7 +282,7 @@ impl Task { #[allow(clippy::too_many_arguments)] pub fn sys_map_bin( &self, - va: UserMutPtr, + va: UserMutPtr, num_bytes: usize, handle: u32, offs: usize, @@ -424,7 +428,7 @@ impl Task { if self .read_ta_bin( handle, - UserMutPtr::from_usize(usable_start_addr), + UserMutPtr::::from_usize(usable_start_addr), offs, num_bytes, ) @@ -447,7 +451,11 @@ impl Task { .and_then(|len| len.checked_next_multiple_of(PAGE_SIZE)) .ok_or(TeeResult::BadParameters)?; if self - .sys_mprotect(UserMutPtr::from_usize(prot_start_addr), prot_len, prot) + .sys_mprotect( + UserMutPtr::::from_usize(prot_start_addr), + prot_len, + prot, + ) .is_err() { return Err(TeeResult::AccessDenied); @@ -485,8 +493,10 @@ impl Task { to_release.remove(range.clone()); } for range in to_release.iter() { - let _ = - self.sys_munmap(UserMutPtr::from_usize(range.start), range.end - range.start); + let _ = self.sys_munmap( + UserMutPtr::::from_usize(range.start), + range.end - range.start, + ); } } @@ -518,8 +528,13 @@ impl Task { "sys_cp_from_bin" ); - self.read_ta_bin(handle, UserMutPtr::from_usize(dst), offs, num_bytes) - .ok_or(TeeResult::ShortBuffer)?; + self.read_ta_bin( + handle, + UserMutPtr::::from_usize(dst), + offs, + num_bytes, + ) + .ok_or(TeeResult::ShortBuffer)?; Ok(()) } @@ -529,7 +544,7 @@ impl Task { fn read_ta_bin( &self, handle: u32, - dst: UserMutPtr, + dst: UserMutPtr, offset: usize, count: usize, ) -> Option<()> { diff --git a/litebox_shim_optee/src/syscalls/mm.rs b/litebox_shim_optee/src/syscalls/mm.rs index 2263e68c6b..ce4ae06102 100644 --- a/litebox_shim_optee/src/syscalls/mm.rs +++ b/litebox_shim_optee/src/syscalls/mm.rs @@ -6,7 +6,7 @@ use litebox::mm::linux::{MappingError, PAGE_SIZE}; use litebox_common_linux::{MapFlags, ProtFlags, errno::Errno, user_pointers::UserPtrMut}; -use crate::{Platform, Task, UserMutPtr}; +use crate::{Task, UserMutPtr}; #[inline] fn align_up(addr: usize, align: usize) -> Option { @@ -14,7 +14,7 @@ fn align_up(addr: usize, align: usize) -> Option { addr.checked_next_multiple_of(align) } -impl Task { +impl Task { #[inline] fn do_mmap_anonymous( &self, @@ -22,7 +22,7 @@ impl Task { len: usize, prot: ProtFlags, flags: MapFlags, - ) -> Result, MappingError> { + ) -> Result, MappingError> { let op = |_| Ok(0); litebox_common_linux::mm::do_mmap( &self.global.pm, @@ -45,7 +45,7 @@ impl Task { flags: MapFlags, _fd: i32, offset: usize, - ) -> Result, Errno> { + ) -> Result, Errno> { // check alignment if !offset.is_multiple_of(PAGE_SIZE) || !addr.is_multiple_of(PAGE_SIZE) || len == 0 { return Err(Errno::EINVAL); @@ -91,7 +91,11 @@ impl Task { } /// Handle syscall `munmap` - pub(crate) fn sys_munmap(&self, addr: UserMutPtr, len: usize) -> Result<(), Errno> { + pub(crate) fn sys_munmap( + &self, + addr: UserMutPtr, + len: usize, + ) -> Result<(), Errno> { let pm = &self.global.pm; litebox_common_linux::mm::sys_munmap( pm, @@ -104,7 +108,7 @@ impl Task { #[inline] pub(crate) fn sys_mprotect( &self, - addr: UserMutPtr, + addr: UserMutPtr, len: usize, prot: ProtFlags, ) -> Result<(), Errno> { diff --git a/litebox_shim_optee/src/syscalls/mod.rs b/litebox_shim_optee/src/syscalls/mod.rs index d77303fbad..4a617a522a 100644 --- a/litebox_shim_optee/src/syscalls/mod.rs +++ b/litebox_shim_optee/src/syscalls/mod.rs @@ -28,11 +28,11 @@ pub(crate) enum Cleanup { impl Cleanup { /// Undo the side effect. Runs only on an error path, so failures are ignored. - pub(crate) fn run(self, task: &Task) { + pub(crate) fn run(self, task: &Task) { match self { Self::None => {} Self::Unmap { addr, len } => { - let _ = task.sys_munmap(UserMutPtr::::from_usize(addr), len); + let _ = task.sys_munmap(UserMutPtr::::from_usize(addr), len); } } } diff --git a/litebox_shim_optee/src/syscalls/pta.rs b/litebox_shim_optee/src/syscalls/pta.rs index ab0c426bac..a92bda6a2e 100644 --- a/litebox_shim_optee/src/syscalls/pta.rs +++ b/litebox_shim_optee/src/syscalls/pta.rs @@ -10,9 +10,7 @@ use alloc::vec; use alloc::vec::Vec; use hmac::{Hmac, Mac}; use litebox::mm::linux::PAGE_SIZE; -use litebox::platform::{ - DerivedKeyError, DerivedKeyProvider, KDFParams, RawConstPointer as _, RawMutPointer as _, -}; +use litebox::platform::{DerivedKeyError, KDFParams, RawConstPointer as _, RawMutPointer as _}; use litebox::utils::TruncateExt; use litebox_common_optee::{ HUK_SUBKEY_MAX_LEN, HukSubkeyUsage, LdelfMapFlags, TaFlags, TeeParamType, TeeResult, TeeUuid, @@ -47,9 +45,9 @@ impl PseudoTa { } } - pub(crate) fn invoke_command( + pub(crate) fn invoke_command( self, - task: &Task, + task: &Task, cmd_id: u32, params: &mut UteeParams, ) -> Result { @@ -59,7 +57,11 @@ impl PseudoTa { } } - fn close_session(self, task: &Task, session_id: u32) { + fn close_session( + self, + task: &Task, + session_id: u32, + ) { match self { Self::System => SystemPta::close_session(task, session_id), } @@ -78,12 +80,12 @@ const PTA_DEFAULT_FLAGS: TaFlags = TaFlags::SINGLE_INSTANCE const MAX_PTA_SESSIONS_PER_TASK: usize = 100; -struct PtaBusyGuard<'a> { - task: &'a Task, +struct PtaBusyGuard<'a, Platform: crate::OpteeShimPlatform> { + task: &'a Task, pta: PseudoTa, } -impl Drop for PtaBusyGuard<'_> { +impl Drop for PtaBusyGuard<'_, Platform> { fn drop(&mut self) { self.task.global.pta_busy.lock().remove(&self.pta); } @@ -133,14 +135,14 @@ enum PtaSystemCommandId { type HmacSha256 = Hmac; -impl Task { +impl Task { /// Try to mark a non-concurrent PTA as busy, returning a guard that clears /// the busy state on drop. This gates both session opening and command /// invocation. /// /// Returns `Ok(None)` for PTAs flagged `TaFlags::CONCURRENT` (no gating). /// For a non-concurrent PTA that is busy, returns `Err(Busy)` immediately. - fn try_set_busy(&self, pta: PseudoTa) -> Result>, TeeResult> { + fn try_set_busy(&self, pta: PseudoTa) -> Result>, TeeResult> { if pta.flags().contains(TaFlags::CONCURRENT) { return Ok(None); } @@ -233,15 +235,15 @@ impl SystemPta { crate::SessionIdPool::allocate().ok_or(TeeResult::Busy) } - fn close_session(_task: &Task, _session_id: u32) { + fn close_session(_task: &Task, _session_id: u32) { // System PTA has no per-session state } /// Handle a command of the system PTA. /// /// See `Cleanup` for the returned rollback; most commands have no cleanup. - fn invoke_command( - task: &Task, + fn invoke_command( + task: &Task, cmd_id: u32, params: &mut UteeParams, ) -> Result { @@ -264,7 +266,10 @@ impl SystemPta { /// /// This follows the OP-TEE `system_derive_ta_unique_key` implementation from /// `core/pta/system.c`. - fn derive_ta_unique_key(task: &Task, params: &UteeParams) -> Result<(), TeeResult> { + fn derive_ta_unique_key( + task: &Task, + params: &UteeParams, + ) -> Result<(), TeeResult> { use TeeParamType::{MemrefInput, MemrefOutput, None}; if !params.has_types([MemrefInput, MemrefOutput, None, None]) { @@ -294,7 +299,7 @@ impl SystemPta { let extra_data = if extra_data_size == 0 { Vec::new().into_boxed_slice() } else { - let extra_data_ptr = UserConstPtr::::from_usize(extra_data_addr.trunc()); + let extra_data_ptr = UserConstPtr::::from_usize(extra_data_addr.trunc()); extra_data_ptr .to_owned_slice(extra_data_size) .ok_or(TeeResult::BadParameters)? @@ -303,7 +308,7 @@ impl SystemPta { // Unlike OP-TEE OS, `UserMutPtr` (and `UserConstPtr`) in LiteBox ensure this // pointer can never be used to access normal-world memory. That is, we don't // need extra security check for detecting key leakage here. - let subkey_ptr = UserMutPtr::::from_usize(subkey_addr.trunc()); + let subkey_ptr = UserMutPtr::::from_usize(subkey_addr.trunc()); // subkey = KDF(huk, usage || ta_uuid || extra_data) let ta_uuid_bytes = task.ta_app_id.to_le_bytes(); @@ -324,8 +329,8 @@ impl SystemPta { /// Derive a subkey using HUK and constant data. /// /// This follows the OP-TEE `huk_subkey_derive` interface from `core/kernel/huk_subkey.c`. - fn huk_subkey_derive( - task: &Task, + fn huk_subkey_derive( + task: &Task, usage: HukSubkeyUsage, const_data: &[&[u8]], subkey: &mut [u8], @@ -359,7 +364,10 @@ impl SystemPta { Ok(()) } - fn map_zi(task: &Task, params: &mut UteeParams) -> Result { + fn map_zi( + task: &Task, + params: &mut UteeParams, + ) -> Result { use TeeParamType::{None, ValueInout, ValueInput}; if !params.has_types([ValueInput, ValueInout, ValueInput, None]) { @@ -402,7 +410,10 @@ impl SystemPta { Ok(cleanup) } - fn unmap(task: &Task, params: &UteeParams) -> Result<(), TeeResult> { + fn unmap( + task: &Task, + params: &UteeParams, + ) -> Result<(), TeeResult> { use TeeParamType::{None, ValueInput}; if !params.has_types([ValueInput, ValueInput, None, None]) { @@ -430,7 +441,7 @@ impl SystemPta { .checked_next_multiple_of(PAGE_SIZE) .ok_or(TeeResult::BadParameters)?; - task.sys_munmap(UserMutPtr::::from_usize(addr), size) + task.sys_munmap(UserMutPtr::::from_usize(addr), size) .map_err(|_| TeeResult::BadParameters) } } diff --git a/litebox_shim_optee/src/syscalls/tee.rs b/litebox_shim_optee/src/syscalls/tee.rs index bb9a7ea803..a09485cead 100644 --- a/litebox_shim_optee/src/syscalls/tee.rs +++ b/litebox_shim_optee/src/syscalls/tee.rs @@ -32,7 +32,7 @@ fn align_down(addr: usize, align: usize) -> usize { addr & !(align - 1) } -impl Task { +impl Task { /// A system call to return to the kernel. A TA calls this function when /// it finishes its job delivered through a TA command invocation. #[allow(clippy::unused_self)] @@ -75,11 +75,11 @@ impl Task { &self, prop_set: TeePropSet, index: u32, - name_buf: Option>, - name_len: Option>, + name_buf: Option>, + name_len: Option>, prop_buf: &mut [u8], - prop_len: UserMutPtr, - prop_type: UserMutPtr, + prop_len: UserMutPtr, + prop_type: UserMutPtr, ) -> Result<(), TeeResult> { if name_buf.is_some() || name_len.is_some() { #[cfg(debug_assertions)] @@ -158,7 +158,7 @@ impl Task { pub fn sys_get_property_name_to_index( prop_set: TeePropSet, name: &[u8], - index: UserMutPtr, + index: UserMutPtr, ) -> Result<(), TeeResult> { let name_str = core::ffi::CStr::from_bytes_with_nul(name).map_err(|_| TeeResult::BadParameters)?; @@ -206,8 +206,8 @@ impl Task { ta_uuid: TeeUuid, _cancel_req_to: u32, usr_params: UteeParams, - ta_sess_id: UserMutPtr, - ret_orig: UserMutPtr, + ta_sess_id: UserMutPtr, + ret_orig: UserMutPtr, ) -> Result<(), TeeResult> { // `cancel_req_to` is a timeout value. Ignore it for now. if let Some(pta) = PseudoTa::from_uuid(&ta_uuid) { @@ -257,7 +257,7 @@ impl Task { _cancel_req_to: u32, cmd_id: u32, params: &mut UteeParams, - ret_orig: UserMutPtr, + ret_orig: UserMutPtr, ) -> Result { // `cancel_req_to` is a timeout value. Ignore it for now. if let Some(pta) = self.pta_for_session(ta_sess_id) { @@ -278,7 +278,7 @@ impl Task { pub fn sys_check_access_rights( &self, flags: TeeMemoryAccessRights, - buf: UserConstPtr, + buf: UserConstPtr, len: usize, ) -> Result<(), TeeResult> { // Ignore the unknown bits of `TeeMemoryAccessRights` for now. @@ -337,7 +337,7 @@ impl Task { pub fn sys_get_time( &self, cat: TeeTimeCategory, - time: UserMutPtr, + time: UserMutPtr, ) -> Result<(), TeeResult> { let tee_time = match cat { TeeTimeCategory::System => { diff --git a/litebox_shim_optee/src/syscalls/tests.rs b/litebox_shim_optee/src/syscalls/tests.rs index 4412289188..dd851643a0 100644 --- a/litebox_shim_optee/src/syscalls/tests.rs +++ b/litebox_shim_optee/src/syscalls/tests.rs @@ -1,28 +1,25 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -use litebox_platform_multiplex::{Platform, set_platform}; - -// Ensure we only init the platform once -static INIT_FUNC: spin::Once = spin::Once::new(); +use litebox_platform_linux_userland::LinuxUserland as Platform; +/// A shim builder bound to the test platform and a single session registry shared by +/// every shim built here, per [`crate::OpteeShimBuilder::new`]'s invariant. #[must_use] -#[cfg_attr( - not(target_os = "linux"), - expect(unused_variables, reason = "ignored parameter on non-linux platforms") -)] -pub(crate) fn init_platform() -> crate::Task { - INIT_FUNC.call_once(|| { - #[cfg(target_os = "linux")] - let platform = Platform::new(None); +pub(crate) fn shim_builder() -> crate::OpteeShimBuilder { + static PLATFORM: spin::Once<&'static Platform> = spin::Once::new(); + static SESSION_MANAGER: once_cell::race::OnceBox> = + once_cell::race::OnceBox::new(); - #[cfg(not(target_os = "linux"))] - let platform = Platform::new(); + let platform = *PLATFORM.call_once(|| Platform::new(None)); + let session_manager = SESSION_MANAGER + .get_or_init(|| alloc::boxed::Box::new(crate::session::SessionManager::new())); - set_platform(platform); - }); + crate::OpteeShimBuilder::new(platform, session_manager) +} - let shim_builder = crate::OpteeShimBuilder::new(); +pub(crate) fn init_platform() -> crate::Task { + let shim_builder = shim_builder(); let _litebox = shim_builder.litebox(); shim_builder.build().0.new_test_task() } @@ -50,12 +47,12 @@ fn test_sys_get_time_system_is_monotonic() { let task = init_platform(); let mut first = TeeTime::default(); - let first_ptr = crate::UserMutPtr::::from_usize(&raw mut first as usize); + let first_ptr = crate::UserMutPtr::::from_usize(&raw mut first as usize); task.sys_get_time(TeeTimeCategory::System, first_ptr) .expect("system time should be supported"); let mut second = TeeTime::default(); - let second_ptr = crate::UserMutPtr::::from_usize(&raw mut second as usize); + let second_ptr = crate::UserMutPtr::::from_usize(&raw mut second as usize); task.sys_get_time(TeeTimeCategory::System, second_ptr) .expect("system time should be supported");