]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
tools/nolibc: add support for 32-bit parisc
authorThomas Weißschuh <linux@weissschuh.net>
Mon, 6 Apr 2026 21:43:05 +0000 (23:43 +0200)
committerThomas Weißschuh <linux@weissschuh.net>
Sun, 3 May 2026 14:39:54 +0000 (16:39 +0200)
Extend nolibc to target the 32-bit parisc architecture.
64-bit is not yet supported.

Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
Acked-by: Willy Tarreau <w@1wt.eu>
Link: https://patch/msgid.link/20260428-nolibc-hppa-v5-2-d843d573111a@weissschuh.net
tools/include/nolibc/Makefile
tools/include/nolibc/arch-parisc.h [new file with mode: 0644]
tools/include/nolibc/arch.h
tools/testing/selftests/nolibc/Makefile.nolibc
tools/testing/selftests/nolibc/run-tests.sh

index 2cb4d610fe535cef7171a2dff26d41a08d6b3aaf..00fd2e566d755956118b9767acae952827a1173e 100644 (file)
@@ -17,7 +17,7 @@ endif
 # it defaults to this nolibc directory.
 OUTPUT ?= $(CURDIR)/
 
-architectures := arm arm64 loongarch m68k mips openrisc powerpc riscv s390 sh sparc x86
+architectures := arm arm64 loongarch m68k mips openrisc parisc powerpc riscv s390 sh sparc x86
 arch_files := arch.h $(addsuffix .h, $(addprefix arch-, $(architectures)))
 all_files := \
                alloca.h \
diff --git a/tools/include/nolibc/arch-parisc.h b/tools/include/nolibc/arch-parisc.h
new file mode 100644 (file)
index 0000000..417043e
--- /dev/null
@@ -0,0 +1,185 @@
+/* SPDX-License-Identifier: LGPL-2.1 OR MIT */
+/*
+ * parisc/hppa (32-bit) specific definitions for NOLIBC
+ * Copyright (C) 2026 Thomas Weißschuh <linux@weissschuh.net>
+ */
+
+#ifndef _NOLIBC_ARCH_PARISC_H
+#define _NOLIBC_ARCH_PARISC_H
+
+#if defined(__LP64__)
+#error 64-bit not supported
+#endif
+
+#include "compiler.h"
+#include "crt.h"
+
+/* Syscalls for parisc :
+ *   - syscall number is passed in r20
+ *   - arguments are in r26 to r21
+ *   - the system call is performed by calling "ble 0x100(%sr2, %r0)",
+ *     the instruction after that is in the delay slot and executed before
+ *     the jump to 0x100 actually happens, use it to load the syscall number
+ *   - syscall return comes in r28
+ *   - the arguments are cast to long and assigned into the target
+ *     registers which are then simply passed as registers to the asm code,
+ *     so that we don't have to experience issues with register constraints.
+ */
+
+#define _NOLIBC_SYSCALL_CLOBBERLIST \
+       "memory", "%r1", "%r2", "%r4", "%r20", "%r29", "%r31"
+
+#define __nolibc_syscall0(num)                                                \
+({                                                                            \
+       register long _ret __asm__ ("r28");                                   \
+                                                                             \
+       __asm__ volatile (                                                    \
+               "ble 0x100(%%sr2, %%r0)\n\t"                                  \
+               "copy %1, %%r20\n\t"                                          \
+               : "=r"(_ret)                                                  \
+               : "r"(num)                                                    \
+               : _NOLIBC_SYSCALL_CLOBBERLIST,                                \
+                 "%r21", "%r22", "%r23", "%r24", "%r25", "%r26"              \
+       );                                                                    \
+       _ret;                                                                 \
+})
+
+#define __nolibc_syscall1(num, arg1)                                          \
+({                                                                            \
+       register long _ret __asm__ ("r28");                                   \
+       register long _arg1 __asm__ ("r26") = (long)(arg1);                   \
+                                                                             \
+       __asm__ volatile (                                                    \
+               "ble 0x100(%%sr2, %%r0)\n\t"                                  \
+               "copy %2, %%r20\n\t"                                          \
+               : "=r"(_ret),                                                 \
+                 "+r"(_arg1)                                                 \
+               : "r"(num)                                                    \
+               : _NOLIBC_SYSCALL_CLOBBERLIST,                                \
+                 "%r21", "%r22", "%r23", "%r24", "%r25"                      \
+       );                                                                    \
+       _ret;                                                                 \
+})
+
+#define __nolibc_syscall2(num, arg1, arg2)                                    \
+({                                                                            \
+       register long _ret __asm__ ("r28");                                   \
+       register long _arg1 __asm__ ("r26") = (long)(arg1);                   \
+       register long _arg2 __asm__ ("r25") = (long)(arg2);                   \
+                                                                             \
+       __asm__ volatile (                                                    \
+               "ble 0x100(%%sr2, %%r0)\n\t"                                  \
+               "copy %3, %%r20\n\t"                                          \
+               : "=r"(_ret),                                                 \
+                 "+r"(_arg1), "+r"(_arg2)                                    \
+               : "r"(num)                                                    \
+               : _NOLIBC_SYSCALL_CLOBBERLIST,                                \
+                 "%r21", "%r22", "%r23", "%r24"                              \
+       );                                                                    \
+       _ret;                                                                 \
+})
+
+#define __nolibc_syscall3(num, arg1, arg2, arg3)                              \
+({                                                                            \
+       register long _ret __asm__ ("r28");                                   \
+       register long _arg1 __asm__ ("r26") = (long)(arg1);                   \
+       register long _arg2 __asm__ ("r25") = (long)(arg2);                   \
+       register long _arg3 __asm__ ("r24") = (long)(arg3);                   \
+                                                                             \
+       __asm__ volatile (                                                    \
+               "ble 0x100(%%sr2, %%r0)\n\t"                                  \
+               "copy %4, %%r20\n\t"                                          \
+               : "=r"(_ret),                                                 \
+                 "+r"(_arg1), "+r"(_arg2), "+r"(_arg3)                       \
+               : "r"(num)                                                    \
+               : _NOLIBC_SYSCALL_CLOBBERLIST,                                \
+                 "%r21", "%r22", "%r23"                                      \
+       );                                                                    \
+       _ret;                                                                 \
+})
+
+#define __nolibc_syscall4(num, arg1, arg2, arg3, arg4)                        \
+({                                                                            \
+       register long _ret __asm__ ("r28");                                   \
+       register long _arg1 __asm__ ("r26") = (long)(arg1);                   \
+       register long _arg2 __asm__ ("r25") = (long)(arg2);                   \
+       register long _arg3 __asm__ ("r24") = (long)(arg3);                   \
+       register long _arg4 __asm__ ("r23") = (long)(arg4);                   \
+                                                                             \
+       __asm__ volatile (                                                    \
+               "ble 0x100(%%sr2, %%r0)\n\t"                                  \
+               "copy %5, %%r20\n\t"                                          \
+               : "=r"(_ret),                                                 \
+                 "+r"(_arg1), "+r"(_arg2), "+r"(_arg3), "+r"(_arg4)          \
+               : "r"(num)                                                    \
+               : _NOLIBC_SYSCALL_CLOBBERLIST,                                \
+                 "%r21", "%r22"                                              \
+       );                                                                    \
+       _ret;                                                                 \
+})
+
+#define __nolibc_syscall5(num, arg1, arg2, arg3, arg4, arg5)                  \
+({                                                                            \
+       register long _ret __asm__ ("r28");                                   \
+       register long _arg1 __asm__ ("r26") = (long)(arg1);                   \
+       register long _arg2 __asm__ ("r25") = (long)(arg2);                   \
+       register long _arg3 __asm__ ("r24") = (long)(arg3);                   \
+       register long _arg4 __asm__ ("r23") = (long)(arg4);                   \
+       register long _arg5 __asm__ ("r22") = (long)(arg5);                   \
+                                                                             \
+       __asm__ volatile (                                                    \
+               "ble 0x100(%%sr2, %%r0)\n\t"                                  \
+               "copy %6, %%r20\n\t"                                          \
+               : "=r"(_ret),                                                 \
+                 "+r"(_arg1), "+r"(_arg2), "+r"(_arg3), "+r"(_arg4),         \
+                 "+r"(_arg5)                                                 \
+               : "r"(num)                                                    \
+               : _NOLIBC_SYSCALL_CLOBBERLIST,                                \
+                 "%r21"                                                      \
+       );                                                                    \
+       _ret;                                                                 \
+})
+
+#define __nolibc_syscall6(num, arg1, arg2, arg3, arg4, arg5, arg6)            \
+({                                                                            \
+       register long _ret __asm__ ("r28");                                   \
+       register long _arg1 __asm__ ("r26") = (long)(arg1);                   \
+       register long _arg2 __asm__ ("r25") = (long)(arg2);                   \
+       register long _arg3 __asm__ ("r24") = (long)(arg3);                   \
+       register long _arg4 __asm__ ("r23") = (long)(arg4);                   \
+       register long _arg5 __asm__ ("r22") = (long)(arg5);                   \
+       register long _arg6 __asm__ ("r21") = (long)(arg6);                   \
+                                                                             \
+       __asm__ volatile (                                                    \
+               "ble 0x100(%%sr2, %%r0)\n\t"                                  \
+               "copy %7, %%r20\n\t"                                          \
+               : "=r"(_ret),                                                 \
+                 "+r"(_arg1), "+r"(_arg2), "+r"(_arg3), "+r"(_arg4),         \
+                 "+r"(_arg5), "+r"(_arg6)                                    \
+               : "r"(num)                                                    \
+               : _NOLIBC_SYSCALL_CLOBBERLIST                                 \
+       );                                                                    \
+       _ret;                                                                 \
+})
+
+#ifndef NOLIBC_NO_RUNTIME
+/* startup code */
+void __attribute__((weak, noreturn)) __nolibc_entrypoint __nolibc_no_stack_protector _start(void)
+{
+       __asm__ volatile (
+               ".import $global$\n"           /* Set up the dp register */
+               "ldil L%$global$, %dp\n"
+               "ldo R%$global$(%r27), %dp\n"
+
+               "b _start_c\n"                 /* Call _start_c, the load below is executed first */
+
+               "ldo -4(%r24), %r26\n"         /* The sp register is special on parisc.
+                                               * r24 points to argv. Subtract 4 to get &argc.
+                                               * Pass that as first argument to _start_c.
+                                               */
+       );
+       __nolibc_entrypoint_epilogue();
+}
+#endif /* NOLIBC_NO_RUNTIME */
+
+#endif /* _NOLIBC_ARCH_PARISC_H */
index 55009dcafe9e16e9c6c9ef89cc563ef43acb466b..b69d9c5ec5c6a7421c69bd7798b21cc8f57ad78c 100644 (file)
@@ -30,6 +30,8 @@
 #include "arch-sh.h"
 #elif defined(__or1k__)
 #include "arch-openrisc.h"
+#elif defined(__hppa__)
+#include "arch-parisc.h"
 #else
 #error Unsupported Architecture
 #endif
index 26cc976536257a5e190feaa99acee20c92432181..c6dcd54078f2a9f8610368b04a12700dfce62bb4 100644 (file)
@@ -64,6 +64,7 @@ ARCH_s390x       = s390
 ARCH_sparc32     = sparc
 ARCH_sparc64     = sparc
 ARCH_sh4         = sh
+ARCH_parisc32    = parisc
 ARCH            := $(or $(ARCH_$(XARCH)),$(XARCH))
 
 # kernel image names by architecture
@@ -130,6 +131,7 @@ QEMU_ARCH_ppc64le    = ppc64
 QEMU_ARCH_loongarch  = loongarch64
 QEMU_ARCH_sparc32    = sparc
 QEMU_ARCH_openrisc   = or1k
+QEMU_ARCH_parisc32   = hppa
 QEMU_ARCH            = $(or $(QEMU_ARCH_$(XARCH)),$(XARCH))
 
 QEMU_ARCH_USER_ppc64le = ppc64le
@@ -170,6 +172,7 @@ QEMU_ARGS_sparc64    = -M sun4u -append "console=ttyS0,115200 panic=-1 $(TEST:%=
 QEMU_ARGS_m68k       = -M virt -append "console=ttyGF0,115200 panic=-1 $(TEST:%=NOLIBC_TEST=%)"
 QEMU_ARGS_sh4        = -M r2d -serial file:/dev/stdout -append "console=ttySC1,115200 panic=-1 $(TEST:%=NOLIBC_TEST=%)"
 QEMU_ARGS_openrisc   = -M virt -m 512M -append "console=ttyS0 panic=-1 $(TEST:%=NOLIBC_TEST=%)"
+QEMU_ARGS_parisc32   = -M B160L -append "console=ttyS0 panic=-1 $(TEST:%=NOLIBC_TEST=%)"
 QEMU_ARGS            = -m 1G $(QEMU_ARGS_$(XARCH)) $(QEMU_ARGS_BIOS) $(QEMU_ARGS_EXTRA)
 
 # OUTPUT is only set when run from the main makefile, otherwise
@@ -186,6 +189,7 @@ CFLAGS_i386 = $(call cc-option,-m32)
 CFLAGS_x32 = -mx32
 CFLAGS_arm = -marm
 CFLAGS_armthumb = -mthumb -march=armv6t2
+CFLAGS_parisc32 = -mfast-indirect-calls
 CFLAGS_ppc = -m32 -mbig-endian -mno-vsx $(call cc-option,-mmultiple)
 CFLAGS_ppc64 = -m64 -mbig-endian -mno-vsx $(call cc-option,-mmultiple)
 CFLAGS_ppc64le = -m64 -mlittle-endian -mno-vsx $(call cc-option,-mabi=elfv2)
index c25ee4be2df43282d4d266e801e5ee9228d70bea..6460e25001de834246a33e61430660af5b40241b 100755 (executable)
@@ -29,6 +29,7 @@ all_archs=(
        sparc32 sparc64
        m68k
        sh4
+       parisc32
 )
 archs="${all_archs[@]}"
 
@@ -118,6 +119,7 @@ crosstool_arch() {
        s390*) echo s390;;
        sparc*) echo sparc64;;
        x32*) echo x86_64;;
+       parisc32) echo hppa;;
        *) echo "$1";;
        esac
 }
@@ -175,6 +177,10 @@ test_arch() {
        fi
        MAKE=(make -f Makefile.nolibc -j"${nproc}" XARCH="${arch}" CROSS_COMPILE="${cross_compile}" LLVM="${llvm}" O="${build_dir}")
 
+       if [ "$arch" = "parisc32" ]; then
+               MAKE+=("CROSS32CC=${cross_compile}gcc")
+       fi
+
        case "$test_mode" in
                'system')
                        test_target=run
@@ -187,7 +193,7 @@ test_arch() {
                        exit 1
        esac
        printf '%-15s' "$arch:"
-       if [ "$arch" = "m68k" -o "$arch" = "sh4" -o "$arch" = "openrisc" ] && [ "$llvm" = "1" ]; then
+       if [ "$arch" = "m68k" -o "$arch" = "sh4" -o "$arch" = "openrisc" -o "$arch" = "parisc32" ] && [ "$llvm" = "1" ]; then
                echo "Unsupported configuration"
                return
        fi