[PATCH v11 37/40] kselftest/arm64: signal: Add SME signal handling tests

Shuah Khan skhan at linuxfoundation.org
Mon Feb 7 17:08:06 PST 2022


On 2/7/22 8:21 AM, Mark Brown wrote:
> Add test cases for the SME signal handing ABI patterned off the SVE tests.
> Due to the small size of the tests and the differences in ABI (especially
> around needing to account for both streaming SVE and ZA) there is some code
> duplication here.
> 
> We currently cover:
>   - Reporting of the vector length.
>   - Lack of support for changing vector length.
>   - Presence and size of register state for streaming SVE and ZA.
> 
> As with the SVE tests we do not yet have any validation of register
> contents.
> 
> Signed-off-by: Mark Brown <broonie at kernel.org>
> ---
>   .../testing/selftests/arm64/signal/.gitignore |   2 +
>   .../selftests/arm64/signal/test_signals.h     |   4 +
>   .../arm64/signal/test_signals_utils.c         |   6 +
>   .../testcases/fake_sigreturn_sme_change_vl.c  |  92 +++++++++++++
>   .../arm64/signal/testcases/sme_trap_no_sm.c   |  38 ++++++
>   .../signal/testcases/sme_trap_non_streaming.c |  45 ++++++
>   .../arm64/signal/testcases/sme_trap_za.c      |  36 +++++
>   .../selftests/arm64/signal/testcases/sme_vl.c |  68 +++++++++
>   .../arm64/signal/testcases/ssve_regs.c        | 129 ++++++++++++++++++
>   9 files changed, 420 insertions(+)
>   create mode 100644 tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_sme_change_vl.c
>   create mode 100644 tools/testing/selftests/arm64/signal/testcases/sme_trap_no_sm.c
>   create mode 100644 tools/testing/selftests/arm64/signal/testcases/sme_trap_non_streaming.c
>   create mode 100644 tools/testing/selftests/arm64/signal/testcases/sme_trap_za.c
>   create mode 100644 tools/testing/selftests/arm64/signal/testcases/sme_vl.c
>   create mode 100644 tools/testing/selftests/arm64/signal/testcases/ssve_regs.c
> 
> diff --git a/tools/testing/selftests/arm64/signal/.gitignore b/tools/testing/selftests/arm64/signal/.gitignore
> index c1742755abb9..4de8eb26d4de 100644
> --- a/tools/testing/selftests/arm64/signal/.gitignore
> +++ b/tools/testing/selftests/arm64/signal/.gitignore
> @@ -1,5 +1,7 @@
>   # SPDX-License-Identifier: GPL-2.0-only
>   mangle_*
>   fake_sigreturn_*
> +sme_*
> +ssve_*
>   sve_*
>   !*.[ch]
> diff --git a/tools/testing/selftests/arm64/signal/test_signals.h b/tools/testing/selftests/arm64/signal/test_signals.h
> index f909b70d9e98..c70fdec7d7c4 100644
> --- a/tools/testing/selftests/arm64/signal/test_signals.h
> +++ b/tools/testing/selftests/arm64/signal/test_signals.h
> @@ -34,11 +34,15 @@
>   enum {
>   	FSSBS_BIT,
>   	FSVE_BIT,
> +	FSME_BIT,
> +	FSME_FA64_BIT,
>   	FMAX_END
>   };
>   
>   #define FEAT_SSBS		(1UL << FSSBS_BIT)
>   #define FEAT_SVE		(1UL << FSVE_BIT)
> +#define FEAT_SME		(1UL << FSME_BIT)
> +#define FEAT_SME_FA64		(1UL << FSME_FA64_BIT)
>   
>   /*
>    * A descriptor used to describe and configure a test case.
> diff --git a/tools/testing/selftests/arm64/signal/test_signals_utils.c b/tools/testing/selftests/arm64/signal/test_signals_utils.c
> index 5743897984b0..b588d10afd5b 100644
> --- a/tools/testing/selftests/arm64/signal/test_signals_utils.c
> +++ b/tools/testing/selftests/arm64/signal/test_signals_utils.c
> @@ -27,6 +27,8 @@ static int sig_copyctx = SIGTRAP;
>   static char const *const feats_names[FMAX_END] = {
>   	" SSBS ",
>   	" SVE ",
> +	" SME ",
> +	" FA64 ",
>   };
>   
>   #define MAX_FEATS_SZ	128
> @@ -268,6 +270,10 @@ int test_init(struct tdescr *td)
>   			td->feats_supported |= FEAT_SSBS;
>   		if (getauxval(AT_HWCAP) & HWCAP_SVE)
>   			td->feats_supported |= FEAT_SVE;
> +		if (getauxval(AT_HWCAP2) & HWCAP2_SME)
> +			td->feats_supported |= FEAT_SME;
> +		if (getauxval(AT_HWCAP2) & HWCAP2_SME_FA64)
> +			td->feats_supported |= FEAT_SME_FA64;
>   		if (feats_ok(td)) {
>   			if (td->feats_required & td->feats_supported)
>   				fprintf(stderr,
> diff --git a/tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_sme_change_vl.c b/tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_sme_change_vl.c
> new file mode 100644
> index 000000000000..7ed762b7202f
> --- /dev/null
> +++ b/tools/testing/selftests/arm64/signal/testcases/fake_sigreturn_sme_change_vl.c
> @@ -0,0 +1,92 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2021 ARM Limited
> + *
> + * Attempt to change the streaming SVE vector length in a signal
> + * handler, this is not supported and is expected to segfault.
> + */
> +
> +#include <signal.h>
> +#include <ucontext.h>
> +#include <sys/prctl.h>
> +
> +#include "test_signals_utils.h"
> +#include "testcases.h"
> +
> +struct fake_sigframe sf;
> +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_SVE_SET_VL, vq * 16);
> +		if (vl == -1)
> +			return false;
> +
> +		vl &= PR_SME_VL_LEN_MASK;
> +
> +		/* Skip missing VLs */
> +		vq = sve_vq_from_vl(vl);
> +
> +		vls[nvls++] = vl;
> +	}
> +
> +	/* We need at least two VLs */
> +	if (nvls < 2) {
> +		fprintf(stderr, "Only %d VL supported\n", nvls);
> +		return false;
> +	}
> +
> +	return true;
> +}
> +
> +static int fake_sigreturn_ssve_change_vl(struct tdescr *td,
> +					 siginfo_t *si, ucontext_t *uc)
> +{
> +	size_t resv_sz, offset;
> +	struct _aarch64_ctx *head = GET_SF_RESV_HEAD(sf);
> +	struct sve_context *sve;
> +
> +	/* Get a signal context with a SME ZA frame in it */
> +	if (!get_current_context(td, &sf.uc))
> +		return 1;
> +
> +	resv_sz = GET_SF_RESV_SIZE(sf);
> +	head = get_header(head, SVE_MAGIC, resv_sz, &offset);
> +	if (!head) {
> +		fprintf(stderr, "No SVE context\n");
> +		return 1;
> +	}
> +
> +	if (head->size != sizeof(struct sve_context)) {
> +		fprintf(stderr, "Register data present, aborting\n");
> +		return 1;
> +	}
> +
> +	sve = (struct sve_context *)head;
> +
> +	/* No changes are supported; init left us at minimum VL so go to max */
> +	fprintf(stderr, "Attempting to change VL from %d to %d\n",
> +		sve->vl, vls[0]);
> +	sve->vl = vls[0];
> +
> +	fake_sigreturn(&sf, sizeof(sf), 0);
> +
> +	return 1;
> +}
> +
> +struct tdescr tde = {
> +	.name = "FAKE_SIGRETURN_SSVE_CHANGE",
> +	.descr = "Attempt to change Streaming SVE VL",
> +	.feats_required = FEAT_SME,
> +	.sig_ok = SIGSEGV,
> +	.timeout = 3,
> +	.init = sme_get_vls,
> +	.run = fake_sigreturn_ssve_change_vl,
> +};
> diff --git a/tools/testing/selftests/arm64/signal/testcases/sme_trap_no_sm.c b/tools/testing/selftests/arm64/signal/testcases/sme_trap_no_sm.c
> new file mode 100644
> index 000000000000..f9d76ae32bba
> --- /dev/null
> +++ b/tools/testing/selftests/arm64/signal/testcases/sme_trap_no_sm.c
> @@ -0,0 +1,38 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2021 ARM Limited
> + *
> + * Verify that using a streaming mode instruction without enabling it
> + * generates a SIGILL.
> + */
> +
> +#include <signal.h>
> +#include <ucontext.h>
> +#include <sys/prctl.h>
> +
> +#include "test_signals_utils.h"
> +#include "testcases.h"
> +
> +int sme_trap_no_sm_trigger(struct tdescr *td)
> +{
> +	/* SMSTART ZA ; ADDHA ZA0.S, P0/M, P0/M, Z0.S */
> +	asm volatile(".inst 0xd503457f ; .inst 0xc0900000");
> +
> +	return 0;
> +}
> +
> +int sme_trap_no_sm_run(struct tdescr *td, siginfo_t *si, ucontext_t *uc)
> +{
> +	return 1;
> +}
> +
> +struct tdescr tde = {
> +	.name = "SME trap without SM",
> +	.descr = "Check that we get a SIGILL if we use streaming mode without enabling it",
> +	.timeout = 3,
> +	.feats_required = FEAT_SME,   /* We need a SMSTART ZA */
> +	.sanity_disabled = true,
> +	.trigger = sme_trap_no_sm_trigger,
> +	.run = sme_trap_no_sm_run,
> +	.sig_ok = SIGILL,
> +};
> diff --git a/tools/testing/selftests/arm64/signal/testcases/sme_trap_non_streaming.c b/tools/testing/selftests/arm64/signal/testcases/sme_trap_non_streaming.c
> new file mode 100644
> index 000000000000..e469ae5348e3
> --- /dev/null
> +++ b/tools/testing/selftests/arm64/signal/testcases/sme_trap_non_streaming.c
> @@ -0,0 +1,45 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2021 ARM Limited
> + *
> + * Verify that using an instruction not supported in streaming mode
> + * traps when in streaming mode.
> + */
> +
> +#include <signal.h>
> +#include <ucontext.h>
> +#include <sys/prctl.h>
> +
> +#include "test_signals_utils.h"
> +#include "testcases.h"
> +
> +int sme_trap_non_streaming_trigger(struct tdescr *td)
> +{
> +	/*
> +	 * The framework will handle SIGILL so we need to exit SM to
> +	 * stop any other code triggering a further SIGILL down the
> +	 * line from using a streaming-illegal instruction.
> +	 */
> +	asm volatile(".inst 0xd503437f; /* SMSTART ZA */ \
> +		      cnt v0.16b, v0.16b; \
> +                      .inst 0xd503447f  /* SMSTOP ZA */");
> +
> +	return 0;
> +}
> +
> +int sme_trap_non_streaming_run(struct tdescr *td, siginfo_t *si, ucontext_t *uc)
> +{
> +	return 1;
> +}
> +
> +struct tdescr tde = {
> +	.name = "SME SM trap unsupported instruction",
> +	.descr = "Check that we get a SIGILL if we use an unsupported instruction in streaming mode",
> +	.feats_required = FEAT_SME,
> +	.feats_incompatible = FEAT_SME_FA64,
> +	.timeout = 3,
> +	.sanity_disabled = true,
> +	.trigger = sme_trap_non_streaming_trigger,
> +	.run = sme_trap_non_streaming_run,
> +	.sig_ok = SIGILL,
> +};
> diff --git a/tools/testing/selftests/arm64/signal/testcases/sme_trap_za.c b/tools/testing/selftests/arm64/signal/testcases/sme_trap_za.c
> new file mode 100644
> index 000000000000..3a7747af4715
> --- /dev/null
> +++ b/tools/testing/selftests/arm64/signal/testcases/sme_trap_za.c
> @@ -0,0 +1,36 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2021 ARM Limited
> + *
> + * Verify that accessing ZA without enabling it generates a SIGILL.
> + */
> +
> +#include <signal.h>
> +#include <ucontext.h>
> +#include <sys/prctl.h>
> +
> +#include "test_signals_utils.h"
> +#include "testcases.h"
> +
> +int sme_trap_za_trigger(struct tdescr *td)
> +{
> +	/* ZERO ZA */
> +	asm volatile(".inst 0xc00800ff");
> +
> +	return 0;
> +}
> +
> +int sme_trap_za_run(struct tdescr *td, siginfo_t *si, ucontext_t *uc)
> +{
> +	return 1;
> +}
> +
> +struct tdescr tde = {
> +	.name = "SME ZA trap",
> +	.descr = "Check that we get a SIGILL if we access ZA without enabling",
> +	.timeout = 3,
> +	.sanity_disabled = true,
> +	.trigger = sme_trap_za_trigger,
> +	.run = sme_trap_za_run,
> +	.sig_ok = SIGILL,
> +};
> diff --git a/tools/testing/selftests/arm64/signal/testcases/sme_vl.c b/tools/testing/selftests/arm64/signal/testcases/sme_vl.c
> new file mode 100644
> index 000000000000..13ff3b35cbaf
> --- /dev/null
> +++ b/tools/testing/selftests/arm64/signal/testcases/sme_vl.c
> @@ -0,0 +1,68 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2021 ARM Limited
> + *
> + * Check that the SME vector length reported in signal contexts is the
> + * expected one.
> + */
> +
> +#include <signal.h>
> +#include <ucontext.h>
> +#include <sys/prctl.h>
> +
> +#include "test_signals_utils.h"
> +#include "testcases.h"
> +
> +struct fake_sigframe sf;
> +unsigned int vl;
> +
> +static bool get_sme_vl(struct tdescr *td)
> +{
> +	int ret = prctl(PR_SME_GET_VL);
> +	if (ret == -1)
> +		return false;
> +
> +	vl = ret;
> +
> +	return true;
> +}
> +
> +static int sme_vl(struct tdescr *td, siginfo_t *si, ucontext_t *uc)
> +{
> +	size_t resv_sz, offset;
> +	struct _aarch64_ctx *head = GET_SF_RESV_HEAD(sf);
> +	struct za_context *za;
> +
> +	/* Get a signal context which should have a ZA frame in it */
> +	if (!get_current_context(td, &sf.uc))
> +		return 1;
> +
> +	resv_sz = GET_SF_RESV_SIZE(sf);
> +	head = get_header(head, ZA_MAGIC, resv_sz, &offset);
> +	if (!head) {
> +		fprintf(stderr, "No ZA context\n");
> +		return 1;
> +	}
> +	za = (struct za_context *)head;
> +
> +	if (za->vl != vl) {
> +		fprintf(stderr, "ZA sigframe VL %u, expected %u\n",
> +			za->vl, vl);
> +		return 1;
> +	} else {
> +		fprintf(stderr, "got expected VL %u\n", vl);
> +	}
> +
> +	td->pass = 1;
> +
> +	return 0;
> +}
> +
> +struct tdescr tde = {
> +	.name = "SME VL",
> +	.descr = "Check that we get the right SME VL reported",
> +	.feats_required = FEAT_SME,
> +	.timeout = 3,
> +	.init = get_sme_vl,
> +	.run = sme_vl,
> +};
> diff --git a/tools/testing/selftests/arm64/signal/testcases/ssve_regs.c b/tools/testing/selftests/arm64/signal/testcases/ssve_regs.c
> new file mode 100644
> index 000000000000..6150862a9cde
> --- /dev/null
> +++ b/tools/testing/selftests/arm64/signal/testcases/ssve_regs.c
> @@ -0,0 +1,129 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2021 ARM Limited
> + *
> + * Verify that the streaming SVE register context in signal frames is
> + * set up as expected.
> + */
> +
> +#include <signal.h>
> +#include <ucontext.h>
> +#include <sys/prctl.h>
> +
> +#include "test_signals_utils.h"
> +#include "testcases.h"
> +
> +struct fake_sigframe sf;
> +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_SVE_SET_VL, vq * 16);
> +		if (vl == -1)
> +			return false;
> +
> +		vl &= PR_SME_VL_LEN_MASK;
> +
> +		/* 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_ssve_regs(void)
> +{
> +	/* SMSTART SM */
> +	asm volatile(".inst 0x7f4303d5");
> +
> +	/* RDVL x16, #1 so we should have SVE regs; real data is TODO */
> +	asm volatile(".inst 0x04bf5030" : : : "x16" );
> +}
> +
> +static int do_one_sme_vl(struct tdescr *td, siginfo_t *si, ucontext_t *uc,
> +			 unsigned int vl)
> +{
> +	size_t resv_sz, offset;
> +	struct _aarch64_ctx *head = GET_SF_RESV_HEAD(sf);
> +	struct sve_context *ssve;
> +
> +	fprintf(stderr, "Testing VL %d\n", vl);
> +
> +	if (prctl(PR_SME_SET_VL, vl) == -1) {
> +		fprintf(stderr, "Failed to set VL\n");
> +		return 1;
> +	}
> +
> +	/*
> +	 * Get a signal context which should have a SVE frame and registers
> +	 * in it.
> +	 */
> +	setup_ssve_regs();
> +	if (!get_current_context(td, &sf.uc))
> +		return 1;
> +
> +	resv_sz = GET_SF_RESV_SIZE(sf);
> +	head = get_header(head, SVE_MAGIC, resv_sz, &offset);
> +	if (!head) {
> +		fprintf(stderr, "No SVE context\n");
> +		return 1;
> +	}
> +
> +	ssve = (struct sve_context *)head;
> +	if (ssve->vl != vl) {
> +		fprintf(stderr, "Got VL %d, expected %d\n", ssve->vl, vl);
> +		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);
> +
> +	return 0;
> +}
> +
> +static int sme_regs(struct tdescr *td, siginfo_t *si, ucontext_t *uc)
> +{
> +	int i;
> +
> +	for (i = 0; i < nvls; i++) {
> +		/*
> +		 * TODO: the signal test helpers can't currently cope
> +		 * with signal frames bigger than struct sigcontext,
> +		 * skip VLs that will trigger that.
> +		 */
> +		if (vls[i] > 64)
> +			continue;

Count the skipped VLs and print counts - helps see how many are
skipped.

> +
> +		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,
> +};
> 

With these explained or fixed:

Reviewed-by: Shuah Khan <skhan at linuxfoundation.org>

thanks,
-- Shuah




More information about the linux-arm-kernel mailing list