From a6fab02586b0637d17854e8bd34a003849a43d28 Mon Sep 17 00:00:00 2001 From: Amaan Qureshi Date: Fri, 17 Apr 2026 12:20:09 -0400 Subject: [PATCH] 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)]