From 277eec55b0c242a871cf07d168b050e9d56a81df Mon Sep 17 00:00:00 2001 From: Amaan Qureshi Date: Fri, 17 Apr 2026 12:52:15 -0400 Subject: [PATCH] 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();