Merge pull request #66 from amaanq/arch
Some checks failed
Rust / Test on aarch64-unknown-linux-gnu (push) Has been cancelled
Rust / Test on armv7-unknown-linux-gnueabihf (push) Has been cancelled
Rust / Test on i686-unknown-linux-gnu (push) Has been cancelled
Rust / Test on loongarch64-unknown-linux-gnu (push) Has been cancelled
Rust / Test on powerpc-unknown-linux-gnu (push) Has been cancelled
Rust / Test on powerpc64-unknown-linux-gnu (push) Has been cancelled
Rust / Test on powerpc64le-unknown-linux-gnu (push) Has been cancelled
Rust / Test on riscv64gc-unknown-linux-gnu (push) Has been cancelled
Rust / Test on s390x-unknown-linux-gnu (push) Has been cancelled
Rust / Test on sparc64-unknown-linux-gnu (push) Has been cancelled
Rust / Test on mips-unknown-linux-gnu (push) Has been cancelled
Rust / Test on mips64-unknown-linux-gnuabi64 (push) Has been cancelled
Rust / Test on riscv32gc-unknown-linux-gnu (push) Has been cancelled
Rust / Test on x86_64-unknown-linux-gnu (push) Has been cancelled

arch: add loongarch64, s390x, powerpc64, powerpc, armv7, riscv32, sparc64, sparc32, mips64, mips32, and i686 support
This commit is contained in:
raf 2026-04-22 01:51:21 +03:00 committed by GitHub
commit 6c7bc1bee2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 3058 additions and 508 deletions

View file

@ -16,10 +16,47 @@ jobs:
strategy:
fail-fast: false
matrix:
target:
- x86_64-unknown-linux-gnu
- aarch64-unknown-linux-gnu
- riscv64gc-unknown-linux-gnu
include:
- target: x86_64-unknown-linux-gnu
toolchain: stable
- target: aarch64-unknown-linux-gnu
toolchain: stable
- target: riscv64gc-unknown-linux-gnu
toolchain: stable
- target: loongarch64-unknown-linux-gnu
toolchain: stable
- target: s390x-unknown-linux-gnu
toolchain: stable
# powerpc64 uses experimental asm features
- target: powerpc64le-unknown-linux-gnu
toolchain: nightly
- target: powerpc64-unknown-linux-gnu
toolchain: nightly
- target: armv7-unknown-linux-gnueabihf
toolchain: stable
# riscv32 is tier-3
- target: riscv32gc-unknown-linux-gnu
toolchain: nightly
components: rust-src
build_std: true
# sparc64 uses experimental asm features
- target: sparc64-unknown-linux-gnu
toolchain: nightly
# mips64 is tier-3 and uses experimental asm features
- target: mips64-unknown-linux-gnuabi64
toolchain: nightly
components: rust-src
build_std: true
- target: i686-unknown-linux-gnu
toolchain: stable
# powerpc uses experimental asm features
- target: powerpc-unknown-linux-gnu
toolchain: nightly
# mips is tier-3 and uses experimental asm features
- target: mips-unknown-linux-gnu
toolchain: nightly
components: rust-src
build_std: true
steps:
- name: "Checkout"
@ -28,9 +65,14 @@ jobs:
- name: "Setup Rust toolchain"
uses: actions-rust-lang/setup-rust-toolchain@v1
with:
target: ${{ matrix.target }}
toolchain: ${{ matrix.toolchain }}
components: ${{ matrix.components || '' }}
rustflags: ""
- name: "Add Rust target"
if: ${{ !matrix.build_std }}
run: rustup target add ${{ matrix.target }}
- name: "Make Mold the default linker"
uses: rui314/setup-mold@v1
@ -40,7 +82,9 @@ jobs:
target: ${{ matrix.target }}
- name: "Build"
run: cargo build --verbose
run: cargo build --verbose --target ${{ matrix.target }} ${{ matrix.build_std && '-Z build-std=core,alloc,panic_abort' || '' }} ${{ matrix.extra_args || '' }}
# tier-3 targets have no prebuilt std and tests are arch-agnostic, so
# run them against the host toolchain instead of the cross target.
- name: "Run tests"
run: cargo test --workspace --exclude microfetch --verbose
run: cargo test --workspace --exclude microfetch --verbose ${{ matrix.build_std && '--target x86_64-unknown-linux-gnu' || '' }}

View file

@ -6,7 +6,7 @@ resolver = "3"
authors = [ "NotAShelf <raf@notashelf.dev>" ]
edition = "2024"
license = "GPL-3.0"
rust-version = "1.92.0"
rust-version = "1.95.0"
version = "1.1.0"
[workspace.dependencies]

164
crates/asm/src/aarch64.rs Normal file
View file

@ -0,0 +1,164 @@
//! Syscall implementations for `aarch64`.
use super::{StatfsBuf, SysInfo, UtsNameBuf};
pub(super) unsafe fn sys_open(path: *const u8, flags: i32) -> i32 {
unsafe {
let fd: i64;
core::arch::asm!(
"svc #0",
in("x8") 56i64, // SYS_openat
in("x0") -100i32, // AT_FDCWD
in("x1") path,
in("x2") flags,
in("x3") 0i32, // mode
lateout("x0") fd,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
fd as i32
}
}
}
pub(super) unsafe fn sys_read(fd: i32, buf: *mut u8, count: usize) -> isize {
unsafe {
let ret: i64;
core::arch::asm!(
"svc #0",
in("x8") 63i64, // SYS_read
in("x0") fd,
in("x1") buf,
in("x2") count,
lateout("x0") ret,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as isize
}
}
}
pub(super) unsafe fn sys_write(fd: i32, buf: *const u8, count: usize) -> isize {
unsafe {
let ret: i64;
core::arch::asm!(
"svc #0",
in("x8") 64i64, // SYS_write
in("x0") fd,
in("x1") buf,
in("x2") count,
lateout("x0") ret,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as isize
}
}
}
pub(super) unsafe fn sys_close(fd: i32) -> i32 {
unsafe {
let ret: i64;
core::arch::asm!(
"svc #0",
in("x8") 57i64, // SYS_close
in("x0") fd,
lateout("x0") ret,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as i32
}
}
}
pub(super) unsafe fn sys_uname(buf: *mut UtsNameBuf) -> i32 {
unsafe {
let ret: i64;
core::arch::asm!(
"svc #0",
in("x8") 160i64, // SYS_uname
in("x0") buf,
lateout("x0") ret,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as i32
}
}
}
pub(super) unsafe fn sys_statfs(path: *const u8, buf: *mut StatfsBuf) -> i32 {
unsafe {
let ret: i64;
core::arch::asm!(
"svc #0",
in("x8") 43i64, // SYS_statfs
in("x0") path,
in("x1") buf,
lateout("x0") ret,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as i32
}
}
}
pub(super) unsafe fn sys_sysinfo(info: *mut SysInfo) -> i64 {
unsafe {
let ret: i64;
core::arch::asm!(
"svc #0",
in("x8") 179_i64, // __NR_sysinfo
in("x0") info,
lateout("x0") ret,
options(nostack)
);
ret
}
}
pub(super) unsafe fn sys_sched_getaffinity(
pid: i32,
mask_size: usize,
mask: *mut u8,
) -> i32 {
unsafe {
let ret: i64;
core::arch::asm!(
"svc #0",
in("x8") 123i64, // __NR_sched_getaffinity
in("x0") pid,
in("x1") mask_size,
in("x2") mask,
lateout("x0") ret,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as i32
}
}
}
pub(super) unsafe fn sys_exit(code: i32) -> ! {
unsafe {
core::arch::asm!(
"svc #0",
in("x8") 93i64, // SYS_exit
in("x0") code,
options(noreturn, nostack)
);
}
}

140
crates/asm/src/arm.rs Normal file
View file

@ -0,0 +1,140 @@
//! Syscall implementations for `arm`.
use super::{StatfsBuf, SysInfo, UtsNameBuf};
pub(super) unsafe fn sys_open(path: *const u8, flags: i32) -> i32 {
unsafe {
let ret: i32;
core::arch::asm!(
"svc #0",
in("r7") 5i32, // SYS_open
in("r0") path,
in("r1") flags,
in("r2") 0i32, // mode
lateout("r0") ret,
options(nostack)
);
ret
}
}
pub(super) unsafe fn sys_read(fd: i32, buf: *mut u8, count: usize) -> isize {
unsafe {
let ret: i32;
core::arch::asm!(
"svc #0",
in("r7") 3i32, // SYS_read
in("r0") fd,
in("r1") buf,
in("r2") count,
lateout("r0") ret,
options(nostack)
);
ret as isize
}
}
pub(super) unsafe fn sys_write(fd: i32, buf: *const u8, count: usize) -> isize {
unsafe {
let ret: i32;
core::arch::asm!(
"svc #0",
in("r7") 4i32, // SYS_write
in("r0") fd,
in("r1") buf,
in("r2") count,
lateout("r0") ret,
options(nostack)
);
ret as isize
}
}
pub(super) unsafe fn sys_close(fd: i32) -> i32 {
unsafe {
let ret: i32;
core::arch::asm!(
"svc #0",
in("r7") 6i32, // SYS_close
in("r0") fd,
lateout("r0") ret,
options(nostack)
);
ret
}
}
pub(super) unsafe fn sys_uname(buf: *mut UtsNameBuf) -> i32 {
unsafe {
let ret: i32;
core::arch::asm!(
"svc #0",
in("r7") 122i32, // SYS_newuname
in("r0") buf,
lateout("r0") ret,
options(nostack)
);
ret
}
}
pub(super) unsafe fn sys_statfs(path: *const u8, buf: *mut StatfsBuf) -> i32 {
unsafe {
let ret: i32;
core::arch::asm!(
"svc #0",
in("r7") 266i32, // SYS_statfs64
in("r0") path,
in("r1") core::mem::size_of::<StatfsBuf>(),
in("r2") buf,
lateout("r0") ret,
options(nostack)
);
ret
}
}
pub(super) unsafe fn sys_sysinfo(info: *mut SysInfo) -> i64 {
unsafe {
let ret: i32;
core::arch::asm!(
"svc #0",
in("r7") 116_i32, // __NR_sysinfo
in("r0") info,
lateout("r0") ret,
options(nostack)
);
i64::from(ret)
}
}
pub(super) unsafe fn sys_sched_getaffinity(
pid: i32,
mask_size: usize,
mask: *mut u8,
) -> i32 {
unsafe {
let ret: i32;
core::arch::asm!(
"svc #0",
in("r7") 242i32, // __NR_sched_getaffinity
in("r0") pid,
in("r1") mask_size,
in("r2") mask,
lateout("r0") ret,
options(nostack)
);
ret
}
}
pub(super) unsafe fn sys_exit(code: i32) -> ! {
unsafe {
core::arch::asm!(
"svc #0",
in("r7") 1i32, // SYS_exit
in("r0") code,
options(noreturn, nostack)
);
}
}

View file

@ -5,19 +5,39 @@
//! What do you mean I wasted two whole hours to make the program only 100µs
//! faster?
//!
//! Supports `x86_64`, `aarch64`, and `riscv64` architectures.
//! Supports `x86_64`, `aarch64`, `riscv64`, `loongarch64`, `s390x`,
//! `powerpc64`, `arm` (armv7), `riscv32`, `sparc64`, `mips64`, `x86` (i686),
//! `powerpc` (ppc32), `sparc` (sparc32), and `mips` (O32) architectures.
#![no_std]
#![cfg_attr(
any(
target_arch = "sparc64",
target_arch = "sparc",
target_arch = "mips64",
target_arch = "mips"
),
feature(asm_experimental_arch)
)]
// Ensure we're compiling for a supported architecture.
#[cfg(not(any(
target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "riscv64"
)))]
compile_error!(
"Unsupported architecture: only x86_64, aarch64, and riscv64 are supported"
);
// Per-arch syscall implementations live in their own module files.
core::cfg_select! {
target_arch = "x86_64" => { #[path = "x86_64.rs" ] mod arch; }
target_arch = "aarch64" => { #[path = "aarch64.rs" ] mod arch; }
target_arch = "riscv64" => { #[path = "riscv64.rs" ] mod arch; }
target_arch = "loongarch64" => { #[path = "loongarch64.rs"] mod arch; }
target_arch = "s390x" => { #[path = "s390x.rs" ] mod arch; }
target_arch = "powerpc64" => { #[path = "powerpc64.rs" ] mod arch; }
target_arch = "arm" => { #[path = "arm.rs" ] mod arch; }
target_arch = "riscv32" => { #[path = "riscv32.rs" ] mod arch; }
target_arch = "sparc64" => { #[path = "sparc64.rs" ] mod arch; }
target_arch = "mips64" => { #[path = "mips64.rs" ] mod arch; }
target_arch = "x86" => { #[path = "x86.rs" ] mod arch; }
target_arch = "powerpc" => { #[path = "powerpc.rs" ] mod arch; }
target_arch = "sparc" => { #[path = "sparc.rs" ] mod arch; }
target_arch = "mips" => { #[path = "mips.rs" ] mod arch; }
_ => { compile_error!("Unsupported architecture"); }
}
/// Copies `n` bytes from `src` to `dest`.
///
@ -165,62 +185,7 @@ unsafe extern "C" {
#[inline]
#[must_use]
pub unsafe fn sys_open(path: *const u8, flags: i32) -> i32 {
#[cfg(target_arch = "x86_64")]
unsafe {
let fd: i64;
core::arch::asm!(
"syscall",
in("rax") 2i64, // SYS_open
in("rdi") path,
in("rsi") flags,
in("rdx") 0i32, // mode (not used for reading)
lateout("rax") fd,
lateout("rcx") _,
lateout("r11") _,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
fd as i32
}
}
#[cfg(target_arch = "aarch64")]
unsafe {
let fd: i64;
core::arch::asm!(
"svc #0",
in("x8") 56i64, // SYS_openat
in("x0") -100i32, // AT_FDCWD
in("x1") path,
in("x2") flags,
in("x3") 0i32, // mode
lateout("x0") fd,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
fd as i32
}
}
#[cfg(target_arch = "riscv64")]
unsafe {
let fd: i64;
core::arch::asm!(
"ecall",
in("a7") 56i64, // SYS_openat
in("a0") -100i32, // AT_FDCWD
in("a1") path,
in("a2") flags,
in("a3") 0i32, // mode
lateout("a0") fd,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
fd as i32
}
}
unsafe { arch::sys_open(path, flags) }
}
/// Direct syscall to read from a file descriptor
@ -237,64 +202,7 @@ pub unsafe fn sys_open(path: *const u8, flags: i32) -> i32 {
/// - `fd` is a valid open file descriptor
#[inline]
pub unsafe fn sys_read(fd: i32, buf: *mut u8, count: usize) -> isize {
#[cfg(target_arch = "x86_64")]
unsafe {
let ret: i64;
core::arch::asm!(
"syscall",
in("rax") 0i64, // SYS_read
in("rdi") fd,
in("rsi") buf,
in("rdx") count,
lateout("rax") ret,
lateout("rcx") _,
lateout("r11") _,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as isize
}
}
#[cfg(target_arch = "aarch64")]
unsafe {
let ret: i64;
core::arch::asm!(
"svc #0",
in("x8") 63i64, // SYS_read
in("x0") fd,
in("x1") buf,
in("x2") count,
lateout("x0") ret,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as isize
}
}
#[cfg(target_arch = "riscv64")]
unsafe {
let ret: i64;
core::arch::asm!(
"ecall",
in("a7") 63i64, // SYS_read
in("a0") fd,
in("a1") buf,
in("a2") count,
lateout("a0") ret,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as isize
}
}
unsafe { arch::sys_read(fd, buf, count) }
}
/// Direct syscall to write to a file descriptor
@ -312,64 +220,7 @@ pub unsafe fn sys_read(fd: i32, buf: *mut u8, count: usize) -> isize {
#[inline]
#[must_use]
pub unsafe fn sys_write(fd: i32, buf: *const u8, count: usize) -> isize {
#[cfg(target_arch = "x86_64")]
unsafe {
let ret: i64;
core::arch::asm!(
"syscall",
in("rax") 1i64, // SYS_write
in("rdi") fd,
in("rsi") buf,
in("rdx") count,
lateout("rax") ret,
lateout("rcx") _,
lateout("r11") _,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as isize
}
}
#[cfg(target_arch = "aarch64")]
unsafe {
let ret: i64;
core::arch::asm!(
"svc #0",
in("x8") 64i64, // SYS_write
in("x0") fd,
in("x1") buf,
in("x2") count,
lateout("x0") ret,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as isize
}
}
#[cfg(target_arch = "riscv64")]
unsafe {
let ret: i64;
core::arch::asm!(
"ecall",
in("a7") 64i64, // SYS_write
in("a0") fd,
in("a1") buf,
in("a2") count,
lateout("a0") ret,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as isize
}
}
unsafe { arch::sys_write(fd, buf, count) }
}
/// Direct syscall to close a file descriptor
@ -380,55 +231,7 @@ pub unsafe fn sys_write(fd: i32, buf: *const u8, count: usize) -> isize {
#[inline]
#[must_use]
pub unsafe fn sys_close(fd: i32) -> i32 {
#[cfg(target_arch = "x86_64")]
unsafe {
let ret: i64;
core::arch::asm!(
"syscall",
in("rax") 3i64, // SYS_close
in("rdi") fd,
lateout("rax") ret,
lateout("rcx") _,
lateout("r11") _,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as i32
}
}
#[cfg(target_arch = "aarch64")]
unsafe {
let ret: i64;
core::arch::asm!(
"svc #0",
in("x8") 57i64, // SYS_close
in("x0") fd,
lateout("x0") ret,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as i32
}
}
#[cfg(target_arch = "riscv64")]
unsafe {
let ret: i64;
core::arch::asm!(
"ecall",
in("a7") 57i64, // SYS_close
in("a0") fd,
lateout("a0") ret,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as i32
}
}
unsafe { arch::sys_close(fd) }
}
/// Raw buffer for the `uname(2)` syscall.
@ -459,57 +262,7 @@ pub struct UtsNameBuf {
#[inline]
#[allow(dead_code)]
pub unsafe fn sys_uname(buf: *mut UtsNameBuf) -> i32 {
#[cfg(target_arch = "x86_64")]
unsafe {
let ret: i64;
core::arch::asm!(
"syscall",
in("rax") 63i64, // SYS_uname
in("rdi") buf,
lateout("rax") ret,
lateout("rcx") _,
lateout("r11") _,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as i32
}
}
#[cfg(target_arch = "aarch64")]
unsafe {
let ret: i64;
core::arch::asm!(
"svc #0",
in("x8") 160i64, // SYS_uname
in("x0") buf,
lateout("x0") ret,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as i32
}
}
#[cfg(target_arch = "riscv64")]
unsafe {
let ret: i64;
core::arch::asm!(
"ecall",
in("a7") 160i64, // SYS_uname
in("a0") buf,
lateout("a0") ret,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as i32
}
}
unsafe { arch::sys_uname(buf) }
}
/// Raw buffer for the `statfs(2)` syscall.
@ -518,6 +271,16 @@ pub unsafe fn sys_uname(buf: *mut UtsNameBuf) -> i32 {
/// offsets on both architectures. Only the fields needed for disk usage are
/// declared; the remainder of the 120-byte struct is covered by `_pad`.
#[repr(C)]
#[cfg(not(any(
target_arch = "s390x",
target_arch = "arm",
target_arch = "riscv32",
target_arch = "x86",
target_arch = "powerpc",
target_arch = "sparc",
target_arch = "mips64",
target_arch = "mips"
)))]
pub struct StatfsBuf {
pub f_type: i64,
pub f_bsize: i64,
@ -535,6 +298,96 @@ pub struct StatfsBuf {
pub _pad: [i64; 4],
}
/// on s390x `f_type` and `f_bsize` are 32-bit.
#[repr(C)]
#[cfg(target_arch = "s390x")]
pub struct StatfsBuf {
pub f_type: u32,
pub f_bsize: u32,
pub f_blocks: u64,
pub f_bfree: u64,
pub f_bavail: u64,
pub f_files: u64,
pub f_ffree: u64,
pub f_fsid: [i32; 2],
pub f_namelen: u32,
pub f_frsize: u32,
pub f_flags: u32,
#[allow(clippy::pub_underscore_fields, reason = "This is not a public API")]
pub _pad: [u32; 5],
}
/// on armv7 `statfs64(2)` has 32-bit word fields; see
/// https://github.com/torvalds/linux/blob/v6.19/include/uapi/asm-generic/statfs.h
#[repr(C)]
#[cfg(any(
target_arch = "arm",
target_arch = "riscv32",
target_arch = "x86",
target_arch = "powerpc",
target_arch = "sparc"
))]
pub struct StatfsBuf {
pub f_type: u32,
pub f_bsize: u32,
pub f_blocks: u64,
pub f_bfree: u64,
pub f_bavail: u64,
pub f_files: u64,
pub f_ffree: u64,
pub f_fsid: [i32; 2],
pub f_namelen: u32,
pub f_frsize: u32,
pub f_flags: u32,
#[allow(clippy::pub_underscore_fields, reason = "This is not a public API")]
pub _pad: [u32; 4],
}
/// mips (O32) uses `compat_statfs64`, same reordering with 32-bit words; see
/// https://github.com/torvalds/linux/blob/v6.19/arch/mips/include/uapi/asm/statfs.h
#[repr(C)]
#[cfg(target_arch = "mips")]
pub struct StatfsBuf {
pub f_type: u32,
pub f_bsize: u32,
pub f_frsize: u32,
_pad: u32,
pub f_blocks: u64,
pub f_bfree: u64,
pub f_files: u64,
pub f_ffree: u64,
pub f_bavail: u64,
pub f_fsid: [i32; 2],
pub f_namelen: u32,
pub f_flags: u32,
#[allow(clippy::pub_underscore_fields, reason = "This is not a public API")]
pub _pad2: [u32; 5],
}
/// mips reorders fields; see
/// https://github.com/torvalds/linux/blob/v6.19/arch/mips/include/uapi/asm/statfs.h
#[repr(C)]
#[cfg(target_arch = "mips64")]
pub struct StatfsBuf {
pub f_type: i64,
pub f_bsize: i64,
pub f_frsize: i64,
pub f_blocks: u64,
pub f_bfree: u64,
pub f_files: u64,
pub f_ffree: u64,
pub f_bavail: u64,
pub f_fsid: [i32; 2],
pub f_namelen: i64,
pub f_flags: i64,
#[allow(clippy::pub_underscore_fields, reason = "This is not a public API")]
pub _pad: [i64; 5],
}
/// Direct `statfs(2)` syscall
///
/// # Returns
@ -549,61 +402,7 @@ pub struct StatfsBuf {
/// - `buf` points to a valid `StatfsBuf`
#[inline]
pub unsafe fn sys_statfs(path: *const u8, buf: *mut StatfsBuf) -> i32 {
#[cfg(target_arch = "x86_64")]
unsafe {
let ret: i64;
core::arch::asm!(
"syscall",
in("rax") 137i64, // SYS_statfs
in("rdi") path,
in("rsi") buf,
lateout("rax") ret,
lateout("rcx") _,
lateout("r11") _,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as i32
}
}
#[cfg(target_arch = "aarch64")]
unsafe {
let ret: i64;
core::arch::asm!(
"svc #0",
in("x8") 43i64, // SYS_statfs
in("x0") path,
in("x1") buf,
lateout("x0") ret,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as i32
}
}
#[cfg(target_arch = "riscv64")]
unsafe {
let ret: i64;
core::arch::asm!(
"ecall",
in("a7") 43i64, // SYS_statfs
in("a0") path,
in("a1") buf,
lateout("a0") ret,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as i32
}
}
unsafe { arch::sys_statfs(path, buf) }
}
/// Read entire file using direct syscalls. This avoids libc overhead and can be
@ -658,6 +457,14 @@ pub fn read_file_fast(path: &str, buffer: &mut [u8]) -> Result<usize, i32> {
/// The layout matches the kernel's `struct sysinfo` *exactly*:
/// `mem_unit` ends at offset 108, then 4 bytes of implicit padding to 112.
#[repr(C)]
#[cfg(not(any(
target_arch = "arm",
target_arch = "riscv32",
target_arch = "x86",
target_arch = "powerpc",
target_arch = "sparc",
target_arch = "mips"
)))]
pub struct SysInfo {
pub uptime: i64,
pub loads: [u64; 3],
@ -678,6 +485,35 @@ pub struct SysInfo {
// needed
}
/// on armv7 `__kernel_long_t` is 4 bytes; see
/// https://github.com/torvalds/linux/blob/v6.19/include/uapi/linux/sysinfo.h
#[repr(C)]
#[cfg(any(
target_arch = "arm",
target_arch = "riscv32",
target_arch = "x86",
target_arch = "powerpc",
target_arch = "sparc",
target_arch = "mips"
))]
pub struct SysInfo {
pub uptime: i32,
pub loads: [u32; 3],
pub totalram: u32,
pub freeram: u32,
pub sharedram: u32,
pub bufferram: u32,
pub totalswap: u32,
pub freeswap: u32,
pub procs: u16,
_pad: u16,
pub totalhigh: u32,
pub freehigh: u32,
pub mem_unit: u32,
#[allow(clippy::pub_underscore_fields, reason = "This is not a public API")]
pub _f: [u8; 8],
}
/// Direct `sysinfo(2)` syscall
///
/// # Returns
@ -689,46 +525,7 @@ pub struct SysInfo {
/// The caller must ensure that `info` points to a valid `SysInfo` buffer.
#[inline]
pub unsafe fn sys_sysinfo(info: *mut SysInfo) -> i64 {
#[cfg(target_arch = "x86_64")]
unsafe {
let ret: i64;
core::arch::asm!(
"syscall",
in("rax") 99_i64, // __NR_sysinfo
in("rdi") info,
out("rcx") _,
out("r11") _,
lateout("rax") ret,
options(nostack)
);
ret
}
#[cfg(target_arch = "aarch64")]
unsafe {
let ret: i64;
core::arch::asm!(
"svc #0",
in("x8") 179_i64, // __NR_sysinfo
in("x0") info,
lateout("x0") ret,
options(nostack)
);
ret
}
#[cfg(target_arch = "riscv64")]
unsafe {
let ret: i64;
core::arch::asm!(
"ecall",
in("a7") 179_i64, // __NR_sysinfo
in("a0") info,
lateout("a0") ret,
options(nostack)
);
ret
}
unsafe { arch::sys_sysinfo(info) }
}
/// Direct `sched_getaffinity(2)` syscall
@ -748,61 +545,7 @@ pub unsafe fn sys_sched_getaffinity(
mask_size: usize,
mask: *mut u8,
) -> i32 {
#[cfg(target_arch = "x86_64")]
unsafe {
let ret: i64;
core::arch::asm!(
"syscall",
in("rax") 204i64, // __NR_sched_getaffinity
in("rdi") pid,
in("rsi") mask_size,
in("rdx") mask,
lateout("rax") ret,
lateout("rcx") _,
lateout("r11") _,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as i32
}
}
#[cfg(target_arch = "aarch64")]
unsafe {
let ret: i64;
core::arch::asm!(
"svc #0",
in("x8") 123i64, // __NR_sched_getaffinity
in("x0") pid,
in("x1") mask_size,
in("x2") mask,
lateout("x0") ret,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as i32
}
}
#[cfg(target_arch = "riscv64")]
unsafe {
let ret: i64;
core::arch::asm!(
"ecall",
in("a7") 123i64, // __NR_sched_getaffinity
in("a0") pid,
in("a1") mask_size,
in("a2") mask,
lateout("a0") ret,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as i32
}
}
unsafe { arch::sys_sched_getaffinity(pid, mask_size, mask) }
}
/// Direct syscall to exit the process
@ -812,33 +555,5 @@ pub unsafe fn sys_sched_getaffinity(
/// This syscall never returns. The process will terminate immediately.
#[inline]
pub unsafe fn sys_exit(code: i32) -> ! {
#[cfg(target_arch = "x86_64")]
unsafe {
core::arch::asm!(
"syscall",
in("rax") 60i64, // SYS_exit
in("rdi") code,
options(noreturn, nostack)
);
}
#[cfg(target_arch = "aarch64")]
unsafe {
core::arch::asm!(
"svc #0",
in("x8") 93i64, // SYS_exit
in("x0") code,
options(noreturn, nostack)
);
}
#[cfg(target_arch = "riscv64")]
unsafe {
core::arch::asm!(
"ecall",
in("a7") 93i64, // SYS_exit
in("a0") code,
options(noreturn, nostack)
);
}
unsafe { arch::sys_exit(code) }
}

View file

@ -0,0 +1,161 @@
//! Syscall implementations for `loongarch64`.
use super::{StatfsBuf, SysInfo, UtsNameBuf};
pub(super) unsafe fn sys_open(path: *const u8, flags: i32) -> i32 {
unsafe {
let fd: i64;
core::arch::asm!(
"syscall 0",
in("$a7") 56i64, // SYS_openat
in("$a0") -100i32, // AT_FDCWD
in("$a1") path,
in("$a2") flags,
in("$a3") 0i32, // mode
lateout("$a0") fd,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
fd as i32
}
}
}
pub(super) unsafe fn sys_read(fd: i32, buf: *mut u8, count: usize) -> isize {
unsafe {
let ret: i64;
core::arch::asm!(
"syscall 0",
in("$a7") 63i64, // SYS_read
in("$a0") fd,
in("$a1") buf,
in("$a2") count,
lateout("$a0") ret,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as isize
}
}
}
pub(super) unsafe fn sys_write(fd: i32, buf: *const u8, count: usize) -> isize {
unsafe {
let ret: i64;
core::arch::asm!(
"syscall 0",
in("$a7") 64i64, // SYS_write
in("$a0") fd,
in("$a1") buf,
in("$a2") count,
lateout("$a0") ret,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as isize
}
}
}
pub(super) unsafe fn sys_close(fd: i32) -> i32 {
unsafe {
let ret: i64;
core::arch::asm!(
"syscall 0",
in("$a7") 57i64, // SYS_close
in("$a0") fd,
lateout("$a0") ret,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as i32
}
}
}
pub(super) unsafe fn sys_uname(buf: *mut UtsNameBuf) -> i32 {
unsafe {
let ret: i64;
core::arch::asm!(
"syscall 0",
in("$a7") 160i64, // SYS_uname
in("$a0") buf,
lateout("$a0") ret,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as i32
}
}
}
pub(super) unsafe fn sys_statfs(path: *const u8, buf: *mut StatfsBuf) -> i32 {
unsafe {
let ret: i64;
core::arch::asm!(
"syscall 0",
in("$a7") 43i64, // SYS_statfs
in("$a0") path,
in("$a1") buf,
lateout("$a0") ret,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as i32
}
}
}
pub(super) unsafe fn sys_sysinfo(info: *mut SysInfo) -> i64 {
unsafe {
let ret: i64;
core::arch::asm!(
"syscall 0",
in("$a7") 179_i64, // __NR_sysinfo
in("$a0") info,
lateout("$a0") ret,
options(nostack)
);
ret
}
}
pub(super) unsafe fn sys_sched_getaffinity(
pid: i32,
mask_size: usize,
mask: *mut u8,
) -> i32 {
unsafe {
let ret: i64;
core::arch::asm!(
"syscall 0",
in("$a7") 123i64, // __NR_sched_getaffinity
in("$a0") pid,
in("$a1") mask_size,
in("$a2") mask,
lateout("$a0") ret,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as i32
}
}
}
pub(super) unsafe fn sys_exit(code: i32) -> ! {
unsafe {
core::arch::asm!(
"syscall 0",
in("$a7") 93i64, // SYS_exit
in("$a0") code,
options(noreturn, nostack)
);
}
}

196
crates/asm/src/mips.rs Normal file
View file

@ -0,0 +1,196 @@
//! Syscall implementations for `mips`.
use super::{StatfsBuf, SysInfo, UtsNameBuf};
pub(super) unsafe fn sys_open(path: *const u8, flags: i32) -> i32 {
unsafe {
let ret: i32;
core::arch::asm!(
"syscall",
"beqz $7, 1f",
"nop",
"subu $2, $0, $2",
"1:",
inlateout("$2") 4000i32 + 5 => ret, // SYS_open
in("$4") path,
in("$5") flags,
in("$6") 0i32, // mode
lateout("$7") _,
lateout("$8") _, lateout("$9") _, lateout("$10") _, lateout("$11") _,
lateout("$12") _, lateout("$13") _, lateout("$14") _, lateout("$15") _,
lateout("$24") _, lateout("$25") _,
options(nostack)
);
ret
}
}
pub(super) unsafe fn sys_read(fd: i32, buf: *mut u8, count: usize) -> isize {
unsafe {
let ret: i32;
core::arch::asm!(
"syscall",
"beqz $7, 1f",
"nop",
"subu $2, $0, $2",
"1:",
inlateout("$2") 4000i32 + 3 => ret, // SYS_read
in("$4") fd,
in("$5") buf,
in("$6") count,
lateout("$7") _,
lateout("$8") _, lateout("$9") _, lateout("$10") _, lateout("$11") _,
lateout("$12") _, lateout("$13") _, lateout("$14") _, lateout("$15") _,
lateout("$24") _, lateout("$25") _,
options(nostack)
);
ret as isize
}
}
pub(super) unsafe fn sys_write(fd: i32, buf: *const u8, count: usize) -> isize {
unsafe {
let ret: i32;
core::arch::asm!(
"syscall",
"beqz $7, 1f",
"nop",
"subu $2, $0, $2",
"1:",
inlateout("$2") 4000i32 + 4 => ret, // SYS_write
in("$4") fd,
in("$5") buf,
in("$6") count,
lateout("$7") _,
lateout("$8") _, lateout("$9") _, lateout("$10") _, lateout("$11") _,
lateout("$12") _, lateout("$13") _, lateout("$14") _, lateout("$15") _,
lateout("$24") _, lateout("$25") _,
options(nostack)
);
ret as isize
}
}
pub(super) unsafe fn sys_close(fd: i32) -> i32 {
unsafe {
let ret: i32;
core::arch::asm!(
"syscall",
"beqz $7, 1f",
"nop",
"subu $2, $0, $2",
"1:",
inlateout("$2") 4000i32 + 6 => ret, // SYS_close
in("$4") fd,
lateout("$7") _,
lateout("$8") _, lateout("$9") _, lateout("$10") _, lateout("$11") _,
lateout("$12") _, lateout("$13") _, lateout("$14") _, lateout("$15") _,
lateout("$24") _, lateout("$25") _,
options(nostack)
);
ret
}
}
pub(super) unsafe fn sys_uname(buf: *mut UtsNameBuf) -> i32 {
unsafe {
let ret: i32;
core::arch::asm!(
"syscall",
"beqz $7, 1f",
"nop",
"subu $2, $0, $2",
"1:",
inlateout("$2") 4000i32 + 122 => ret, // SYS_newuname
in("$4") buf,
lateout("$7") _,
lateout("$8") _, lateout("$9") _, lateout("$10") _, lateout("$11") _,
lateout("$12") _, lateout("$13") _, lateout("$14") _, lateout("$15") _,
lateout("$24") _, lateout("$25") _,
options(nostack)
);
ret
}
}
pub(super) unsafe fn sys_statfs(path: *const u8, buf: *mut StatfsBuf) -> i32 {
unsafe {
let ret: i32;
core::arch::asm!(
"syscall",
"beqz $7, 1f",
"nop",
"subu $2, $0, $2",
"1:",
inlateout("$2") 4000i32 + 255 => ret, // SYS_statfs64
in("$4") path,
in("$5") core::mem::size_of::<StatfsBuf>(),
in("$6") buf,
lateout("$7") _,
lateout("$8") _, lateout("$9") _, lateout("$10") _, lateout("$11") _,
lateout("$12") _, lateout("$13") _, lateout("$14") _, lateout("$15") _,
lateout("$24") _, lateout("$25") _,
options(nostack)
);
ret
}
}
pub(super) unsafe fn sys_sysinfo(info: *mut SysInfo) -> i64 {
unsafe {
let ret: i32;
core::arch::asm!(
"syscall",
"beqz $7, 1f",
"nop",
"subu $2, $0, $2",
"1:",
inlateout("$2") 4000_i32 + 116 => ret, // __NR_sysinfo
in("$4") info,
lateout("$7") _,
lateout("$8") _, lateout("$9") _, lateout("$10") _, lateout("$11") _,
lateout("$12") _, lateout("$13") _, lateout("$14") _, lateout("$15") _,
lateout("$24") _, lateout("$25") _,
options(nostack)
);
i64::from(ret)
}
}
pub(super) unsafe fn sys_sched_getaffinity(
pid: i32,
mask_size: usize,
mask: *mut u8,
) -> i32 {
unsafe {
let ret: i32;
core::arch::asm!(
"syscall",
"beqz $7, 1f",
"nop",
"subu $2, $0, $2",
"1:",
inlateout("$2") 4000i32 + 240 => ret, // __NR_sched_getaffinity
in("$4") pid,
in("$5") mask_size,
in("$6") mask,
lateout("$7") _,
lateout("$8") _, lateout("$9") _, lateout("$10") _, lateout("$11") _,
lateout("$12") _, lateout("$13") _, lateout("$14") _, lateout("$15") _,
lateout("$24") _, lateout("$25") _,
options(nostack)
);
ret
}
}
pub(super) unsafe fn sys_exit(code: i32) -> ! {
unsafe {
core::arch::asm!(
"syscall",
in("$2") 4000i32 + 1, // SYS_exit
in("$4") code,
options(noreturn, nostack)
);
}
}

206
crates/asm/src/mips64.rs Normal file
View file

@ -0,0 +1,206 @@
//! Syscall implementations for `mips64`.
use super::{StatfsBuf, SysInfo, UtsNameBuf};
pub(super) unsafe fn sys_open(path: *const u8, flags: i32) -> i32 {
unsafe {
let ret: i64;
core::arch::asm!(
"syscall",
"beqz $7, 1f",
"nop",
"dsubu $2, $0, $2",
"1:",
inlateout("$2") 5000i64 + 2 => ret, // SYS_open
in("$4") path,
in("$5") flags,
in("$6") 0i32, // mode
lateout("$7") _,
lateout("$8") _, lateout("$9") _, lateout("$10") _, lateout("$11") _,
lateout("$12") _, lateout("$13") _, lateout("$14") _, lateout("$15") _,
lateout("$24") _, lateout("$25") _,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as i32
}
}
}
pub(super) unsafe fn sys_read(fd: i32, buf: *mut u8, count: usize) -> isize {
unsafe {
let ret: i64;
core::arch::asm!(
"syscall",
"beqz $7, 1f",
"nop",
"dsubu $2, $0, $2",
"1:",
inlateout("$2") 5000i64 + 0 => ret, // SYS_read
in("$4") fd,
in("$5") buf,
in("$6") count,
lateout("$7") _,
lateout("$8") _, lateout("$9") _, lateout("$10") _, lateout("$11") _,
lateout("$12") _, lateout("$13") _, lateout("$14") _, lateout("$15") _,
lateout("$24") _, lateout("$25") _,
options(nostack)
);
ret as isize
}
}
pub(super) unsafe fn sys_write(fd: i32, buf: *const u8, count: usize) -> isize {
unsafe {
let ret: i64;
core::arch::asm!(
"syscall",
"beqz $7, 1f",
"nop",
"dsubu $2, $0, $2",
"1:",
inlateout("$2") 5000i64 + 1 => ret, // SYS_write
in("$4") fd,
in("$5") buf,
in("$6") count,
lateout("$7") _,
lateout("$8") _, lateout("$9") _, lateout("$10") _, lateout("$11") _,
lateout("$12") _, lateout("$13") _, lateout("$14") _, lateout("$15") _,
lateout("$24") _, lateout("$25") _,
options(nostack)
);
ret as isize
}
}
pub(super) unsafe fn sys_close(fd: i32) -> i32 {
unsafe {
let ret: i64;
core::arch::asm!(
"syscall",
"beqz $7, 1f",
"nop",
"dsubu $2, $0, $2",
"1:",
inlateout("$2") 5000i64 + 3 => ret, // SYS_close
in("$4") fd,
lateout("$7") _,
lateout("$8") _, lateout("$9") _, lateout("$10") _, lateout("$11") _,
lateout("$12") _, lateout("$13") _, lateout("$14") _, lateout("$15") _,
lateout("$24") _, lateout("$25") _,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as i32
}
}
}
pub(super) unsafe fn sys_uname(buf: *mut UtsNameBuf) -> i32 {
unsafe {
let ret: i64;
core::arch::asm!(
"syscall",
"beqz $7, 1f",
"nop",
"dsubu $2, $0, $2",
"1:",
inlateout("$2") 5000i64 + 61 => ret, // SYS_newuname
in("$4") buf,
lateout("$7") _,
lateout("$8") _, lateout("$9") _, lateout("$10") _, lateout("$11") _,
lateout("$12") _, lateout("$13") _, lateout("$14") _, lateout("$15") _,
lateout("$24") _, lateout("$25") _,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as i32
}
}
}
pub(super) unsafe fn sys_statfs(path: *const u8, buf: *mut StatfsBuf) -> i32 {
unsafe {
let ret: i64;
core::arch::asm!(
"syscall",
"beqz $7, 1f",
"nop",
"dsubu $2, $0, $2",
"1:",
inlateout("$2") 5000i64 + 134 => ret, // SYS_statfs
in("$4") path,
in("$5") buf,
lateout("$7") _,
lateout("$8") _, lateout("$9") _, lateout("$10") _, lateout("$11") _,
lateout("$12") _, lateout("$13") _, lateout("$14") _, lateout("$15") _,
lateout("$24") _, lateout("$25") _,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as i32
}
}
}
pub(super) unsafe fn sys_sysinfo(info: *mut SysInfo) -> i64 {
unsafe {
let ret: i64;
core::arch::asm!(
"syscall",
"beqz $7, 1f",
"nop",
"dsubu $2, $0, $2",
"1:",
inlateout("$2") 5000_i64 + 97 => ret, // SYS_sysinfo
in("$4") info,
lateout("$7") _,
lateout("$8") _, lateout("$9") _, lateout("$10") _, lateout("$11") _,
lateout("$12") _, lateout("$13") _, lateout("$14") _, lateout("$15") _,
lateout("$24") _, lateout("$25") _,
options(nostack)
);
ret
}
}
pub(super) unsafe fn sys_sched_getaffinity(
pid: i32,
mask_size: usize,
mask: *mut u8,
) -> i32 {
unsafe {
let ret: i64;
core::arch::asm!(
"syscall",
inlateout("$2") 5000i64 + 196 => ret, // __NR_sched_getaffinity
in("$4") pid,
in("$5") mask_size,
in("$6") mask,
lateout("$7") _,
lateout("$8") _, lateout("$9") _, lateout("$10") _, lateout("$11") _,
lateout("$12") _, lateout("$13") _, lateout("$14") _, lateout("$15") _,
lateout("$24") _, lateout("$25") _,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as i32
}
}
}
pub(super) unsafe fn sys_exit(code: i32) -> ! {
unsafe {
core::arch::asm!(
"syscall",
in("$2") 5000i64 + 58, // SYS_exit
in("$4") code,
options(noreturn, nostack)
);
}
}

180
crates/asm/src/powerpc.rs Normal file
View file

@ -0,0 +1,180 @@
//! Syscall implementations for `powerpc`.
use super::{StatfsBuf, SysInfo, UtsNameBuf};
pub(super) unsafe fn sys_open(path: *const u8, flags: i32) -> i32 {
unsafe {
let ret: i32;
core::arch::asm!(
"sc",
"bns+ 2f",
"neg 3, 3",
"2:",
inlateout("r0") 5i32 => _, // SYS_open
inlateout("r3") path => ret,
inlateout("r4") flags => _,
inlateout("r5") 0i32 => _,
out("r6") _, out("r7") _, out("r8") _,
out("r9") _, out("r10") _, out("r11") _, out("r12") _,
out("cr0") _,
options(nostack)
);
ret
}
}
pub(super) unsafe fn sys_read(fd: i32, buf: *mut u8, count: usize) -> isize {
unsafe {
let ret: i32;
core::arch::asm!(
"sc",
"bns+ 2f",
"neg 3, 3",
"2:",
inlateout("r0") 3i32 => _, // SYS_read
inlateout("r3") fd => ret,
inlateout("r4") buf => _,
inlateout("r5") count => _,
out("r6") _, out("r7") _, out("r8") _,
out("r9") _, out("r10") _, out("r11") _, out("r12") _,
out("cr0") _,
options(nostack)
);
ret as isize
}
}
pub(super) unsafe fn sys_write(fd: i32, buf: *const u8, count: usize) -> isize {
unsafe {
let ret: i32;
core::arch::asm!(
"sc",
"bns+ 2f",
"neg 3, 3",
"2:",
inlateout("r0") 4i32 => _, // SYS_write
inlateout("r3") fd => ret,
inlateout("r4") buf => _,
inlateout("r5") count => _,
out("r6") _, out("r7") _, out("r8") _,
out("r9") _, out("r10") _, out("r11") _, out("r12") _,
out("cr0") _,
options(nostack)
);
ret as isize
}
}
pub(super) unsafe fn sys_close(fd: i32) -> i32 {
unsafe {
let ret: i32;
core::arch::asm!(
"sc",
"bns+ 2f",
"neg 3, 3",
"2:",
inlateout("r0") 6i32 => _, // SYS_close
inlateout("r3") fd => ret,
out("r4") _, out("r5") _, out("r6") _, out("r7") _, out("r8") _,
out("r9") _, out("r10") _, out("r11") _, out("r12") _,
out("cr0") _,
options(nostack)
);
ret
}
}
pub(super) unsafe fn sys_uname(buf: *mut UtsNameBuf) -> i32 {
unsafe {
let ret: i32;
core::arch::asm!(
"sc",
"bns+ 2f",
"neg 3, 3",
"2:",
inlateout("r0") 122i32 => _, // SYS_newuname
inlateout("r3") buf => ret,
out("r4") _, out("r5") _, out("r6") _, out("r7") _, out("r8") _,
out("r9") _, out("r10") _, out("r11") _, out("r12") _,
out("cr0") _,
options(nostack)
);
ret
}
}
pub(super) unsafe fn sys_statfs(path: *const u8, buf: *mut StatfsBuf) -> i32 {
unsafe {
let ret: i32;
core::arch::asm!(
"sc",
"bns+ 2f",
"neg 3, 3",
"2:",
inlateout("r0") 252i32 => _, // SYS_statfs64
inlateout("r3") path => ret,
inlateout("r4") core::mem::size_of::<StatfsBuf>() => _,
inlateout("r5") buf => _,
out("r6") _, out("r7") _, out("r8") _,
out("r9") _, out("r10") _, out("r11") _, out("r12") _,
out("cr0") _,
options(nostack)
);
ret
}
}
pub(super) unsafe fn sys_sysinfo(info: *mut SysInfo) -> i64 {
unsafe {
let ret: i32;
core::arch::asm!(
"sc",
"bns+ 2f",
"neg 3, 3",
"2:",
inlateout("r0") 116_i32 => _, // __NR_sysinfo
inlateout("r3") info => ret,
out("r4") _, out("r5") _, out("r6") _, out("r7") _, out("r8") _,
out("r9") _, out("r10") _, out("r11") _, out("r12") _,
out("cr0") _,
options(nostack)
);
i64::from(ret)
}
}
pub(super) unsafe fn sys_sched_getaffinity(
pid: i32,
mask_size: usize,
mask: *mut u8,
) -> i32 {
unsafe {
let ret: i32;
core::arch::asm!(
"sc",
"bns+ 2f",
"neg 3, 3",
"2:",
inlateout("r0") 223i32 => _, // __NR_sched_getaffinity
inlateout("r3") pid => ret,
inlateout("r4") mask_size => _,
inlateout("r5") mask => _,
out("r6") _, out("r7") _, out("r8") _,
out("r9") _, out("r10") _, out("r11") _, out("r12") _,
out("cr0") _,
options(nostack)
);
ret
}
}
pub(super) unsafe fn sys_exit(code: i32) -> ! {
unsafe {
core::arch::asm!(
"li 0, 1", // SYS_exit
"sc",
in("r3") code,
options(noreturn, nostack)
);
}
}

200
crates/asm/src/powerpc64.rs Normal file
View file

@ -0,0 +1,200 @@
//! Syscall implementations for `powerpc64`.
use super::{StatfsBuf, SysInfo, UtsNameBuf};
pub(super) unsafe fn sys_open(path: *const u8, flags: i32) -> i32 {
unsafe {
let ret: i64;
core::arch::asm!(
"sc",
"bns+ 2f",
"neg 3, 3",
"2:",
inlateout("r0") 5i64 => _, // SYS_open
inlateout("r3") path => ret,
inlateout("r4") flags => _,
inlateout("r5") 0i32 => _, // mode
out("r6") _, out("r7") _, out("r8") _,
out("r9") _, out("r10") _, out("r11") _, out("r12") _,
out("cr0") _,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as i32
}
}
}
pub(super) unsafe fn sys_read(fd: i32, buf: *mut u8, count: usize) -> isize {
unsafe {
let ret: i64;
core::arch::asm!(
"sc",
"bns+ 2f",
"neg 3, 3",
"2:",
inlateout("r0") 3i64 => _, // SYS_read
inlateout("r3") fd as i64 => ret,
inlateout("r4") buf => _,
inlateout("r5") count => _,
out("r6") _, out("r7") _, out("r8") _,
out("r9") _, out("r10") _, out("r11") _, out("r12") _,
out("cr0") _,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as isize
}
}
}
pub(super) unsafe fn sys_write(fd: i32, buf: *const u8, count: usize) -> isize {
unsafe {
let ret: i64;
core::arch::asm!(
"sc",
"bns+ 2f",
"neg 3, 3",
"2:",
inlateout("r0") 4i64 => _, // SYS_write
inlateout("r3") fd as i64 => ret,
inlateout("r4") buf => _,
inlateout("r5") count => _,
out("r6") _, out("r7") _, out("r8") _,
out("r9") _, out("r10") _, out("r11") _, out("r12") _,
out("cr0") _,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as isize
}
}
}
pub(super) unsafe fn sys_close(fd: i32) -> i32 {
unsafe {
let ret: i64;
core::arch::asm!(
"sc",
"bns+ 2f",
"neg 3, 3",
"2:",
inlateout("r0") 6i64 => _, // SYS_close
inlateout("r3") fd as i64 => ret,
out("r4") _, out("r5") _, out("r6") _, out("r7") _, out("r8") _,
out("r9") _, out("r10") _, out("r11") _, out("r12") _,
out("cr0") _,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as i32
}
}
}
pub(super) unsafe fn sys_uname(buf: *mut UtsNameBuf) -> i32 {
unsafe {
let ret: i64;
core::arch::asm!(
"sc",
"bns+ 2f",
"neg 3, 3",
"2:",
inlateout("r0") 122i64 => _, // SYS_uname
inlateout("r3") buf => ret,
out("r4") _, out("r5") _, out("r6") _, out("r7") _, out("r8") _,
out("r9") _, out("r10") _, out("r11") _, out("r12") _,
out("cr0") _,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as i32
}
}
}
pub(super) unsafe fn sys_statfs(path: *const u8, buf: *mut StatfsBuf) -> i32 {
unsafe {
let ret: i64;
core::arch::asm!(
"sc",
"bns+ 2f",
"neg 3, 3",
"2:",
inlateout("r0") 99i64 => _, // SYS_statfs
inlateout("r3") path => ret,
inlateout("r4") buf => _,
out("r5") _, out("r6") _, out("r7") _, out("r8") _,
out("r9") _, out("r10") _, out("r11") _, out("r12") _,
out("cr0") _,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as i32
}
}
}
pub(super) unsafe fn sys_sysinfo(info: *mut SysInfo) -> i64 {
unsafe {
let ret: i64;
core::arch::asm!(
"sc",
"bns+ 2f",
"neg 3, 3",
"2:",
inlateout("r0") 116_i64 => _, // __NR_sysinfo
inlateout("r3") info => ret,
out("r4") _, out("r5") _, out("r6") _, out("r7") _, out("r8") _,
out("r9") _, out("r10") _, out("r11") _, out("r12") _,
out("cr0") _,
options(nostack)
);
ret
}
}
pub(super) unsafe fn sys_sched_getaffinity(
pid: i32,
mask_size: usize,
mask: *mut u8,
) -> i32 {
unsafe {
let ret: i64;
core::arch::asm!(
"sc",
"bns+ 2f",
"neg 3, 3",
"2:",
inlateout("r0") 223i64 => _, // __NR_sched_getaffinity
inlateout("r3") pid as i64 => ret,
inlateout("r4") mask_size => _,
inlateout("r5") mask => _,
out("r6") _, out("r7") _, out("r8") _,
out("r9") _, out("r10") _, out("r11") _, out("r12") _,
out("cr0") _,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as i32
}
}
}
pub(super) unsafe fn sys_exit(code: i32) -> ! {
unsafe {
core::arch::asm!(
"li 0, 1", // SYS_exit
"sc",
in("r3") code,
options(noreturn, nostack)
);
}
}

142
crates/asm/src/riscv32.rs Normal file
View file

@ -0,0 +1,142 @@
//! Syscall implementations for `riscv32`.
use super::{StatfsBuf, SysInfo, UtsNameBuf};
pub(super) unsafe fn sys_open(path: *const u8, flags: i32) -> i32 {
unsafe {
let ret: i32;
core::arch::asm!(
"ecall",
in("a7") 56i32, // SYS_openat
in("a0") -100i32, // AT_FDCWD
in("a1") path,
in("a2") flags,
in("a3") 0i32, // mode
lateout("a0") ret,
options(nostack)
);
ret
}
}
pub(super) unsafe fn sys_read(fd: i32, buf: *mut u8, count: usize) -> isize {
unsafe {
let ret: i32;
core::arch::asm!(
"ecall",
in("a7") 63i32, // SYS_read
in("a0") fd,
in("a1") buf,
in("a2") count,
lateout("a0") ret,
options(nostack)
);
ret as isize
}
}
pub(super) unsafe fn sys_write(fd: i32, buf: *const u8, count: usize) -> isize {
unsafe {
let ret: i32;
core::arch::asm!(
"ecall",
in("a7") 64i32, // SYS_write
in("a0") fd,
in("a1") buf,
in("a2") count,
lateout("a0") ret,
options(nostack)
);
ret as isize
}
}
pub(super) unsafe fn sys_close(fd: i32) -> i32 {
unsafe {
let ret: i32;
core::arch::asm!(
"ecall",
in("a7") 57i32, // SYS_close
in("a0") fd,
lateout("a0") ret,
options(nostack)
);
ret
}
}
pub(super) unsafe fn sys_uname(buf: *mut UtsNameBuf) -> i32 {
unsafe {
let ret: i32;
core::arch::asm!(
"ecall",
in("a7") 160i32, // SYS_uname
in("a0") buf,
lateout("a0") ret,
options(nostack)
);
ret
}
}
pub(super) unsafe fn sys_statfs(path: *const u8, buf: *mut StatfsBuf) -> i32 {
unsafe {
// asm-generic __NR_statfs routes to sys_statfs64 on 32-bit: 3-arg form.
let ret: i32;
core::arch::asm!(
"ecall",
in("a7") 43i32, // __NR_statfs
in("a0") path,
in("a1") core::mem::size_of::<StatfsBuf>(),
in("a2") buf,
lateout("a0") ret,
options(nostack)
);
ret
}
}
pub(super) unsafe fn sys_sysinfo(info: *mut SysInfo) -> i64 {
unsafe {
let ret: i32;
core::arch::asm!(
"ecall",
in("a7") 179_i32, // __NR_sysinfo
in("a0") info,
lateout("a0") ret,
options(nostack)
);
i64::from(ret)
}
}
pub(super) unsafe fn sys_sched_getaffinity(
pid: i32,
mask_size: usize,
mask: *mut u8,
) -> i32 {
unsafe {
let ret: i32;
core::arch::asm!(
"ecall",
in("a7") 123i32, // __NR_sched_getaffinity
in("a0") pid,
in("a1") mask_size,
in("a2") mask,
lateout("a0") ret,
options(nostack)
);
ret
}
}
pub(super) unsafe fn sys_exit(code: i32) -> ! {
unsafe {
core::arch::asm!(
"ecall",
in("a7") 93i32, // SYS_exit
in("a0") code,
options(noreturn, nostack)
);
}
}

166
crates/asm/src/riscv64.rs Normal file
View file

@ -0,0 +1,166 @@
//! Syscall implementations for `riscv64`.
use super::{StatfsBuf, SysInfo, UtsNameBuf};
pub(super) unsafe fn sys_open(path: *const u8, flags: i32) -> i32 {
unsafe {
let fd: i64;
core::arch::asm!(
"ecall",
in("a7") 56i64, // SYS_openat
in("a0") -100i32, // AT_FDCWD
in("a1") path,
in("a2") flags,
in("a3") 0i32, // mode
lateout("a0") fd,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
fd as i32
}
}
}
pub(super) unsafe fn sys_read(fd: i32, buf: *mut u8, count: usize) -> isize {
unsafe {
let ret: i64;
core::arch::asm!(
"ecall",
in("a7") 63i64, // SYS_read
in("a0") fd,
in("a1") buf,
in("a2") count,
lateout("a0") ret,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as isize
}
}
}
pub(super) unsafe fn sys_write(fd: i32, buf: *const u8, count: usize) -> isize {
unsafe {
let ret: i64;
core::arch::asm!(
"ecall",
in("a7") 64i64, // SYS_write
in("a0") fd,
in("a1") buf,
in("a2") count,
lateout("a0") ret,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as isize
}
}
}
pub(super) unsafe fn sys_close(fd: i32) -> i32 {
unsafe {
let ret: i64;
core::arch::asm!(
"ecall",
in("a7") 57i64, // SYS_close
in("a0") fd,
lateout("a0") ret,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as i32
}
}
}
pub(super) unsafe fn sys_uname(buf: *mut UtsNameBuf) -> i32 {
unsafe {
let ret: i64;
core::arch::asm!(
"ecall",
in("a7") 160i64, // SYS_uname
in("a0") buf,
lateout("a0") ret,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as i32
}
}
}
pub(super) unsafe fn sys_statfs(path: *const u8, buf: *mut StatfsBuf) -> i32 {
unsafe {
let ret: i64;
core::arch::asm!(
"ecall",
in("a7") 43i64, // SYS_statfs
in("a0") path,
in("a1") buf,
lateout("a0") ret,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as i32
}
}
}
pub(super) unsafe fn sys_sysinfo(info: *mut SysInfo) -> i64 {
unsafe {
let ret: i64;
core::arch::asm!(
"ecall",
in("a7") 179_i64, // __NR_sysinfo
in("a0") info,
lateout("a0") ret,
options(nostack)
);
ret
}
}
pub(super) unsafe fn sys_sched_getaffinity(
pid: i32,
mask_size: usize,
mask: *mut u8,
) -> i32 {
unsafe {
let ret: i64;
core::arch::asm!(
"ecall",
in("a7") 123i64, // __NR_sched_getaffinity
in("a0") pid,
in("a1") mask_size,
in("a2") mask,
lateout("a0") ret,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as i32
}
}
}
pub(super) unsafe fn sys_exit(code: i32) -> ! {
unsafe {
core::arch::asm!(
"ecall",
in("a7") 93i64, // SYS_exit
in("a0") code,
options(noreturn, nostack)
);
}
}

160
crates/asm/src/s390x.rs Normal file
View file

@ -0,0 +1,160 @@
//! Syscall implementations for `s390x`.
use super::{StatfsBuf, SysInfo, UtsNameBuf};
pub(super) unsafe fn sys_open(path: *const u8, flags: i32) -> i32 {
unsafe {
let fd: i64;
core::arch::asm!(
"svc 0",
in("r1") 5i64, // SYS_open
in("r2") path,
in("r3") flags,
in("r4") 0i32, // mode
lateout("r2") fd,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
fd as i32
}
}
}
pub(super) unsafe fn sys_read(fd: i32, buf: *mut u8, count: usize) -> isize {
unsafe {
let ret: i64;
core::arch::asm!(
"svc 0",
in("r1") 3i64, // SYS_read
in("r2") fd,
in("r3") buf,
in("r4") count,
lateout("r2") ret,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as isize
}
}
}
pub(super) unsafe fn sys_write(fd: i32, buf: *const u8, count: usize) -> isize {
unsafe {
let ret: i64;
core::arch::asm!(
"svc 0",
in("r1") 4i64, // SYS_write
in("r2") fd,
in("r3") buf,
in("r4") count,
lateout("r2") ret,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as isize
}
}
}
pub(super) unsafe fn sys_close(fd: i32) -> i32 {
unsafe {
let ret: i64;
core::arch::asm!(
"svc 0",
in("r1") 6i64, // SYS_close
in("r2") fd,
lateout("r2") ret,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as i32
}
}
}
pub(super) unsafe fn sys_uname(buf: *mut UtsNameBuf) -> i32 {
unsafe {
let ret: i64;
core::arch::asm!(
"svc 0",
in("r1") 122i64, // SYS_uname
in("r2") buf,
lateout("r2") ret,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as i32
}
}
}
pub(super) unsafe fn sys_statfs(path: *const u8, buf: *mut StatfsBuf) -> i32 {
unsafe {
let ret: i64;
core::arch::asm!(
"svc 0",
in("r1") 99i64, // SYS_statfs
in("r2") path,
in("r3") buf,
lateout("r2") ret,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as i32
}
}
}
pub(super) unsafe fn sys_sysinfo(info: *mut SysInfo) -> i64 {
unsafe {
let ret: i64;
core::arch::asm!(
"svc 0",
in("r1") 116_i64, // __NR_sysinfo
in("r2") info,
lateout("r2") ret,
options(nostack)
);
ret
}
}
pub(super) unsafe fn sys_sched_getaffinity(
pid: i32,
mask_size: usize,
mask: *mut u8,
) -> i32 {
unsafe {
let ret: i64;
core::arch::asm!(
"svc 0",
in("r1") 240i64, // __NR_sched_getaffinity
in("r2") pid,
in("r3") mask_size,
in("r4") mask,
lateout("r2") ret,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as i32
}
}
}
pub(super) unsafe fn sys_exit(code: i32) -> ! {
unsafe {
core::arch::asm!(
"svc 0",
in("r1") 1i64, // SYS_exit
in("r2") code,
options(noreturn, nostack)
);
}
}

165
crates/asm/src/sparc.rs Normal file
View file

@ -0,0 +1,165 @@
//! Syscall implementations for `sparc`.
use super::{StatfsBuf, SysInfo, UtsNameBuf};
pub(super) unsafe fn sys_open(path: *const u8, flags: i32) -> i32 {
unsafe {
let ret: i32;
core::arch::asm!(
"mov {nr}, %g1",
"ta 0x10",
"bcs,a 1f",
"sub %g0, %o0, %o0",
"1:",
nr = in(reg) 5i32, // SYS_open
inlateout("o0") path => ret,
in("o1") flags,
in("o2") 0i32, // mode
options(nostack)
);
ret
}
}
pub(super) unsafe fn sys_read(fd: i32, buf: *mut u8, count: usize) -> isize {
unsafe {
let ret: i32;
core::arch::asm!(
"mov {nr}, %g1",
"ta 0x10",
"bcs,a 1f",
"sub %g0, %o0, %o0",
"1:",
nr = in(reg) 3i32, // SYS_read
inlateout("o0") fd => ret,
in("o1") buf,
in("o2") count,
options(nostack)
);
ret as isize
}
}
pub(super) unsafe fn sys_write(fd: i32, buf: *const u8, count: usize) -> isize {
unsafe {
let ret: i32;
core::arch::asm!(
"mov {nr}, %g1",
"ta 0x10",
"bcs,a 1f",
"sub %g0, %o0, %o0",
"1:",
nr = in(reg) 4i32, // SYS_write
inlateout("o0") fd => ret,
in("o1") buf,
in("o2") count,
options(nostack)
);
ret as isize
}
}
pub(super) unsafe fn sys_close(fd: i32) -> i32 {
unsafe {
let ret: i32;
core::arch::asm!(
"mov {nr}, %g1",
"ta 0x10",
"bcs,a 1f",
"sub %g0, %o0, %o0",
"1:",
nr = in(reg) 6i32, // SYS_close
inlateout("o0") fd => ret,
options(nostack)
);
ret
}
}
pub(super) unsafe fn sys_uname(buf: *mut UtsNameBuf) -> i32 {
unsafe {
let ret: i32;
core::arch::asm!(
"mov {nr}, %g1",
"ta 0x10",
"bcs,a 1f",
"sub %g0, %o0, %o0",
"1:",
nr = in(reg) 189i32, // SYS_newuname
inlateout("o0") buf => ret,
options(nostack)
);
ret
}
}
pub(super) unsafe fn sys_statfs(path: *const u8, buf: *mut StatfsBuf) -> i32 {
unsafe {
let ret: i32;
core::arch::asm!(
"mov {nr}, %g1",
"ta 0x10",
"bcs,a 1f",
"sub %g0, %o0, %o0",
"1:",
nr = in(reg) 234i32, // SYS_statfs64
inlateout("o0") path => ret,
in("o1") core::mem::size_of::<StatfsBuf>(),
in("o2") buf,
options(nostack)
);
ret
}
}
pub(super) unsafe fn sys_sysinfo(info: *mut SysInfo) -> i64 {
unsafe {
let ret: i32;
core::arch::asm!(
"mov {nr}, %g1",
"ta 0x10",
"bcs,a 1f",
"sub %g0, %o0, %o0",
"1:",
nr = in(reg) 214_i32, // __NR_sysinfo
inlateout("o0") info => ret,
options(nostack)
);
i64::from(ret)
}
}
pub(super) unsafe fn sys_sched_getaffinity(
pid: i32,
mask_size: usize,
mask: *mut u8,
) -> i32 {
unsafe {
let ret: i32;
core::arch::asm!(
"mov {nr}, %g1",
"ta 0x10",
"bcs,a 1f",
"sub %g0, %o0, %o0",
"1:",
nr = in(reg) 260i32, // __NR_sched_getaffinity
inlateout("o0") pid => ret,
in("o1") mask_size,
in("o2") mask,
options(nostack)
);
ret
}
}
pub(super) unsafe fn sys_exit(code: i32) -> ! {
unsafe {
core::arch::asm!(
"mov {nr}, %g1",
"ta 0x10",
nr = in(reg) 1i32, // SYS_exit
in("o0") code,
options(noreturn, nostack)
);
}
}

179
crates/asm/src/sparc64.rs Normal file
View file

@ -0,0 +1,179 @@
//! Syscall implementations for `sparc64`.
use super::{StatfsBuf, SysInfo, UtsNameBuf};
pub(super) unsafe fn sys_open(path: *const u8, flags: i32) -> i32 {
unsafe {
let ret: i64;
core::arch::asm!(
"mov {nr}, %g1",
"t 0x6d",
"bcs,a %xcc, 1f",
"sub %g0, %o0, %o0",
"1:",
nr = in(reg) 5i64, // SYS_open
inlateout("o0") path => ret,
in("o1") flags,
in("o2") 0i32, // mode
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as i32
}
}
}
pub(super) unsafe fn sys_read(fd: i32, buf: *mut u8, count: usize) -> isize {
unsafe {
let ret: i64;
core::arch::asm!(
"mov {nr}, %g1",
"t 0x6d",
"bcs,a %xcc, 1f",
"sub %g0, %o0, %o0",
"1:",
nr = in(reg) 3i64, // SYS_read
inlateout("o0") fd as i64 => ret,
in("o1") buf,
in("o2") count,
options(nostack)
);
ret as isize
}
}
pub(super) unsafe fn sys_write(fd: i32, buf: *const u8, count: usize) -> isize {
unsafe {
let ret: i64;
core::arch::asm!(
"mov {nr}, %g1",
"t 0x6d",
"bcs,a %xcc, 1f",
"sub %g0, %o0, %o0",
"1:",
nr = in(reg) 4i64, // SYS_write
inlateout("o0") fd as i64 => ret,
in("o1") buf,
in("o2") count,
options(nostack)
);
ret as isize
}
}
pub(super) unsafe fn sys_close(fd: i32) -> i32 {
unsafe {
let ret: i64;
core::arch::asm!(
"mov {nr}, %g1",
"t 0x6d",
"bcs,a %xcc, 1f",
"sub %g0, %o0, %o0",
"1:",
nr = in(reg) 6i64, // SYS_close
inlateout("o0") fd as i64 => ret,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as i32
}
}
}
pub(super) unsafe fn sys_uname(buf: *mut UtsNameBuf) -> i32 {
unsafe {
let ret: i64;
core::arch::asm!(
"mov {nr}, %g1",
"t 0x6d",
"bcs,a %xcc, 1f",
"sub %g0, %o0, %o0",
"1:",
nr = in(reg) 189i64, // SYS_newuname
inlateout("o0") buf => ret,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as i32
}
}
}
pub(super) unsafe fn sys_statfs(path: *const u8, buf: *mut StatfsBuf) -> i32 {
unsafe {
let ret: i64;
core::arch::asm!(
"mov {nr}, %g1",
"t 0x6d",
"bcs,a %xcc, 1f",
"sub %g0, %o0, %o0",
"1:",
nr = in(reg) 157i64, // SYS_statfs
inlateout("o0") path => ret,
in("o1") buf,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as i32
}
}
}
pub(super) unsafe fn sys_sysinfo(info: *mut SysInfo) -> i64 {
unsafe {
let ret: i64;
core::arch::asm!(
"mov {nr}, %g1",
"t 0x6d",
"bcs,a %xcc, 1f",
"sub %g0, %o0, %o0",
"1:",
nr = in(reg) 214_i64, // __NR_sysinfo
inlateout("o0") info => ret,
options(nostack)
);
ret
}
}
pub(super) unsafe fn sys_sched_getaffinity(
pid: i32,
mask_size: usize,
mask: *mut u8,
) -> i32 {
unsafe {
let ret: i64;
core::arch::asm!(
"mov {nr}, %g1",
"t 0x6d",
"bcs,a %xcc, 1f",
"sub %g0, %o0, %o0",
"1:",
nr = in(reg) 260i64, // __NR_sched_getaffinity
inlateout("o0") pid as i64 => ret,
in("o1") mask_size,
in("o2") mask,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as i32
}
}
}
pub(super) unsafe fn sys_exit(code: i32) -> ! {
unsafe {
core::arch::asm!(
"mov {nr}, %g1",
"t 0x6d",
nr = in(reg) 1i64, // SYS_exit
in("o0") code,
options(noreturn, nostack)
);
}
}

149
crates/asm/src/x86.rs Normal file
View file

@ -0,0 +1,149 @@
//! Syscall implementations for `x86`.
use super::{StatfsBuf, SysInfo, UtsNameBuf};
pub(super) unsafe fn sys_open(path: *const u8, flags: i32) -> i32 {
unsafe {
let ret: i32;
core::arch::asm!(
"push ebx",
"mov ebx, {arg1:e}",
"int 0x80",
"pop ebx",
arg1 = in(reg) path,
inlateout("eax") 5i32 => ret, // SYS_open
in("ecx") flags,
in("edx") 0i32, // mode
);
ret
}
}
pub(super) unsafe fn sys_read(fd: i32, buf: *mut u8, count: usize) -> isize {
unsafe {
let ret: i32;
core::arch::asm!(
"push ebx",
"mov ebx, {arg1:e}",
"int 0x80",
"pop ebx",
arg1 = in(reg) fd,
inlateout("eax") 3i32 => ret, // SYS_read
in("ecx") buf,
in("edx") count,
);
ret as isize
}
}
pub(super) unsafe fn sys_write(fd: i32, buf: *const u8, count: usize) -> isize {
unsafe {
let ret: i32;
core::arch::asm!(
"push ebx",
"mov ebx, {arg1:e}",
"int 0x80",
"pop ebx",
arg1 = in(reg) fd,
inlateout("eax") 4i32 => ret, // SYS_write
in("ecx") buf,
in("edx") count,
);
ret as isize
}
}
pub(super) unsafe fn sys_close(fd: i32) -> i32 {
unsafe {
let ret: i32;
core::arch::asm!(
"push ebx",
"mov ebx, {arg1:e}",
"int 0x80",
"pop ebx",
arg1 = in(reg) fd,
inlateout("eax") 6i32 => ret, // SYS_close
);
ret
}
}
pub(super) unsafe fn sys_uname(buf: *mut UtsNameBuf) -> i32 {
unsafe {
let ret: i32;
core::arch::asm!(
"push ebx",
"mov ebx, {arg1:e}",
"int 0x80",
"pop ebx",
arg1 = in(reg) buf,
inlateout("eax") 122i32 => ret, // SYS_newuname
);
ret
}
}
pub(super) unsafe fn sys_statfs(path: *const u8, buf: *mut StatfsBuf) -> i32 {
unsafe {
let ret: i32;
core::arch::asm!(
"push ebx",
"mov ebx, {arg1:e}",
"int 0x80",
"pop ebx",
arg1 = in(reg) path,
inlateout("eax") 268i32 => ret, // SYS_statfs64
in("ecx") core::mem::size_of::<StatfsBuf>(),
in("edx") buf,
);
ret
}
}
pub(super) unsafe fn sys_sysinfo(info: *mut SysInfo) -> i64 {
unsafe {
let ret: i32;
core::arch::asm!(
"push ebx",
"mov ebx, {arg1:e}",
"int 0x80",
"pop ebx",
arg1 = in(reg) info,
inlateout("eax") 116_i32 => ret, // __NR_sysinfo
);
i64::from(ret)
}
}
pub(super) unsafe fn sys_sched_getaffinity(
pid: i32,
mask_size: usize,
mask: *mut u8,
) -> i32 {
unsafe {
let ret: i32;
core::arch::asm!(
"push ebx",
"mov ebx, {arg1:e}",
"int 0x80",
"pop ebx",
arg1 = in(reg) pid,
inlateout("eax") 242i32 => ret, // __NR_sched_getaffinity
in("ecx") mask_size,
in("edx") mask,
);
ret
}
}
pub(super) unsafe fn sys_exit(code: i32) -> ! {
unsafe {
core::arch::asm!(
"mov ebx, {code:e}",
"int 0x80",
code = in(reg) code,
in("eax") 1i32, // SYS_exit
options(noreturn)
);
}
}

180
crates/asm/src/x86_64.rs Normal file
View file

@ -0,0 +1,180 @@
//! Syscall implementations for `x86_64`.
use super::{StatfsBuf, SysInfo, UtsNameBuf};
pub(super) unsafe fn sys_open(path: *const u8, flags: i32) -> i32 {
unsafe {
let fd: i64;
core::arch::asm!(
"syscall",
in("rax") 2i64, // SYS_open
in("rdi") path,
in("rsi") flags,
in("rdx") 0i32, // mode (not used for reading)
lateout("rax") fd,
lateout("rcx") _,
lateout("r11") _,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
fd as i32
}
}
}
pub(super) unsafe fn sys_read(fd: i32, buf: *mut u8, count: usize) -> isize {
unsafe {
let ret: i64;
core::arch::asm!(
"syscall",
in("rax") 0i64, // SYS_read
in("rdi") fd,
in("rsi") buf,
in("rdx") count,
lateout("rax") ret,
lateout("rcx") _,
lateout("r11") _,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as isize
}
}
}
pub(super) unsafe fn sys_write(fd: i32, buf: *const u8, count: usize) -> isize {
unsafe {
let ret: i64;
core::arch::asm!(
"syscall",
in("rax") 1i64, // SYS_write
in("rdi") fd,
in("rsi") buf,
in("rdx") count,
lateout("rax") ret,
lateout("rcx") _,
lateout("r11") _,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as isize
}
}
}
pub(super) unsafe fn sys_close(fd: i32) -> i32 {
unsafe {
let ret: i64;
core::arch::asm!(
"syscall",
in("rax") 3i64, // SYS_close
in("rdi") fd,
lateout("rax") ret,
lateout("rcx") _,
lateout("r11") _,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as i32
}
}
}
pub(super) unsafe fn sys_uname(buf: *mut UtsNameBuf) -> i32 {
unsafe {
let ret: i64;
core::arch::asm!(
"syscall",
in("rax") 63i64, // SYS_uname
in("rdi") buf,
lateout("rax") ret,
lateout("rcx") _,
lateout("r11") _,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as i32
}
}
}
pub(super) unsafe fn sys_statfs(path: *const u8, buf: *mut StatfsBuf) -> i32 {
unsafe {
let ret: i64;
core::arch::asm!(
"syscall",
in("rax") 137i64, // SYS_statfs
in("rdi") path,
in("rsi") buf,
lateout("rax") ret,
lateout("rcx") _,
lateout("r11") _,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as i32
}
}
}
pub(super) unsafe fn sys_sysinfo(info: *mut SysInfo) -> i64 {
unsafe {
let ret: i64;
core::arch::asm!(
"syscall",
in("rax") 99_i64, // __NR_sysinfo
in("rdi") info,
out("rcx") _,
out("r11") _,
lateout("rax") ret,
options(nostack)
);
ret
}
}
pub(super) unsafe fn sys_sched_getaffinity(
pid: i32,
mask_size: usize,
mask: *mut u8,
) -> i32 {
unsafe {
let ret: i64;
core::arch::asm!(
"syscall",
in("rax") 204i64, // __NR_sched_getaffinity
in("rdi") pid,
in("rsi") mask_size,
in("rdx") mask,
lateout("rax") ret,
lateout("rcx") _,
lateout("r11") _,
options(nostack)
);
#[allow(clippy::cast_possible_truncation)]
{
ret as i32
}
}
}
pub(super) unsafe fn sys_exit(code: i32) -> ! {
unsafe {
core::arch::asm!(
"syscall",
in("rax") 60i64, // SYS_exit
in("rdi") code,
options(noreturn, nostack)
);
}
}

View file

@ -121,29 +121,78 @@ fn parse_num(data: &[u8], i: &mut usize) -> u32 {
n
}
/// Build `/sys/devices/system/cpu/cpu{n}/cpufreq/cpuinfo_max_freq` into buf,
/// returning the byte length written.
fn format_cpufreq_path(buf: &mut [u8; 64], cpu: u32) -> usize {
const PREFIX: &[u8] = b"/sys/devices/system/cpu/cpu";
const SUFFIX: &[u8] = b"/cpufreq/cpuinfo_max_freq";
buf[..PREFIX.len()].copy_from_slice(PREFIX);
let mut i = PREFIX.len();
let mut tmp = [0u8; 3];
let mut n = cpu;
let mut digits = 0;
loop {
tmp[digits] = b'0' + (n % 10) as u8;
digits += 1;
n /= 10;
if n == 0 {
break;
}
}
while digits > 0 {
digits -= 1;
buf[i] = tmp[digits];
i += 1;
}
buf[i..i + SUFFIX.len()].copy_from_slice(SUFFIX);
i + SUFFIX.len()
}
/// Read CPU frequency in MHz. Tries sysfs first, then cpuinfo fields.
fn get_cpu_freq_mhz() -> Option<u32> {
// Try sysfs cpuinfo_max_freq (in kHz)
let mut buf = [0u8; 32];
if let Ok(n) = read_file_fast(
"/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq",
&mut buf,
) {
// Read cpuinfo_max_freq across all CPUs (in kHz) and take the max so
// heterogeneous (big.LITTLE) topologies report the performance cluster.
let mut max_khz = 0u32;
let mut path = [0u8; 64];
for cpu in 0u32..64 {
let n = format_cpufreq_path(&mut path, cpu);
let p = match core::str::from_utf8(&path[..n]) {
Ok(s) => s,
Err(_) => continue,
};
let mut buf = [0u8; 32];
let Ok(m) = read_file_fast(p, &mut buf) else {
if cpu == 0 {
continue;
}
break;
};
let mut khz = 0u32;
for &b in &buf[..n] {
for &b in &buf[..m] {
if b.is_ascii_digit() {
khz = khz * 10 + u32::from(b - b'0');
}
}
if khz > 0 {
return Some(khz / 1000);
if khz > max_khz {
max_khz = khz;
}
}
if max_khz > 0 {
return Some(max_khz / 1000);
}
// Fall back to cpuinfo fields
let mut buf2 = [0u8; 2048];
let mut buf2 = [0u8; 4096];
let n = read_file_fast("/proc/cpuinfo", &mut buf2).ok()?;
let data = &buf2[..n];
for key in &[b"cpu MHz" as &[u8], b"cpu MHz dynamic", b"CPU MHz"] {
for key in &[
b"cpu MHz" as &[u8],
b"cpu MHz dynamic",
b"cpu MHz static",
b"CPU MHz",
b"clock",
// BogoMIPS on MIPS is calibrated to the clock frequency (unlike x86).
b"BogoMIPS",
] {
if let Some(val) = extract_field(data, key) {
// Parse integer part of the MHz value (e.g. "5200.00" -> 5200)
let mut mhz = 0u32;
@ -160,6 +209,32 @@ fn get_cpu_freq_mhz() -> Option<u32> {
}
}
}
// SPARC exposes its clock as `Cpu0ClkTck : <hex>`,
// which signifies ticks per second in hex.
if let Some(val) = extract_field(data, b"Cpu0ClkTck") {
let mut hz = 0u64;
let mut seen = false;
for &b in val.as_bytes() {
let d = match b {
b'0'..=b'9' => Some(u64::from(b - b'0')),
b'a'..=b'f' => Some(u64::from(b - b'a' + 10)),
b'A'..=b'F' => Some(u64::from(b - b'A' + 10)),
_ => None,
};
match d {
Some(d) => {
hz = hz * 16 + d;
seen = true;
},
None if seen => break,
None => {},
}
}
if hz > 0 {
#[allow(clippy::cast_possible_truncation)]
return Some((hz / 1_000_000) as u32);
}
}
None
}
@ -169,10 +244,35 @@ fn get_model_name() -> Option<String> {
let n = read_file_fast("/proc/cpuinfo", &mut buf).ok()?;
let data = &buf[..n];
let base = extract_name(data)?;
let mut name = base;
if let Some(mhz) = get_cpu_freq_mhz() {
name.push_str(" @ ");
// Round to nearest 0.01 GHz, then split so carries (e.g. 1999 MHz)
// roll into the integer part instead of overflowing the fraction.
let rounded_centesimal = (mhz + 5) / 10;
let ghz_int = rounded_centesimal / 100;
let ghz_frac = rounded_centesimal % 100;
write_u64(&mut name, u64::from(ghz_int));
name.push('.');
if ghz_frac < 10 {
name.push('0');
}
write_u64(&mut name, u64::from(ghz_frac));
name.push_str(" GHz");
}
Some(name)
}
/// Extract a human-readable CPU name. Tries cpuinfo fields first, then
/// falls back to the device-tree `compatible` string on SoCs that don't
/// expose a model through cpuinfo.
fn extract_name(data: &[u8]) -> Option<String> {
for key in &[
b"model name" as &[u8],
b"Model Name",
b"uarch",
b"cpu model",
b"isa",
b"cpu",
b"machine",
@ -181,28 +281,34 @@ fn get_model_name() -> Option<String> {
if let Some(val) = extract_field(data, key) {
let trimmed = trim(val);
if !trimmed.is_empty() {
let mut name = String::from(trimmed);
if let Some(mhz) = get_cpu_freq_mhz() {
name.push_str(" @ ");
// Round to nearest 0.01 GHz, then split so carries (e.g. 1999 MHz)
// roll into the integer part instead of overflowing the fraction.
let rounded_centesimal = (mhz + 5) / 10;
let ghz_int = rounded_centesimal / 100;
let ghz_frac = rounded_centesimal % 100;
write_u64(&mut name, u64::from(ghz_int));
name.push('.');
if ghz_frac < 10 {
name.push('0');
}
write_u64(&mut name, u64::from(ghz_frac));
name.push_str(" GHz");
}
return Some(name);
return Some(String::from(trimmed));
}
}
}
parse_dt_compatible()
}
None
/// Parse the SoC name from `/sys/firmware/devicetree/base/compatible`.
/// The file holds NUL-separated `vendor,model` strings from most-specific
/// (board) to most-generic (SoC); we take the last entry and return just
/// the model portion after the comma.
fn parse_dt_compatible() -> Option<String> {
let mut buf = [0u8; 256];
let n = read_file_fast("/sys/firmware/devicetree/base/compatible", &mut buf)
.ok()?;
// Drop the terminating NUL so the rposition below locates the entry
// separator rather than the end-of-string marker.
let end = if n > 0 && buf[n - 1] == 0 { n - 1 } else { n };
let data = &buf[..end];
let start = data.iter().rposition(|&b| b == 0).map_or(0, |p| p + 1);
let entry = &data[start..];
let comma = entry.iter().position(|&b| b == b',')?;
let model = core::str::from_utf8(&entry[comma + 1..]).ok()?;
if model.is_empty() {
None
} else {
Some(String::from(model))
}
}
/// Extract value of first occurrence of `key` in cpuinfo.

View file

@ -401,7 +401,13 @@ fn print_system_info(fields: &Fields) -> Result<(), Error> {
// Row format mirrors the default logo path exactly.
let rows: [(&str, &str, &str, &str, &str); 11] = [
("", "", user_info.as_str(), " ", " ~"),
("\u{F313} ", "System", os_name.as_str(), " \u{E621} ", ""),
(
"\u{F313} ",
"System",
os_name.as_str(),
" \u{E621} ",
"",
),
(
"\u{E712} ",
"Kernel",
@ -409,11 +415,41 @@ fn print_system_info(fields: &Fields) -> Result<(), Error> {
" \u{E621} ",
"",
),
("\u{F2DB} ", "CPU", cpu_name.as_str(), " \u{E621} ", ""),
("\u{F4BC} ", "Topology", cpu_cores.as_str(), " \u{E621} ", ""),
("\u{E795} ", "Shell", shell.as_str(), " \u{E621} ", ""),
("\u{F017} ", "Uptime", uptime.as_str(), " \u{E621} ", ""),
("\u{F2D2} ", "Desktop", desktop.as_str(), " \u{E621} ", ""),
(
"\u{F2DB} ",
"CPU",
cpu_name.as_str(),
" \u{E621} ",
"",
),
(
"\u{F4BC} ",
"Topology",
cpu_cores.as_str(),
" \u{E621} ",
"",
),
(
"\u{E795} ",
"Shell",
shell.as_str(),
" \u{E621} ",
"",
),
(
"\u{F017} ",
"Uptime",
uptime.as_str(),
" \u{E621} ",
"",
),
(
"\u{F2D2} ",
"Desktop",
desktop.as_str(),
" \u{E621} ",
"",
),
(
"\u{F035B} ",
"Memory",
@ -421,8 +457,20 @@ fn print_system_info(fields: &Fields) -> Result<(), Error> {
" \u{E621} ",
"",
),
("\u{F194E} ", "Storage (/)", storage.as_str(), " \u{E621} ", ""),
("\u{E22B} ", "Colors", colors.as_str(), " \u{E621} ", ""),
(
"\u{F194E} ",
"Storage (/)",
storage.as_str(),
" \u{E621} ",
"",
),
(
"\u{E22B} ",
"Colors",
colors.as_str(),
" \u{E621} ",
"",
),
];
core::fmt::write(&mut w, format_args!("\n")).ok();

View file

@ -1,14 +1,23 @@
#![no_std]
#![no_main]
#![cfg_attr(
any(
target_arch = "sparc64",
target_arch = "sparc",
target_arch = "mips64",
target_arch = "mips"
),
feature(asm_experimental_arch)
)]
extern crate alloc;
use core::{arch::naked_asm, panic::PanicInfo};
use microfetch_alloc::BumpAllocator;
use microfetch_asm::{entry_rust, sys_exit, sys_write};
// Re-export libc replacement functions from asm crate
pub use microfetch_asm::{memcpy, memset, strlen};
use microfetch_asm::{entry_rust, sys_exit, sys_write};
#[cfg(target_arch = "x86_64")]
#[unsafe(no_mangle)]
@ -25,6 +34,23 @@ unsafe extern "C" fn _start() {
);
}
#[cfg(target_arch = "x86")]
#[unsafe(no_mangle)]
#[unsafe(naked)]
unsafe extern "C" fn _start() {
naked_asm!(
"mov eax, esp", // save original sp (argc/argv)
"and esp, -16", // align stack to 16 bytes
"sub esp, 12", // leave room so that the one-arg push keeps alignment
"push eax", // arg: initial stack pointer
"call {entry_rust}",
"mov ebx, eax", // exit code -> first syscall arg
"mov eax, 1", // SYS_exit
"int 0x80",
entry_rust = sym entry_rust,
);
}
#[cfg(target_arch = "aarch64")]
#[unsafe(no_mangle)]
#[unsafe(naked)]
@ -57,6 +83,218 @@ unsafe extern "C" fn _start() {
);
}
#[cfg(target_arch = "loongarch64")]
#[unsafe(no_mangle)]
#[unsafe(naked)]
unsafe extern "C" fn _start() {
naked_asm!(
"or $a0, $sp, $zero",
"bstrins.d $sp, $zero, 3, 0",
"bl {entry_rust}",
"or $a0, $a0, $zero",
"li.w $a7, 93",
"syscall 0",
entry_rust = sym entry_rust,
);
}
#[cfg(target_arch = "s390x")]
#[unsafe(no_mangle)]
#[unsafe(naked)]
unsafe extern "C" fn _start() {
naked_asm!(
"lgr %r2, %r15", // save original sp (argc/argv) as arg
"aghi %r15, -160", // allocate s390x mandatory stack frame
"lghi %r0, -16",
"ngr %r15, %r0", // align stack to 16 bytes
"brasl %r14, {entry_rust}",
"lghi %r1, 1", // SYS_exit
"svc 0",
entry_rust = sym entry_rust,
);
}
// ELFv2 (ppc64le / new BE): entry point is code directly, no function
// descriptor needed.
#[cfg(all(target_arch = "powerpc64", target_abi = "elfv2"))]
#[unsafe(no_mangle)]
#[unsafe(naked)]
unsafe extern "C" fn _start() {
naked_asm!(
// Set up TOC (r2) for ELFv2 by computing from current PC
"bl 0f",
"0: mflr 12",
"addis 2, 12, .TOC. - 0b@ha",
"addi 2, 2, .TOC. - 0b@l",
// Save sp as first arg, set up stack frame
"mr 3, 1",
"clrrdi 1, 1, 4",
"stdu 1, -64(1)",
"bl {entry_rust}",
"nop",
"li 0, 1",
"sc",
entry_rust = sym entry_rust,
);
}
// ppc32 has no function descriptors; entry is direct code like ELFv2.
#[cfg(target_arch = "powerpc")]
#[unsafe(no_mangle)]
#[unsafe(naked)]
unsafe extern "C" fn _start() {
naked_asm!(
"mr 3, 1", // first arg = initial sp (argc/argv)
"clrrwi 1, 1, 4", // align sp to 16 bytes
"stwu 1, -64(1)", // reserve a frame with back-chain
"bl {entry_rust}",
"li 0, 1", // SYS_exit
"sc",
entry_rust = sym entry_rust,
);
}
// On ELFv1, the ELF entry point must be a function descriptor
// in .opd whose first word is the real code address. We emit both manually via
// `global_asm` so the kernel reads the descriptor and jumps into `_start_impl`.
#[cfg(all(target_arch = "powerpc64", target_abi = "elfv1"))]
core::arch::global_asm!(
".section .opd, \"aw\"",
".balign 8",
".globl _start",
".type _start, @object",
".size _start, 24",
"_start:",
" .quad _start_impl",
" .quad .TOC.@tocbase",
" .quad 0",
".previous",
".text",
".type _start_impl, @function",
"_start_impl:",
" mr 3, 1",
" clrrdi 1, 1, 4",
" stdu 1, -64(1)",
" bl entry_rust",
" nop",
" li 0, 1",
" sc",
);
#[cfg(target_arch = "arm")]
#[unsafe(no_mangle)]
#[unsafe(naked)]
unsafe extern "C" fn _start() {
naked_asm!(
"mov r0, sp", // first arg = original sp (argc/argv)
"bic sp, sp, #7", // align sp to 8 bytes (AAPCS)
"bl {entry_rust}",
"mov r7, #1", // SYS_exit
"svc #0",
entry_rust = sym entry_rust,
);
}
#[cfg(target_arch = "riscv32")]
#[unsafe(no_mangle)]
#[unsafe(naked)]
unsafe extern "C" fn _start() {
naked_asm!(
"mv a0, sp", // first arg = original sp (argc/argv)
"andi sp, sp, -16", // align sp to 16 bytes
"call {entry_rust}",
"li a7, 93", // SYS_exit
"ecall",
entry_rust = sym entry_rust,
);
}
#[cfg(target_arch = "sparc64")]
#[unsafe(no_mangle)]
#[unsafe(naked)]
unsafe extern "C" fn _start() {
naked_asm!(
// SPARC v9: kernel sets %sp biased by -2047; argc is at
// %sp + 2047 + 128 (bias + 128-byte register save area).
// See glibc sysdeps/sparc/sparc64/start.S.
"mov %g0, %fp",
"add %sp, 2047+128, %o0", // first arg = &argc
"add %sp, 2047, %sp", // unbias
"sub %sp, 176, %sp", // reserve register save area
"and %sp, -16, %sp", // align
"sub %sp, 2047, %sp", // rebias
"call {entry_rust}",
"nop", // delay slot
"mov 1, %g1", // SYS_exit
"t 0x6d",
entry_rust = sym entry_rust,
);
}
#[cfg(target_arch = "mips64")]
#[unsafe(no_mangle)]
#[unsafe(naked)]
unsafe extern "C" fn _start() {
naked_asm!(
"move $a0, $sp", // first arg = original sp (argc/argv)
"daddiu $sp, $sp, -16",// reserve + keep 16-byte alignment (N64 ABI)
"jal {entry_rust}",
"nop", // delay slot
"move $a0, $v0", // exit code = entry_rust return value
"li $v0, 5058", // SYS_exit (5000 + 58)
"syscall",
entry_rust = sym entry_rust,
);
}
#[cfg(target_arch = "sparc")]
#[unsafe(no_mangle)]
#[unsafe(naked)]
unsafe extern "C" fn _start() {
naked_asm!(
"mov %g0, %fp",
"add %sp, 64, %o0", // first arg = &argc (past the 64-byte window save)
"and %sp, -16, %sp", // align sp to 16 bytes
"sub %sp, 64, %sp", // reserve window save area
"call {entry_rust}",
"nop", // delay slot
"mov 1, %g1", // SYS_exit (code already in %o0)
"ta 0x10",
entry_rust = sym entry_rust,
);
}
// SPARC V8 has no 1-byte CAS, so rustc emits calls to libatomic for byte
// atomics. We're single-threaded with panic=abort, so a plain load/store is
// already correct.
#[cfg(all(not(test), target_arch = "sparc"))]
#[unsafe(no_mangle)]
extern "C" fn __atomic_load_1(ptr: *const u8, _order: i32) -> u8 {
unsafe { core::ptr::read_volatile(ptr) }
}
#[cfg(all(not(test), target_arch = "sparc"))]
#[unsafe(no_mangle)]
extern "C" fn __atomic_store_1(ptr: *mut u8, val: u8, _order: i32) {
unsafe { core::ptr::write_volatile(ptr, val) }
}
#[cfg(target_arch = "mips")]
#[unsafe(no_mangle)]
#[unsafe(naked)]
unsafe extern "C" fn _start() {
naked_asm!(
"move $a0, $sp", // first arg = original sp (argc/argv)
"addiu $sp, $sp, -24", // reserve 16-byte O32 shadow space + align
"jal {entry_rust}",
"nop", // delay slot
"move $a0, $v0", // exit code = entry_rust return value
"li $v0, 4001", // SYS_exit (4000 + 1)
"syscall",
entry_rust = sym entry_rust,
);
}
// Global allocator
#[global_allocator]
static ALLOCATOR: BumpAllocator = BumpAllocator::new();
@ -110,3 +348,14 @@ const extern "C" fn rust_eh_personality() {}
extern "C" fn _Unwind_Resume() -> ! {
unsafe { sys_exit(1) }
}
// compiler_builtins emits `.ARM.exidx` entries that reference these even
// with panic=abort. libgcc/libunwind would normally resolve them; we're
// nostdlib, so we stub them. They're never called.
#[cfg(all(not(test), target_arch = "arm"))]
#[unsafe(no_mangle)]
extern "C" fn __aeabi_unwind_cpp_pr0() {}
#[cfg(all(not(test), target_arch = "arm"))]
#[unsafe(no_mangle)]
extern "C" fn __aeabi_unwind_cpp_pr1() {}