There is no libc for the R5900, so its tests can not run in user mode [*]. Provide what is needed to run them bare-metal on the 'none' machine: start-up code, linker script, minimal assert.h, inttypes.h, stdio.h and string.h, and a gdb script.
A first generic loader loads the ELF at its physical addresses, a second one sets the PC to the KSEG0 entry point, sign-extended as the CPU is 64-bit. Tests end in test_done(), where the gdb script checks the status and kills QEMU. Tests use 32-bit pointers, so gdb must send 64-bit addresses to the remote stub. No test is registered yet. [*] https://lore.kernel.org/qemu-devel/[email protected]/ Inspired-by: Maciej W. Rozycki <[email protected]> Signed-off-by: Philippe Mathieu-Daudé <[email protected]> --- tests/tcg/mips64el/system/include/assert.h | 15 +++++ tests/tcg/mips64el/system/include/inttypes.h | 7 +++ tests/tcg/mips64el/system/include/stdio.h | 17 +++++ tests/tcg/mips64el/system/include/string.h | 16 +++++ tests/tcg/mips64el/system/harness.c | 66 ++++++++++++++++++++ tests/tcg/mips64el/meson.build | 2 + tests/tcg/mips64el/system/boot.S | 23 +++++++ tests/tcg/mips64el/system/kernel.ld | 43 +++++++++++++ tests/tcg/mips64el/system/meson.build | 33 ++++++++++ tests/tcg/mips64el/system/test-r5900.py | 30 +++++++++ 10 files changed, 252 insertions(+) create mode 100644 tests/tcg/mips64el/system/include/assert.h create mode 100644 tests/tcg/mips64el/system/include/inttypes.h create mode 100644 tests/tcg/mips64el/system/include/stdio.h create mode 100644 tests/tcg/mips64el/system/include/string.h create mode 100644 tests/tcg/mips64el/system/harness.c create mode 100644 tests/tcg/mips64el/system/boot.S create mode 100644 tests/tcg/mips64el/system/kernel.ld create mode 100644 tests/tcg/mips64el/system/meson.build create mode 100644 tests/tcg/mips64el/system/test-r5900.py diff --git a/tests/tcg/mips64el/system/include/assert.h b/tests/tcg/mips64el/system/include/assert.h new file mode 100644 index 000000000000..7b466a3b8ab5 --- /dev/null +++ b/tests/tcg/mips64el/system/include/assert.h @@ -0,0 +1,15 @@ +/* + * Freestanding assert() for bare-metal R5900 tests + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef R5900_SYSTEM_ASSERT_H +#define R5900_SYSTEM_ASSERT_H + +void test_assert_fail(int line) __attribute__((noreturn)); +void test_done(int status) __attribute__((noreturn)); + +#define assert(cond) ((cond) ? (void)0 : test_assert_fail(__LINE__)) + +#endif diff --git a/tests/tcg/mips64el/system/include/inttypes.h b/tests/tcg/mips64el/system/include/inttypes.h new file mode 100644 index 000000000000..3bfbd2ce65ea --- /dev/null +++ b/tests/tcg/mips64el/system/include/inttypes.h @@ -0,0 +1,7 @@ +/* + * Freestanding <inttypes.h> for bare-metal R5900 tests + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include <stdint.h> diff --git a/tests/tcg/mips64el/system/include/stdio.h b/tests/tcg/mips64el/system/include/stdio.h new file mode 100644 index 000000000000..6130f77b77d1 --- /dev/null +++ b/tests/tcg/mips64el/system/include/stdio.h @@ -0,0 +1,17 @@ +/* + * Freestanding <stdio.h> for bare-metal R5900 tests + * + * There is no console: fprintf() only records its format string, + * which the gdb script reports when the test fails. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef R5900_SYSTEM_STDIO_H +#define R5900_SYSTEM_STDIO_H + +void test_message(const char *msg); + +#define fprintf(stream, fmt, ...) test_message(fmt) + +#endif diff --git a/tests/tcg/mips64el/system/include/string.h b/tests/tcg/mips64el/system/include/string.h new file mode 100644 index 000000000000..819da7cb1ada --- /dev/null +++ b/tests/tcg/mips64el/system/include/string.h @@ -0,0 +1,16 @@ +/* + * Freestanding <string.h> for bare-metal R5900 tests + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef R5900_SYSTEM_STRING_H +#define R5900_SYSTEM_STRING_H + +#include <stddef.h> + +void *memset(void *s, int c, size_t n); +void *memcpy(void *dest, const void *src, size_t n); +int memcmp(const void *s1, const void *s2, size_t n); + +#endif diff --git a/tests/tcg/mips64el/system/harness.c b/tests/tcg/mips64el/system/harness.c new file mode 100644 index 000000000000..aa6c75579e59 --- /dev/null +++ b/tests/tcg/mips64el/system/harness.c @@ -0,0 +1,66 @@ +/* + * Bare-metal harness for R5900 tests + * + * There is no libc nor any way to exit QEMU on the 'none' machine. + * Tests end in test_done(), where the gdb script reads the status. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include <assert.h> +#include <stdio.h> +#include <string.h> + +volatile int test_failed_line; +const char *volatile test_failed_message; + +void __attribute__((noinline, noreturn)) test_done(int status) +{ + for (;;) { + /* The gdb script stops here */ + } +} + +void test_assert_fail(int line) +{ + test_failed_line = line; + test_done(1); +} + +void test_message(const char *msg) +{ + test_failed_message = msg; +} + +void *memset(void *s, int c, size_t n) +{ + unsigned char *p = s; + + while (n--) { + *p++ = c; + } + return s; +} + +void *memcpy(void *dest, const void *src, size_t n) +{ + unsigned char *d = dest; + const unsigned char *s = src; + + while (n--) { + *d++ = *s++; + } + return dest; +} + +int memcmp(const void *s1, const void *s2, size_t n) +{ + const unsigned char *a = s1, *b = s2; + + for (; n; n--, a++, b++) { + if (*a != *b) { + return *a - *b; + } + } + return 0; +} diff --git a/tests/tcg/mips64el/meson.build b/tests/tcg/mips64el/meson.build index 606f300804a4..a68ceab3b4b9 100644 --- a/tests/tcg/mips64el/meson.build +++ b/tests/tcg/mips64el/meson.build @@ -1,3 +1,5 @@ +subdir('system') + if 'qemu-mips64el' in emulators tcg_tests += { 'mips64el-linux-user': { diff --git a/tests/tcg/mips64el/system/boot.S b/tests/tcg/mips64el/system/boot.S new file mode 100644 index 000000000000..0448f983bbeb --- /dev/null +++ b/tests/tcg/mips64el/system/boot.S @@ -0,0 +1,23 @@ +/* + * Minimal start-up code for bare-metal R5900 tests + * + * The CPU starts in kernel mode at _start (PC set by the generic + * loader device). Set up a stack, run main() and report its return + * value to test_done(), where the gdb script is waiting. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + + .set noreorder + .section .text.boot, "ax" + .globl _start + .ent _start +_start: + la $sp, _stack_top + jal main + nop + jal test_done + move $a0, $v0 +1: b 1b + nop + .end _start diff --git a/tests/tcg/mips64el/system/kernel.ld b/tests/tcg/mips64el/system/kernel.ld new file mode 100644 index 000000000000..1a0aae7df4da --- /dev/null +++ b/tests/tcg/mips64el/system/kernel.ld @@ -0,0 +1,43 @@ +/* + * Linker script for bare-metal R5900 tests + * + * Code runs from KSEG0 (unmapped, cached) and is loaded at the + * corresponding physical address, in RAM mapped at zero. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +ENTRY(_start) + +KSEG0 = 0x80000000; + +SECTIONS +{ + . = KSEG0 + 0x100000; + .text : AT(ADDR(.text) - KSEG0) { + *(.text.boot) + *(.text*) + } + .rodata : AT(ADDR(.rodata) - KSEG0) { + *(.rodata*) + } + .data : AT(ADDR(.data) - KSEG0) { + *(.data*) + *(.sdata*) + } + .bss : AT(ADDR(.bss) - KSEG0) { + *(.sbss*) + *(.bss*) + *(COMMON) + } + . = ALIGN(16) + 0x10000; + _stack_top = .; + + /DISCARD/ : { + *(.MIPS.abiflags) + *(.reginfo) + *(.pdr) + *(.gnu.attributes) + *(.comment) + } +} diff --git a/tests/tcg/mips64el/system/meson.build b/tests/tcg/mips64el/system/meson.build new file mode 100644 index 000000000000..265a2eca4ff1 --- /dev/null +++ b/tests/tcg/mips64el/system/meson.build @@ -0,0 +1,33 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +tests = [] + +cflags = ['-nostdlib', '-ffreestanding', + '-march=r5900', '-msoft-float', + '-mno-abicalls', '-fno-pic', '-G0', + '-I', meson.current_source_dir() / 'include', + files('boot.S')[0], files('harness.c')[0], + '-Wl,-T', files('kernel.ld')[0], + '-Wl,--no-warn-rwx-segments', '-Wl,--build-id=none'] + +qemu_args = ['-M', 'none', '-cpu', 'R5900', '-m', '16M', + '-display', 'none', + '-device', 'loader,file=@BINARY@', + '-device', 'loader,addr=0xffffffff80100000,cpu-num=0'] + +# Pointers are 32-bit (o32, n32), sign-extend them for the 64-bit CPU +gdb_test = ['--gdb-args=-ex "set remoteaddresssize 64"', + '--test', files('test-r5900.py')] + +if 'qemu-system-mips64el' in emulators + tcg_tests += { + 'mips64el-softmmu': { + 'cc': 'mips64el-linux-gnuabi64-gcc', + 'cc_dockerfile': 'debian-all-test-cross', + 'folder': 'mips64el/system', + 'gdb_arch': 'mips64el', + 'qemu': emulators['qemu-system-mips64el'], + 'tests': tests, + } + } +endif diff --git a/tests/tcg/mips64el/system/test-r5900.py b/tests/tcg/mips64el/system/test-r5900.py new file mode 100644 index 000000000000..2e7ed191b3b6 --- /dev/null +++ b/tests/tcg/mips64el/system/test-r5900.py @@ -0,0 +1,30 @@ +"""Run a bare-metal R5900 test and check its result + +The guest runs until test_done(); a non-zero status means the test +failed: test_failed_line holds the line of a failed assert(), and +test_failed_message the last message the test tried to print. + +SPDX-License-Identifier: GPL-2.0-or-later +""" + +import gdb +from test_gdbstub import main, report + + +def run_test(): + gdb.execute("break test_done") + gdb.execute("continue") + + status = int(gdb.parse_and_eval("status")) + details = "" + if status: + line = int(gdb.parse_and_eval("test_failed_line")) + message = gdb.parse_and_eval("test_failed_message") + if line: + details = f" (assert failed at line {line})" + elif int(message): + details = f" ({message.string().strip()})" + report(status == 0, "guest test" + details) + + +main(run_test) -- 2.55.0
