selftests/arm64: Add a testcase for handling of ZA on clone()

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>
This commit is contained in:
Mark Brown 2022-04-19 12:22:47 +01:00 committed by Catalin Marinas
parent 43e3f85523
commit 212b0426bc
4 changed files with 226 additions and 1 deletions

View File

@ -8,5 +8,6 @@ sve-test
ssve-test
vec-syscfg
vlset
za-fork
za-ptrace
za-test

View 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

View File

@ -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

View File

@ -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;
}