#![allow(clippy::missing_safety_doc, clippy::undocumented_unsafe_blocks)]
pub(crate) mod errno;
#[cfg(feature = "fs")]
pub(crate) mod fs;
#[cfg(feature = "stdio")]
pub(crate) mod stdio;
pub mod syscall;
use core::{
ffi::{CStr, c_void},
mem::{self, MaybeUninit},
};
use self::syscall::{
OperationNumber, ParamRegR, ParamRegW, syscall, syscall_noreturn_readonly,
syscall_param_unchanged, syscall_param_unchanged_readonly, syscall_readonly,
syscall0_param_unchanged,
};
use crate::{
fd::{BorrowedFd, OwnedFd, RawFd},
io,
utils::slice_assume_init_mut,
};
#[allow(missing_docs)]
#[derive(Debug, Clone, Copy)]
#[repr(usize)]
#[non_exhaustive]
pub enum ExitReason {
ADP_Stopped_BranchThroughZero = 0x20000,
ADP_Stopped_UndefinedInstr = 0x20001,
ADP_Stopped_SoftwareInterrupt = 0x20002,
ADP_Stopped_PrefetchAbort = 0x20003,
ADP_Stopped_DataAbort = 0x20004,
ADP_Stopped_AddressException = 0x20005,
ADP_Stopped_IRQ = 0x20006,
ADP_Stopped_FIQ = 0x20007,
ADP_Stopped_BreakPoint = 0x20020,
ADP_Stopped_WatchPoint = 0x20021,
ADP_Stopped_StepComplete = 0x20022,
ADP_Stopped_RunTimeErrorUnknown = 0x20023,
ADP_Stopped_InternalError = 0x20024,
ADP_Stopped_UserInterruption = 0x20025,
ADP_Stopped_ApplicationExit = 0x20026,
ADP_Stopped_StackOverflow = 0x20027,
ADP_Stopped_DivisionByZero = 0x20028,
ADP_Stopped_OSSpecific = 0x20029,
}
#[allow(missing_docs)]
#[derive(Debug, Clone, Copy)]
#[repr(usize)]
#[non_exhaustive]
pub enum OpenMode {
#[doc(alias = "r")]
#[doc(alias = "O_RDONLY")]
RDONLY = 0,
#[doc(alias = "rb")]
#[doc(alias = "O_RDONLY", alias = "O_BINARY")]
RDONLY_BINARY = 1,
#[doc(alias = "r+")]
#[doc(alias = "O_RDWR")]
RDWR = 2,
#[doc(alias = "r+b")]
#[doc(alias = "O_RDWR", alias = "O_BINARY")]
RDWR_BINARY = 3,
#[doc(alias = "w")]
#[doc(alias = "O_WRONLY", alias = "O_CREAT", alias = "O_TRUNC")]
WRONLY_TRUNC = 4,
#[doc(alias = "wb")]
#[doc(alias = "O_WRONLY", alias = "O_CREAT", alias = "O_TRUNC", alias = "O_BINARY")]
WRONLY_TRUNC_BINARY = 5,
#[doc(alias = "w+")]
#[doc(alias = "O_RDWR", alias = "O_CREAT", alias = "O_TRUNC")]
RDWR_TRUNC = 6,
#[doc(alias = "w+b")]
#[doc(alias = "O_RDWR", alias = "O_CREAT", alias = "O_TRUNC", alias = "O_BINARY")]
RDWR_TRUNC_BINARY = 7,
#[doc(alias = "a")]
#[doc(alias = "O_WRONLY", alias = "O_CREAT", alias = "O_APPEND")]
WRONLY_APPEND = 8,
#[doc(alias = "ab")]
#[doc(alias = "O_WRONLY", alias = "O_CREAT", alias = "O_APPEND", alias = "O_BINARY")]
WRONLY_APPEND_BINARY = 9,
#[doc(alias = "a+")]
#[doc(alias = "O_RDWR", alias = "O_CREAT", alias = "O_APPEND")]
RDWR_APPEND = 10,
#[doc(alias = "a+b`")]
#[doc(alias = "O_RDWR", alias = "O_CREAT", alias = "O_APPEND", alias = "O_BINARY")]
RDWR_APPEND_BINARY = 11,
}
#[allow(missing_docs)]
#[allow(clippy::exhaustive_structs)] #[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct HeapInfo {
pub heap_base: *mut c_void,
pub heap_limit: *mut c_void,
pub stack_base: *mut c_void,
pub stack_limit: *mut c_void,
}
#[allow(missing_docs)]
#[allow(clippy::exhaustive_structs)]
#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct CommandLine {
pub ptr: *mut u8,
pub size: usize,
}
#[cold]
fn from_errno() -> io::Error {
io::Error::from_raw_os_error(sys_errno())
}
pub fn sys_clock() -> io::Result<usize> {
let ret = unsafe { syscall0_param_unchanged(OperationNumber::SYS_CLOCK) };
if ret.signed() == -1 { Err(from_errno()) } else { Ok(ret.unsigned()) }
}
pub unsafe fn sys_close(fd: RawFd) -> io::Result<()> {
let block = [ParamRegR::raw_fd(fd)];
let ret = unsafe {
syscall_param_unchanged_readonly(OperationNumber::SYS_CLOSE, ParamRegR::block(&block))
};
if ret.unsigned() == 0 {
Ok(())
} else {
debug_assert_eq!(ret.signed(), -1);
Err(from_errno())
}
}
pub(crate) use self::sys_close as close;
pub fn sys_elapsed() -> io::Result<u64> {
let mut block = [0_u64];
let ret = unsafe { syscall(OperationNumber::SYS_ELAPSED, ParamRegW::ref_(&mut block)) };
if ret.unsigned() == 0 {
Ok(block[0])
} else {
debug_assert_eq!(ret.signed(), -1);
Err(from_errno())
}
}
pub fn sys_errno() -> io::RawOsError {
let ret = unsafe { syscall0_param_unchanged(OperationNumber::SYS_ERRNO) };
ret.errno()
}
pub fn sys_exit(reason: ExitReason) {
#[cfg(target_pointer_width = "32")]
let arg = ParamRegR::unsigned(reason as usize);
#[cfg(target_pointer_width = "64")]
let block = [ParamRegR::unsigned(reason as usize), ParamRegR::unsigned(0)];
#[cfg(target_pointer_width = "64")]
let arg = ParamRegR::block(&block);
unsafe {
syscall_readonly(OperationNumber::SYS_EXIT, arg);
}
}
pub(crate) fn exit(code: i32) -> ! {
let reason = ExitReason::ADP_Stopped_ApplicationExit;
#[allow(clippy::cast_sign_loss)]
let subcode = code as isize as usize;
#[cfg(target_pointer_width = "64")]
unsafe {
let block = [ParamRegR::unsigned(reason as usize), ParamRegR::unsigned(subcode)];
syscall_noreturn_readonly(OperationNumber::SYS_EXIT, ParamRegR::block(&block))
}
#[cfg(target_pointer_width = "32")]
{
sys_exit_extended(reason, subcode);
let reason = match code {
0 => ExitReason::ADP_Stopped_ApplicationExit,
_ => ExitReason::ADP_Stopped_RunTimeErrorUnknown,
};
unsafe {
syscall_noreturn_readonly(
OperationNumber::SYS_EXIT,
ParamRegR::unsigned(reason as usize),
)
}
}
}
pub fn sys_exit_extended(reason: ExitReason, subcode: usize) {
let block = [ParamRegR::unsigned(reason as usize), ParamRegR::unsigned(subcode)];
#[cfg(target_pointer_width = "32")]
let number = OperationNumber::SYS_EXIT_EXTENDED;
#[cfg(target_pointer_width = "64")]
let number = OperationNumber::SYS_EXIT;
unsafe {
syscall_readonly(number, ParamRegR::block(&block));
}
}
pub fn sys_flen(fd: BorrowedFd<'_>) -> io::Result<usize> {
let block = [ParamRegR::fd(fd)];
let ret = unsafe {
syscall_param_unchanged_readonly(OperationNumber::SYS_FLEN, ParamRegR::block(&block))
};
if ret.signed() == -1 { Err(from_errno()) } else { Ok(ret.unsigned()) }
}
#[deprecated(note = "use safer [`sys_get_cmdline_uninit`] instead")] pub unsafe fn sys_get_cmdline(cmdline: &mut CommandLine) -> io::Result<()> {
let len = cmdline.size;
let ret = unsafe {
syscall_param_unchanged(OperationNumber::SYS_GET_CMDLINE, ParamRegW::ref_(cmdline))
};
if ret.unsigned() == 0 {
debug_assert!(!cmdline.ptr.is_null());
let size = cmdline.size;
debug_assert!(size < len); Ok(())
} else {
debug_assert_eq!(ret.signed(), -1);
Err(from_errno())
}
}
pub fn sys_get_cmdline_uninit(buf: &mut [MaybeUninit<u8>]) -> io::Result<&mut [u8]> {
let len = buf.len();
let mut block = [ParamRegW::buf(buf), ParamRegW::unsigned(len)];
let ret = unsafe {
syscall_param_unchanged(OperationNumber::SYS_GET_CMDLINE, ParamRegW::block(&mut block))
};
if ret.unsigned() == 0 {
debug_assert!(!block[0].to_ret().ptr().is_null());
let size = block[1].to_ret().unsigned();
debug_assert!(size < len); Ok(unsafe { slice_assume_init_mut(&mut buf[..size]) })
} else {
debug_assert_eq!(ret.signed(), -1);
Err(from_errno())
}
}
pub fn sys_heapinfo() -> HeapInfo {
let mut buf: HeapInfo = unsafe { mem::zeroed() };
unsafe {
syscall_param_unchanged(OperationNumber::SYS_HEAPINFO, ParamRegW::ref_(&mut buf));
}
buf
}
pub fn sys_iserror(res: isize) -> bool {
let block = [ParamRegR::signed(res)];
let ret = unsafe {
syscall_param_unchanged_readonly(OperationNumber::SYS_ISERROR, ParamRegR::block(&block))
};
ret.unsigned() != 0
}
pub fn sys_istty(fd: BorrowedFd<'_>) -> io::Result<bool> {
let block = [ParamRegR::fd(fd)];
let ret = unsafe {
syscall_param_unchanged_readonly(OperationNumber::SYS_ISTTY, ParamRegR::block(&block))
};
match ret.unsigned() {
1 => Ok(true),
0 => Ok(false),
_ => Err(from_errno()), }
}
pub fn sys_open(path: &CStr, mode: OpenMode) -> io::Result<OwnedFd> {
let block =
[ParamRegR::c_str(path), ParamRegR::unsigned(mode as usize), ParamRegR::c_str_len(path)];
let ret = unsafe {
syscall_param_unchanged_readonly(OperationNumber::SYS_OPEN, ParamRegR::block(&block))
};
match ret.raw_fd() {
Some(fd) => {
debug_assert_ne!(ret.unsigned(), 0);
Ok(unsafe { OwnedFd::from_raw_fd(fd) })
}
None => Err(from_errno()),
}
}
#[deprecated(
note = "use [`sys::read`](crate::sys::read), [`sys::read_uninit`](crate::sys::read_uninit), or [`sys_read_orig`] instead"
)] pub fn sys_read(fd: BorrowedFd<'_>, buf: &mut [MaybeUninit<u8>]) -> io::Result<usize> {
Ok(read_uninit(fd, buf)?.0.len())
}
pub fn sys_read_orig(fd: BorrowedFd<'_>, buf: &mut [u8]) -> io::Result<usize> {
let num_read = read(fd, buf)?;
Ok(buf.len() - num_read)
}
pub(crate) fn read(fd: BorrowedFd<'_>, buf: &mut [u8]) -> io::Result<usize> {
let len = buf.len();
let buf =
unsafe { core::slice::from_raw_parts_mut(buf.as_mut_ptr().cast::<MaybeUninit<u8>>(), len) };
Ok(read_uninit(fd, buf)?.0.len())
}
pub(crate) fn read_uninit<'a>(
fd: BorrowedFd<'_>,
buf: &'a mut [MaybeUninit<u8>],
) -> io::Result<(&'a mut [u8], &'a mut [MaybeUninit<u8>])> {
let len = buf.len();
let mut block = [ParamRegW::fd(fd), ParamRegW::buf(buf), ParamRegW::unsigned(len)];
let ret =
unsafe { syscall_param_unchanged(OperationNumber::SYS_READ, ParamRegW::block(&mut block)) };
let not_read = ret.unsigned();
if not_read <= len {
let (filled, rest) = buf.split_at_mut(buf.len() - not_read);
Ok((unsafe { slice_assume_init_mut(filled) }, rest))
} else {
debug_assert!(ret.signed().is_negative());
Err(from_errno())
}
}
pub fn sys_readc() -> u8 {
let ret = unsafe { syscall0_param_unchanged(OperationNumber::SYS_READC) };
ret.u8()
}
pub fn sys_remove(path: &CStr) -> io::Result<()> {
let block = [ParamRegR::c_str(path), ParamRegR::c_str_len(path)];
let ret = unsafe {
syscall_param_unchanged_readonly(OperationNumber::SYS_REMOVE, ParamRegR::block(&block))
};
if ret.unsigned() == 0 { Ok(()) } else { Err(from_errno()) }
}
pub fn sys_rename(from: &CStr, to: &CStr) -> io::Result<()> {
let block = [
ParamRegR::c_str(from),
ParamRegR::c_str_len(from),
ParamRegR::c_str(to),
ParamRegR::c_str_len(to),
];
let ret = unsafe {
syscall_param_unchanged_readonly(OperationNumber::SYS_RENAME, ParamRegR::block(&block))
};
if ret.unsigned() == 0 { Ok(()) } else { Err(from_errno()) }
}
pub unsafe fn sys_seek(fd: BorrowedFd<'_>, abs_pos: usize) -> io::Result<()> {
let block = [ParamRegR::fd(fd), ParamRegR::unsigned(abs_pos)];
let ret = unsafe {
syscall_param_unchanged_readonly(OperationNumber::SYS_SEEK, ParamRegR::block(&block))
};
if ret.unsigned() == 0 {
Ok(())
} else {
debug_assert!(ret.signed().is_negative());
Err(from_errno())
}
}
pub fn sys_system(cmd: &CStr) -> usize {
let block = [ParamRegR::c_str(cmd), ParamRegR::c_str_len(cmd)];
let ret = unsafe {
syscall_param_unchanged_readonly(OperationNumber::SYS_SYSTEM, ParamRegR::block(&block))
};
ret.unsigned()
}
pub fn sys_tickfreq() -> io::Result<usize> {
let ret = unsafe { syscall0_param_unchanged(OperationNumber::SYS_TICKFREQ) };
if ret.signed() == -1 { Err(from_errno()) } else { Ok(ret.unsigned()) }
}
#[allow(clippy::unnecessary_wraps)] pub fn sys_time() -> io::Result<usize> {
let ret = unsafe { syscall0_param_unchanged(OperationNumber::SYS_TIME) };
Ok(ret.unsigned())
}
#[deprecated(note = "use [`sys::write`](crate::sys::write) or [`sys_write_orig`] instead")] pub fn sys_write(fd: BorrowedFd<'_>, buf: &[u8]) -> io::Result<usize> {
write(fd, buf)
}
pub fn sys_write_orig(fd: BorrowedFd<'_>, buf: &[u8]) -> io::Result<usize> {
let len = buf.len();
let block = [ParamRegR::fd(fd), ParamRegR::buf(buf), ParamRegR::unsigned(len)];
let ret = unsafe {
syscall_param_unchanged_readonly(OperationNumber::SYS_WRITE, ParamRegR::block(&block))
};
let not_written = ret.unsigned();
#[allow(clippy::comparison_chain)]
if not_written < len {
Ok(not_written)
} else if not_written == len {
if len == 0 {
Ok(not_written)
} else {
Err(from_errno())
}
} else {
debug_assert!(ret.signed().is_negative());
Err(from_errno())
}
}
pub(crate) fn write(fd: BorrowedFd<'_>, buf: &[u8]) -> io::Result<usize> {
let not_written = sys_write_orig(fd, buf)?;
Ok(buf.len() - not_written)
}
pub fn sys_writec(character: u8) {
unsafe {
syscall_param_unchanged_readonly(OperationNumber::SYS_WRITEC, ParamRegR::ref_(&character));
}
}
pub fn sys_write0(string: &CStr) {
unsafe {
syscall_param_unchanged_readonly(OperationNumber::SYS_WRITE0, ParamRegR::c_str(string));
}
}