arch: add riscv32 support

This commit is contained in:
Amaan Qureshi 2026-04-16 23:41:43 -04:00
commit 0f95ca0f68
No known key found for this signature in database
4 changed files with 185 additions and 10 deletions

View file

@ -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' || '' }}

View file

@ -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<usize, i32> {
/// 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],

142
crates/asm/src/riscv32.rs Normal file
View file

@ -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::<StatfsBuf>(),
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)
);
}
}

View file

@ -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();