From 7bbc47eb9f01bb3b8b5114cd31086ca1bd753922 Mon Sep 17 00:00:00 2001 From: Amaan Qureshi Date: Fri, 17 Apr 2026 12:32:03 -0400 Subject: [PATCH] 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`.