From 78d2d816c45af0231d797e958250ad9ac590c627 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 13 Sep 2021 13:54:58 +0100 Subject: [PATCH 01/14] selftests: arm64: Use a define for the number of SVE ptrace tests to be run Partly in preparation for future refactoring move from hard coding the number of tests in main() to putting #define at the top of the source instead. Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20210913125505.52619-2-broonie@kernel.org Signed-off-by: Will Deacon --- tools/testing/selftests/arm64/fp/sve-ptrace.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/arm64/fp/sve-ptrace.c b/tools/testing/selftests/arm64/fp/sve-ptrace.c index 612d3899614a..7f7ed1c96867 100644 --- a/tools/testing/selftests/arm64/fp/sve-ptrace.c +++ b/tools/testing/selftests/arm64/fp/sve-ptrace.c @@ -19,6 +19,8 @@ #include "../../kselftest.h" +#define EXPECTED_TESTS 20 + /* and don't like each other, so: */ #ifndef NT_ARM_SVE #define NT_ARM_SVE 0x405 @@ -313,7 +315,7 @@ int main(void) pid_t child; ksft_print_header(); - ksft_set_plan(20); + ksft_set_plan(EXPECTED_TESTS); if (!(getauxval(AT_HWCAP) & HWCAP_SVE)) ksft_exit_skip("SVE not available\n"); From 09121ad7186ecd28eb7fca1b22c17f517aaea87f Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 13 Sep 2021 13:54:59 +0100 Subject: [PATCH 02/14] selftests: arm64: Don't log child creation as a test in SVE ptrace test Currently we log the creation of the child process as a test but it's not really relevant to what we're trying to test and can make the output a little confusing so don't do that. Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20210913125505.52619-3-broonie@kernel.org Signed-off-by: Will Deacon --- tools/testing/selftests/arm64/fp/sve-ptrace.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tools/testing/selftests/arm64/fp/sve-ptrace.c b/tools/testing/selftests/arm64/fp/sve-ptrace.c index 7f7ed1c96867..7035f01423b3 100644 --- a/tools/testing/selftests/arm64/fp/sve-ptrace.c +++ b/tools/testing/selftests/arm64/fp/sve-ptrace.c @@ -19,7 +19,7 @@ #include "../../kselftest.h" -#define EXPECTED_TESTS 20 +#define EXPECTED_TESTS 19 /* and don't like each other, so: */ #ifndef NT_ARM_SVE @@ -169,8 +169,6 @@ static int do_parent(pid_t child) if (WIFEXITED(status) || WIFSIGNALED(status)) ksft_exit_fail_msg("Child died unexpectedly\n"); - ksft_test_result(WIFSTOPPED(status), "WIFSTOPPED(%d)\n", - status); if (!WIFSTOPPED(status)) goto error; From eab281e3afa6a740df9b9c2e313eb4a9944deeea Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 13 Sep 2021 13:55:00 +0100 Subject: [PATCH 03/14] selftests: arm64: Remove extraneous register setting code For some reason the SVE ptrace test code starts off by setting values in some of the SVE vector registers in the parent process which it then never interacts with when verifying the ptrace interfaces. This is not especially relevant to what's being tested and somewhat confusing when reading the code so let's remove it. Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20210913125505.52619-4-broonie@kernel.org Signed-off-by: Will Deacon --- tools/testing/selftests/arm64/fp/Makefile | 2 +- .../selftests/arm64/fp/sve-ptrace-asm.S | 33 ------------------- tools/testing/selftests/arm64/fp/sve-ptrace.c | 28 ---------------- 3 files changed, 1 insertion(+), 62 deletions(-) delete mode 100644 tools/testing/selftests/arm64/fp/sve-ptrace-asm.S diff --git a/tools/testing/selftests/arm64/fp/Makefile b/tools/testing/selftests/arm64/fp/Makefile index f2abdd6ba12e..4367125b7c27 100644 --- a/tools/testing/selftests/arm64/fp/Makefile +++ b/tools/testing/selftests/arm64/fp/Makefile @@ -12,7 +12,7 @@ all: $(TEST_GEN_PROGS) $(TEST_PROGS_EXTENDED) fpsimd-test: fpsimd-test.o $(CC) -nostdlib $^ -o $@ rdvl-sve: rdvl-sve.o rdvl.o -sve-ptrace: sve-ptrace.o sve-ptrace-asm.o +sve-ptrace: sve-ptrace.o sve-probe-vls: sve-probe-vls.o rdvl.o sve-test: sve-test.o $(CC) -nostdlib $^ -o $@ diff --git a/tools/testing/selftests/arm64/fp/sve-ptrace-asm.S b/tools/testing/selftests/arm64/fp/sve-ptrace-asm.S deleted file mode 100644 index 3e81f9fab574..000000000000 --- a/tools/testing/selftests/arm64/fp/sve-ptrace-asm.S +++ /dev/null @@ -1,33 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -// Copyright (C) 2015-2019 ARM Limited. -// Original author: Dave Martin -#include - -.arch_extension sve - -.globl sve_store_patterns - -sve_store_patterns: - mov x1, x0 - - index z0.b, #0, #1 - str q0, [x1] - - mov w8, #__NR_getpid - svc #0 - str q0, [x1, #0x10] - - mov z1.d, z0.d - str q0, [x1, #0x20] - - mov w8, #__NR_getpid - svc #0 - str q0, [x1, #0x30] - - mov z1.d, z0.d - str q0, [x1, #0x40] - - ret - -.size sve_store_patterns, . - sve_store_patterns -.type sve_store_patterns, @function diff --git a/tools/testing/selftests/arm64/fp/sve-ptrace.c b/tools/testing/selftests/arm64/fp/sve-ptrace.c index 7035f01423b3..d2ec48f649f9 100644 --- a/tools/testing/selftests/arm64/fp/sve-ptrace.c +++ b/tools/testing/selftests/arm64/fp/sve-ptrace.c @@ -26,11 +26,6 @@ #define NT_ARM_SVE 0x405 #endif -/* Number of registers filled in by sve_store_patterns */ -#define NR_VREGS 5 - -void sve_store_patterns(__uint128_t v[NR_VREGS]); - static void dump(const void *buf, size_t size) { size_t i; @@ -40,23 +35,6 @@ static void dump(const void *buf, size_t size) printf(" %.2x", *p++); } -static int check_vregs(const __uint128_t vregs[NR_VREGS]) -{ - int i; - int ok = 1; - - for (i = 0; i < NR_VREGS; ++i) { - printf("# v[%d]:", i); - dump(&vregs[i], sizeof vregs[i]); - putchar('\n'); - - if (vregs[i] != vregs[0]) - ok = 0; - } - - return ok; -} - static int do_child(void) { if (ptrace(PTRACE_TRACEME, -1, NULL, NULL)) @@ -309,7 +287,6 @@ static int do_parent(pid_t child) int main(void) { int ret = EXIT_SUCCESS; - __uint128_t v[NR_VREGS]; pid_t child; ksft_print_header(); @@ -318,11 +295,6 @@ int main(void) if (!(getauxval(AT_HWCAP) & HWCAP_SVE)) ksft_exit_skip("SVE not available\n"); - sve_store_patterns(v); - - if (!check_vregs(v)) - ksft_exit_fail_msg("Initial check_vregs() failed\n"); - child = fork(); if (!child) return do_child(); From 736e6d5a54511e1438cb81d38e5d9f164eb2af1a Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 13 Sep 2021 13:55:01 +0100 Subject: [PATCH 04/14] selftests: arm64: Document what the SVE ptrace test is doing Before we go modifying it further let's add some comments and output clarifications explaining what this test is actually doing. Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20210913125505.52619-5-broonie@kernel.org Signed-off-by: Will Deacon --- tools/testing/selftests/arm64/fp/sve-ptrace.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/tools/testing/selftests/arm64/fp/sve-ptrace.c b/tools/testing/selftests/arm64/fp/sve-ptrace.c index d2ec48f649f9..fc4a672825eb 100644 --- a/tools/testing/selftests/arm64/fp/sve-ptrace.c +++ b/tools/testing/selftests/arm64/fp/sve-ptrace.c @@ -181,6 +181,7 @@ static int do_parent(pid_t child) } } + /* New process should start with FPSIMD registers only */ sve = get_sve(pid, &svebuf, &svebufsz); if (!sve) { int e = errno; @@ -191,14 +192,15 @@ static int do_parent(pid_t child) goto error; } else { - ksft_test_result_pass("get_sve\n"); + ksft_test_result_pass("get_sve(FPSIMD)\n"); } ksft_test_result((sve->flags & SVE_PT_REGS_MASK) == SVE_PT_REGS_FPSIMD, - "FPSIMD registers\n"); + "Set FPSIMD registers\n"); if ((sve->flags & SVE_PT_REGS_MASK) != SVE_PT_REGS_FPSIMD) goto error; + /* Try to set a known FPSIMD state via PT_REGS_SVE */ fpsimd = (struct user_fpsimd_state *)((char *)sve + SVE_PT_FPSIMD_OFFSET); for (i = 0; i < 32; ++i) { @@ -219,6 +221,7 @@ static int do_parent(pid_t child) goto error; } + /* Zero the first SVE Z register */ vq = sve_vq_from_vl(sve->vl); newsvebufsz = SVE_PT_SVE_ZREG_OFFSET(vq, 1); @@ -245,6 +248,7 @@ static int do_parent(pid_t child) goto error; } + /* Try to read back the value we just set */ new_sve = get_sve(pid, &newsvebuf, &newsvebufsz); if (!new_sve) { int e = errno; @@ -257,12 +261,13 @@ static int do_parent(pid_t child) } ksft_test_result((new_sve->flags & SVE_PT_REGS_MASK) == SVE_PT_REGS_SVE, - "SVE registers\n"); + "Get SVE registers\n"); if ((new_sve->flags & SVE_PT_REGS_MASK) != SVE_PT_REGS_SVE) goto error; dump_sve_regs(new_sve, 3, sizeof fpsimd->vregs[0]); + /* Verify that the register we set has the value we expected */ p = (unsigned char *)new_sve + SVE_PT_SVE_ZREG_OFFSET(vq, 1); for (i = 0; i < sizeof fpsimd->vregs[0]; ++i) { unsigned char expected = i; From 8c9eece0bfbf90064470e173c80fe3495f24b397 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 13 Sep 2021 13:55:02 +0100 Subject: [PATCH 05/14] selftests: arm64: Clarify output when verifying SVE register set When verifying setting a Z register via ptrace we check each byte by hand, iterating over the buffer using a pointer called p and treating each register value written as a test. This creates output referring to "p[X]" which is confusing since SVE also has predicate registers Pn. Tweak the output to avoid confusion here. Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20210913125505.52619-6-broonie@kernel.org Signed-off-by: Will Deacon --- tools/testing/selftests/arm64/fp/sve-ptrace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/arm64/fp/sve-ptrace.c b/tools/testing/selftests/arm64/fp/sve-ptrace.c index fc4a672825eb..2d130fedc019 100644 --- a/tools/testing/selftests/arm64/fp/sve-ptrace.c +++ b/tools/testing/selftests/arm64/fp/sve-ptrace.c @@ -275,7 +275,7 @@ static int do_parent(pid_t child) if (__BYTE_ORDER == __BIG_ENDIAN) expected = sizeof fpsimd->vregs[0] - 1 - expected; - ksft_test_result(p[i] == expected, "p[%d] == expected\n", i); + ksft_test_result(p[i] == expected, "buf[%d] == expected\n", i); if (p[i] != expected) goto error; } From 9f7d03a2c5a1e57bb4ec5fddecff4c000f34a786 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 13 Sep 2021 13:55:03 +0100 Subject: [PATCH 06/14] selftests: arm64: Verify interoperation of SVE and FPSIMD register sets After setting the FPSIMD registers via the SVE register set read them back via the FPSIMD register set, validating that the two register sets are interoperating and that the values we thought we set made it into the child process. Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20210913125505.52619-7-broonie@kernel.org Signed-off-by: Will Deacon --- tools/testing/selftests/arm64/fp/sve-ptrace.c | 28 +++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/arm64/fp/sve-ptrace.c b/tools/testing/selftests/arm64/fp/sve-ptrace.c index 2d130fedc019..31a2c2fc529d 100644 --- a/tools/testing/selftests/arm64/fp/sve-ptrace.c +++ b/tools/testing/selftests/arm64/fp/sve-ptrace.c @@ -46,6 +46,15 @@ static int do_child(void) return EXIT_SUCCESS; } +static int get_fpsimd(pid_t pid, struct user_fpsimd_state *fpsimd) +{ + struct iovec iov; + + iov.iov_base = fpsimd; + iov.iov_len = sizeof(*fpsimd); + return ptrace(PTRACE_GETREGSET, pid, NT_PRFPREG, &iov); +} + static struct user_sve_header *get_sve(pid_t pid, void **buf, size_t *size) { struct user_sve_header *sve; @@ -122,7 +131,7 @@ static int do_parent(pid_t child) void *svebuf = NULL, *newsvebuf; size_t svebufsz = 0, newsvebufsz; struct user_sve_header *sve, *new_sve; - struct user_fpsimd_state *fpsimd; + struct user_fpsimd_state *fpsimd, new_fpsimd; unsigned int i, j; unsigned char *p; unsigned int vq; @@ -221,7 +230,22 @@ static int do_parent(pid_t child) goto error; } - /* Zero the first SVE Z register */ + /* Verify via the FPSIMD regset */ + if (get_fpsimd(pid, &new_fpsimd)) { + int e = errno; + + ksft_test_result_fail("get_fpsimd(): %s\n", + strerror(errno)); + if (e == ESRCH) + goto disappeared; + + goto error; + } + if (memcmp(fpsimd, &new_fpsimd, sizeof(*fpsimd)) == 0) + ksft_test_result_pass("get_fpsimd() gave same state\n"); + else + ksft_test_result_fail("get_fpsimd() gave different state\n"); + vq = sve_vq_from_vl(sve->vl); newsvebufsz = SVE_PT_SVE_ZREG_OFFSET(vq, 1); From a1d7111257cdfba87a18a630fd96a44ba69c3d9c Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 13 Sep 2021 13:55:04 +0100 Subject: [PATCH 07/14] selftests: arm64: More comprehensively test the SVE ptrace interface Currently the selftest for the SVE register set is not quite as thorough as is desirable - it only validates that the value of a single Z register is not modified by a partial write to a lower numbered Z register after having previously been set through the FPSIMD regset. Make this more thorough: - Test the ability to set vector lengths and enumerate those supported in the system. - Validate data in all Z and P registers, plus FPSR and FPCR. - Test reads via the FPSIMD regset after set via the SVE regset. There's still some oversights, the main one being that due to the need to generate a pattern in FFR and the fact that this rewrite is primarily motivated by SME's streaming SVE which doesn't have FFR we don't currently test FFR. Update the TODO to reflect those that occurred to me (and fix an adjacent typo in there). Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20210913125505.52619-8-broonie@kernel.org Signed-off-by: Will Deacon --- tools/testing/selftests/arm64/fp/TODO | 9 +- tools/testing/selftests/arm64/fp/sve-ptrace.c | 327 +++++++++++++----- 2 files changed, 254 insertions(+), 82 deletions(-) diff --git a/tools/testing/selftests/arm64/fp/TODO b/tools/testing/selftests/arm64/fp/TODO index b6b7ebfcf362..44004e53da33 100644 --- a/tools/testing/selftests/arm64/fp/TODO +++ b/tools/testing/selftests/arm64/fp/TODO @@ -1,4 +1,7 @@ - Test unsupported values in the ABIs. -- More coverage for ptrace (eg, vector length conversions). -- Coverage for signals. -- Test PR_SVE_VL_INHERITY after a double fork. +- More coverage for ptrace: + - Get/set of FFR. + - Ensure ptraced processes actually see the register state visible through + the ptrace interface. + - Big endian. +- Test PR_SVE_VL_INHERIT after a double fork. diff --git a/tools/testing/selftests/arm64/fp/sve-ptrace.c b/tools/testing/selftests/arm64/fp/sve-ptrace.c index 31a2c2fc529d..199710ba65c7 100644 --- a/tools/testing/selftests/arm64/fp/sve-ptrace.c +++ b/tools/testing/selftests/arm64/fp/sve-ptrace.c @@ -1,15 +1,17 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (C) 2015-2020 ARM Limited. + * Copyright (C) 2015-2021 ARM Limited. * Original author: Dave Martin */ #include +#include #include #include #include #include #include #include +#include #include #include #include @@ -19,20 +21,22 @@ #include "../../kselftest.h" -#define EXPECTED_TESTS 19 +#define VL_TESTS (((SVE_VQ_MAX - SVE_VQ_MIN) + 1) * 3) +#define FPSIMD_TESTS 3 + +#define EXPECTED_TESTS (VL_TESTS + FPSIMD_TESTS) /* and don't like each other, so: */ #ifndef NT_ARM_SVE #define NT_ARM_SVE 0x405 #endif -static void dump(const void *buf, size_t size) +static void fill_buf(char *buf, size_t size) { - size_t i; - const unsigned char *p = buf; + int i; - for (i = 0; i < size; ++i) - printf(" %.2x", *p++); + for (i = 0; i < size; i++) + buf[i] = random(); } static int do_child(void) @@ -101,25 +105,228 @@ static int set_sve(pid_t pid, const struct user_sve_header *sve) return ptrace(PTRACE_SETREGSET, pid, NT_ARM_SVE, &iov); } -static void dump_sve_regs(const struct user_sve_header *sve, unsigned int num, - unsigned int vlmax) +/* Validate attempting to set the specfied VL via ptrace */ +static void ptrace_set_get_vl(pid_t child, unsigned int vl, bool *supported) { - unsigned int vq; - unsigned int i; + struct user_sve_header sve; + struct user_sve_header *new_sve = NULL; + size_t new_sve_size = 0; + int ret, prctl_vl; - if ((sve->flags & SVE_PT_REGS_MASK) != SVE_PT_REGS_SVE) - ksft_exit_fail_msg("Dumping non-SVE register\n"); + *supported = false; - if (vlmax > sve->vl) - vlmax = sve->vl; + /* Check if the VL is supported in this process */ + prctl_vl = prctl(PR_SVE_SET_VL, vl); + if (prctl_vl == -1) + ksft_exit_fail_msg("prctl(PR_SVE_SET_VL) failed: %s (%d)\n", + strerror(errno), errno); - vq = sve_vq_from_vl(sve->vl); - for (i = 0; i < num; ++i) { - printf("# z%u:", i); - dump((const char *)sve + SVE_PT_SVE_ZREG_OFFSET(vq, i), - vlmax); - printf("%s\n", vlmax == sve->vl ? "" : " ..."); + /* If the VL is not supported then a supported VL will be returned */ + *supported = (prctl_vl == vl); + + /* Set the VL by doing a set with no register payload */ + memset(&sve, 0, sizeof(sve)); + sve.size = sizeof(sve); + sve.vl = vl; + ret = set_sve(child, &sve); + if (ret != 0) { + ksft_test_result_fail("Failed to set VL %u\n", vl); + return; } + + /* + * Read back the new register state and verify that we have the + * same VL that we got from prctl() on ourselves. + */ + if (!get_sve(child, (void **)&new_sve, &new_sve_size)) { + ksft_test_result_fail("Failed to read VL %u\n", vl); + return; + } + + ksft_test_result(new_sve->vl = prctl_vl, "Set VL %u\n", vl); + + free(new_sve); +} + +static void check_u32(unsigned int vl, const char *reg, + uint32_t *in, uint32_t *out, int *errors) +{ + if (*in != *out) { + printf("# VL %d %s wrote %x read %x\n", + vl, reg, *in, *out); + (*errors)++; + } +} + +/* Validate attempting to set SVE data and read SVE data */ +static void ptrace_set_sve_get_sve_data(pid_t child, unsigned int vl) +{ + void *write_buf; + void *read_buf = NULL; + struct user_sve_header *write_sve; + struct user_sve_header *read_sve; + size_t read_sve_size = 0; + unsigned int vq = sve_vq_from_vl(vl); + int ret, i; + size_t data_size; + int errors = 0; + + data_size = SVE_PT_SVE_OFFSET + SVE_PT_SVE_SIZE(vq, SVE_PT_REGS_SVE); + write_buf = malloc(data_size); + if (!write_buf) { + ksft_test_result_fail("Error allocating %d byte buffer for VL %u\n", + data_size, vl); + return; + } + write_sve = write_buf; + + /* Set up some data and write it out */ + memset(write_sve, 0, data_size); + write_sve->size = data_size; + write_sve->vl = vl; + write_sve->flags = SVE_PT_REGS_SVE; + + for (i = 0; i < __SVE_NUM_ZREGS; i++) + fill_buf(write_buf + SVE_PT_SVE_ZREG_OFFSET(vq, i), + SVE_PT_SVE_ZREG_SIZE(vq)); + + for (i = 0; i < __SVE_NUM_PREGS; i++) + fill_buf(write_buf + SVE_PT_SVE_PREG_OFFSET(vq, i), + SVE_PT_SVE_PREG_SIZE(vq)); + + fill_buf(write_buf + SVE_PT_SVE_FPSR_OFFSET(vq), SVE_PT_SVE_FPSR_SIZE); + fill_buf(write_buf + SVE_PT_SVE_FPCR_OFFSET(vq), SVE_PT_SVE_FPCR_SIZE); + + /* TODO: Generate a valid FFR pattern */ + + ret = set_sve(child, write_sve); + if (ret != 0) { + ksft_test_result_fail("Failed to set VL %u data\n", vl); + goto out; + } + + /* Read the data back */ + if (!get_sve(child, (void **)&read_buf, &read_sve_size)) { + ksft_test_result_fail("Failed to read VL %u data\n", vl); + goto out; + } + read_sve = read_buf; + + /* We might read more data if there's extensions we don't know */ + if (read_sve->size < write_sve->size) { + ksft_test_result_fail("Wrote %d bytes, only read %d\n", + write_sve->size, read_sve->size); + goto out_read; + } + + for (i = 0; i < __SVE_NUM_ZREGS; i++) { + if (memcmp(write_buf + SVE_PT_SVE_ZREG_OFFSET(vq, i), + read_buf + SVE_PT_SVE_ZREG_OFFSET(vq, i), + SVE_PT_SVE_ZREG_SIZE(vq)) != 0) { + printf("# Mismatch in %u Z%d\n", vl, i); + errors++; + } + } + + for (i = 0; i < __SVE_NUM_PREGS; i++) { + if (memcmp(write_buf + SVE_PT_SVE_PREG_OFFSET(vq, i), + read_buf + SVE_PT_SVE_PREG_OFFSET(vq, i), + SVE_PT_SVE_PREG_SIZE(vq)) != 0) { + printf("# Mismatch in %u P%d\n", vl, i); + errors++; + } + } + + check_u32(vl, "FPSR", write_buf + SVE_PT_SVE_FPSR_OFFSET(vq), + read_buf + SVE_PT_SVE_FPSR_OFFSET(vq), &errors); + check_u32(vl, "FPCR", write_buf + SVE_PT_SVE_FPCR_OFFSET(vq), + read_buf + SVE_PT_SVE_FPCR_OFFSET(vq), &errors); + + ksft_test_result(errors == 0, "Set and get SVE data for VL %u\n", vl); + +out_read: + free(read_buf); +out: + free(write_buf); +} + +/* Validate attempting to set SVE data and read SVE data */ +static void ptrace_set_sve_get_fpsimd_data(pid_t child, unsigned int vl) +{ + void *write_buf; + struct user_sve_header *write_sve; + unsigned int vq = sve_vq_from_vl(vl); + struct user_fpsimd_state fpsimd_state; + int ret, i; + size_t data_size; + int errors = 0; + + if (__BYTE_ORDER == __BIG_ENDIAN) { + ksft_test_result_skip("Big endian not supported\n"); + return; + } + + data_size = SVE_PT_SVE_OFFSET + SVE_PT_SVE_SIZE(vq, SVE_PT_REGS_SVE); + write_buf = malloc(data_size); + if (!write_buf) { + ksft_test_result_fail("Error allocating %d byte buffer for VL %u\n", + data_size, vl); + return; + } + write_sve = write_buf; + + /* Set up some data and write it out */ + memset(write_sve, 0, data_size); + write_sve->size = data_size; + write_sve->vl = vl; + write_sve->flags = SVE_PT_REGS_SVE; + + for (i = 0; i < __SVE_NUM_ZREGS; i++) + fill_buf(write_buf + SVE_PT_SVE_ZREG_OFFSET(vq, i), + SVE_PT_SVE_ZREG_SIZE(vq)); + + fill_buf(write_buf + SVE_PT_SVE_FPSR_OFFSET(vq), SVE_PT_SVE_FPSR_SIZE); + fill_buf(write_buf + SVE_PT_SVE_FPCR_OFFSET(vq), SVE_PT_SVE_FPCR_SIZE); + + ret = set_sve(child, write_sve); + if (ret != 0) { + ksft_test_result_fail("Failed to set VL %u data\n", vl); + goto out; + } + + /* Read the data back */ + if (get_fpsimd(child, &fpsimd_state)) { + ksft_test_result_fail("Failed to read VL %u FPSIMD data\n", + vl); + goto out; + } + + for (i = 0; i < __SVE_NUM_ZREGS; i++) { + __uint128_t tmp = 0; + + /* + * Z regs are stored endianness invariant, this won't + * work for big endian + */ + memcpy(&tmp, write_buf + SVE_PT_SVE_ZREG_OFFSET(vq, i), + sizeof(tmp)); + + if (tmp != fpsimd_state.vregs[i]) { + printf("# Mismatch in FPSIMD for VL %u Z%d\n", vl, i); + errors++; + } + } + + check_u32(vl, "FPSR", write_buf + SVE_PT_SVE_FPSR_OFFSET(vq), + &fpsimd_state.fpsr, &errors); + check_u32(vl, "FPCR", write_buf + SVE_PT_SVE_FPCR_OFFSET(vq), + &fpsimd_state.fpcr, &errors); + + ksft_test_result(errors == 0, "Set and get FPSIMD data for VL %u\n", + vl); + +out: + free(write_buf); } static int do_parent(pid_t child) @@ -128,13 +335,14 @@ static int do_parent(pid_t child) pid_t pid; int status; siginfo_t si; - void *svebuf = NULL, *newsvebuf; - size_t svebufsz = 0, newsvebufsz; - struct user_sve_header *sve, *new_sve; + void *svebuf = NULL; + size_t svebufsz = 0; + struct user_sve_header *sve; struct user_fpsimd_state *fpsimd, new_fpsimd; unsigned int i, j; unsigned char *p; - unsigned int vq; + unsigned int vq, vl; + bool vl_supported; /* Attach to the child */ while (1) { @@ -246,62 +454,21 @@ static int do_parent(pid_t child) else ksft_test_result_fail("get_fpsimd() gave different state\n"); - vq = sve_vq_from_vl(sve->vl); + /* Step through every possible VQ */ + for (vq = SVE_VQ_MIN; vq <= SVE_VQ_MAX; vq++) { + vl = sve_vl_from_vq(vq); - newsvebufsz = SVE_PT_SVE_ZREG_OFFSET(vq, 1); - new_sve = newsvebuf = malloc(newsvebufsz); - if (!new_sve) { - errno = ENOMEM; - perror(NULL); - goto error; - } + /* First, try to set this vector length */ + ptrace_set_get_vl(child, vl, &vl_supported); - *new_sve = *sve; - new_sve->flags &= ~SVE_PT_REGS_MASK; - new_sve->flags |= SVE_PT_REGS_SVE; - memset((char *)new_sve + SVE_PT_SVE_ZREG_OFFSET(vq, 0), - 0, SVE_PT_SVE_ZREG_SIZE(vq)); - new_sve->size = SVE_PT_SVE_ZREG_OFFSET(vq, 1); - if (set_sve(pid, new_sve)) { - int e = errno; - - ksft_test_result_fail("set_sve(ZREG): %s\n", strerror(errno)); - if (e == ESRCH) - goto disappeared; - - goto error; - } - - /* Try to read back the value we just set */ - new_sve = get_sve(pid, &newsvebuf, &newsvebufsz); - if (!new_sve) { - int e = errno; - - ksft_test_result_fail("get_sve(ZREG): %s\n", strerror(errno)); - if (e == ESRCH) - goto disappeared; - - goto error; - } - - ksft_test_result((new_sve->flags & SVE_PT_REGS_MASK) == SVE_PT_REGS_SVE, - "Get SVE registers\n"); - if ((new_sve->flags & SVE_PT_REGS_MASK) != SVE_PT_REGS_SVE) - goto error; - - dump_sve_regs(new_sve, 3, sizeof fpsimd->vregs[0]); - - /* Verify that the register we set has the value we expected */ - p = (unsigned char *)new_sve + SVE_PT_SVE_ZREG_OFFSET(vq, 1); - for (i = 0; i < sizeof fpsimd->vregs[0]; ++i) { - unsigned char expected = i; - - if (__BYTE_ORDER == __BIG_ENDIAN) - expected = sizeof fpsimd->vregs[0] - 1 - expected; - - ksft_test_result(p[i] == expected, "buf[%d] == expected\n", i); - if (p[i] != expected) - goto error; + /* If the VL is supported validate data set/get */ + if (vl_supported) { + ptrace_set_sve_get_sve_data(child, vl); + ptrace_set_sve_get_fpsimd_data(child, vl); + } else { + ksft_test_result_skip("set SVE get SVE for VL %d\n", vl); + ksft_test_result_skip("set SVE get FPSIMD for VL %d\n", vl); + } } ret = EXIT_SUCCESS; @@ -318,6 +485,8 @@ int main(void) int ret = EXIT_SUCCESS; pid_t child; + srandom(getpid()); + ksft_print_header(); ksft_set_plan(EXPECTED_TESTS); From 34785030dc069302be20b09f0f647ddb03203d72 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 13 Sep 2021 13:55:05 +0100 Subject: [PATCH 08/14] selftests: arm64: Move FPSIMD in SVE ptrace test into a function Now that all the other tests are in functions rather than inline in the main parent process function also move the test for accessing the FPSIMD registers via the SVE regset out into their own function. Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20210913125505.52619-9-broonie@kernel.org Signed-off-by: Will Deacon --- tools/testing/selftests/arm64/fp/sve-ptrace.c | 120 +++++++++--------- 1 file changed, 59 insertions(+), 61 deletions(-) diff --git a/tools/testing/selftests/arm64/fp/sve-ptrace.c b/tools/testing/selftests/arm64/fp/sve-ptrace.c index 199710ba65c7..ac0629f05365 100644 --- a/tools/testing/selftests/arm64/fp/sve-ptrace.c +++ b/tools/testing/selftests/arm64/fp/sve-ptrace.c @@ -158,6 +158,63 @@ static void check_u32(unsigned int vl, const char *reg, } } +/* Access the FPSIMD registers via the SVE regset */ +static void ptrace_sve_fpsimd(pid_t child) +{ + void *svebuf = NULL; + size_t svebufsz = 0; + struct user_sve_header *sve; + struct user_fpsimd_state *fpsimd, new_fpsimd; + unsigned int i, j; + unsigned char *p; + + /* New process should start with FPSIMD registers only */ + sve = get_sve(child, &svebuf, &svebufsz); + if (!sve) { + ksft_test_result_fail("get_sve: %s\n", strerror(errno)); + + return; + } else { + ksft_test_result_pass("get_sve(FPSIMD)\n"); + } + + ksft_test_result((sve->flags & SVE_PT_REGS_MASK) == SVE_PT_REGS_FPSIMD, + "Set FPSIMD registers\n"); + if ((sve->flags & SVE_PT_REGS_MASK) != SVE_PT_REGS_FPSIMD) + goto out; + + /* Try to set a known FPSIMD state via PT_REGS_SVE */ + fpsimd = (struct user_fpsimd_state *)((char *)sve + + SVE_PT_FPSIMD_OFFSET); + for (i = 0; i < 32; ++i) { + p = (unsigned char *)&fpsimd->vregs[i]; + + for (j = 0; j < sizeof(fpsimd->vregs[i]); ++j) + p[j] = j; + } + + if (set_sve(child, sve)) { + ksft_test_result_fail("set_sve(FPSIMD): %s\n", + strerror(errno)); + + goto out; + } + + /* Verify via the FPSIMD regset */ + if (get_fpsimd(child, &new_fpsimd)) { + ksft_test_result_fail("get_fpsimd(): %s\n", + strerror(errno)); + goto out; + } + if (memcmp(fpsimd, &new_fpsimd, sizeof(*fpsimd)) == 0) + ksft_test_result_pass("get_fpsimd() gave same state\n"); + else + ksft_test_result_fail("get_fpsimd() gave different state\n"); + +out: + free(svebuf); +} + /* Validate attempting to set SVE data and read SVE data */ static void ptrace_set_sve_get_sve_data(pid_t child, unsigned int vl) { @@ -335,12 +392,6 @@ static int do_parent(pid_t child) pid_t pid; int status; siginfo_t si; - void *svebuf = NULL; - size_t svebufsz = 0; - struct user_sve_header *sve; - struct user_fpsimd_state *fpsimd, new_fpsimd; - unsigned int i, j; - unsigned char *p; unsigned int vq, vl; bool vl_supported; @@ -398,61 +449,8 @@ static int do_parent(pid_t child) } } - /* New process should start with FPSIMD registers only */ - sve = get_sve(pid, &svebuf, &svebufsz); - if (!sve) { - int e = errno; - - ksft_test_result_fail("get_sve: %s\n", strerror(errno)); - if (e == ESRCH) - goto disappeared; - - goto error; - } else { - ksft_test_result_pass("get_sve(FPSIMD)\n"); - } - - ksft_test_result((sve->flags & SVE_PT_REGS_MASK) == SVE_PT_REGS_FPSIMD, - "Set FPSIMD registers\n"); - if ((sve->flags & SVE_PT_REGS_MASK) != SVE_PT_REGS_FPSIMD) - goto error; - - /* Try to set a known FPSIMD state via PT_REGS_SVE */ - fpsimd = (struct user_fpsimd_state *)((char *)sve + - SVE_PT_FPSIMD_OFFSET); - for (i = 0; i < 32; ++i) { - p = (unsigned char *)&fpsimd->vregs[i]; - - for (j = 0; j < sizeof fpsimd->vregs[i]; ++j) - p[j] = j; - } - - if (set_sve(pid, sve)) { - int e = errno; - - ksft_test_result_fail("set_sve(FPSIMD): %s\n", - strerror(errno)); - if (e == ESRCH) - goto disappeared; - - goto error; - } - - /* Verify via the FPSIMD regset */ - if (get_fpsimd(pid, &new_fpsimd)) { - int e = errno; - - ksft_test_result_fail("get_fpsimd(): %s\n", - strerror(errno)); - if (e == ESRCH) - goto disappeared; - - goto error; - } - if (memcmp(fpsimd, &new_fpsimd, sizeof(*fpsimd)) == 0) - ksft_test_result_pass("get_fpsimd() gave same state\n"); - else - ksft_test_result_fail("get_fpsimd() gave different state\n"); + /* FPSIMD via SVE regset */ + ptrace_sve_fpsimd(child); /* Step through every possible VQ */ for (vq = SVE_VQ_MIN; vq <= SVE_VQ_MAX; vq++) { From ff944c44b782d34dc95ea9dcf3a5b62a0bdbac18 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 29 Sep 2021 16:19:22 +0100 Subject: [PATCH 09/14] selftests: arm64: Fix printf() format mismatch in vec-syscfg The format for this error message calls for the plain text version of the error but we weren't supply it. Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20210929151925.9601-2-broonie@kernel.org Signed-off-by: Will Deacon --- tools/testing/selftests/arm64/fp/vec-syscfg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/arm64/fp/vec-syscfg.c b/tools/testing/selftests/arm64/fp/vec-syscfg.c index c02071dcb563..b2de002ee325 100644 --- a/tools/testing/selftests/arm64/fp/vec-syscfg.c +++ b/tools/testing/selftests/arm64/fp/vec-syscfg.c @@ -109,7 +109,7 @@ static int get_child_rdvl(struct vec_data *data) /* exec() a new binary which puts the VL on stdout */ ret = execl(data->rdvl_binary, data->rdvl_binary, NULL); - fprintf(stderr, "execl(%s) failed: %d\n", + fprintf(stderr, "execl(%s) failed: %d (%s)\n", data->rdvl_binary, errno, strerror(errno)); exit(EXIT_FAILURE); From 4caf339c037c5e70d0ea789af093e123c2c6fc80 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 29 Sep 2021 16:19:23 +0100 Subject: [PATCH 10/14] selftests: arm64: Remove bogus error check on writing to files Due to some refactoring with the error handling we ended up mangling things so we never actually set ret and therefore shouldn't be checking it. Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20210929151925.9601-3-broonie@kernel.org Signed-off-by: Will Deacon --- tools/testing/selftests/arm64/fp/vec-syscfg.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tools/testing/selftests/arm64/fp/vec-syscfg.c b/tools/testing/selftests/arm64/fp/vec-syscfg.c index b2de002ee325..d48d3ee1bc36 100644 --- a/tools/testing/selftests/arm64/fp/vec-syscfg.c +++ b/tools/testing/selftests/arm64/fp/vec-syscfg.c @@ -180,7 +180,6 @@ static int file_read_integer(const char *name, int *val) static int file_write_integer(const char *name, int val) { FILE *f; - int ret; f = fopen(name, "w"); if (!f) { @@ -192,11 +191,6 @@ static int file_write_integer(const char *name, int val) fprintf(f, "%d", val); fclose(f); - if (ret < 0) { - ksft_test_result_fail("Error writing %d to %s\n", - val, name); - return -1; - } return 0; } From e42391150eabcfb2cc42f58c8b0394ebc3039c34 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 29 Sep 2021 16:19:24 +0100 Subject: [PATCH 11/14] selftests: arm64: Fix and enable test for setting current VL in vec-syscfg We had some test code for verifying that we can write the current VL via the prctl() interface but the condition for the test was inverted which wasn't noticed as it was never actually hooked up to the array of tests we execute. Fix this. Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20210929151925.9601-4-broonie@kernel.org Signed-off-by: Will Deacon --- tools/testing/selftests/arm64/fp/vec-syscfg.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/tools/testing/selftests/arm64/fp/vec-syscfg.c b/tools/testing/selftests/arm64/fp/vec-syscfg.c index d48d3ee1bc36..9d6ac843e651 100644 --- a/tools/testing/selftests/arm64/fp/vec-syscfg.c +++ b/tools/testing/selftests/arm64/fp/vec-syscfg.c @@ -329,12 +329,9 @@ static void prctl_set_same(struct vec_data *data) return; } - if (cur_vl != data->rdvl()) - ksft_test_result_pass("%s current VL is %d\n", - data->name, ret); - else - ksft_test_result_fail("%s prctl() VL %d but RDVL is %d\n", - data->name, ret, data->rdvl()); + ksft_test_result(cur_vl == data->rdvl(), + "%s set VL %d and have VL %d\n", + data->name, cur_vl, data->rdvl()); } /* Can we set a new VL for this process? */ @@ -555,6 +552,7 @@ static const test_type tests[] = { proc_write_max, prctl_get, + prctl_set_same, prctl_set, prctl_set_no_child, prctl_set_for_child, From 8694e5e6388695195a32bd5746635ca166a8df56 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 29 Sep 2021 16:19:25 +0100 Subject: [PATCH 12/14] selftests: arm64: Verify that all possible vector lengths are handled As part of the enumeration interface for setting vector lengths it is valid to set vector lengths not supported in the system, these will be rounded to a supported vector length and returned from the prctl(). Add a test which exercises this for every valid vector length and makes sure that the return value is as expected and that this is reflected in the actual system state. Signed-off-by: Mark Brown Reviewed-by: Tomohiro Misono Link: https://lore.kernel.org/r/20210929151925.9601-5-broonie@kernel.org Signed-off-by: Will Deacon --- tools/testing/selftests/arm64/fp/vec-syscfg.c | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/tools/testing/selftests/arm64/fp/vec-syscfg.c b/tools/testing/selftests/arm64/fp/vec-syscfg.c index 9d6ac843e651..272b888e018e 100644 --- a/tools/testing/selftests/arm64/fp/vec-syscfg.c +++ b/tools/testing/selftests/arm64/fp/vec-syscfg.c @@ -540,6 +540,82 @@ static void prctl_set_onexec(struct vec_data *data) file_write_integer(data->default_vl_file, data->default_vl); } +/* For each VQ verify that setting via prctl() does the right thing */ +static void prctl_set_all_vqs(struct vec_data *data) +{ + int ret, vq, vl, new_vl; + int errors = 0; + + if (!data->min_vl || !data->max_vl) { + ksft_test_result_skip("%s Failed to enumerate VLs, not testing VL setting\n", + data->name); + return; + } + + for (vq = SVE_VQ_MIN; vq <= SVE_VQ_MAX; vq++) { + vl = sve_vl_from_vq(vq); + + /* Attempt to set the VL */ + ret = prctl(data->prctl_set, vl); + if (ret < 0) { + errors++; + ksft_print_msg("%s prctl set failed for %d: %d (%s)\n", + data->name, vl, + errno, strerror(errno)); + continue; + } + + new_vl = ret & PR_SVE_VL_LEN_MASK; + + /* Check that we actually have the reported new VL */ + if (data->rdvl() != new_vl) { + ksft_print_msg("Set %s VL %d but RDVL reports %d\n", + data->name, new_vl, data->rdvl()); + errors++; + } + + /* Was that the VL we asked for? */ + if (new_vl == vl) + continue; + + /* Should round up to the minimum VL if below it */ + if (vl < data->min_vl) { + if (new_vl != data->min_vl) { + ksft_print_msg("%s VL %d returned %d not minimum %d\n", + data->name, vl, new_vl, + data->min_vl); + errors++; + } + + continue; + } + + /* Should round down to maximum VL if above it */ + if (vl > data->max_vl) { + if (new_vl != data->max_vl) { + ksft_print_msg("%s VL %d returned %d not maximum %d\n", + data->name, vl, new_vl, + data->max_vl); + errors++; + } + + continue; + } + + /* Otherwise we should've rounded down */ + if (!(new_vl < vl)) { + ksft_print_msg("%s VL %d returned %d, did not round down\n", + data->name, vl, new_vl); + errors++; + + continue; + } + } + + ksft_test_result(errors == 0, "%s prctl() set all VLs, %d errors\n", + data->name, errors); +} + typedef void (*test_type)(struct vec_data *); static const test_type tests[] = { @@ -557,6 +633,7 @@ static const test_type tests[] = { prctl_set_no_child, prctl_set_for_child, prctl_set_onexec, + prctl_set_all_vqs, }; int main(void) From 0ba1ce1e86052deea3f115285802ce8ffff3b152 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 5 Oct 2021 13:35:37 +0100 Subject: [PATCH 13/14] selftests: arm64: Add coverage of ptrace flags for SVE VL inheritance Add a test that covers enabling and disabling of SVE vector length inheritance via the ptrace interface. Signed-off-by: Mark Brown Acked-by: Catalin Marinas Link: https://lore.kernel.org/r/20211005123537.976795-1-broonie@kernel.org Signed-off-by: Will Deacon --- tools/testing/selftests/arm64/fp/sve-ptrace.c | 55 ++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/arm64/fp/sve-ptrace.c b/tools/testing/selftests/arm64/fp/sve-ptrace.c index ac0629f05365..c4417bc48d4f 100644 --- a/tools/testing/selftests/arm64/fp/sve-ptrace.c +++ b/tools/testing/selftests/arm64/fp/sve-ptrace.c @@ -22,7 +22,7 @@ #include "../../kselftest.h" #define VL_TESTS (((SVE_VQ_MAX - SVE_VQ_MIN) + 1) * 3) -#define FPSIMD_TESTS 3 +#define FPSIMD_TESTS 5 #define EXPECTED_TESTS (VL_TESTS + FPSIMD_TESTS) @@ -105,6 +105,56 @@ static int set_sve(pid_t pid, const struct user_sve_header *sve) return ptrace(PTRACE_SETREGSET, pid, NT_ARM_SVE, &iov); } +/* Validate setting and getting the inherit flag */ +static void ptrace_set_get_inherit(pid_t child) +{ + struct user_sve_header sve; + struct user_sve_header *new_sve = NULL; + size_t new_sve_size = 0; + int ret; + + /* First set the flag */ + memset(&sve, 0, sizeof(sve)); + sve.size = sizeof(sve); + sve.vl = sve_vl_from_vq(SVE_VQ_MIN); + sve.flags = SVE_PT_VL_INHERIT; + ret = set_sve(child, &sve); + if (ret != 0) { + ksft_test_result_fail("Failed to set SVE_PT_VL_INHERIT\n"); + return; + } + + /* + * Read back the new register state and verify that we have + * set the flags we expected. + */ + if (!get_sve(child, (void **)&new_sve, &new_sve_size)) { + ksft_test_result_fail("Failed to read SVE flags\n"); + return; + } + + ksft_test_result(new_sve->flags & SVE_PT_VL_INHERIT, + "SVE_PT_VL_INHERIT set\n"); + + /* Now clear */ + sve.flags &= ~SVE_PT_VL_INHERIT; + ret = set_sve(child, &sve); + if (ret != 0) { + ksft_test_result_fail("Failed to clear SVE_PT_VL_INHERIT\n"); + return; + } + + if (!get_sve(child, (void **)&new_sve, &new_sve_size)) { + ksft_test_result_fail("Failed to read SVE flags\n"); + return; + } + + ksft_test_result(!(new_sve->flags & SVE_PT_VL_INHERIT), + "SVE_PT_VL_INHERIT cleared\n"); + + free(new_sve); +} + /* Validate attempting to set the specfied VL via ptrace */ static void ptrace_set_get_vl(pid_t child, unsigned int vl, bool *supported) { @@ -452,6 +502,9 @@ static int do_parent(pid_t child) /* FPSIMD via SVE regset */ ptrace_sve_fpsimd(child); + /* prctl() flags */ + ptrace_set_get_inherit(child); + /* Step through every possible VQ */ for (vq = SVE_VQ_MIN; vq <= SVE_VQ_MAX; vq++) { vl = sve_vl_from_vq(vq); From 260ea4ba94e88385724754c644212ce552de8b01 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 19 Oct 2021 19:18:51 +0100 Subject: [PATCH 14/14] selftests: arm64: Factor out utility functions for assembly FP tests The various floating point test programs written in assembly have a bunch of helper functions and macros which are cut'n'pasted between them. Factor them out into a separate source file which is linked into all of them. We don't include memcmp() since it isn't as generic as it should be and directly branches to report an error in the programs. Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20211019181851.3341232-1-broonie@kernel.org Signed-off-by: Will Deacon --- tools/testing/selftests/arm64/fp/Makefile | 4 +- tools/testing/selftests/arm64/fp/asm-utils.S | 172 ++++++++++++++++++ tools/testing/selftests/arm64/fp/assembler.h | 11 ++ .../testing/selftests/arm64/fp/fpsimd-test.S | 164 ----------------- tools/testing/selftests/arm64/fp/sve-test.S | 163 ----------------- 5 files changed, 185 insertions(+), 329 deletions(-) create mode 100644 tools/testing/selftests/arm64/fp/asm-utils.S diff --git a/tools/testing/selftests/arm64/fp/Makefile b/tools/testing/selftests/arm64/fp/Makefile index 4367125b7c27..ba1488c7c315 100644 --- a/tools/testing/selftests/arm64/fp/Makefile +++ b/tools/testing/selftests/arm64/fp/Makefile @@ -9,12 +9,12 @@ TEST_PROGS_EXTENDED := fpsimd-test fpsimd-stress \ all: $(TEST_GEN_PROGS) $(TEST_PROGS_EXTENDED) -fpsimd-test: fpsimd-test.o +fpsimd-test: fpsimd-test.o asm-utils.o $(CC) -nostdlib $^ -o $@ rdvl-sve: rdvl-sve.o rdvl.o sve-ptrace: sve-ptrace.o sve-probe-vls: sve-probe-vls.o rdvl.o -sve-test: sve-test.o +sve-test: sve-test.o asm-utils.o $(CC) -nostdlib $^ -o $@ vec-syscfg: vec-syscfg.o rdvl.o vlset: vlset.o diff --git a/tools/testing/selftests/arm64/fp/asm-utils.S b/tools/testing/selftests/arm64/fp/asm-utils.S new file mode 100644 index 000000000000..4b9728efc18d --- /dev/null +++ b/tools/testing/selftests/arm64/fp/asm-utils.S @@ -0,0 +1,172 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2015-2021 ARM Limited. +// Original author: Dave Martin +// +// Utility functions for assembly code. + +#include +#include "assembler.h" + +// Print a single character x0 to stdout +// Clobbers x0-x2,x8 +function putc + str x0, [sp, #-16]! + + mov x0, #1 // STDOUT_FILENO + mov x1, sp + mov x2, #1 + mov x8, #__NR_write + svc #0 + + add sp, sp, #16 + ret +endfunction +.globl putc + +// Print a NUL-terminated string starting at address x0 to stdout +// Clobbers x0-x3,x8 +function puts + mov x1, x0 + + mov x2, #0 +0: ldrb w3, [x0], #1 + cbz w3, 1f + add x2, x2, #1 + b 0b + +1: mov w0, #1 // STDOUT_FILENO + mov x8, #__NR_write + svc #0 + + ret +endfunction +.globl puts + +// Print an unsigned decimal number x0 to stdout +// Clobbers x0-x4,x8 +function putdec + mov x1, sp + str x30, [sp, #-32]! // Result can't be > 20 digits + + mov x2, #0 + strb w2, [x1, #-1]! // Write the NUL terminator + + mov x2, #10 +0: udiv x3, x0, x2 // div-mod loop to generate the digits + msub x0, x3, x2, x0 + add w0, w0, #'0' + strb w0, [x1, #-1]! + mov x0, x3 + cbnz x3, 0b + + ldrb w0, [x1] + cbnz w0, 1f + mov w0, #'0' // Print "0" for 0, not "" + strb w0, [x1, #-1]! + +1: mov x0, x1 + bl puts + + ldr x30, [sp], #32 + ret +endfunction +.globl putdec + +// Print an unsigned decimal number x0 to stdout, followed by a newline +// Clobbers x0-x5,x8 +function putdecn + mov x5, x30 + + bl putdec + mov x0, #'\n' + bl putc + + ret x5 +endfunction +.globl putdecn + +// Clobbers x0-x3,x8 +function puthexb + str x30, [sp, #-0x10]! + + mov w3, w0 + lsr w0, w0, #4 + bl puthexnibble + mov w0, w3 + + ldr x30, [sp], #0x10 + // fall through to puthexnibble +endfunction +.globl puthexb + +// Clobbers x0-x2,x8 +function puthexnibble + and w0, w0, #0xf + cmp w0, #10 + blo 1f + add w0, w0, #'a' - ('9' + 1) +1: add w0, w0, #'0' + b putc +endfunction +.globl puthexnibble + +// x0=data in, x1=size in, clobbers x0-x5,x8 +function dumphex + str x30, [sp, #-0x10]! + + mov x4, x0 + mov x5, x1 + +0: subs x5, x5, #1 + b.lo 1f + ldrb w0, [x4], #1 + bl puthexb + b 0b + +1: ldr x30, [sp], #0x10 + ret +endfunction +.globl dumphex + + // Trivial memory copy: copy x2 bytes, starting at address x1, to address x0. +// Clobbers x0-x3 +function memcpy + cmp x2, #0 + b.eq 1f +0: ldrb w3, [x1], #1 + strb w3, [x0], #1 + subs x2, x2, #1 + b.ne 0b +1: ret +endfunction +.globl memcpy + +// Fill x1 bytes starting at x0 with 0xae (for canary purposes) +// Clobbers x1, x2. +function memfill_ae + mov w2, #0xae + b memfill +endfunction +.globl memfill_ae + +// Fill x1 bytes starting at x0 with 0. +// Clobbers x1, x2. +function memclr + mov w2, #0 +endfunction +.globl memclr + // fall through to memfill + +// Trivial memory fill: fill x1 bytes starting at address x0 with byte w2 +// Clobbers x1 +function memfill + cmp x1, #0 + b.eq 1f + +0: strb w2, [x0], #1 + subs x1, x1, #1 + b.ne 0b + +1: ret +endfunction +.globl memfill diff --git a/tools/testing/selftests/arm64/fp/assembler.h b/tools/testing/selftests/arm64/fp/assembler.h index 8944f2189206..90bd433d2665 100644 --- a/tools/testing/selftests/arm64/fp/assembler.h +++ b/tools/testing/selftests/arm64/fp/assembler.h @@ -54,4 +54,15 @@ endfunction .purgem \name\()_entry .endm +// Utility macro to print a literal string +// Clobbers x0-x4,x8 +.macro puts string + .pushsection .rodata.str1.1, "aMS", 1 +.L__puts_literal\@: .string "\string" + .popsection + + ldr x0, =.L__puts_literal\@ + bl puts +.endm + #endif /* ! ASSEMBLER_H */ diff --git a/tools/testing/selftests/arm64/fp/fpsimd-test.S b/tools/testing/selftests/arm64/fp/fpsimd-test.S index 0dbd594c2747..e21e8ea52c7e 100644 --- a/tools/testing/selftests/arm64/fp/fpsimd-test.S +++ b/tools/testing/selftests/arm64/fp/fpsimd-test.S @@ -33,131 +33,6 @@ define_accessor setv, NVR, _vldr define_accessor getv, NVR, _vstr -// Print a single character x0 to stdout -// Clobbers x0-x2,x8 -function putc - str x0, [sp, #-16]! - - mov x0, #1 // STDOUT_FILENO - mov x1, sp - mov x2, #1 - mov x8, #__NR_write - svc #0 - - add sp, sp, #16 - ret -endfunction - -// Print a NUL-terminated string starting at address x0 to stdout -// Clobbers x0-x3,x8 -function puts - mov x1, x0 - - mov x2, #0 -0: ldrb w3, [x0], #1 - cbz w3, 1f - add x2, x2, #1 - b 0b - -1: mov w0, #1 // STDOUT_FILENO - mov x8, #__NR_write - svc #0 - - ret -endfunction - -// Utility macro to print a literal string -// Clobbers x0-x4,x8 -.macro puts string - .pushsection .rodata.str1.1, "aMS", 1 -.L__puts_literal\@: .string "\string" - .popsection - - ldr x0, =.L__puts_literal\@ - bl puts -.endm - -// Print an unsigned decimal number x0 to stdout -// Clobbers x0-x4,x8 -function putdec - mov x1, sp - str x30, [sp, #-32]! // Result can't be > 20 digits - - mov x2, #0 - strb w2, [x1, #-1]! // Write the NUL terminator - - mov x2, #10 -0: udiv x3, x0, x2 // div-mod loop to generate the digits - msub x0, x3, x2, x0 - add w0, w0, #'0' - strb w0, [x1, #-1]! - mov x0, x3 - cbnz x3, 0b - - ldrb w0, [x1] - cbnz w0, 1f - mov w0, #'0' // Print "0" for 0, not "" - strb w0, [x1, #-1]! - -1: mov x0, x1 - bl puts - - ldr x30, [sp], #32 - ret -endfunction - -// Print an unsigned decimal number x0 to stdout, followed by a newline -// Clobbers x0-x5,x8 -function putdecn - mov x5, x30 - - bl putdec - mov x0, #'\n' - bl putc - - ret x5 -endfunction - - -// Clobbers x0-x3,x8 -function puthexb - str x30, [sp, #-0x10]! - - mov w3, w0 - lsr w0, w0, #4 - bl puthexnibble - mov w0, w3 - - ldr x30, [sp], #0x10 - // fall through to puthexnibble -endfunction -// Clobbers x0-x2,x8 -function puthexnibble - and w0, w0, #0xf - cmp w0, #10 - blo 1f - add w0, w0, #'a' - ('9' + 1) -1: add w0, w0, #'0' - b putc -endfunction - -// x0=data in, x1=size in, clobbers x0-x5,x8 -function dumphex - str x30, [sp, #-0x10]! - - mov x4, x0 - mov x5, x1 - -0: subs x5, x5, #1 - b.lo 1f - ldrb w0, [x4], #1 - bl puthexb - b 0b - -1: ldr x30, [sp], #0x10 - ret -endfunction - // Declare some storate space to shadow the SVE register contents: .pushsection .text .data @@ -168,18 +43,6 @@ scratch: .space MAXVL_B .popsection -// Trivial memory copy: copy x2 bytes, starting at address x1, to address x0. -// Clobbers x0-x3 -function memcpy - cmp x2, #0 - b.eq 1f -0: ldrb w3, [x1], #1 - strb w3, [x0], #1 - subs x2, x2, #1 - b.ne 0b -1: ret -endfunction - // Generate a test pattern for storage in SVE registers // x0: pid (16 bits) // x1: register number (6 bits) @@ -227,33 +90,6 @@ function setup_vreg ret x4 endfunction -// Fill x1 bytes starting at x0 with 0xae (for canary purposes) -// Clobbers x1, x2. -function memfill_ae - mov w2, #0xae - b memfill -endfunction - -// Fill x1 bytes starting at x0 with 0. -// Clobbers x1, x2. -function memclr - mov w2, #0 -endfunction - // fall through to memfill - -// Trivial memory fill: fill x1 bytes starting at address x0 with byte w2 -// Clobbers x1 -function memfill - cmp x1, #0 - b.eq 1f - -0: strb w2, [x0], #1 - subs x1, x1, #1 - b.ne 0b - -1: ret -endfunction - // Trivial memory compare: compare x2 bytes starting at address x0 with // bytes starting at address x1. // Returns only if all bytes match; otherwise, the program is aborted. diff --git a/tools/testing/selftests/arm64/fp/sve-test.S b/tools/testing/selftests/arm64/fp/sve-test.S index e3e08d9c7020..f5b1b48ffff2 100644 --- a/tools/testing/selftests/arm64/fp/sve-test.S +++ b/tools/testing/selftests/arm64/fp/sve-test.S @@ -46,130 +46,6 @@ define_accessor getz, NZR, _sve_str_v define_accessor setp, NPR, _sve_ldr_p define_accessor getp, NPR, _sve_str_p -// Print a single character x0 to stdout -// Clobbers x0-x2,x8 -function putc - str x0, [sp, #-16]! - - mov x0, #1 // STDOUT_FILENO - mov x1, sp - mov x2, #1 - mov x8, #__NR_write - svc #0 - - add sp, sp, #16 - ret -endfunction - -// Print a NUL-terminated string starting at address x0 to stdout -// Clobbers x0-x3,x8 -function puts - mov x1, x0 - - mov x2, #0 -0: ldrb w3, [x0], #1 - cbz w3, 1f - add x2, x2, #1 - b 0b - -1: mov w0, #1 // STDOUT_FILENO - mov x8, #__NR_write - svc #0 - - ret -endfunction - -// Utility macro to print a literal string -// Clobbers x0-x4,x8 -.macro puts string - .pushsection .rodata.str1.1, "aMS", 1 -.L__puts_literal\@: .string "\string" - .popsection - - ldr x0, =.L__puts_literal\@ - bl puts -.endm - -// Print an unsigned decimal number x0 to stdout -// Clobbers x0-x4,x8 -function putdec - mov x1, sp - str x30, [sp, #-32]! // Result can't be > 20 digits - - mov x2, #0 - strb w2, [x1, #-1]! // Write the NUL terminator - - mov x2, #10 -0: udiv x3, x0, x2 // div-mod loop to generate the digits - msub x0, x3, x2, x0 - add w0, w0, #'0' - strb w0, [x1, #-1]! - mov x0, x3 - cbnz x3, 0b - - ldrb w0, [x1] - cbnz w0, 1f - mov w0, #'0' // Print "0" for 0, not "" - strb w0, [x1, #-1]! - -1: mov x0, x1 - bl puts - - ldr x30, [sp], #32 - ret -endfunction - -// Print an unsigned decimal number x0 to stdout, followed by a newline -// Clobbers x0-x5,x8 -function putdecn - mov x5, x30 - - bl putdec - mov x0, #'\n' - bl putc - - ret x5 -endfunction - -// Clobbers x0-x3,x8 -function puthexb - str x30, [sp, #-0x10]! - - mov w3, w0 - lsr w0, w0, #4 - bl puthexnibble - mov w0, w3 - - ldr x30, [sp], #0x10 - // fall through to puthexnibble -endfunction -// Clobbers x0-x2,x8 -function puthexnibble - and w0, w0, #0xf - cmp w0, #10 - blo 1f - add w0, w0, #'a' - ('9' + 1) -1: add w0, w0, #'0' - b putc -endfunction - -// x0=data in, x1=size in, clobbers x0-x5,x8 -function dumphex - str x30, [sp, #-0x10]! - - mov x4, x0 - mov x5, x1 - -0: subs x5, x5, #1 - b.lo 1f - ldrb w0, [x4], #1 - bl puthexb - b 0b - -1: ldr x30, [sp], #0x10 - ret -endfunction - // Declare some storate space to shadow the SVE register contents: .pushsection .text .data @@ -184,18 +60,6 @@ scratch: .space MAXVL_B .popsection -// Trivial memory copy: copy x2 bytes, starting at address x1, to address x0. -// Clobbers x0-x3 -function memcpy - cmp x2, #0 - b.eq 1f -0: ldrb w3, [x1], #1 - strb w3, [x0], #1 - subs x2, x2, #1 - b.ne 0b -1: ret -endfunction - // Generate a test pattern for storage in SVE registers // x0: pid (16 bits) // x1: register number (6 bits) @@ -316,33 +180,6 @@ function setup_ffr ret x4 endfunction -// Fill x1 bytes starting at x0 with 0xae (for canary purposes) -// Clobbers x1, x2. -function memfill_ae - mov w2, #0xae - b memfill -endfunction - -// Fill x1 bytes starting at x0 with 0. -// Clobbers x1, x2. -function memclr - mov w2, #0 -endfunction - // fall through to memfill - -// Trivial memory fill: fill x1 bytes starting at address x0 with byte w2 -// Clobbers x1 -function memfill - cmp x1, #0 - b.eq 1f - -0: strb w2, [x0], #1 - subs x1, x1, #1 - b.ne 0b - -1: ret -endfunction - // Trivial memory compare: compare x2 bytes starting at address x0 with // bytes starting at address x1. // Returns only if all bytes match; otherwise, the program is aborted.