hardware 0.0.9

A no_std bare-metal hardware abstraction layer — all port I/O, memory and swap allocations are guarded at runtime. Do not consider this dependency stable before x.1.x
Documentation
use crate::common::once::OnceCopy;
use core::sync::atomic::{AtomicI64, Ordering};

pub struct SyscallNrTable {
    pub read: i64,
    pub write: i64,
    pub openat: i64,
    pub close: i64,
    pub mmap: i64,
    pub munmap: i64,
    pub ioctl: i64,
    pub sched_yield: i64,
    pub nanosleep: i64,
    pub clone: i64,
    pub exit: i64,
    pub wait4: i64,
    pub kill: i64,
    pub fsync: i64,
    pub unlinkat: i64,
    pub getdents64: i64,
    pub clock_gettime: i64,
    pub sched_setaffinity: i64,
    pub sched_getaffinity: i64,
    pub stat: i64,
    pub socket: i64,
    pub connect: i64,
    pub accept: i64,
    pub bind: i64,
    pub listen: i64,
    pub execve: i64,
    pub fcntl: i64,
    pub getcwd: i64,
    pub rt_sigaction: i64,
    pub iopl: i64,
    pub mkdirat: i64,
    pub sysinfo: i64,
}

mod nr_defaults {
    use crate::common::error::ERR_NOT_IMPLEMENTED;
    pub const READ: i64 = ERR_NOT_IMPLEMENTED;
    pub const WRITE: i64 = ERR_NOT_IMPLEMENTED;
    pub const OPENAT: i64 = ERR_NOT_IMPLEMENTED;
    pub const CLOSE: i64 = ERR_NOT_IMPLEMENTED;
    pub const MMAP: i64 = ERR_NOT_IMPLEMENTED;
    pub const MUNMAP: i64 = ERR_NOT_IMPLEMENTED;
    pub const IOCTL: i64 = ERR_NOT_IMPLEMENTED;
    pub const SCHED_YIELD: i64 = ERR_NOT_IMPLEMENTED;
    pub const NANOSLEEP: i64 = ERR_NOT_IMPLEMENTED;
    pub const CLONE: i64 = ERR_NOT_IMPLEMENTED;
    pub const EXIT: i64 = ERR_NOT_IMPLEMENTED;
    pub const WAIT4: i64 = ERR_NOT_IMPLEMENTED;
    pub const KILL: i64 = ERR_NOT_IMPLEMENTED;
    pub const FSYNC: i64 = ERR_NOT_IMPLEMENTED;
    pub const UNLINKAT: i64 = ERR_NOT_IMPLEMENTED;
    pub const GETDENTS64: i64 = ERR_NOT_IMPLEMENTED;
    pub const CLOCK_GETTIME: i64 = ERR_NOT_IMPLEMENTED;
    pub const SCHED_SETAFFINITY: i64 = ERR_NOT_IMPLEMENTED;
    pub const SCHED_GETAFFINITY: i64 = ERR_NOT_IMPLEMENTED;
    pub const STAT: i64 = ERR_NOT_IMPLEMENTED;
    pub const SOCKET: i64 = ERR_NOT_IMPLEMENTED;
    pub const CONNECT: i64 = ERR_NOT_IMPLEMENTED;
    pub const ACCEPT: i64 = ERR_NOT_IMPLEMENTED;
    pub const BIND: i64 = ERR_NOT_IMPLEMENTED;
    pub const LISTEN: i64 = ERR_NOT_IMPLEMENTED;
    pub const EXECVE: i64 = ERR_NOT_IMPLEMENTED;
    pub const FCNTL: i64 = ERR_NOT_IMPLEMENTED;
    pub const GETCWD: i64 = ERR_NOT_IMPLEMENTED;
    pub const RT_SIGACTION: i64 = ERR_NOT_IMPLEMENTED;
    pub const IOPL: i64 = ERR_NOT_IMPLEMENTED;
    pub const MKDIRAT: i64 = ERR_NOT_IMPLEMENTED;
    pub const SYSINFO: i64 = ERR_NOT_IMPLEMENTED;
}

static NR_READ: AtomicI64 = AtomicI64::new(nr_defaults::READ);
static NR_WRITE: AtomicI64 = AtomicI64::new(nr_defaults::WRITE);
static NR_OPENAT: AtomicI64 = AtomicI64::new(nr_defaults::OPENAT);
static NR_CLOSE: AtomicI64 = AtomicI64::new(nr_defaults::CLOSE);
static NR_MMAP: AtomicI64 = AtomicI64::new(nr_defaults::MMAP);
static NR_MUNMAP: AtomicI64 = AtomicI64::new(nr_defaults::MUNMAP);
static NR_IOCTL: AtomicI64 = AtomicI64::new(nr_defaults::IOCTL);
static NR_SCHED_YIELD: AtomicI64 = AtomicI64::new(nr_defaults::SCHED_YIELD);
static NR_NANOSLEEP: AtomicI64 = AtomicI64::new(nr_defaults::NANOSLEEP);
static NR_CLONE: AtomicI64 = AtomicI64::new(nr_defaults::CLONE);
static NR_EXIT: AtomicI64 = AtomicI64::new(nr_defaults::EXIT);
static NR_WAIT4: AtomicI64 = AtomicI64::new(nr_defaults::WAIT4);
static NR_KILL: AtomicI64 = AtomicI64::new(nr_defaults::KILL);
static NR_FSYNC: AtomicI64 = AtomicI64::new(nr_defaults::FSYNC);
static NR_UNLINKAT: AtomicI64 = AtomicI64::new(nr_defaults::UNLINKAT);
static NR_GETDENTS64: AtomicI64 = AtomicI64::new(nr_defaults::GETDENTS64);
static NR_CLOCK_GETTIME: AtomicI64 = AtomicI64::new(nr_defaults::CLOCK_GETTIME);
static NR_SCHED_SETAFFINITY: AtomicI64 = AtomicI64::new(nr_defaults::SCHED_SETAFFINITY);
static NR_SCHED_GETAFFINITY: AtomicI64 = AtomicI64::new(nr_defaults::SCHED_GETAFFINITY);
static NR_STAT: AtomicI64 = AtomicI64::new(nr_defaults::STAT);
static NR_SOCKET: AtomicI64 = AtomicI64::new(nr_defaults::SOCKET);
static NR_CONNECT: AtomicI64 = AtomicI64::new(nr_defaults::CONNECT);
static NR_ACCEPT: AtomicI64 = AtomicI64::new(nr_defaults::ACCEPT);
static NR_BIND: AtomicI64 = AtomicI64::new(nr_defaults::BIND);
static NR_LISTEN: AtomicI64 = AtomicI64::new(nr_defaults::LISTEN);
static NR_EXECVE: AtomicI64 = AtomicI64::new(nr_defaults::EXECVE);
static NR_FCNTL: AtomicI64 = AtomicI64::new(nr_defaults::FCNTL);
static NR_GETCWD: AtomicI64 = AtomicI64::new(nr_defaults::GETCWD);
static NR_RT_SIGACTION: AtomicI64 = AtomicI64::new(nr_defaults::RT_SIGACTION);
static NR_IOPL: AtomicI64 = AtomicI64::new(nr_defaults::IOPL);
static NR_MKDIRAT: AtomicI64 = AtomicI64::new(nr_defaults::MKDIRAT);
static NR_SYSINFO: AtomicI64 = AtomicI64::new(nr_defaults::SYSINFO);

pub fn set_syscall_nrs(t: &SyscallNrTable) {
    NR_READ.store(t.read, Ordering::Release);
    NR_WRITE.store(t.write, Ordering::Release);
    NR_OPENAT.store(t.openat, Ordering::Release);
    NR_CLOSE.store(t.close, Ordering::Release);
    NR_MMAP.store(t.mmap, Ordering::Release);
    NR_MUNMAP.store(t.munmap, Ordering::Release);
    NR_IOCTL.store(t.ioctl, Ordering::Release);
    NR_SCHED_YIELD.store(t.sched_yield, Ordering::Release);
    NR_NANOSLEEP.store(t.nanosleep, Ordering::Release);
    NR_CLONE.store(t.clone, Ordering::Release);
    NR_EXIT.store(t.exit, Ordering::Release);
    NR_WAIT4.store(t.wait4, Ordering::Release);
    NR_KILL.store(t.kill, Ordering::Release);
    NR_FSYNC.store(t.fsync, Ordering::Release);
    NR_UNLINKAT.store(t.unlinkat, Ordering::Release);
    NR_GETDENTS64.store(t.getdents64, Ordering::Release);
    NR_CLOCK_GETTIME.store(t.clock_gettime, Ordering::Release);
    NR_SCHED_SETAFFINITY.store(t.sched_setaffinity, Ordering::Release);
    NR_SCHED_GETAFFINITY.store(t.sched_getaffinity, Ordering::Release);
    NR_STAT.store(t.stat, Ordering::Release);
    NR_SOCKET.store(t.socket, Ordering::Release);
    NR_CONNECT.store(t.connect, Ordering::Release);
    NR_ACCEPT.store(t.accept, Ordering::Release);
    NR_BIND.store(t.bind, Ordering::Release);
    NR_LISTEN.store(t.listen, Ordering::Release);
    NR_EXECVE.store(t.execve, Ordering::Release);
    NR_FCNTL.store(t.fcntl, Ordering::Release);
    NR_GETCWD.store(t.getcwd, Ordering::Release);
    NR_RT_SIGACTION.store(t.rt_sigaction, Ordering::Release);
    NR_IOPL.store(t.iopl, Ordering::Release);
    NR_MKDIRAT.store(t.mkdirat, Ordering::Release);
    NR_SYSINFO.store(t.sysinfo, Ordering::Release);
}

type RawSyscallFn = unsafe extern "C" fn(i64, u64, u64, u64, u64, u64, u64) -> i64;
static RAW_SYSCALL_FN: OnceCopy<RawSyscallFn> = OnceCopy::new();

pub fn set_raw_syscall_fn(f: RawSyscallFn) -> bool {
    RAW_SYSCALL_FN.set(f)
}

#[repr(C, align(4))]
struct AlignedBlob<const N: usize>([u8; N]);

#[link_section = ".text"]
#[used]
static X86_64_SYSCALL_BLOB: AlignedBlob<26> = AlignedBlob([
    0x48, 0x89, 0xf8, 0x48, 0x89, 0xf7, 0x48, 0x89, 0xd6, 0x48, 0x89, 0xca, 0x4d, 0x89, 0xc2, 0x4d,
    0x89, 0xc8, 0x4c, 0x8b, 0x4c, 0x24, 0x08, 0x0f, 0x05, 0xc3,
]);

#[link_section = ".text"]
#[used]
static AARCH64_SYSCALL_BLOB: AlignedBlob<36> = AlignedBlob([
    0xe8, 0x03, 0x00, 0xaa, 0xe0, 0x03, 0x01, 0xaa, 0xe1, 0x03, 0x02, 0xaa, 0xe2, 0x03, 0x03, 0xaa,
    0xe3, 0x03, 0x04, 0xaa, 0xe4, 0x03, 0x05, 0xaa, 0xe5, 0x03, 0x06, 0xaa, 0x01, 0x00, 0x00, 0xd4,
    0xc0, 0x03, 0x5f, 0xd6,
]);

pub(crate) fn register_native_syscall() {
    let arch = super::detect_arch();
    match arch {
        crate::arch::Architecture::X86_64 => {
            let f: RawSyscallFn = unsafe { core::mem::transmute(X86_64_SYSCALL_BLOB.0.as_ptr()) };
            RAW_SYSCALL_FN.set(f);
        }
        crate::arch::Architecture::AArch64 => {
            let f: RawSyscallFn = unsafe { core::mem::transmute(AARCH64_SYSCALL_BLOB.0.as_ptr()) };
            RAW_SYSCALL_FN.set(f);
        }
        _ => {}
    }
}

/// # Safety
/// Caller must ensure `nr` is a valid syscall number and arguments are correct.
pub unsafe fn raw_syscall(nr: i64, a0: u64, a1: u64, a2: u64, a3: u64, a4: u64, a5: u64) -> i64 {
    if let Some(f) = RAW_SYSCALL_FN.get() {
        return f(nr, a0, a1, a2, a3, a4, a5);
    }
    super::init_shims();
    if let Some(f) = RAW_SYSCALL_FN.get() {
        return f(nr, a0, a1, a2, a3, a4, a5);
    }
    crate::common::error::ERR_NOT_IMPLEMENTED
}

pub fn nr_read() -> i64 {
    NR_READ.load(Ordering::Acquire)
}
pub fn nr_write() -> i64 {
    NR_WRITE.load(Ordering::Acquire)
}
pub fn nr_openat() -> i64 {
    NR_OPENAT.load(Ordering::Acquire)
}
pub fn nr_close() -> i64 {
    NR_CLOSE.load(Ordering::Acquire)
}
pub fn nr_mmap() -> i64 {
    NR_MMAP.load(Ordering::Acquire)
}
pub fn nr_munmap() -> i64 {
    NR_MUNMAP.load(Ordering::Acquire)
}
pub fn nr_ioctl() -> i64 {
    NR_IOCTL.load(Ordering::Acquire)
}
pub fn nr_sched_yield() -> i64 {
    NR_SCHED_YIELD.load(Ordering::Acquire)
}
pub fn nr_nanosleep() -> i64 {
    NR_NANOSLEEP.load(Ordering::Acquire)
}
pub fn nr_clone() -> i64 {
    NR_CLONE.load(Ordering::Acquire)
}
pub fn nr_exit() -> i64 {
    NR_EXIT.load(Ordering::Acquire)
}
pub fn nr_wait4() -> i64 {
    NR_WAIT4.load(Ordering::Acquire)
}
pub fn nr_kill() -> i64 {
    NR_KILL.load(Ordering::Acquire)
}
pub fn nr_fsync() -> i64 {
    NR_FSYNC.load(Ordering::Acquire)
}
pub fn nr_unlinkat() -> i64 {
    NR_UNLINKAT.load(Ordering::Acquire)
}
pub fn nr_getdents64() -> i64 {
    NR_GETDENTS64.load(Ordering::Acquire)
}
pub fn nr_clock_gettime() -> i64 {
    NR_CLOCK_GETTIME.load(Ordering::Acquire)
}
pub fn nr_sched_setaffinity() -> i64 {
    NR_SCHED_SETAFFINITY.load(Ordering::Acquire)
}
pub fn nr_sched_getaffinity() -> i64 {
    NR_SCHED_GETAFFINITY.load(Ordering::Acquire)
}
pub fn nr_stat() -> i64 {
    NR_STAT.load(Ordering::Acquire)
}
pub fn nr_socket() -> i64 {
    NR_SOCKET.load(Ordering::Acquire)
}
pub fn nr_connect() -> i64 {
    NR_CONNECT.load(Ordering::Acquire)
}
pub fn nr_accept() -> i64 {
    NR_ACCEPT.load(Ordering::Acquire)
}
pub fn nr_bind() -> i64 {
    NR_BIND.load(Ordering::Acquire)
}
pub fn nr_listen() -> i64 {
    NR_LISTEN.load(Ordering::Acquire)
}
pub fn nr_execve() -> i64 {
    NR_EXECVE.load(Ordering::Acquire)
}
pub fn nr_fcntl() -> i64 {
    NR_FCNTL.load(Ordering::Acquire)
}
pub fn nr_getcwd() -> i64 {
    NR_GETCWD.load(Ordering::Acquire)
}
pub fn nr_rt_sigaction() -> i64 {
    NR_RT_SIGACTION.load(Ordering::Acquire)
}
pub fn nr_iopl() -> i64 {
    NR_IOPL.load(Ordering::Acquire)
}
pub fn nr_mkdirat() -> i64 {
    NR_MKDIRAT.load(Ordering::Acquire)
}
pub fn nr_sysinfo() -> i64 {
    NR_SYSINFO.load(Ordering::Acquire)
}