From e77976b94bde3156ef09a02b584a95417cf80bb4 Mon Sep 17 00:00:00 2001 From: Amaan Qureshi Date: Fri, 17 Apr 2026 14:29:28 -0400 Subject: [PATCH 01/13] asm: extract per-arch syscall impls into separate modules The monolithic lib.rs had grown to 2600+ lines of `#[cfg(target_arch)]` blocks interleaved inside every `sys_*` function. Reading or modifying one arch's syscalls meant scrolling past thirteen others. Pull each arch's asm into `.rs`, re-exported via a single `#[cfg] #[path] mod arch;` so `lib.rs` only contains wrappers, structs, and module dispatch. Adding a new architecture now touches one new file plus one three-line mod decl. --- crates/asm/src/aarch64.rs | 164 +++++++++++++ crates/asm/src/lib.rs | 471 ++------------------------------------ crates/asm/src/riscv64.rs | 166 ++++++++++++++ crates/asm/src/x86_64.rs | 180 +++++++++++++++ crates/lib/src/lib.rs | 64 +++++- microfetch/src/main.rs | 2 +- 6 files changed, 587 insertions(+), 460 deletions(-) create mode 100644 crates/asm/src/aarch64.rs create mode 100644 crates/asm/src/riscv64.rs create mode 100644 crates/asm/src/x86_64.rs 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/lib.rs b/crates/asm/src/lib.rs index 04db2db..83052c8 100644 --- a/crates/asm/src/lib.rs +++ b/crates/asm/src/lib.rs @@ -19,6 +19,17 @@ compile_error!( "Unsupported architecture: only x86_64, aarch64, and riscv64 are supported" ); +// Per-arch syscall implementations live in their own module files. +#[cfg(target_arch = "x86_64")] +#[path = "x86_64.rs"] +mod arch; +#[cfg(target_arch = "aarch64")] +#[path = "aarch64.rs"] +mod arch; +#[cfg(target_arch = "riscv64")] +#[path = "riscv64.rs"] +mod arch; + /// Copies `n` bytes from `src` to `dest`. /// /// # Safety @@ -165,62 +176,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 +193,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 +211,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 +222,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 +253,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. @@ -549,61 +293,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 @@ -689,46 +379,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 +399,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 +409,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/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/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/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..338133f 100644 --- a/microfetch/src/main.rs +++ b/microfetch/src/main.rs @@ -6,9 +6,9 @@ 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)] From ce7a8553a881a9aeb8beacdcb1a2054bb1df4034 Mon Sep 17 00:00:00 2001 From: Amaan Qureshi Date: Thu, 16 Apr 2026 18:23:22 -0400 Subject: [PATCH 02/13] arch: add loongarch64 support --- .github/workflows/rust.yml | 1 + crates/asm/src/lib.rs | 11 ++- crates/asm/src/loongarch64.rs | 161 ++++++++++++++++++++++++++++++++++ microfetch/src/main.rs | 15 ++++ 4 files changed, 185 insertions(+), 3 deletions(-) create mode 100644 crates/asm/src/loongarch64.rs diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 99668ae..6ef14a9 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -20,6 +20,7 @@ jobs: - x86_64-unknown-linux-gnu - aarch64-unknown-linux-gnu - riscv64gc-unknown-linux-gnu + - loongarch64-unknown-linux-gnu steps: - name: "Checkout" diff --git a/crates/asm/src/lib.rs b/crates/asm/src/lib.rs index 83052c8..66ad335 100644 --- a/crates/asm/src/lib.rs +++ b/crates/asm/src/lib.rs @@ -5,7 +5,7 @@ //! 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`, and `loongarch64` architectures. #![no_std] @@ -13,10 +13,12 @@ #[cfg(not(any( target_arch = "x86_64", target_arch = "aarch64", - target_arch = "riscv64" + target_arch = "riscv64", + target_arch = "loongarch64" )))] compile_error!( - "Unsupported architecture: only x86_64, aarch64, and riscv64 are supported" + "Unsupported architecture: only x86_64, aarch64, riscv64, and loongarch64 \ + are supported" ); // Per-arch syscall implementations live in their own module files. @@ -29,6 +31,9 @@ mod arch; #[cfg(target_arch = "riscv64")] #[path = "riscv64.rs"] mod arch; +#[cfg(target_arch = "loongarch64")] +#[path = "loongarch64.rs"] +mod arch; /// Copies `n` bytes from `src` to `dest`. /// 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/microfetch/src/main.rs b/microfetch/src/main.rs index 338133f..c24e69a 100644 --- a/microfetch/src/main.rs +++ b/microfetch/src/main.rs @@ -57,6 +57,21 @@ 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, + ); +} + // Global allocator #[global_allocator] static ALLOCATOR: BumpAllocator = BumpAllocator::new(); From 05fc3d8df9e5baabcaa52504e97b94cd634f60a3 Mon Sep 17 00:00:00 2001 From: Amaan Qureshi Date: Thu, 16 Apr 2026 18:23:22 -0400 Subject: [PATCH 03/13] arch: add s390x support --- .github/workflows/rust.yml | 1 + crates/asm/src/lib.rs | 34 +++++++- crates/asm/src/s390x.rs | 160 +++++++++++++++++++++++++++++++++++++ crates/lib/src/cpu.rs | 9 ++- microfetch/src/main.rs | 16 ++++ 5 files changed, 214 insertions(+), 6 deletions(-) create mode 100644 crates/asm/src/s390x.rs diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 6ef14a9..6ff6843 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -21,6 +21,7 @@ jobs: - aarch64-unknown-linux-gnu - riscv64gc-unknown-linux-gnu - loongarch64-unknown-linux-gnu + - s390x-unknown-linux-gnu steps: - name: "Checkout" diff --git a/crates/asm/src/lib.rs b/crates/asm/src/lib.rs index 66ad335..92cc420 100644 --- a/crates/asm/src/lib.rs +++ b/crates/asm/src/lib.rs @@ -5,7 +5,8 @@ //! What do you mean I wasted two whole hours to make the program only 100µs //! faster? //! -//! Supports `x86_64`, `aarch64`, `riscv64`, and `loongarch64` architectures. +//! Supports `x86_64`, `aarch64`, `riscv64`, `loongarch64`, and `s390x` +//! architectures. #![no_std] @@ -14,11 +15,12 @@ target_arch = "x86_64", target_arch = "aarch64", target_arch = "riscv64", - target_arch = "loongarch64" + target_arch = "loongarch64", + target_arch = "s390x" )))] compile_error!( - "Unsupported architecture: only x86_64, aarch64, riscv64, and loongarch64 \ - are supported" + "Unsupported architecture: only x86_64, aarch64, riscv64, loongarch64, and \ + s390x are supported" ); // Per-arch syscall implementations live in their own module files. @@ -34,6 +36,9 @@ mod arch; #[cfg(target_arch = "loongarch64")] #[path = "loongarch64.rs"] mod arch; +#[cfg(target_arch = "s390x")] +#[path = "s390x.rs"] +mod arch; /// Copies `n` bytes from `src` to `dest`. /// @@ -267,6 +272,7 @@ 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(target_arch = "s390x"))] pub struct StatfsBuf { pub f_type: i64, pub f_bsize: i64, @@ -284,6 +290,26 @@ 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], +} + /// Direct `statfs(2)` syscall /// /// # Returns 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/lib/src/cpu.rs b/crates/lib/src/cpu.rs index c6ed8a0..ebff676 100644 --- a/crates/lib/src/cpu.rs +++ b/crates/lib/src/cpu.rs @@ -140,10 +140,15 @@ fn get_cpu_freq_mhz() -> Option { } } // 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", + ] { if let Some(val) = extract_field(data, key) { // Parse integer part of the MHz value (e.g. "5200.00" -> 5200) let mut mhz = 0u32; diff --git a/microfetch/src/main.rs b/microfetch/src/main.rs index c24e69a..c3957df 100644 --- a/microfetch/src/main.rs +++ b/microfetch/src/main.rs @@ -72,6 +72,22 @@ unsafe extern "C" fn _start() { ); } +#[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, + ); +} + // Global allocator #[global_allocator] static ALLOCATOR: BumpAllocator = BumpAllocator::new(); From 03e6d1f4cf0604a9cc9c928befe21e5451b1a491 Mon Sep 17 00:00:00 2001 From: Amaan Qureshi Date: Thu, 16 Apr 2026 18:23:22 -0400 Subject: [PATCH 04/13] arch: add powerpc64 support --- .github/workflows/rust.yml | 23 +++-- crates/asm/src/lib.rs | 15 ++- crates/asm/src/powerpc64.rs | 200 ++++++++++++++++++++++++++++++++++++ crates/lib/src/cpu.rs | 1 + microfetch/src/main.rs | 52 ++++++++++ 5 files changed, 280 insertions(+), 11 deletions(-) create mode 100644 crates/asm/src/powerpc64.rs diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 6ff6843..75e3064 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -16,12 +16,22 @@ jobs: strategy: fail-fast: false matrix: - target: - - x86_64-unknown-linux-gnu - - aarch64-unknown-linux-gnu - - riscv64gc-unknown-linux-gnu - - loongarch64-unknown-linux-gnu - - s390x-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 steps: - name: "Checkout" @@ -30,6 +40,7 @@ jobs: - name: "Setup Rust toolchain" uses: actions-rust-lang/setup-rust-toolchain@v1 with: + toolchain: ${{ matrix.toolchain }} target: ${{ matrix.target }} rustflags: "" diff --git a/crates/asm/src/lib.rs b/crates/asm/src/lib.rs index 92cc420..238cd69 100644 --- a/crates/asm/src/lib.rs +++ b/crates/asm/src/lib.rs @@ -5,10 +5,11 @@ //! What do you mean I wasted two whole hours to make the program only 100µs //! faster? //! -//! Supports `x86_64`, `aarch64`, `riscv64`, `loongarch64`, and `s390x` -//! architectures. +//! Supports `x86_64`, `aarch64`, `riscv64`, `loongarch64`, `s390x`, and +//! `powerpc64` architectures. #![no_std] +#![cfg_attr(target_arch = "powerpc64", feature(asm_experimental_arch))] // Ensure we're compiling for a supported architecture. #[cfg(not(any( @@ -16,11 +17,12 @@ target_arch = "aarch64", target_arch = "riscv64", target_arch = "loongarch64", - target_arch = "s390x" + target_arch = "s390x", + target_arch = "powerpc64" )))] compile_error!( - "Unsupported architecture: only x86_64, aarch64, riscv64, loongarch64, and \ - s390x are supported" + "Unsupported architecture: only x86_64, aarch64, riscv64, loongarch64, \ + s390x, and powerpc64 are supported" ); // Per-arch syscall implementations live in their own module files. @@ -39,6 +41,9 @@ mod arch; #[cfg(target_arch = "s390x")] #[path = "s390x.rs"] mod arch; +#[cfg(target_arch = "powerpc64")] +#[path = "powerpc64.rs"] +mod arch; /// Copies `n` bytes from `src` to `dest`. /// 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/lib/src/cpu.rs b/crates/lib/src/cpu.rs index ebff676..c3e23bd 100644 --- a/crates/lib/src/cpu.rs +++ b/crates/lib/src/cpu.rs @@ -148,6 +148,7 @@ fn get_cpu_freq_mhz() -> Option { b"cpu MHz dynamic", b"cpu MHz static", b"CPU MHz", + b"clock", ] { if let Some(val) = extract_field(data, key) { // Parse integer part of the MHz value (e.g. "5200.00" -> 5200) diff --git a/microfetch/src/main.rs b/microfetch/src/main.rs index c3957df..b22f291 100644 --- a/microfetch/src/main.rs +++ b/microfetch/src/main.rs @@ -1,5 +1,6 @@ #![no_std] #![no_main] +#![cfg_attr(target_arch = "powerpc64", feature(asm_experimental_arch))] extern crate alloc; @@ -88,6 +89,57 @@ unsafe extern "C" fn _start() { ); } +// 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, + ); +} + +// 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", +); + // Global allocator #[global_allocator] static ALLOCATOR: BumpAllocator = BumpAllocator::new(); From 07239e2c0ba8952fbadcf0e31433d7cfc9ea3e8d Mon Sep 17 00:00:00 2001 From: Amaan Qureshi Date: Thu, 16 Apr 2026 23:37:34 -0400 Subject: [PATCH 05/13] arch: add armv7 support --- .github/workflows/rust.yml | 2 + crates/asm/src/arm.rs | 140 +++++++++++++++++++++++++++++++++++++ crates/asm/src/lib.rs | 58 +++++++++++++-- crates/lib/src/cpu.rs | 125 ++++++++++++++++++++++++++------- microfetch/src/main.rs | 25 +++++++ 5 files changed, 318 insertions(+), 32 deletions(-) create mode 100644 crates/asm/src/arm.rs diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 75e3064..c405912 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -32,6 +32,8 @@ jobs: toolchain: nightly - target: powerpc64-unknown-linux-gnu toolchain: nightly + - target: armv7-unknown-linux-gnueabihf + toolchain: stable steps: - name: "Checkout" 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 238cd69..3148f94 100644 --- a/crates/asm/src/lib.rs +++ b/crates/asm/src/lib.rs @@ -5,8 +5,8 @@ //! What do you mean I wasted two whole hours to make the program only 100µs //! faster? //! -//! Supports `x86_64`, `aarch64`, `riscv64`, `loongarch64`, `s390x`, and -//! `powerpc64` architectures. +//! Supports `x86_64`, `aarch64`, `riscv64`, `loongarch64`, `s390x`, +//! `powerpc64`, and `arm` (armv7) architectures. #![no_std] #![cfg_attr(target_arch = "powerpc64", feature(asm_experimental_arch))] @@ -18,11 +18,12 @@ target_arch = "riscv64", target_arch = "loongarch64", target_arch = "s390x", - target_arch = "powerpc64" + target_arch = "powerpc64", + target_arch = "arm" )))] compile_error!( "Unsupported architecture: only x86_64, aarch64, riscv64, loongarch64, \ - s390x, and powerpc64 are supported" + s390x, powerpc64, and arm are supported" ); // Per-arch syscall implementations live in their own module files. @@ -44,6 +45,9 @@ mod arch; #[cfg(target_arch = "powerpc64")] #[path = "powerpc64.rs"] mod arch; +#[cfg(target_arch = "arm")] +#[path = "arm.rs"] +mod arch; /// Copies `n` bytes from `src` to `dest`. /// @@ -277,7 +281,7 @@ 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(target_arch = "s390x"))] +#[cfg(not(any(target_arch = "s390x", target_arch = "arm")))] pub struct StatfsBuf { pub f_type: i64, pub f_bsize: i64, @@ -315,6 +319,27 @@ pub struct StatfsBuf { 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(target_arch = "arm")] +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], +} + /// Direct `statfs(2)` syscall /// /// # Returns @@ -384,6 +409,7 @@ 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(target_arch = "arm"))] pub struct SysInfo { pub uptime: i64, pub loads: [u64; 3], @@ -404,6 +430,28 @@ 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(target_arch = "arm")] +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 diff --git a/crates/lib/src/cpu.rs b/crates/lib/src/cpu.rs index c3e23bd..2b10a32 100644 --- a/crates/lib/src/cpu.rs +++ b/crates/lib/src/cpu.rs @@ -121,24 +121,65 @@ 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; 4096]; let n = read_file_fast("/proc/cpuinfo", &mut buf2).ok()?; @@ -175,6 +216,30 @@ 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", @@ -187,28 +252,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/microfetch/src/main.rs b/microfetch/src/main.rs index b22f291..7f6a4cb 100644 --- a/microfetch/src/main.rs +++ b/microfetch/src/main.rs @@ -140,6 +140,20 @@ core::arch::global_asm!( " 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, + ); +} + // Global allocator #[global_allocator] static ALLOCATOR: BumpAllocator = BumpAllocator::new(); @@ -193,3 +207,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() {} From 0f95ca0f682a2a0c45d4a4503306f88b732c5be7 Mon Sep 17 00:00:00 2001 From: Amaan Qureshi Date: Thu, 16 Apr 2026 23:41:43 -0400 Subject: [PATCH 06/13] arch: add riscv32 support --- .github/workflows/rust.yml | 17 ++++- crates/asm/src/lib.rs | 22 ++++-- crates/asm/src/riscv32.rs | 142 +++++++++++++++++++++++++++++++++++++ microfetch/src/main.rs | 14 ++++ 4 files changed, 185 insertions(+), 10 deletions(-) create mode 100644 crates/asm/src/riscv32.rs diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index c405912..3f786d8 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -34,6 +34,11 @@ jobs: 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 steps: - name: "Checkout" @@ -43,9 +48,13 @@ jobs: uses: actions-rust-lang/setup-rust-toolchain@v1 with: toolchain: ${{ matrix.toolchain }} - target: ${{ matrix.target }} + 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 @@ -55,7 +64,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' || '' }} + # 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/crates/asm/src/lib.rs b/crates/asm/src/lib.rs index 3148f94..01afbde 100644 --- a/crates/asm/src/lib.rs +++ b/crates/asm/src/lib.rs @@ -6,7 +6,7 @@ //! faster? //! //! Supports `x86_64`, `aarch64`, `riscv64`, `loongarch64`, `s390x`, -//! `powerpc64`, and `arm` (armv7) architectures. +//! `powerpc64`, `arm` (armv7), and `riscv32` architectures. #![no_std] #![cfg_attr(target_arch = "powerpc64", feature(asm_experimental_arch))] @@ -19,11 +19,12 @@ target_arch = "loongarch64", target_arch = "s390x", target_arch = "powerpc64", - target_arch = "arm" + target_arch = "arm", + target_arch = "riscv32" )))] compile_error!( "Unsupported architecture: only x86_64, aarch64, riscv64, loongarch64, \ - s390x, powerpc64, and arm are supported" + s390x, powerpc64, arm, and riscv32 are supported" ); // Per-arch syscall implementations live in their own module files. @@ -48,6 +49,9 @@ mod arch; #[cfg(target_arch = "arm")] #[path = "arm.rs"] mod arch; +#[cfg(target_arch = "riscv32")] +#[path = "riscv32.rs"] +mod arch; /// Copies `n` bytes from `src` to `dest`. /// @@ -281,7 +285,11 @@ 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")))] +#[cfg(not(any( + target_arch = "s390x", + target_arch = "arm", + target_arch = "riscv32" +)))] pub struct StatfsBuf { pub f_type: i64, pub f_bsize: i64, @@ -322,7 +330,7 @@ pub struct StatfsBuf { /// 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(target_arch = "arm")] +#[cfg(any(target_arch = "arm", target_arch = "riscv32"))] pub struct StatfsBuf { pub f_type: u32, pub f_bsize: u32, @@ -409,7 +417,7 @@ 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(target_arch = "arm"))] +#[cfg(not(any(target_arch = "arm", target_arch = "riscv32")))] pub struct SysInfo { pub uptime: i64, pub loads: [u64; 3], @@ -433,7 +441,7 @@ pub struct SysInfo { /// 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(target_arch = "arm")] +#[cfg(any(target_arch = "arm", target_arch = "riscv32"))] pub struct SysInfo { pub uptime: i32, pub loads: [u32; 3], 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/microfetch/src/main.rs b/microfetch/src/main.rs index 7f6a4cb..68a7bbf 100644 --- a/microfetch/src/main.rs +++ b/microfetch/src/main.rs @@ -154,6 +154,20 @@ unsafe extern "C" fn _start() { ); } +#[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, + ); +} + // Global allocator #[global_allocator] static ALLOCATOR: BumpAllocator = BumpAllocator::new(); From 1b4dcf4a1a50f9da09480f7854b884eb4d5c3e27 Mon Sep 17 00:00:00 2001 From: Amaan Qureshi Date: Fri, 17 Apr 2026 02:31:53 -0400 Subject: [PATCH 07/13] arch: add sparc64 support --- .github/workflows/rust.yml | 3 + crates/asm/src/lib.rs | 15 +++- crates/asm/src/sparc64.rs | 179 +++++++++++++++++++++++++++++++++++++ crates/lib/src/cpu.rs | 26 ++++++ microfetch/src/main.rs | 27 +++++- 5 files changed, 245 insertions(+), 5 deletions(-) create mode 100644 crates/asm/src/sparc64.rs diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 3f786d8..fff5aea 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -39,6 +39,9 @@ jobs: toolchain: nightly components: rust-src build_std: true + # sparc64 uses experimental asm features + - target: sparc64-unknown-linux-gnu + toolchain: nightly steps: - name: "Checkout" diff --git a/crates/asm/src/lib.rs b/crates/asm/src/lib.rs index 01afbde..c877c1a 100644 --- a/crates/asm/src/lib.rs +++ b/crates/asm/src/lib.rs @@ -6,10 +6,13 @@ //! faster? //! //! Supports `x86_64`, `aarch64`, `riscv64`, `loongarch64`, `s390x`, -//! `powerpc64`, `arm` (armv7), and `riscv32` architectures. +//! `powerpc64`, `arm` (armv7), `riscv32`, and `sparc64` architectures. #![no_std] -#![cfg_attr(target_arch = "powerpc64", feature(asm_experimental_arch))] +#![cfg_attr( + any(target_arch = "powerpc64", target_arch = "sparc64"), + feature(asm_experimental_arch) +)] // Ensure we're compiling for a supported architecture. #[cfg(not(any( @@ -20,11 +23,12 @@ target_arch = "s390x", target_arch = "powerpc64", target_arch = "arm", - target_arch = "riscv32" + target_arch = "riscv32", + target_arch = "sparc64" )))] compile_error!( "Unsupported architecture: only x86_64, aarch64, riscv64, loongarch64, \ - s390x, powerpc64, arm, and riscv32 are supported" + s390x, powerpc64, arm, riscv32, and sparc64 are supported" ); // Per-arch syscall implementations live in their own module files. @@ -52,6 +56,9 @@ mod arch; #[cfg(target_arch = "riscv32")] #[path = "riscv32.rs"] mod arch; +#[cfg(target_arch = "sparc64")] +#[path = "sparc64.rs"] +mod arch; /// Copies `n` bytes from `src` to `dest`. /// 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/lib/src/cpu.rs b/crates/lib/src/cpu.rs index 2b10a32..3bb53a0 100644 --- a/crates/lib/src/cpu.rs +++ b/crates/lib/src/cpu.rs @@ -207,6 +207,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 } diff --git a/microfetch/src/main.rs b/microfetch/src/main.rs index 68a7bbf..42bad34 100644 --- a/microfetch/src/main.rs +++ b/microfetch/src/main.rs @@ -1,6 +1,9 @@ #![no_std] #![no_main] -#![cfg_attr(target_arch = "powerpc64", feature(asm_experimental_arch))] +#![cfg_attr( + any(target_arch = "powerpc64", target_arch = "sparc64"), + feature(asm_experimental_arch) +)] extern crate alloc; @@ -168,6 +171,28 @@ unsafe extern "C" fn _start() { ); } +#[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, + ); +} + // Global allocator #[global_allocator] static ALLOCATOR: BumpAllocator = BumpAllocator::new(); From 950d9f4d2c388b3fb8e099447ddb158e429a46aa Mon Sep 17 00:00:00 2001 From: Amaan Qureshi Date: Fri, 17 Apr 2026 02:34:24 -0400 Subject: [PATCH 08/13] arch: add mips64 support --- .github/workflows/rust.yml | 5 + crates/asm/src/lib.rs | 41 +++++++- crates/asm/src/mips64.rs | 206 +++++++++++++++++++++++++++++++++++++ crates/lib/src/cpu.rs | 3 + microfetch/src/main.rs | 22 +++- 5 files changed, 271 insertions(+), 6 deletions(-) create mode 100644 crates/asm/src/mips64.rs diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index fff5aea..6d24a66 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -42,6 +42,11 @@ jobs: # 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 steps: - name: "Checkout" diff --git a/crates/asm/src/lib.rs b/crates/asm/src/lib.rs index c877c1a..26bae90 100644 --- a/crates/asm/src/lib.rs +++ b/crates/asm/src/lib.rs @@ -6,11 +6,16 @@ //! faster? //! //! Supports `x86_64`, `aarch64`, `riscv64`, `loongarch64`, `s390x`, -//! `powerpc64`, `arm` (armv7), `riscv32`, and `sparc64` architectures. +//! `powerpc64`, `arm` (armv7), `riscv32`, `sparc64`, and `mips64` +//! architectures. #![no_std] #![cfg_attr( - any(target_arch = "powerpc64", target_arch = "sparc64"), + any( + target_arch = "powerpc64", + target_arch = "sparc64", + target_arch = "mips64" + ), feature(asm_experimental_arch) )] @@ -24,11 +29,12 @@ target_arch = "powerpc64", target_arch = "arm", target_arch = "riscv32", - target_arch = "sparc64" + target_arch = "sparc64", + target_arch = "mips64" )))] compile_error!( "Unsupported architecture: only x86_64, aarch64, riscv64, loongarch64, \ - s390x, powerpc64, arm, riscv32, and sparc64 are supported" + s390x, powerpc64, arm, riscv32, sparc64, and mips64 are supported" ); // Per-arch syscall implementations live in their own module files. @@ -59,6 +65,9 @@ mod arch; #[cfg(target_arch = "sparc64")] #[path = "sparc64.rs"] mod arch; +#[cfg(target_arch = "mips64")] +#[path = "mips64.rs"] +mod arch; /// Copies `n` bytes from `src` to `dest`. /// @@ -295,7 +304,8 @@ pub unsafe fn sys_uname(buf: *mut UtsNameBuf) -> i32 { #[cfg(not(any( target_arch = "s390x", target_arch = "arm", - target_arch = "riscv32" + target_arch = "riscv32", + target_arch = "mips64" )))] pub struct StatfsBuf { pub f_type: i64, @@ -355,6 +365,27 @@ pub struct StatfsBuf { pub _pad: [u32; 4], } +/// 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 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/lib/src/cpu.rs b/crates/lib/src/cpu.rs index 3bb53a0..dca327b 100644 --- a/crates/lib/src/cpu.rs +++ b/crates/lib/src/cpu.rs @@ -190,6 +190,8 @@ fn get_cpu_freq_mhz() -> Option { 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) @@ -270,6 +272,7 @@ fn extract_name(data: &[u8]) -> Option { b"model name" as &[u8], b"Model Name", b"uarch", + b"cpu model", b"isa", b"cpu", b"machine", diff --git a/microfetch/src/main.rs b/microfetch/src/main.rs index 42bad34..4745d63 100644 --- a/microfetch/src/main.rs +++ b/microfetch/src/main.rs @@ -1,7 +1,11 @@ #![no_std] #![no_main] #![cfg_attr( - any(target_arch = "powerpc64", target_arch = "sparc64"), + any( + target_arch = "powerpc64", + target_arch = "sparc64", + target_arch = "mips64" + ), feature(asm_experimental_arch) )] @@ -193,6 +197,22 @@ unsafe extern "C" fn _start() { ); } +#[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, + ); +} + // Global allocator #[global_allocator] static ALLOCATOR: BumpAllocator = BumpAllocator::new(); From a6fab02586b0637d17854e8bd34a003849a43d28 Mon Sep 17 00:00:00 2001 From: Amaan Qureshi Date: Fri, 17 Apr 2026 12:20:09 -0400 Subject: [PATCH 09/13] arch: add i686 support --- .github/workflows/rust.yml | 2 + crates/asm/src/lib.rs | 23 ++++-- crates/asm/src/x86.rs | 149 +++++++++++++++++++++++++++++++++++++ microfetch/src/main.rs | 17 +++++ 4 files changed, 184 insertions(+), 7 deletions(-) create mode 100644 crates/asm/src/x86.rs diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 6d24a66..8f19a1e 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -47,6 +47,8 @@ jobs: toolchain: nightly components: rust-src build_std: true + - target: i686-unknown-linux-gnu + toolchain: stable steps: - name: "Checkout" diff --git a/crates/asm/src/lib.rs b/crates/asm/src/lib.rs index 26bae90..99cc710 100644 --- a/crates/asm/src/lib.rs +++ b/crates/asm/src/lib.rs @@ -6,8 +6,8 @@ //! faster? //! //! Supports `x86_64`, `aarch64`, `riscv64`, `loongarch64`, `s390x`, -//! `powerpc64`, `arm` (armv7), `riscv32`, `sparc64`, and `mips64` -//! architectures. +//! `powerpc64`, `arm` (armv7), `riscv32`, `sparc64`, `mips64`, and `x86` +//! (i686) architectures. #![no_std] #![cfg_attr( @@ -30,11 +30,12 @@ target_arch = "arm", target_arch = "riscv32", target_arch = "sparc64", - target_arch = "mips64" + target_arch = "mips64", + target_arch = "x86" )))] compile_error!( "Unsupported architecture: only x86_64, aarch64, riscv64, loongarch64, \ - s390x, powerpc64, arm, riscv32, sparc64, and mips64 are supported" + s390x, powerpc64, arm, riscv32, sparc64, mips64, and x86 are supported" ); // Per-arch syscall implementations live in their own module files. @@ -68,6 +69,9 @@ mod arch; #[cfg(target_arch = "mips64")] #[path = "mips64.rs"] mod arch; +#[cfg(target_arch = "x86")] +#[path = "x86.rs"] +mod arch; /// Copies `n` bytes from `src` to `dest`. /// @@ -305,6 +309,7 @@ pub unsafe fn sys_uname(buf: *mut UtsNameBuf) -> i32 { target_arch = "s390x", target_arch = "arm", target_arch = "riscv32", + target_arch = "x86", target_arch = "mips64" )))] pub struct StatfsBuf { @@ -347,7 +352,7 @@ pub struct StatfsBuf { /// 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"))] +#[cfg(any(target_arch = "arm", target_arch = "riscv32", target_arch = "x86"))] pub struct StatfsBuf { pub f_type: u32, pub f_bsize: u32, @@ -455,7 +460,11 @@ 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")))] +#[cfg(not(any( + target_arch = "arm", + target_arch = "riscv32", + target_arch = "x86" +)))] pub struct SysInfo { pub uptime: i64, pub loads: [u64; 3], @@ -479,7 +488,7 @@ pub struct SysInfo { /// 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"))] +#[cfg(any(target_arch = "arm", target_arch = "riscv32", target_arch = "x86"))] pub struct SysInfo { pub uptime: i32, pub loads: [u32; 3], 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/microfetch/src/main.rs b/microfetch/src/main.rs index 4745d63..7a9b624 100644 --- a/microfetch/src/main.rs +++ b/microfetch/src/main.rs @@ -33,6 +33,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)] From 7bbc47eb9f01bb3b8b5114cd31086ca1bd753922 Mon Sep 17 00:00:00 2001 From: Amaan Qureshi Date: Fri, 17 Apr 2026 12:32:03 -0400 Subject: [PATCH 10/13] arch: add powerpc32 support --- .github/workflows/rust.yml | 3 + crates/asm/src/lib.rs | 32 +++++-- crates/asm/src/powerpc.rs | 180 +++++++++++++++++++++++++++++++++++++ microfetch/src/main.rs | 17 ++++ 4 files changed, 225 insertions(+), 7 deletions(-) create mode 100644 crates/asm/src/powerpc.rs diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 8f19a1e..a764da5 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -49,6 +49,9 @@ jobs: build_std: true - target: i686-unknown-linux-gnu toolchain: stable + # powerpc uses experimental asm features + - target: powerpc-unknown-linux-gnu + toolchain: nightly steps: - name: "Checkout" diff --git a/crates/asm/src/lib.rs b/crates/asm/src/lib.rs index 99cc710..9dba972 100644 --- a/crates/asm/src/lib.rs +++ b/crates/asm/src/lib.rs @@ -6,13 +6,14 @@ //! faster? //! //! Supports `x86_64`, `aarch64`, `riscv64`, `loongarch64`, `s390x`, -//! `powerpc64`, `arm` (armv7), `riscv32`, `sparc64`, `mips64`, and `x86` -//! (i686) architectures. +//! `powerpc64`, `arm` (armv7), `riscv32`, `sparc64`, `mips64`, `x86` (i686), +//! and `powerpc` (ppc32) architectures. #![no_std] #![cfg_attr( any( target_arch = "powerpc64", + target_arch = "powerpc", target_arch = "sparc64", target_arch = "mips64" ), @@ -31,11 +32,13 @@ target_arch = "riscv32", target_arch = "sparc64", target_arch = "mips64", - target_arch = "x86" + target_arch = "x86", + target_arch = "powerpc" )))] compile_error!( "Unsupported architecture: only x86_64, aarch64, riscv64, loongarch64, \ - s390x, powerpc64, arm, riscv32, sparc64, mips64, and x86 are supported" + s390x, powerpc64, arm, riscv32, sparc64, mips64, x86, and powerpc are \ + supported" ); // Per-arch syscall implementations live in their own module files. @@ -72,6 +75,9 @@ mod arch; #[cfg(target_arch = "x86")] #[path = "x86.rs"] mod arch; +#[cfg(target_arch = "powerpc")] +#[path = "powerpc.rs"] +mod arch; /// Copies `n` bytes from `src` to `dest`. /// @@ -310,6 +316,7 @@ pub unsafe fn sys_uname(buf: *mut UtsNameBuf) -> i32 { target_arch = "arm", target_arch = "riscv32", target_arch = "x86", + target_arch = "powerpc", target_arch = "mips64" )))] pub struct StatfsBuf { @@ -352,7 +359,12 @@ pub struct StatfsBuf { /// 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"))] +#[cfg(any( + target_arch = "arm", + target_arch = "riscv32", + target_arch = "x86", + target_arch = "powerpc" +))] pub struct StatfsBuf { pub f_type: u32, pub f_bsize: u32, @@ -463,7 +475,8 @@ pub fn read_file_fast(path: &str, buffer: &mut [u8]) -> Result { #[cfg(not(any( target_arch = "arm", target_arch = "riscv32", - target_arch = "x86" + target_arch = "x86", + target_arch = "powerpc" )))] pub struct SysInfo { pub uptime: i64, @@ -488,7 +501,12 @@ pub struct SysInfo { /// 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"))] +#[cfg(any( + target_arch = "arm", + target_arch = "riscv32", + target_arch = "x86", + target_arch = "powerpc" +))] pub struct SysInfo { pub uptime: i32, pub loads: [u32; 3], 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/microfetch/src/main.rs b/microfetch/src/main.rs index 7a9b624..8f9fb23 100644 --- a/microfetch/src/main.rs +++ b/microfetch/src/main.rs @@ -3,6 +3,7 @@ #![cfg_attr( any( target_arch = "powerpc64", + target_arch = "powerpc", target_arch = "sparc64", target_arch = "mips64" ), @@ -137,6 +138,22 @@ unsafe extern "C" fn _start() { ); } +// 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`. From 205e637d89a1defbe789da112c15edb60869c92d Mon Sep 17 00:00:00 2001 From: Amaan Qureshi Date: Fri, 17 Apr 2026 12:42:13 -0400 Subject: [PATCH 11/13] arch: add sparc32 support --- crates/asm/src/lib.rs | 23 ++++-- crates/asm/src/sparc.rs | 165 ++++++++++++++++++++++++++++++++++++++++ microfetch/src/main.rs | 33 ++++++++ 3 files changed, 214 insertions(+), 7 deletions(-) create mode 100644 crates/asm/src/sparc.rs diff --git a/crates/asm/src/lib.rs b/crates/asm/src/lib.rs index 9dba972..4f213bc 100644 --- a/crates/asm/src/lib.rs +++ b/crates/asm/src/lib.rs @@ -7,7 +7,7 @@ //! //! Supports `x86_64`, `aarch64`, `riscv64`, `loongarch64`, `s390x`, //! `powerpc64`, `arm` (armv7), `riscv32`, `sparc64`, `mips64`, `x86` (i686), -//! and `powerpc` (ppc32) architectures. +//! `powerpc` (ppc32), and `sparc` (sparc32) architectures. #![no_std] #![cfg_attr( @@ -15,6 +15,7 @@ target_arch = "powerpc64", target_arch = "powerpc", target_arch = "sparc64", + target_arch = "sparc", target_arch = "mips64" ), feature(asm_experimental_arch) @@ -33,12 +34,13 @@ target_arch = "sparc64", target_arch = "mips64", target_arch = "x86", - target_arch = "powerpc" + target_arch = "powerpc", + target_arch = "sparc" )))] compile_error!( "Unsupported architecture: only x86_64, aarch64, riscv64, loongarch64, \ - s390x, powerpc64, arm, riscv32, sparc64, mips64, x86, and powerpc are \ - supported" + s390x, powerpc64, arm, riscv32, sparc64, mips64, x86, powerpc, and sparc \ + are supported" ); // Per-arch syscall implementations live in their own module files. @@ -78,6 +80,9 @@ mod arch; #[cfg(target_arch = "powerpc")] #[path = "powerpc.rs"] mod arch; +#[cfg(target_arch = "sparc")] +#[path = "sparc.rs"] +mod arch; /// Copies `n` bytes from `src` to `dest`. /// @@ -317,6 +322,7 @@ pub unsafe fn sys_uname(buf: *mut UtsNameBuf) -> i32 { target_arch = "riscv32", target_arch = "x86", target_arch = "powerpc", + target_arch = "sparc", target_arch = "mips64" )))] pub struct StatfsBuf { @@ -363,7 +369,8 @@ pub struct StatfsBuf { target_arch = "arm", target_arch = "riscv32", target_arch = "x86", - target_arch = "powerpc" + target_arch = "powerpc", + target_arch = "sparc" ))] pub struct StatfsBuf { pub f_type: u32, @@ -476,7 +483,8 @@ pub fn read_file_fast(path: &str, buffer: &mut [u8]) -> Result { target_arch = "arm", target_arch = "riscv32", target_arch = "x86", - target_arch = "powerpc" + target_arch = "powerpc", + target_arch = "sparc" )))] pub struct SysInfo { pub uptime: i64, @@ -505,7 +513,8 @@ pub struct SysInfo { target_arch = "arm", target_arch = "riscv32", target_arch = "x86", - target_arch = "powerpc" + target_arch = "powerpc", + target_arch = "sparc" ))] pub struct SysInfo { pub uptime: i32, 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/microfetch/src/main.rs b/microfetch/src/main.rs index 8f9fb23..84fce24 100644 --- a/microfetch/src/main.rs +++ b/microfetch/src/main.rs @@ -5,6 +5,7 @@ target_arch = "powerpc64", target_arch = "powerpc", target_arch = "sparc64", + target_arch = "sparc", target_arch = "mips64" ), feature(asm_experimental_arch) @@ -247,6 +248,38 @@ unsafe extern "C" fn _start() { ); } +#[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) } +} + // Global allocator #[global_allocator] static ALLOCATOR: BumpAllocator = BumpAllocator::new(); From 277eec55b0c242a871cf07d168b050e9d56a81df Mon Sep 17 00:00:00 2001 From: Amaan Qureshi Date: Fri, 17 Apr 2026 12:52:15 -0400 Subject: [PATCH 12/13] arch: add mips32 support --- .github/workflows/rust.yml | 7 +- crates/asm/src/lib.rs | 46 +++++++-- crates/asm/src/mips.rs | 196 +++++++++++++++++++++++++++++++++++++ microfetch/src/main.rs | 19 +++- 4 files changed, 258 insertions(+), 10 deletions(-) create mode 100644 crates/asm/src/mips.rs diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index a764da5..2eb3a1f 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -52,6 +52,11 @@ jobs: # 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" @@ -77,7 +82,7 @@ jobs: target: ${{ matrix.target }} - name: "Build" - run: cargo build --verbose --target ${{ matrix.target }} ${{ matrix.build_std && '-Z build-std=core,alloc,panic_abort' || '' }} + 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. diff --git a/crates/asm/src/lib.rs b/crates/asm/src/lib.rs index 4f213bc..a356f1c 100644 --- a/crates/asm/src/lib.rs +++ b/crates/asm/src/lib.rs @@ -7,7 +7,7 @@ //! //! Supports `x86_64`, `aarch64`, `riscv64`, `loongarch64`, `s390x`, //! `powerpc64`, `arm` (armv7), `riscv32`, `sparc64`, `mips64`, `x86` (i686), -//! `powerpc` (ppc32), and `sparc` (sparc32) architectures. +//! `powerpc` (ppc32), `sparc` (sparc32), and `mips` (O32) architectures. #![no_std] #![cfg_attr( @@ -16,7 +16,8 @@ target_arch = "powerpc", target_arch = "sparc64", target_arch = "sparc", - target_arch = "mips64" + target_arch = "mips64", + target_arch = "mips" ), feature(asm_experimental_arch) )] @@ -35,12 +36,13 @@ target_arch = "mips64", target_arch = "x86", target_arch = "powerpc", - target_arch = "sparc" + target_arch = "sparc", + target_arch = "mips" )))] compile_error!( "Unsupported architecture: only x86_64, aarch64, riscv64, loongarch64, \ - s390x, powerpc64, arm, riscv32, sparc64, mips64, x86, powerpc, and sparc \ - are supported" + s390x, powerpc64, arm, riscv32, sparc64, mips64, x86, powerpc, sparc, and \ + mips are supported" ); // Per-arch syscall implementations live in their own module files. @@ -83,6 +85,9 @@ mod arch; #[cfg(target_arch = "sparc")] #[path = "sparc.rs"] mod arch; +#[cfg(target_arch = "mips")] +#[path = "mips.rs"] +mod arch; /// Copies `n` bytes from `src` to `dest`. /// @@ -323,7 +328,8 @@ pub unsafe fn sys_uname(buf: *mut UtsNameBuf) -> i32 { target_arch = "x86", target_arch = "powerpc", target_arch = "sparc", - target_arch = "mips64" + target_arch = "mips64", + target_arch = "mips" )))] pub struct StatfsBuf { pub f_type: i64, @@ -389,6 +395,28 @@ pub struct StatfsBuf { 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)] @@ -484,7 +512,8 @@ pub fn read_file_fast(path: &str, buffer: &mut [u8]) -> Result { target_arch = "riscv32", target_arch = "x86", target_arch = "powerpc", - target_arch = "sparc" + target_arch = "sparc", + target_arch = "mips" )))] pub struct SysInfo { pub uptime: i64, @@ -514,7 +543,8 @@ pub struct SysInfo { target_arch = "riscv32", target_arch = "x86", target_arch = "powerpc", - target_arch = "sparc" + target_arch = "sparc", + target_arch = "mips" ))] pub struct SysInfo { pub uptime: i32, 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/microfetch/src/main.rs b/microfetch/src/main.rs index 84fce24..9d135c3 100644 --- a/microfetch/src/main.rs +++ b/microfetch/src/main.rs @@ -6,7 +6,8 @@ target_arch = "powerpc", target_arch = "sparc64", target_arch = "sparc", - target_arch = "mips64" + target_arch = "mips64", + target_arch = "mips" ), feature(asm_experimental_arch) )] @@ -280,6 +281,22 @@ 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(); From fb245bb5eecdc405610e224dd38fa005fe19d0b8 Mon Sep 17 00:00:00 2001 From: Amaan Qureshi Date: Sat, 18 Apr 2026 19:36:05 -0400 Subject: [PATCH 13/13] asm: bump MSRV to 1.95 and collapse arch dispatch with cfg_select! --- Cargo.toml | 2 +- crates/asm/src/lib.rs | 84 +++++++++--------------------------------- microfetch/src/main.rs | 2 - 3 files changed, 18 insertions(+), 70 deletions(-) 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/lib.rs b/crates/asm/src/lib.rs index a356f1c..fff8d7f 100644 --- a/crates/asm/src/lib.rs +++ b/crates/asm/src/lib.rs @@ -12,8 +12,6 @@ #![no_std] #![cfg_attr( any( - target_arch = "powerpc64", - target_arch = "powerpc", target_arch = "sparc64", target_arch = "sparc", target_arch = "mips64", @@ -22,72 +20,24 @@ 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", - target_arch = "loongarch64", - target_arch = "s390x", - target_arch = "powerpc64", - target_arch = "arm", - target_arch = "riscv32", - target_arch = "sparc64", - target_arch = "mips64", - target_arch = "x86", - target_arch = "powerpc", - target_arch = "sparc", - target_arch = "mips" -)))] -compile_error!( - "Unsupported architecture: only x86_64, aarch64, riscv64, loongarch64, \ - s390x, powerpc64, arm, riscv32, sparc64, mips64, x86, powerpc, sparc, and \ - mips are supported" -); - // Per-arch syscall implementations live in their own module files. -#[cfg(target_arch = "x86_64")] -#[path = "x86_64.rs"] -mod arch; -#[cfg(target_arch = "aarch64")] -#[path = "aarch64.rs"] -mod arch; -#[cfg(target_arch = "riscv64")] -#[path = "riscv64.rs"] -mod arch; -#[cfg(target_arch = "loongarch64")] -#[path = "loongarch64.rs"] -mod arch; -#[cfg(target_arch = "s390x")] -#[path = "s390x.rs"] -mod arch; -#[cfg(target_arch = "powerpc64")] -#[path = "powerpc64.rs"] -mod arch; -#[cfg(target_arch = "arm")] -#[path = "arm.rs"] -mod arch; -#[cfg(target_arch = "riscv32")] -#[path = "riscv32.rs"] -mod arch; -#[cfg(target_arch = "sparc64")] -#[path = "sparc64.rs"] -mod arch; -#[cfg(target_arch = "mips64")] -#[path = "mips64.rs"] -mod arch; -#[cfg(target_arch = "x86")] -#[path = "x86.rs"] -mod arch; -#[cfg(target_arch = "powerpc")] -#[path = "powerpc.rs"] -mod arch; -#[cfg(target_arch = "sparc")] -#[path = "sparc.rs"] -mod arch; -#[cfg(target_arch = "mips")] -#[path = "mips.rs"] -mod arch; +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`. /// diff --git a/microfetch/src/main.rs b/microfetch/src/main.rs index 9d135c3..d1b39b0 100644 --- a/microfetch/src/main.rs +++ b/microfetch/src/main.rs @@ -2,8 +2,6 @@ #![no_main] #![cfg_attr( any( - target_arch = "powerpc64", - target_arch = "powerpc", target_arch = "sparc64", target_arch = "sparc", target_arch = "mips64",