From 403f3b2f5a4569596374af01e539b369d124ec3b Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sat, 5 Oct 2024 02:09:49 +0900 Subject: [PATCH] s390x linux_raw support --- .github/workflows/main.yml | 12 +- Cargo.toml | 6 +- build.rs | 3 +- src/backend/linux_raw/arch/mod.rs | 2 + src/backend/linux_raw/arch/s390x.rs | 287 ++++++++++++++++++++++ src/backend/linux_raw/net/syscalls.rs | 18 +- src/backend/linux_raw/reg.rs | 2 +- src/backend/linux_raw/runtime/syscalls.rs | 1 + src/backend/linux_raw/vdso_wrappers.rs | 5 +- src/runtime.rs | 14 +- 10 files changed, 339 insertions(+), 11 deletions(-) create mode 100644 src/backend/linux_raw/arch/s390x.rs diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9ec7356cc..0ef2a951b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -638,7 +638,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - build: [powerpc64le-linux] + build: [powerpc64le-linux, s390x-linux] include: - build: powerpc64le-linux os: ubuntu-latest @@ -649,11 +649,21 @@ jobs: qemu: qemu-ppc64le qemu_args: -L /usr/powerpc64le-linux-gnu qemu_target: ppc64le-linux-user + - build: s390x-linux + os: ubuntu-latest + rust: nightly + target: s390x-unknown-linux-gnu + gcc_package: gcc-s390x-linux-gnu + gcc: s390x-linux-gnu-gcc + qemu: qemu-s390x + qemu_args: -L /usr/s390x-linux-gnu + qemu_target: s390x-linux-user env: # -D warnings is commented out in our install-rust action; re-add it here. RUSTFLAGS: --cfg rustix_use_experimental_asm -D warnings -D elided-lifetimes-in-paths RUSTDOCFLAGS: --cfg rustix_use_experimental_asm CARGO_TARGET_POWERPC64LE_UNKNOWN_LINUX_GNU_RUSTFLAGS: --cfg rustix_use_experimental_asm + CARGO_TARGET_S390X_UNKNOWN_LINUX_GNU_RUSTFLAGS: --cfg rustix_use_experimental_asm QEMU_BUILD_VERSION: 8.1.2 steps: - uses: actions/checkout@v4 diff --git a/Cargo.toml b/Cargo.toml index 04cf77264..9cba82985 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,7 +35,7 @@ once_cell = { version = "1.5.2", optional = true } # addition to the libc backend. The linux_raw backend is used by default. The # libc backend can be selected via adding `--cfg=rustix_use_libc` to # `RUSTFLAGS` or enabling the `use-libc` cargo feature. -[target.'cfg(all(not(rustix_use_libc), not(miri), target_os = "linux", target_endian = "little", any(target_arch = "arm", all(target_arch = "aarch64", target_pointer_width = "64"), target_arch = "riscv64", all(rustix_use_experimental_asm, target_arch = "powerpc64"), all(rustix_use_experimental_asm, target_arch = "mips"), all(rustix_use_experimental_asm, target_arch = "mips32r6"), all(rustix_use_experimental_asm, target_arch = "mips64"), all(rustix_use_experimental_asm, target_arch = "mips64r6"), target_arch = "x86", all(target_arch = "x86_64", target_pointer_width = "64"))))'.dependencies] +[target.'cfg(all(not(rustix_use_libc), not(miri), target_os = "linux", any(target_endian = "little", target_arch = "s390x"), any(target_arch = "arm", all(target_arch = "aarch64", target_pointer_width = "64"), target_arch = "riscv64", all(rustix_use_experimental_asm, target_arch = "powerpc64"), all(rustix_use_experimental_asm, target_arch = "s390x"), all(rustix_use_experimental_asm, target_arch = "mips"), all(rustix_use_experimental_asm, target_arch = "mips32r6"), all(rustix_use_experimental_asm, target_arch = "mips64"), all(rustix_use_experimental_asm, target_arch = "mips64r6"), target_arch = "x86", all(target_arch = "x86_64", target_pointer_width = "64"))))'.dependencies] linux-raw-sys = { version = "0.4.14", default-features = false, features = ["general", "errno", "ioctl", "no_std", "elf"] } libc_errno = { package = "errno", version = "0.3.8", default-features = false, optional = true } libc = { version = "0.2.156", default-features = false, optional = true } @@ -44,7 +44,7 @@ libc = { version = "0.2.156", default-features = false, optional = true } # # On all other Unix-family platforms, and under Miri, we always use the libc # backend, so enable its dependencies unconditionally. -[target.'cfg(all(not(windows), any(rustix_use_libc, miri, not(all(target_os = "linux", target_endian = "little", any(target_arch = "arm", all(target_arch = "aarch64", target_pointer_width = "64"), target_arch = "riscv64", all(rustix_use_experimental_asm, target_arch = "powerpc64"), all(rustix_use_experimental_asm, target_arch = "mips"), all(rustix_use_experimental_asm, target_arch = "mips32r6"), all(rustix_use_experimental_asm, target_arch = "mips64"), all(rustix_use_experimental_asm, target_arch = "mips64r6"), target_arch = "x86", all(target_arch = "x86_64", target_pointer_width = "64")))))))'.dependencies] +[target.'cfg(all(not(windows), any(rustix_use_libc, miri, not(all(target_os = "linux", any(target_endian = "little", target_arch = "s390x"), any(target_arch = "arm", all(target_arch = "aarch64", target_pointer_width = "64"), target_arch = "riscv64", all(rustix_use_experimental_asm, target_arch = "powerpc64"), all(rustix_use_experimental_asm, target_arch = "s390x"), all(rustix_use_experimental_asm, target_arch = "mips"), all(rustix_use_experimental_asm, target_arch = "mips32r6"), all(rustix_use_experimental_asm, target_arch = "mips64"), all(rustix_use_experimental_asm, target_arch = "mips64r6"), target_arch = "x86", all(target_arch = "x86_64", target_pointer_width = "64")))))))'.dependencies] libc_errno = { package = "errno", version = "0.3.8", default-features = false } libc = { version = "0.2.156", default-features = false } @@ -52,7 +52,7 @@ libc = { version = "0.2.156", default-features = false } # # Some syscalls do not have libc wrappers, such as in `io_uring`. For these, # the libc backend uses the linux-raw-sys ABI and `libc::syscall`. -[target.'cfg(all(any(target_os = "android", target_os = "linux"), any(rustix_use_libc, miri, not(all(target_os = "linux", target_endian = "little", any(target_arch = "arm", all(target_arch = "aarch64", target_pointer_width = "64"), target_arch = "riscv64", all(rustix_use_experimental_asm, target_arch = "powerpc64"), all(rustix_use_experimental_asm, target_arch = "mips"), all(rustix_use_experimental_asm, target_arch = "mips32r6"), all(rustix_use_experimental_asm, target_arch = "mips64"), all(rustix_use_experimental_asm, target_arch = "mips64r6"), target_arch = "x86", all(target_arch = "x86_64", target_pointer_width = "64")))))))'.dependencies] +[target.'cfg(all(any(target_os = "android", target_os = "linux"), any(rustix_use_libc, miri, not(all(target_os = "linux", any(target_endian = "little", target_arch = "s390x"), any(target_arch = "arm", all(target_arch = "aarch64", target_pointer_width = "64"), target_arch = "riscv64", all(rustix_use_experimental_asm, target_arch = "powerpc64"), all(rustix_use_experimental_asm, target_arch = "s390x"), all(rustix_use_experimental_asm, target_arch = "mips"), all(rustix_use_experimental_asm, target_arch = "mips32r6"), all(rustix_use_experimental_asm, target_arch = "mips64"), all(rustix_use_experimental_asm, target_arch = "mips64r6"), target_arch = "x86", all(target_arch = "x86_64", target_pointer_width = "64")))))))'.dependencies] linux-raw-sys = { version = "0.4.14", default-features = false, features = ["general", "ioctl", "no_std"] } # For the libc backend on Windows, use the Winsock API in windows-sys. diff --git a/build.rs b/build.rs index feff5bf2f..cac0aeb36 100644 --- a/build.rs +++ b/build.rs @@ -95,7 +95,8 @@ fn main() { || !inline_asm_name_present || is_unsupported_abi || miri - || ((arch == "powerpc64" || arch.starts_with("mips")) && !rustix_use_experimental_asm); + || ((arch == "powerpc64" || arch == "s390x" || arch.starts_with("mips")) + && !rustix_use_experimental_asm); if libc { // Use the libc backend. use_feature("libc"); diff --git a/src/backend/linux_raw/arch/mod.rs b/src/backend/linux_raw/arch/mod.rs index ac9e25fa7..7dd54baf6 100644 --- a/src/backend/linux_raw/arch/mod.rs +++ b/src/backend/linux_raw/arch/mod.rs @@ -32,6 +32,7 @@ #[cfg_attr(target_arch = "mips64r6", path = "mips64r6.rs")] #[cfg_attr(target_arch = "powerpc64", path = "powerpc64.rs")] #[cfg_attr(target_arch = "riscv64", path = "riscv64.rs")] +#[cfg_attr(target_arch = "s390x", path = "s390x.rs")] #[cfg_attr(target_arch = "x86", path = "x86.rs")] #[cfg_attr(target_arch = "x86_64", path = "x86_64.rs")] pub(in crate::backend) mod asm; @@ -47,6 +48,7 @@ pub(in crate::backend) mod asm; target_arch = "mips64r6", target_arch = "powerpc64", target_arch = "riscv64", + target_arch = "s390x", target_arch = "x86_64", ))] pub(in crate::backend) use self::asm as choose; diff --git a/src/backend/linux_raw/arch/s390x.rs b/src/backend/linux_raw/arch/s390x.rs new file mode 100644 index 000000000..121a50806 --- /dev/null +++ b/src/backend/linux_raw/arch/s390x.rs @@ -0,0 +1,287 @@ +//! s390x Linux system calls. + +use crate::backend::reg::{ + ArgReg, FromAsm, RetReg, SyscallNumber, ToAsm, A0, A1, A2, A3, A4, A5, R0, +}; +use core::arch::asm; + +#[inline] +pub(in crate::backend) unsafe fn syscall0_readonly(nr: SyscallNumber<'_>) -> RetReg { + let r0; + asm!( + "svc 0", + in("r1") nr.to_asm(), + lateout("r2") r0, + options(nostack, preserves_flags, readonly) + ); + FromAsm::from_asm(r0) +} + +#[inline] +pub(in crate::backend) unsafe fn syscall1(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> RetReg { + let r0; + asm!( + "svc 0", + in("r1") nr.to_asm(), + inlateout("r2") a0.to_asm() => r0, + options(nostack, preserves_flags) + ); + FromAsm::from_asm(r0) +} + +#[inline] +pub(in crate::backend) unsafe fn syscall1_readonly( + nr: SyscallNumber<'_>, + a0: ArgReg<'_, A0>, +) -> RetReg { + let r0; + asm!( + "svc 0", + in("r1") nr.to_asm(), + inlateout("r2") a0.to_asm() => r0, + options(nostack, preserves_flags, readonly) + ); + FromAsm::from_asm(r0) +} + +#[inline] +pub(in crate::backend) unsafe fn syscall1_noreturn(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> ! { + asm!( + "svc 0", + in("r1") nr.to_asm(), + in("r2") a0.to_asm(), + options(nostack, preserves_flags, noreturn) + ) +} + +#[inline] +pub(in crate::backend) unsafe fn syscall2( + nr: SyscallNumber<'_>, + a0: ArgReg<'_, A0>, + a1: ArgReg<'_, A1>, +) -> RetReg { + let r0; + asm!( + "svc 0", + in("r1") nr.to_asm(), + inlateout("r2") a0.to_asm() => r0, + in("r3") a1.to_asm(), + options(nostack, preserves_flags) + ); + FromAsm::from_asm(r0) +} + +#[inline] +pub(in crate::backend) unsafe fn syscall2_readonly( + nr: SyscallNumber<'_>, + a0: ArgReg<'_, A0>, + a1: ArgReg<'_, A1>, +) -> RetReg { + let r0; + asm!( + "svc 0", + in("r1") nr.to_asm(), + inlateout("r2") a0.to_asm() => r0, + in("r3") a1.to_asm(), + options(nostack, preserves_flags, readonly) + ); + FromAsm::from_asm(r0) +} + +#[inline] +pub(in crate::backend) unsafe fn syscall3( + nr: SyscallNumber<'_>, + a0: ArgReg<'_, A0>, + a1: ArgReg<'_, A1>, + a2: ArgReg<'_, A2>, +) -> RetReg { + let r0; + asm!( + "svc 0", + in("r1") nr.to_asm(), + inlateout("r2") a0.to_asm() => r0, + in("r3") a1.to_asm(), + in("r4") a2.to_asm(), + options(nostack, preserves_flags) + ); + FromAsm::from_asm(r0) +} + +#[inline] +pub(in crate::backend) unsafe fn syscall3_readonly( + nr: SyscallNumber<'_>, + a0: ArgReg<'_, A0>, + a1: ArgReg<'_, A1>, + a2: ArgReg<'_, A2>, +) -> RetReg { + let r0; + asm!( + "svc 0", + in("r1") nr.to_asm(), + inlateout("r2") a0.to_asm() => r0, + in("r3") a1.to_asm(), + in("r4") a2.to_asm(), + options(nostack, preserves_flags, readonly) + ); + FromAsm::from_asm(r0) +} + +#[inline] +pub(in crate::backend) unsafe fn syscall4( + nr: SyscallNumber<'_>, + a0: ArgReg<'_, A0>, + a1: ArgReg<'_, A1>, + a2: ArgReg<'_, A2>, + a3: ArgReg<'_, A3>, +) -> RetReg { + let r0; + asm!( + "svc 0", + in("r1") nr.to_asm(), + inlateout("r2") a0.to_asm() => r0, + in("r3") a1.to_asm(), + in("r4") a2.to_asm(), + in("r5") a3.to_asm(), + options(nostack, preserves_flags) + ); + FromAsm::from_asm(r0) +} + +#[inline] +pub(in crate::backend) unsafe fn syscall4_readonly( + nr: SyscallNumber<'_>, + a0: ArgReg<'_, A0>, + a1: ArgReg<'_, A1>, + a2: ArgReg<'_, A2>, + a3: ArgReg<'_, A3>, +) -> RetReg { + let r0; + asm!( + "svc 0", + in("r1") nr.to_asm(), + inlateout("r2") a0.to_asm() => r0, + in("r3") a1.to_asm(), + in("r4") a2.to_asm(), + in("r5") a3.to_asm(), + options(nostack, preserves_flags, readonly) + ); + FromAsm::from_asm(r0) +} + +#[inline] +pub(in crate::backend) unsafe fn syscall5( + nr: SyscallNumber<'_>, + a0: ArgReg<'_, A0>, + a1: ArgReg<'_, A1>, + a2: ArgReg<'_, A2>, + a3: ArgReg<'_, A3>, + a4: ArgReg<'_, A4>, +) -> RetReg { + let r0; + asm!( + "svc 0", + in("r1") nr.to_asm(), + inlateout("r2") a0.to_asm() => r0, + in("r3") a1.to_asm(), + in("r4") a2.to_asm(), + in("r5") a3.to_asm(), + in("r6") a4.to_asm(), + options(nostack, preserves_flags) + ); + FromAsm::from_asm(r0) +} + +#[inline] +pub(in crate::backend) unsafe fn syscall5_readonly( + nr: SyscallNumber<'_>, + a0: ArgReg<'_, A0>, + a1: ArgReg<'_, A1>, + a2: ArgReg<'_, A2>, + a3: ArgReg<'_, A3>, + a4: ArgReg<'_, A4>, +) -> RetReg { + let r0; + asm!( + "svc 0", + in("r1") nr.to_asm(), + inlateout("r2") a0.to_asm() => r0, + in("r3") a1.to_asm(), + in("r4") a2.to_asm(), + in("r5") a3.to_asm(), + in("r6") a4.to_asm(), + options(nostack, preserves_flags, readonly) + ); + FromAsm::from_asm(r0) +} + +#[inline] +pub(in crate::backend) unsafe fn syscall6( + nr: SyscallNumber<'_>, + a0: ArgReg<'_, A0>, + a1: ArgReg<'_, A1>, + a2: ArgReg<'_, A2>, + a3: ArgReg<'_, A3>, + a4: ArgReg<'_, A4>, + a5: ArgReg<'_, A5>, +) -> RetReg { + if nr.nr == linux_raw_sys::general::__NR_mmap as usize { + let mut a = [ + a0.to_asm(), + a1.to_asm(), + a2.to_asm(), + a3.to_asm(), + a4.to_asm(), + a5.to_asm(), + ]; + return syscall1(nr, a.as_mut_ptr().into()); + } + let r0; + asm!( + "svc 0", + in("r1") nr.to_asm(), + inlateout("r2") a0.to_asm() => r0, + in("r3") a1.to_asm(), + in("r4") a2.to_asm(), + in("r5") a3.to_asm(), + in("r6") a4.to_asm(), + in("r7") a5.to_asm(), + options(nostack, preserves_flags) + ); + FromAsm::from_asm(r0) +} + +#[inline] +pub(in crate::backend) unsafe fn syscall6_readonly( + nr: SyscallNumber<'_>, + a0: ArgReg<'_, A0>, + a1: ArgReg<'_, A1>, + a2: ArgReg<'_, A2>, + a3: ArgReg<'_, A3>, + a4: ArgReg<'_, A4>, + a5: ArgReg<'_, A5>, +) -> RetReg { + if nr.nr == linux_raw_sys::general::__NR_mmap as usize { + let a = [ + a0.to_asm(), + a1.to_asm(), + a2.to_asm(), + a3.to_asm(), + a4.to_asm(), + a5.to_asm(), + ]; + return syscall1_readonly(nr, a.as_ptr().into()); + } + let r0; + asm!( + "svc 0", + in("r1") nr.to_asm(), + inlateout("r2") a0.to_asm() => r0, + in("r3") a1.to_asm(), + in("r4") a2.to_asm(), + in("r5") a3.to_asm(), + in("r6") a4.to_asm(), + in("r7") a5.to_asm(), + options(nostack, preserves_flags, readonly) + ); + FromAsm::from_asm(r0) +} diff --git a/src/backend/linux_raw/net/syscalls.rs b/src/backend/linux_raw/net/syscalls.rs index 4d4427a40..213ac9c4a 100644 --- a/src/backend/linux_raw/net/syscalls.rs +++ b/src/backend/linux_raw/net/syscalls.rs @@ -135,7 +135,7 @@ pub(crate) fn socketpair( #[inline] pub(crate) fn accept(fd: BorrowedFd<'_>) -> io::Result { - #[cfg(not(target_arch = "x86"))] + #[cfg(not(any(target_arch = "x86", target_arch = "s390x")))] unsafe { let fd = ret_owned_fd(syscall_readonly!(__NR_accept, fd, zero(), zero()))?; Ok(fd) @@ -149,6 +149,11 @@ pub(crate) fn accept(fd: BorrowedFd<'_>) -> io::Result { ))?; Ok(fd) } + #[cfg(target_arch = "s390x")] + { + // accept is not available on s390x + accept_with(fd, SocketFlags::empty()) + } } #[inline] @@ -171,7 +176,7 @@ pub(crate) fn accept_with(fd: BorrowedFd<'_>, flags: SocketFlags) -> io::Result< #[inline] pub(crate) fn acceptfrom(fd: BorrowedFd<'_>) -> io::Result<(OwnedFd, Option)> { - #[cfg(not(target_arch = "x86"))] + #[cfg(not(any(target_arch = "x86", target_arch = "s390x")))] unsafe { let mut addrlen = core::mem::size_of::() as socklen_t; let mut storage = MaybeUninit::::uninit(); @@ -204,6 +209,11 @@ pub(crate) fn acceptfrom(fd: BorrowedFd<'_>) -> io::Result<(OwnedFd, Option, buf: &[u8], flags: SendFlags) -> io::Resu target_arch = "mips64", target_arch = "mips64r6", target_arch = "riscv64", + target_arch = "s390x", target_arch = "x86", target_arch = "x86_64", )))] @@ -479,6 +490,7 @@ pub(crate) fn send(fd: BorrowedFd<'_>, buf: &[u8], flags: SendFlags) -> io::Resu target_arch = "mips64", target_arch = "mips64r6", target_arch = "riscv64", + target_arch = "s390x", target_arch = "x86_64", ))] unsafe { @@ -672,6 +684,7 @@ pub(crate) unsafe fn recv( target_arch = "mips64", target_arch = "mips64r6", target_arch = "riscv64", + target_arch = "s390x", target_arch = "x86", target_arch = "x86_64", )))] @@ -683,6 +696,7 @@ pub(crate) unsafe fn recv( target_arch = "mips64", target_arch = "mips64r6", target_arch = "riscv64", + target_arch = "s390x", target_arch = "x86_64", ))] { diff --git a/src/backend/linux_raw/reg.rs b/src/backend/linux_raw/reg.rs index f05ac5f3c..57a8d2697 100644 --- a/src/backend/linux_raw/reg.rs +++ b/src/backend/linux_raw/reg.rs @@ -206,7 +206,7 @@ impl FromAsm for RetReg { #[repr(transparent)] pub(super) struct SyscallNumber<'a> { - nr: usize, + pub(super) nr: usize, _phantom: PhantomData<&'a ()>, } diff --git a/src/backend/linux_raw/runtime/syscalls.rs b/src/backend/linux_raw/runtime/syscalls.rs index 4b16ff5ba..eac0da324 100644 --- a/src/backend/linux_raw/runtime/syscalls.rs +++ b/src/backend/linux_raw/runtime/syscalls.rs @@ -60,6 +60,7 @@ pub(crate) unsafe fn fork() -> io::Result { target_arch = "mips64r6", target_arch = "powerpc64", target_arch = "riscv64", + target_arch = "s390x", target_arch = "x86" ))] let pid = ret_c_int(syscall_readonly!( diff --git a/src/backend/linux_raw/vdso_wrappers.rs b/src/backend/linux_raw/vdso_wrappers.rs index f97857f8d..bd539e96c 100644 --- a/src/backend/linux_raw/vdso_wrappers.rs +++ b/src/backend/linux_raw/vdso_wrappers.rs @@ -505,6 +505,8 @@ fn init() { let ptr = vdso.sym(cstr!("LINUX_4.15"), cstr!("__vdso_clock_gettime")); #[cfg(target_arch = "powerpc64")] let ptr = vdso.sym(cstr!("LINUX_2.6.15"), cstr!("__kernel_clock_gettime")); + #[cfg(target_arch = "s390x")] + let ptr = vdso.sym(cstr!("LINUX_2.6.29"), cstr!("__kernel_clock_gettime")); #[cfg(any(target_arch = "mips", target_arch = "mips32r6"))] let ptr = vdso.sym(cstr!("LINUX_2.6"), cstr!("__vdso_clock_gettime64")); #[cfg(any(target_arch = "mips64", target_arch = "mips64r6"))] @@ -573,7 +575,8 @@ fn init() { target_arch = "mips", target_arch = "mips32r6", target_arch = "mips64", - target_arch = "mips64r6" + target_arch = "mips64r6", + target_arch = "s390x", ))] let ok = false; diff --git a/src/runtime.rs b/src/runtime.rs index eab7c1d0e..a690dde7d 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -583,13 +583,23 @@ pub const SIGRTMIN: u32 = linux_raw_sys::general::SIGRTMIN; #[cfg(linux_raw)] pub const SIGRTMAX: u32 = { // Use the actual `SIGRTMAX` value on platforms which define it. - #[cfg(not(any(target_arch = "arm", target_arch = "x86", target_arch = "x86_64")))] + #[cfg(not(any( + target_arch = "arm", + target_arch = "s390x", + target_arch = "x86", + target_arch = "x86_64", + )))] { linux_raw_sys::general::SIGRTMAX } // On platforms that don't, derive it from `_NSIG`. - #[cfg(any(target_arch = "arm", target_arch = "x86", target_arch = "x86_64"))] + #[cfg(any( + target_arch = "arm", + target_arch = "s390x", + target_arch = "x86", + target_arch = "x86_64", + ))] { linux_raw_sys::general::_NSIG - 1 }