diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 99668ae..2eb3a1f 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -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' || '' }} diff --git a/Cargo.toml b/Cargo.toml index ab63681..391e0dd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ resolver = "3" authors = [ "NotAShelf " ] edition = "2024" license = "GPL-3.0" -rust-version = "1.92.0" +rust-version = "1.95.0" version = "1.1.0" [workspace.dependencies] diff --git a/crates/asm/src/aarch64.rs b/crates/asm/src/aarch64.rs new file mode 100644 index 0000000..6997f62 --- /dev/null +++ b/crates/asm/src/aarch64.rs @@ -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) + ); + } +} diff --git a/crates/asm/src/arm.rs b/crates/asm/src/arm.rs new file mode 100644 index 0000000..892b012 --- /dev/null +++ b/crates/asm/src/arm.rs @@ -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::(), + 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) + ); + } +} diff --git a/crates/asm/src/lib.rs b/crates/asm/src/lib.rs index 04db2db..fff8d7f 100644 --- a/crates/asm/src/lib.rs +++ b/crates/asm/src/lib.rs @@ -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] - -// 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" -); +#![cfg_attr( + any( + target_arch = "sparc64", + target_arch = "sparc", + target_arch = "mips64", + target_arch = "mips" + ), + feature(asm_experimental_arch) +)] + +// 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 { /// 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) } } diff --git a/crates/asm/src/loongarch64.rs b/crates/asm/src/loongarch64.rs new file mode 100644 index 0000000..d5ab5ac --- /dev/null +++ b/crates/asm/src/loongarch64.rs @@ -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) + ); + } +} diff --git a/crates/asm/src/mips.rs b/crates/asm/src/mips.rs new file mode 100644 index 0000000..c5adf17 --- /dev/null +++ b/crates/asm/src/mips.rs @@ -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::(), + 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) + ); + } +} diff --git a/crates/asm/src/mips64.rs b/crates/asm/src/mips64.rs new file mode 100644 index 0000000..9127a1e --- /dev/null +++ b/crates/asm/src/mips64.rs @@ -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) + ); + } +} diff --git a/crates/asm/src/powerpc.rs b/crates/asm/src/powerpc.rs new file mode 100644 index 0000000..e2e7829 --- /dev/null +++ b/crates/asm/src/powerpc.rs @@ -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::() => _, + 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) + ); + } +} diff --git a/crates/asm/src/powerpc64.rs b/crates/asm/src/powerpc64.rs new file mode 100644 index 0000000..13e0b33 --- /dev/null +++ b/crates/asm/src/powerpc64.rs @@ -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) + ); + } +} diff --git a/crates/asm/src/riscv32.rs b/crates/asm/src/riscv32.rs new file mode 100644 index 0000000..1e4b29a --- /dev/null +++ b/crates/asm/src/riscv32.rs @@ -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::(), + 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) + ); + } +} diff --git a/crates/asm/src/riscv64.rs b/crates/asm/src/riscv64.rs new file mode 100644 index 0000000..2a0c10b --- /dev/null +++ b/crates/asm/src/riscv64.rs @@ -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) + ); + } +} diff --git a/crates/asm/src/s390x.rs b/crates/asm/src/s390x.rs new file mode 100644 index 0000000..3112ed6 --- /dev/null +++ b/crates/asm/src/s390x.rs @@ -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) + ); + } +} diff --git a/crates/asm/src/sparc.rs b/crates/asm/src/sparc.rs new file mode 100644 index 0000000..a4cc3f7 --- /dev/null +++ b/crates/asm/src/sparc.rs @@ -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::(), + 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) + ); + } +} diff --git a/crates/asm/src/sparc64.rs b/crates/asm/src/sparc64.rs new file mode 100644 index 0000000..020657e --- /dev/null +++ b/crates/asm/src/sparc64.rs @@ -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) + ); + } +} diff --git a/crates/asm/src/x86.rs b/crates/asm/src/x86.rs new file mode 100644 index 0000000..f0e619e --- /dev/null +++ b/crates/asm/src/x86.rs @@ -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::(), + 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) + ); + } +} diff --git a/crates/asm/src/x86_64.rs b/crates/asm/src/x86_64.rs new file mode 100644 index 0000000..967ddbf --- /dev/null +++ b/crates/asm/src/x86_64.rs @@ -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) + ); + } +} diff --git a/crates/lib/src/cpu.rs b/crates/lib/src/cpu.rs index c6ed8a0..dca327b 100644 --- a/crates/lib/src/cpu.rs +++ b/crates/lib/src/cpu.rs @@ -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 { - // 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 { } } } + // SPARC exposes its clock as `Cpu0ClkTck : `, + // 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 { 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 { 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 { 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 { + 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. diff --git a/crates/lib/src/lib.rs b/crates/lib/src/lib.rs index 1f74569..d33bee1 100644 --- a/crates/lib/src/lib.rs +++ b/crates/lib/src/lib.rs @@ -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(); diff --git a/microfetch/src/main.rs b/microfetch/src/main.rs index fcefcc8..d1b39b0 100644 --- a/microfetch/src/main.rs +++ b/microfetch/src/main.rs @@ -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() {}