A straightforward new architecture.
Signed-off-by: Thomas Weißschuh linux@weissschuh.net --- Only tested on QEMU so far. Testing on real hardware would be very welcome.
Test instructions: $ cd tools/testings/selftests/nolibc/ $ make -f Makefile.nolibc ARCH=alpha CROSS_COMPILE=alpha-linux- nolibc-test $ file nolibc-test nolibc-test: ELF 64-bit LSB executable, Alpha (unofficial), version 1 (SYSV), statically linked, not stripped $ ./nolibc-test Running test 'startup' 0 argc = 1 [OK] ... Total number of errors: 0 Exiting with status 0 --- tools/include/nolibc/arch-alpha.h | 164 +++++++++++++++++++++++++ tools/include/nolibc/arch.h | 2 + tools/testing/selftests/nolibc/Makefile.nolibc | 5 + tools/testing/selftests/nolibc/nolibc-test.c | 4 + tools/testing/selftests/nolibc/run-tests.sh | 3 +- 5 files changed, 177 insertions(+), 1 deletion(-)
diff --git a/tools/include/nolibc/arch-alpha.h b/tools/include/nolibc/arch-alpha.h new file mode 100644 index 0000000000000000000000000000000000000000..6b9bb6c749b931f30ce7bd6cd125622828405604 --- /dev/null +++ b/tools/include/nolibc/arch-alpha.h @@ -0,0 +1,164 @@ +/* SPDX-License-Identifier: LGPL-2.1 OR MIT */ +/* + * Alpha specific definitions for NOLIBC + * Copyright (C) 2025 Thomas Weißschuh linux@weissschuh.net + */ + +#ifndef _NOLIBC_ARCH_ALPHA_H +#define _NOLIBC_ARCH_ALPHA_H + +#include "compiler.h" +#include "crt.h" + +/* + * Syscalls for Alpha: + * - registers are 64-bit + * - syscall number is passed in $0/v0 + * - the system call is performed by calling callsys + * - syscall return comes in $0/v0, error flag in $19/a4 + * - arguments are passed in $16/a0 to $21/a5 + * - GCC does not support symbol register names + */ + +#define my_syscall0(num) \ +({ \ + register long _num __asm__ ("$0") = (num); \ + register long _ret __asm__ ("$0"); \ + register long _err __asm__ ("$19"); \ + \ + __asm__ volatile ( \ + "callsys" \ + : "=r"(_ret), "=r"(_err) \ + : "r"(_num) \ + : "memory", "cc" \ + ); \ + _err ? -_ret : _ret; \ +}) + +#define my_syscall1(num, arg1) \ +({ \ + register long _num __asm__ ("$0") = (num); \ + register long _ret __asm__ ("$0"); \ + register long _err __asm__ ("$19"); \ + register long _arg1 __asm__ ("$16") = (long)(arg1); \ + \ + __asm__ volatile ( \ + "callsys" \ + : "=r"(_ret), "=r"(_err) \ + : "r"(_num), "r"(_arg1) \ + : "memory", "cc" \ + ); \ + _err ? -_ret : _ret; \ +}) + +#define my_syscall2(num, arg1, arg2) \ +({ \ + register long _num __asm__ ("$0") = (num); \ + register long _ret __asm__ ("$0"); \ + register long _err __asm__ ("$19"); \ + register long _arg1 __asm__ ("$16") = (long)(arg1); \ + register long _arg2 __asm__ ("$17") = (long)(arg2); \ + \ + __asm__ volatile ( \ + "callsys" \ + : "=r"(_ret), "=r"(_err) \ + : "r"(_num), "r"(_arg1), "r"(_arg2) \ + : "memory", "cc" \ + ); \ + _err ? -_ret : _ret; \ +}) + +#define my_syscall3(num, arg1, arg2, arg3) \ +({ \ + register long _num __asm__ ("$0") = (num); \ + register long _ret __asm__ ("$0"); \ + register long _err __asm__ ("$19"); \ + register long _arg1 __asm__ ("$16") = (long)(arg1); \ + register long _arg2 __asm__ ("$17") = (long)(arg2); \ + register long _arg3 __asm__ ("$18") = (long)(arg3); \ + \ + __asm__ volatile ( \ + "callsys" \ + : "=r"(_ret), "=r"(_err) \ + : "r"(_num), "r"(_arg1), "r"(_arg2), "r"(_arg3) \ + : "memory", "cc" \ + ); \ + _err ? -_ret : _ret; \ +}) + +#define my_syscall4(num, arg1, arg2, arg3, arg4) \ +({ \ + register long _num __asm__ ("$0") = (num); \ + register long _ret __asm__ ("$0"); \ + register long _err __asm__ ("$19"); \ + register long _arg1 __asm__ ("$16") = (long)(arg1); \ + register long _arg2 __asm__ ("$17") = (long)(arg2); \ + register long _arg3 __asm__ ("$18") = (long)(arg3); \ + register long _arg4 __asm__ ("$19") = (long)(arg4); \ + \ + __asm__ volatile ( \ + "callsys" \ + : "=r"(_ret), "=r"(_err) \ + : "r"(_num), "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4) \ + : "memory", "cc" \ + ); \ + _err ? -_ret : _ret; \ +}) + +#define my_syscall5(num, arg1, arg2, arg3, arg4, arg5) \ +({ \ + register long _num __asm__ ("$0") = (num); \ + register long _ret __asm__ ("$0"); \ + register long _err __asm__ ("$19"); \ + register long _arg1 __asm__ ("$16") = (long)(arg1); \ + register long _arg2 __asm__ ("$17") = (long)(arg2); \ + register long _arg3 __asm__ ("$18") = (long)(arg3); \ + register long _arg4 __asm__ ("$19") = (long)(arg4); \ + register long _arg5 __asm__ ("$20") = (long)(arg5); \ + \ + __asm__ volatile ( \ + "callsys" \ + : "=r"(_ret), "=r"(_err) \ + : "r"(_num), "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), \ + "r"(_arg5) \ + : "memory", "cc" \ + ); \ + _err ? -_ret : _ret; \ +}) + +#define my_syscall6(num, arg1, arg2, arg3, arg4, arg5, arg6) \ +({ \ + register long _num __asm__ ("$0") = (num); \ + register long _ret __asm__ ("$0"); \ + register long _err __asm__ ("$19"); \ + register long _arg1 __asm__ ("$16") = (long)(arg1); \ + register long _arg2 __asm__ ("$17") = (long)(arg2); \ + register long _arg3 __asm__ ("$18") = (long)(arg3); \ + register long _arg4 __asm__ ("$19") = (long)(arg4); \ + register long _arg5 __asm__ ("$20") = (long)(arg5); \ + register long _arg6 __asm__ ("$21") = (long)(arg6); \ + \ + __asm__ volatile ( \ + "callsys" \ + : "=r"(_ret), "=r"(_err) \ + : "r"(_num), "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), \ + "r"(_arg5), "r"(_arg6) \ + : "memory", "cc" \ + ); \ + _err ? -_ret : _ret; \ +}) + +/* startup code */ +void __attribute__((weak, noreturn)) __nolibc_entrypoint __no_stack_protector _start(void) +{ + __asm__ volatile ( + "br $gp, 0f\n" /* setup $gp, so that 'lda' works */ + "0: ldgp $gp, 0($gp)\n" + "lda $27, _start_c\n" /* setup current function address for _start_c */ + "mov $sp, $16\n" /* save argc pointer to $16, as arg1 of _start_c */ + "br _start_c\n" /* transfer to c runtime */ + ); + __nolibc_entrypoint_epilogue(); +} + +#endif /* _NOLIBC_ARCH_ALPHA_H */ diff --git a/tools/include/nolibc/arch.h b/tools/include/nolibc/arch.h index 426c89198135564acca44c485e5c2d8ba36a6fe9..72585d4c04e2896a275faadf881e98286f914fb3 100644 --- a/tools/include/nolibc/arch.h +++ b/tools/include/nolibc/arch.h @@ -37,6 +37,8 @@ #include "arch-m68k.h" #elif defined(__sh__) #include "arch-sh.h" +#elif defined(__alpha__) +#include "arch-alpha.h" #else #error Unsupported Architecture #endif diff --git a/tools/testing/selftests/nolibc/Makefile.nolibc b/tools/testing/selftests/nolibc/Makefile.nolibc index 0fb759ba992ee6b1693b88f1b2e77463afa9f38b..0da33fe99bd630ec5100b5beed939d524af2b3d4 100644 --- a/tools/testing/selftests/nolibc/Makefile.nolibc +++ b/tools/testing/selftests/nolibc/Makefile.nolibc @@ -93,6 +93,7 @@ IMAGE_sparc32 = arch/sparc/boot/image IMAGE_sparc64 = arch/sparc/boot/image IMAGE_m68k = vmlinux IMAGE_sh4 = arch/sh/boot/zImage +IMAGE_alpha = vmlinux IMAGE = $(objtree)/$(IMAGE_$(XARCH)) IMAGE_NAME = $(notdir $(IMAGE))
@@ -123,6 +124,7 @@ DEFCONFIG_sparc32 = sparc32_defconfig DEFCONFIG_sparc64 = sparc64_defconfig DEFCONFIG_m68k = virt_defconfig DEFCONFIG_sh4 = rts7751r2dplus_defconfig +DEFCONFIG_alpha = defconfig DEFCONFIG = $(DEFCONFIG_$(XARCH))
EXTRACONFIG_x32 = -e CONFIG_X86_X32_ABI @@ -130,6 +132,7 @@ EXTRACONFIG_arm = -e CONFIG_NAMESPACES EXTRACONFIG_armthumb = -e CONFIG_NAMESPACES EXTRACONFIG_m68k = -e CONFIG_BLK_DEV_INITRD EXTRACONFIG_sh4 = -e CONFIG_BLK_DEV_INITRD -e CONFIG_CMDLINE_FROM_BOOTLOADER +EXTRACONFIG_alpha = -e CONFIG_BLK_DEV_INITRD EXTRACONFIG = $(EXTRACONFIG_$(XARCH))
# optional tests to run (default = all) @@ -162,6 +165,7 @@ QEMU_ARCH_sparc32 = sparc QEMU_ARCH_sparc64 = sparc64 QEMU_ARCH_m68k = m68k QEMU_ARCH_sh4 = sh4 +QEMU_ARCH_alpha = alpha QEMU_ARCH = $(QEMU_ARCH_$(XARCH))
QEMU_ARCH_USER_ppc64le = ppc64le @@ -203,6 +207,7 @@ QEMU_ARGS_sparc32 = -M SS-5 -m 256M -append "console=ttyS0,115200 panic=-1 $( QEMU_ARGS_sparc64 = -M sun4u -append "console=ttyS0,115200 panic=-1 $(TEST:%=NOLIBC_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_alpha = -M clipper -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 diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c index a297ee0d6d0754dfcd9f9e5609d42c7442dabc4e..bbbb2a485f220fed69556baaf2603d9cf24a1c36 100644 --- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -709,6 +709,10 @@ int run_startup(int min, int max) /* checking NULL for argv/argv0, environ and _auxv is not enough, let's compare with sbrk(0) or &end */ extern char end; char *brk = sbrk(0) != (void *)-1 ? sbrk(0) : &end; +#if defined(__alpha__) + /* the ordering above does not work on an alpha kernel */ + brk = NULL; +#endif /* differ from nolibc, both glibc and musl have no global _auxv */ const unsigned long *test_auxv = (void *)-1; #ifdef NOLIBC diff --git a/tools/testing/selftests/nolibc/run-tests.sh b/tools/testing/selftests/nolibc/run-tests.sh index e8af1fb505cf3573b4a6b37228dee764fe2e5277..8ce57d7006594c531f471d777d579c4f08d87efe 100755 --- a/tools/testing/selftests/nolibc/run-tests.sh +++ b/tools/testing/selftests/nolibc/run-tests.sh @@ -28,6 +28,7 @@ all_archs=( sparc32 sparc64 m68k sh4 + alpha ) archs="${all_archs[@]}"
@@ -189,7 +190,7 @@ test_arch() { echo "Unsupported configuration" return fi - if [ "$arch" = "m68k" -o "$arch" = "sh4" ] && [ "$llvm" = "1" ]; then + if [ "$arch" = "m68k" -o "$arch" = "sh4" -o "$arch" = "alpha" ] && [ "$llvm" = "1" ]; then echo "Unsupported configuration" return fi
--- base-commit: b9e50363178a40c76bebaf2f00faa2b0b6baf8d1 change-id: 20250609-nolibc-alpha-33e79644544c
Best regards,
On Sun, 2025-07-13 at 22:08 +0200, Thomas Weißschuh wrote:
A straightforward new architecture.
A straight-forward what? ;-) I assume you mean port.
Signed-off-by: Thomas Weißschuh linux@weissschuh.net
Only tested on QEMU so far. Testing on real hardware would be very welcome.
I'll test on real hardware next week and report back.
Adrian
On 7/13/25 14:08, Thomas Weißschuh wrote:
+++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -709,6 +709,10 @@ int run_startup(int min, int max) /* checking NULL for argv/argv0, environ and _auxv is not enough, let's compare with sbrk(0) or &end */ extern char end; char *brk = sbrk(0) != (void *)-1 ? sbrk(0) : &end; +#if defined(__alpha__)
- /* the ordering above does not work on an alpha kernel */
- brk = NULL;
+#endif
The syscall api is different for brk on alpha. A change to sys_brk or brk in include/nolibc/sys.h is required.
r~
Hi Richard,
On 2025-07-13 16:21:58-0600, Richard Henderson wrote:
On 7/13/25 14:08, Thomas Weißschuh wrote:
+++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -709,6 +709,10 @@ int run_startup(int min, int max) /* checking NULL for argv/argv0, environ and _auxv is not enough, let's compare with sbrk(0) or &end */ extern char end; char *brk = sbrk(0) != (void *)-1 ? sbrk(0) : &end; +#if defined(__alpha__)
- /* the ordering above does not work on an alpha kernel */
- brk = NULL;
+#endif
The syscall api is different for brk on alpha. A change to sys_brk or brk in include/nolibc/sys.h is required.
You are referring to osf_brk, right? I think that should work as-is with the current wrappers. On alpha, mm->brk and mm->arg_start are ordered differently from other architectures. Personally I think the nolibc tests are a bit bogus here.
Thomas
Hi,
On Mon, Jul 14, 2025 at 07:21:38AM +0200, Thomas Weißschuh wrote:
Hi Richard,
On 2025-07-13 16:21:58-0600, Richard Henderson wrote:
On 7/13/25 14:08, Thomas Weißschuh wrote:
+++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -709,6 +709,10 @@ int run_startup(int min, int max) /* checking NULL for argv/argv0, environ and _auxv is not enough, let's compare with sbrk(0) or &end */ extern char end; char *brk = sbrk(0) != (void *)-1 ? sbrk(0) : &end; +#if defined(__alpha__)
- /* the ordering above does not work on an alpha kernel */
- brk = NULL;
+#endif
The syscall api is different for brk on alpha. A change to sys_brk or brk in include/nolibc/sys.h is required.
You are referring to osf_brk, right? I think that should work as-is with the current wrappers.
I finally managed to reinstall my DS10 to build and test this and FWIW the test passes:
$ ./nolibc-test Running test 'startup' 0 argc = 1 [OK] 1 argv_addr = <0x11fc7b428> [OK] 2 argv_environ = <0x11fc7b428> [OK] 3 argv_total = 1 [OK] 4 argv0_addr = <0x11fc7b665> [OK] 5 argv0_str = <./nolibc-test> [OK] 6 argv0_len = 13 [OK] 7 environ_addr = <0x11fc7b438> [OK] 8 environ_envp = <0x11fc7b438> [OK] 9 environ_auxv = <0x11fc7b438> [OK] 10 environ_total = 175 [OK] 11 environ_HOME = <0x11fc7b6f4> [OK] 12 auxv_addr = <0x11fc7b4e8> [OK] 13 auxv_AT_UID = 509 [OK] 14 constructor = 3 [OK] 15 linkage_errno = <0x1200200f8> [OK] 16 linkage_constr = 3 [OK] Errors during this test: 0
Running test 'syscall' 0 access = 0 [OK] 1 access_bad = -1 EPERM [OK] 2 clock_getres = 0 [OK] 3 clock_gettime = 0 [OK] 4 clock_settime = -1 EINVAL [OK] 5 getpid = 9201 [OK] 6 getppid = 419 [OK] 7 gettid = 9201 [OK] 8 getpgid_self = 9201 [OK] 9 getpgid_bad = -1 ESRCH [OK] 10 kill_0 = 0 [OK] 11 kill_CONT = 0 [OK] 12 kill_BADPID = -1 ESRCH [OK] 13 sbrk_0 = <0x120024000> [OK] 14 sbrk = 0 [OK] 15 brk = 0 [OK] (...) Total number of errors: 0 Exiting with status 0
The result is exactly the same if I comment that line that resets brk, as brk was apparently already NULL:
13 sbrk_0 = <0x120024000> [OK] 14 sbrk = 0 [OK] 15 brk = 0 [OK]
On alpha, mm->brk and mm->arg_start are ordered differently from other architectures. Personally I think the nolibc tests are a bit bogus here.
I seem to remember that these are among the older minimal consistency tests and that it could be time to revisit this :-/
Willy
On 2025-07-15 08:28:09+0200, Willy Tarreau wrote:
Hi,
On Mon, Jul 14, 2025 at 07:21:38AM +0200, Thomas Weißschuh wrote:
Hi Richard,
On 2025-07-13 16:21:58-0600, Richard Henderson wrote:
On 7/13/25 14:08, Thomas Weißschuh wrote:
+++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -709,6 +709,10 @@ int run_startup(int min, int max) /* checking NULL for argv/argv0, environ and _auxv is not enough, let's compare with sbrk(0) or &end */ extern char end; char *brk = sbrk(0) != (void *)-1 ? sbrk(0) : &end; +#if defined(__alpha__)
- /* the ordering above does not work on an alpha kernel */
- brk = NULL;
+#endif
The syscall api is different for brk on alpha. A change to sys_brk or brk in include/nolibc/sys.h is required.
You are referring to osf_brk, right? I think that should work as-is with the current wrappers.
I finally managed to reinstall my DS10 to build and test this and FWIW the test passes:
Thanks for getting real hardware involved!
$ ./nolibc-test Running test 'startup' 0 argc = 1 [OK] 1 argv_addr = <0x11fc7b428> [OK] 2 argv_environ = <0x11fc7b428> [OK] 3 argv_total = 1 [OK] 4 argv0_addr = <0x11fc7b665> [OK] 5 argv0_str = <./nolibc-test> [OK] 6 argv0_len = 13 [OK] 7 environ_addr = <0x11fc7b438> [OK] 8 environ_envp = <0x11fc7b438> [OK] 9 environ_auxv = <0x11fc7b438> [OK] 10 environ_total = 175 [OK] 11 environ_HOME = <0x11fc7b6f4> [OK] 12 auxv_addr = <0x11fc7b4e8> [OK] 13 auxv_AT_UID = 509 [OK] 14 constructor = 3 [OK] 15 linkage_errno = <0x1200200f8> [OK] 16 linkage_constr = 3 [OK] Errors during this test: 0 Running test 'syscall' 0 access = 0 [OK] 1 access_bad = -1 EPERM [OK] 2 clock_getres = 0 [OK] 3 clock_gettime = 0 [OK] 4 clock_settime = -1 EINVAL [OK] 5 getpid = 9201 [OK] 6 getppid = 419 [OK] 7 gettid = 9201 [OK] 8 getpgid_self = 9201 [OK] 9 getpgid_bad = -1 ESRCH [OK] 10 kill_0 = 0 [OK] 11 kill_CONT = 0 [OK] 12 kill_BADPID = -1 ESRCH [OK] 13 sbrk_0 = <0x120024000> [OK] 14 sbrk = 0 [OK] 15 brk = 0 [OK] (...) Total number of errors: 0 Exiting with status 0
The result is exactly the same if I comment that line that resets brk, as brk was apparently already NULL:
13 sbrk_0 = <0x120024000> [OK] 14 sbrk = 0 [OK] 15 brk = 0 [OK]
brk shouldn't be NULL I think. It looks instead like it's 0x120024000. And it looks weird because the raw numbers look similar to my machine.
1 argv_addr = <0x11fc7b428> [OK]
13 sbrk_0 = <0x120024000> [OK]
argv is not greater than brk.
Could you double-check your test modification? How does it behave in QEMU for you? Also could you provide your kernel config?
On alpha, mm->brk and mm->arg_start are ordered differently from other architectures. Personally I think the nolibc tests are a bit bogus here.
I seem to remember that these are among the older minimal consistency tests and that it could be time to revisit this :-/
I do like them in general to be fair.
On Tue, Jul 15, 2025 at 04:42:10PM +0200, Thomas Weißschuh wrote:
I finally managed to reinstall my DS10 to build and test this and FWIW the test passes:
Thanks for getting real hardware involved!
You're welcome, I was happy to revive it after 15yr of downtime and upgrade it from 2.4.37 to 6.12 ;-)
The result is exactly the same if I comment that line that resets brk, as brk was apparently already NULL:
13 sbrk_0 = <0x120024000> [OK] 14 sbrk = 0 [OK] 15 brk = 0 [OK]
brk shouldn't be NULL I think. It looks instead like it's 0x120024000. And it looks weird because the raw numbers look similar to my machine.
1 argv_addr = <0x11fc7b428> [OK]
13 sbrk_0 = <0x120024000> [OK]
argv is not greater than brk.
Could you double-check your test modification?
I only commented out the "brk = NULL;" line enclosed in the "#if defined(__alpha__)" block. But I can recheck everything. I must say, the machine is old and super slow, I untarred a kernel and applied the for-next patches then your alpha series on top of it. Cloning via git would take a day or two based on my experience with haproxy which is hundreds of times smaller...
How does it behave in QEMU for you?
Not tested.
Also could you provide your kernel config?
I could but for this I'll need to find a way to access it again. For an unknown reason the console stopped responding yesterday, and now it only speaks but I cannot enter anything. I suspect a voltage issue (same with two adapters). Anyway it was the default config from the 6.12 debian 13 kernel apparently.
Will report back when I figure out this console issue (I'm still having machines with real serial ports anyway).
Willy
linux-kselftest-mirror@lists.linaro.org