#![deny(missing_docs)]
#![deny(clippy::all)]
#![cfg_attr(not(test), no_std)]
pub mod elf;
pub mod syscall;
mod tests;
pub mod untrusted;
#[cfg(feature = "asm")]
use core::arch::asm;
use core::mem::size_of;
use core::mem::MaybeUninit;
use core::ptr::NonNull;
use primordial::{Page, Register};
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
pub const REQUIRES: [u8; VERSION.len() + 1] = {
let mut value = [0u8; VERSION.len() + 1];
let mut i = 0;
value[0] = b'^';
while i < VERSION.len() {
value[i + 1] = VERSION.as_bytes()[i];
i += 1;
}
value
};
pub const KVM_SYSCALL_TRIGGER_PORT: u16 = 0xFF;
pub const MAX_UDP_PACKET_SIZE: usize = 65507;
#[macro_export]
macro_rules! request {
($num:expr) => {
$crate::Request { num: $num.into(), arg: [Register::default(); 7] }
};
($num:expr => $($arg:expr),*) => {{
let args = [$($arg.into()),*];
$crate::Request {
num: $num.into(),
arg: [
args.get(0).copied().unwrap_or_default(),
args.get(1).copied().unwrap_or_default(),
args.get(2).copied().unwrap_or_default(),
args.get(3).copied().unwrap_or_default(),
args.get(4).copied().unwrap_or_default(),
args.get(5).copied().unwrap_or_default(),
args.get(6).copied().unwrap_or_default(),
]
}
}};
}
#[repr(C)]
#[derive(Copy, Clone, Default, PartialEq, Debug)]
pub struct Request {
pub num: Register<usize>,
pub arg: [Register<usize>; 7],
}
impl Request {
#[cfg(feature = "asm")]
pub unsafe fn syscall(&self) -> Reply {
let rax: usize;
let rdx: usize;
if i64::from(self.num) == libc::SYS_clock_gettime {
let mut ret = libc::clock_gettime(usize::from(self.arg[0]) as _, self.arg[1].into());
if ret != 0 {
ret = *libc::__errno_location();
}
Reply {
ret: [ret.into(), 0.into()],
err: Default::default(),
}
} else {
asm!(
"syscall",
inlateout("rax") usize::from(self.num) => rax,
in("rdi") usize::from(self.arg[0]),
in("rsi") usize::from(self.arg[1]),
inlateout("rdx") usize::from(self.arg[2]) => rdx,
in("r10") usize::from(self.arg[3]),
in("r8") usize::from(self.arg[4]),
in("r9") usize::from(self.arg[5]),
lateout("rcx") _, lateout("r11") _, );
Reply {
ret: [rax.into(), rdx.into()],
err: Default::default(),
}
}
}
}
#[repr(C)]
#[derive(Copy, Clone, Default, PartialEq, Debug)]
pub struct Reply {
ret: [Register<usize>; 2],
err: Register<usize>,
}
pub type Result = core::result::Result<[Register<usize>; 2], libc::c_int>;
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
impl From<Result> for Reply {
#[inline]
fn from(value: Result) -> Self {
match value {
Ok(val) => Self {
ret: val,
err: Default::default(),
},
Err(val) => Self {
ret: [(-val as usize).into(), Default::default()],
err: Default::default(),
},
}
}
}
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
impl From<Reply> for Result {
#[inline]
fn from(value: Reply) -> Self {
let reg: usize = value.ret[0].into();
if reg > -4096isize as usize {
Err(-(reg as libc::c_int))
} else {
Ok(value.ret)
}
}
}
#[repr(C)]
#[derive(Copy, Clone)]
pub union Message {
pub req: Request,
pub rep: Reply,
}
#[repr(C)]
#[derive(Copy, Clone)]
pub struct Block {
pub msg: Message,
buf: [u8; Block::buf_capacity()],
}
impl Default for Block {
fn default() -> Self {
Self {
msg: Message {
req: Request::default(),
},
buf: [0u8; Block::buf_capacity()],
}
}
}
impl Block {
pub const fn buf_capacity() -> usize {
(MAX_UDP_PACKET_SIZE + size_of::<Message>() + Page::SIZE - 1) / Page::SIZE * Page::SIZE
- size_of::<Message>()
}
#[allow(dead_code)]
pub fn cursor(&mut self) -> Cursor {
Cursor(&mut self.buf)
}
}
pub struct Cursor<'a>(&'a mut [u8]);
#[derive(Copy, Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct OutOfSpace;
impl<'short, 'a: 'short> Cursor<'a> {
pub fn alloc<T>(
self,
count: usize,
) -> core::result::Result<(Cursor<'a>, &'short mut [MaybeUninit<T>]), OutOfSpace> {
let mid = {
let (padding, data, _) = unsafe { self.0.align_to_mut::<MaybeUninit<T>>() };
if data.len() < count {
return Err(OutOfSpace);
}
padding.len() + size_of::<MaybeUninit<T>>() * count
};
let (data, next) = self.0.split_at_mut(mid);
Ok((
Cursor(next),
unsafe { data.align_to_mut::<MaybeUninit<T>>() }.1,
))
}
#[allow(dead_code)]
pub fn copy_from_slice<T: 'a + Copy>(
self,
src: &[T],
) -> core::result::Result<(Cursor<'a>, &'short mut [T]), OutOfSpace> {
let (c, dst) = self.alloc::<T>(src.len())?;
unsafe {
core::ptr::copy_nonoverlapping(src.as_ptr(), dst.as_mut_ptr() as _, src.len());
}
let dst = unsafe { &mut *(dst as *mut [MaybeUninit<T>] as *mut [T]) };
Ok((c, dst))
}
#[allow(dead_code)]
pub unsafe fn copy_into_slice<T: 'a + Copy>(
self,
src_len: usize,
dst: &mut [T],
) -> core::result::Result<Cursor<'a>, OutOfSpace> {
self.copy_into_raw_parts(src_len, dst.as_mut_ptr(), dst.len())
}
#[allow(dead_code)]
pub unsafe fn copy_from_raw_parts<T: 'a + Copy>(
self,
src: *const T,
src_len: usize,
) -> core::result::Result<(Cursor<'a>, *mut MaybeUninit<T>), OutOfSpace> {
let (c, dst) = self.alloc::<T>(src_len)?;
core::ptr::copy_nonoverlapping(src, dst.as_mut_ptr() as *mut _, src_len);
Ok((c, dst.as_mut_ptr()))
}
#[allow(dead_code)]
pub unsafe fn copy_into_raw_parts<T: 'a + Copy>(
self,
src_len: usize,
dst: *mut T,
dst_len: usize,
) -> core::result::Result<Cursor<'a>, OutOfSpace> {
assert!(src_len >= dst_len);
let (c, src) = self.alloc::<T>(src_len)?;
core::ptr::copy_nonoverlapping(src.as_ptr(), dst as _, dst_len);
Ok(c)
}
#[allow(dead_code)]
pub unsafe fn read<T: 'a + Copy>(self) -> core::result::Result<(Cursor<'a>, T), OutOfSpace> {
let (c, src) = self.alloc::<T>(1)?;
Ok((c, src[0].as_ptr().read()))
}
#[allow(dead_code)]
pub fn write<T: 'a + Copy>(
self,
src: &T,
) -> core::result::Result<(Cursor<'a>, &'short mut T), OutOfSpace> {
let (c, dst) = self.alloc::<T>(1)?;
let dst = dst[0].as_mut_ptr();
unsafe {
core::ptr::write(dst, *src);
}
Ok((c, unsafe { &mut *dst }))
}
#[allow(dead_code)]
pub unsafe fn copy_into<T: 'a + Copy>(
self,
dst: NonNull<T>,
) -> core::result::Result<Cursor<'a>, OutOfSpace> {
let (c, src) = self.alloc::<T>(1)?;
core::ptr::write(dst.as_ptr(), src[0].as_ptr().read());
Ok(c)
}
}