]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
selftests/arm64: Add a testcase for handling of ZA on clone()
authorMark Brown <broonie@kernel.org>
Tue, 19 Apr 2022 11:22:47 +0000 (12:22 +0100)
committerCatalin Marinas <catalin.marinas@arm.com>
Thu, 28 Apr 2022 16:57:12 +0000 (17:57 +0100)
Add a small testcase that attempts to do a clone() with ZA enabled and
verifies that it remains enabled with the same contents. We only check
one word in one horizontal vector of ZA since there's already other tests
that check for data corruption more broadly, we're just looking to make
sure that ZA is still enabled and it looks like the data got copied.

Signed-off-by: Mark Brown <broonie@kernel.org>
Link: https://lore.kernel.org/r/20220419112247.711548-40-broonie@kernel.org
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
tools/testing/selftests/arm64/fp/.gitignore
tools/testing/selftests/arm64/fp/Makefile
tools/testing/selftests/arm64/fp/za-fork-asm.S [new file with mode: 0644]
tools/testing/selftests/arm64/fp/za-fork.c [new file with mode: 0644]

index d98d3d48b504cfe435a012e57a08ab87882f3159..ea947af6388243c31504eb5f2468580fd1573bf8 100644 (file)
@@ -8,5 +8,6 @@ sve-test
 ssve-test
 vec-syscfg
 vlset
+za-fork
 za-ptrace
 za-test
index 807a8faf8d5777a1f1d34259200578f1e639a219..95e707e32247b6397b8d161752edfdf3faf7997e 100644 (file)
@@ -1,7 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0
 
 CFLAGS += -I../../../../../usr/include/
-TEST_GEN_PROGS := sve-ptrace sve-probe-vls vec-syscfg za-ptrace
+TEST_GEN_PROGS := sve-ptrace sve-probe-vls vec-syscfg za-fork za-ptrace
 TEST_PROGS_EXTENDED := fp-pidbench fpsimd-test fpsimd-stress \
        rdvl-sme rdvl-sve \
        sve-test sve-stress \
@@ -11,6 +11,7 @@ TEST_PROGS_EXTENDED := fp-pidbench fpsimd-test fpsimd-stress \
 
 all: $(TEST_GEN_PROGS) $(TEST_PROGS_EXTENDED)
 
+# Build with nolibc to avoid effects due to libc's clone() support
 fp-pidbench: fp-pidbench.S asm-utils.o
        $(CC) -nostdlib $^ -o $@
 fpsimd-test: fpsimd-test.o asm-utils.o
@@ -25,6 +26,12 @@ ssve-test: sve-test.S asm-utils.o
        $(CC) -DSSVE -nostdlib $^ -o $@
 vec-syscfg: vec-syscfg.o rdvl.o
 vlset: vlset.o
+za-fork: za-fork.o za-fork-asm.o
+       $(CC) -nostdlib -static $^ -o $@ -lgcc
+za-fork.o: za-fork.c
+       $(CC) -c -fno-asynchronous-unwind-tables -fno-ident -s -Os -nostdlib \
+               -include ../../../../include/nolibc/nolibc.h \
+               -ffreestanding -Wall $^ -o $@
 za-test: za-test.o asm-utils.o
        $(CC) -nostdlib $^ -o $@
 za-ptrace: za-ptrace.o
diff --git a/tools/testing/selftests/arm64/fp/za-fork-asm.S b/tools/testing/selftests/arm64/fp/za-fork-asm.S
new file mode 100644 (file)
index 0000000..2fafadd
--- /dev/null
@@ -0,0 +1,61 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (C) 2021 ARM Limited.
+
+#include "sme-inst.h"
+
+.arch_extension sve
+
+#define MAGIC     42
+
+#define MAXVL     2048
+#define MAXVL_B   (MAXVL / 8)
+
+.pushsection .text
+.data
+.align 4
+scratch:
+       .space  MAXVL_B
+.popsection
+
+.globl fork_test
+fork_test:
+       smstart_za
+
+       // For simplicity just set one word in one vector, other tests
+       // cover general data corruption issues.
+       ldr     x0, =scratch
+       mov     x1, #MAGIC
+       str     x1, [x0]
+       mov     w12, wzr
+       _ldr_za 12, 0                   // ZA.H[W12] loaded from [X0]
+
+       // Tail call into the C portion that does the fork & verify
+       b       fork_test_c
+
+.globl verify_fork
+verify_fork:
+       // SVCR should have ZA=1, SM=0
+       mrs     x0, S3_3_C4_C2_2
+       and     x1, x0, #3
+       cmp     x1, #2
+       beq     1f
+       mov     x0, xzr
+       b       100f
+1:
+
+       // ZA should still have the value we loaded
+       ldr     x0, =scratch
+       mov     w12, wzr
+       _str_za 12, 0                   // ZA.H[W12] stored to [X0]
+       ldr     x1, [x0]
+       cmp     x1, #MAGIC
+       beq     2f
+       mov     x0, xzr
+       b       100f
+
+2:
+       // All tests passed
+       mov     x0, #1
+100:
+       ret
+
diff --git a/tools/testing/selftests/arm64/fp/za-fork.c b/tools/testing/selftests/arm64/fp/za-fork.c
new file mode 100644 (file)
index 0000000..ff475c6
--- /dev/null
@@ -0,0 +1,156 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2022 ARM Limited.
+ * Original author: Mark Brown <broonie@kernel.org>
+ */
+
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/sched.h>
+#include <linux/wait.h>
+
+#define EXPECTED_TESTS 1
+
+static void putstr(const char *str)
+{
+       write(1, str, strlen(str));
+}
+
+static void putnum(unsigned int num)
+{
+       char c;
+
+       if (num / 10)
+               putnum(num / 10);
+
+       c = '0' + (num % 10);
+       write(1, &c, 1);
+}
+
+static int tests_run;
+static int tests_passed;
+static int tests_failed;
+static int tests_skipped;
+
+static void print_summary(void)
+{
+       if (tests_passed + tests_failed + tests_skipped != EXPECTED_TESTS)
+               putstr("# UNEXPECTED TEST COUNT: ");
+
+       putstr("# Totals: pass:");
+       putnum(tests_passed);
+       putstr(" fail:");
+       putnum(tests_failed);
+       putstr(" xfail:0 xpass:0 skip:");
+       putnum(tests_skipped);
+       putstr(" error:0\n");
+}
+
+int fork_test(void);
+int verify_fork(void);
+
+/*
+ * If we fork the value in the parent should be unchanged and the
+ * child should start with the same value.  This is called from the
+ * fork_test() asm function.
+ */
+int fork_test_c(void)
+{
+       pid_t newpid, waiting;
+       int child_status, parent_result;
+
+       newpid = fork();
+       if (newpid == 0) {
+               /* In child */
+               if (!verify_fork()) {
+                       putstr("# ZA state invalid in child\n");
+                       exit(0);
+               } else {
+                       exit(1);
+               }
+       }
+       if (newpid < 0) {
+               putstr("# fork() failed: -");
+               putnum(-newpid);
+               putstr("\n");
+               return 0;
+       }
+
+       parent_result = verify_fork();
+       if (!parent_result)
+               putstr("# ZA state invalid in parent\n");
+
+       for (;;) {
+               waiting = waitpid(newpid, &child_status, 0);
+
+               if (waiting < 0) {
+                       if (errno == EINTR)
+                               continue;
+                       putstr("# waitpid() failed: ");
+                       putnum(errno);
+                       putstr("\n");
+                       return 0;
+               }
+               if (waiting != newpid) {
+                       putstr("# waitpid() returned wrong PID\n");
+                       return 0;
+               }
+
+               if (!WIFEXITED(child_status)) {
+                       putstr("# child did not exit\n");
+                       return 0;
+               }
+
+               return WEXITSTATUS(child_status) && parent_result;
+       }
+}
+
+#define run_test(name)                      \
+       if (name()) {                        \
+               tests_passed++;              \
+       } else {                             \
+               tests_failed++;              \
+               putstr("not ");              \
+       }                                    \
+       putstr("ok ");                       \
+       putnum(++tests_run);                 \
+       putstr(" " #name "\n");
+
+int main(int argc, char **argv)
+{
+       int ret, i;
+
+       putstr("TAP version 13\n");
+       putstr("1..");
+       putnum(EXPECTED_TESTS);
+       putstr("\n");
+
+       putstr("# PID: ");
+       putnum(getpid());
+       putstr("\n");
+
+       /*
+        * This test is run with nolibc which doesn't support hwcap and
+        * it's probably disproportionate to implement so instead check
+        * for the default vector length configuration in /proc.
+        */
+       ret = open("/proc/sys/abi/sme_default_vector_length", O_RDONLY, 0);
+       if (ret >= 0) {
+               run_test(fork_test);
+
+       } else {
+               putstr("# SME support not present\n");
+
+               for (i = 0; i < EXPECTED_TESTS; i++) {
+                       putstr("ok ");
+                       putnum(i);
+                       putstr(" skipped\n");
+               }
+
+               tests_skipped += EXPECTED_TESTS;
+       }
+
+       print_summary();
+
+       return 0;
+}