blob: 9a78169dd739e946f1d0c774eadc041a3a2af09c [file] [log] [blame]
//! Provides the Syscalls trait which directly represents Tock's system call
//! APIs. Syscalls is implemented by both `libtock_runtime` which makes system
//! calls into a real Tock kernel, and `libtock_fake` which is a fake Tock
//! kernel.
// TODO: Implement `libtock_runtime` and `libtock_fake`.
/// Syscalls represents Tock's system call APIs. It is designed to be
/// implemented as easily as possible -- its arguments and return values
/// correspond directly to registers in the ABI. For a higher-level abstraction,
/// see Platform.
///
/// By design, syscalls is designed to be zero-cost in a TBF binary and
/// functional (but not zero-cost) in unit tests. In a TBF binary, Syscalls is
/// implemented with the `'static` lifetime, and is a zero-sized type. Syscalls
/// requires `Copy` in order to support defining it usefully on zero-sized
/// types. When used in unit tests, the Syscalls implementation carries a
/// lifetime local to that unit test.
///
/// With the exception of `memop`, this trait aligns closely to Tock's
/// kernel::Driver trait.
pub trait Syscalls<'k>: Copy {
/// Calls the `allow` system call.
///
/// # Safety
/// `allow` is unsafe because callers must guarantee that `pointer` and
/// `length` refer to memory that the kernel can mutate safely. The buffer
/// must last for the lifetime 'k.
// `driver` and `minor` are `usize` because the kernel internally treats
// them as `usize`s. `allow`'s return value is a kernel `ReturnCode`;
// Platform translates the `isize` into a `ReturnCode`.
unsafe fn allow(self, driver: usize, minor: usize, pointer: *mut u8, length: usize) -> isize;
/// Calls the `command` system call.
// `driver`, `minor`, `arg1`, and `arg2` are all `usize` (rather than `u32`)
// because the kernel refers to them internally as `usize`s. command returns
// a kernel ReturnCode; Platform is responsible for translating an isize
// into the local ReturnCode.
fn command(self, driver: usize, minor: usize, arg1: usize, arg2: usize) -> isize;
/// Calls the `memop` system call with an argument. Note that memop() cannot
/// cause memory unsafety, although it can cause the app to fault (e.g. Brk
/// can move the app break below the stack, causing a fault). The isize
/// returned is a kernel ReturnCode.
// Platform performs the translation from isize into ReturnCode to keep
// Syscalls implementations simple.
fn memop_arg(self, op: MemopWithArg, arg: usize) -> isize;
/// Calls the `memop` system call with no arguments. This version is
/// slightly cheaper because it does not need to set the argument register.
// We're okay with leaking the value in the argument register because
// memop() is always handled by the core kernel, never by an untrusted
// capsule.
fn memop_noarg(self, op: MemopNoArg) -> isize;
/// Calls the `subscribe` system call.
///
/// # Safety
/// `subscribe` is unsafe because the callback can potentially be unsafe,
/// and callers of `subscribe` must assert that calling the callback with
/// the provided `data` value is safe. The callback must last for the 'k
/// lifetime.
// Driver, minor, the callback args, and data are all represented as `usize`
// because that is the type the kernel uses internally to store them (e.g.
// as opposed to u32).
unsafe fn subscribe(
self,
driver: usize,
minor: usize,
callback: Option<unsafe extern "C" fn(usize, usize, usize, usize)>,
data: usize,
);
/// Puts the process to sleep until a callback becomes pending, then invokes
/// the callback.
fn yieldk(self);
}
#[non_exhaustive]
#[repr(usize)]
pub enum MemopWithArg {
Brk = 0,
Sbrk = 1,
FlashRegionStart = 8,
FlashRegionEnd = 9,
SpecifyStackTop = 10,
SpecifyHeapStart = 11,
}
#[non_exhaustive]
#[repr(usize)]
pub enum MemopNoArg {
MemoryStart = 2,
MemoryEnd = 3,
FlashStart = 4,
FlashEnd = 5,
GrantStart = 6,
FlashRegions = 7,
}