Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1 @@
- Validate that register contents are saved and restored as expected.
@@ -0,0 +1,52 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 ARM Limited
*
* Place a fake sigframe on the stack including a BAD Unknown magic
* record: on sigreturn Kernel must spot this attempt and the test
* case is expected to be terminated via SEGV.
*/
#include <signal.h>
#include <ucontext.h>
#include "test_signals_utils.h"
#include "testcases.h"
struct fake_sigframe sf;
static int fake_sigreturn_bad_magic_run(struct tdescr *td,
siginfo_t *si, ucontext_t *uc)
{
struct _aarch64_ctx *shead = GET_SF_RESV_HEAD(sf), *head;
/* just to fill the ucontext_t with something real */
if (!get_current_context(td, &sf.uc, sizeof(sf.uc)))
return 1;
/* need at least 2*HDR_SZ space: KSFT_BAD_MAGIC + terminator. */
head = get_starting_head(shead, HDR_SZ * 2, GET_SF_RESV_SIZE(sf), NULL);
if (!head)
return 0;
/*
* use a well known NON existent bad magic...something
* we should pretty sure won't be ever defined in Kernel
*/
head->magic = KSFT_BAD_MAGIC;
head->size = HDR_SZ;
write_terminator_record(GET_RESV_NEXT_HEAD(head));
ASSERT_BAD_CONTEXT(&sf.uc);
fake_sigreturn(&sf, sizeof(sf), 0);
return 1;
}
struct tdescr tde = {
.name = "FAKE_SIGRETURN_BAD_MAGIC",
.descr = "Trigger a sigreturn with a sigframe with a bad magic",
.sig_ok = SIGSEGV,
.timeout = 3,
.run = fake_sigreturn_bad_magic_run,
};
@@ -0,0 +1,77 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 ARM Limited
*
* Place a fake sigframe on the stack including a bad record overflowing
* the __reserved space: on sigreturn Kernel must spot this attempt and
* the test case is expected to be terminated via SEGV.
*/
#include <signal.h>
#include <ucontext.h>
#include "test_signals_utils.h"
#include "testcases.h"
struct fake_sigframe sf;
#define MIN_SZ_ALIGN 16
static int fake_sigreturn_bad_size_run(struct tdescr *td,
siginfo_t *si, ucontext_t *uc)
{
size_t resv_sz, need_sz, offset;
struct _aarch64_ctx *shead = GET_SF_RESV_HEAD(sf), *head;
/* just to fill the ucontext_t with something real */
if (!get_current_context(td, &sf.uc, sizeof(sf.uc)))
return 1;
resv_sz = GET_SF_RESV_SIZE(sf);
/* at least HDR_SZ + bad sized esr_context needed */
need_sz = sizeof(struct esr_context) + HDR_SZ;
head = get_starting_head(shead, need_sz, resv_sz, &offset);
if (!head)
return 0;
/*
* Use an esr_context to build a fake header with a
* size greater then the free __reserved area minus HDR_SZ;
* using ESR_MAGIC here since it is not checked for size nor
* is limited to one instance.
*
* At first inject an additional normal esr_context
*/
head->magic = ESR_MAGIC;
head->size = sizeof(struct esr_context);
/* and terminate properly */
write_terminator_record(GET_RESV_NEXT_HEAD(head));
ASSERT_GOOD_CONTEXT(&sf.uc);
/*
* now mess with fake esr_context size: leaving less space than
* needed while keeping size value 16-aligned
*
* It must trigger a SEGV from Kernel on:
*
* resv_sz - offset < sizeof(*head)
*/
/* at first set the maximum good 16-aligned size */
head->size = (resv_sz - offset - need_sz + MIN_SZ_ALIGN) & ~0xfUL;
/* plus a bit more of 16-aligned sized stuff */
head->size += MIN_SZ_ALIGN;
/* and terminate properly */
write_terminator_record(GET_RESV_NEXT_HEAD(head));
ASSERT_BAD_CONTEXT(&sf.uc);
fake_sigreturn(&sf, sizeof(sf), 0);
return 1;
}
struct tdescr tde = {
.name = "FAKE_SIGRETURN_BAD_SIZE",
.descr = "Triggers a sigreturn with a overrun __reserved area",
.sig_ok = SIGSEGV,
.timeout = 3,
.run = fake_sigreturn_bad_size_run,
};
@@ -0,0 +1,46 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 ARM Limited
*
* Place a fake sigframe on the stack including a badly sized terminator
* record: on sigreturn Kernel must spot this attempt and the test case
* is expected to be terminated via SEGV.
*/
#include <signal.h>
#include <ucontext.h>
#include "test_signals_utils.h"
#include "testcases.h"
struct fake_sigframe sf;
static int fake_sigreturn_bad_size_for_magic0_run(struct tdescr *td,
siginfo_t *si, ucontext_t *uc)
{
struct _aarch64_ctx *shead = GET_SF_RESV_HEAD(sf), *head;
/* just to fill the ucontext_t with something real */
if (!get_current_context(td, &sf.uc, sizeof(sf.uc)))
return 1;
/* at least HDR_SZ for the badly sized terminator. */
head = get_starting_head(shead, HDR_SZ, GET_SF_RESV_SIZE(sf), NULL);
if (!head)
return 0;
head->magic = 0;
head->size = HDR_SZ;
ASSERT_BAD_CONTEXT(&sf.uc);
fake_sigreturn(&sf, sizeof(sf), 0);
return 1;
}
struct tdescr tde = {
.name = "FAKE_SIGRETURN_BAD_SIZE_FOR_TERMINATOR",
.descr = "Trigger a sigreturn using non-zero size terminator",
.sig_ok = SIGSEGV,
.timeout = 3,
.run = fake_sigreturn_bad_size_for_magic0_run,
};
@@ -0,0 +1,50 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 ARM Limited
*
* Place a fake sigframe on the stack including an additional FPSIMD
* record: on sigreturn Kernel must spot this attempt and the test
* case is expected to be terminated via SEGV.
*/
#include <signal.h>
#include <ucontext.h>
#include "test_signals_utils.h"
#include "testcases.h"
struct fake_sigframe sf;
static int fake_sigreturn_duplicated_fpsimd_run(struct tdescr *td,
siginfo_t *si, ucontext_t *uc)
{
struct _aarch64_ctx *shead = GET_SF_RESV_HEAD(sf), *head;
/* just to fill the ucontext_t with something real */
if (!get_current_context(td, &sf.uc, sizeof(sf.uc)))
return 1;
head = get_starting_head(shead, sizeof(struct fpsimd_context) + HDR_SZ,
GET_SF_RESV_SIZE(sf), NULL);
if (!head)
return 0;
/* Add a spurious fpsimd_context */
head->magic = FPSIMD_MAGIC;
head->size = sizeof(struct fpsimd_context);
/* and terminate */
write_terminator_record(GET_RESV_NEXT_HEAD(head));
ASSERT_BAD_CONTEXT(&sf.uc);
fake_sigreturn(&sf, sizeof(sf), 0);
return 1;
}
struct tdescr tde = {
.name = "FAKE_SIGRETURN_DUPLICATED_FPSIMD",
.descr = "Triggers a sigreturn including two fpsimd_context",
.sig_ok = SIGSEGV,
.timeout = 3,
.run = fake_sigreturn_duplicated_fpsimd_run,
};
@@ -0,0 +1,37 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 ARM Limited
*
* Place a fake sigframe on the stack at a misaligned SP: on sigreturn
* Kernel must spot this attempt and the test case is expected to be
* terminated via SEGV.
*/
#include <signal.h>
#include <ucontext.h>
#include "test_signals_utils.h"
#include "testcases.h"
struct fake_sigframe sf;
static int fake_sigreturn_misaligned_run(struct tdescr *td,
siginfo_t *si, ucontext_t *uc)
{
/* just to fill the ucontext_t with something real */
if (!get_current_context(td, &sf.uc, sizeof(sf.uc)))
return 1;
/* Forcing sigframe on misaligned SP (16 + 3) */
fake_sigreturn(&sf, sizeof(sf), 3);
return 1;
}
struct tdescr tde = {
.name = "FAKE_SIGRETURN_MISALIGNED_SP",
.descr = "Triggers a sigreturn with a misaligned sigframe",
.sig_ok = SIGSEGV,
.timeout = 3,
.run = fake_sigreturn_misaligned_run,
};
@@ -0,0 +1,50 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 ARM Limited
*
* Place a fake sigframe on the stack missing the mandatory FPSIMD
* record: on sigreturn Kernel must spot this attempt and the test
* case is expected to be terminated via SEGV.
*/
#include <stdio.h>
#include <signal.h>
#include <ucontext.h>
#include "test_signals_utils.h"
#include "testcases.h"
struct fake_sigframe sf;
static int fake_sigreturn_missing_fpsimd_run(struct tdescr *td,
siginfo_t *si, ucontext_t *uc)
{
size_t resv_sz, offset;
struct _aarch64_ctx *head = GET_SF_RESV_HEAD(sf);
/* just to fill the ucontext_t with something real */
if (!get_current_context(td, &sf.uc, sizeof(sf.uc)))
return 1;
resv_sz = GET_SF_RESV_SIZE(sf);
head = get_header(head, FPSIMD_MAGIC, resv_sz, &offset);
if (head && resv_sz - offset >= HDR_SZ) {
fprintf(stderr, "Mangling template header. Spare space:%zd\n",
resv_sz - offset);
/* Just overwrite fpsmid_context */
write_terminator_record(head);
ASSERT_BAD_CONTEXT(&sf.uc);
fake_sigreturn(&sf, sizeof(sf), 0);
}
return 1;
}
struct tdescr tde = {
.name = "FAKE_SIGRETURN_MISSING_FPSIMD",
.descr = "Triggers a sigreturn with a missing fpsimd_context",
.sig_ok = SIGSEGV,
.timeout = 3,
.run = fake_sigreturn_missing_fpsimd_run,
};
@@ -0,0 +1,76 @@
// 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 <kselftest.h>
#include <signal.h>
#include <ucontext.h>
#include <sys/prctl.h>
#include "test_signals_utils.h"
#include "sve_helpers.h"
#include "testcases.h"
struct fake_sigframe sf;
static bool sme_get_vls(struct tdescr *td)
{
int res = sve_fill_vls(VLS_USE_SME, 2);
if (!res)
return true;
if (res == KSFT_SKIP)
td->result = KSFT_SKIP;
return false;
}
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 za_context *za;
/* Get a signal context with a SME ZA frame in it */
if (!get_current_context(td, &sf.uc, sizeof(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;
}
if (head->size != sizeof(struct za_context)) {
fprintf(stderr, "Register data present, aborting\n");
return 1;
}
za = (struct za_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",
za->vl, vls[0]);
za->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,
};
@@ -0,0 +1,76 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2021 ARM Limited
*
* Attempt to change the SVE vector length in a signal hander, this is not
* supported and is expected to segfault.
*/
#include <kselftest.h>
#include <signal.h>
#include <ucontext.h>
#include <sys/prctl.h>
#include "test_signals_utils.h"
#include "sve_helpers.h"
#include "testcases.h"
struct fake_sigframe sf;
static bool sve_get_vls(struct tdescr *td)
{
int res = sve_fill_vls(VLS_USE_SVE, 2);
if (!res)
return true;
if (res == KSFT_SKIP)
td->result = KSFT_SKIP;
return false;
}
static int fake_sigreturn_sve_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 SVE frame in it */
if (!get_current_context(td, &sf.uc, sizeof(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, "SVE register state active, skipping\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_SVE_CHANGE",
.descr = "Attempt to change SVE VL",
.feats_required = FEAT_SVE,
.sig_ok = SIGSEGV,
.timeout = 3,
.init = sve_get_vls,
.run = fake_sigreturn_sve_change_vl,
};
@@ -0,0 +1,82 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2023 ARM Limited
*
* Verify that the FPMR 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_FPMR "S3_3_C4_C4_2"
static uint64_t get_fpmr(void)
{
uint64_t val;
asm volatile (
"mrs %0, " SYS_FPMR "\n"
: "=r"(val)
:
: "cc");
return val;
}
int fpmr_present(struct tdescr *td, siginfo_t *si, ucontext_t *uc)
{
struct _aarch64_ctx *head = GET_BUF_RESV_HEAD(context);
struct fpmr_context *fpmr_ctx;
size_t offset;
bool in_sigframe;
bool have_fpmr;
__u64 orig_fpmr;
have_fpmr = getauxval(AT_HWCAP2) & HWCAP2_FPMR;
if (have_fpmr)
orig_fpmr = get_fpmr();
if (!get_current_context(td, &context.uc, sizeof(context)))
return 1;
fpmr_ctx = (struct fpmr_context *)
get_header(head, FPMR_MAGIC, td->live_sz, &offset);
in_sigframe = fpmr_ctx != NULL;
fprintf(stderr, "FPMR sigframe %s on system %s FPMR\n",
in_sigframe ? "present" : "absent",
have_fpmr ? "with" : "without");
td->pass = (in_sigframe == have_fpmr);
if (have_fpmr && fpmr_ctx) {
if (fpmr_ctx->fpmr != orig_fpmr) {
fprintf(stderr, "FPMR in frame is %llx, was %llx\n",
fpmr_ctx->fpmr, orig_fpmr);
td->pass = false;
}
}
return 0;
}
struct tdescr tde = {
.name = "FPMR",
.descr = "Validate that FPMR is present as expected",
.timeout = 3,
.run = fpmr_present,
};
@@ -0,0 +1,62 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2023 ARM Limited
*/
#include <errno.h>
#include <signal.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/prctl.h>
#include "test_signals_utils.h"
#include "testcases.h"
/*
* We should get this from asm/siginfo.h but the testsuite is being
* clever with redefining siginfo_t.
*/
#ifndef SEGV_CPERR
#define SEGV_CPERR 10
#endif
static inline void gcsss1(uint64_t Xt)
{
asm volatile (
"sys #3, C7, C7, #2, %0\n"
:
: "rZ" (Xt)
: "memory");
}
static int gcs_op_fault_trigger(struct tdescr *td)
{
/*
* The slot below our current GCS should be in a valid GCS but
* must not have a valid cap in it.
*/
gcsss1(get_gcspr_el0() - 8);
return 0;
}
static int gcs_op_fault_signal(struct tdescr *td, siginfo_t *si,
ucontext_t *uc)
{
ASSERT_GOOD_CONTEXT(uc);
return 1;
}
struct tdescr tde = {
.name = "Invalid GCS operation",
.descr = "An invalid GCS operation generates the expected signal",
.feats_required = FEAT_GCS,
.timeout = 3,
.sig_ok = SIGSEGV,
.sig_ok_code = SEGV_CPERR,
.sanity_disabled = true,
.trigger = gcs_op_fault_trigger,
.run = gcs_op_fault_signal,
};
@@ -0,0 +1,88 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2023 ARM Limited
*/
#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 * 64];
} context;
static int gcs_regs(struct tdescr *td, siginfo_t *si, ucontext_t *uc)
{
size_t offset;
struct _aarch64_ctx *head = GET_BUF_RESV_HEAD(context);
struct gcs_context *gcs;
unsigned long expected, gcspr;
uint64_t *u64_val;
int ret;
ret = prctl(PR_GET_SHADOW_STACK_STATUS, &expected, 0, 0, 0);
if (ret != 0) {
fprintf(stderr, "Unable to query GCS status\n");
return 1;
}
/* We expect a cap to be added to the GCS in the signal frame */
gcspr = get_gcspr_el0();
gcspr -= 8;
fprintf(stderr, "Expecting GCSPR_EL0 %lx\n", gcspr);
if (!get_current_context(td, &context.uc, sizeof(context))) {
fprintf(stderr, "Failed getting context\n");
return 1;
}
/* Ensure that the signal restore token was consumed */
u64_val = (uint64_t *)get_gcspr_el0() + 1;
if (*u64_val) {
fprintf(stderr, "GCS value at %p is %lx not 0\n",
u64_val, *u64_val);
return 1;
}
fprintf(stderr, "Got context\n");
head = get_header(head, GCS_MAGIC, GET_BUF_RESV_SIZE(context),
&offset);
if (!head) {
fprintf(stderr, "No GCS context\n");
return 1;
}
gcs = (struct gcs_context *)head;
/* Basic size validation is done in get_current_context() */
if (gcs->features_enabled != expected) {
fprintf(stderr, "Features enabled %llx but expected %lx\n",
gcs->features_enabled, expected);
return 1;
}
if (gcs->gcspr != gcspr) {
fprintf(stderr, "Got GCSPR %llx but expected %lx\n",
gcs->gcspr, gcspr);
return 1;
}
fprintf(stderr, "GCS context validated\n");
td->pass = 1;
return 0;
}
struct tdescr tde = {
.name = "GCS basics",
.descr = "Validate a GCS signal context",
.feats_required = FEAT_GCS,
.timeout = 3,
.run = gcs_regs,
};
@@ -0,0 +1,76 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2026 ARM Limited
*/
#include <errno.h>
#include <signal.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/prctl.h>
#include "test_signals_utils.h"
#include "testcases.h"
static uint64_t *gcs_page;
static bool post_mprotect;
#ifndef __NR_map_shadow_stack
#define __NR_map_shadow_stack 453
#endif
static bool alloc_gcs(struct tdescr *td)
{
long page_size = sysconf(_SC_PAGE_SIZE);
gcs_page = (void *)syscall(__NR_map_shadow_stack, 0,
page_size, 0);
if (gcs_page == MAP_FAILED) {
fprintf(stderr, "Failed to map %ld byte GCS: %d\n",
page_size, errno);
return false;
}
return true;
}
static int gcs_prot_none_fault_trigger(struct tdescr *td)
{
/* Verify that the page is readable (ie, not completely unmapped) */
fprintf(stderr, "Read value 0x%lx\n", gcs_page[0]);
if (mprotect(gcs_page, sysconf(_SC_PAGE_SIZE), PROT_NONE) != 0) {
fprintf(stderr, "mprotect(PROT_NONE) failed: %d\n", errno);
return 0;
}
post_mprotect = true;
/* This should trigger a fault if PROT_NONE is honoured for the GCS page */
fprintf(stderr, "Read value after mprotect(PROT_NONE) 0x%lx\n", gcs_page[0]);
return 0;
}
static int gcs_prot_none_fault_signal(struct tdescr *td, siginfo_t *si,
ucontext_t *uc)
{
ASSERT_GOOD_CONTEXT(uc);
/* A fault before mprotect(PROT_NONE) is unexpected. */
if (!post_mprotect)
return 0;
return 1;
}
struct tdescr tde = {
.name = "GCS PROT_NONE fault",
.descr = "Read from GCS after mprotect(PROT_NONE) segfaults",
.feats_required = FEAT_GCS,
.timeout = 3,
.sig_ok = SIGSEGV,
.sanity_disabled = true,
.init = alloc_gcs,
.trigger = gcs_prot_none_fault_trigger,
.run = gcs_prot_none_fault_signal,
};
@@ -0,0 +1,67 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2023 ARM Limited
*/
#include <errno.h>
#include <signal.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/prctl.h>
#include "test_signals_utils.h"
#include "testcases.h"
static uint64_t *gcs_page;
#ifndef __NR_map_shadow_stack
#define __NR_map_shadow_stack 453
#endif
static bool alloc_gcs(struct tdescr *td)
{
long page_size = sysconf(_SC_PAGE_SIZE);
gcs_page = (void *)syscall(__NR_map_shadow_stack, 0,
page_size, 0);
if (gcs_page == MAP_FAILED) {
fprintf(stderr, "Failed to map %ld byte GCS: %d\n",
page_size, errno);
return false;
}
return true;
}
static int gcs_write_fault_trigger(struct tdescr *td)
{
/* Verify that the page is readable (ie, not completely unmapped) */
fprintf(stderr, "Read value 0x%lx\n", gcs_page[0]);
/* A regular write should trigger a fault */
gcs_page[0] = EINVAL;
return 0;
}
static int gcs_write_fault_signal(struct tdescr *td, siginfo_t *si,
ucontext_t *uc)
{
ASSERT_GOOD_CONTEXT(uc);
return 1;
}
struct tdescr tde = {
.name = "GCS write fault",
.descr = "Normal writes to a GCS segfault",
.feats_required = FEAT_GCS,
.timeout = 3,
.sig_ok = SIGSEGV,
.sanity_disabled = true,
.init = alloc_gcs,
.trigger = gcs_write_fault_trigger,
.run = gcs_write_fault_signal,
};
@@ -0,0 +1,31 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 ARM Limited
*
* Try to mangle the ucontext from inside a signal handler, toggling
* the execution state bit: this attempt must be spotted by Kernel and
* the test case is expected to be terminated via SEGV.
*/
#include "test_signals_utils.h"
#include "testcases.h"
static int mangle_invalid_pstate_run(struct tdescr *td, siginfo_t *si,
ucontext_t *uc)
{
ASSERT_GOOD_CONTEXT(uc);
/* This config should trigger a SIGSEGV by Kernel */
uc->uc_mcontext.pstate ^= PSR_MODE32_BIT;
return 1;
}
struct tdescr tde = {
.sanity_disabled = true,
.name = "MANGLE_PSTATE_INVALID_STATE_TOGGLE",
.descr = "Mangling uc_mcontext with INVALID STATE_TOGGLE",
.sig_trig = SIGUSR1,
.sig_ok = SIGSEGV,
.run = mangle_invalid_pstate_run,
};
@@ -0,0 +1,35 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 ARM Limited
*
* Try to mangle the ucontext from inside a signal handler, mangling the
* DAIF bits in an illegal manner: this attempt must be spotted by Kernel
* and the test case is expected to be terminated via SEGV.
*
*/
#include "test_signals_utils.h"
#include "testcases.h"
static int mangle_invalid_pstate_run(struct tdescr *td, siginfo_t *si,
ucontext_t *uc)
{
ASSERT_GOOD_CONTEXT(uc);
/*
* This config should trigger a SIGSEGV by Kernel when it checks
* the sigframe consistency in valid_user_regs() routine.
*/
uc->uc_mcontext.pstate |= PSR_D_BIT | PSR_A_BIT | PSR_I_BIT | PSR_F_BIT;
return 1;
}
struct tdescr tde = {
.sanity_disabled = true,
.name = "MANGLE_PSTATE_INVALID_DAIF_BITS",
.descr = "Mangling uc_mcontext with INVALID DAIF_BITS",
.sig_trig = SIGUSR1,
.sig_ok = SIGSEGV,
.run = mangle_invalid_pstate_run,
};
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 ARM Limited
*
* Try to mangle the ucontext from inside a signal handler, toggling
* the mode bit to escalate exception level: this attempt must be spotted
* by Kernel and the test case is expected to be termninated via SEGV.
*/
#include "test_signals_utils.h"
#include "testcases.h"
#include "mangle_pstate_invalid_mode_template.h"
DEFINE_TESTCASE_MANGLE_PSTATE_INVALID_MODE(1h);
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 ARM Limited
*
* Try to mangle the ucontext from inside a signal handler, toggling
* the mode bit to escalate exception level: this attempt must be spotted
* by Kernel and the test case is expected to be termninated via SEGV.
*/
#include "test_signals_utils.h"
#include "testcases.h"
#include "mangle_pstate_invalid_mode_template.h"
DEFINE_TESTCASE_MANGLE_PSTATE_INVALID_MODE(1t);
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 ARM Limited
*
* Try to mangle the ucontext from inside a signal handler, toggling
* the mode bit to escalate exception level: this attempt must be spotted
* by Kernel and the test case is expected to be termninated via SEGV.
*/
#include "test_signals_utils.h"
#include "testcases.h"
#include "mangle_pstate_invalid_mode_template.h"
DEFINE_TESTCASE_MANGLE_PSTATE_INVALID_MODE(2h);
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 ARM Limited
*
* Try to mangle the ucontext from inside a signal handler, toggling
* the mode bit to escalate exception level: this attempt must be spotted
* by Kernel and the test case is expected to be termninated via SEGV.
*/
#include "test_signals_utils.h"
#include "testcases.h"
#include "mangle_pstate_invalid_mode_template.h"
DEFINE_TESTCASE_MANGLE_PSTATE_INVALID_MODE(2t);
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 ARM Limited
*
* Try to mangle the ucontext from inside a signal handler, toggling
* the mode bit to escalate exception level: this attempt must be spotted
* by Kernel and the test case is expected to be termninated via SEGV.
*/
#include "test_signals_utils.h"
#include "testcases.h"
#include "mangle_pstate_invalid_mode_template.h"
DEFINE_TESTCASE_MANGLE_PSTATE_INVALID_MODE(3h);
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 ARM Limited
*
* Try to mangle the ucontext from inside a signal handler, toggling
* the mode bit to escalate exception level: this attempt must be spotted
* by Kernel and the test case is expected to be termninated via SEGV.
*/
#include "test_signals_utils.h"
#include "testcases.h"
#include "mangle_pstate_invalid_mode_template.h"
DEFINE_TESTCASE_MANGLE_PSTATE_INVALID_MODE(3t);
@@ -0,0 +1,28 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2019 ARM Limited
*
* Utility macro to ease definition of testcases toggling mode EL
*/
#define DEFINE_TESTCASE_MANGLE_PSTATE_INVALID_MODE(_mode) \
\
static int mangle_invalid_pstate_run(struct tdescr *td, siginfo_t *si, \
ucontext_t *uc) \
{ \
ASSERT_GOOD_CONTEXT(uc); \
\
uc->uc_mcontext.pstate &= ~PSR_MODE_MASK; \
uc->uc_mcontext.pstate |= PSR_MODE_EL ## _mode; \
\
return 1; \
} \
\
struct tdescr tde = { \
.sanity_disabled = true, \
.name = "MANGLE_PSTATE_INVALID_MODE_EL"#_mode, \
.descr = "Mangling uc_mcontext INVALID MODE EL"#_mode, \
.sig_trig = SIGUSR1, \
.sig_ok = SIGSEGV, \
.run = mangle_invalid_pstate_run, \
}
@@ -0,0 +1,73 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2026 Arm Ltd
*
* Verify that the POR_EL0 register is left untouched on sigreturn if the
* POE frame record is missing.
*/
#include <asm/sigcontext.h>
#include "test_signals_utils.h"
#include "testcases.h"
#define POR_EL0_INIT 0x07ul
#define POR_EL0_CUSTOM 0x77ul
static bool failed_check;
static bool modify_por_el0(struct tdescr *td)
{
set_por_el0(POR_EL0_CUSTOM);
return true;
}
static int signal_remove_poe_context(struct tdescr *td, siginfo_t *si,
ucontext_t *uc)
{
struct _aarch64_ctx *ctx = GET_UC_RESV_HEAD(uc);
size_t resv_size = GET_UCP_RESV_SIZE(uc);
struct _aarch64_ctx *poe_ctx_head;
poe_ctx_head = get_header(ctx, POE_MAGIC, resv_size, NULL);
if (!poe_ctx_head) {
fprintf(stderr, "Missing poe_context record\n");
failed_check = true;
return 0;
}
/*
* Actually removing the record would require moving down the next
* records. An easier option is to turn it into an ESR record, which is
* ignored by sigreturn().
*/
poe_ctx_head->magic = ESR_MAGIC;
return 0;
}
static void check_por_el0_preserved(struct tdescr *td)
{
uint64_t por_el0 = get_por_el0();
if (por_el0 == POR_EL0_INIT) {
fprintf(stderr, "POR_EL0 preserved\n");
} else {
fprintf(stderr, "POR_EL0 unexpectedly set to %lx\n", por_el0);
failed_check = true;
}
td->pass = !failed_check;
}
struct tdescr tde = {
.name = "POR_EL0 missing poe_context",
.descr = "Remove poe_context record and check POR_EL0 is preserved",
.feats_required = FEAT_POE,
.timeout = 3,
.sig_trig = SIGUSR1,
.init = modify_por_el0,
.run = signal_remove_poe_context,
.check_result = check_por_el0_preserved,
};
@@ -0,0 +1,64 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2026 Arm Ltd
*
* Verify that the POR_EL0 register is saved and restored as expected on signal
* entry/return.
*/
#include <asm/sigcontext.h>
#include "test_signals_utils.h"
#include "testcases.h"
#define POR_EL0_INIT 0x07ul
#define POR_EL0_CUSTOM 0x77ul
static bool failed_check;
static bool modify_por_el0(struct tdescr *td)
{
set_por_el0(POR_EL0_CUSTOM);
return true;
}
static int signal_check_por_el0_reset(struct tdescr *td, siginfo_t *si,
ucontext_t *uc)
{
uint64_t signal_por_el0 = get_por_el0();
if (signal_por_el0 != POR_EL0_INIT) {
fprintf(stderr, "POR_EL0 is %lx in signal handler (expected %lx)\n",
signal_por_el0, POR_EL0_INIT);
failed_check = true;
}
return 0;
}
static void check_por_el0_restored(struct tdescr *td)
{
uint64_t por_el0 = get_por_el0();
if (por_el0 == POR_EL0_CUSTOM) {
fprintf(stderr, "POR_EL0 restored\n");
} else {
fprintf(stderr, "POR_EL0 was %lx but is now %lx\n",
POR_EL0_CUSTOM, por_el0);
failed_check = true;
}
td->pass = !failed_check;
}
struct tdescr tde = {
.name = "POR_EL0 restore",
.descr = "Validate that POR_EL0 is saved/restored on signal entry/return",
.feats_required = FEAT_POE,
.timeout = 3,
.sig_trig = SIGUSR1,
.init = modify_por_el0,
.run = signal_check_por_el0_reset,
.check_result = check_por_el0_restored,
};
@@ -0,0 +1,71 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2023 Arm Limited
*
* Verify that the POR_EL0 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;
int poe_present(struct tdescr *td, siginfo_t *si, ucontext_t *uc)
{
struct _aarch64_ctx *head = GET_BUF_RESV_HEAD(context);
struct poe_context *poe_ctx;
size_t offset;
bool in_sigframe;
bool have_poe;
__u64 orig_poe;
have_poe = getauxval(AT_HWCAP2) & HWCAP2_POE;
if (have_poe)
orig_poe = get_por_el0();
if (!get_current_context(td, &context.uc, sizeof(context)))
return 1;
poe_ctx = (struct poe_context *)
get_header(head, POE_MAGIC, td->live_sz, &offset);
in_sigframe = poe_ctx != NULL;
fprintf(stderr, "POR_EL0 sigframe %s on system %s POE\n",
in_sigframe ? "present" : "absent",
have_poe ? "with" : "without");
td->pass = (in_sigframe == have_poe);
/*
* Check that the value we read back was the one present at
* the time that the signal was triggered.
*/
if (have_poe && poe_ctx) {
if (poe_ctx->por_el0 != orig_poe) {
fprintf(stderr, "POR_EL0 in frame is %llx, was %llx\n",
poe_ctx->por_el0, orig_poe);
td->pass = false;
}
}
return 0;
}
struct tdescr tde = {
.name = "POR_EL0",
.descr = "Validate that POR_EL0 is present as expected",
.timeout = 3,
.run = poe_present,
};
@@ -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,
};
@@ -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,
};
@@ -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,
};
@@ -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, sizeof(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,
};
@@ -0,0 +1,117 @@
// 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 <kselftest.h>
#include <signal.h>
#include <ucontext.h>
#include <sys/prctl.h>
#include "test_signals_utils.h"
#include "sve_helpers.h"
#include "testcases.h"
static union {
ucontext_t uc;
char buf[1024 * 64];
} context;
static bool sme_get_vls(struct tdescr *td)
{
int res = sve_fill_vls(VLS_USE_SME, 1);
if (!res)
return true;
if (res == KSFT_SKIP)
td->result = KSFT_SKIP;
return false;
}
static void setup_ssve_regs(void)
{
/* smstart sm; real data is TODO */
asm volatile(".inst 0xd503437f" : : : );
}
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 sve_context *ssve;
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 a SVE frame and registers
* in it.
*/
setup_ssve_regs();
if (!get_current_context(td, &context.uc, sizeof(context)))
return 1;
head = get_header(head, SVE_MAGIC, GET_BUF_RESV_SIZE(context),
&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;
}
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);
if (get_svcr() != 0) {
fprintf(stderr, "Unexpected SVCR %lx\n", get_svcr());
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,
};
@@ -0,0 +1,141 @@
// 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 <kselftest.h>
#include <signal.h>
#include <ucontext.h>
#include <sys/prctl.h>
#include "test_signals_utils.h"
#include "sve_helpers.h"
#include "testcases.h"
static union {
ucontext_t uc;
char buf[1024 * 128];
} context;
static bool sme_get_vls(struct tdescr *td)
{
int res = sve_fill_vls(VLS_USE_SME, 1);
if (!res)
return true;
if (res == KSFT_SKIP)
td->result = KSFT_SKIP;
return false;
}
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,
};
@@ -0,0 +1,105 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2021 ARM Limited
*
* Verify that the SVE register context in signal frames is set up as
* expected.
*/
#include <kselftest.h>
#include <signal.h>
#include <ucontext.h>
#include <sys/prctl.h>
#include "test_signals_utils.h"
#include "sve_helpers.h"
#include "testcases.h"
static union {
ucontext_t uc;
char buf[1024 * 64];
} context;
static bool sve_get_vls(struct tdescr *td)
{
int res = sve_fill_vls(VLS_USE_SVE, 1);
if (!res)
return true;
if (res == KSFT_SKIP)
td->result = KSFT_SKIP;
return false;
}
static void setup_sve_regs(void)
{
/* RDVL x16, #1 so we should have SVE regs; real data is TODO */
asm volatile(".inst 0x04bf5030" : : : "x16" );
}
static int do_one_sve_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 sve_context *sve;
fprintf(stderr, "Testing VL %d\n", vl);
if (prctl(PR_SVE_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_sve_regs();
if (!get_current_context(td, &context.uc, sizeof(context)))
return 1;
head = get_header(head, SVE_MAGIC, GET_BUF_RESV_SIZE(context),
&offset);
if (!head) {
fprintf(stderr, "No SVE context\n");
return 1;
}
sve = (struct sve_context *)head;
if (sve->vl != vl) {
fprintf(stderr, "Got VL %d, expected %d\n", sve->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, sve->vl);
return 0;
}
static int sve_regs(struct tdescr *td, siginfo_t *si, ucontext_t *uc)
{
int i;
for (i = 0; i < nvls; i++) {
if (do_one_sve_vl(td, si, uc, vls[i]))
return 1;
}
td->pass = 1;
return 0;
}
struct tdescr tde = {
.name = "SVE registers",
.descr = "Check that we get the right SVE registers reported",
.feats_required = FEAT_SVE,
.timeout = 3,
.init = sve_get_vls,
.run = sve_regs,
};
@@ -0,0 +1,68 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2021 ARM Limited
*
* Check that the SVE 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_sve_vl(struct tdescr *td)
{
int ret = prctl(PR_SVE_GET_VL);
if (ret == -1)
return false;
vl = ret;
return true;
}
static int sve_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 which should have a SVE frame in it */
if (!get_current_context(td, &sf.uc, sizeof(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;
}
sve = (struct sve_context *)head;
if (sve->vl != vl) {
fprintf(stderr, "sigframe VL %u, expected %u\n",
sve->vl, vl);
return 1;
} else {
fprintf(stderr, "got expected VL %u\n", vl);
}
td->pass = 1;
return 0;
}
struct tdescr tde = {
.name = "SVE VL",
.descr = "Check that we get the right SVE VL reported",
.feats_required = FEAT_SVE,
.timeout = 3,
.init = get_sve_vl,
.run = sve_vl,
};
@@ -0,0 +1,331 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (C) 2019 ARM Limited */
#include <ctype.h>
#include <string.h>
#include "testcases.h"
bool validate_extra_context(struct extra_context *extra, char **err,
void **extra_data, size_t *extra_size)
{
struct _aarch64_ctx *term;
if (!extra || !err)
return false;
fprintf(stderr, "Validating EXTRA...\n");
term = GET_RESV_NEXT_HEAD(&extra->head);
if (!term || term->magic || term->size) {
*err = "Missing terminator after EXTRA context";
return false;
}
if (extra->datap & 0x0fUL)
*err = "Extra DATAP misaligned";
else if (extra->size & 0x0fUL)
*err = "Extra SIZE misaligned";
else if (extra->datap != (uint64_t)term + 0x10UL)
*err = "Extra DATAP misplaced (not contiguous)";
if (*err)
return false;
*extra_data = (void *)extra->datap;
*extra_size = extra->size;
return true;
}
bool validate_sve_context(struct sve_context *sve, char **err)
{
/* Size will be rounded up to a multiple of 16 bytes */
size_t regs_size
= ((SVE_SIG_CONTEXT_SIZE(sve_vq_from_vl(sve->vl)) + 15) / 16) * 16;
if (!sve || !err)
return false;
/* Either a bare sve_context or a sve_context followed by regs data */
if ((sve->head.size != sizeof(struct sve_context)) &&
(sve->head.size != regs_size)) {
*err = "bad size for SVE context";
return false;
}
if (!sve_vl_valid(sve->vl)) {
*err = "SVE VL invalid";
return false;
}
return true;
}
bool validate_za_context(struct za_context *za, char **err)
{
/* Size will be rounded up to a multiple of 16 bytes */
size_t regs_size
= ((ZA_SIG_CONTEXT_SIZE(sve_vq_from_vl(za->vl)) + 15) / 16) * 16;
if (!za || !err)
return false;
/* Either a bare za_context or a za_context followed by regs data */
if ((za->head.size != sizeof(struct za_context)) &&
(za->head.size != regs_size)) {
*err = "bad size for ZA context";
return false;
}
if (!sve_vl_valid(za->vl)) {
*err = "SME VL in ZA context invalid";
return false;
}
return true;
}
bool validate_zt_context(struct zt_context *zt, char **err)
{
if (!zt || !err)
return false;
/* If the context is present there should be at least one register */
if (zt->nregs == 0) {
*err = "no registers";
return false;
}
/* Size should agree with the number of registers */
if (zt->head.size != ZT_SIG_CONTEXT_SIZE(zt->nregs)) {
*err = "register count does not match size";
return false;
}
return true;
}
bool validate_reserved(ucontext_t *uc, size_t resv_sz, char **err)
{
bool terminated = false;
size_t offs = 0;
int flags = 0;
int new_flags, i;
struct extra_context *extra = NULL;
struct sve_context *sve = NULL;
struct za_context *za = NULL;
struct zt_context *zt = NULL;
struct _aarch64_ctx *head =
(struct _aarch64_ctx *)uc->uc_mcontext.__reserved;
void *extra_data = NULL;
size_t extra_sz = 0;
char magic[4];
if (!err)
return false;
/* Walk till the end terminator verifying __reserved contents */
while (head && !terminated && offs < resv_sz) {
if ((uint64_t)head & 0x0fUL) {
*err = "Misaligned HEAD";
return false;
}
new_flags = 0;
switch (head->magic) {
case 0:
if (head->size) {
*err = "Bad size for terminator";
} else if (extra_data) {
/* End of main data, walking the extra data */
head = extra_data;
resv_sz = extra_sz;
offs = 0;
extra_data = NULL;
extra_sz = 0;
continue;
} else {
terminated = true;
}
break;
case FPSIMD_MAGIC:
if (flags & FPSIMD_CTX)
*err = "Multiple FPSIMD_MAGIC";
else if (head->size !=
sizeof(struct fpsimd_context))
*err = "Bad size for fpsimd_context";
new_flags |= FPSIMD_CTX;
break;
case ESR_MAGIC:
if (head->size != sizeof(struct esr_context))
*err = "Bad size for esr_context";
break;
case POE_MAGIC:
if (head->size != sizeof(struct poe_context))
*err = "Bad size for poe_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";
/* Size is validated in validate_sve_context() */
sve = (struct sve_context *)head;
new_flags |= SVE_CTX;
break;
case ZA_MAGIC:
if (flags & ZA_CTX)
*err = "Multiple ZA_MAGIC";
/* Size is validated in validate_za_context() */
za = (struct za_context *)head;
new_flags |= ZA_CTX;
break;
case ZT_MAGIC:
if (flags & ZT_CTX)
*err = "Multiple ZT_MAGIC";
/* Size is validated in validate_za_context() */
zt = (struct zt_context *)head;
new_flags |= ZT_CTX;
break;
case FPMR_MAGIC:
if (flags & FPMR_CTX)
*err = "Multiple FPMR_MAGIC";
else if (head->size !=
sizeof(struct fpmr_context))
*err = "Bad size for fpmr_context";
new_flags |= FPMR_CTX;
break;
case GCS_MAGIC:
if (flags & GCS_CTX)
*err = "Multiple GCS_MAGIC";
if (head->size != sizeof(struct gcs_context))
*err = "Bad size for gcs_context";
new_flags |= GCS_CTX;
break;
case EXTRA_MAGIC:
if (flags & EXTRA_CTX)
*err = "Multiple EXTRA_MAGIC";
else if (head->size !=
sizeof(struct extra_context))
*err = "Bad size for extra_context";
new_flags |= EXTRA_CTX;
extra = (struct extra_context *)head;
break;
case KSFT_BAD_MAGIC:
/*
* This is a BAD magic header defined
* artificially by a testcase and surely
* unknown to the Kernel parse_user_sigframe().
* It MUST cause a Kernel induced SEGV
*/
*err = "BAD MAGIC !";
break;
default:
/*
* A still unknown Magic: potentially freshly added
* to the Kernel code and still unknown to the
* tests. Magic numbers are supposed to be allocated
* as somewhat meaningful ASCII strings so try to
* print as such as well as the raw number.
*/
memcpy(magic, &head->magic, sizeof(magic));
for (i = 0; i < sizeof(magic); i++)
if (!isalnum(magic[i]))
magic[i] = '?';
fprintf(stdout,
"SKIP Unknown MAGIC: 0x%X (%c%c%c%c) - Is KSFT arm64/signal up to date ?\n",
head->magic,
magic[3], magic[2], magic[1], magic[0]);
break;
}
if (*err)
return false;
offs += head->size;
if (resv_sz < offs + sizeof(*head)) {
*err = "HEAD Overrun";
return false;
}
if (new_flags & EXTRA_CTX)
if (!validate_extra_context(extra, err,
&extra_data, &extra_sz))
return false;
if (new_flags & SVE_CTX)
if (!validate_sve_context(sve, err))
return false;
if (new_flags & ZA_CTX)
if (!validate_za_context(za, err))
return false;
if (new_flags & ZT_CTX)
if (!validate_zt_context(zt, err))
return false;
flags |= new_flags;
head = GET_RESV_NEXT_HEAD(head);
}
if (terminated && !(flags & FPSIMD_CTX)) {
*err = "Missing FPSIMD";
return false;
}
if (terminated && (flags & ZT_CTX) && !(flags & ZA_CTX)) {
*err = "ZT context but no ZA context";
return false;
}
return true;
}
/*
* This function walks through the records inside the provided reserved area
* trying to find enough space to fit @need_sz bytes: if not enough space is
* available and an extra_context record is present, it throws away the
* extra_context record.
*
* It returns a pointer to a new header where it is possible to start storing
* our need_sz bytes.
*
* @shead: points to the start of reserved area
* @need_sz: needed bytes
* @resv_sz: reserved area size in bytes
* @offset: if not null, this will be filled with the offset of the return
* head pointer from @shead
*
* @return: pointer to a new head where to start storing need_sz bytes, or
* NULL if space could not be made available.
*/
struct _aarch64_ctx *get_starting_head(struct _aarch64_ctx *shead,
size_t need_sz, size_t resv_sz,
size_t *offset)
{
size_t offs = 0;
struct _aarch64_ctx *head;
head = get_terminator(shead, resv_sz, &offs);
/* not found a terminator...no need to update offset if any */
if (!head)
return head;
if (resv_sz - offs < need_sz) {
fprintf(stderr, "Low on space:%zd. Discarding extra_context.\n",
resv_sz - offs);
head = get_header(shead, EXTRA_MAGIC, resv_sz, &offs);
if (!head || resv_sz - offs < need_sz) {
fprintf(stderr,
"Failed to reclaim space on sigframe.\n");
return NULL;
}
}
fprintf(stderr, "Available space:%zd\n", resv_sz - offs);
if (offset)
*offset = offs;
return head;
}
@@ -0,0 +1,137 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (C) 2019 ARM Limited */
#ifndef __TESTCASES_H__
#define __TESTCASES_H__
#include <stddef.h>
#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <ucontext.h>
#include <signal.h>
/* Architecture specific sigframe definitions */
#include <asm/sigcontext.h>
#define FPSIMD_CTX (1 << 0)
#define SVE_CTX (1 << 1)
#define ZA_CTX (1 << 2)
#define EXTRA_CTX (1 << 3)
#define ZT_CTX (1 << 4)
#define FPMR_CTX (1 << 5)
#define GCS_CTX (1 << 6)
#define KSFT_BAD_MAGIC 0xdeadbeef
#define HDR_SZ \
sizeof(struct _aarch64_ctx)
#define GET_UC_RESV_HEAD(uc) \
(struct _aarch64_ctx *)(&(uc->uc_mcontext.__reserved))
#define GET_SF_RESV_HEAD(sf) \
(struct _aarch64_ctx *)(&(sf).uc.uc_mcontext.__reserved)
#define GET_SF_RESV_SIZE(sf) \
sizeof((sf).uc.uc_mcontext.__reserved)
#define GET_BUF_RESV_HEAD(buf) \
(struct _aarch64_ctx *)(&(buf).uc.uc_mcontext.__reserved)
#define GET_BUF_RESV_SIZE(buf) \
(sizeof(buf) - sizeof(buf.uc) + \
sizeof((buf).uc.uc_mcontext.__reserved))
#define GET_UCP_RESV_SIZE(ucp) \
sizeof((ucp)->uc_mcontext.__reserved)
#define ASSERT_BAD_CONTEXT(uc) do { \
char *err = NULL; \
if (!validate_reserved((uc), GET_UCP_RESV_SIZE((uc)), &err)) { \
if (err) \
fprintf(stderr, \
"Using badly built context - ERR: %s\n",\
err); \
} else { \
abort(); \
} \
} while (0)
#define ASSERT_GOOD_CONTEXT(uc) do { \
char *err = NULL; \
if (!validate_reserved((uc), GET_UCP_RESV_SIZE((uc)), &err)) { \
if (err) \
fprintf(stderr, \
"Detected BAD context - ERR: %s\n", err);\
abort(); \
} else { \
fprintf(stderr, "uc context validated.\n"); \
} \
} while (0)
/*
* A simple record-walker for __reserved area: it walks through assuming
* only to find a proper struct __aarch64_ctx header descriptor.
*
* Instead it makes no assumptions on the content and ordering of the
* records, any needed bounds checking must be enforced by the caller
* if wanted: this way can be used by caller on any maliciously built bad
* contexts.
*
* head->size accounts both for payload and header _aarch64_ctx size !
*/
#define GET_RESV_NEXT_HEAD(h) \
(struct _aarch64_ctx *)((char *)(h) + (h)->size)
struct fake_sigframe {
siginfo_t info;
ucontext_t uc;
};
bool validate_reserved(ucontext_t *uc, size_t resv_sz, char **err);
static inline struct _aarch64_ctx *get_header(struct _aarch64_ctx *head, uint32_t magic,
size_t resv_sz, size_t *offset)
{
size_t offs = 0;
struct _aarch64_ctx *found = NULL;
if (!head || resv_sz < HDR_SZ)
return found;
while (offs <= resv_sz - HDR_SZ &&
head->magic != magic && head->magic) {
offs += head->size;
head = GET_RESV_NEXT_HEAD(head);
}
if (head->magic == magic) {
found = head;
if (offset)
*offset = offs;
}
return found;
}
static inline struct _aarch64_ctx *get_terminator(struct _aarch64_ctx *head,
size_t resv_sz,
size_t *offset)
{
return get_header(head, 0, resv_sz, offset);
}
static inline void write_terminator_record(struct _aarch64_ctx *tail)
{
if (tail) {
tail->magic = 0;
tail->size = 0;
}
}
struct _aarch64_ctx *get_starting_head(struct _aarch64_ctx *shead,
size_t need_sz, size_t resv_sz,
size_t *offset);
#endif
@@ -0,0 +1,86 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2023 ARM Limited
*
* Verify that the TPIDR2 register context in signal frames is restored.
*/
#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"
#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;
}
static void set_tpidr2(uint64_t val)
{
asm volatile (
"msr " SYS_TPIDR2 ", %0\n"
:
: "r"(val)
: "cc");
}
static uint64_t initial_tpidr2;
static bool save_tpidr2(struct tdescr *td)
{
initial_tpidr2 = get_tpidr2();
fprintf(stderr, "Initial TPIDR2: %lx\n", initial_tpidr2);
return true;
}
static int modify_tpidr2(struct tdescr *td, siginfo_t *si, ucontext_t *uc)
{
uint64_t my_tpidr2 = get_tpidr2();
my_tpidr2++;
fprintf(stderr, "Setting TPIDR2 to %lx\n", my_tpidr2);
set_tpidr2(my_tpidr2);
return 0;
}
static void check_tpidr2(struct tdescr *td)
{
uint64_t tpidr2 = get_tpidr2();
td->pass = tpidr2 == initial_tpidr2;
if (td->pass)
fprintf(stderr, "TPIDR2 restored\n");
else
fprintf(stderr, "TPIDR2 was %lx but is now %lx\n",
initial_tpidr2, tpidr2);
}
struct tdescr tde = {
.name = "TPIDR2 restore",
.descr = "Validate that TPIDR2 is restored from the sigframe",
.feats_required = FEAT_SME,
.timeout = 3,
.sig_trig = SIGUSR1,
.init = save_tpidr2,
.run = modify_tpidr2,
.check_result = check_tpidr2,
};
@@ -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,
};
@@ -0,0 +1,103 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2021 ARM Limited
*
* Verify that the ZA register context in signal frames is set up as
* expected.
*/
#include <kselftest.h>
#include <signal.h>
#include <ucontext.h>
#include <sys/prctl.h>
#include "test_signals_utils.h"
#include "sve_helpers.h"
#include "testcases.h"
static union {
ucontext_t uc;
char buf[1024 * 128];
} context;
static bool sme_get_vls(struct tdescr *td)
{
int res = sve_fill_vls(VLS_USE_SME, 1);
if (!res)
return true;
if (res == KSFT_SKIP)
td->result = KSFT_SKIP;
return false;
}
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 za_context *za;
fprintf(stderr, "Testing VL %d\n", vl);
if (prctl(PR_SME_SET_VL, vl) != vl) {
fprintf(stderr, "Failed to set VL\n");
return 1;
}
/*
* Get a signal context which should have a SVE frame and registers
* in it.
*/
if (!get_current_context(td, &context.uc, sizeof(context)))
return 1;
head = get_header(head, ZA_MAGIC, GET_BUF_RESV_SIZE(context), &offset);
if (!head) {
fprintf(stderr, "No ZA context\n");
return 1;
}
za = (struct za_context *)head;
if (za->vl != vl) {
fprintf(stderr, "Got VL %d, expected %d\n", za->vl, vl);
return 1;
}
if (head->size != ZA_SIG_REGS_OFFSET) {
fprintf(stderr, "Context size %u, expected %lu\n",
head->size, ZA_SIG_REGS_OFFSET);
return 1;
}
/* The actual size validation is done in get_current_context() */
fprintf(stderr, "Got expected size %u and VL %d\n",
head->size, za->vl);
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 = "ZA registers - ZA disabled",
.descr = "Check ZA context with ZA disabled",
.feats_required = FEAT_SME,
.timeout = 3,
.init = sme_get_vls,
.run = sme_regs,
};
@@ -0,0 +1,123 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2021 ARM Limited
*
* Verify that the ZA register context in signal frames is set up as
* expected.
*/
#include <kselftest.h>
#include <signal.h>
#include <ucontext.h>
#include <sys/prctl.h>
#include "test_signals_utils.h"
#include "sve_helpers.h"
#include "testcases.h"
static union {
ucontext_t uc;
char buf[1024 * 128];
} context;
static bool sme_get_vls(struct tdescr *td)
{
int res = sve_fill_vls(VLS_USE_SME, 1);
if (!res)
return true;
if (res == KSFT_SKIP)
td->result = KSFT_SKIP;
return false;
}
static void setup_za_regs(void)
{
/* 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 za_context *za;
fprintf(stderr, "Testing VL %d\n", vl);
if (prctl(PR_SME_SET_VL, vl) != vl) {
fprintf(stderr, "Failed to set VL\n");
return 1;
}
/*
* Get a signal context which should have a SVE frame and registers
* in it.
*/
setup_za_regs();
if (!get_current_context(td, &context.uc, sizeof(context)))
return 1;
head = get_header(head, ZA_MAGIC, GET_BUF_RESV_SIZE(context), &offset);
if (!head) {
fprintf(stderr, "No ZA context\n");
return 1;
}
za = (struct za_context *)head;
if (za->vl != vl) {
fprintf(stderr, "Got VL %d, expected %d\n", za->vl, vl);
return 1;
}
if (head->size != ZA_SIG_CONTEXT_SIZE(sve_vq_from_vl(vl))) {
fprintf(stderr, "ZA context size %u, expected %lu\n",
head->size, ZA_SIG_CONTEXT_SIZE(sve_vq_from_vl(vl)));
return 1;
}
fprintf(stderr, "Got expected size %u and VL %d\n",
head->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;
}
if (get_svcr() != 0) {
fprintf(stderr, "Unexpected SVCR %lx\n", get_svcr());
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 = "ZA register",
.descr = "Check that we get the right ZA registers reported",
.feats_required = FEAT_SME,
.timeout = 3,
.init = sme_get_vls,
.run = sme_regs,
};
@@ -0,0 +1,51 @@
// 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"
static union {
ucontext_t uc;
char buf[1024 * 128];
} context;
int zt_no_regs_run(struct tdescr *td, siginfo_t *si, ucontext_t *uc)
{
size_t offset;
struct _aarch64_ctx *head = GET_BUF_RESV_HEAD(context);
/*
* Get a signal context which should not have a ZT frame and
* registers in it.
*/
if (!get_current_context(td, &context.uc, sizeof(context)))
return 1;
head = get_header(head, ZT_MAGIC, GET_BUF_RESV_SIZE(context), &offset);
if (head) {
fprintf(stderr, "Got unexpected ZT context\n");
return 1;
}
td->pass = 1;
return 0;
}
struct tdescr tde = {
.name = "ZT register data not present",
.descr = "Validate that ZT is not present when ZA is disabled",
.feats_required = FEAT_SME2,
.timeout = 3,
.sanity_disabled = true,
.run = zt_no_regs_run,
};
@@ -0,0 +1,86 @@
// 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"
static union {
ucontext_t uc;
char buf[1024 * 128];
} context;
static void enable_za(void)
{
/* smstart za; real data is TODO */
asm volatile(".inst 0xd503457f" : : : );
}
int zt_regs_run(struct tdescr *td, siginfo_t *si, ucontext_t *uc)
{
size_t offset;
struct _aarch64_ctx *head = GET_BUF_RESV_HEAD(context);
struct zt_context *zt;
char *zeros;
/*
* Get a signal context which should have a ZT frame and registers
* in it.
*/
enable_za();
if (!get_current_context(td, &context.uc, sizeof(context)))
return 1;
head = get_header(head, ZT_MAGIC, GET_BUF_RESV_SIZE(context), &offset);
if (!head) {
fprintf(stderr, "No ZT context\n");
return 1;
}
zt = (struct zt_context *)head;
if (zt->nregs == 0) {
fprintf(stderr, "Got context with no registers\n");
return 1;
}
fprintf(stderr, "Got expected size %u for %d registers\n",
head->size, zt->nregs);
/* We didn't load any data into ZT so it should be all zeros */
zeros = malloc(ZT_SIG_REGS_SIZE(zt->nregs));
if (!zeros) {
fprintf(stderr, "Out of memory, nregs=%u\n", zt->nregs);
return 1;
}
memset(zeros, 0, ZT_SIG_REGS_SIZE(zt->nregs));
if (memcmp(zeros, (char *)zt + ZT_SIG_REGS_OFFSET,
ZT_SIG_REGS_SIZE(zt->nregs)) != 0) {
fprintf(stderr, "ZT data invalid\n");
free(zeros);
return 1;
}
free(zeros);
td->pass = 1;
return 0;
}
struct tdescr tde = {
.name = "ZT register data",
.descr = "Validate that ZT is present and has data when ZA is enabled",
.feats_required = FEAT_SME2,
.timeout = 3,
.sanity_disabled = true,
.run = zt_regs_run,
};