mod generated {
#![allow(non_camel_case_types, non_upper_case_globals, dead_code)]
include!("generated.rs");
}
pub use generated::*;
mod addr;
mod collections;
mod sandbox;
mod scan;
mod utils;
mod vmcall;
pub use crate::{
as_mut, as_ref, impl_sandbox_safe, newtype_ops, slice, slice_mut, tag_enum, vmcall_enum,
write_field,
};
pub use addr::*;
pub use collections::*;
pub use sandbox::SandboxSafe;
pub use scan::*;
pub use utils::*;
pub use vmcall::*;
use std::{
cmp::Ordering,
ffi::CStr,
fmt,
hash::{Hash, Hasher},
ops::{Add, Deref, DerefMut, Range, Sub},
ptr::copy_nonoverlapping,
};
#[cfg(feature = "component")]
use std::{
simd::Simd,
slice::{from_raw_parts, from_raw_parts_mut},
};
#[cfg(feature = "component")]
use gxhash::GxHasher;
#[derive(Debug, Clone, Copy)]
pub enum MemAccessError {
UnmappedVirt(VirtAddr),
UnmappedPhys(PhysAddr),
CrossesPageBoundary { addr: VirtAddr, len: usize },
InvalidRef(VirtAddr),
}
impl fmt::Display for MemAccessError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::UnmappedVirt(va) => write!(f, "unmapped guest VA {va:?}"),
Self::UnmappedPhys(pa) => write!(f, "unmapped guest PA {pa:?}"),
Self::CrossesPageBoundary { addr, len } => {
write!(f, "range of {len} bytes at {addr:?} crosses a page boundary")
},
Self::InvalidRef(va) => write!(f, "invalid typed reference at {va:?}"),
}
}
}
impl std::error::Error for MemAccessError {}
#[derive(Debug, Clone, Copy)]
pub enum BpInsertError {
DuplicateOwner(PhysAddr),
}
impl fmt::Display for BpInsertError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::DuplicateOwner(pa) => {
write!(f, "breakpoint at {pa:?} already owned by this component")
},
}
}
}
impl std::error::Error for BpInsertError {}
#[cfg(feature = "component")]
const SEED: i64 = 0x52c8_611d_3941_be6a_u64 as i64;
#[cfg(feature = "component")]
pub(crate) fn hash64<T: Hash + ?Sized>(data: &T) -> u64 {
let mut hasher = GxHasher::with_seed(SEED);
data.hash(&mut hasher);
hasher.finish()
}
#[macro_export]
macro_rules! as_ref {
($p:expr) => {
unsafe { &*$p }
};
}
#[macro_export]
macro_rules! as_mut {
($p:expr) => {{
let p = $p;
unsafe { &mut *p }
}};
}
#[macro_export]
macro_rules! write_field {
($p:expr, $($field:ident).+, $value:expr) => {
unsafe { (&raw mut (*$p).$($field).+).write($value) }
};
}
#[macro_export]
macro_rules! slice {
($p:expr, $len:expr) => {
unsafe { ::core::slice::from_raw_parts($p, $len as usize) }
};
}
#[macro_export]
macro_rules! slice_mut {
($p:expr, $len:expr) => {
unsafe { ::core::slice::from_raw_parts_mut($p, $len as usize) }
};
}
macro_rules! pool_ptr {
($Name:ident) => {
#[repr(transparent)]
pub struct $Name<T>(*mut T);
unsafe impl<T> Send for $Name<T> {}
unsafe impl<T> Sync for $Name<T> {}
impl<T> Copy for $Name<T> {}
impl<T> Clone for $Name<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T> $Name<T> {
pub fn from_addr(addr: u64) -> Self {
Self(addr as usize as *mut T)
}
pub unsafe fn from_raw(ptr: *mut T) -> Self {
Self(ptr)
}
pub fn as_raw_ptr(&self) -> *mut T {
self.0
}
pub fn to_u64(&self) -> u64 {
self.0 as u64
}
pub fn is_null(&self) -> bool {
self.0.is_null()
}
pub fn cast<U>(self) -> $Name<U> {
$Name(self.0 as *mut U)
}
pub fn byte_add(self, offset: usize) -> Self {
Self(self.0.wrapping_byte_add(offset))
}
}
impl $Name<u8> {
pub fn as_slice(&self, len: usize) -> &[u8] {
unsafe { core::slice::from_raw_parts(self.0, len) }
}
pub fn as_mut_slice(&mut self, len: usize) -> &mut [u8] {
unsafe { core::slice::from_raw_parts_mut(self.0, len) }
}
pub fn copy_from_slice(&self, src: &[u8]) {
unsafe { copy_nonoverlapping(src.as_ptr(), self.0, src.len()) };
}
pub fn as_cstr(&self) -> &CStr {
unsafe { CStr::from_ptr(self.0 as *const core::ffi::c_char) }
}
}
impl<T> Deref for $Name<T> {
type Target = T;
fn deref(&self) -> &T {
unsafe { &*self.0 }
}
}
impl<T> DerefMut for $Name<T> {
fn deref_mut(&mut self) -> &mut T {
unsafe { &mut *self.0 }
}
}
impl<T> fmt::Debug for $Name<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}({:#x})", stringify!($Name), self.0 as usize)
}
}
impl<T> fmt::LowerHex for $Name<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::LowerHex::fmt(&(self.0 as usize), f)
}
}
impl<T> PartialEq for $Name<T> {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl<T> Eq for $Name<T> {}
impl<T> PartialOrd for $Name<T> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<T> Ord for $Name<T> {
fn cmp(&self, other: &Self) -> Ordering {
(self.0 as usize).cmp(&(other.0 as usize))
}
}
impl<T> Hash for $Name<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
(self.0 as usize).hash(state);
}
}
impl<T> Add<usize> for $Name<T> {
type Output = Self;
fn add(self, rhs: usize) -> Self {
self.byte_add(rhs)
}
}
impl<T> Sub<$Name<T>> for $Name<T> {
type Output = usize;
fn sub(self, rhs: Self) -> usize {
(self.0 as usize) - (rhs.0 as usize)
}
}
};
}
pool_ptr!(SbxPtr);
pool_ptr!(AnonPtr);
#[cfg(feature = "component")]
const MEMCPY_LANES: usize = 64;
macro_rules! pool_arr {
($Arr:ident, $Ptr:ident) => {
#[repr(C)]
pub struct $Arr<T: SandboxSafe> {
ptr: $Ptr<T>,
len: usize,
}
unsafe impl<T: SandboxSafe> Send for $Arr<T> {}
unsafe impl<T: SandboxSafe> Sync for $Arr<T> {}
impl<T: SandboxSafe> Copy for $Arr<T> {}
impl<T: SandboxSafe> Clone for $Arr<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: SandboxSafe> $Arr<T> {
pub unsafe fn from_raw_parts(ptr: $Ptr<T>, len: usize) -> Self {
Self { ptr, len }
}
pub fn as_ptr(&self) -> $Ptr<T> {
self.ptr
}
pub fn len(&self) -> usize {
self.len
}
pub fn is_empty(&self) -> bool {
self.len == 0
}
pub fn as_slice(&self) -> &[T] {
unsafe { core::slice::from_raw_parts(self.ptr.as_raw_ptr(), self.len) }
}
pub fn as_mut_slice(&mut self) -> &mut [T] {
unsafe { core::slice::from_raw_parts_mut(self.ptr.as_raw_ptr(), self.len) }
}
pub fn subslice(&self, range: Range<usize>) -> Self {
assert!(
range.end <= self.len,
"{}::subslice: {range:?} out of bounds (len={})",
stringify!($Arr),
self.len
);
Self {
ptr: self.ptr.byte_add(range.start * core::mem::size_of::<T>()),
len: range.end - range.start,
}
}
}
#[cfg(feature = "component")]
impl $Arr<u8> {
pub fn copy_from_slice(&self, src: &[u8]) {
assert_eq!(
src.len(),
self.len,
"{}<u8>::copy_from_slice: length mismatch",
stringify!($Arr)
);
let dst =
unsafe { core::slice::from_raw_parts_mut(self.ptr.as_raw_ptr(), self.len) };
simd_memcpy(dst, src);
}
pub fn copy_to_slice(&self, dst: &mut [u8]) {
assert_eq!(
dst.len(),
self.len,
"{}<u8>::copy_to_slice: length mismatch",
stringify!($Arr)
);
let src = unsafe { core::slice::from_raw_parts(self.ptr.as_raw_ptr(), self.len) };
simd_memcpy(dst, src);
}
}
impl<T: SandboxSafe> fmt::Debug for $Arr<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}({:#x}, len={})", stringify!($Arr), self.ptr.to_u64(), self.len)
}
}
impl<T: SandboxSafe> PartialEq for $Arr<T> {
fn eq(&self, other: &Self) -> bool {
self.ptr == other.ptr && self.len == other.len
}
}
impl<T: SandboxSafe> Eq for $Arr<T> {}
impl<T: SandboxSafe> Hash for $Arr<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.ptr.hash(state);
self.len.hash(state);
}
}
};
}
pool_arr!(SbxArray, SbxPtr);
#[cfg(feature = "component")]
macro_rules! simd_load {
($src:expr, $i:literal) => {{
let chunk = unsafe { from_raw_parts($src.add($i * MEMCPY_LANES), MEMCPY_LANES) };
Simd::<u8, MEMCPY_LANES>::from_slice(chunk)
}};
}
#[cfg(feature = "component")]
macro_rules! simd_store {
($dst:expr, $i:literal, $v:expr) => {{
let chunk = unsafe { from_raw_parts_mut($dst.add($i * MEMCPY_LANES), MEMCPY_LANES) };
$v.copy_to_slice(chunk);
}};
}
#[cfg(feature = "component")]
macro_rules! simd_unrolled_copy {
(1, $src:ident, $dst:ident, $chunks:ident) => {{
let v0 = simd_load!($src, 0);
simd_store!($dst, 0, v0);
$src = unsafe { $src.add(MEMCPY_LANES) };
$dst = unsafe { $dst.add(MEMCPY_LANES) };
$chunks -= 1;
}};
(2, $src:ident, $dst:ident, $chunks:ident) => {{
let v0 = simd_load!($src, 0);
let v1 = simd_load!($src, 1);
simd_store!($dst, 0, v0);
simd_store!($dst, 1, v1);
$src = unsafe { $src.add(2 * MEMCPY_LANES) };
$dst = unsafe { $dst.add(2 * MEMCPY_LANES) };
$chunks -= 2;
}};
(4, $src:ident, $dst:ident, $chunks:ident) => {{
let v0 = simd_load!($src, 0);
let v1 = simd_load!($src, 1);
let v2 = simd_load!($src, 2);
let v3 = simd_load!($src, 3);
simd_store!($dst, 0, v0);
simd_store!($dst, 1, v1);
simd_store!($dst, 2, v2);
simd_store!($dst, 3, v3);
$src = unsafe { $src.add(4 * MEMCPY_LANES) };
$dst = unsafe { $dst.add(4 * MEMCPY_LANES) };
$chunks -= 4;
}};
(8, $src:ident, $dst:ident, $chunks:ident) => {{
let v0 = simd_load!($src, 0);
let v1 = simd_load!($src, 1);
let v2 = simd_load!($src, 2);
let v3 = simd_load!($src, 3);
let v4 = simd_load!($src, 4);
let v5 = simd_load!($src, 5);
let v6 = simd_load!($src, 6);
let v7 = simd_load!($src, 7);
simd_store!($dst, 0, v0);
simd_store!($dst, 1, v1);
simd_store!($dst, 2, v2);
simd_store!($dst, 3, v3);
simd_store!($dst, 4, v4);
simd_store!($dst, 5, v5);
simd_store!($dst, 6, v6);
simd_store!($dst, 7, v7);
$src = unsafe { $src.add(8 * MEMCPY_LANES) };
$dst = unsafe { $dst.add(8 * MEMCPY_LANES) };
$chunks -= 8;
}};
}
#[cfg(feature = "component")]
#[inline]
#[allow(unused_assignments)] pub fn simd_memcpy(dst: &mut [u8], src: &[u8]) {
assert_eq!(dst.len(), src.len(), "simd_memcpy: length mismatch");
let len = src.len();
let tail = len % MEMCPY_LANES;
let mut chunks = len / MEMCPY_LANES;
let mut src = src.as_ptr();
let mut dst = dst.as_mut_ptr();
while chunks >= 8 {
simd_unrolled_copy!(8, src, dst, chunks);
}
if chunks >= 4 {
simd_unrolled_copy!(4, src, dst, chunks);
}
if chunks >= 2 {
simd_unrolled_copy!(2, src, dst, chunks);
}
if chunks >= 1 {
simd_unrolled_copy!(1, src, dst, chunks);
}
if tail != 0 {
unsafe { copy_nonoverlapping(src, dst, tail) };
}
}