diff options
Diffstat (limited to 'tools/testing/selftests/arm64/signal')
9 files changed, 281 insertions, 13 deletions
diff --git a/tools/testing/selftests/arm64/signal/.gitignore b/tools/testing/selftests/arm64/signal/.gitignore index b7fbb65183e8..8ab4c86837fd 100644 --- a/tools/testing/selftests/arm64/signal/.gitignore +++ b/tools/testing/selftests/arm64/signal/.gitignore @@ -4,6 +4,7 @@ fake_sigreturn_* sme_* ssve_* sve_* +tpidr2_siginfo za_* zt_* !*.[ch] diff --git a/tools/testing/selftests/arm64/signal/Makefile b/tools/testing/selftests/arm64/signal/Makefile index be7520a863b0..8f5febaf1a9a 100644 --- a/tools/testing/selftests/arm64/signal/Makefile +++ b/tools/testing/selftests/arm64/signal/Makefile @@ -22,6 +22,10 @@ $(TEST_GEN_PROGS): $(PROGS) # Common test-unit targets to build common-layout test-cases executables # Needs secondary expansion to properly include the testcase c-file in pre-reqs +COMMON_SOURCES := test_signals.c test_signals_utils.c testcases/testcases.c \ + signals.S +COMMON_HEADERS := test_signals.h test_signals_utils.h testcases/testcases.h + .SECONDEXPANSION: -$(PROGS): test_signals.c test_signals_utils.c testcases/testcases.c signals.S $$@.c test_signals.h test_signals_utils.h testcases/testcases.h - $(CC) $(CFLAGS) $^ -o $@ +$(PROGS): $$@.c ${COMMON_SOURCES} ${COMMON_HEADERS} + $(CC) $(CFLAGS) ${@}.c ${COMMON_SOURCES} -o $@ diff --git a/tools/testing/selftests/arm64/signal/test_signals.c b/tools/testing/selftests/arm64/signal/test_signals.c index 416b1ff43199..00051b40d71e 100644 --- a/tools/testing/selftests/arm64/signal/test_signals.c +++ b/tools/testing/selftests/arm64/signal/test_signals.c @@ -12,12 +12,10 @@ #include "test_signals.h" #include "test_signals_utils.h" -struct tdescr *current; +struct tdescr *current = &tde; int main(int argc, char *argv[]) { - current = &tde; - ksft_print_msg("%s :: %s\n", current->name, current->descr); if (test_setup(current) && test_init(current)) { test_run(current); diff --git a/tools/testing/selftests/arm64/signal/test_signals_utils.c b/tools/testing/selftests/arm64/signal/test_signals_utils.c index 07f518f0e58d..40be8443949d 100644 --- a/tools/testing/selftests/arm64/signal/test_signals_utils.c +++ b/tools/testing/selftests/arm64/signal/test_signals_utils.c @@ -193,8 +193,10 @@ static bool handle_signal_copyctx(struct tdescr *td, * in the copy, this was previously validated in * ASSERT_GOOD_CONTEXT(). */ - to_copy = offset + sizeof(struct extra_context) + 16 + - extra->size; + to_copy = __builtin_offsetof(ucontext_t, + uc_mcontext.__reserved); + to_copy += offset + sizeof(struct extra_context) + 16; + to_copy += extra->size; copied_extra = (struct extra_context *)&(td->live_uc->uc_mcontext.__reserved[offset]); } else { copied_extra = NULL; diff --git a/tools/testing/selftests/arm64/signal/testcases/ssve_regs.c b/tools/testing/selftests/arm64/signal/testcases/ssve_regs.c index d0a178945b1a..3d37daafcff5 100644 --- a/tools/testing/selftests/arm64/signal/testcases/ssve_regs.c +++ b/tools/testing/selftests/arm64/signal/testcases/ssve_regs.c @@ -34,6 +34,10 @@ static bool sme_get_vls(struct tdescr *td) vl &= PR_SME_VL_LEN_MASK; + /* Did we find the lowest supported VL? */ + if (vq < sve_vq_from_vl(vl)) + break; + /* Skip missing VLs */ vq = sve_vq_from_vl(vl); @@ -92,6 +96,11 @@ static int do_one_sme_vl(struct tdescr *td, siginfo_t *si, ucontext_t *uc, return 1; } + if (!(ssve->flags & SVE_SIG_FLAG_SM)) { + fprintf(stderr, "SVE_SIG_FLAG_SM not set in SVE record\n"); + return 1; + } + /* The actual size validation is done in get_current_context() */ fprintf(stderr, "Got expected size %u and VL %d\n", head->size, ssve->vl); @@ -116,12 +125,7 @@ static int sme_regs(struct tdescr *td, siginfo_t *si, ucontext_t *uc) struct tdescr tde = { .name = "Streaming SVE registers", .descr = "Check that we get the right Streaming SVE registers reported", - /* - * We shouldn't require FA64 but things like memset() used in the - * helpers might use unsupported instructions so for now disable - * the test unless we've got the full instruction set. - */ - .feats_required = FEAT_SME | FEAT_SME_FA64, + .feats_required = FEAT_SME, .timeout = 3, .init = sme_get_vls, .run = sme_regs, diff --git a/tools/testing/selftests/arm64/signal/testcases/ssve_za_regs.c b/tools/testing/selftests/arm64/signal/testcases/ssve_za_regs.c new file mode 100644 index 000000000000..9dc5f128bbc0 --- /dev/null +++ b/tools/testing/selftests/arm64/signal/testcases/ssve_za_regs.c @@ -0,0 +1,161 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021 ARM Limited + * + * Verify that both the streaming SVE and ZA register context in + * signal frames is set up as expected when enabled simultaneously. + */ + +#include <signal.h> +#include <ucontext.h> +#include <sys/prctl.h> + +#include "test_signals_utils.h" +#include "testcases.h" + +static union { + ucontext_t uc; + char buf[1024 * 128]; +} context; +static unsigned int vls[SVE_VQ_MAX]; +unsigned int nvls = 0; + +static bool sme_get_vls(struct tdescr *td) +{ + int vq, vl; + + /* + * Enumerate up to SVE_VQ_MAX vector lengths + */ + for (vq = SVE_VQ_MAX; vq > 0; --vq) { + vl = prctl(PR_SME_SET_VL, vq * 16); + if (vl == -1) + return false; + + vl &= PR_SME_VL_LEN_MASK; + + /* Did we find the lowest supported VL? */ + if (vq < sve_vq_from_vl(vl)) + break; + + /* Skip missing VLs */ + vq = sve_vq_from_vl(vl); + + vls[nvls++] = vl; + } + + /* We need at least one VL */ + if (nvls < 1) { + fprintf(stderr, "Only %d VL supported\n", nvls); + return false; + } + + return true; +} + +static void setup_regs(void) +{ + /* smstart sm; real data is TODO */ + asm volatile(".inst 0xd503437f" : : : ); + + /* smstart za; real data is TODO */ + asm volatile(".inst 0xd503457f" : : : ); +} + +static char zeros[ZA_SIG_REGS_SIZE(SVE_VQ_MAX)]; + +static int do_one_sme_vl(struct tdescr *td, siginfo_t *si, ucontext_t *uc, + unsigned int vl) +{ + size_t offset; + struct _aarch64_ctx *head = GET_BUF_RESV_HEAD(context); + struct _aarch64_ctx *regs; + struct sve_context *ssve; + struct za_context *za; + int ret; + + fprintf(stderr, "Testing VL %d\n", vl); + + ret = prctl(PR_SME_SET_VL, vl); + if (ret != vl) { + fprintf(stderr, "Failed to set VL, got %d\n", ret); + return 1; + } + + /* + * Get a signal context which should have the SVE and ZA + * frames in it. + */ + setup_regs(); + if (!get_current_context(td, &context.uc, sizeof(context))) + return 1; + + regs = get_header(head, SVE_MAGIC, GET_BUF_RESV_SIZE(context), + &offset); + if (!regs) { + fprintf(stderr, "No SVE context\n"); + return 1; + } + + ssve = (struct sve_context *)regs; + if (ssve->vl != vl) { + fprintf(stderr, "Got SSVE VL %d, expected %d\n", ssve->vl, vl); + return 1; + } + + if (!(ssve->flags & SVE_SIG_FLAG_SM)) { + fprintf(stderr, "SVE_SIG_FLAG_SM not set in SVE record\n"); + return 1; + } + + fprintf(stderr, "Got expected SSVE size %u and VL %d\n", + regs->size, ssve->vl); + + regs = get_header(head, ZA_MAGIC, GET_BUF_RESV_SIZE(context), + &offset); + if (!regs) { + fprintf(stderr, "No ZA context\n"); + return 1; + } + + za = (struct za_context *)regs; + if (za->vl != vl) { + fprintf(stderr, "Got ZA VL %d, expected %d\n", za->vl, vl); + return 1; + } + + fprintf(stderr, "Got expected ZA size %u and VL %d\n", + regs->size, za->vl); + + /* We didn't load any data into ZA so it should be all zeros */ + if (memcmp(zeros, (char *)za + ZA_SIG_REGS_OFFSET, + ZA_SIG_REGS_SIZE(sve_vq_from_vl(za->vl))) != 0) { + fprintf(stderr, "ZA data invalid\n"); + return 1; + } + + return 0; +} + +static int sme_regs(struct tdescr *td, siginfo_t *si, ucontext_t *uc) +{ + int i; + + for (i = 0; i < nvls; i++) { + if (do_one_sme_vl(td, si, uc, vls[i])) + return 1; + } + + td->pass = 1; + + return 0; +} + +struct tdescr tde = { + .name = "Streaming SVE registers", + .descr = "Check that we get the right Streaming SVE registers reported", + .feats_required = FEAT_SME, + .timeout = 3, + .init = sme_get_vls, + .run = sme_regs, +}; diff --git a/tools/testing/selftests/arm64/signal/testcases/testcases.c b/tools/testing/selftests/arm64/signal/testcases/testcases.c index 27d495fa52f8..9f580b55b388 100644 --- a/tools/testing/selftests/arm64/signal/testcases/testcases.c +++ b/tools/testing/selftests/arm64/signal/testcases/testcases.c @@ -184,6 +184,10 @@ bool validate_reserved(ucontext_t *uc, size_t resv_sz, char **err) if (head->size != sizeof(struct esr_context)) *err = "Bad size for esr_context"; break; + case TPIDR2_MAGIC: + if (head->size != sizeof(struct tpidr2_context)) + *err = "Bad size for tpidr2_context"; + break; case SVE_MAGIC: if (flags & SVE_CTX) *err = "Multiple SVE_MAGIC"; diff --git a/tools/testing/selftests/arm64/signal/testcases/tpidr2_siginfo.c b/tools/testing/selftests/arm64/signal/testcases/tpidr2_siginfo.c new file mode 100644 index 000000000000..6a2c82bf7ead --- /dev/null +++ b/tools/testing/selftests/arm64/signal/testcases/tpidr2_siginfo.c @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2022 ARM Limited + * + * Verify that the TPIDR2 register context in signal frames is set up as + * expected. + */ + +#include <signal.h> +#include <ucontext.h> +#include <sys/auxv.h> +#include <sys/prctl.h> +#include <unistd.h> +#include <asm/sigcontext.h> + +#include "test_signals_utils.h" +#include "testcases.h" + +static union { + ucontext_t uc; + char buf[1024 * 128]; +} context; + +#define SYS_TPIDR2 "S3_3_C13_C0_5" + +static uint64_t get_tpidr2(void) +{ + uint64_t val; + + asm volatile ( + "mrs %0, " SYS_TPIDR2 "\n" + : "=r"(val) + : + : "cc"); + + return val; +} + +int tpidr2_present(struct tdescr *td, siginfo_t *si, ucontext_t *uc) +{ + struct _aarch64_ctx *head = GET_BUF_RESV_HEAD(context); + struct tpidr2_context *tpidr2_ctx; + size_t offset; + bool in_sigframe; + bool have_sme; + __u64 orig_tpidr2; + + have_sme = getauxval(AT_HWCAP2) & HWCAP2_SME; + if (have_sme) + orig_tpidr2 = get_tpidr2(); + + if (!get_current_context(td, &context.uc, sizeof(context))) + return 1; + + tpidr2_ctx = (struct tpidr2_context *) + get_header(head, TPIDR2_MAGIC, td->live_sz, &offset); + + in_sigframe = tpidr2_ctx != NULL; + + fprintf(stderr, "TPIDR2 sigframe %s on system %s SME\n", + in_sigframe ? "present" : "absent", + have_sme ? "with" : "without"); + + td->pass = (in_sigframe == have_sme); + + /* + * Check that the value we read back was the one present at + * the time that the signal was triggered. TPIDR2 is owned by + * libc so we can't safely choose the value and it is possible + * that we may need to revisit this in future if something + * starts deciding to set a new TPIDR2 between us reading and + * the signal. + */ + if (have_sme && tpidr2_ctx) { + if (tpidr2_ctx->tpidr2 != orig_tpidr2) { + fprintf(stderr, "TPIDR2 in frame is %llx, was %llx\n", + tpidr2_ctx->tpidr2, orig_tpidr2); + td->pass = false; + } + } + + return 0; +} + +struct tdescr tde = { + .name = "TPIDR2", + .descr = "Validate that TPIDR2 is present as expected", + .timeout = 3, + .run = tpidr2_present, +}; diff --git a/tools/testing/selftests/arm64/signal/testcases/za_regs.c b/tools/testing/selftests/arm64/signal/testcases/za_regs.c index ea45acb115d5..174ad6656696 100644 --- a/tools/testing/selftests/arm64/signal/testcases/za_regs.c +++ b/tools/testing/selftests/arm64/signal/testcases/za_regs.c @@ -34,6 +34,10 @@ static bool sme_get_vls(struct tdescr *td) vl &= PR_SME_VL_LEN_MASK; + /* Did we find the lowest supported VL? */ + if (vq < sve_vq_from_vl(vl)) + break; + /* Skip missing VLs */ vq = sve_vq_from_vl(vl); |