From e9bcdb8d28034a7bb5f7284ddd104cb7f4ba832d Mon Sep 17 00:00:00 2001 From: Sangho Lee Date: Fri, 1 May 2026 03:25:28 +0000 Subject: [PATCH 01/19] use physcal pointer abstraction in HVCI/HEKI --- litebox_common_linux/src/lib.rs | 1 + .../src/physical_pointers.rs | 201 ++++++++++-- litebox_common_linux/src/vmap.rs | 4 +- .../src/arch/x86/mm/paging.rs | 2 - litebox_platform_lvbs/src/lib.rs | 290 ++---------------- litebox_platform_lvbs/src/mm/mod.rs | 2 - litebox_platform_lvbs/src/mshv/ringbuffer.rs | 33 +- litebox_platform_lvbs/src/mshv/vsm.rs | 99 +++--- litebox_shim_optee/src/lib.rs | 49 ++- 9 files changed, 338 insertions(+), 343 deletions(-) rename litebox_shim_optee/src/ptr.rs => litebox_common_linux/src/physical_pointers.rs (80%) diff --git a/litebox_common_linux/src/lib.rs b/litebox_common_linux/src/lib.rs index 379b27e1d..78498bc08 100644 --- a/litebox_common_linux/src/lib.rs +++ b/litebox_common_linux/src/lib.rs @@ -21,6 +21,7 @@ use crate::signal::SigSet; pub mod errno; pub mod loader; pub mod mm; +pub mod physical_pointers; pub mod signal; pub mod vmap; diff --git a/litebox_shim_optee/src/ptr.rs b/litebox_common_linux/src/physical_pointers.rs similarity index 80% rename from litebox_shim_optee/src/ptr.rs rename to litebox_common_linux/src/physical_pointers.rs index 06a492506..da3f253d7 100644 --- a/litebox_shim_optee/src/ptr.rs +++ b/litebox_common_linux/src/physical_pointers.rs @@ -58,15 +58,58 @@ //! if this module always requires a list of physical addresses, the caller might //! provide a wrong list by mistake or intentionally). -// TODO: Since the below `PhysMutPtr` and `PhysConstPtr` are not OP-TEE specific, -// we can move them to a different crate (e.g., `litebox`) if needed. - -use litebox_common_linux::vmap::{ - PhysPageAddr, PhysPageMapInfo, PhysPageMapPermissions, PhysPointerError, VmapManager, +use crate::vmap::{ + PhysPageAddr, PhysPageAddrArray, PhysPageMapInfo, PhysPageMapPermissions, PhysPointerError, + VmapManager, }; -use litebox_platform_multiplex::platform; use zerocopy::FromBytes; +/// Provider for physical page mapping operations used by physical pointers. +pub trait PhysMapProvider: Clone { + fn validate_unowned(&self, _pages: &PhysPageAddrArray) -> Result<(), PhysPointerError> { + Ok(()) + } + + /// # Safety + /// + /// Same as [`VmapManager::vmap`]. + unsafe fn vmap( + &self, + _pages: &PhysPageAddrArray, + _perms: PhysPageMapPermissions, + ) -> Result, PhysPointerError> { + Err(PhysPointerError::UnsupportedOperation) + } + + /// # Safety + /// + /// Same as [`VmapManager::vunmap`]. + unsafe fn vunmap(&self, _map_info: PhysPageMapInfo) -> Result<(), PhysPointerError> { + Err(PhysPointerError::UnsupportedOperation) + } +} + +impl PhysMapProvider for &P +where + P: VmapManager + ?Sized, +{ + fn validate_unowned(&self, pages: &PhysPageAddrArray) -> Result<(), PhysPointerError> { +

>::validate_unowned(*self, pages) + } + + unsafe fn vmap( + &self, + pages: &PhysPageAddrArray, + perms: PhysPageMapPermissions, + ) -> Result, PhysPointerError> { + unsafe {

>::vmap(*self, pages, perms) } + } + + unsafe fn vunmap(&self, map_info: PhysPageMapInfo) -> Result<(), PhysPointerError> { + unsafe {

>::vunmap(*self, map_info) } + } +} + /// Allocate a zeroed `Box` on the heap. /// /// # Panics @@ -104,7 +147,8 @@ fn align_down(address: usize, align: usize) -> usize { /// memory for an object of type `T`. #[derive(Clone)] #[repr(C)] -pub struct PhysMutPtr { +pub struct PhysMutPtr> { + provider: P, pages: alloc::boxed::Box<[PhysPageAddr]>, offset: usize, count: usize, @@ -112,14 +156,38 @@ pub struct PhysMutPtr { _type: core::marker::PhantomData, } -impl PhysMutPtr { +impl PhysMutPtr +where + P: PhysMapProvider, +{ /// Create a new `PhysMutPtr` from the given physical page array and offset. /// /// All addresses in `pages` should be valid and aligned to `ALIGN`, and `offset` should be /// smaller 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(pages: &[PhysPageAddr], offset: usize) -> Result + where + P: Default, + { + Self::new_with_provider(P::default(), pages, offset) + } + + pub fn new_with_provider( + provider: P, + pages: &[PhysPageAddr], + offset: usize, + ) -> Result { + if core::mem::size_of::() == 0 { + return Ok(Self { + provider, + pages: pages.into(), + offset, + count: usize::MAX, + map_info: None, + _type: core::marker::PhantomData, + }); + } if offset >= ALIGN { return Err(PhysPointerError::InvalidBaseOffset(offset, ALIGN)); } @@ -138,8 +206,9 @@ impl PhysMutPtr { core::mem::size_of::(), )); } - platform().validate_unowned(pages)?; + provider.validate_unowned(pages)?; Ok(Self { + provider, pages: pages.into(), offset, count: size / core::mem::size_of::(), @@ -154,7 +223,18 @@ impl PhysMutPtr { /// `PhysMutPtr::new([align_down(pa), align_down(pa) + ALIGN, ..., align_up(pa + bytes) - ALIGN], pa % ALIGN)`. /// This function assumes that `pa`, ..., `pa+bytes` are both physically and virtually contiguous. If not, /// later accesses through `PhysMutPtr` may read/write data in a wrong order. - pub fn with_contiguous_pages(pa: usize, bytes: usize) -> Result { + pub fn with_contiguous_pages(pa: usize, bytes: usize) -> Result + where + P: Default, + { + Self::with_contiguous_pages_with_provider(P::default(), pa, bytes) + } + + pub fn with_contiguous_pages_with_provider( + provider: P, + pa: usize, + bytes: usize, + ) -> Result { if bytes < core::mem::size_of::() { return Err(PhysPointerError::InsufficientPhysicalPages( bytes, @@ -180,7 +260,7 @@ impl PhysMutPtr { .checked_add(ALIGN) .ok_or(PhysPointerError::Overflow)?; } - Self::new(&pages, pa - start_page) + Self::new_with_provider(provider, &pages, pa - start_page) } /// Create a new `PhysMutPtr` from the given physical address for a single object. @@ -188,8 +268,15 @@ impl PhysMutPtr { /// 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(pa: usize) -> Result + where + P: Default, + { + Self::with_usize_with_provider(P::default(), pa) + } + + pub fn with_usize_with_provider(provider: P, pa: usize) -> Result { + Self::with_contiguous_pages_with_provider(provider, pa, core::mem::size_of::()) } /// Read the value at the given offset from the physical pointer. @@ -207,6 +294,9 @@ impl PhysMutPtr { where T: FromBytes, { + if core::mem::size_of::() == 0 { + return Ok(alloc::boxed::Box::new(T::new_zeroed())); + } if count >= self.count { return Err(PhysPointerError::IndexOutOfBounds(count, self.count)); } @@ -247,6 +337,9 @@ impl PhysMutPtr { where T: FromBytes, { + if core::mem::size_of::() == 0 || values.is_empty() { + return Ok(()); + } if count .checked_add(values.len()) .is_none_or(|end| end > self.count) @@ -285,6 +378,9 @@ impl PhysMutPtr { count: usize, value: T, ) -> Result<(), PhysPointerError> { + if core::mem::size_of::() == 0 { + return Ok(()); + } if count >= self.count { return Err(PhysPointerError::IndexOutOfBounds(count, self.count)); } @@ -320,6 +416,9 @@ impl PhysMutPtr { count: usize, values: &[T], ) -> Result<(), PhysPointerError> { + if core::mem::size_of::() == 0 || values.is_empty() { + return Ok(()); + } if count .checked_add(values.len()) .is_none_or(|end| end > self.count) @@ -367,7 +466,7 @@ impl PhysMutPtr { count: usize, size: usize, perms: PhysPageMapPermissions, - ) -> Result, PhysPointerError> { + ) -> Result, PhysPointerError> { let skip = self .offset .checked_add( @@ -419,7 +518,7 @@ impl PhysMutPtr { if self.map_info.is_none() { let sub_pages = &self.pages[start..end]; unsafe { - self.map_info = Some(platform().vmap(sub_pages, perms)?); + self.map_info = Some(self.provider.vmap(sub_pages, perms)?); } Ok(()) } else { @@ -438,7 +537,7 @@ impl PhysMutPtr { unsafe fn unmap(&mut self) -> Result<(), PhysPointerError> { if let Some(map_info) = self.map_info.take() { unsafe { - platform().vunmap(map_info)?; + self.provider.vunmap(map_info)?; } Ok(()) } else { @@ -453,13 +552,15 @@ impl PhysMutPtr { /// /// Created by `map_and_get_ptr_guard`. Holds a mutable borrow on the parent /// `PhysMutPtr` and provides the mapped base pointer for the duration of the mapping. -struct MappedGuard<'a, T: Clone, const ALIGN: usize> { - owner: &'a mut PhysMutPtr, +struct MappedGuard<'a, T: Clone, const ALIGN: usize, P: PhysMapProvider> { + owner: &'a mut PhysMutPtr, ptr: *mut T, size: usize, } -impl Drop for MappedGuard<'_, T, ALIGN> { +impl> Drop + for MappedGuard<'_, T, ALIGN, P> +{ fn drop(&mut self) { // SAFETY: The platform is expected to handle unmapping safely, including // the case where pages were never mapped (returns Unmapped error, ignored). @@ -471,7 +572,7 @@ impl Drop for MappedGuard<'_, T, ALIGN> { } } -impl Drop for PhysMutPtr { +impl> Drop for PhysMutPtr { fn drop(&mut self) { // SAFETY: The platform is expected to handle unmapping safely, including // the case where pages were never mapped (returns Unmapped error, ignored). @@ -483,7 +584,9 @@ impl Drop for PhysMutPtr { } } -impl core::fmt::Debug for PhysMutPtr { +impl> core::fmt::Debug + for PhysMutPtr +{ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("PhysMutPtr") .field("pages[0]", &self.pages.first().map_or(0, |p| p.as_usize())) @@ -496,20 +599,34 @@ impl core::fmt::Debug for PhysMutPtr { /// exposes only read access. #[derive(Clone)] #[repr(C)] -pub struct PhysConstPtr { - inner: PhysMutPtr, +pub struct PhysConstPtr> { + inner: PhysMutPtr, } -impl PhysConstPtr { +impl PhysConstPtr +where + P: PhysMapProvider, +{ /// Create a new `PhysConstPtr` from the given physical page array and offset. /// /// All addresses in `pages` should be valid and aligned to `ALIGN`, and `offset` should be smaller /// 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(pages: &[PhysPageAddr], offset: usize) -> Result + where + P: Default, + { + Self::new_with_provider(P::default(), pages, offset) + } + + pub fn new_with_provider( + provider: P, + pages: &[PhysPageAddr], + offset: usize, + ) -> Result { Ok(Self { - inner: PhysMutPtr::new(pages, offset)?, + inner: PhysMutPtr::new_with_provider(provider, pages, offset)?, }) } @@ -519,9 +636,20 @@ impl PhysConstPtr { /// `PhysConstPtr::new([align_down(pa), align_down(pa) + ALIGN, ..., align_up(pa + bytes) - ALIGN], pa % ALIGN)`. /// This function assumes that `pa`, ..., `pa+bytes` are both physically and virtually contiguous. If not, /// later accesses through `PhysConstPtr` may read data in a wrong order. - pub fn with_contiguous_pages(pa: usize, bytes: usize) -> Result { + pub fn with_contiguous_pages(pa: usize, bytes: usize) -> Result + where + P: Default, + { + Self::with_contiguous_pages_with_provider(P::default(), pa, bytes) + } + + pub fn with_contiguous_pages_with_provider( + provider: P, + pa: usize, + bytes: usize, + ) -> Result { Ok(Self { - inner: PhysMutPtr::with_contiguous_pages(pa, bytes)?, + inner: PhysMutPtr::with_contiguous_pages_with_provider(provider, pa, bytes)?, }) } @@ -530,9 +658,16 @@ impl PhysConstPtr { /// 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(pa: usize) -> Result + where + P: Default, + { + Self::with_usize_with_provider(P::default(), pa) + } + + pub fn with_usize_with_provider(provider: P, pa: usize) -> Result { Ok(Self { - inner: PhysMutPtr::with_usize(pa)?, + inner: PhysMutPtr::with_usize_with_provider(provider, pa)?, }) } @@ -572,7 +707,9 @@ impl PhysConstPtr { } } -impl core::fmt::Debug for PhysConstPtr { +impl> core::fmt::Debug + for PhysConstPtr +{ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("PhysConstPtr") .field( diff --git a/litebox_common_linux/src/vmap.rs b/litebox_common_linux/src/vmap.rs index e747ca5a3..02db10706 100644 --- a/litebox_common_linux/src/vmap.rs +++ b/litebox_common_linux/src/vmap.rs @@ -8,8 +8,8 @@ use thiserror::Error; /// /// `ALIGN`: The page frame size. /// -/// This provider exists to service `litebox_shim_optee::ptr::PhysMutPtr` and -/// `litebox_shim_optee::ptr::PhysConstPtr`. It can benefit other modules which need +/// 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). pub trait VmapManager { /// Map the given `PhysPageAddrArray` into virtually contiguous addresses with the given diff --git a/litebox_platform_lvbs/src/arch/x86/mm/paging.rs b/litebox_platform_lvbs/src/arch/x86/mm/paging.rs index 82edf6824..d259e9c51 100644 --- a/litebox_platform_lvbs/src/arch/x86/mm/paging.rs +++ b/litebox_platform_lvbs/src/arch/x86/mm/paging.rs @@ -656,7 +656,6 @@ impl X64PageTable<'_, M, ALIGN> { /// # Behavior /// - Any existing mapping is treated as an error /// - On error, all pages mapped by this call are unmapped (atomic) - #[cfg(feature = "optee_syscall")] pub(crate) fn map_non_contiguous_phys_frames( &self, frames: &[PhysFrame], @@ -725,7 +724,6 @@ impl X64PageTable<'_, M, ALIGN> { /// /// Note: The caller must already hold the page table lock (`self.inner`). /// This function accepts the locked `MappedPageTable` directly. - #[cfg(feature = "optee_syscall")] fn rollback_mapped_pages( inner: &mut MappedPageTable<'_, FrameMapping>, pages: x86_64::structures::paging::page::PageRangeInclusive, diff --git a/litebox_platform_lvbs/src/lib.rs b/litebox_platform_lvbs/src/lib.rs index 145f3617c..b181e1786 100644 --- a/litebox_platform_lvbs/src/lib.rs +++ b/litebox_platform_lvbs/src/lib.rs @@ -24,11 +24,14 @@ use litebox::{ shim::ContinueOperation, utils::TruncateExt, }; -use litebox_common_linux::errno::Errno; -#[cfg(feature = "optee_syscall")] -use litebox_common_linux::vmap::{ - PhysPageAddr, PhysPageAddrArray, PhysPageMapInfo, PhysPageMapPermissions, PhysPointerError, - VmapManager, +use litebox_common_linux::{PunchthroughSyscall, errno::Errno}; +use litebox_common_linux::{ + physical_pointers::PhysMapProvider, + #[cfg(feature = "optee_syscall")] + vmap::{ + PhysPageAddr, PhysPageAddrArray, PhysPageMapInfo, PhysPageMapPermissions, PhysPointerError, + VmapManager, + }, }; use x86_64::{ VirtAddr, @@ -39,7 +42,6 @@ use x86_64::{ }; use zerocopy::{FromBytes, IntoBytes}; -#[cfg(feature = "optee_syscall")] use crate::mm::vmap::vmap_allocator; extern crate alloc; @@ -51,31 +53,6 @@ pub mod mshv; pub mod syscall_entry; -/// Allocate a zeroed `Box` directly on the heap, avoiding stack intermediaries -/// for large types (e.g., 4096-byte `HekiPage`). -/// -/// This is safe because `T: FromBytes` guarantees that all-zero bytes are a valid `T`. -/// -/// # Panics -/// -/// Panics if `T` is a zero-sized type, since `alloc_zeroed` with a zero-sized -/// layout is undefined behavior. -fn box_new_zeroed() -> alloc::boxed::Box { - assert!( - core::mem::size_of::() > 0, - "box_new_zeroed does not support zero-sized types" - ); - let layout = core::alloc::Layout::new::(); - // Safety: layout has a non-zero size and correct alignment for T. - let ptr = unsafe { alloc::alloc::alloc_zeroed(layout) }.cast::(); - if ptr.is_null() { - alloc::alloc::handle_alloc_error(layout); - } - // Safety: ptr is a valid, zeroed, properly aligned heap allocation for T. - // T: FromBytes guarantees all-zero is a valid bit pattern. - unsafe { alloc::boxed::Box::from_raw(ptr) } -} - static CPU_MHZ: AtomicU64 = AtomicU64::new(0); /// Special page table ID for the base (kernel-only) page table. @@ -455,6 +432,32 @@ type UserConstPtr = type UserMutPtr = litebox::platform::common_providers::userspace_pointers::UserMutPtr; +#[derive(Clone, Copy, Debug, Default)] +pub struct Vtl0PhysMapProvider; + +impl PhysMapProvider for Vtl0PhysMapProvider { + fn validate_unowned(&self, pages: &PhysPageAddrArray) -> Result<(), PhysPointerError> { + VmapManager::validate_unowned(crate::platform_low(), pages) + } + + unsafe fn vmap( + &self, + pages: &PhysPageAddrArray, + perms: PhysPageMapPermissions, + ) -> Result, PhysPointerError> { + unsafe { VmapManager::vmap(crate::platform_low(), pages, perms) } + } + + unsafe fn vunmap(&self, map_info: PhysPageMapInfo) -> Result<(), PhysPointerError> { + unsafe { VmapManager::vunmap(crate::platform_low(), map_info) } + } +} + +pub type Vtl0PhysConstPtr = + litebox_common_linux::physical_pointers::PhysConstPtr; +pub type Vtl0PhysMutPtr = + litebox_common_linux::physical_pointers::PhysMutPtr; + impl RawPointerProvider for LinuxKernel { type RawConstPointer = UserConstPtr; type RawMutPointer = UserMutPtr; @@ -625,41 +628,6 @@ impl LinuxKernel { self.vtl1_phys_frame_range } - /// This function maps VTL0 physical page frames containing the physical addresses - /// from `phys_start` to `phys_end` to the VTL1 kernel page table. It internally page aligns - /// the input addresses to ensure the mapped memory area covers the entire input addresses - /// at the page level. It returns a page-aligned address (as `mmap` does) and the length of the mapped memory. - /// - /// Note: VTL0 physical memory is external/remote memory that this Rust binary doesn't own, - /// so mapping it doesn't create aliasing issues within the Rust memory model. - fn map_vtl0_phys_range( - &self, - phys_start: x86_64::PhysAddr, - phys_end: x86_64::PhysAddr, - flags: PageTableFlags, - ) -> Result<(*mut u8, usize), MapToError> { - let frame_range = PhysFrame::range( - PhysFrame::containing_address(phys_start), - PhysFrame::containing_address(phys_end.align_up(Size4KiB::SIZE)), - ); - - // ensure the input address range does not overlap with VTL1 memory - if frame_range.start < self.vtl1_phys_frame_range.end - && self.vtl1_phys_frame_range.start < frame_range.end - { - return Err(MapToError::FrameAllocationFailed); - } - - let flags = flags | PageTableFlags::NO_EXECUTE; - - Ok(( - self.page_table_manager - .current_page_table() - .map_phys_frame_range_direct(frame_range, flags, None)?, - usize::try_from(frame_range.len()).unwrap() * PAGE_SIZE, - )) - } - /// This function unmaps VTL0 pages from the page table. /// /// Allocator does not allocate memory frames for VTL0 pages, so frame deallocation is not needed. @@ -699,174 +667,6 @@ impl LinuxKernel { } } - /// Map a VTL0 physical range and return a guard that unmaps on drop. - fn map_vtl0_guard( - &self, - phys_addr: x86_64::PhysAddr, - size: u64, - flags: PageTableFlags, - ) -> Option> { - let phys_end = phys_addr - .as_u64() - .checked_add(size) - .and_then(|end| x86_64::PhysAddr::try_new(end).ok())?; - let (page_addr, page_aligned_length) = - self.map_vtl0_phys_range(phys_addr, phys_end, flags).ok()?; - let page_offset: usize = (phys_addr - phys_addr.align_down(Size4KiB::SIZE)).trunc(); - Some(Vtl0MappedGuard { - owner: self, - page_addr, - page_aligned_length, - ptr: page_addr.wrapping_add(page_offset), - size: size.trunc(), - }) - } - - /// This function copies data from VTL0 physical memory to the VTL1 kernel through `Box`. - /// Use this function instead of map/unmap functions to avoid potential TOCTTOU. - /// - /// # Safety - /// - /// The caller must ensure that the `phys_addr` is a valid VTL0 physical address - pub unsafe fn copy_from_vtl0_phys( - &self, - phys_addr: x86_64::PhysAddr, - ) -> Option> { - if core::mem::size_of::() == 0 { - return Some(alloc::boxed::Box::new(T::new_zeroed())); - } - - let src_guard = self.map_vtl0_guard( - phys_addr, - core::mem::size_of::() as u64, - PageTableFlags::PRESENT, - )?; - - let mut boxed = box_new_zeroed::(); - // Use memcpy_fallible instead of ptr::copy_nonoverlapping to handle - // the race where another core unmaps this page (via a shared page - // table) between map_vtl0_guard and the copy. The mapping is valid - // at this point, so a fault is not expected in the common case. - // TODO: Once VTL0 page-range locking is in place, this fallible copy - // may become unnecessary since the lock would prevent concurrent - // unmapping. It could still serve as a safety net against callers - // that forget to acquire the lock. - let result = unsafe { - litebox::mm::exception_table::memcpy_fallible( - core::ptr::from_mut::(boxed.as_mut()).cast(), - src_guard.ptr, - src_guard.size, - ) - }; - debug_assert!(result.is_ok(), "fault copying from VTL0 mapped page"); - - result.ok().map(|()| boxed) - } - - /// This function copies data from the VTL1 kernel to VTL0 physical memory. - /// Use this function instead of map/unmap functions to avoid potential TOCTTOU. - /// # Safety - /// - /// The caller must ensure that the `phys_addr` is a valid VTL0 physical address - pub unsafe fn copy_to_vtl0_phys( - &self, - phys_addr: x86_64::PhysAddr, - value: &T, - ) -> bool { - if core::mem::size_of::() == 0 { - return true; - } - - let Some(dst_guard) = self.map_vtl0_guard( - phys_addr, - core::mem::size_of::() as u64, - PageTableFlags::PRESENT | PageTableFlags::WRITABLE, - ) else { - return false; - }; - - // Fallible: another core may unmap this page concurrently. - let result = unsafe { - litebox::mm::exception_table::memcpy_fallible( - dst_guard.ptr, - core::ptr::from_ref::(value).cast::(), - dst_guard.size, - ) - }; - debug_assert!(result.is_ok(), "fault copying to VTL0 mapped page"); - result.is_ok() - } - - /// This function copies a slice from the VTL1 kernel to VTL0 physical memory. - /// Use this function instead of map/unmap functions to avoid potential TOCTTOU. - /// - /// # Safety - /// - /// The caller must ensure that the `phys_addr` is a valid VTL0 physical address. - pub unsafe fn copy_slice_to_vtl0_phys( - &self, - phys_addr: x86_64::PhysAddr, - value: &[T], - ) -> bool { - if core::mem::size_of_val(value) == 0 { - return true; - } - - let Some(dst_guard) = self.map_vtl0_guard( - phys_addr, - core::mem::size_of_val(value) as u64, - PageTableFlags::PRESENT | PageTableFlags::WRITABLE, - ) else { - return false; - }; - - // Fallible: another core may unmap this page concurrently. - let result = unsafe { - litebox::mm::exception_table::memcpy_fallible( - dst_guard.ptr, - value.as_ptr().cast::(), - dst_guard.size, - ) - }; - debug_assert!(result.is_ok(), "fault copying to VTL0 mapped page"); - result.is_ok() - } - - /// This function copies a slice from VTL0 physical memory to the VTL1 kernel. - /// Use this function instead of map/unmap functions to avoid potential TOCTTOU. - /// - /// # Safety - /// - /// The caller must ensure that the `phys_addr` is a valid VTL0 physical address. - pub unsafe fn copy_slice_from_vtl0_phys( - &self, - phys_addr: x86_64::PhysAddr, - buf: &mut [T], - ) -> bool { - if core::mem::size_of_val(buf) == 0 { - return true; - } - - let Some(src_guard) = self.map_vtl0_guard( - phys_addr, - core::mem::size_of_val(buf) as u64, - PageTableFlags::PRESENT, - ) else { - return false; - }; - - // Fallible: another core may unmap this page concurrently. - let result = unsafe { - litebox::mm::exception_table::memcpy_fallible( - buf.as_mut_ptr().cast::(), - src_guard.ptr, - src_guard.size, - ) - }; - debug_assert!(result.is_ok(), "fault copying from VTL0 mapped page"); - result.is_ok() - } - /// Create a new task page table for VTL1 user space and returns its ID. /// /// The kernel address space is duplicated from the base page table, @@ -947,26 +747,6 @@ impl LinuxKernel { } } -/// RAII guard that unmaps VTL0 physical pages when dropped. -struct Vtl0MappedGuard<'a, Host: HostInterface> { - owner: &'a LinuxKernel, - page_addr: *mut u8, - page_aligned_length: usize, - ptr: *mut u8, - size: usize, -} - -impl Drop for Vtl0MappedGuard<'_, Host> { - fn drop(&mut self) { - assert!( - self.owner - .unmap_vtl0_pages(self.page_addr, self.page_aligned_length) - .is_ok(), - "Failed to unmap VTL0 pages" - ); - } -} - impl RawMutexProvider for LinuxKernel { type RawMutex = RawMutex; } @@ -1337,7 +1117,6 @@ impl litebox::platform::SystemInfoProvider for LinuxKernel< } } -#[cfg(feature = "optee_syscall")] /// Checks whether the given physical addresses are contiguous with respect to ALIGN. fn is_contiguous(addrs: &[PhysPageAddr]) -> bool { for window in addrs.windows(2) { @@ -1354,7 +1133,6 @@ fn is_contiguous(addrs: &[PhysPageAddr]) -> bool { true } -#[cfg(feature = "optee_syscall")] impl VmapManager for LinuxKernel { unsafe fn vmap( &self, diff --git a/litebox_platform_lvbs/src/mm/mod.rs b/litebox_platform_lvbs/src/mm/mod.rs index df04d4209..a96d5fd12 100644 --- a/litebox_platform_lvbs/src/mm/mod.rs +++ b/litebox_platform_lvbs/src/mm/mod.rs @@ -6,7 +6,6 @@ use crate::arch::{PhysAddr, VirtAddr}; pub(crate) mod pgtable; -#[cfg(feature = "optee_syscall")] pub(crate) mod vmap; #[cfg(test)] @@ -56,7 +55,6 @@ pub trait MemoryProvider { fn pa_to_va_direct(pa: PhysAddr) -> VirtAddr { let pa = pa.as_u64() & !Self::PRIVATE_PTE_MASK; let va = VirtAddr::new_truncate(pa + Self::GVA_OFFSET.as_u64()); - #[cfg(feature = "optee_syscall")] assert!( va.as_u64() < crate::VMAP_START as u64, "VA {va:#x} is out of range for direct mapping" diff --git a/litebox_platform_lvbs/src/mshv/ringbuffer.rs b/litebox_platform_lvbs/src/mshv/ringbuffer.rs index 92a75da26..acabddb72 100644 --- a/litebox_platform_lvbs/src/mshv/ringbuffer.rs +++ b/litebox_platform_lvbs/src/mshv/ringbuffer.rs @@ -3,7 +3,10 @@ //! RingBuffer implementation and functions +use crate::Vtl0PhysMutPtr; use core::fmt; +use litebox::mm::linux::PAGE_SIZE; +use litebox::utils::TruncateExt; use spin::{Mutex, Once}; use x86_64::PhysAddr; @@ -27,8 +30,11 @@ impl RingBuffer { // the final [ring buffer size] values from the input buffer if buf.len() >= self.size { let single_slice = &buf[(buf.len() - self.size)..]; - unsafe { - crate::platform_low().copy_slice_to_vtl0_phys(self.rb_pa, single_slice); + if let Ok(mut ptr) = Vtl0PhysMutPtr::::with_contiguous_pages( + self.rb_pa.as_u64().truncate(), + single_slice.len(), + ) { + let _ = unsafe { ptr.write_slice_at_offset(0, single_slice) }; } self.write_offset = 0; return; @@ -39,16 +45,23 @@ impl RingBuffer { if buf.len() > space_remaining { let first_slice = &buf[..space_remaining]; let wraparound_slice = &buf[space_remaining..]; - unsafe { - crate::platform_low() - .copy_slice_to_vtl0_phys(self.rb_pa + self.write_offset as u64, first_slice); - crate::platform_low().copy_slice_to_vtl0_phys(self.rb_pa, wraparound_slice); + if let Ok(mut ptr) = Vtl0PhysMutPtr::::with_contiguous_pages( + (self.rb_pa + self.write_offset as u64).as_u64().truncate(), + first_slice.len(), + ) { + let _ = unsafe { ptr.write_slice_at_offset(0, first_slice) }; } - } else { - unsafe { - crate::platform_low() - .copy_slice_to_vtl0_phys(self.rb_pa + self.write_offset as u64, buf); + if let Ok(mut ptr) = Vtl0PhysMutPtr::::with_contiguous_pages( + self.rb_pa.as_u64().truncate(), + wraparound_slice.len(), + ) { + let _ = unsafe { ptr.write_slice_at_offset(0, wraparound_slice) }; } + } else if let Ok(mut ptr) = Vtl0PhysMutPtr::::with_contiguous_pages( + (self.rb_pa + self.write_offset as u64).as_u64().truncate(), + buf.len(), + ) { + let _ = unsafe { ptr.write_slice_at_offset(0, buf) }; } self.write_offset = (self.write_offset + buf.len()) % self.size; } diff --git a/litebox_platform_lvbs/src/mshv/vsm.rs b/litebox_platform_lvbs/src/mshv/vsm.rs index e2184c2f0..17e68d2f1 100644 --- a/litebox_platform_lvbs/src/mshv/vsm.rs +++ b/litebox_platform_lvbs/src/mshv/vsm.rs @@ -7,7 +7,7 @@ use crate::mshv::mem_integrity::parse_modinfo; use crate::mshv::ringbuffer::set_ringbuffer; use crate::{ - debug_serial_println, + Vtl0PhysConstPtr, Vtl0PhysMutPtr, debug_serial_println, host::{ PRK_LEN, bootparam::get_vtl1_memory_info, @@ -128,11 +128,12 @@ pub fn mshv_vsm_boot_aps(cpu_online_mask_pfn: u64) -> Result { .and_then(|pa| PhysAddr::try_new(pa).ok()) .ok_or(VsmError::InvalidPhysicalAddress)?; - let Some(cpu_mask) = (unsafe { - crate::platform_low().copy_from_vtl0_phys::(cpu_online_mask_page_addr) - }) else { - return Err(VsmError::CpuOnlineMaskCopyFailed); - }; + let mut cpu_mask_ptr = Vtl0PhysConstPtr::::with_usize( + cpu_online_mask_page_addr.as_u64().truncate(), + ) + .map_err(|_| VsmError::CpuOnlineMaskCopyFailed)?; + let cpu_mask = + unsafe { cpu_mask_ptr.read_at_offset(0) }.map_err(|_| VsmError::CpuOnlineMaskCopyFailed)?; #[cfg(debug_assertions)] { @@ -848,22 +849,34 @@ fn copy_heki_patch_from_vtl0(patch_pa_0: u64, patch_pa_1: u64) -> Result(patch_pa_0) } + let mut ptr = + Vtl0PhysConstPtr::::with_usize(patch_pa_0.as_u64().truncate()) + .map_err(|_| VsmError::Vtl0CopyFailed)?; + unsafe { ptr.read_at_offset(0) } .map(|boxed| *boxed) - .ok_or(VsmError::Vtl0CopyFailed) + .map_err(|_| VsmError::Vtl0CopyFailed) } else { let mut heki_patch = HekiPatch::new_zeroed(); let heki_patch_bytes = heki_patch.as_mut_bytes(); unsafe { - if !crate::platform_low().copy_slice_from_vtl0_phys( - patch_pa_0, - heki_patch_bytes.get_unchecked_mut(..bytes_in_first_page), - ) || !crate::platform_low().copy_slice_from_vtl0_phys( - patch_pa_1, - heki_patch_bytes.get_unchecked_mut(bytes_in_first_page..), - ) { - return Err(VsmError::Vtl0CopyFailed); - } + let mut first_ptr = Vtl0PhysConstPtr::::with_contiguous_pages( + patch_pa_0.as_u64().truncate(), + bytes_in_first_page, + ) + .map_err(|_| VsmError::Vtl0CopyFailed)?; + first_ptr + .read_slice_at_offset(0, heki_patch_bytes.get_unchecked_mut(..bytes_in_first_page)) + .map_err(|_| VsmError::Vtl0CopyFailed)?; + + let second_len = core::mem::size_of::() - bytes_in_first_page; + let mut second_ptr = Vtl0PhysConstPtr::::with_contiguous_pages( + patch_pa_1.as_u64().truncate(), + second_len, + ) + .map_err(|_| VsmError::Vtl0CopyFailed)?; + second_ptr + .read_slice_at_offset(0, heki_patch_bytes.get_unchecked_mut(bytes_in_first_page..)) + .map_err(|_| VsmError::Vtl0CopyFailed)?; } Ok(heki_patch) }?; @@ -889,24 +902,36 @@ fn apply_vtl0_text_patch(heki_patch: HekiPatch) -> Result<(), VsmError> { if heki_patch_pa_1.is_null() || (heki_patch_pa_0.align_up(Size4KiB::SIZE) == heki_patch_pa_1.align_down(Size4KiB::SIZE)) { - if !unsafe { - crate::platform_low().copy_slice_to_vtl0_phys( - heki_patch_pa_0, - &heki_patch.code[..usize::from(heki_patch.size)], + let patch = &heki_patch.code[..usize::from(heki_patch.size)]; + if !patch.is_empty() { + let mut ptr = Vtl0PhysMutPtr::::with_contiguous_pages( + heki_patch_pa_0.as_u64().truncate(), + patch.len(), ) - } { - return Err(VsmError::Vtl0CopyFailed); + .map_err(|_| VsmError::Vtl0CopyFailed)?; + unsafe { ptr.write_slice_at_offset(0, patch) }.map_err(|_| VsmError::Vtl0CopyFailed)?; } } else { let (patch_first, patch_second) = heki_patch.code[..usize::from(heki_patch.size)].split_at(bytes_in_first_page); - unsafe { - if !crate::platform_low().copy_slice_to_vtl0_phys(heki_patch_pa_0, patch_first) - || !crate::platform_low().copy_slice_to_vtl0_phys(heki_patch_pa_1, patch_second) - { - return Err(VsmError::Vtl0CopyFailed); - } + if !patch_first.is_empty() { + let mut ptr = Vtl0PhysMutPtr::::with_contiguous_pages( + heki_patch_pa_0.as_u64().truncate(), + patch_first.len(), + ) + .map_err(|_| VsmError::Vtl0CopyFailed)?; + unsafe { ptr.write_slice_at_offset(0, patch_first) } + .map_err(|_| VsmError::Vtl0CopyFailed)?; + } + if !patch_second.is_empty() { + let mut ptr = Vtl0PhysMutPtr::::with_contiguous_pages( + heki_patch_pa_1.as_u64().truncate(), + patch_second.len(), + ) + .map_err(|_| VsmError::Vtl0CopyFailed)?; + unsafe { ptr.write_slice_at_offset(0, patch_second) } + .map_err(|_| VsmError::Vtl0CopyFailed)?; } } Ok(()) @@ -1373,7 +1398,10 @@ fn copy_heki_pages_from_vtl0(pa: u64, nranges: u64) -> Option> { if visited_pages.contains(&cur_pa.as_u64()) { return None; } - let heki_page = (unsafe { crate::platform_low().copy_from_vtl0_phys::(cur_pa) })?; + let mut ptr = + Vtl0PhysConstPtr::::with_usize(next_pa.as_u64().truncate()) + .ok()?; + let heki_page = unsafe { ptr.read_at_offset(0) }.ok()?; if !heki_page.is_valid() { return None; } @@ -1649,11 +1677,12 @@ impl MemoryContainer { while phys_cur < phys_end { let phys_aligned = phys_cur.align_down(Size4KiB::SIZE); - let Some(page) = - (unsafe { crate::platform_low().copy_from_vtl0_phys::(phys_aligned) }) - else { - return Err(MemoryContainerError::CopyFromVtl0Failed); - }; + let mut ptr = Vtl0PhysConstPtr::::with_usize( + phys_aligned.as_u64().truncate(), + ) + .map_err(|_| MemoryContainerError::CopyFromVtl0Failed)?; + let page = unsafe { ptr.read_at_offset(0) } + .map_err(|_| MemoryContainerError::CopyFromVtl0Failed)?; let src_offset: usize = (phys_cur - phys_aligned).trunc(); let src_len = core::cmp::min(bytes_to_copy, PAGE_SIZE - src_offset); diff --git a/litebox_shim_optee/src/lib.rs b/litebox_shim_optee/src/lib.rs index af70c43b6..1c8b74713 100644 --- a/litebox_shim_optee/src/lib.rs +++ b/litebox_shim_optee/src/lib.rs @@ -21,7 +21,12 @@ use litebox::{ shim::ContinueOperation, utils::{ReinterpretUnsignedExt, TruncateExt}, }; -use litebox_common_linux::{MapFlags, ProtFlags, errno::Errno}; +use litebox_common_linux::{ + MapFlags, ProtFlags, + errno::Errno, + physical_pointers::PhysMapProvider, + vmap::{PhysPageAddrArray, PhysPageMapInfo, PhysPageMapPermissions, PhysPointerError}, +}; use litebox_common_optee::{ LdelfArg, LdelfSyscallRequest, SyscallRequest, TaFlags, TeeAlgorithm, TeeAlgorithmClass, TeeAttributeType, TeeCrypStateHandle, TeeHandleFlag, TeeIdentity, TeeLogin, TeeObjHandle, @@ -34,7 +39,6 @@ pub mod session; pub(crate) mod syscalls; pub mod msg_handler; -pub mod ptr; // Re-export session management types for convenience pub use session::{ @@ -1408,8 +1412,45 @@ impl SessionIdPool { } } -pub type NormalWorldConstPtr = crate::ptr::PhysConstPtr; -pub type NormalWorldMutPtr = crate::ptr::PhysMutPtr; +#[derive(Clone, Copy, Debug, Default)] +pub struct NormalWorldVmapProvider; + +impl PhysMapProvider for NormalWorldVmapProvider { + fn validate_unowned(&self, pages: &PhysPageAddrArray) -> Result<(), PhysPointerError> { + litebox_common_linux::vmap::VmapManager::validate_unowned( + litebox_platform_multiplex::platform(), + pages, + ) + } + + unsafe fn vmap( + &self, + pages: &PhysPageAddrArray, + perms: PhysPageMapPermissions, + ) -> Result, PhysPointerError> { + unsafe { + litebox_common_linux::vmap::VmapManager::vmap( + litebox_platform_multiplex::platform(), + pages, + perms, + ) + } + } + + unsafe fn vunmap(&self, map_info: PhysPageMapInfo) -> Result<(), PhysPointerError> { + unsafe { + litebox_common_linux::vmap::VmapManager::vunmap( + litebox_platform_multiplex::platform(), + map_info, + ) + } + } +} + +pub type NormalWorldConstPtr = + litebox_common_linux::physical_pointers::PhysConstPtr; +pub type NormalWorldMutPtr = + litebox_common_linux::physical_pointers::PhysMutPtr; #[cfg(test)] mod test_utils { From 2b368489e95e555c58bc2b8b70075dad22a95129 Mon Sep 17 00:00:00 2001 From: Sangho Lee Date: Fri, 1 May 2026 13:45:06 +0000 Subject: [PATCH 02/19] refactoring: associated func and phantom data --- litebox_common_linux/src/physical_pointers.rs | 135 ++++-------------- litebox_platform_lvbs/src/lib.rs | 5 +- litebox_shim_optee/src/lib.rs | 5 +- 3 files changed, 33 insertions(+), 112 deletions(-) diff --git a/litebox_common_linux/src/physical_pointers.rs b/litebox_common_linux/src/physical_pointers.rs index da3f253d7..222138c1a 100644 --- a/litebox_common_linux/src/physical_pointers.rs +++ b/litebox_common_linux/src/physical_pointers.rs @@ -60,21 +60,20 @@ use crate::vmap::{ PhysPageAddr, PhysPageAddrArray, PhysPageMapInfo, PhysPageMapPermissions, PhysPointerError, - VmapManager, }; +use core::marker::PhantomData; use zerocopy::FromBytes; /// Provider for physical page mapping operations used by physical pointers. -pub trait PhysMapProvider: Clone { - fn validate_unowned(&self, _pages: &PhysPageAddrArray) -> Result<(), PhysPointerError> { +pub trait PhysMapProvider { + fn validate_unowned(_pages: &PhysPageAddrArray) -> Result<(), PhysPointerError> { Ok(()) } /// # Safety /// - /// Same as [`VmapManager::vmap`]. + /// Same as [`crate::vmap::VmapManager::vmap`]. unsafe fn vmap( - &self, _pages: &PhysPageAddrArray, _perms: PhysPageMapPermissions, ) -> Result, PhysPointerError> { @@ -83,33 +82,12 @@ pub trait PhysMapProvider: Clone { /// # Safety /// - /// Same as [`VmapManager::vunmap`]. - unsafe fn vunmap(&self, _map_info: PhysPageMapInfo) -> Result<(), PhysPointerError> { + /// Same as [`crate::vmap::VmapManager::vunmap`]. + unsafe fn vunmap(_map_info: PhysPageMapInfo) -> Result<(), PhysPointerError> { Err(PhysPointerError::UnsupportedOperation) } } -impl PhysMapProvider for &P -where - P: VmapManager + ?Sized, -{ - fn validate_unowned(&self, pages: &PhysPageAddrArray) -> Result<(), PhysPointerError> { -

>::validate_unowned(*self, pages) - } - - unsafe fn vmap( - &self, - pages: &PhysPageAddrArray, - perms: PhysPageMapPermissions, - ) -> Result, PhysPointerError> { - unsafe {

>::vmap(*self, pages, perms) } - } - - unsafe fn vunmap(&self, map_info: PhysPageMapInfo) -> Result<(), PhysPointerError> { - unsafe {

>::vunmap(*self, map_info) } - } -} - /// Allocate a zeroed `Box` on the heap. /// /// # Panics @@ -148,12 +126,12 @@ fn align_down(address: usize, align: usize) -> usize { #[derive(Clone)] #[repr(C)] pub struct PhysMutPtr> { - provider: P, pages: alloc::boxed::Box<[PhysPageAddr]>, offset: usize, count: usize, map_info: Option>, - _type: core::marker::PhantomData, + _type: PhantomData, + _provider: PhantomData

, } impl PhysMutPtr @@ -166,26 +144,15 @@ where /// smaller 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 - where - P: Default, - { - Self::new_with_provider(P::default(), pages, offset) - } - - pub fn new_with_provider( - provider: P, - pages: &[PhysPageAddr], - offset: usize, - ) -> Result { + pub fn new(pages: &[PhysPageAddr], offset: usize) -> Result { if core::mem::size_of::() == 0 { return Ok(Self { - provider, pages: pages.into(), offset, count: usize::MAX, map_info: None, - _type: core::marker::PhantomData, + _type: PhantomData, + _provider: PhantomData, }); } if offset >= ALIGN { @@ -206,14 +173,14 @@ where core::mem::size_of::(), )); } - provider.validate_unowned(pages)?; + P::validate_unowned(pages)?; Ok(Self { - provider, pages: pages.into(), offset, count: size / core::mem::size_of::(), map_info: None, - _type: core::marker::PhantomData, + _type: PhantomData, + _provider: PhantomData, }) } @@ -223,18 +190,10 @@ where /// `PhysMutPtr::new([align_down(pa), align_down(pa) + ALIGN, ..., align_up(pa + bytes) - ALIGN], pa % ALIGN)`. /// This function assumes that `pa`, ..., `pa+bytes` are both physically and virtually contiguous. If not, /// later accesses through `PhysMutPtr` may read/write data in a wrong order. - pub fn with_contiguous_pages(pa: usize, bytes: usize) -> Result - where - P: Default, - { - Self::with_contiguous_pages_with_provider(P::default(), pa, bytes) - } - - pub fn with_contiguous_pages_with_provider( - provider: P, - pa: usize, - bytes: usize, - ) -> Result { + pub fn with_contiguous_pages(pa: usize, bytes: usize) -> Result { + if core::mem::size_of::() == 0 { + return Self::new(&[], 0); + } if bytes < core::mem::size_of::() { return Err(PhysPointerError::InsufficientPhysicalPages( bytes, @@ -260,7 +219,7 @@ where .checked_add(ALIGN) .ok_or(PhysPointerError::Overflow)?; } - Self::new_with_provider(provider, &pages, pa - start_page) + Self::new(&pages, pa - start_page) } /// Create a new `PhysMutPtr` from the given physical address for a single object. @@ -268,15 +227,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 - where - P: Default, - { - Self::with_usize_with_provider(P::default(), pa) - } - - pub fn with_usize_with_provider(provider: P, pa: usize) -> Result { - Self::with_contiguous_pages_with_provider(provider, pa, core::mem::size_of::()) + pub fn with_usize(pa: usize) -> Result { + Self::with_contiguous_pages(pa, core::mem::size_of::()) } /// Read the value at the given offset from the physical pointer. @@ -518,7 +470,7 @@ where if self.map_info.is_none() { let sub_pages = &self.pages[start..end]; unsafe { - self.map_info = Some(self.provider.vmap(sub_pages, perms)?); + self.map_info = Some(P::vmap(sub_pages, perms)?); } Ok(()) } else { @@ -537,7 +489,7 @@ where unsafe fn unmap(&mut self) -> Result<(), PhysPointerError> { if let Some(map_info) = self.map_info.take() { unsafe { - self.provider.vunmap(map_info)?; + P::vunmap(map_info)?; } Ok(()) } else { @@ -613,20 +565,9 @@ 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 - where - P: Default, - { - Self::new_with_provider(P::default(), pages, offset) - } - - pub fn new_with_provider( - provider: P, - pages: &[PhysPageAddr], - offset: usize, - ) -> Result { + pub fn new(pages: &[PhysPageAddr], offset: usize) -> Result { Ok(Self { - inner: PhysMutPtr::new_with_provider(provider, pages, offset)?, + inner: PhysMutPtr::new(pages, offset)?, }) } @@ -636,20 +577,9 @@ where /// `PhysConstPtr::new([align_down(pa), align_down(pa) + ALIGN, ..., align_up(pa + bytes) - ALIGN], pa % ALIGN)`. /// This function assumes that `pa`, ..., `pa+bytes` are both physically and virtually contiguous. If not, /// later accesses through `PhysConstPtr` may read data in a wrong order. - pub fn with_contiguous_pages(pa: usize, bytes: usize) -> Result - where - P: Default, - { - Self::with_contiguous_pages_with_provider(P::default(), pa, bytes) - } - - pub fn with_contiguous_pages_with_provider( - provider: P, - pa: usize, - bytes: usize, - ) -> Result { + pub fn with_contiguous_pages(pa: usize, bytes: usize) -> Result { Ok(Self { - inner: PhysMutPtr::with_contiguous_pages_with_provider(provider, pa, bytes)?, + inner: PhysMutPtr::with_contiguous_pages(pa, bytes)?, }) } @@ -658,16 +588,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 - where - P: Default, - { - Self::with_usize_with_provider(P::default(), pa) - } - - pub fn with_usize_with_provider(provider: P, pa: usize) -> Result { + pub fn with_usize(pa: usize) -> Result { Ok(Self { - inner: PhysMutPtr::with_usize_with_provider(provider, pa)?, + inner: PhysMutPtr::with_usize(pa)?, }) } diff --git a/litebox_platform_lvbs/src/lib.rs b/litebox_platform_lvbs/src/lib.rs index b181e1786..4c84b702a 100644 --- a/litebox_platform_lvbs/src/lib.rs +++ b/litebox_platform_lvbs/src/lib.rs @@ -436,19 +436,18 @@ type UserMutPtr = pub struct Vtl0PhysMapProvider; impl PhysMapProvider for Vtl0PhysMapProvider { - fn validate_unowned(&self, pages: &PhysPageAddrArray) -> Result<(), PhysPointerError> { + fn validate_unowned(pages: &PhysPageAddrArray) -> Result<(), PhysPointerError> { VmapManager::validate_unowned(crate::platform_low(), pages) } unsafe fn vmap( - &self, pages: &PhysPageAddrArray, perms: PhysPageMapPermissions, ) -> Result, PhysPointerError> { unsafe { VmapManager::vmap(crate::platform_low(), pages, perms) } } - unsafe fn vunmap(&self, map_info: PhysPageMapInfo) -> Result<(), PhysPointerError> { + unsafe fn vunmap(map_info: PhysPageMapInfo) -> Result<(), PhysPointerError> { unsafe { VmapManager::vunmap(crate::platform_low(), map_info) } } } diff --git a/litebox_shim_optee/src/lib.rs b/litebox_shim_optee/src/lib.rs index 1c8b74713..a2f06a2d9 100644 --- a/litebox_shim_optee/src/lib.rs +++ b/litebox_shim_optee/src/lib.rs @@ -1416,7 +1416,7 @@ impl SessionIdPool { pub struct NormalWorldVmapProvider; impl PhysMapProvider for NormalWorldVmapProvider { - fn validate_unowned(&self, pages: &PhysPageAddrArray) -> Result<(), PhysPointerError> { + fn validate_unowned(pages: &PhysPageAddrArray) -> Result<(), PhysPointerError> { litebox_common_linux::vmap::VmapManager::validate_unowned( litebox_platform_multiplex::platform(), pages, @@ -1424,7 +1424,6 @@ impl PhysMapProvider for NormalWorldVmapProvider { } unsafe fn vmap( - &self, pages: &PhysPageAddrArray, perms: PhysPageMapPermissions, ) -> Result, PhysPointerError> { @@ -1437,7 +1436,7 @@ impl PhysMapProvider for NormalWorldVmapProvider { } } - unsafe fn vunmap(&self, map_info: PhysPageMapInfo) -> Result<(), PhysPointerError> { + unsafe fn vunmap(map_info: PhysPageMapInfo) -> Result<(), PhysPointerError> { unsafe { litebox_common_linux::vmap::VmapManager::vunmap( litebox_platform_multiplex::platform(), From 1025056ac01be2e893229c7b0efa68f9f2568288 Mon Sep 17 00:00:00 2001 From: Sangho Lee Date: Fri, 1 May 2026 14:46:31 +0000 Subject: [PATCH 03/19] use physical pointer's non-contiguous read/write --- litebox_platform_lvbs/src/mshv/ringbuffer.rs | 41 +++++-- litebox_platform_lvbs/src/mshv/vsm.rs | 119 ++++++++++--------- 2 files changed, 90 insertions(+), 70 deletions(-) diff --git a/litebox_platform_lvbs/src/mshv/ringbuffer.rs b/litebox_platform_lvbs/src/mshv/ringbuffer.rs index acabddb72..8e086aa1a 100644 --- a/litebox_platform_lvbs/src/mshv/ringbuffer.rs +++ b/litebox_platform_lvbs/src/mshv/ringbuffer.rs @@ -4,9 +4,11 @@ //! RingBuffer implementation and functions use crate::Vtl0PhysMutPtr; +use alloc::vec::Vec; use core::fmt; use litebox::mm::linux::PAGE_SIZE; use litebox::utils::TruncateExt; +use litebox_common_linux::vmap::PhysPageAddr; use spin::{Mutex, Once}; use x86_64::PhysAddr; @@ -43,19 +45,34 @@ impl RingBuffer { // Otherwise, calculate if wraparound needed let space_remaining: usize = self.size - self.write_offset; if buf.len() > space_remaining { - let first_slice = &buf[..space_remaining]; - let wraparound_slice = &buf[space_remaining..]; - if let Ok(mut ptr) = Vtl0PhysMutPtr::::with_contiguous_pages( - (self.rb_pa + self.write_offset as u64).as_u64().truncate(), - first_slice.len(), - ) { - let _ = unsafe { ptr.write_slice_at_offset(0, first_slice) }; + let write_pa = self.rb_pa + self.write_offset as u64; + let write_start = write_pa.align_down(PAGE_SIZE as u64); + let write_end = (write_pa + space_remaining as u64).align_up(PAGE_SIZE as u64); + let wraparound_end = + (self.rb_pa + (buf.len() - space_remaining) as u64).align_up(PAGE_SIZE as u64); + let tail_page_count: usize = ((write_end - write_start) / PAGE_SIZE as u64).truncate(); + let wraparound_page_count: usize = + ((wraparound_end - self.rb_pa.align_down(PAGE_SIZE as u64)) / PAGE_SIZE as u64) + .truncate(); + let mut pages = Vec::with_capacity(tail_page_count + wraparound_page_count); + let mut cur_page = write_start; + while cur_page < write_end { + if let Some(page) = PhysPageAddr::::new(cur_page.as_u64().truncate()) { + pages.push(page); + } + cur_page += PAGE_SIZE as u64; } - if let Ok(mut ptr) = Vtl0PhysMutPtr::::with_contiguous_pages( - self.rb_pa.as_u64().truncate(), - wraparound_slice.len(), - ) { - let _ = unsafe { ptr.write_slice_at_offset(0, wraparound_slice) }; + cur_page = self.rb_pa.align_down(PAGE_SIZE as u64); + while cur_page < wraparound_end { + if let Some(page) = PhysPageAddr::::new(cur_page.as_u64().truncate()) { + pages.push(page); + } + cur_page += PAGE_SIZE as u64; + } + if let Ok(mut ptr) = + Vtl0PhysMutPtr::::new(&pages, (write_pa - write_start).truncate()) + { + let _ = unsafe { ptr.write_slice_at_offset(0, buf) }; } } else if let Ok(mut ptr) = Vtl0PhysMutPtr::::with_contiguous_pages( (self.rb_pa + self.write_offset as u64).as_u64().truncate(), diff --git a/litebox_platform_lvbs/src/mshv/vsm.rs b/litebox_platform_lvbs/src/mshv/vsm.rs index 17e68d2f1..7ab224f7c 100644 --- a/litebox_platform_lvbs/src/mshv/vsm.rs +++ b/litebox_platform_lvbs/src/mshv/vsm.rs @@ -49,7 +49,7 @@ use core::{ }; use hashbrown::{HashMap, HashSet}; use litebox::utils::TruncateExt; -use litebox_common_linux::errno::Errno; +use litebox_common_linux::{errno::Errno, vmap::PhysPageAddr}; use spin::Once; use thiserror::Error; use x86_64::{ @@ -57,13 +57,9 @@ use x86_64::{ structures::paging::{PageSize, PhysFrame, Size4KiB, frame::PhysFrameRange}, }; use x509_cert::{Certificate, der::Decode}; -use zerocopy::{FromBytes, FromZeros, Immutable, IntoBytes, KnownLayout}; +use zerocopy::{FromBytes, FromZeros, IntoBytes}; use zeroize::Zeroizing; -#[derive(Copy, Clone, FromBytes, Immutable, KnownLayout)] -#[repr(align(4096))] -struct AlignedPage([u8; PAGE_SIZE]); - // For now, we do not validate large kernel modules due to the VTL1's memory size limitation. const MODULE_VALIDATION_MAX_SIZE: usize = 64 * 1024 * 1024; @@ -129,7 +125,7 @@ pub fn mshv_vsm_boot_aps(cpu_online_mask_pfn: u64) -> Result { .ok_or(VsmError::InvalidPhysicalAddress)?; let mut cpu_mask_ptr = Vtl0PhysConstPtr::::with_usize( - cpu_online_mask_page_addr.as_u64().truncate(), + cpu_online_mask_page_addr.as_u64().trunc(), ) .map_err(|_| VsmError::CpuOnlineMaskCopyFailed)?; let cpu_mask = @@ -850,7 +846,7 @@ fn copy_heki_patch_from_vtl0(patch_pa_0: u64, patch_pa_1: u64) -> Result::with_usize(patch_pa_0.as_u64().truncate()) + Vtl0PhysConstPtr::::with_usize(patch_pa_0.as_u64().trunc()) .map_err(|_| VsmError::Vtl0CopyFailed)?; unsafe { ptr.read_at_offset(0) } .map(|boxed| *boxed) @@ -858,24 +854,21 @@ fn copy_heki_patch_from_vtl0(patch_pa_0: u64, patch_pa_1: u64) -> Result::with_contiguous_pages( - patch_pa_0.as_u64().truncate(), - bytes_in_first_page, + let pages = [ + PhysPageAddr::::new( + patch_pa_0.align_down(Size4KiB::SIZE).as_u64().trunc(), ) - .map_err(|_| VsmError::Vtl0CopyFailed)?; - first_ptr - .read_slice_at_offset(0, heki_patch_bytes.get_unchecked_mut(..bytes_in_first_page)) - .map_err(|_| VsmError::Vtl0CopyFailed)?; - - let second_len = core::mem::size_of::() - bytes_in_first_page; - let mut second_ptr = Vtl0PhysConstPtr::::with_contiguous_pages( - patch_pa_1.as_u64().truncate(), - second_len, - ) - .map_err(|_| VsmError::Vtl0CopyFailed)?; - second_ptr - .read_slice_at_offset(0, heki_patch_bytes.get_unchecked_mut(bytes_in_first_page..)) + .ok_or(VsmError::Vtl0CopyFailed)?, + PhysPageAddr::::new(patch_pa_1.as_u64().trunc()) + .ok_or(VsmError::Vtl0CopyFailed)?, + ]; + let mut ptr = Vtl0PhysConstPtr::::new( + &pages, + (patch_pa_0 - patch_pa_0.align_down(Size4KiB::SIZE)).trunc(), + ) + .map_err(|_| VsmError::Vtl0CopyFailed)?; + unsafe { + ptr.read_slice_at_offset(0, heki_patch_bytes) .map_err(|_| VsmError::Vtl0CopyFailed)?; } Ok(heki_patch) @@ -905,7 +898,7 @@ fn apply_vtl0_text_patch(heki_patch: HekiPatch) -> Result<(), VsmError> { let patch = &heki_patch.code[..usize::from(heki_patch.size)]; if !patch.is_empty() { let mut ptr = Vtl0PhysMutPtr::::with_contiguous_pages( - heki_patch_pa_0.as_u64().truncate(), + heki_patch_pa_0.as_u64().trunc(), patch.len(), ) .map_err(|_| VsmError::Vtl0CopyFailed)?; @@ -914,23 +907,29 @@ fn apply_vtl0_text_patch(heki_patch: HekiPatch) -> Result<(), VsmError> { } else { let (patch_first, patch_second) = heki_patch.code[..usize::from(heki_patch.size)].split_at(bytes_in_first_page); + let pages = [ + PhysPageAddr::::new( + heki_patch_pa_0 + .align_down(Size4KiB::SIZE) + .as_u64() + .trunc(), + ) + .ok_or(VsmError::Vtl0CopyFailed)?, + PhysPageAddr::::new(heki_patch_pa_1.as_u64().trunc()) + .ok_or(VsmError::Vtl0CopyFailed)?, + ]; + let mut ptr = Vtl0PhysMutPtr::::new( + &pages, + (heki_patch_pa_0 - heki_patch_pa_0.align_down(Size4KiB::SIZE)).trunc(), + ) + .map_err(|_| VsmError::Vtl0CopyFailed)?; if !patch_first.is_empty() { - let mut ptr = Vtl0PhysMutPtr::::with_contiguous_pages( - heki_patch_pa_0.as_u64().truncate(), - patch_first.len(), - ) - .map_err(|_| VsmError::Vtl0CopyFailed)?; unsafe { ptr.write_slice_at_offset(0, patch_first) } .map_err(|_| VsmError::Vtl0CopyFailed)?; } if !patch_second.is_empty() { - let mut ptr = Vtl0PhysMutPtr::::with_contiguous_pages( - heki_patch_pa_1.as_u64().truncate(), - patch_second.len(), - ) - .map_err(|_| VsmError::Vtl0CopyFailed)?; - unsafe { ptr.write_slice_at_offset(0, patch_second) } + unsafe { ptr.write_slice_at_offset(patch_first.len(), patch_second) } .map_err(|_| VsmError::Vtl0CopyFailed)?; } } @@ -1399,7 +1398,7 @@ fn copy_heki_pages_from_vtl0(pa: u64, nranges: u64) -> Option> { return None; } let mut ptr = - Vtl0PhysConstPtr::::with_usize(next_pa.as_u64().truncate()) + Vtl0PhysConstPtr::::with_usize(next_pa.as_u64().trunc()) .ok()?; let heki_page = unsafe { ptr.read_at_offset(0) }.ok()?; if !heki_page.is_valid() { @@ -1672,29 +1671,33 @@ impl MemoryContainer { phys_start: PhysAddr, phys_end: PhysAddr, ) -> Result<(), MemoryContainerError> { - let mut bytes_to_copy: usize = (phys_end - phys_start).trunc(); - let mut phys_cur = phys_start; + let bytes_to_copy: usize = (phys_end - phys_start).trunc(); + if bytes_to_copy == 0 { + return Ok(()); + } - while phys_cur < phys_end { - let phys_aligned = phys_cur.align_down(Size4KiB::SIZE); - let mut ptr = Vtl0PhysConstPtr::::with_usize( - phys_aligned.as_u64().truncate(), - ) - .map_err(|_| MemoryContainerError::CopyFromVtl0Failed)?; - let page = unsafe { ptr.read_at_offset(0) } - .map_err(|_| MemoryContainerError::CopyFromVtl0Failed)?; + let phys_aligned_start = phys_start.align_down(Size4KiB::SIZE); + let phys_aligned_end = phys_end.align_up(Size4KiB::SIZE); + let aligned_len: usize = (phys_aligned_end - phys_aligned_start).trunc(); + let mut pages = Vec::with_capacity(aligned_len.div_ceil(PAGE_SIZE)); + let mut phys_cur = phys_aligned_start; + while phys_cur < phys_aligned_end { + pages.push( + PhysPageAddr::::new(phys_cur.as_u64().trunc()) + .ok_or(MemoryContainerError::CopyFromVtl0Failed)?, + ); + phys_cur += PAGE_SIZE as u64; + } - let src_offset: usize = (phys_cur - phys_aligned).trunc(); - let src_len = core::cmp::min(bytes_to_copy, PAGE_SIZE - src_offset); - let src = &page.0[src_offset..src_offset + src_len]; + let page_offset: usize = (phys_start - phys_aligned_start).trunc(); + let mut ptr = Vtl0PhysConstPtr::::new(&pages, page_offset) + .map_err(|_| MemoryContainerError::CopyFromVtl0Failed)?; - self.buf.extend_from_slice(src); - phys_cur = phys_cur - .as_u64() - .checked_add(src_len as u64) - .and_then(|next| PhysAddr::try_new(next).ok()) - .ok_or(MemoryContainerError::Overflow)?; - bytes_to_copy -= src_len; + let old_len = self.buf.len(); + self.buf.resize(old_len + bytes_to_copy, 0); + if unsafe { ptr.read_slice_at_offset(0, &mut self.buf[old_len..]) }.is_err() { + self.buf.truncate(old_len); + return Err(MemoryContainerError::CopyFromVtl0Failed); } Ok(()) } From ab131ca4befeb1de8c8014d12710da9461079eee Mon Sep 17 00:00:00 2001 From: Sangho Lee Date: Fri, 1 May 2026 15:41:37 +0000 Subject: [PATCH 04/19] refactoring --- litebox_platform_lvbs/src/mshv/ringbuffer.rs | 100 +++++++++++-------- 1 file changed, 56 insertions(+), 44 deletions(-) diff --git a/litebox_platform_lvbs/src/mshv/ringbuffer.rs b/litebox_platform_lvbs/src/mshv/ringbuffer.rs index 8e086aa1a..4db84b4ed 100644 --- a/litebox_platform_lvbs/src/mshv/ringbuffer.rs +++ b/litebox_platform_lvbs/src/mshv/ringbuffer.rs @@ -30,57 +30,69 @@ impl RingBuffer { pub fn write(&mut self, buf: &[u8]) { // If the input buffer is longer than the ring buffer, fill the whole ring buffer with // the final [ring buffer size] values from the input buffer - if buf.len() >= self.size { - let single_slice = &buf[(buf.len() - self.size)..]; - if let Ok(mut ptr) = Vtl0PhysMutPtr::::with_contiguous_pages( + let (mut ptr, write_buf, next_write_offset) = if buf.len() >= self.size { + let write_buf = &buf[(buf.len() - self.size)..]; + let Ok(ptr) = Vtl0PhysMutPtr::::with_contiguous_pages( self.rb_pa.as_u64().truncate(), - single_slice.len(), - ) { - let _ = unsafe { ptr.write_slice_at_offset(0, single_slice) }; - } - self.write_offset = 0; - return; - } - - // Otherwise, calculate if wraparound needed - let space_remaining: usize = self.size - self.write_offset; - if buf.len() > space_remaining { - let write_pa = self.rb_pa + self.write_offset as u64; - let write_start = write_pa.align_down(PAGE_SIZE as u64); - let write_end = (write_pa + space_remaining as u64).align_up(PAGE_SIZE as u64); - let wraparound_end = - (self.rb_pa + (buf.len() - space_remaining) as u64).align_up(PAGE_SIZE as u64); - let tail_page_count: usize = ((write_end - write_start) / PAGE_SIZE as u64).truncate(); - let wraparound_page_count: usize = - ((wraparound_end - self.rb_pa.align_down(PAGE_SIZE as u64)) / PAGE_SIZE as u64) - .truncate(); - let mut pages = Vec::with_capacity(tail_page_count + wraparound_page_count); - let mut cur_page = write_start; - while cur_page < write_end { - if let Some(page) = PhysPageAddr::::new(cur_page.as_u64().truncate()) { + write_buf.len(), + ) else { + return; + }; + (ptr, write_buf, 0) + } else { + // Otherwise, calculate if wraparound needed + let space_remaining: usize = self.size - self.write_offset; + if buf.len() > space_remaining { + let write_pa = self.rb_pa + self.write_offset as u64; + let write_start = write_pa.align_down(PAGE_SIZE as u64); + let write_end = (write_pa + space_remaining as u64).align_up(PAGE_SIZE as u64); + let wraparound_end = + (self.rb_pa + (buf.len() - space_remaining) as u64).align_up(PAGE_SIZE as u64); + let tail_page_count: usize = + ((write_end - write_start) / PAGE_SIZE as u64).truncate(); + let wraparound_page_count: usize = + ((wraparound_end - self.rb_pa.align_down(PAGE_SIZE as u64)) / PAGE_SIZE as u64) + .truncate(); + let mut pages = Vec::with_capacity(tail_page_count + wraparound_page_count); + let mut cur_page = write_start; + while cur_page < write_end { + let Some(page) = PhysPageAddr::::new(cur_page.as_u64().truncate()) + else { + return; + }; pages.push(page); + cur_page += PAGE_SIZE as u64; } - cur_page += PAGE_SIZE as u64; - } - cur_page = self.rb_pa.align_down(PAGE_SIZE as u64); - while cur_page < wraparound_end { - if let Some(page) = PhysPageAddr::::new(cur_page.as_u64().truncate()) { + cur_page = self.rb_pa.align_down(PAGE_SIZE as u64); + while cur_page < wraparound_end { + let Some(page) = PhysPageAddr::::new(cur_page.as_u64().truncate()) + else { + return; + }; pages.push(page); + cur_page += PAGE_SIZE as u64; } - cur_page += PAGE_SIZE as u64; - } - if let Ok(mut ptr) = - Vtl0PhysMutPtr::::new(&pages, (write_pa - write_start).truncate()) - { - let _ = unsafe { ptr.write_slice_at_offset(0, buf) }; + let Ok(ptr) = Vtl0PhysMutPtr::::new( + &pages, + (write_pa - write_start).truncate(), + ) else { + return; + }; + (ptr, buf, (self.write_offset + buf.len()) % self.size) + } else { + let Ok(ptr) = Vtl0PhysMutPtr::::with_contiguous_pages( + (self.rb_pa + self.write_offset as u64).as_u64().truncate(), + buf.len(), + ) else { + return; + }; + (ptr, buf, (self.write_offset + buf.len()) % self.size) } - } else if let Ok(mut ptr) = Vtl0PhysMutPtr::::with_contiguous_pages( - (self.rb_pa + self.write_offset as u64).as_u64().truncate(), - buf.len(), - ) { - let _ = unsafe { ptr.write_slice_at_offset(0, buf) }; + }; + + if unsafe { ptr.write_slice_at_offset(0, write_buf) }.is_ok() { + self.write_offset = next_write_offset; } - self.write_offset = (self.write_offset + buf.len()) % self.size; } } From 6a26c5836084a6ea778f3af77c93c84408e52747 Mon Sep 17 00:00:00 2001 From: Sangho Lee Date: Fri, 1 May 2026 15:47:52 +0000 Subject: [PATCH 05/19] ZST --- litebox_common_linux/src/physical_pointers.rs | 22 +++---------------- litebox_common_linux/src/vmap.rs | 2 ++ 2 files changed, 5 insertions(+), 19 deletions(-) diff --git a/litebox_common_linux/src/physical_pointers.rs b/litebox_common_linux/src/physical_pointers.rs index 222138c1a..47e54cf36 100644 --- a/litebox_common_linux/src/physical_pointers.rs +++ b/litebox_common_linux/src/physical_pointers.rs @@ -146,14 +146,7 @@ where /// returns `Err(PhysPointerError)`. pub fn new(pages: &[PhysPageAddr], offset: usize) -> Result { if core::mem::size_of::() == 0 { - return Ok(Self { - pages: pages.into(), - offset, - count: usize::MAX, - map_info: None, - _type: PhantomData, - _provider: PhantomData, - }); + return Err(PhysPointerError::UnsupportedZeroSizedType); } if offset >= ALIGN { return Err(PhysPointerError::InvalidBaseOffset(offset, ALIGN)); @@ -191,9 +184,6 @@ where /// This function assumes that `pa`, ..., `pa+bytes` are both physically and virtually contiguous. If not, /// later accesses through `PhysMutPtr` may read/write data in a wrong order. pub fn with_contiguous_pages(pa: usize, bytes: usize) -> Result { - if core::mem::size_of::() == 0 { - return Self::new(&[], 0); - } if bytes < core::mem::size_of::() { return Err(PhysPointerError::InsufficientPhysicalPages( bytes, @@ -246,9 +236,6 @@ where where T: FromBytes, { - if core::mem::size_of::() == 0 { - return Ok(alloc::boxed::Box::new(T::new_zeroed())); - } if count >= self.count { return Err(PhysPointerError::IndexOutOfBounds(count, self.count)); } @@ -289,7 +276,7 @@ where where T: FromBytes, { - if core::mem::size_of::() == 0 || values.is_empty() { + if values.is_empty() { return Ok(()); } if count @@ -330,9 +317,6 @@ where count: usize, value: T, ) -> Result<(), PhysPointerError> { - if core::mem::size_of::() == 0 { - return Ok(()); - } if count >= self.count { return Err(PhysPointerError::IndexOutOfBounds(count, self.count)); } @@ -368,7 +352,7 @@ where count: usize, values: &[T], ) -> Result<(), PhysPointerError> { - if core::mem::size_of::() == 0 || values.is_empty() { + if values.is_empty() { return Ok(()); } if count diff --git a/litebox_common_linux/src/vmap.rs b/litebox_common_linux/src/vmap.rs index 02db10706..b4ca48310 100644 --- a/litebox_common_linux/src/vmap.rs +++ b/litebox_common_linux/src/vmap.rs @@ -156,6 +156,8 @@ pub enum PhysPointerError { "The total size of the given pages ({0} bytes) is insufficient for the requested type ({1} bytes)" )] InsufficientPhysicalPages(usize, usize), + #[error("Zero-sized types are unsupported for physical pointers")] + UnsupportedZeroSizedType, #[error("Index {0} is out of bounds (count: {1})")] IndexOutOfBounds(usize, usize), #[error("Physical address {0:#x} is already mapped")] From f6e9a2e1f7f37317d9db47c30d7a64c550860e89 Mon Sep 17 00:00:00 2001 From: Sangho Lee Date: Fri, 1 May 2026 16:25:08 +0000 Subject: [PATCH 06/19] revert --- litebox_platform_lvbs/src/mshv/ringbuffer.rs | 108 +++++++++---------- 1 file changed, 50 insertions(+), 58 deletions(-) diff --git a/litebox_platform_lvbs/src/mshv/ringbuffer.rs b/litebox_platform_lvbs/src/mshv/ringbuffer.rs index 4db84b4ed..55828b336 100644 --- a/litebox_platform_lvbs/src/mshv/ringbuffer.rs +++ b/litebox_platform_lvbs/src/mshv/ringbuffer.rs @@ -4,17 +4,17 @@ //! RingBuffer implementation and functions use crate::Vtl0PhysMutPtr; -use alloc::vec::Vec; use core::fmt; use litebox::mm::linux::PAGE_SIZE; use litebox::utils::TruncateExt; -use litebox_common_linux::vmap::PhysPageAddr; use spin::{Mutex, Once}; use x86_64::PhysAddr; pub struct RingBuffer { rb_pa: PhysAddr, write_offset: usize, + // TODO: If ring buffers are constrained to page-sized/page-aligned ranges, + // wraparound writes can be optimized into a single non-contiguous mapping. size: usize, } @@ -28,71 +28,63 @@ impl RingBuffer { } pub fn write(&mut self, buf: &[u8]) { + if self.size == 0 || buf.is_empty() { + return; + } + // If the input buffer is longer than the ring buffer, fill the whole ring buffer with // the final [ring buffer size] values from the input buffer - let (mut ptr, write_buf, next_write_offset) = if buf.len() >= self.size { - let write_buf = &buf[(buf.len() - self.size)..]; - let Ok(ptr) = Vtl0PhysMutPtr::::with_contiguous_pages( + if buf.len() >= self.size { + let single_slice = &buf[(buf.len() - self.size)..]; + let Ok(mut ptr) = Vtl0PhysMutPtr::::with_contiguous_pages( self.rb_pa.as_u64().truncate(), - write_buf.len(), + single_slice.len(), ) else { return; }; - (ptr, write_buf, 0) - } else { - // Otherwise, calculate if wraparound needed - let space_remaining: usize = self.size - self.write_offset; - if buf.len() > space_remaining { - let write_pa = self.rb_pa + self.write_offset as u64; - let write_start = write_pa.align_down(PAGE_SIZE as u64); - let write_end = (write_pa + space_remaining as u64).align_up(PAGE_SIZE as u64); - let wraparound_end = - (self.rb_pa + (buf.len() - space_remaining) as u64).align_up(PAGE_SIZE as u64); - let tail_page_count: usize = - ((write_end - write_start) / PAGE_SIZE as u64).truncate(); - let wraparound_page_count: usize = - ((wraparound_end - self.rb_pa.align_down(PAGE_SIZE as u64)) / PAGE_SIZE as u64) - .truncate(); - let mut pages = Vec::with_capacity(tail_page_count + wraparound_page_count); - let mut cur_page = write_start; - while cur_page < write_end { - let Some(page) = PhysPageAddr::::new(cur_page.as_u64().truncate()) - else { - return; - }; - pages.push(page); - cur_page += PAGE_SIZE as u64; - } - cur_page = self.rb_pa.align_down(PAGE_SIZE as u64); - while cur_page < wraparound_end { - let Some(page) = PhysPageAddr::::new(cur_page.as_u64().truncate()) - else { - return; - }; - pages.push(page); - cur_page += PAGE_SIZE as u64; - } - let Ok(ptr) = Vtl0PhysMutPtr::::new( - &pages, - (write_pa - write_start).truncate(), - ) else { - return; - }; - (ptr, buf, (self.write_offset + buf.len()) % self.size) - } else { - let Ok(ptr) = Vtl0PhysMutPtr::::with_contiguous_pages( - (self.rb_pa + self.write_offset as u64).as_u64().truncate(), - buf.len(), - ) else { - return; - }; - (ptr, buf, (self.write_offset + buf.len()) % self.size) + if unsafe { ptr.write_slice_at_offset(0, single_slice) }.is_err() { + return; + } + self.write_offset = 0; + return; + } + + // Otherwise, calculate if wraparound needed + let space_remaining: usize = self.size - self.write_offset; + if buf.len() > space_remaining { + let first_slice = &buf[..space_remaining]; + let wraparound_slice = &buf[space_remaining..]; + let Ok(mut ptr) = Vtl0PhysMutPtr::::with_contiguous_pages( + (self.rb_pa + self.write_offset as u64).as_u64().truncate(), + first_slice.len(), + ) else { + return; + }; + if unsafe { ptr.write_slice_at_offset(0, first_slice) }.is_err() { + return; } - }; - if unsafe { ptr.write_slice_at_offset(0, write_buf) }.is_ok() { - self.write_offset = next_write_offset; + let Ok(mut ptr) = Vtl0PhysMutPtr::::with_contiguous_pages( + self.rb_pa.as_u64().truncate(), + wraparound_slice.len(), + ) else { + return; + }; + if unsafe { ptr.write_slice_at_offset(0, wraparound_slice) }.is_err() { + return; + } + } else { + let Ok(mut ptr) = Vtl0PhysMutPtr::::with_contiguous_pages( + (self.rb_pa + self.write_offset as u64).as_u64().truncate(), + buf.len(), + ) else { + return; + }; + if unsafe { ptr.write_slice_at_offset(0, buf) }.is_err() { + return; + } } + self.write_offset = (self.write_offset + buf.len()) % self.size; } } From 6e3310da5a42f45fc8ff714781a9e515715de056 Mon Sep 17 00:00:00 2001 From: Sangho Lee Date: Fri, 1 May 2026 19:51:23 +0000 Subject: [PATCH 07/19] refactoring --- litebox_platform_lvbs/src/mshv/vsm.rs | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/litebox_platform_lvbs/src/mshv/vsm.rs b/litebox_platform_lvbs/src/mshv/vsm.rs index 7ab224f7c..4c765453c 100644 --- a/litebox_platform_lvbs/src/mshv/vsm.rs +++ b/litebox_platform_lvbs/src/mshv/vsm.rs @@ -1676,22 +1676,11 @@ impl MemoryContainer { return Ok(()); } - let phys_aligned_start = phys_start.align_down(Size4KiB::SIZE); - let phys_aligned_end = phys_end.align_up(Size4KiB::SIZE); - let aligned_len: usize = (phys_aligned_end - phys_aligned_start).trunc(); - let mut pages = Vec::with_capacity(aligned_len.div_ceil(PAGE_SIZE)); - let mut phys_cur = phys_aligned_start; - while phys_cur < phys_aligned_end { - pages.push( - PhysPageAddr::::new(phys_cur.as_u64().trunc()) - .ok_or(MemoryContainerError::CopyFromVtl0Failed)?, - ); - phys_cur += PAGE_SIZE as u64; - } - - let page_offset: usize = (phys_start - phys_aligned_start).trunc(); - let mut ptr = Vtl0PhysConstPtr::::new(&pages, page_offset) - .map_err(|_| MemoryContainerError::CopyFromVtl0Failed)?; + let mut ptr = Vtl0PhysConstPtr::::with_contiguous_pages( + phys_start.as_u64().trunc(), + bytes_to_copy, + ) + .map_err(|_| MemoryContainerError::CopyFromVtl0Failed)?; let old_len = self.buf.len(); self.buf.resize(old_len + bytes_to_copy, 0); From b7bb1794cff9c979a19bd8ba4d8053777523830f Mon Sep 17 00:00:00 2001 From: Sangho Lee Date: Fri, 1 May 2026 19:56:11 +0000 Subject: [PATCH 08/19] refactoring --- litebox_platform_lvbs/src/mshv/ringbuffer.rs | 45 ++++++-------------- 1 file changed, 14 insertions(+), 31 deletions(-) diff --git a/litebox_platform_lvbs/src/mshv/ringbuffer.rs b/litebox_platform_lvbs/src/mshv/ringbuffer.rs index 55828b336..79762f2ef 100644 --- a/litebox_platform_lvbs/src/mshv/ringbuffer.rs +++ b/litebox_platform_lvbs/src/mshv/ringbuffer.rs @@ -32,17 +32,20 @@ impl RingBuffer { return; } + let write_slice = |pa: PhysAddr, slice: &[u8]| -> bool { + Vtl0PhysMutPtr::::with_contiguous_pages( + pa.as_u64().truncate(), + slice.len(), + ) + .and_then(|mut ptr| unsafe { ptr.write_slice_at_offset(0, slice) }) + .is_ok() + }; + // If the input buffer is longer than the ring buffer, fill the whole ring buffer with // the final [ring buffer size] values from the input buffer if buf.len() >= self.size { let single_slice = &buf[(buf.len() - self.size)..]; - let Ok(mut ptr) = Vtl0PhysMutPtr::::with_contiguous_pages( - self.rb_pa.as_u64().truncate(), - single_slice.len(), - ) else { - return; - }; - if unsafe { ptr.write_slice_at_offset(0, single_slice) }.is_err() { + if !write_slice(self.rb_pa, single_slice) { return; } self.write_offset = 0; @@ -54,33 +57,13 @@ impl RingBuffer { if buf.len() > space_remaining { let first_slice = &buf[..space_remaining]; let wraparound_slice = &buf[space_remaining..]; - let Ok(mut ptr) = Vtl0PhysMutPtr::::with_contiguous_pages( - (self.rb_pa + self.write_offset as u64).as_u64().truncate(), - first_slice.len(), - ) else { - return; - }; - if unsafe { ptr.write_slice_at_offset(0, first_slice) }.is_err() { - return; - } - - let Ok(mut ptr) = Vtl0PhysMutPtr::::with_contiguous_pages( - self.rb_pa.as_u64().truncate(), - wraparound_slice.len(), - ) else { - return; - }; - if unsafe { ptr.write_slice_at_offset(0, wraparound_slice) }.is_err() { + if !write_slice(self.rb_pa + self.write_offset as u64, first_slice) + || !write_slice(self.rb_pa, wraparound_slice) + { return; } } else { - let Ok(mut ptr) = Vtl0PhysMutPtr::::with_contiguous_pages( - (self.rb_pa + self.write_offset as u64).as_u64().truncate(), - buf.len(), - ) else { - return; - }; - if unsafe { ptr.write_slice_at_offset(0, buf) }.is_err() { + if !write_slice(self.rb_pa + self.write_offset as u64, buf) { return; } } From 6b758af0c541e77da718a9610062eeeb1d6bca0c Mon Sep 17 00:00:00 2001 From: Sangho Lee Date: Fri, 1 May 2026 20:06:05 +0000 Subject: [PATCH 09/19] format --- litebox_platform_lvbs/src/mshv/ringbuffer.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/litebox_platform_lvbs/src/mshv/ringbuffer.rs b/litebox_platform_lvbs/src/mshv/ringbuffer.rs index 79762f2ef..bf36be859 100644 --- a/litebox_platform_lvbs/src/mshv/ringbuffer.rs +++ b/litebox_platform_lvbs/src/mshv/ringbuffer.rs @@ -62,10 +62,8 @@ impl RingBuffer { { return; } - } else { - if !write_slice(self.rb_pa + self.write_offset as u64, buf) { - return; - } + } else if !write_slice(self.rb_pa + self.write_offset as u64, buf) { + return; } self.write_offset = (self.write_offset + buf.len()) % self.size; } From ec775634e5cd09718472d77cf07be8199c1c75e2 Mon Sep 17 00:00:00 2001 From: Sangho Lee Date: Fri, 15 May 2026 16:39:56 +0000 Subject: [PATCH 10/19] rebase --- litebox_platform_lvbs/src/mshv/vsm.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/litebox_platform_lvbs/src/mshv/vsm.rs b/litebox_platform_lvbs/src/mshv/vsm.rs index 4c765453c..c400c772d 100644 --- a/litebox_platform_lvbs/src/mshv/vsm.rs +++ b/litebox_platform_lvbs/src/mshv/vsm.rs @@ -969,12 +969,15 @@ fn mshv_vsm_set_platform_root_key(key_pa: u64) -> Result { let key_pa = PhysAddr::try_new(key_pa).map_err(|_| VsmError::InvalidPhysicalAddress)?; let mut keybuf = Zeroizing::new([0u8; PRK_LEN]); - if unsafe { crate::platform_low().copy_slice_from_vtl0_phys(key_pa, &mut *keybuf) } { - set_platform_root_key(&*keybuf); - Ok(0) - } else { - Err(VsmError::Vtl0CopyFailed) - } + let mut key_ptr = Vtl0PhysConstPtr::::with_contiguous_pages( + key_pa.as_u64().truncate(), + PRK_LEN, + ) + .map_err(|_| VsmError::Vtl0CopyFailed)?; + unsafe { key_ptr.read_slice_at_offset(0, &mut *keybuf) } + .map_err(|_| VsmError::Vtl0CopyFailed)?; + set_platform_root_key(&*keybuf); + Ok(0) } /// VSM function dispatcher From d5476b1c781af7ad1775ddbabb75ca899d6195fb Mon Sep 17 00:00:00 2001 From: Sangho Lee Date: Fri, 15 May 2026 18:42:28 +0000 Subject: [PATCH 11/19] ringbuffer fast path --- litebox_platform_lvbs/src/mshv/ringbuffer.rs | 122 ++++++++++++++----- 1 file changed, 92 insertions(+), 30 deletions(-) diff --git a/litebox_platform_lvbs/src/mshv/ringbuffer.rs b/litebox_platform_lvbs/src/mshv/ringbuffer.rs index bf36be859..e4075b022 100644 --- a/litebox_platform_lvbs/src/mshv/ringbuffer.rs +++ b/litebox_platform_lvbs/src/mshv/ringbuffer.rs @@ -7,23 +7,32 @@ use crate::Vtl0PhysMutPtr; use core::fmt; use litebox::mm::linux::PAGE_SIZE; use litebox::utils::TruncateExt; +use litebox_common_linux::vmap::PhysPageAddr; use spin::{Mutex, Once}; use x86_64::PhysAddr; pub struct RingBuffer { rb_pa: PhysAddr, write_offset: usize, - // TODO: If ring buffers are constrained to page-sized/page-aligned ranges, - // wraparound writes can be optimized into a single non-contiguous mapping. size: usize, + // True iff `rb_pa` is page-aligned and `size` is a non-zero page multiple. + // The fast path collapses wraparound into a single map/unmap by passing the + // wrap span as a non-contiguous page list; pages themselves are derived from + // `rb_pa + idx * PAGE_SIZE` since the ring is physically contiguous. + page_aligned: bool, } impl RingBuffer { pub fn new(phys_addr: PhysAddr, requested_size: usize) -> Self { + let pa: usize = phys_addr.as_u64().trunc(); + let page_aligned = requested_size > 0 + && requested_size.is_multiple_of(PAGE_SIZE) + && pa.is_multiple_of(PAGE_SIZE); RingBuffer { rb_pa: phys_addr, write_offset: 0, size: requested_size, + page_aligned, } } @@ -31,41 +40,94 @@ impl RingBuffer { if self.size == 0 || buf.is_empty() { return; } + self.write_offset = if self.page_aligned { + write_fast(self.rb_pa, self.size, self.write_offset, buf) + } else { + write_slow(self.rb_pa, self.size, self.write_offset, buf) + }; + } +} + +/// Fast path for a page-aligned, page-sized ring buffer. Wraparound becomes a +/// 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 (unchanged on failure). +fn write_fast(rb_pa: PhysAddr, size: usize, write_offset: usize, buf: &[u8]) -> usize { + // If `buf` would force the start page into the span twice, vmap rejects the + // duplicate. Hand off to the two-write slow path before truncating `buf`. + if buf.len() < size && write_offset % PAGE_SIZE + buf.len() > size { + return write_slow(rb_pa, size, write_offset, buf); + } + + // Inputs longer than the buffer overwrite the whole ring with the trailing bytes. + let (buf, start) = if buf.len() >= size { + (&buf[(buf.len() - size)..], 0) + } else { + (buf, write_offset) + }; + + let rb_pa: usize = rb_pa.as_u64().trunc(); + let page_count = size / PAGE_SIZE; + let start_page = start / PAGE_SIZE; + let in_page_offset = start % PAGE_SIZE; + let span_pages = (in_page_offset + buf.len()).div_ceil(PAGE_SIZE); + let mut span = alloc::vec::Vec::with_capacity(span_pages); + for i in 0..span_pages { + let page_idx = (start_page + i) % page_count; + let Some(addr) = page_idx + .checked_mul(PAGE_SIZE) + .and_then(|off| rb_pa.checked_add(off)) + .and_then(PhysPageAddr::::new) + else { + return write_offset; + }; + span.push(addr); + } - let write_slice = |pa: PhysAddr, slice: &[u8]| -> bool { - Vtl0PhysMutPtr::::with_contiguous_pages( - pa.as_u64().truncate(), - slice.len(), - ) + let Ok(mut ptr) = Vtl0PhysMutPtr::::new(&span, in_page_offset) else { + return write_offset; + }; + if unsafe { ptr.write_slice_at_offset(0, buf) }.is_ok() { + (start + buf.len()) % size + } else { + write_offset + } +} + +/// Slow path used when `rb_pa` or `size` is not page-aligned/page-multiple. +/// Wraparound issues two map/unmap cycles; the returned offset advances by +/// bytes actually written so a mid-sequence failure does not strand stale data. +fn write_slow(rb_pa: PhysAddr, size: usize, write_offset: usize, buf: &[u8]) -> usize { + let write_slice = |pa: PhysAddr, slice: &[u8]| -> bool { + Vtl0PhysMutPtr::::with_contiguous_pages(pa.as_u64().trunc(), slice.len()) .and_then(|mut ptr| unsafe { ptr.write_slice_at_offset(0, slice) }) .is_ok() + }; + + if buf.len() >= size { + let single_slice = &buf[(buf.len() - size)..]; + return if write_slice(rb_pa, single_slice) { + 0 + } else { + write_offset }; + } - // If the input buffer is longer than the ring buffer, fill the whole ring buffer with - // the final [ring buffer size] values from the input buffer - if buf.len() >= self.size { - let single_slice = &buf[(buf.len() - self.size)..]; - if !write_slice(self.rb_pa, single_slice) { - return; - } - self.write_offset = 0; - return; + let space_remaining = size - write_offset; + if buf.len() > space_remaining { + let first_slice = &buf[..space_remaining]; + let wraparound_slice = &buf[space_remaining..]; + if !write_slice(rb_pa + write_offset as u64, first_slice) { + return write_offset; } - - // Otherwise, calculate if wraparound needed - let space_remaining: usize = self.size - self.write_offset; - if buf.len() > space_remaining { - let first_slice = &buf[..space_remaining]; - let wraparound_slice = &buf[space_remaining..]; - if !write_slice(self.rb_pa + self.write_offset as u64, first_slice) - || !write_slice(self.rb_pa, wraparound_slice) - { - return; - } - } else if !write_slice(self.rb_pa + self.write_offset as u64, buf) { - return; + if !write_slice(rb_pa, wraparound_slice) { + return 0; } - self.write_offset = (self.write_offset + buf.len()) % self.size; + wraparound_slice.len() + } else if write_slice(rb_pa + write_offset as u64, buf) { + (write_offset + buf.len()) % size + } else { + write_offset } } From 2bac44f22866c5468f5485bb166c660cc04aa6e2 Mon Sep 17 00:00:00 2001 From: Sangho Lee Date: Fri, 15 May 2026 20:08:55 +0000 Subject: [PATCH 12/19] minor --- litebox_platform_lvbs/Cargo.toml | 3 +-- litebox_platform_lvbs/src/mshv/ringbuffer.rs | 5 +++++ litebox_platform_multiplex/Cargo.toml | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/litebox_platform_lvbs/Cargo.toml b/litebox_platform_lvbs/Cargo.toml index a6ceaee6b..c7b91e243 100644 --- a/litebox_platform_lvbs/Cargo.toml +++ b/litebox_platform_lvbs/Cargo.toml @@ -44,8 +44,7 @@ x86_64 = { version = "0.15.2", default-features = false, features = ["instructio libc = "0.2.177" [features] -default = ["optee_syscall"] -optee_syscall = [] +default = [] linux_syscall = [] devbox = [] diff --git a/litebox_platform_lvbs/src/mshv/ringbuffer.rs b/litebox_platform_lvbs/src/mshv/ringbuffer.rs index e4075b022..415445955 100644 --- a/litebox_platform_lvbs/src/mshv/ringbuffer.rs +++ b/litebox_platform_lvbs/src/mshv/ringbuffer.rs @@ -97,6 +97,10 @@ 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; the returned offset advances by /// bytes actually written so a mid-sequence failure does not strand stale data. +/// Failure return values: +/// - First slice fails: nothing written, cursor unchanged (`write_offset`). +/// - Second slice fails after first succeeded: `space_remaining` bytes were +/// written, so the cursor advances to `(write_offset + space_remaining) % size = 0`. fn write_slow(rb_pa: PhysAddr, size: usize, write_offset: usize, buf: &[u8]) -> usize { let write_slice = |pa: PhysAddr, slice: &[u8]| -> bool { Vtl0PhysMutPtr::::with_contiguous_pages(pa.as_u64().trunc(), slice.len()) @@ -121,6 +125,7 @@ fn write_slow(rb_pa: PhysAddr, size: usize, write_offset: usize, buf: &[u8]) -> return write_offset; } if !write_slice(rb_pa, wraparound_slice) { + // `space_remaining` bytes written; cursor wraps to 0. return 0; } wraparound_slice.len() diff --git a/litebox_platform_multiplex/Cargo.toml b/litebox_platform_multiplex/Cargo.toml index 1c48c3a73..1099d334e 100644 --- a/litebox_platform_multiplex/Cargo.toml +++ b/litebox_platform_multiplex/Cargo.toml @@ -21,7 +21,7 @@ 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", "litebox_platform_lvbs/optee_syscall"] +platform_lvbs_with_optee_syscall = ["platform_lvbs"] [lints] workspace = true From 095edefd010345cf589bb7b8450bbfdda257f6af Mon Sep 17 00:00:00 2001 From: Sangho Lee Date: Fri, 15 May 2026 23:53:59 +0000 Subject: [PATCH 13/19] collapse apply_vtl0_text_patch --- litebox_platform_lvbs/src/mshv/vsm.rs | 36 +++++++++------------------ 1 file changed, 12 insertions(+), 24 deletions(-) diff --git a/litebox_platform_lvbs/src/mshv/vsm.rs b/litebox_platform_lvbs/src/mshv/vsm.rs index c400c772d..ee38057a5 100644 --- a/litebox_platform_lvbs/src/mshv/vsm.rs +++ b/litebox_platform_lvbs/src/mshv/vsm.rs @@ -888,25 +888,21 @@ fn apply_vtl0_text_patch(heki_patch: HekiPatch) -> Result<(), VsmError> { let heki_patch_pa_0 = PhysAddr::new(heki_patch.pa[0]); let heki_patch_pa_1 = PhysAddr::new(heki_patch.pa[1]); - let patch_target_page_offset: usize = - (heki_patch_pa_0 - heki_patch_pa_0.align_down(Size4KiB::SIZE)).trunc(); - let bytes_in_first_page = PAGE_SIZE - patch_target_page_offset; + let patch = &heki_patch.code[..usize::from(heki_patch.size)]; + if patch.is_empty() { + return Ok(()); + } if heki_patch_pa_1.is_null() || (heki_patch_pa_0.align_up(Size4KiB::SIZE) == heki_patch_pa_1.align_down(Size4KiB::SIZE)) { - let patch = &heki_patch.code[..usize::from(heki_patch.size)]; - if !patch.is_empty() { - let mut ptr = Vtl0PhysMutPtr::::with_contiguous_pages( - heki_patch_pa_0.as_u64().trunc(), - patch.len(), - ) - .map_err(|_| VsmError::Vtl0CopyFailed)?; - unsafe { ptr.write_slice_at_offset(0, patch) }.map_err(|_| VsmError::Vtl0CopyFailed)?; - } + let mut ptr = Vtl0PhysMutPtr::::with_contiguous_pages( + heki_patch_pa_0.as_u64().trunc(), + patch.len(), + ) + .map_err(|_| VsmError::Vtl0CopyFailed)?; + unsafe { ptr.write_slice_at_offset(0, patch) }.map_err(|_| VsmError::Vtl0CopyFailed)?; } else { - let (patch_first, patch_second) = - heki_patch.code[..usize::from(heki_patch.size)].split_at(bytes_in_first_page); let pages = [ PhysPageAddr::::new( heki_patch_pa_0 @@ -923,15 +919,7 @@ fn apply_vtl0_text_patch(heki_patch: HekiPatch) -> Result<(), VsmError> { (heki_patch_pa_0 - heki_patch_pa_0.align_down(Size4KiB::SIZE)).trunc(), ) .map_err(|_| VsmError::Vtl0CopyFailed)?; - - if !patch_first.is_empty() { - unsafe { ptr.write_slice_at_offset(0, patch_first) } - .map_err(|_| VsmError::Vtl0CopyFailed)?; - } - if !patch_second.is_empty() { - unsafe { ptr.write_slice_at_offset(patch_first.len(), patch_second) } - .map_err(|_| VsmError::Vtl0CopyFailed)?; - } + unsafe { ptr.write_slice_at_offset(0, patch) }.map_err(|_| VsmError::Vtl0CopyFailed)?; } Ok(()) } @@ -970,7 +958,7 @@ fn mshv_vsm_set_platform_root_key(key_pa: u64) -> Result { let mut keybuf = Zeroizing::new([0u8; PRK_LEN]); let mut key_ptr = Vtl0PhysConstPtr::::with_contiguous_pages( - key_pa.as_u64().truncate(), + key_pa.as_u64().trunc(), PRK_LEN, ) .map_err(|_| VsmError::Vtl0CopyFailed)?; From 93c4f17617ec50f7c43a72d014538c293bcf3cf9 Mon Sep 17 00:00:00 2001 From: Sangho Lee Date: Sat, 16 May 2026 01:59:59 +0000 Subject: [PATCH 14/19] simplify traits --- litebox_common_linux/src/physical_pointers.rs | 71 +++++++------------ litebox_common_linux/src/vmap.rs | 16 +++++ litebox_platform_lvbs/src/lib.rs | 37 ++++------ litebox_shim_optee/src/lib.rs | 45 +++--------- 4 files changed, 60 insertions(+), 109 deletions(-) diff --git a/litebox_common_linux/src/physical_pointers.rs b/litebox_common_linux/src/physical_pointers.rs index 47e54cf36..20025dd37 100644 --- a/litebox_common_linux/src/physical_pointers.rs +++ b/litebox_common_linux/src/physical_pointers.rs @@ -59,35 +59,12 @@ //! provide a wrong list by mistake or intentionally). use crate::vmap::{ - PhysPageAddr, PhysPageAddrArray, PhysPageMapInfo, PhysPageMapPermissions, PhysPointerError, + GlobalVmapManager, PhysPageAddr, PhysPageMapInfo, PhysPageMapPermissions, PhysPointerError, + VmapManager, }; use core::marker::PhantomData; use zerocopy::FromBytes; -/// Provider for physical page mapping operations used by physical pointers. -pub trait PhysMapProvider { - fn validate_unowned(_pages: &PhysPageAddrArray) -> Result<(), PhysPointerError> { - Ok(()) - } - - /// # Safety - /// - /// Same as [`crate::vmap::VmapManager::vmap`]. - unsafe fn vmap( - _pages: &PhysPageAddrArray, - _perms: PhysPageMapPermissions, - ) -> Result, PhysPointerError> { - Err(PhysPointerError::UnsupportedOperation) - } - - /// # Safety - /// - /// Same as [`crate::vmap::VmapManager::vunmap`]. - unsafe fn vunmap(_map_info: PhysPageMapInfo) -> Result<(), PhysPointerError> { - Err(PhysPointerError::UnsupportedOperation) - } -} - /// Allocate a zeroed `Box` on the heap. /// /// # Panics @@ -125,18 +102,18 @@ fn align_down(address: usize, align: usize) -> usize { /// memory for an object of type `T`. #[derive(Clone)] #[repr(C)] -pub struct PhysMutPtr> { +pub struct PhysMutPtr> { pages: alloc::boxed::Box<[PhysPageAddr]>, offset: usize, count: usize, map_info: Option>, _type: PhantomData, - _provider: PhantomData

, + _vmap: PhantomData, } -impl PhysMutPtr +impl PhysMutPtr where - P: PhysMapProvider, + V: GlobalVmapManager, { /// Create a new `PhysMutPtr` from the given physical page array and offset. /// @@ -166,14 +143,14 @@ where core::mem::size_of::(), )); } - P::validate_unowned(pages)?; + V::manager().validate_unowned(pages)?; Ok(Self { pages: pages.into(), offset, count: size / core::mem::size_of::(), map_info: None, _type: PhantomData, - _provider: PhantomData, + _vmap: PhantomData, }) } @@ -402,7 +379,7 @@ where count: usize, size: usize, perms: PhysPageMapPermissions, - ) -> Result, PhysPointerError> { + ) -> Result, PhysPointerError> { let skip = self .offset .checked_add( @@ -454,7 +431,7 @@ where if self.map_info.is_none() { let sub_pages = &self.pages[start..end]; unsafe { - self.map_info = Some(P::vmap(sub_pages, perms)?); + self.map_info = Some(V::manager().vmap(sub_pages, perms)?); } Ok(()) } else { @@ -473,7 +450,7 @@ where unsafe fn unmap(&mut self) -> Result<(), PhysPointerError> { if let Some(map_info) = self.map_info.take() { unsafe { - P::vunmap(map_info)?; + V::manager().vunmap(map_info)?; } Ok(()) } else { @@ -488,14 +465,14 @@ where /// /// Created by `map_and_get_ptr_guard`. Holds a mutable borrow on the parent /// `PhysMutPtr` and provides the mapped base pointer for the duration of the mapping. -struct MappedGuard<'a, T: Clone, const ALIGN: usize, P: PhysMapProvider> { - owner: &'a mut PhysMutPtr, +struct MappedGuard<'a, T: Clone, const ALIGN: usize, V: GlobalVmapManager> { + owner: &'a mut PhysMutPtr, ptr: *mut T, size: usize, } -impl> Drop - for MappedGuard<'_, T, ALIGN, P> +impl> Drop + for MappedGuard<'_, T, ALIGN, V> { fn drop(&mut self) { // SAFETY: The platform is expected to handle unmapping safely, including @@ -508,7 +485,7 @@ impl> Drop } } -impl> Drop for PhysMutPtr { +impl> Drop for PhysMutPtr { fn drop(&mut self) { // SAFETY: The platform is expected to handle unmapping safely, including // the case where pages were never mapped (returns Unmapped error, ignored). @@ -520,8 +497,8 @@ impl> Drop for PhysMutPt } } -impl> core::fmt::Debug - for PhysMutPtr +impl> core::fmt::Debug + for PhysMutPtr { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("PhysMutPtr") @@ -535,13 +512,13 @@ impl> core::fmt::Debug /// exposes only read access. #[derive(Clone)] #[repr(C)] -pub struct PhysConstPtr> { - inner: PhysMutPtr, +pub struct PhysConstPtr> { + inner: PhysMutPtr, } -impl PhysConstPtr +impl PhysConstPtr where - P: PhysMapProvider, + V: GlobalVmapManager, { /// Create a new `PhysConstPtr` from the given physical page array and offset. /// @@ -614,8 +591,8 @@ where } } -impl> core::fmt::Debug - for PhysConstPtr +impl> core::fmt::Debug + for PhysConstPtr { 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 b4ca48310..102f0c9e3 100644 --- a/litebox_common_linux/src/vmap.rs +++ b/litebox_common_linux/src/vmap.rs @@ -79,6 +79,22 @@ pub trait VmapManager { } } +/// 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 4c84b702a..0b8dcd789 100644 --- a/litebox_platform_lvbs/src/lib.rs +++ b/litebox_platform_lvbs/src/lib.rs @@ -24,15 +24,11 @@ use litebox::{ shim::ContinueOperation, utils::TruncateExt, }; -use litebox_common_linux::{PunchthroughSyscall, errno::Errno}; -use litebox_common_linux::{ - physical_pointers::PhysMapProvider, - #[cfg(feature = "optee_syscall")] - vmap::{ - PhysPageAddr, PhysPageAddrArray, PhysPageMapInfo, PhysPageMapPermissions, PhysPointerError, - VmapManager, - }, +use litebox_common_linux::vmap::{ + GlobalVmapManager, PhysPageAddr, PhysPageAddrArray, PhysPageMapInfo, PhysPageMapPermissions, + PhysPointerError, VmapManager, }; +use litebox_common_linux::{PunchthroughSyscall, errno::Errno}; use x86_64::{ VirtAddr, structures::paging::{ @@ -433,29 +429,19 @@ type UserMutPtr = litebox::platform::common_providers::userspace_pointers::UserMutPtr; #[derive(Clone, Copy, Debug, Default)] -pub struct Vtl0PhysMapProvider; - -impl PhysMapProvider for Vtl0PhysMapProvider { - fn validate_unowned(pages: &PhysPageAddrArray) -> Result<(), PhysPointerError> { - VmapManager::validate_unowned(crate::platform_low(), pages) - } - - unsafe fn vmap( - pages: &PhysPageAddrArray, - perms: PhysPageMapPermissions, - ) -> Result, PhysPointerError> { - unsafe { VmapManager::vmap(crate::platform_low(), pages, perms) } - } +pub struct Vmap; - unsafe fn vunmap(map_info: PhysPageMapInfo) -> Result<(), PhysPointerError> { - unsafe { VmapManager::vunmap(crate::platform_low(), map_info) } +impl GlobalVmapManager for Vmap { + type Manager = crate::host::LvbsLinuxKernel; + fn manager() -> &'static Self::Manager { + crate::platform_low() } } pub type Vtl0PhysConstPtr = - litebox_common_linux::physical_pointers::PhysConstPtr; + litebox_common_linux::physical_pointers::PhysConstPtr; pub type Vtl0PhysMutPtr = - litebox_common_linux::physical_pointers::PhysMutPtr; + litebox_common_linux::physical_pointers::PhysMutPtr; impl RawPointerProvider for LinuxKernel { type RawConstPointer = UserConstPtr; @@ -1283,6 +1269,7 @@ impl VmapManager for LinuxKernel Ok(()) } + #[allow(dead_code, reason = "will be used soon")] unsafe fn protect( &self, pages: &PhysPageAddrArray, diff --git a/litebox_shim_optee/src/lib.rs b/litebox_shim_optee/src/lib.rs index a2f06a2d9..ec0b2c161 100644 --- a/litebox_shim_optee/src/lib.rs +++ b/litebox_shim_optee/src/lib.rs @@ -21,12 +21,7 @@ use litebox::{ shim::ContinueOperation, utils::{ReinterpretUnsignedExt, TruncateExt}, }; -use litebox_common_linux::{ - MapFlags, ProtFlags, - errno::Errno, - physical_pointers::PhysMapProvider, - vmap::{PhysPageAddrArray, PhysPageMapInfo, PhysPageMapPermissions, PhysPointerError}, -}; +use litebox_common_linux::{MapFlags, ProtFlags, errno::Errno, vmap::GlobalVmapManager}; use litebox_common_optee::{ LdelfArg, LdelfSyscallRequest, SyscallRequest, TaFlags, TeeAlgorithm, TeeAlgorithmClass, TeeAttributeType, TeeCrypStateHandle, TeeHandleFlag, TeeIdentity, TeeLogin, TeeObjHandle, @@ -1413,43 +1408,19 @@ impl SessionIdPool { } #[derive(Clone, Copy, Debug, Default)] -pub struct NormalWorldVmapProvider; - -impl PhysMapProvider for NormalWorldVmapProvider { - fn validate_unowned(pages: &PhysPageAddrArray) -> Result<(), PhysPointerError> { - litebox_common_linux::vmap::VmapManager::validate_unowned( - litebox_platform_multiplex::platform(), - pages, - ) - } - - unsafe fn vmap( - pages: &PhysPageAddrArray, - perms: PhysPageMapPermissions, - ) -> Result, PhysPointerError> { - unsafe { - litebox_common_linux::vmap::VmapManager::vmap( - litebox_platform_multiplex::platform(), - pages, - perms, - ) - } - } +pub struct Vmap; - unsafe fn vunmap(map_info: PhysPageMapInfo) -> Result<(), PhysPointerError> { - unsafe { - litebox_common_linux::vmap::VmapManager::vunmap( - litebox_platform_multiplex::platform(), - map_info, - ) - } +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; + litebox_common_linux::physical_pointers::PhysConstPtr; pub type NormalWorldMutPtr = - litebox_common_linux::physical_pointers::PhysMutPtr; + litebox_common_linux::physical_pointers::PhysMutPtr; #[cfg(test)] mod test_utils { From 51cf3dc988d343f6fb071b757e35bdbd898ed230 Mon Sep 17 00:00:00 2001 From: Sangho Lee Date: Thu, 21 May 2026 20:28:40 +0000 Subject: [PATCH 15/19] rebase --- litebox_platform_lvbs/src/mshv/vsm.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/litebox_platform_lvbs/src/mshv/vsm.rs b/litebox_platform_lvbs/src/mshv/vsm.rs index ee38057a5..c6bcc40de 100644 --- a/litebox_platform_lvbs/src/mshv/vsm.rs +++ b/litebox_platform_lvbs/src/mshv/vsm.rs @@ -1389,8 +1389,7 @@ fn copy_heki_pages_from_vtl0(pa: u64, nranges: u64) -> Option> { return None; } let mut ptr = - Vtl0PhysConstPtr::::with_usize(next_pa.as_u64().trunc()) - .ok()?; + Vtl0PhysConstPtr::::with_usize(cur_pa.as_u64().trunc()).ok()?; let heki_page = unsafe { ptr.read_at_offset(0) }.ok()?; if !heki_page.is_valid() { return None; From 361f944a59b74da9414e155d657a8970dec7ce65 Mon Sep 17 00:00:00 2001 From: Sangho Lee Date: Thu, 21 May 2026 20:52:06 +0000 Subject: [PATCH 16/19] refactoring --- litebox_platform_lvbs/src/mshv/ringbuffer.rs | 27 ++++++++++++-------- litebox_platform_lvbs/src/mshv/vsm.rs | 8 ++++++ 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/litebox_platform_lvbs/src/mshv/ringbuffer.rs b/litebox_platform_lvbs/src/mshv/ringbuffer.rs index 415445955..150f5b1e0 100644 --- a/litebox_platform_lvbs/src/mshv/ringbuffer.rs +++ b/litebox_platform_lvbs/src/mshv/ringbuffer.rs @@ -15,24 +15,24 @@ pub struct RingBuffer { rb_pa: PhysAddr, write_offset: usize, size: usize, - // True iff `rb_pa` is page-aligned and `size` is a non-zero page multiple. - // The fast path collapses wraparound into a single map/unmap by passing the - // wrap span as a non-contiguous page list; pages themselves are derived from - // `rb_pa + idx * PAGE_SIZE` since the ring is physically contiguous. - page_aligned: bool, + // True iff `rb_pa` is page-aligned and `size` is a non-zero page multiple, + // i.e. wraparound can be collapsed into a single non-contiguous mapping. + // Pages themselves are derived from `rb_pa + idx * PAGE_SIZE` since the ring + // is physically contiguous. + fast_path_eligible: bool, } impl RingBuffer { pub fn new(phys_addr: PhysAddr, requested_size: usize) -> Self { let pa: usize = phys_addr.as_u64().trunc(); - let page_aligned = requested_size > 0 + let fast_path_eligible = requested_size > 0 && requested_size.is_multiple_of(PAGE_SIZE) && pa.is_multiple_of(PAGE_SIZE); RingBuffer { rb_pa: phys_addr, write_offset: 0, size: requested_size, - page_aligned, + fast_path_eligible, } } @@ -40,7 +40,7 @@ impl RingBuffer { if self.size == 0 || buf.is_empty() { return; } - self.write_offset = if self.page_aligned { + self.write_offset = if self.fast_path_eligible { write_fast(self.rb_pa, self.size, self.write_offset, buf) } else { write_slow(self.rb_pa, self.size, self.write_offset, buf) @@ -53,6 +53,7 @@ impl RingBuffer { /// the wrap span as `[rb_pa + (start_page + i) % page_count * PAGE_SIZE]`. /// Returns the new write offset (unchanged on failure). fn write_fast(rb_pa: PhysAddr, size: usize, write_offset: usize, buf: &[u8]) -> usize { + const MAX_SPAN_PAGES: usize = 16; // If `buf` would force the start page into the span twice, vmap rejects the // duplicate. Hand off to the two-write slow path before truncating `buf`. if buf.len() < size && write_offset % PAGE_SIZE + buf.len() > size { @@ -66,12 +67,16 @@ fn write_fast(rb_pa: PhysAddr, size: usize, write_offset: usize, buf: &[u8]) -> (buf, write_offset) }; - let rb_pa: usize = rb_pa.as_u64().trunc(); let page_count = size / PAGE_SIZE; let start_page = start / PAGE_SIZE; let in_page_offset = start % PAGE_SIZE; let span_pages = (in_page_offset + buf.len()).div_ceil(PAGE_SIZE); - let mut span = alloc::vec::Vec::with_capacity(span_pages); + if span_pages > MAX_SPAN_PAGES { + return write_slow(rb_pa, size, write_offset, buf); + } + let rb_pa: usize = rb_pa.as_u64().trunc(); + let mut span: arrayvec::ArrayVec, MAX_SPAN_PAGES> = + arrayvec::ArrayVec::new(); for i in 0..span_pages { let page_idx = (start_page + i) % page_count; let Some(addr) = page_idx @@ -128,7 +133,7 @@ fn write_slow(rb_pa: PhysAddr, size: usize, write_offset: usize, buf: &[u8]) -> // `space_remaining` bytes written; cursor wraps to 0. return 0; } - wraparound_slice.len() + (write_offset + buf.len()) % size } else if write_slice(rb_pa + write_offset as u64, buf) { (write_offset + buf.len()) % size } else { diff --git a/litebox_platform_lvbs/src/mshv/vsm.rs b/litebox_platform_lvbs/src/mshv/vsm.rs index c6bcc40de..0786710cf 100644 --- a/litebox_platform_lvbs/src/mshv/vsm.rs +++ b/litebox_platform_lvbs/src/mshv/vsm.rs @@ -896,6 +896,14 @@ fn apply_vtl0_text_patch(heki_patch: HekiPatch) -> Result<(), VsmError> { if heki_patch_pa_1.is_null() || (heki_patch_pa_0.align_up(Size4KiB::SIZE) == heki_patch_pa_1.align_down(Size4KiB::SIZE)) { + // Single contiguous span: either fits in one page (pa_1 null) or pa_1 is the + // adjacent next page. `HekiPatch::is_valid` enforces this; assert in debug builds. + debug_assert!( + !heki_patch_pa_1.is_null() + || heki_patch_pa_0.as_u64() + patch.len() as u64 + <= heki_patch_pa_0.align_down(Size4KiB::SIZE).as_u64() + Size4KiB::SIZE, + "patch crosses page boundary but pa_1 is null" + ); let mut ptr = Vtl0PhysMutPtr::::with_contiguous_pages( heki_patch_pa_0.as_u64().trunc(), patch.len(), From 5a18977bfe7b4b07323b2fc5aa11f802865ffe54 Mon Sep 17 00:00:00 2001 From: Sangho Lee Date: Fri, 29 May 2026 02:52:08 +0000 Subject: [PATCH 17/19] rebase --- litebox_platform_lvbs/src/mshv/vsm.rs | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/litebox_platform_lvbs/src/mshv/vsm.rs b/litebox_platform_lvbs/src/mshv/vsm.rs index 0786710cf..7888d3ab2 100644 --- a/litebox_platform_lvbs/src/mshv/vsm.rs +++ b/litebox_platform_lvbs/src/mshv/vsm.rs @@ -855,10 +855,8 @@ fn copy_heki_patch_from_vtl0(patch_pa_0: u64, patch_pa_1: u64) -> Result::new( - patch_pa_0.align_down(Size4KiB::SIZE).as_u64().trunc(), - ) - .ok_or(VsmError::Vtl0CopyFailed)?, + PhysPageAddr::::new(patch_pa_0.align_down(Size4KiB::SIZE).as_u64().trunc()) + .ok_or(VsmError::Vtl0CopyFailed)?, PhysPageAddr::::new(patch_pa_1.as_u64().trunc()) .ok_or(VsmError::Vtl0CopyFailed)?, ]; @@ -913,10 +911,7 @@ fn apply_vtl0_text_patch(heki_patch: HekiPatch) -> Result<(), VsmError> { } else { let pages = [ PhysPageAddr::::new( - heki_patch_pa_0 - .align_down(Size4KiB::SIZE) - .as_u64() - .trunc(), + heki_patch_pa_0.align_down(Size4KiB::SIZE).as_u64().trunc(), ) .ok_or(VsmError::Vtl0CopyFailed)?, PhysPageAddr::::new(heki_patch_pa_1.as_u64().trunc()) @@ -965,11 +960,9 @@ fn mshv_vsm_set_platform_root_key(key_pa: u64) -> Result { let key_pa = PhysAddr::try_new(key_pa).map_err(|_| VsmError::InvalidPhysicalAddress)?; let mut keybuf = Zeroizing::new([0u8; PRK_LEN]); - let mut key_ptr = Vtl0PhysConstPtr::::with_contiguous_pages( - key_pa.as_u64().trunc(), - PRK_LEN, - ) - .map_err(|_| VsmError::Vtl0CopyFailed)?; + let mut key_ptr = + Vtl0PhysConstPtr::::with_contiguous_pages(key_pa.as_u64().trunc(), PRK_LEN) + .map_err(|_| VsmError::Vtl0CopyFailed)?; unsafe { key_ptr.read_slice_at_offset(0, &mut *keybuf) } .map_err(|_| VsmError::Vtl0CopyFailed)?; set_platform_root_key(&*keybuf); From 0e0e307b5f2d2ad5c23e11acbd7def036dcbacb1 Mon Sep 17 00:00:00 2001 From: Sangho Lee Date: Thu, 18 Jun 2026 08:50:30 -0700 Subject: [PATCH 18/19] Refactor physical pointer API (#824) This PR refactors the physical pointer API. --------- Co-authored-by: Sangho Lee --- litebox_common_linux/src/physical_pointers.rs | 416 +++++++----------- litebox_common_linux/src/vmap.rs | 82 +++- litebox_common_optee/src/lib.rs | 2 +- litebox_platform_linux_userland/src/lib.rs | 4 +- litebox_platform_lvbs/src/lib.rs | 97 ++-- litebox_platform_lvbs/src/mshv/ringbuffer.rs | 6 +- litebox_platform_lvbs/src/mshv/vsm.rs | 48 +- litebox_runner_lvbs/src/lib.rs | 29 +- litebox_shim_optee/src/msg_handler.rs | 26 +- 9 files changed, 337 insertions(+), 373 deletions(-) diff --git a/litebox_common_linux/src/physical_pointers.rs b/litebox_common_linux/src/physical_pointers.rs index 20025dd37..c1afc25db 100644 --- a/litebox_common_linux/src/physical_pointers.rs +++ b/litebox_common_linux/src/physical_pointers.rs @@ -3,67 +3,39 @@ //! Physical Pointer Abstraction with On-demand Mapping //! -//! This module adds supports for accessing physical addresses (e.g., VTL0 or -//! normal-world physical memory) from LiteBox with on-demand mapping. -//! In the context of LVBS and OP-TEE, accessing physical memory is necessary -//! because VTL0 and VTL1 as well as normal world and secure world do not share -//! the same virtual address space, but they still have to share data through memory. -//! VTL1 and secure world receive physical addresses from VTL0 and normal world, -//! respectively, and they need to read from or write to those addresses. +//! This module adds supports for accessing physical addresses (e.g., VTL0 +//! or normal-world physical memory) from LiteBox with on-demand mapping. +//! In the context of LVBS and OP-TEE, accessing physical memory is +//! necessary because VTL0 and VTL1 as well as normal world and secure +//! world exchange data using physical addresses. //! -//! To simplify all these, we could persistently map the entire VTL0/normal-world -//! physical memory into VTL1/secure-world address space at once and just access them -//! through corresponding virtual addresses. However, this module does not take these -//! approaches due to scalability (e.g., how to deal with a system with terabytes of -//! physical memory?) and security concerns (e.g., data corruption or information -//! leakage due to concurrent or persistent access). +//! The safe read/write APIs in this module follow the same safety model as +//! safe wrappers around DMA buffers or shared physical memory. The +//! physical memory is external to Rust's ordinary ownership model and may +//! be changed by hardware or another privilege level. These APIs remain +//! safe because they do not create Rust references into that external +//! memory; they only perform bounded copies between a temporary mapping +//! and memory owned by LiteBox. //! -//! Instead, the approach this module takes is to map the required physical memory -//! region on-demand when accessing them while using a LiteBox-owned buffer to copy -//! data to/from those regions. This way, this module can ensure that data must be -//! copied into LiteBox-owned memory before being used while avoiding any unknown -//! side effects due to persistent memory mapping. -//! -//! Considerations: -//! -//! Ideally, this module should be able to validate whether a given physical address -//! is okay to access or even exists in the first place. For example, accessing -//! LiteBox's own memory with this physical pointer abstraction must be prohibited to -//! prevent the Boomerang attack and any other undefined memory access. Also, some -//! device memory is mapped to certain physical address ranges and LiteBox should not -//! touch them without in-depth knowledge. However, this is a bit tricky because, in -//! many cases, LiteBox does not directly interact with the underlying hardware or -//! BIOS/UEFI such that it does not have complete knowledge of the physical memory -//! layout. In the case of LVBS, LiteBox obtains the physical memory information -//! from VTL0 including the total physical memory size and the memory range assigned -//! to VTL1/LiteBox. Thus, this module can at least confirm a given physical address -//! does not belong to VTL1's physical memory. -//! -//! This module should allow byte-level access while transparently handling page -//! mapping and data access across page boundaries. This could become complicated -//! when we consider multiple page sizes (e.g., 4 KiB, 2 MiB, 1 GiB). Also, -//! unaligned access is a matter to be considered. -//! -//! In addition, often times, this physical pointer abstraction is involved with -//! a list of physical addresses (i.e., scatter-gather list). For example, in -//! the worse case, a two-byte value can span across two non-contiguous physical -//! pages (the last byte of the first page and the first byte of the second page). -//! Thus, to enhance the performance, we may need to consider mapping multiple pages -//! at once, copy data from/to them, and unmap them later. -//! -//! When this module needs to access data across physical page boundaries, it assumes -//! that those physical pages are virtually contiguous in VTL0 or normal-world address -//! space. Otherwise, this module could end up with accessing misordered data. This is -//! best-effort assumption and ensuring this is the caller's responsibility (e.g., even -//! if this module always requires a list of physical addresses, the caller might -//! provide a wrong list by mistake or intentionally). +//! The safe APIs should validate whether a given physical address is okay +//! to access. For example, accessing LiteBox's own memory through this +//! physical pointer abstraction is prohibited to avoid confused-deputy +//! attacks and to ensure Rust memory safety. In the case of LVBS, LiteBox +//! obtains the physical memory information from VTL0 like the total +//! physical memory range assigned to VTL1/LiteBox. Thus, this module can +//! confirm a given physical address does not belong to VTL1's physical +//! memory. use crate::vmap::{ GlobalVmapManager, PhysPageAddr, PhysPageMapInfo, PhysPageMapPermissions, PhysPointerError, VmapManager, }; use core::marker::PhantomData; -use zerocopy::FromBytes; +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. /// @@ -93,20 +65,25 @@ fn align_down(address: usize, align: usize) -> usize { } /// Represent a physical pointer to an object with on-demand mapping. +/// +/// Safe methods on this type copy to or from a temporary mapping. They never expose +/// references or slices into the mapped physical memory. +/// +/// Read methods require `T: FromBytes` because external memory may contain any bit pattern. +/// Write methods require `T: IntoBytes` because values are written by copying their byte +/// representation. +/// /// - `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. -/// - `map_info`: The mapping information of the currently mapped physical pages, if any. /// - `T`: The type of the object being pointed to. `pages` with respect to `offset` should cover enough /// memory for an object of type `T`. -#[derive(Clone)] #[repr(C)] pub struct PhysMutPtr> { pages: alloc::boxed::Box<[PhysPageAddr]>, offset: usize, count: usize, - map_info: Option>, _type: PhantomData, _vmap: PhantomData, } @@ -148,7 +125,6 @@ where pages: pages.into(), offset, count: size / core::mem::size_of::(), - map_info: None, _type: PhantomData, _vmap: PhantomData, }) @@ -200,53 +176,31 @@ where /// Read the value at the given offset from the physical pointer. /// - /// # Safety - /// - /// The caller should be aware that the given physical address might be concurrently written by - /// other entities (e.g., the normal world kernel) if there is no extra security mechanism - /// in place (e.g., by the hypervisor or hardware). That is, it might read corrupt data. - /// `FromBytes` is required to ensure T is valid for any bit pattern from untrusted physical memory. - pub unsafe fn read_at_offset( - &mut self, - count: usize, - ) -> Result, PhysPointerError> + /// Returns an owned copy of the value read from physical memory. + pub fn read_at_offset(&self, count: usize) -> Result, PhysPointerError> where T: FromBytes, { if count >= self.count { return Err(PhysPointerError::IndexOutOfBounds(count, self.count)); } - let guard = unsafe { - self.map_and_get_ptr_guard( - count, - core::mem::size_of::(), - PhysPageMapPermissions::READ, - )? - }; + let guard = self.map_and_get_ptr_guard( + count, + core::mem::size_of::(), + PhysPageMapPermissions::READ, + )?; let mut boxed = box_new_zeroed::(); - // Fallible: another core may unmap this page concurrently. - let result = unsafe { - litebox::mm::exception_table::memcpy_fallible( - core::ptr::from_mut::(boxed.as_mut()).cast::(), - guard.ptr.cast::(), - guard.size, - ) - }; - debug_assert!(result.is_ok(), "fault reading from mapped physical page"); - result.map_err(|_| PhysPointerError::CopyFailed)?; + // SAFETY: `boxed` is a freshly allocated `T` and is thus valid for writes + // of `size_of::()` bytes, which is the guard's mapped size. + unsafe { guard.copy_out(core::ptr::from_mut::(boxed.as_mut()).cast::())? }; Ok(boxed) } /// Read a slice of values at the given offset from the physical pointer. /// - /// # Safety - /// - /// The caller should be aware that the given physical address might be concurrently written by - /// other entities (e.g., the normal world kernel) if there is no extra security mechanism - /// in place (e.g., by the hypervisor or hardware). That is, it might read corrupt data. - /// `FromBytes` is required to ensure T is valid for any bit pattern from untrusted physical memory. - pub unsafe fn read_slice_at_offset( - &mut self, + /// Copies values from physical memory into the caller-provided slice. + pub fn read_slice_at_offset( + &self, count: usize, values: &mut [T], ) -> Result<(), PhysPointerError> @@ -262,73 +216,41 @@ where { return Err(PhysPointerError::IndexOutOfBounds(count, self.count)); } - let guard = unsafe { - self.map_and_get_ptr_guard( - count, - core::mem::size_of_val(values), - PhysPageMapPermissions::READ, - )? - }; - // Fallible: another core may unmap this page concurrently. - let result = unsafe { - litebox::mm::exception_table::memcpy_fallible( - values.as_mut_ptr().cast::(), - guard.ptr.cast::(), - guard.size, - ) - }; - debug_assert!(result.is_ok(), "fault reading from mapped physical page"); - result.map_err(|_| PhysPointerError::CopyFailed)?; + let guard = self.map_and_get_ptr_guard( + count, + core::mem::size_of_val(values), + PhysPageMapPermissions::READ, + )?; + // SAFETY: `values` is valid for writes of `size_of_val(values)` bytes, which is + // the guard's mapped size. + unsafe { guard.copy_out(values.as_mut_ptr().cast::())? }; Ok(()) } /// Write the value at the given offset to the physical pointer. - /// - /// # Safety - /// - /// The caller should be aware that the given physical address might be concurrently written by - /// other entities (e.g., the normal world kernel) if there is no extra security mechanism - /// in place (e.g., by the hypervisor or hardware). That is, data it writes might be overwritten. - pub unsafe fn write_at_offset( - &mut self, - count: usize, - value: T, - ) -> Result<(), PhysPointerError> { + pub fn write_at_offset(&self, count: usize, value: T) -> Result<(), PhysPointerError> + where + T: IntoBytes, + { if count >= self.count { return Err(PhysPointerError::IndexOutOfBounds(count, self.count)); } - let guard = unsafe { - self.map_and_get_ptr_guard( - count, - core::mem::size_of::(), - PhysPageMapPermissions::READ | PhysPageMapPermissions::WRITE, - )? - }; - // Fallible: another core may unmap this page concurrently. - let result = unsafe { - litebox::mm::exception_table::memcpy_fallible( - guard.ptr.cast::(), - core::ptr::from_ref(&value).cast::(), - guard.size, - ) - }; - debug_assert!(result.is_ok(), "fault writing to mapped physical page"); - result.map_err(|_| PhysPointerError::CopyFailed)?; + let guard = self.map_and_get_ptr_guard( + count, + core::mem::size_of::(), + PhysPageMapPermissions::READ | PhysPageMapPermissions::WRITE, + )?; + // SAFETY: `value` is valid for reads of `size_of::()` bytes, which is the + // guard's mapped size. + unsafe { guard.copy_in(core::ptr::from_ref(&value).cast::())? }; Ok(()) } /// Write a slice of values at the given offset to the physical pointer. - /// - /// # Safety - /// - /// The caller should be aware that the given physical address might be concurrently written by - /// other entities (e.g., the normal world kernel) if there is no extra security mechanism - /// in place (e.g., by the hypervisor or hardware). That is, data it writes might be overwritten. - pub unsafe fn write_slice_at_offset( - &mut self, - count: usize, - values: &[T], - ) -> Result<(), PhysPointerError> { + pub fn write_slice_at_offset(&self, count: usize, values: &[T]) -> Result<(), PhysPointerError> + where + T: IntoBytes, + { if values.is_empty() { return Ok(()); } @@ -338,23 +260,14 @@ where { return Err(PhysPointerError::IndexOutOfBounds(count, self.count)); } - let guard = unsafe { - self.map_and_get_ptr_guard( - count, - core::mem::size_of_val(values), - PhysPageMapPermissions::READ | PhysPageMapPermissions::WRITE, - )? - }; - // Fallible: another core may unmap this page concurrently. - let result = unsafe { - litebox::mm::exception_table::memcpy_fallible( - guard.ptr.cast::(), - values.as_ptr().cast::(), - guard.size, - ) - }; - debug_assert!(result.is_ok(), "fault writing to mapped physical page"); - result.map_err(|_| PhysPointerError::CopyFailed)?; + let guard = self.map_and_get_ptr_guard( + count, + core::mem::size_of_val(values), + PhysPageMapPermissions::READ | PhysPageMapPermissions::WRITE, + )?; + // SAFETY: `values` is valid for reads of `size_of_val(values)` bytes, which is + // the guard's mapped size. + unsafe { guard.copy_in(values.as_ptr().cast::())? }; Ok(()) } @@ -370,12 +283,10 @@ where /// - `size`: Total byte size to map (must cover the data being accessed). /// - `perms`: Required page permissions (read, write). /// - /// # Safety - /// - /// Same as [`Self::map_range`]. The returned guard borrows `self` mutably, ensuring - /// the mapping is released when the guard goes out of scope. - unsafe fn map_and_get_ptr_guard( - &mut self, + /// The returned guard is tied to `self`'s lifetime and releases the mapping when it + /// goes out of scope. + fn map_and_get_ptr_guard( + &self, count: usize, size: usize, perms: PhysPageMapPermissions, @@ -393,34 +304,23 @@ where .checked_add(size) .ok_or(PhysPointerError::Overflow)? .div_ceil(ALIGN); - unsafe { - self.map_range(start, end, perms)?; - } - let map_info = self - .map_info - .as_ref() - .ok_or(PhysPointerError::NoMappingInfo)?; - let ptr = map_info.base.wrapping_add(skip % ALIGN).cast::(); - let _ = map_info; + let map_info = self.map_range(start, end, perms)?; + let ptr = map_info.base().wrapping_add(skip % ALIGN).cast::(); Ok(MappedGuard { - owner: self, + map_info: Some(map_info), ptr, size, + _owner: PhantomData, }) } /// Map the physical pages from `start` to `end` indexes. - /// - /// # Safety - /// - /// This function assumes that the underlying platform safely handles concurrent mapping/unmapping - /// requests for the same physical pages. - unsafe fn map_range( - &mut self, + fn map_range( + &self, start: usize, end: usize, perms: PhysPageMapPermissions, - ) -> Result<(), PhysPointerError> { + ) -> Result, PhysPointerError> { if start >= end || end > self.pages.len() { return Err(PhysPointerError::IndexOutOfBounds(end, self.pages.len())); } @@ -428,72 +328,78 @@ where if perms.bits() & !accept_perms.bits() != 0 { return Err(PhysPointerError::UnsupportedPermissions(perms.bits())); } - if self.map_info.is_none() { - let sub_pages = &self.pages[start..end]; - unsafe { - self.map_info = Some(V::manager().vmap(sub_pages, perms)?); - } - Ok(()) - } else { - Err(PhysPointerError::AlreadyMapped( - self.pages.first().map_or(0, |p| p.as_usize()), - )) - } - } - - /// Unmap the physical pages if mapped. - /// - /// # Safety - /// - /// This function assumes that the underlying platform safely handles concurrent mapping/unmapping - /// requests for the same physical pages. - unsafe fn unmap(&mut self) -> Result<(), PhysPointerError> { - if let Some(map_info) = self.map_info.take() { - unsafe { - V::manager().vunmap(map_info)?; - } - Ok(()) - } else { - Err(PhysPointerError::Unmapped( - self.pages.first().map_or(0, |p| p.as_usize()), - )) - } + let sub_pages = &self.pages[start..end]; + // SAFETY: This caller never creates Rust references from the returned mapped pointer. + // The mapping is wrapped in `MapInfo`, then consumed by `MappedGuard`, which accesses it + // only through fault-tolerant raw copies. That avoids relying on Rust aliasing or validity + // guarantees for the external physical memory. + unsafe { V::manager().vmap(sub_pages, perms) } } } /// RAII guard that unmaps physical pages when dropped. /// -/// Created by `map_and_get_ptr_guard`. Holds a mutable borrow on the parent -/// `PhysMutPtr` and provides the mapped base pointer for the duration of the mapping. +/// 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. +/// +/// # Invariant +/// +/// `ptr` points into the live mapping owned by `map_info`, and the `size` bytes starting +/// 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: Clone, const ALIGN: usize, V: GlobalVmapManager> { - owner: &'a mut PhysMutPtr, + map_info: Option>, ptr: *mut T, size: usize, + _owner: PhantomData<&'a PhysMutPtr>, } -impl> Drop - for MappedGuard<'_, T, ALIGN, V> -{ - fn drop(&mut self) { - // SAFETY: The platform is expected to handle unmapping safely, including - // the case where pages were never mapped (returns Unmapped error, ignored). - let result = unsafe { self.owner.unmap() }; - debug_assert!( - result.is_ok() || matches!(result, Err(PhysPointerError::Unmapped(_))), - "unexpected error during unmap in drop: {result:?}", - ); +impl> MappedGuard<'_, T, ALIGN, V> { + /// Copy the `self.size` mapped bytes out into `dst`. + /// + /// This is the only path through which the raw mapped pointer is dereferenced. + /// + /// # Safety + /// + /// `dst` must be valid for writes of `self.size` bytes. + unsafe fn copy_out(&self, dst: *mut u8) -> Result<(), PhysPointerError> { + // Fallible: another core may unmap this page concurrently. + let result = unsafe { + litebox::mm::exception_table::memcpy_fallible(dst, self.ptr.cast::(), self.size) + }; + debug_assert!(result.is_ok(), "fault reading from mapped physical page"); + result.map_err(|_| PhysPointerError::CopyFailed) + } + + /// Copy `self.size` bytes from `src` into the mapped memory. + /// + /// This is the only path through which the raw mapped pointer is dereferenced. + /// + /// # Safety + /// + /// `src` must be valid for reads of `self.size` bytes. + unsafe fn copy_in(&self, src: *const u8) -> Result<(), PhysPointerError> { + // Fallible: another core may unmap this page concurrently. + let result = unsafe { + litebox::mm::exception_table::memcpy_fallible(self.ptr.cast::(), src, self.size) + }; + debug_assert!(result.is_ok(), "fault writing to mapped physical page"); + result.map_err(|_| PhysPointerError::CopyFailed) } } -impl> Drop for PhysMutPtr { +impl> Drop + for MappedGuard<'_, T, ALIGN, V> +{ fn drop(&mut self) { - // SAFETY: The platform is expected to handle unmapping safely, including - // the case where pages were never mapped (returns Unmapped error, ignored). - let result = unsafe { self.unmap() }; - debug_assert!( - result.is_ok() || matches!(result, Err(PhysPointerError::Unmapped(_))), - "unexpected error during unmap in drop: {result:?}", - ); + // 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) }; + } } } @@ -509,8 +415,7 @@ impl> core::fmt::Debug } /// Represent a physical pointer to a read-only object. This wraps around [`PhysMutPtr`] and -/// exposes only read access. -#[derive(Clone)] +/// exposes only copy-out access. #[repr(C)] pub struct PhysConstPtr> { inner: PhysMutPtr, @@ -557,37 +462,26 @@ where /// Read the value at the given offset from the physical pointer. /// - /// # Safety - /// - /// The caller should be aware that the given physical address might be concurrently written by - /// other entities (e.g., the normal world kernel) if there is no extra security mechanism - /// in place (e.g., by the hypervisor or hardware). That is, it might read corrupt data. - pub unsafe fn read_at_offset( - &mut self, - count: usize, - ) -> Result, PhysPointerError> + /// Returns an owned copy of the value read from physical memory. + pub fn read_at_offset(&self, count: usize) -> Result, PhysPointerError> where T: FromBytes, { - unsafe { self.inner.read_at_offset(count) } + self.inner.read_at_offset(count) } /// Read a slice of values at the given offset from the physical pointer. /// - /// # Safety - /// - /// The caller should be aware that the given physical address might be concurrently written by - /// other entities (e.g., the normal world kernel) if there is no extra security mechanism - /// in place (e.g., by the hypervisor or hardware). That is, it might read corrupt data. - pub unsafe fn read_slice_at_offset( - &mut self, + /// Copies values from physical memory into the caller-provided slice. + pub fn read_slice_at_offset( + &self, count: usize, values: &mut [T], ) -> Result<(), PhysPointerError> where T: FromBytes, { - unsafe { self.inner.read_slice_at_offset(count, values) } + self.inner.read_slice_at_offset(count, values) } } diff --git a/litebox_common_linux/src/vmap.rs b/litebox_common_linux/src/vmap.rs index 102f0c9e3..afaa41897 100644 --- a/litebox_common_linux/src/vmap.rs +++ b/litebox_common_linux/src/vmap.rs @@ -11,38 +11,56 @@ use thiserror::Error; /// 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). -pub trait VmapManager { +/// +/// # Safety +/// +/// Implementors must uphold each unsafe method's contract and keep [`Self::MapInfo`] tied to the +/// mapping it identifies. +pub unsafe trait VmapManager { + /// Implementors use this to carry the virtual mapping and any platform-specific bookkeeping + /// needed for unmapping. + type MapInfo: PhysPageMapInfo; + /// Map the given `PhysPageAddrArray` into virtually contiguous addresses with the given - /// [`PhysPageMapPermissions`] while returning [`PhysPageMapInfo`]. + /// [`PhysPageMapPermissions`] while returning [`Self::MapInfo`]. /// /// This function is analogous to Linux kernel's `vmap()`. /// /// # Safety /// - /// The caller should ensure that `pages` are not in active use by other entities - /// (especially, there should be no read/write or write/write conflicts). - /// Unfortunately, LiteBox itself cannot fully guarantee this and it needs some helps - /// from the caller, hypervisor, or hardware. - /// Multiple LiteBox threads might concurrently call this function with overlapping - /// physical pages, so the implementation should safely handle such cases. + /// The returned pointer is a raw address; creating or holding it does not access memory or + /// create a Rust reference. Any later use of that pointer must satisfy the platform's access + /// requirements for the mapped physical pages. Even when access is logically exclusive, callers + /// must treat the mapped memory like DMA/shared physical memory rather than ordinary Rust-owned + /// RAM. unsafe fn vmap( &self, _pages: &PhysPageAddrArray, _perms: PhysPageMapPermissions, - ) -> Result, PhysPointerError> { + ) -> Result { Err(PhysPointerError::UnsupportedOperation) } - /// Unmap the previously mapped virtually contiguous addresses ([`PhysPageMapInfo`]). + /// Unmap the previously mapped virtually contiguous addresses ([`Self::MapInfo`]). /// /// This function is analogous to Linux kernel's `vunmap()`. /// + /// On failure, the unchanged `vmap_info` is returned alongside the error so the caller can + /// retry or otherwise preserve the mapping state. Dropping returned map info is not guaranteed + /// to release platform resources; each implementation owns the retention policy for resources + /// that cannot be safely reclaimed after a failed unmap. + /// /// # Safety /// - /// The caller should ensure that the virtual addresses in `vmap_info` are not in active - /// use by other entities. - unsafe fn vunmap(&self, _vmap_info: PhysPageMapInfo) -> Result<(), PhysPointerError> { - Err(PhysPointerError::UnsupportedOperation) + /// The caller must ensure there are no outstanding raw-pointer uses or Rust references derived + /// from `PhysPageMapInfo::base()`. After a successful call, the virtual mapping is invalid and + /// any platform resources tied to the mapping lifetime have been released or otherwise handled + /// by the implementation. + unsafe fn vunmap( + &self, + vmap_info: Self::MapInfo, + ) -> Result<(), (PhysPointerError, Self::MapInfo)> { + Err((PhysPointerError::UnsupportedOperation, vmap_info)) } /// Validate that the given physical pages are not owned by LiteBox. @@ -106,13 +124,39 @@ pub type PhysPageAddr = litebox::mm::linux::NonZeroAddress = [PhysPageAddr]; -/// Data structure to maintain the mapping information returned by `vmap()`. -#[derive(Clone)] -pub struct PhysPageMapInfo { +/// Mapping information returned by `vmap()`. +/// +/// Implementors use this value to track the virtual mapping and any platform-specific resources +/// tied to it. Callers must pass it back to the same platform's `vunmap()` to explicitly unmap; +/// drop behavior is implementation-specific. +pub trait PhysPageMapInfo { /// Virtual address of the mapped region which is page aligned. - pub base: *mut u8, + fn base(&self) -> *mut u8; /// The size of the mapped region in bytes. - pub size: usize, + fn size(&self) -> usize; +} + +/// A no-op [`PhysPageMapInfo`] for platforms that do not support `vmap()`/`vunmap()`. +#[derive(Debug)] +pub struct NoopPhysPageMapInfo { + base: *mut u8, + size: usize, +} + +impl NoopPhysPageMapInfo { + pub fn new(base: *mut u8, size: usize) -> Self { + Self { base, size } + } +} + +impl PhysPageMapInfo for NoopPhysPageMapInfo { + fn base(&self) -> *mut u8 { + self.base + } + + fn size(&self) -> usize { + self.size + } } bitflags::bitflags! { diff --git a/litebox_common_optee/src/lib.rs b/litebox_common_optee/src/lib.rs index bcff985e1..8523c6061 100644 --- a/litebox_common_optee/src/lib.rs +++ b/litebox_common_optee/src/lib.rs @@ -2058,7 +2058,7 @@ impl From<&OpteeSmcArgsPage> for OpteeSmcArgs { } /// OP-TEE SMC call arguments. -#[derive(Clone, Copy, Default, FromBytes)] +#[derive(Clone, Copy, Default, FromBytes, IntoBytes, Immutable)] pub struct OpteeSmcArgs { args: [usize; Self::NUM_OPTEE_SMC_ARGS], } diff --git a/litebox_platform_linux_userland/src/lib.rs b/litebox_platform_linux_userland/src/lib.rs index dfd5f6cbc..1ffd00e42 100644 --- a/litebox_platform_linux_userland/src/lib.rs +++ b/litebox_platform_linux_userland/src/lib.rs @@ -2373,7 +2373,9 @@ impl litebox::platform::DerivedKeyProvider for LinuxUserland { /// In general, userland platforms do not support `vmap` and `vunmap` (which are kernel functions). /// We might need to emulate these functions' behaviors using virtual addresses for development or /// testing, or use a kernel module to provide this functionality (if needed). -impl VmapManager for LinuxUserland {} +unsafe impl VmapManager for LinuxUserland { + type MapInfo = litebox_common_linux::vmap::NoopPhysPageMapInfo; +} /// Dummy `VmemPageFaultHandler`. /// diff --git a/litebox_platform_lvbs/src/lib.rs b/litebox_platform_lvbs/src/lib.rs index 0b8dcd789..693d12115 100644 --- a/litebox_platform_lvbs/src/lib.rs +++ b/litebox_platform_lvbs/src/lib.rs @@ -51,6 +51,28 @@ pub mod syscall_entry; static CPU_MHZ: AtomicU64 = AtomicU64::new(0); +/// Mapping info returned by [`LinuxKernel`]'s [`VmapManager::vmap`]. +pub struct LvbsPhysPageMapInfo { + base: *mut u8, + size: usize, +} + +impl LvbsPhysPageMapInfo { + fn new(base: *mut u8, size: usize) -> Self { + Self { base, size } + } +} + +impl PhysPageMapInfo for LvbsPhysPageMapInfo { + fn base(&self) -> *mut u8 { + self.base + } + + fn size(&self) -> usize { + self.size + } +} + /// Special page table ID for the base (kernel-only) page table. /// No real physical frame has address 0, so this is a safe sentinel. pub const BASE_PAGE_TABLE_ID: usize = 0; @@ -617,11 +639,10 @@ impl LinuxKernel { /// /// Allocator does not allocate memory frames for VTL0 pages, so frame deallocation is not needed. /// - /// Note: VTL0 physical memory is external memory not owned by LiteBox (similar to MMIO). - /// LiteBox accesses it by creating a temporary non-shared mapping, copying data to/from a - /// LiteBox-owned buffer, and unmapping immediately. No Rust references are created to the - /// mapped VTL0 memory; all accesses use raw pointer operations (read_volatile / - /// copy_nonoverlapping) to avoid violating Rust's aliasing model. + /// Note: VTL0 physical memory is external memory not owned by LiteBox, similar to DMA/shared + /// physical memory. Physical pointer APIs access it by creating a temporary mapping, copying + /// data to/from a LiteBox-owned buffer with fallible raw-pointer copies, and unmapping + /// immediately. These APIs do not create Rust references to the mapped VTL0 memory. fn unmap_vtl0_pages( &self, page_addr: *const u8, @@ -1118,12 +1139,14 @@ fn is_contiguous(addrs: &[PhysPageAddr]) -> bool { true } -impl VmapManager for LinuxKernel { +unsafe impl VmapManager for LinuxKernel { + type MapInfo = LvbsPhysPageMapInfo; + unsafe fn vmap( &self, pages: &PhysPageAddrArray, perms: PhysPageMapPermissions, - ) -> Result, PhysPointerError> { + ) -> Result { if pages.is_empty() { return Err(PhysPointerError::InvalidPhysicalAddress(0)); } @@ -1132,6 +1155,17 @@ impl VmapManager for LinuxKernel unimplemented!("ALIGN other than 4KiB is not supported yet"); } + // Reject duplicates early as an API-level validation. The page-table implementation also + // rejects duplicate/shared mappings, but this keeps the error local to the input array. + if !is_contiguous(pages) { + let mut seen = hashbrown::HashSet::with_capacity(pages.len()); + for page in pages { + if !seen.insert(page.as_usize()) { + return Err(PhysPointerError::DuplicatePhysicalAddress(page.as_usize())); + } + } + } + // VTL0 memory must never be executable from VTL1 (DEP). let mut flags = PageTableFlags::PRESENT | PageTableFlags::NO_EXECUTE; if perms.contains(PhysPageMapPermissions::WRITE) { @@ -1160,10 +1194,7 @@ impl VmapManager for LinuxKernel .current_page_table() .map_phys_frame_range_direct(frame_range, flags, None) { - Ok(page_addr) => Ok(PhysPageMapInfo { - base: page_addr, - size: pages.len() * ALIGN, - }), + Ok(page_addr) => Ok(LvbsPhysPageMapInfo::new(page_addr, pages.len() * ALIGN)), Err(MapToError::PageAlreadyMapped(_)) => { Err(PhysPointerError::AlreadyMapped(pages[0].as_usize())) } @@ -1175,16 +1206,6 @@ impl VmapManager for LinuxKernel ), } } else { - // Reject duplicate page addresses - { - let mut seen = hashbrown::HashSet::with_capacity(pages.len()); - for page in pages { - if !seen.insert(page.as_usize()) { - return Err(PhysPointerError::DuplicatePhysicalAddress(page.as_usize())); - } - } - } - let frames: alloc::vec::Vec> = pages .iter() .map(|p| PhysFrame::containing_address(x86_64::PhysAddr::new(p.as_usize() as u64))) @@ -1209,10 +1230,7 @@ impl VmapManager for LinuxKernel .current_page_table() .map_non_contiguous_phys_frames(&frames, base_va, flags) { - Ok(page_addr) => Ok(PhysPageMapInfo { - base: page_addr, - size: pages.len() * ALIGN, - }), + Ok(page_addr) => Ok(LvbsPhysPageMapInfo::new(page_addr, pages.len() * ALIGN)), Err(e) => { let _ = vmap_allocator().unregister_allocation(base_va); match e { @@ -1231,24 +1249,37 @@ impl VmapManager for LinuxKernel } } - unsafe fn vunmap(&self, vmap_info: PhysPageMapInfo) -> Result<(), PhysPointerError> { + unsafe fn vunmap( + &self, + vmap_info: Self::MapInfo, + ) -> Result<(), (PhysPointerError, Self::MapInfo)> { if ALIGN != PAGE_SIZE { unimplemented!("ALIGN other than 4KiB is not supported yet"); } - let base_va = x86_64::VirtAddr::new(vmap_info.base as u64); + let base = vmap_info.base(); + let size = vmap_info.size(); + let base_va = x86_64::VirtAddr::new(base as u64); // Unmap the page table entries first. Only release the VA range back // to the allocator when unmapping succeeds; if it fails, stale PTE // entries remain and recycling the VA would cause collisions. - self.unmap_vtl0_pages(vmap_info.base, vmap_info.size) - .map_err(|_| PhysPointerError::Unmapped(vmap_info.base as usize))?; + if self.unmap_vtl0_pages(base, size).is_err() { + return Err((PhysPointerError::Unmapped(base as usize), vmap_info)); + } - if crate::mm::vmap::is_vmap_address(base_va) { - crate::mm::vmap::vmap_allocator() + // PTEs are already cleared at this point, so the mapping is functionally gone + // and a retry would only re-fail against empty page-table entries. If the VA + // allocator's bookkeeping is inconsistent, surface it via `debug_assert!`. The + // VA region is leaked but cannot be safely recycled. + let unregister_ok = !crate::mm::vmap::is_vmap_address(base_va) + || crate::mm::vmap::vmap_allocator() .unregister_allocation(base_va) - .ok_or(PhysPointerError::Unmapped(vmap_info.base as usize))?; - } + .is_some(); + debug_assert!( + unregister_ok, + "vmap allocator unregister failed at {base_va:?}", + ); Ok(()) } diff --git a/litebox_platform_lvbs/src/mshv/ringbuffer.rs b/litebox_platform_lvbs/src/mshv/ringbuffer.rs index 150f5b1e0..751bd19aa 100644 --- a/litebox_platform_lvbs/src/mshv/ringbuffer.rs +++ b/litebox_platform_lvbs/src/mshv/ringbuffer.rs @@ -89,10 +89,10 @@ fn write_fast(rb_pa: PhysAddr, size: usize, write_offset: usize, buf: &[u8]) -> span.push(addr); } - let Ok(mut ptr) = Vtl0PhysMutPtr::::new(&span, in_page_offset) else { + let Ok(ptr) = Vtl0PhysMutPtr::::new(&span, in_page_offset) else { return write_offset; }; - if unsafe { ptr.write_slice_at_offset(0, buf) }.is_ok() { + if ptr.write_slice_at_offset(0, buf).is_ok() { (start + buf.len()) % size } else { write_offset @@ -109,7 +109,7 @@ fn write_fast(rb_pa: PhysAddr, size: usize, write_offset: usize, buf: &[u8]) -> fn write_slow(rb_pa: PhysAddr, size: usize, write_offset: usize, buf: &[u8]) -> usize { let write_slice = |pa: PhysAddr, slice: &[u8]| -> bool { Vtl0PhysMutPtr::::with_contiguous_pages(pa.as_u64().trunc(), slice.len()) - .and_then(|mut ptr| unsafe { ptr.write_slice_at_offset(0, slice) }) + .and_then(|ptr| ptr.write_slice_at_offset(0, slice)) .is_ok() }; diff --git a/litebox_platform_lvbs/src/mshv/vsm.rs b/litebox_platform_lvbs/src/mshv/vsm.rs index 7888d3ab2..29cc0442f 100644 --- a/litebox_platform_lvbs/src/mshv/vsm.rs +++ b/litebox_platform_lvbs/src/mshv/vsm.rs @@ -124,12 +124,13 @@ pub fn mshv_vsm_boot_aps(cpu_online_mask_pfn: u64) -> Result { .and_then(|pa| PhysAddr::try_new(pa).ok()) .ok_or(VsmError::InvalidPhysicalAddress)?; - let mut cpu_mask_ptr = Vtl0PhysConstPtr::::with_usize( + let cpu_mask_ptr = Vtl0PhysConstPtr::::with_usize( cpu_online_mask_page_addr.as_u64().trunc(), ) .map_err(|_| VsmError::CpuOnlineMaskCopyFailed)?; - let cpu_mask = - unsafe { cpu_mask_ptr.read_at_offset(0) }.map_err(|_| VsmError::CpuOnlineMaskCopyFailed)?; + let cpu_mask = cpu_mask_ptr + .read_at_offset(0) + .map_err(|_| VsmError::CpuOnlineMaskCopyFailed)?; #[cfg(debug_assertions)] { @@ -845,10 +846,9 @@ fn copy_heki_patch_from_vtl0(patch_pa_0: u64, patch_pa_1: u64) -> Result::with_usize(patch_pa_0.as_u64().trunc()) - .map_err(|_| VsmError::Vtl0CopyFailed)?; - unsafe { ptr.read_at_offset(0) } + let ptr = Vtl0PhysConstPtr::::with_usize(patch_pa_0.as_u64().trunc()) + .map_err(|_| VsmError::Vtl0CopyFailed)?; + ptr.read_at_offset(0) .map(|boxed| *boxed) .map_err(|_| VsmError::Vtl0CopyFailed) } else { @@ -860,15 +860,13 @@ fn copy_heki_patch_from_vtl0(patch_pa_0: u64, patch_pa_1: u64) -> Result::new(patch_pa_1.as_u64().trunc()) .ok_or(VsmError::Vtl0CopyFailed)?, ]; - let mut ptr = Vtl0PhysConstPtr::::new( + let ptr = Vtl0PhysConstPtr::::new( &pages, (patch_pa_0 - patch_pa_0.align_down(Size4KiB::SIZE)).trunc(), ) .map_err(|_| VsmError::Vtl0CopyFailed)?; - unsafe { - ptr.read_slice_at_offset(0, heki_patch_bytes) - .map_err(|_| VsmError::Vtl0CopyFailed)?; - } + ptr.read_slice_at_offset(0, heki_patch_bytes) + .map_err(|_| VsmError::Vtl0CopyFailed)?; Ok(heki_patch) }?; @@ -902,12 +900,13 @@ fn apply_vtl0_text_patch(heki_patch: HekiPatch) -> Result<(), VsmError> { <= heki_patch_pa_0.align_down(Size4KiB::SIZE).as_u64() + Size4KiB::SIZE, "patch crosses page boundary but pa_1 is null" ); - let mut ptr = Vtl0PhysMutPtr::::with_contiguous_pages( + let ptr = Vtl0PhysMutPtr::::with_contiguous_pages( heki_patch_pa_0.as_u64().trunc(), patch.len(), ) .map_err(|_| VsmError::Vtl0CopyFailed)?; - unsafe { ptr.write_slice_at_offset(0, patch) }.map_err(|_| VsmError::Vtl0CopyFailed)?; + ptr.write_slice_at_offset(0, patch) + .map_err(|_| VsmError::Vtl0CopyFailed)?; } else { let pages = [ PhysPageAddr::::new( @@ -917,12 +916,13 @@ fn apply_vtl0_text_patch(heki_patch: HekiPatch) -> Result<(), VsmError> { PhysPageAddr::::new(heki_patch_pa_1.as_u64().trunc()) .ok_or(VsmError::Vtl0CopyFailed)?, ]; - let mut ptr = Vtl0PhysMutPtr::::new( + let ptr = Vtl0PhysMutPtr::::new( &pages, (heki_patch_pa_0 - heki_patch_pa_0.align_down(Size4KiB::SIZE)).trunc(), ) .map_err(|_| VsmError::Vtl0CopyFailed)?; - unsafe { ptr.write_slice_at_offset(0, patch) }.map_err(|_| VsmError::Vtl0CopyFailed)?; + ptr.write_slice_at_offset(0, patch) + .map_err(|_| VsmError::Vtl0CopyFailed)?; } Ok(()) } @@ -960,10 +960,11 @@ fn mshv_vsm_set_platform_root_key(key_pa: u64) -> Result { let key_pa = PhysAddr::try_new(key_pa).map_err(|_| VsmError::InvalidPhysicalAddress)?; let mut keybuf = Zeroizing::new([0u8; PRK_LEN]); - let mut key_ptr = + let key_ptr = Vtl0PhysConstPtr::::with_contiguous_pages(key_pa.as_u64().trunc(), PRK_LEN) .map_err(|_| VsmError::Vtl0CopyFailed)?; - unsafe { key_ptr.read_slice_at_offset(0, &mut *keybuf) } + key_ptr + .read_slice_at_offset(0, &mut *keybuf) .map_err(|_| VsmError::Vtl0CopyFailed)?; set_platform_root_key(&*keybuf); Ok(0) @@ -1389,9 +1390,9 @@ fn copy_heki_pages_from_vtl0(pa: u64, nranges: u64) -> Option> { if visited_pages.contains(&cur_pa.as_u64()) { return None; } - let mut ptr = + let ptr = Vtl0PhysConstPtr::::with_usize(cur_pa.as_u64().trunc()).ok()?; - let heki_page = unsafe { ptr.read_at_offset(0) }.ok()?; + let heki_page = ptr.read_at_offset(0).ok()?; if !heki_page.is_valid() { return None; } @@ -1667,7 +1668,7 @@ impl MemoryContainer { return Ok(()); } - let mut ptr = Vtl0PhysConstPtr::::with_contiguous_pages( + let ptr = Vtl0PhysConstPtr::::with_contiguous_pages( phys_start.as_u64().trunc(), bytes_to_copy, ) @@ -1675,7 +1676,10 @@ impl MemoryContainer { let old_len = self.buf.len(); self.buf.resize(old_len + bytes_to_copy, 0); - if unsafe { ptr.read_slice_at_offset(0, &mut self.buf[old_len..]) }.is_err() { + if ptr + .read_slice_at_offset(0, &mut self.buf[old_len..]) + .is_err() + { self.buf.truncate(old_len); return Err(MemoryContainerError::CopyFromVtl0Failed); } diff --git a/litebox_runner_lvbs/src/lib.rs b/litebox_runner_lvbs/src/lib.rs index 0ae319e59..43773ab1e 100644 --- a/litebox_runner_lvbs/src/lib.rs +++ b/litebox_runner_lvbs/src/lib.rs @@ -280,10 +280,10 @@ fn optee_smc_handler_entry_inner( // 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 mut smc_args_ptr = NormalWorldMutPtr::::with_usize(smc_args_addr) + let smc_args_ptr = NormalWorldMutPtr::::with_usize(smc_args_addr) .map_err(|_| litebox_common_linux::errno::Errno::EINVAL)?; - // SAFETY: The SMC args are written back to normal world memory. - unsafe { smc_args_ptr.write_at_offset(0, smc_args_updated) } + smc_args_ptr + .write_at_offset(0, smc_args_updated) .map_err(|_| litebox_common_linux::errno::Errno::EFAULT)?; Ok(0) } @@ -422,13 +422,12 @@ fn optee_smc_handler(smc_args_addr: usize) -> OpteeSmcArgs { args }; - let Ok(mut smc_args_ptr) = + let Ok(smc_args_ptr) = NormalWorldConstPtr::::with_usize(smc_args_addr) else { return make_error_response(OpteeSmcReturnCode::EBadAddr); }; - // SAFETY: The SMC args are read from normal world memory into an owned copy. - let Ok(mut smc_args) = (unsafe { smc_args_ptr.read_at_offset(0) }) else { + 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 { @@ -1297,13 +1296,11 @@ fn write_msg_args_to_normal_world( let mut blob = vec![0u8; msg_args_size]; msg_args.serialize(&mut blob)?; - let mut ptr = NormalWorldMutPtr::::with_contiguous_pages( + let ptr = NormalWorldMutPtr::::with_contiguous_pages( msg_args_phys_addr.trunc(), msg_args_size, )?; - // SAFETY: Writing msg_args back to normal world memory at a valid physical address. - // The blob contains the serialized variable-length optee_msg_arg structure(s). - unsafe { ptr.write_slice_at_offset(0, &blob) }?; + ptr.write_slice_at_offset(0, &blob)?; Ok(()) } @@ -1323,13 +1320,11 @@ fn write_non_ta_msg_args_to_normal_world( let mut blob = vec![0u8; msg_args_size]; msg_args.serialize(&mut blob)?; - let mut ptr = NormalWorldMutPtr::::with_contiguous_pages( + let ptr = NormalWorldMutPtr::::with_contiguous_pages( msg_args_phys_addr.trunc(), msg_args_size, )?; - // SAFETY: Writing msg_args back to normal world memory at a valid physical address. - // The blob contains the serialized variable-length optee_msg_arg structure(s). - unsafe { ptr.write_slice_at_offset(0, &blob) }?; + ptr.write_slice_at_offset(0, &blob)?; Ok(()) } @@ -1355,10 +1350,8 @@ 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 mut ptr = NormalWorldMutPtr::::with_contiguous_pages(rpc_pa, rpc_args_size)?; - // SAFETY: Writing rpc_args back to normal world memory at a valid physical address. - // The blob contains the serialized variable-length optee_msg_arg structure(s). - unsafe { ptr.write_slice_at_offset(0, &blob) }?; + let ptr = NormalWorldMutPtr::::with_contiguous_pages(rpc_pa, rpc_args_size)?; + ptr.write_slice_at_offset(0, &blob)?; Ok(()) } diff --git a/litebox_shim_optee/src/msg_handler.rs b/litebox_shim_optee/src/msg_handler.rs index 9187d5b7f..826839b04 100644 --- a/litebox_shim_optee/src/msg_handler.rs +++ b/litebox_shim_optee/src/msg_handler.rs @@ -181,10 +181,11 @@ pub fn read_optee_msg_args_from_phys( let mut blob = alloc::vec![0u8; copy_size]; - let mut blob_ptr = + let blob_ptr = NormalWorldConstPtr::::with_contiguous_pages(phys_addr, copy_size) .map_err(|_| OpteeSmcReturnCode::EBadAddr)?; - unsafe { blob_ptr.read_slice_at_offset(0, &mut blob) } + blob_ptr + .read_slice_at_offset(0, &mut blob) .map_err(|_| OpteeSmcReturnCode::EBadAddr)?; parse_optee_msg_args(&blob, has_rpc_arg) @@ -695,11 +696,8 @@ impl ShmInfo { { return Err(OpteeSmcReturnCode::EBadAddr); } - let mut ptr = NormalWorldConstPtr::::new(&self.page_addrs, self.page_offset)?; - // SAFETY: bounds validated above; copy lands in a buffer owned by LiteBox to avoid TOCTOU issues. - unsafe { - ptr.read_slice_at_offset(offset, buffer)?; - } + let ptr = NormalWorldConstPtr::::new(&self.page_addrs, self.page_offset)?; + ptr.read_slice_at_offset(offset, buffer)?; Ok(()) } @@ -710,11 +708,8 @@ impl ShmInfo { if buffer.len() > self.len { return Err(OpteeSmcReturnCode::EBadAddr); } - let mut ptr = NormalWorldMutPtr::::new(&self.page_addrs, self.page_offset)?; - // SAFETY: bounds validated above; data comes from a buffer owned by LiteBox. - unsafe { - ptr.write_slice_at_offset(0, buffer)?; - } + let ptr = NormalWorldMutPtr::::new(&self.page_addrs, self.page_offset)?; + ptr.write_slice_at_offset(0, buffer)?; Ok(()) } } @@ -791,10 +786,11 @@ impl ShmRefMap { return Err(OpteeSmcReturnCode::EBadAddr); } visited_pages_data.insert(cur_addr); - let mut cur_ptr = NormalWorldConstPtr::::with_usize(cur_addr) + let cur_ptr = NormalWorldConstPtr::::with_usize(cur_addr) + .map_err(|_| OpteeSmcReturnCode::EBadAddr)?; + let pages_data = cur_ptr + .read_at_offset(0) .map_err(|_| OpteeSmcReturnCode::EBadAddr)?; - let pages_data = - unsafe { cur_ptr.read_at_offset(0) }.map_err(|_| OpteeSmcReturnCode::EBadAddr)?; let pages_len_before = pages.len(); for page in &pages_data.pages_list { if *page == 0 || pages.len() == num_pages { From 146da5e2164e92e92ea664857d983c227596ce55 Mon Sep 17 00:00:00 2001 From: Sangho Lee Date: Thu, 18 Jun 2026 16:52:50 +0000 Subject: [PATCH 19/19] fix rebase issue --- litebox_platform_lvbs/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/litebox_platform_lvbs/src/lib.rs b/litebox_platform_lvbs/src/lib.rs index 693d12115..fecee32ee 100644 --- a/litebox_platform_lvbs/src/lib.rs +++ b/litebox_platform_lvbs/src/lib.rs @@ -24,11 +24,11 @@ use litebox::{ shim::ContinueOperation, utils::TruncateExt, }; +use litebox_common_linux::errno::Errno; use litebox_common_linux::vmap::{ GlobalVmapManager, PhysPageAddr, PhysPageAddrArray, PhysPageMapInfo, PhysPageMapPermissions, PhysPointerError, VmapManager, }; -use litebox_common_linux::{PunchthroughSyscall, errno::Errno}; use x86_64::{ VirtAddr, structures::paging::{