#![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, mem, mem::MaybeUninit};
use self::syscall::{
OperationCode, ParamRegR, ParamRegW, RetReg, syscall0, syscall1_noreturn_readonly,
syscall1_readonly, syscall2, syscall2_readonly, syscall3, syscall3_readonly, syscall4,
syscall4_readonly,
};
use crate::{
fd::{BorrowedFd, OwnedFd, RawFd},
io,
utils::slice_assume_init_mut,
};
#[allow(missing_docs)]
mod consts {
pub const O_RDONLY: i32 = 0x0;
pub const O_WRONLY: i32 = 0x1;
pub const O_RDWR: i32 = 0x2;
pub const O_APPEND: i32 = 0x8;
pub const O_CREAT: i32 = 0x200;
pub const O_TRUNC: i32 = 0x400;
pub const O_EXCL: i32 = 0x800;
pub const S_IXOTH: i32 = 0o1;
pub const S_IWOTH: i32 = 0o2;
pub const S_IROTH: i32 = 0o4;
pub const S_IRWXO: i32 = 0o7;
pub const S_IXGRP: i32 = 0o10;
pub const S_IWGRP: i32 = 0o20;
pub const S_IRGRP: i32 = 0o40;
pub const S_IRWXG: i32 = 0o70;
pub const S_IXUSR: i32 = 0o100;
pub const S_IWUSR: i32 = 0o200;
pub const S_IRUSR: i32 = 0o400;
pub const S_IRWXU: i32 = 0o700;
}
pub use self::consts::*;
#[allow(missing_docs)]
#[derive(Debug, Clone, Copy)]
#[repr(i32)]
#[non_exhaustive]
pub enum SeekWhence {
SEEK_SET = 0,
SEEK_CUR = 1,
SEEK_END = 2,
}
#[allow(missing_docs)]
#[allow(clippy::exhaustive_structs)] #[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct uhi_stat {
pub st_dev: i16,
pub st_ino: u16,
pub st_mode: u32,
pub st_nlink: u16,
pub st_uid: u16,
pub st_gid: u16,
pub st_rdev: i16,
pub st_size: u64,
pub st_atime: u64,
pub st_spare1: u64,
pub st_mtime: u64,
pub st_spare2: u64,
pub st_ctime: u64,
pub st_spare3: u64,
pub st_blksize: u64,
pub st_blocks: u64,
pub st_spare4: [u64; 2],
}
#[cold]
fn from_errno(res: RetReg) -> io::Error {
io::Error::from_raw_os_error(res.errno())
}
pub fn mips_exit(code: i32) {
#[allow(clippy::cast_sign_loss)]
unsafe {
syscall1_readonly(OperationCode::UHI_EXIT, ParamRegR::unsigned(code as isize as usize));
}
}
pub(crate) fn exit(code: i32) -> ! {
#[allow(clippy::cast_sign_loss)]
unsafe {
syscall1_noreturn_readonly(
OperationCode::UHI_EXIT,
ParamRegR::unsigned(code as isize as usize),
)
}
}
pub fn mips_open(path: &CStr, flags: i32, mode: i32) -> io::Result<OwnedFd> {
#[allow(clippy::cast_sign_loss)]
let (res, errno) = unsafe {
syscall3_readonly(
OperationCode::UHI_OPEN,
ParamRegR::c_str(path),
ParamRegR::unsigned(flags as usize),
ParamRegR::unsigned(mode as usize),
)
};
match res.raw_fd() {
Some(fd) => Ok(unsafe { OwnedFd::from_raw_fd(fd) }),
None => Err(from_errno(errno)),
}
}
pub unsafe fn mips_close(fd: RawFd) -> io::Result<()> {
let (res, errno) =
unsafe { syscall1_readonly(OperationCode::UHI_CLOSE, ParamRegR::raw_fd(fd)) };
if res.unsigned() == 0 {
Ok(())
} else {
debug_assert_eq!(res.signed(), -1);
Err(from_errno(errno))
}
}
pub(crate) use self::mips_close as close;
pub fn mips_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) use self::mips_read as read;
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 (res, errno) = unsafe {
syscall3(
OperationCode::UHI_READ,
ParamRegW::fd(fd),
ParamRegW::buf(buf),
ParamRegW::unsigned(len),
)
};
if res.signed() == -1 {
Err(from_errno(errno))
} else {
debug_assert!(res.unsigned() <= buf.len());
let (filled, rest) = buf.split_at_mut(res.unsigned());
Ok((unsafe { slice_assume_init_mut(filled) }, rest))
}
}
pub fn mips_write(fd: BorrowedFd<'_>, buf: &[u8]) -> io::Result<usize> {
let (res, errno) = unsafe {
syscall3_readonly(
OperationCode::UHI_WRITE,
ParamRegR::fd(fd),
ParamRegR::buf(buf),
ParamRegR::unsigned(buf.len()),
)
};
if res.signed() == -1 {
Err(from_errno(errno))
} else {
debug_assert!(res.unsigned() <= buf.len());
Ok(res.unsigned())
}
}
pub(crate) use self::mips_write as write;
pub unsafe fn mips_lseek(
fd: BorrowedFd<'_>,
offset: isize,
whence: SeekWhence,
) -> io::Result<usize> {
let (res, errno) = unsafe {
syscall3_readonly(
OperationCode::UHI_LSEEK,
ParamRegR::fd(fd),
ParamRegR::signed(offset),
ParamRegR::unsigned(whence as usize),
)
};
if res.signed() == -1 { Err(from_errno(errno)) } else { Ok(res.unsigned()) }
}
pub fn mips_unlink(path: &CStr) -> io::Result<()> {
let (res, errno) =
unsafe { syscall1_readonly(OperationCode::UHI_UNLINK, ParamRegR::c_str(path)) };
if res.unsigned() == 0 { Ok(()) } else { Err(from_errno(errno)) }
}
pub fn mips_fstat(fd: BorrowedFd<'_>) -> io::Result<uhi_stat> {
let mut buf: uhi_stat = unsafe { mem::zeroed() };
let (res, errno) =
unsafe { syscall2(OperationCode::UHI_FSTAT, ParamRegW::fd(fd), ParamRegW::ref_(&mut buf)) };
if res.unsigned() == 0 { Ok(buf) } else { Err(from_errno(errno)) }
}
pub fn mips_argc() -> usize {
let (res, _errno) = unsafe { syscall0(OperationCode::UHI_ARGC) };
debug_assert!(!res.signed().is_negative(), "{}", res.signed());
res.unsigned()
}
pub fn mips_argnlen(n: usize) -> io::Result<usize> {
let (res, errno) =
unsafe { syscall1_readonly(OperationCode::UHI_ARGNLEN, ParamRegR::unsigned(n)) };
if res.signed() == -1 { Err(from_errno(errno)) } else { Ok(res.unsigned()) }
}
pub unsafe fn mips_argn(n: usize, buf: *mut u8) -> io::Result<()> {
let (res, errno) =
unsafe { syscall2(OperationCode::UHI_ARGN, ParamRegW::unsigned(n), ParamRegW::ptr(buf)) };
if res.unsigned() == 0 {
Ok(())
} else {
debug_assert_eq!(res.signed(), -1);
Err(from_errno(errno))
}
}
pub fn mips_plog(msg: &CStr) -> io::Result<usize> {
let (res, errno) = unsafe { syscall1_readonly(OperationCode::UHI_PLOG, ParamRegR::c_str(msg)) };
if res.signed() == -1 {
Err(from_errno(errno))
} else {
debug_assert_eq!(res.unsigned(), msg.to_bytes().len());
Ok(res.unsigned())
}
}
pub fn mips_pread(fd: BorrowedFd<'_>, buf: &mut [u8], offset: usize) -> 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(mips_pread_uninit(fd, buf, offset)?.0.len())
}
fn mips_pread_uninit<'a>(
fd: BorrowedFd<'_>,
buf: &'a mut [MaybeUninit<u8>],
offset: usize,
) -> io::Result<(&'a mut [u8], &'a mut [MaybeUninit<u8>])> {
let len = buf.len();
let (res, errno) = unsafe {
syscall4(
OperationCode::UHI_PREAD,
ParamRegW::fd(fd),
ParamRegW::buf(buf),
ParamRegW::unsigned(len),
ParamRegW::unsigned(offset),
)
};
if res.signed() == -1 {
Err(from_errno(errno))
} else {
debug_assert!(res.unsigned() <= buf.len());
let (filled, rest) = buf.split_at_mut(res.unsigned());
Ok((unsafe { slice_assume_init_mut(filled) }, rest))
}
}
pub fn mips_pwrite(fd: BorrowedFd<'_>, buf: &[u8], offset: usize) -> io::Result<usize> {
let (res, errno) = unsafe {
syscall4_readonly(
OperationCode::UHI_PWRITE,
ParamRegR::fd(fd),
ParamRegR::buf(buf),
ParamRegR::unsigned(buf.len()),
ParamRegR::unsigned(offset),
)
};
if res.signed() == -1 {
Err(from_errno(errno))
} else {
debug_assert!(res.unsigned() <= buf.len());
Ok(res.unsigned())
}
}
pub fn mips_link(old: &CStr, new: &CStr) -> io::Result<()> {
let (res, errno) = unsafe {
syscall2_readonly(OperationCode::UHI_LINK, ParamRegR::c_str(old), ParamRegR::c_str(new))
};
if res.unsigned() == 0 {
Ok(())
} else {
debug_assert_eq!(res.signed(), -1);
Err(from_errno(errno))
}
}