diff --git a/tools/include/nolibc/arch-mips.h b/tools/include/nolibc/arch-mips.h index 4ab6fa54beee..62cc50ef3288 100644 --- a/tools/include/nolibc/arch-mips.h +++ b/tools/include/nolibc/arch-mips.h @@ -10,6 +10,10 @@ #include "compiler.h" #include "crt.h" +#if !defined(_ABIO32) +#error Unsupported MIPS ABI +#endif + /* Syscalls for MIPS ABI O32 : * - WARNING! there's always a delayed slot! * - WARNING again, the syntax is different, registers take a '$' and numbers @@ -180,8 +184,13 @@ void __attribute__((weak, noreturn, optimize("Os", "omit-frame-pointer"))) __no_ __asm__ volatile ( ".set push\n" ".set noreorder\n" - ".option pic0\n" + "bal 1f\n" /* prime $ra for .cpload */ + "nop\n" + "1:\n" + ".cpload $ra\n" "move $a0, $sp\n" /* save stack pointer to $a0, as arg1 of _start_c */ + "addiu $sp, $sp, -4\n" /* space for .cprestore to store $gp */ + ".cprestore 0\n" "li $t0, -8\n" "and $sp, $sp, $t0\n" /* $sp must be 8-byte aligned */ "addiu $sp, $sp, -16\n" /* the callee expects to save a0..a3 there */ diff --git a/tools/include/nolibc/arch.h b/tools/include/nolibc/arch.h index e276fb0680af..c8f4e5d3add9 100644 --- a/tools/include/nolibc/arch.h +++ b/tools/include/nolibc/arch.h @@ -23,7 +23,7 @@ #include "arch-arm.h" #elif defined(__aarch64__) #include "arch-aarch64.h" -#elif defined(__mips__) && defined(_ABIO32) +#elif defined(__mips__) #include "arch-mips.h" #elif defined(__powerpc__) #include "arch-powerpc.h" @@ -33,6 +33,8 @@ #include "arch-s390.h" #elif defined(__loongarch__) #include "arch-loongarch.h" +#else +#error Unsupported Architecture #endif #endif /* _NOLIBC_ARCH_H */ diff --git a/tools/include/nolibc/stdio.h b/tools/include/nolibc/stdio.h index d7ef43973916..16cd4d807251 100644 --- a/tools/include/nolibc/stdio.h +++ b/tools/include/nolibc/stdio.h @@ -212,7 +212,7 @@ char *fgets(char *s, int size, FILE *stream) * - %s * - unknown modifiers are ignored. */ -static __attribute__((unused)) +static __attribute__((unused, format(printf, 2, 0))) int vfprintf(FILE *stream, const char *fmt, va_list args) { char escape, lpref, c; @@ -318,7 +318,7 @@ int vfprintf(FILE *stream, const char *fmt, va_list args) return written; } -static __attribute__((unused)) +static __attribute__((unused, format(printf, 1, 0))) int vprintf(const char *fmt, va_list args) { return vfprintf(stdout, fmt, args); diff --git a/tools/include/nolibc/sys.h b/tools/include/nolibc/sys.h index 2f359cb03d10..dda9dffd1d74 100644 --- a/tools/include/nolibc/sys.h +++ b/tools/include/nolibc/sys.h @@ -21,6 +21,7 @@ #include /* for O_* and AT_* */ #include /* for statx() */ #include +#include #include "arch.h" #include "errno.h" @@ -898,6 +899,43 @@ int reboot(int cmd) } +/* + * int getrlimit(int resource, struct rlimit *rlim); + * int setrlimit(int resource, const struct rlimit *rlim); + */ + +static __attribute__((unused)) +int sys_prlimit64(pid_t pid, int resource, + const struct rlimit64 *new_limit, struct rlimit64 *old_limit) +{ + return my_syscall4(__NR_prlimit64, pid, resource, new_limit, old_limit); +} + +static __attribute__((unused)) +int getrlimit(int resource, struct rlimit *rlim) +{ + struct rlimit64 rlim64; + int ret; + + ret = __sysret(sys_prlimit64(0, resource, NULL, &rlim64)); + rlim->rlim_cur = rlim64.rlim_cur; + rlim->rlim_max = rlim64.rlim_max; + + return ret; +} + +static __attribute__((unused)) +int setrlimit(int resource, const struct rlimit *rlim) +{ + struct rlimit64 rlim64 = { + .rlim_cur = rlim->rlim_cur, + .rlim_max = rlim->rlim_max, + }; + + return __sysret(sys_prlimit64(0, resource, &rlim64, NULL)); +} + + /* * int sched_yield(void); */ diff --git a/tools/include/nolibc/types.h b/tools/include/nolibc/types.h index 8cfc4c860fa4..b26a5d0c417c 100644 --- a/tools/include/nolibc/types.h +++ b/tools/include/nolibc/types.h @@ -12,6 +12,8 @@ #include /* for LINUX_REBOOT_* */ #include #include +#include +#include /* Only the generic macros and types may be defined here. The arch-specific @@ -108,9 +110,6 @@ #define WTERMSIG(status) ((status) & 0x7f) #define WIFSIGNALED(status) ((status) - 1 < 0xff) -/* waitpid() flags */ -#define WNOHANG 1 - /* standard exit() codes */ #define EXIT_SUCCESS 0 #define EXIT_FAILURE 1 @@ -180,26 +179,6 @@ struct linux_dirent64 { char d_name[]; }; -/* needed by wait4() */ -struct rusage { - struct timeval ru_utime; - struct timeval ru_stime; - long ru_maxrss; - long ru_ixrss; - long ru_idrss; - long ru_isrss; - long ru_minflt; - long ru_majflt; - long ru_nswap; - long ru_inblock; - long ru_oublock; - long ru_msgsnd; - long ru_msgrcv; - long ru_nsignals; - long ru_nvcsw; - long ru_nivcsw; -}; - /* The format of the struct as returned by the libc to the application, which * significantly differs from the format returned by the stat() syscall flavours. */ diff --git a/tools/testing/selftests/nolibc/.gitignore b/tools/testing/selftests/nolibc/.gitignore index 5119f9f7afd2..35d247a0d5bd 100644 --- a/tools/testing/selftests/nolibc/.gitignore +++ b/tools/testing/selftests/nolibc/.gitignore @@ -3,4 +3,5 @@ /libc-test /nolibc-test /run.out +/run.out.* /sysroot/ diff --git a/tools/testing/selftests/nolibc/Makefile b/tools/testing/selftests/nolibc/Makefile index a0fc07253baf..40dd95228051 100644 --- a/tools/testing/selftests/nolibc/Makefile +++ b/tools/testing/selftests/nolibc/Makefile @@ -1,9 +1,16 @@ # SPDX-License-Identifier: GPL-2.0 # Makefile for nolibc tests include ../../../scripts/Makefile.include +include ../../../scripts/utilities.mak # We need this for the "cc-option" macro. include ../../../build/Build.include +ifneq ($(O),) +ifneq ($(call is-absolute,$(O)),y) +$(error Only absolute O= parameters are supported) +endif +endif + # we're in ".../tools/testing/selftests/nolibc" ifeq ($(srctree),) srctree := $(patsubst %/tools/testing/selftests/,%,$(dir $(CURDIR))) @@ -14,6 +21,8 @@ include $(srctree)/scripts/subarch.include ARCH = $(SUBARCH) endif +objtree ?= $(srctree) + # XARCH extends the kernel's ARCH with a few variants of the same # architecture that only differ by the configuration, the toolchain # and the Qemu program used. It is copied as-is into ARCH except for @@ -31,12 +40,15 @@ endif # configure default variants for target kernel supported architectures XARCH_powerpc = ppc +XARCH_mips = mips32le XARCH = $(or $(XARCH_$(ARCH)),$(ARCH)) # map from user input variants to their kernel supported architectures ARCH_ppc = powerpc ARCH_ppc64 = powerpc ARCH_ppc64le = powerpc +ARCH_mips32le = mips +ARCH_mips32be = mips ARCH := $(or $(ARCH_$(XARCH)),$(XARCH)) # kernel image names by architecture @@ -45,14 +57,15 @@ IMAGE_x86_64 = arch/x86/boot/bzImage IMAGE_x86 = arch/x86/boot/bzImage IMAGE_arm64 = arch/arm64/boot/Image IMAGE_arm = arch/arm/boot/zImage -IMAGE_mips = vmlinuz +IMAGE_mips32le = vmlinuz +IMAGE_mips32be = vmlinuz IMAGE_ppc = vmlinux IMAGE_ppc64 = vmlinux IMAGE_ppc64le = arch/powerpc/boot/zImage IMAGE_riscv = arch/riscv/boot/Image IMAGE_s390 = arch/s390/boot/bzImage IMAGE_loongarch = arch/loongarch/boot/vmlinuz.efi -IMAGE = $(IMAGE_$(XARCH)) +IMAGE = $(objtree)/$(IMAGE_$(XARCH)) IMAGE_NAME = $(notdir $(IMAGE)) # default kernel configurations that appear to be usable @@ -61,7 +74,8 @@ DEFCONFIG_x86_64 = defconfig DEFCONFIG_x86 = defconfig DEFCONFIG_arm64 = defconfig DEFCONFIG_arm = multi_v7_defconfig -DEFCONFIG_mips = malta_defconfig +DEFCONFIG_mips32le = malta_defconfig +DEFCONFIG_mips32be = malta_defconfig DEFCONFIG_ppc = pmac32_defconfig DEFCONFIG_ppc64 = powernv_be_defconfig DEFCONFIG_ppc64le = powernv_defconfig @@ -70,6 +84,9 @@ DEFCONFIG_s390 = defconfig DEFCONFIG_loongarch = defconfig DEFCONFIG = $(DEFCONFIG_$(XARCH)) +EXTRACONFIG_mips32be = -d CONFIG_CPU_LITTLE_ENDIAN -e CONFIG_CPU_BIG_ENDIAN +EXTRACONFIG = $(EXTRACONFIG_$(XARCH)) + # optional tests to run (default = all) TEST = @@ -79,7 +96,8 @@ QEMU_ARCH_x86_64 = x86_64 QEMU_ARCH_x86 = x86_64 QEMU_ARCH_arm64 = aarch64 QEMU_ARCH_arm = arm -QEMU_ARCH_mips = mipsel # works with malta_defconfig +QEMU_ARCH_mips32le = mipsel # works with malta_defconfig +QEMU_ARCH_mips32be = mips QEMU_ARCH_ppc = ppc QEMU_ARCH_ppc64 = ppc64 QEMU_ARCH_ppc64le = ppc64 @@ -88,20 +106,31 @@ QEMU_ARCH_s390 = s390x QEMU_ARCH_loongarch = loongarch64 QEMU_ARCH = $(QEMU_ARCH_$(XARCH)) +QEMU_ARCH_USER_ppc64le = ppc64le +QEMU_ARCH_USER = $(or $(QEMU_ARCH_USER_$(XARCH)),$(QEMU_ARCH_$(XARCH))) + +QEMU_BIOS_DIR = /usr/share/edk2/ +QEMU_BIOS_loongarch = $(QEMU_BIOS_DIR)/loongarch64/OVMF_CODE.fd + +ifneq ($(QEMU_BIOS_$(XARCH)),) +QEMU_ARGS_BIOS = -bios $(QEMU_BIOS_$(XARCH)) +endif + # QEMU_ARGS : some arch-specific args to pass to qemu QEMU_ARGS_i386 = -M pc -append "console=ttyS0,9600 i8042.noaux panic=-1 $(TEST:%=NOLIBC_TEST=%)" QEMU_ARGS_x86_64 = -M pc -append "console=ttyS0,9600 i8042.noaux panic=-1 $(TEST:%=NOLIBC_TEST=%)" QEMU_ARGS_x86 = -M pc -append "console=ttyS0,9600 i8042.noaux panic=-1 $(TEST:%=NOLIBC_TEST=%)" QEMU_ARGS_arm64 = -M virt -cpu cortex-a53 -append "panic=-1 $(TEST:%=NOLIBC_TEST=%)" QEMU_ARGS_arm = -M virt -append "panic=-1 $(TEST:%=NOLIBC_TEST=%)" -QEMU_ARGS_mips = -M malta -append "panic=-1 $(TEST:%=NOLIBC_TEST=%)" +QEMU_ARGS_mips32le = -M malta -append "panic=-1 $(TEST:%=NOLIBC_TEST=%)" +QEMU_ARGS_mips32be = -M malta -append "panic=-1 $(TEST:%=NOLIBC_TEST=%)" QEMU_ARGS_ppc = -M g3beige -append "console=ttyS0 panic=-1 $(TEST:%=NOLIBC_TEST=%)" QEMU_ARGS_ppc64 = -M powernv -append "console=hvc0 panic=-1 $(TEST:%=NOLIBC_TEST=%)" QEMU_ARGS_ppc64le = -M powernv -append "console=hvc0 panic=-1 $(TEST:%=NOLIBC_TEST=%)" QEMU_ARGS_riscv = -M virt -append "console=ttyS0 panic=-1 $(TEST:%=NOLIBC_TEST=%)" QEMU_ARGS_s390 = -M s390-ccw-virtio -m 1G -append "console=ttyS0 panic=-1 $(TEST:%=NOLIBC_TEST=%)" QEMU_ARGS_loongarch = -M virt -append "console=ttyS0,115200 panic=-1 $(TEST:%=NOLIBC_TEST=%)" -QEMU_ARGS = $(QEMU_ARGS_$(XARCH)) $(QEMU_ARGS_EXTRA) +QEMU_ARGS = $(QEMU_ARGS_$(XARCH)) $(QEMU_ARGS_BIOS) $(QEMU_ARGS_EXTRA) # OUTPUT is only set when run from the main makefile, otherwise # it defaults to this nolibc directory. @@ -118,7 +147,8 @@ 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) CFLAGS_s390 = -m64 -CFLAGS_mips = -EL +CFLAGS_mips32le = -EL -mabi=32 -fPIC +CFLAGS_mips32be = -EB -mabi=32 CFLAGS_STACKPROTECTOR ?= $(call cc-option,-mstack-protector-guard=global $(call cc-option,-fstack-protector-all)) CFLAGS ?= -Os -fno-ident -fno-asynchronous-unwind-tables -std=c89 -W -Wall -Wextra \ $(call cc-option,-fno-stack-protector) \ @@ -167,7 +197,8 @@ sysroot: sysroot/$(ARCH)/include sysroot/$(ARCH)/include: $(Q)rm -rf sysroot/$(ARCH) sysroot/sysroot $(QUIET_MKDIR)mkdir -p sysroot - $(Q)$(MAKE) -C ../../../include/nolibc ARCH=$(ARCH) OUTPUT=$(CURDIR)/sysroot/ headers_standalone + $(Q)$(MAKE) -C $(srctree) outputmakefile + $(Q)$(MAKE) -C $(srctree)/tools/include/nolibc ARCH=$(ARCH) OUTPUT=$(CURDIR)/sysroot/ headers_standalone $(Q)mv sysroot/sysroot sysroot/$(ARCH) ifneq ($(NOLIBC_SYSROOT),0) @@ -177,7 +208,7 @@ nolibc-test: nolibc-test.c nolibc-test-linkage.c sysroot/$(ARCH)/include else nolibc-test: nolibc-test.c nolibc-test-linkage.c $(QUIET_CC)$(CC) $(CFLAGS) $(LDFLAGS) -o $@ \ - -nostdlib -static -include ../../../include/nolibc/nolibc.h nolibc-test.c nolibc-test-linkage.c -lgcc + -nostdlib -static -include $(srctree)/tools/include/nolibc/nolibc.h nolibc-test.c nolibc-test-linkage.c -lgcc endif libc-test: nolibc-test.c nolibc-test-linkage.c @@ -195,11 +226,11 @@ run-nolibc-test: nolibc-test # qemu user-land test run-user: nolibc-test - $(Q)qemu-$(QEMU_ARCH) ./nolibc-test > "$(CURDIR)/run.out" || : + $(Q)qemu-$(QEMU_ARCH_USER) ./nolibc-test > "$(CURDIR)/run.out" || : $(Q)$(REPORT) $(CURDIR)/run.out initramfs.cpio: kernel nolibc-test - $(QUIET_GEN)echo 'file /init nolibc-test 755 0 0' | $(srctree)/usr/gen_init_cpio - > initramfs.cpio + $(QUIET_GEN)echo 'file /init nolibc-test 755 0 0' | $(objtree)/usr/gen_init_cpio - > initramfs.cpio initramfs: nolibc-test $(QUIET_MKDIR)mkdir -p initramfs @@ -208,21 +239,25 @@ initramfs: nolibc-test defconfig: $(Q)$(MAKE) -C $(srctree) ARCH=$(ARCH) CC=$(CC) CROSS_COMPILE=$(CROSS_COMPILE) mrproper $(DEFCONFIG) prepare + $(Q)if [ -n "$(EXTRACONFIG)" ]; then \ + $(srctree)/scripts/config --file $(objtree)/.config $(EXTRACONFIG); \ + $(MAKE) -C $(srctree) ARCH=$(ARCH) CC=$(CC) CROSS_COMPILE=$(CROSS_COMPILE) olddefconfig < /dev/null; \ + fi kernel: - $(Q)$(MAKE) -C $(srctree) ARCH=$(ARCH) CC=$(CC) CROSS_COMPILE=$(CROSS_COMPILE) $(IMAGE_NAME) + $(Q)$(MAKE) -C $(srctree) ARCH=$(ARCH) CC=$(CC) CROSS_COMPILE=$(CROSS_COMPILE) $(IMAGE_NAME) < /dev/null kernel-standalone: initramfs - $(Q)$(MAKE) -C $(srctree) ARCH=$(ARCH) CC=$(CC) CROSS_COMPILE=$(CROSS_COMPILE) $(IMAGE_NAME) CONFIG_INITRAMFS_SOURCE=$(CURDIR)/initramfs + $(Q)$(MAKE) -C $(srctree) ARCH=$(ARCH) CC=$(CC) CROSS_COMPILE=$(CROSS_COMPILE) $(IMAGE_NAME) CONFIG_INITRAMFS_SOURCE=$(CURDIR)/initramfs < /dev/null # run the tests after building the kernel run: kernel initramfs.cpio - $(Q)qemu-system-$(QEMU_ARCH) -display none -no-reboot -kernel "$(srctree)/$(IMAGE)" -initrd initramfs.cpio -serial stdio $(QEMU_ARGS) > "$(CURDIR)/run.out" + $(Q)qemu-system-$(QEMU_ARCH) -display none -no-reboot -kernel "$(IMAGE)" -initrd initramfs.cpio -serial stdio $(QEMU_ARGS) > "$(CURDIR)/run.out" $(Q)$(REPORT) $(CURDIR)/run.out # re-run the tests from an existing kernel rerun: - $(Q)qemu-system-$(QEMU_ARCH) -display none -no-reboot -kernel "$(srctree)/$(IMAGE)" -initrd initramfs.cpio -serial stdio $(QEMU_ARGS) > "$(CURDIR)/run.out" + $(Q)qemu-system-$(QEMU_ARCH) -display none -no-reboot -kernel "$(IMAGE)" -initrd initramfs.cpio -serial stdio $(QEMU_ARGS) > "$(CURDIR)/run.out" $(Q)$(REPORT) $(CURDIR)/run.out # report with existing test log diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c index 2f10541e6f38..6ba4f8275ac4 100644 --- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -130,11 +131,17 @@ static const char *errorname(int err) } } -static void putcharn(char c, size_t n) +static void align_result(size_t llen) { - char buf[64]; + const size_t align = 64; + char buf[align]; + size_t n; - memset(buf, c, n); + if (llen >= align) + return; + + n = align - llen; + memset(buf, ' ', n); buf[n] = '\0'; fputs(buf, stdout); } @@ -150,14 +157,13 @@ static void result(int llen, enum RESULT r) const char *msg; if (r == OK) - msg = " [OK]"; + msg = " [OK]"; else if (r == SKIPPED) msg = "[SKIPPED]"; else - msg = "[FAIL]"; + msg = " [FAIL]"; - if (llen < 64) - putcharn(' ', 64 - llen); + align_result(llen); puts(msg); } @@ -834,6 +840,33 @@ int test_pipe(void) return !!memcmp(buf, msg, len); } +int test_rlimit(void) +{ + struct rlimit rlim = { + .rlim_cur = 1 << 20, + .rlim_max = 1 << 21, + }; + int ret; + + ret = setrlimit(RLIMIT_CORE, &rlim); + if (ret) + return -1; + + rlim.rlim_cur = 0; + rlim.rlim_max = 0; + + ret = getrlimit(RLIMIT_CORE, &rlim); + if (ret) + return -1; + + if (rlim.rlim_cur != 1 << 20) + return -1; + if (rlim.rlim_max != 1 << 21) + return -1; + + return 0; +} + /* Run syscall tests between IDs and . * Return 0 on success, non-zero on failure. @@ -905,7 +938,6 @@ int run_syscall(int min, int max) CASE_TEST(gettimeofday_tv_tz);EXPECT_SYSZR(1, gettimeofday(&tv, &tz)); break; CASE_TEST(getpagesize); EXPECT_SYSZR(1, test_getpagesize()); break; CASE_TEST(ioctl_tiocinq); EXPECT_SYSZR(1, ioctl(0, TIOCINQ, &tmp)); break; - CASE_TEST(ioctl_tiocinq); EXPECT_SYSZR(1, ioctl(0, TIOCINQ, &tmp)); break; CASE_TEST(link_root1); EXPECT_SYSER(1, link("/", "/"), -1, EEXIST); break; CASE_TEST(link_blah); EXPECT_SYSER(1, link("/proc/self/blah", "/blah"), -1, ENOENT); break; CASE_TEST(link_dir); EXPECT_SYSER(euid0, link("/", "/blah"), -1, EPERM); break; @@ -924,6 +956,7 @@ int run_syscall(int min, int max) CASE_TEST(poll_fault); EXPECT_SYSER(1, poll(NULL, 1, 0), -1, EFAULT); break; CASE_TEST(prctl); EXPECT_SYSER(1, prctl(PR_SET_NAME, (unsigned long)NULL, 0, 0, 0), -1, EFAULT); break; CASE_TEST(read_badf); EXPECT_SYSER(1, read(-1, &tmp, 1), -1, EBADF); break; + CASE_TEST(rlimit); EXPECT_SYSZR(1, test_rlimit()); break; CASE_TEST(rmdir_blah); EXPECT_SYSER(1, rmdir("/blah"), -1, ENOENT); break; CASE_TEST(sched_yield); EXPECT_SYSZR(1, sched_yield()); break; CASE_TEST(select_null); EXPECT_SYSZR(1, ({ struct timeval tv = { 0 }; select(0, NULL, NULL, NULL, &tv); })); break; @@ -1133,6 +1166,7 @@ static int run_protection(int min __attribute__((unused)), { pid_t pid; int llen = 0, status; + struct rlimit rlimit = { 0, 0 }; llen += printf("0 -fstackprotector "); @@ -1164,6 +1198,7 @@ static int run_protection(int min __attribute__((unused)), close(STDERR_FILENO); prctl(PR_SET_DUMPABLE, 0, 0, 0, 0); + setrlimit(RLIMIT_CORE, &rlimit); smash_stack(); return 1; diff --git a/tools/testing/selftests/nolibc/run-tests.sh b/tools/testing/selftests/nolibc/run-tests.sh new file mode 100755 index 000000000000..c0a5a7cea9fa --- /dev/null +++ b/tools/testing/selftests/nolibc/run-tests.sh @@ -0,0 +1,169 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 + +# Test runner for nolibc tests + +set -e + +trap 'echo Aborting...' 'ERR' + +crosstool_version=13.2.0 +hostarch=x86_64 +nproc=$(( $(nproc) + 2)) +cache_dir="${XDG_CACHE_HOME:-"$HOME"/.cache}" +download_location="${cache_dir}/crosstools/" +build_location="$(realpath "${cache_dir}"/nolibc-tests/)" +perform_download=0 +test_mode=system +archs="i386 x86_64 arm64 arm mips32le mips32be ppc ppc64 ppc64le riscv s390 loongarch" + +TEMP=$(getopt -o 'j:d:c:b:a:m:ph' -n "$0" -- "$@") + +eval set -- "$TEMP" +unset TEMP + +print_usage() { + cat < + +Known architectures: + ${archs} + +Options: + -j [N] Allow N jobs at once (default: ${nproc}) + -p Allow download of toolchains + -d [DIR] Download location for toolchains (default: ${download_location}) + -c [VERSION] Version of toolchains to use (default: ${crosstool_version}) + -a [ARCH] Host architecture of toolchains to use (default: ${hostarch}) + -b [DIR] Build location (default: ${build_location}) + -m [MODE] Test mode user/system (default: ${test_mode}) +EOF +} + +while true; do + case "$1" in + '-j') + nproc="$2" + shift 2; continue ;; + '-p') + perform_download=1 + shift; continue ;; + '-d') + download_location="$2" + shift 2; continue ;; + '-c') + crosstool_version="$2" + shift 2; continue ;; + '-a') + hostarch="$2" + shift 2; continue ;; + '-b') + build_location="$(realpath "$2")" + shift 2; continue ;; + '-m') + test_mode="$2" + shift 2; continue ;; + '-h') + print_usage + exit 0 + ;; + '--') + shift; break ;; + *) + echo 'Internal error!' >&2; exit 1 ;; + esac +done + +if [[ -n "$*" ]]; then + archs="$*" +fi + +crosstool_arch() { + case "$1" in + arm64) echo aarch64;; + ppc) echo powerpc;; + ppc64) echo powerpc64;; + ppc64le) echo powerpc64;; + riscv) echo riscv64;; + loongarch) echo loongarch64;; + mips*) echo mips;; + *) echo "$1";; + esac +} + +crosstool_abi() { + case "$1" in + arm) echo linux-gnueabi;; + *) echo linux;; + esac +} + +download_crosstool() { + arch="$(crosstool_arch "$1")" + abi="$(crosstool_abi "$1")" + + archive_name="${hostarch}-gcc-${crosstool_version}-nolibc-${arch}-${abi}.tar.gz" + url="https://mirrors.edge.kernel.org/pub/tools/crosstool/files/bin/${hostarch}/${crosstool_version}/${archive_name}" + archive="${download_location}${archive_name}" + stamp="${archive}.stamp" + + [ -f "${stamp}" ] && return + + echo "Downloading crosstools ${arch} ${crosstool_version}" + mkdir -p "${download_location}" + curl -o "${archive}" --fail --continue-at - "${url}" + tar -C "${download_location}" -xf "${archive}" + touch "${stamp}" +} + +# capture command output, print it on failure +# mimics chronic(1) from moreutils +function swallow_output() { + if ! OUTPUT="$("$@" 2>&1)"; then + echo "$OUTPUT" + return 1 + fi + return 0 +} + +test_arch() { + arch=$1 + ct_arch=$(crosstool_arch "$arch") + ct_abi=$(crosstool_abi "$1") + cross_compile=$(realpath "${download_location}gcc-${crosstool_version}-nolibc/${ct_arch}-${ct_abi}/bin/${ct_arch}-${ct_abi}-") + build_dir="${build_location}/${arch}" + MAKE=(make -j"${nproc}" XARCH="${arch}" CROSS_COMPILE="${cross_compile}" O="${build_dir}") + + mkdir -p "$build_dir" + if [ "$test_mode" = "system" ] && [ ! -f "${build_dir}/.config" ]; then + swallow_output "${MAKE[@]}" defconfig + fi + case "$test_mode" in + 'system') + test_target=run + ;; + 'user') + test_target=run-user + ;; + *) + echo "Unknown mode $test_mode" + exit 1 + esac + printf '%-15s' "$arch:" + swallow_output "${MAKE[@]}" "$test_target" V=1 + cp run.out run.out."${arch}" + "${MAKE[@]}" report | grep passed +} + +if [ "$perform_download" -ne 0 ]; then + for arch in $archs; do + download_crosstool "$arch" + done +fi + +for arch in $archs; do + test_arch "$arch" +done