Merge branch 'for-next/kselftest' into for-next/core

* for-next/kselftest: (28 commits)
  : Kselftest updates for arm64
  kselftest/arm64: Handle EINTR while reading data from children
  kselftest/arm64: Flag fp-stress as exiting when we begin finishing up
  kselftest/arm64: Don't repeat termination handler for fp-stress
  kselftest/arm64: Don't enable v8.5 for MTE selftest builds
  kselftest/arm64: Fix typo in hwcap check
  kselftest/arm64: Add hwcap test for RNG
  kselftest/arm64: Add SVE 2 to the tested hwcaps
  kselftest/arm64: Add missing newline in hwcap output
  kselftest/arm64: Fix spelling misakes of signal names
  kselftest/arm64: Enforce actual ABI for SVE syscalls
  kselftest/arm64: Correct buffer allocation for SVE Z registers
  kselftest/arm64: Include larger SVE and SME VLs in signal tests
  kselftest/arm64: Allow larger buffers in get_signal_context()
  kselftest/arm64: Preserve any EXTRA_CONTEXT in handle_signal_copyctx()
  kselftest/arm64: Validate contents of EXTRA_CONTEXT blocks
  kselftest/arm64: Only validate each signal context once
  kselftest/arm64: Remove unneeded protype for validate_extra_context()
  kselftest/arm64: Fix validation of EXTRA_CONTEXT signal context location
  kselftest/arm64: Fix validatation termination record after EXTRA_CONTEXT
  kselftest/arm64: Validate signal ucontext in place
  ...
This commit is contained in:
Catalin Marinas 2022-09-30 09:18:11 +01:00
commit c397623262
31 changed files with 1312 additions and 167 deletions

View File

@ -1,3 +1,4 @@
hwcap
ptrace
syscall-abi
tpidr2

View File

@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
# Copyright (C) 2021 ARM Limited
TEST_GEN_PROGS := ptrace syscall-abi tpidr2
TEST_GEN_PROGS := hwcap ptrace syscall-abi tpidr2
include ../../lib.mk

View File

@ -0,0 +1,336 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2022 ARM Limited.
*/
#include <errno.h>
#include <signal.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/auxv.h>
#include <sys/prctl.h>
#include <asm/hwcap.h>
#include <asm/sigcontext.h>
#include <asm/unistd.h>
#include "../../kselftest.h"
#define TESTS_PER_HWCAP 2
/*
* Function expected to generate SIGILL when the feature is not
* supported and return when it is supported. If SIGILL is generated
* then the handler must be able to skip over the instruction safely.
*
* Note that it is expected that for many architecture extensions
* there are no specific traps due to no architecture state being
* added so we may not fault if running on a kernel which doesn't know
* to add the hwcap.
*/
typedef void (*sigill_fn)(void);
static void rng_sigill(void)
{
asm volatile("mrs x0, S3_3_C2_C4_0" : : : "x0");
}
static void sme_sigill(void)
{
/* RDSVL x0, #0 */
asm volatile(".inst 0x04bf5800" : : : "x0");
}
static void sve_sigill(void)
{
/* RDVL x0, #0 */
asm volatile(".inst 0x04bf5000" : : : "x0");
}
static void sve2_sigill(void)
{
/* SQABS Z0.b, P0/M, Z0.B */
asm volatile(".inst 0x4408A000" : : : "z0");
}
static void sveaes_sigill(void)
{
/* AESD z0.b, z0.b, z0.b */
asm volatile(".inst 0x4522e400" : : : "z0");
}
static void svepmull_sigill(void)
{
/* PMULLB Z0.Q, Z0.D, Z0.D */
asm volatile(".inst 0x45006800" : : : "z0");
}
static void svebitperm_sigill(void)
{
/* BDEP Z0.B, Z0.B, Z0.B */
asm volatile(".inst 0x4500b400" : : : "z0");
}
static void svesha3_sigill(void)
{
/* EOR3 Z0.D, Z0.D, Z0.D, Z0.D */
asm volatile(".inst 0x4203800" : : : "z0");
}
static void svesm4_sigill(void)
{
/* SM4E Z0.S, Z0.S, Z0.S */
asm volatile(".inst 0x4523e000" : : : "z0");
}
static void svei8mm_sigill(void)
{
/* USDOT Z0.S, Z0.B, Z0.B[0] */
asm volatile(".inst 0x44a01800" : : : "z0");
}
static void svef32mm_sigill(void)
{
/* FMMLA Z0.S, Z0.S, Z0.S */
asm volatile(".inst 0x64a0e400" : : : "z0");
}
static void svef64mm_sigill(void)
{
/* FMMLA Z0.D, Z0.D, Z0.D */
asm volatile(".inst 0x64e0e400" : : : "z0");
}
static void svebf16_sigill(void)
{
/* BFCVT Z0.H, P0/M, Z0.S */
asm volatile(".inst 0x658aa000" : : : "z0");
}
static const struct hwcap_data {
const char *name;
unsigned long at_hwcap;
unsigned long hwcap_bit;
const char *cpuinfo;
sigill_fn sigill_fn;
bool sigill_reliable;
} hwcaps[] = {
{
.name = "RNG",
.at_hwcap = AT_HWCAP2,
.hwcap_bit = HWCAP2_RNG,
.cpuinfo = "rng",
.sigill_fn = rng_sigill,
},
{
.name = "SME",
.at_hwcap = AT_HWCAP2,
.hwcap_bit = HWCAP2_SME,
.cpuinfo = "sme",
.sigill_fn = sme_sigill,
.sigill_reliable = true,
},
{
.name = "SVE",
.at_hwcap = AT_HWCAP,
.hwcap_bit = HWCAP_SVE,
.cpuinfo = "sve",
.sigill_fn = sve_sigill,
.sigill_reliable = true,
},
{
.name = "SVE 2",
.at_hwcap = AT_HWCAP2,
.hwcap_bit = HWCAP2_SVE2,
.cpuinfo = "sve2",
.sigill_fn = sve2_sigill,
},
{
.name = "SVE AES",
.at_hwcap = AT_HWCAP2,
.hwcap_bit = HWCAP2_SVEAES,
.cpuinfo = "sveaes",
.sigill_fn = sveaes_sigill,
},
{
.name = "SVE2 PMULL",
.at_hwcap = AT_HWCAP2,
.hwcap_bit = HWCAP2_SVEPMULL,
.cpuinfo = "svepmull",
.sigill_fn = svepmull_sigill,
},
{
.name = "SVE2 BITPERM",
.at_hwcap = AT_HWCAP2,
.hwcap_bit = HWCAP2_SVEBITPERM,
.cpuinfo = "svebitperm",
.sigill_fn = svebitperm_sigill,
},
{
.name = "SVE2 SHA3",
.at_hwcap = AT_HWCAP2,
.hwcap_bit = HWCAP2_SVESHA3,
.cpuinfo = "svesha3",
.sigill_fn = svesha3_sigill,
},
{
.name = "SVE2 SM4",
.at_hwcap = AT_HWCAP2,
.hwcap_bit = HWCAP2_SVESM4,
.cpuinfo = "svesm4",
.sigill_fn = svesm4_sigill,
},
{
.name = "SVE2 I8MM",
.at_hwcap = AT_HWCAP2,
.hwcap_bit = HWCAP2_SVEI8MM,
.cpuinfo = "svei8mm",
.sigill_fn = svei8mm_sigill,
},
{
.name = "SVE2 F32MM",
.at_hwcap = AT_HWCAP2,
.hwcap_bit = HWCAP2_SVEF32MM,
.cpuinfo = "svef32mm",
.sigill_fn = svef32mm_sigill,
},
{
.name = "SVE2 F64MM",
.at_hwcap = AT_HWCAP2,
.hwcap_bit = HWCAP2_SVEF64MM,
.cpuinfo = "svef64mm",
.sigill_fn = svef64mm_sigill,
},
{
.name = "SVE2 BF16",
.at_hwcap = AT_HWCAP2,
.hwcap_bit = HWCAP2_SVEBF16,
.cpuinfo = "svebf16",
.sigill_fn = svebf16_sigill,
},
{
.name = "SVE2 EBF16",
.at_hwcap = AT_HWCAP2,
.hwcap_bit = HWCAP2_SVE_EBF16,
.cpuinfo = "sveebf16",
},
};
static bool seen_sigill;
static void handle_sigill(int sig, siginfo_t *info, void *context)
{
ucontext_t *uc = context;
seen_sigill = true;
/* Skip over the offending instruction */
uc->uc_mcontext.pc += 4;
}
bool cpuinfo_present(const char *name)
{
FILE *f;
char buf[2048], name_space[30], name_newline[30];
char *s;
/*
* The feature should appear with a leading space and either a
* trailing space or a newline.
*/
snprintf(name_space, sizeof(name_space), " %s ", name);
snprintf(name_newline, sizeof(name_newline), " %s\n", name);
f = fopen("/proc/cpuinfo", "r");
if (!f) {
ksft_print_msg("Failed to open /proc/cpuinfo\n");
return false;
}
while (fgets(buf, sizeof(buf), f)) {
/* Features: line? */
if (strncmp(buf, "Features\t:", strlen("Features\t:")) != 0)
continue;
/* All CPUs should be symmetric, don't read any more */
fclose(f);
s = strstr(buf, name_space);
if (s)
return true;
s = strstr(buf, name_newline);
if (s)
return true;
return false;
}
ksft_print_msg("Failed to find Features in /proc/cpuinfo\n");
fclose(f);
return false;
}
int main(void)
{
const struct hwcap_data *hwcap;
int i, ret;
bool have_cpuinfo, have_hwcap;
struct sigaction sa;
ksft_print_header();
ksft_set_plan(ARRAY_SIZE(hwcaps) * TESTS_PER_HWCAP);
memset(&sa, 0, sizeof(sa));
sa.sa_sigaction = handle_sigill;
sa.sa_flags = SA_RESTART | SA_SIGINFO;
sigemptyset(&sa.sa_mask);
ret = sigaction(SIGILL, &sa, NULL);
if (ret < 0)
ksft_exit_fail_msg("Failed to install SIGILL handler: %s (%d)\n",
strerror(errno), errno);
for (i = 0; i < ARRAY_SIZE(hwcaps); i++) {
hwcap = &hwcaps[i];
have_hwcap = getauxval(hwcap->at_hwcap) & hwcap->hwcap_bit;
have_cpuinfo = cpuinfo_present(hwcap->cpuinfo);
if (have_hwcap)
ksft_print_msg("%s present\n", hwcap->name);
ksft_test_result(have_hwcap == have_cpuinfo,
"cpuinfo_match_%s\n", hwcap->name);
if (hwcap->sigill_fn) {
seen_sigill = false;
hwcap->sigill_fn();
if (have_hwcap) {
/* Should be able to use the extension */
ksft_test_result(!seen_sigill, "sigill_%s\n",
hwcap->name);
} else if (hwcap->sigill_reliable) {
/* Guaranteed a SIGILL */
ksft_test_result(seen_sigill, "sigill_%s\n",
hwcap->name);
} else {
/* Missing SIGILL might be fine */
ksft_print_msg("SIGILL %sreported for %s\n",
seen_sigill ? "" : "not ",
hwcap->name);
ksft_test_result_skip("sigill_%s\n",
hwcap->name);
}
} else {
ksft_test_result_skip("sigill_%s\n",
hwcap->name);
}
}
ksft_print_cnts();
return 0;
}

View File

@ -112,9 +112,11 @@ static int check_fpr(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
return errors;
}
#define SVE_Z_SHARED_BYTES (128 / 8)
static uint8_t z_zero[__SVE_ZREG_SIZE(SVE_VQ_MAX)];
uint8_t z_in[SVE_NUM_PREGS * __SVE_ZREG_SIZE(SVE_VQ_MAX)];
uint8_t z_out[SVE_NUM_PREGS * __SVE_ZREG_SIZE(SVE_VQ_MAX)];
uint8_t z_in[SVE_NUM_ZREGS * __SVE_ZREG_SIZE(SVE_VQ_MAX)];
uint8_t z_out[SVE_NUM_ZREGS * __SVE_ZREG_SIZE(SVE_VQ_MAX)];
static void setup_z(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
uint64_t svcr)
@ -133,22 +135,39 @@ static int check_z(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
if (!sve_vl)
return 0;
/*
* After a syscall the low 128 bits of the Z registers should
* be preserved and the rest be zeroed or preserved, except if
* we were in streaming mode in which case the low 128 bits may
* also be cleared by the transition out of streaming mode.
*/
for (i = 0; i < SVE_NUM_ZREGS; i++) {
void *in = &z_in[reg_size * i];
void *out = &z_out[reg_size * i];
uint8_t *in = &z_in[reg_size * i];
uint8_t *out = &z_out[reg_size * i];
if ((memcmp(in, out, SVE_VQ_BYTES) != 0) &&
!((svcr & SVCR_SM_MASK) &&
memcmp(z_zero, out, SVE_VQ_BYTES) == 0)) {
ksft_print_msg("%s SVE VL %d Z%d low 128 bits changed\n",
cfg->name, sve_vl, i);
errors++;
if (svcr & SVCR_SM_MASK) {
/*
* In streaming mode the whole register should
* be cleared by the transition out of
* streaming mode.
*/
if (memcmp(z_zero, out, reg_size) != 0) {
ksft_print_msg("%s SVE VL %d Z%d non-zero\n",
cfg->name, sve_vl, i);
errors++;
}
} else {
/*
* For standard SVE the low 128 bits should be
* preserved and any additional bits cleared.
*/
if (memcmp(in, out, SVE_Z_SHARED_BYTES) != 0) {
ksft_print_msg("%s SVE VL %d Z%d low 128 bits changed\n",
cfg->name, sve_vl, i);
errors++;
}
if (reg_size > SVE_Z_SHARED_BYTES &&
(memcmp(z_zero, out + SVE_Z_SHARED_BYTES,
reg_size - SVE_Z_SHARED_BYTES) != 0)) {
ksft_print_msg("%s SVE VL %d Z%d high bits non-zero\n",
cfg->name, sve_vl, i);
errors++;
}
}
}
@ -176,9 +195,9 @@ static int check_p(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
if (!sve_vl)
return 0;
/* After a syscall the P registers should be preserved or zeroed */
/* After a syscall the P registers should be zeroed */
for (i = 0; i < SVE_NUM_PREGS * reg_size; i++)
if (p_out[i] && (p_in[i] != p_out[i]))
if (p_out[i])
errors++;
if (errors)
ksft_print_msg("%s SVE VL %d predicate registers non-zero\n",
@ -226,9 +245,9 @@ static int check_ffr(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
!(getauxval(AT_HWCAP2) & HWCAP2_SME_FA64))
return 0;
/* After a syscall the P registers should be preserved or zeroed */
/* After a syscall FFR should be zeroed */
for (i = 0; i < reg_size; i++)
if (ffr_out[i] && (ffr_in[i] != ffr_out[i]))
if (ffr_out[i])
errors++;
if (errors)
ksft_print_msg("%s SVE VL %d FFR non-zero\n",

View File

@ -1,4 +1,5 @@
fp-pidbench
fp-stress
fpsimd-test
rdvl-sme
rdvl-sve

View File

@ -5,7 +5,10 @@ top_srcdir = $(realpath ../../../../../)
CFLAGS += -I$(top_srcdir)/usr/include/
TEST_GEN_PROGS := sve-ptrace sve-probe-vls vec-syscfg za-fork za-ptrace
TEST_GEN_PROGS := fp-stress \
sve-ptrace sve-probe-vls \
vec-syscfg \
za-fork za-ptrace
TEST_GEN_PROGS_EXTENDED := fp-pidbench fpsimd-test \
rdvl-sme rdvl-sve \
sve-test \

View File

@ -3,6 +3,7 @@
#define sa_handler 0
#define sa_mask_sz 8
#define SIGUSR1 10
#define SIGUSR2 12
#define SIGTERM 15
#define SIGINT 2
#define SIGABRT 6

View File

@ -0,0 +1,555 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2022 ARM Limited.
*/
#define _GNU_SOURCE
#define _POSIX_C_SOURCE 199309L
#include <errno.h>
#include <getopt.h>
#include <poll.h>
#include <signal.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/auxv.h>
#include <sys/epoll.h>
#include <sys/prctl.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/wait.h>
#include <asm/hwcap.h>
#include "../../kselftest.h"
#define MAX_VLS 16
struct child_data {
char *name, *output;
pid_t pid;
int stdout;
bool output_seen;
bool exited;
int exit_status;
};
static int epoll_fd;
static struct child_data *children;
static int num_children;
static bool terminate;
static void drain_output(bool flush);
static int num_processors(void)
{
long nproc = sysconf(_SC_NPROCESSORS_CONF);
if (nproc < 0) {
perror("Unable to read number of processors\n");
exit(EXIT_FAILURE);
}
return nproc;
}
static void child_start(struct child_data *child, const char *program)
{
int ret, pipefd[2], i;
struct epoll_event ev;
ret = pipe(pipefd);
if (ret != 0)
ksft_exit_fail_msg("Failed to create stdout pipe: %s (%d)\n",
strerror(errno), errno);
child->pid = fork();
if (child->pid == -1)
ksft_exit_fail_msg("fork() failed: %s (%d)\n",
strerror(errno), errno);
if (!child->pid) {
/*
* In child, replace stdout with the pipe, errors to
* stderr from here as kselftest prints to stdout.
*/
ret = dup2(pipefd[1], 1);
if (ret == -1) {
fprintf(stderr, "dup2() %d\n", errno);
exit(EXIT_FAILURE);
}
/*
* Very dumb mechanism to clean open FDs other than
* stdio. We don't want O_CLOEXEC for the pipes...
*/
for (i = 3; i < 8192; i++)
close(i);
ret = execl(program, program, NULL);
fprintf(stderr, "execl(%s) failed: %d (%s)\n",
program, errno, strerror(errno));
exit(EXIT_FAILURE);
} else {
/*
* In parent, remember the child and close our copy of the
* write side of stdout.
*/
close(pipefd[1]);
child->stdout = pipefd[0];
child->output = NULL;
child->exited = false;
child->output_seen = false;
ev.events = EPOLLIN | EPOLLHUP;
ev.data.ptr = child;
ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, child->stdout, &ev);
if (ret < 0) {
ksft_exit_fail_msg("%s EPOLL_CTL_ADD failed: %s (%d)\n",
child->name, strerror(errno), errno);
}
/*
* Keep output flowing during child startup so logs
* are more timely, can help debugging.
*/
drain_output(false);
}
}
static bool child_output_read(struct child_data *child)
{
char read_data[1024];
char work[1024];
int ret, len, cur_work, cur_read;
ret = read(child->stdout, read_data, sizeof(read_data));
if (ret < 0) {
if (errno == EINTR)
return true;
ksft_print_msg("%s: read() failed: %s (%d)\n",
child->name, strerror(errno),
errno);
return false;
}
len = ret;
child->output_seen = true;
/* Pick up any partial read */
if (child->output) {
strncpy(work, child->output, sizeof(work) - 1);
cur_work = strnlen(work, sizeof(work));
free(child->output);
child->output = NULL;
} else {
cur_work = 0;
}
cur_read = 0;
while (cur_read < len) {
work[cur_work] = read_data[cur_read++];
if (work[cur_work] == '\n') {
work[cur_work] = '\0';
ksft_print_msg("%s: %s\n", child->name, work);
cur_work = 0;
} else {
cur_work++;
}
}
if (cur_work) {
work[cur_work] = '\0';
ret = asprintf(&child->output, "%s", work);
if (ret == -1)
ksft_exit_fail_msg("Out of memory\n");
}
return false;
}
static void child_output(struct child_data *child, uint32_t events,
bool flush)
{
bool read_more;
if (events & EPOLLIN) {
do {
read_more = child_output_read(child);
} while (read_more);
}
if (events & EPOLLHUP) {
close(child->stdout);
child->stdout = -1;
flush = true;
}
if (flush && child->output) {
ksft_print_msg("%s: %s<EOF>\n", child->name, child->output);
free(child->output);
child->output = NULL;
}
}
static void child_tickle(struct child_data *child)
{
if (child->output_seen && !child->exited)
kill(child->pid, SIGUSR2);
}
static void child_stop(struct child_data *child)
{
if (!child->exited)
kill(child->pid, SIGTERM);
}
static void child_cleanup(struct child_data *child)
{
pid_t ret;
int status;
bool fail = false;
if (!child->exited) {
do {
ret = waitpid(child->pid, &status, 0);
if (ret == -1 && errno == EINTR)
continue;
if (ret == -1) {
ksft_print_msg("waitpid(%d) failed: %s (%d)\n",
child->pid, strerror(errno),
errno);
fail = true;
break;
}
} while (!WIFEXITED(status));
child->exit_status = WEXITSTATUS(status);
}
if (!child->output_seen) {
ksft_print_msg("%s no output seen\n", child->name);
fail = true;
}
if (child->exit_status != 0) {
ksft_print_msg("%s exited with error code %d\n",
child->name, child->exit_status);
fail = true;
}
ksft_test_result(!fail, "%s\n", child->name);
}
static void handle_child_signal(int sig, siginfo_t *info, void *context)
{
int i;
bool found = false;
for (i = 0; i < num_children; i++) {
if (children[i].pid == info->si_pid) {
children[i].exited = true;
children[i].exit_status = info->si_status;
found = true;
break;
}
}
if (!found)
ksft_print_msg("SIGCHLD for unknown PID %d with status %d\n",
info->si_pid, info->si_status);
}
static void handle_exit_signal(int sig, siginfo_t *info, void *context)
{
int i;
/* If we're already exiting then don't signal again */
if (terminate)
return;
ksft_print_msg("Got signal, exiting...\n");
terminate = true;
/*
* This should be redundant, the main loop should clean up
* after us, but for safety stop everything we can here.
*/
for (i = 0; i < num_children; i++)
child_stop(&children[i]);
}
static void start_fpsimd(struct child_data *child, int cpu, int copy)
{
int ret;
child_start(child, "./fpsimd-test");
ret = asprintf(&child->name, "FPSIMD-%d-%d", cpu, copy);
if (ret == -1)
ksft_exit_fail_msg("asprintf() failed\n");
ksft_print_msg("Started %s\n", child->name);
}
static void start_sve(struct child_data *child, int vl, int cpu)
{
int ret;
ret = prctl(PR_SVE_SET_VL, vl | PR_SVE_VL_INHERIT);
if (ret < 0)
ksft_exit_fail_msg("Failed to set SVE VL %d\n", vl);
child_start(child, "./sve-test");
ret = asprintf(&child->name, "SVE-VL-%d-%d", vl, cpu);
if (ret == -1)
ksft_exit_fail_msg("asprintf() failed\n");
ksft_print_msg("Started %s\n", child->name);
}
static void start_ssve(struct child_data *child, int vl, int cpu)
{
int ret;
ret = prctl(PR_SME_SET_VL, vl | PR_SME_VL_INHERIT);
if (ret < 0)
ksft_exit_fail_msg("Failed to set SME VL %d\n", ret);
child_start(child, "./ssve-test");
ret = asprintf(&child->name, "SSVE-VL-%d-%d", vl, cpu);
if (ret == -1)
ksft_exit_fail_msg("asprintf() failed\n");
ksft_print_msg("Started %s\n", child->name);
}
static void start_za(struct child_data *child, int vl, int cpu)
{
int ret;
ret = prctl(PR_SME_SET_VL, vl | PR_SVE_VL_INHERIT);
if (ret < 0)
ksft_exit_fail_msg("Failed to set SME VL %d\n", ret);
child_start(child, "./za-test");
ret = asprintf(&child->name, "ZA-VL-%d-%d", vl, cpu);
if (ret == -1)
ksft_exit_fail_msg("asprintf() failed\n");
ksft_print_msg("Started %s\n", child->name);
}
static void probe_vls(int vls[], int *vl_count, int set_vl)
{
unsigned int vq;
int vl;
*vl_count = 0;
for (vq = SVE_VQ_MAX; vq > 0; --vq) {
vl = prctl(set_vl, vq * 16);
if (vl == -1)
ksft_exit_fail_msg("SET_VL failed: %s (%d)\n",
strerror(errno), errno);
vl &= PR_SVE_VL_LEN_MASK;
vq = sve_vq_from_vl(vl);
vls[*vl_count] = vl;
*vl_count += 1;
}
}
/* Handle any pending output without blocking */
static void drain_output(bool flush)
{
struct epoll_event ev;
int ret = 1;
while (ret > 0) {
ret = epoll_wait(epoll_fd, &ev, 1, 0);
if (ret < 0) {
if (errno == EINTR)
continue;
ksft_print_msg("epoll_wait() failed: %s (%d)\n",
strerror(errno), errno);
}
if (ret == 1)
child_output(ev.data.ptr, ev.events, flush);
}
}
static const struct option options[] = {
{ "timeout", required_argument, NULL, 't' },
{ }
};
int main(int argc, char **argv)
{
int ret;
int timeout = 10;
int cpus, tests, i, j, c;
int sve_vl_count, sme_vl_count, fpsimd_per_cpu;
int sve_vls[MAX_VLS], sme_vls[MAX_VLS];
struct epoll_event ev;
struct sigaction sa;
while ((c = getopt_long(argc, argv, "t:", options, NULL)) != -1) {
switch (c) {
case 't':
ret = sscanf(optarg, "%d", &timeout);
if (ret != 1)
ksft_exit_fail_msg("Failed to parse timeout %s\n",
optarg);
break;
default:
ksft_exit_fail_msg("Unknown argument\n");
}
}
cpus = num_processors();
tests = 0;
if (getauxval(AT_HWCAP) & HWCAP_SVE) {
probe_vls(sve_vls, &sve_vl_count, PR_SVE_SET_VL);
tests += sve_vl_count * cpus;
} else {
sve_vl_count = 0;
}
if (getauxval(AT_HWCAP2) & HWCAP2_SME) {
probe_vls(sme_vls, &sme_vl_count, PR_SME_SET_VL);
tests += sme_vl_count * cpus * 2;
} else {
sme_vl_count = 0;
}
/* Force context switching if we only have FPSIMD */
if (!sve_vl_count && !sme_vl_count)
fpsimd_per_cpu = 2;
else
fpsimd_per_cpu = 1;
tests += cpus * fpsimd_per_cpu;
ksft_print_header();
ksft_set_plan(tests);
ksft_print_msg("%d CPUs, %d SVE VLs, %d SME VLs\n",
cpus, sve_vl_count, sme_vl_count);
if (timeout > 0)
ksft_print_msg("Will run for %ds\n", timeout);
else
ksft_print_msg("Will run until terminated\n");
children = calloc(sizeof(*children), tests);
if (!children)
ksft_exit_fail_msg("Unable to allocate child data\n");
ret = epoll_create1(EPOLL_CLOEXEC);
if (ret < 0)
ksft_exit_fail_msg("epoll_create1() failed: %s (%d)\n",
strerror(errno), ret);
epoll_fd = ret;
/* Get signal handers ready before we start any children */
memset(&sa, 0, sizeof(sa));
sa.sa_sigaction = handle_exit_signal;
sa.sa_flags = SA_RESTART | SA_SIGINFO;
sigemptyset(&sa.sa_mask);
ret = sigaction(SIGINT, &sa, NULL);
if (ret < 0)
ksft_print_msg("Failed to install SIGINT handler: %s (%d)\n",
strerror(errno), errno);
ret = sigaction(SIGTERM, &sa, NULL);
if (ret < 0)
ksft_print_msg("Failed to install SIGTERM handler: %s (%d)\n",
strerror(errno), errno);
sa.sa_sigaction = handle_child_signal;
ret = sigaction(SIGCHLD, &sa, NULL);
if (ret < 0)
ksft_print_msg("Failed to install SIGCHLD handler: %s (%d)\n",
strerror(errno), errno);
for (i = 0; i < cpus; i++) {
for (j = 0; j < fpsimd_per_cpu; j++)
start_fpsimd(&children[num_children++], i, j);
for (j = 0; j < sve_vl_count; j++)
start_sve(&children[num_children++], sve_vls[j], i);
for (j = 0; j < sme_vl_count; j++) {
start_ssve(&children[num_children++], sme_vls[j], i);
start_za(&children[num_children++], sme_vls[j], i);
}
}
for (;;) {
/* Did we get a signal asking us to exit? */
if (terminate)
break;
/*
* Timeout is counted in seconds with no output, the
* tests print during startup then are silent when
* running so this should ensure they all ran enough
* to install the signal handler, this is especially
* useful in emulation where we will both be slow and
* likely to have a large set of VLs.
*/
ret = epoll_wait(epoll_fd, &ev, 1, 1000);
if (ret < 0) {
if (errno == EINTR)
continue;
ksft_exit_fail_msg("epoll_wait() failed: %s (%d)\n",
strerror(errno), errno);
}
/* Output? */
if (ret == 1) {
child_output(ev.data.ptr, ev.events, false);
continue;
}
/* Otherwise epoll_wait() timed out */
for (i = 0; i < num_children; i++)
child_tickle(&children[i]);
/* Negative timeout means run indefinitely */
if (timeout < 0)
continue;
if (--timeout == 0)
break;
}
ksft_print_msg("Finishing up...\n");
terminate = true;
for (i = 0; i < tests; i++)
child_stop(&children[i]);
drain_output(false);
for (i = 0; i < tests; i++)
child_cleanup(&children[i]);
drain_output(true);
ksft_print_cnts();
return 0;
}

View File

@ -151,6 +151,15 @@ function irritator_handler
ret
endfunction
function tickle_handler
// Increment the signal count (x23):
ldr x0, [x2, #ucontext_regs + 8 * 23]
add x0, x0, #1
str x0, [x2, #ucontext_regs + 8 * 23]
ret
endfunction
function terminate_handler
mov w21, w0
mov x20, x2
@ -207,6 +216,30 @@ endfunction
.globl _start
function _start
_start:
mov x23, #0 // signal count
mov w0, #SIGINT
adr x1, terminate_handler
mov w2, #SA_SIGINFO
bl setsignal
mov w0, #SIGTERM
adr x1, terminate_handler
mov w2, #SA_SIGINFO
bl setsignal
mov w0, #SIGUSR1
adr x1, irritator_handler
mov w2, #SA_SIGINFO
orr w2, w2, #SA_NODEFER
bl setsignal
mov w0, #SIGUSR2
adr x1, tickle_handler
mov w2, #SA_SIGINFO
orr w2, w2, #SA_NODEFER
bl setsignal
// Sanity-check and report the vector length
mov x19, #128
@ -237,24 +270,6 @@ _start:
mov x0, x20
bl putdecn
mov x23, #0 // Irritation signal count
mov w0, #SIGINT
adr x1, terminate_handler
mov w2, #SA_SIGINFO
bl setsignal
mov w0, #SIGTERM
adr x1, terminate_handler
mov w2, #SA_SIGINFO
bl setsignal
mov w0, #SIGUSR1
adr x1, irritator_handler
mov w2, #SA_SIGINFO
orr w2, w2, #SA_NODEFER
bl setsignal
mov x22, #0 // generation number, increments per iteration
.Ltest_loop:

View File

@ -314,6 +314,15 @@ function irritator_handler
ret
endfunction
function tickle_handler
// Increment the signal count (x23):
ldr x0, [x2, #ucontext_regs + 8 * 23]
add x0, x0, #1
str x0, [x2, #ucontext_regs + 8 * 23]
ret
endfunction
function terminate_handler
mov w21, w0
mov x20, x2
@ -370,6 +379,30 @@ endfunction
.globl _start
function _start
_start:
mov x23, #0 // Irritation signal count
mov w0, #SIGINT
adr x1, terminate_handler
mov w2, #SA_SIGINFO
bl setsignal
mov w0, #SIGTERM
adr x1, terminate_handler
mov w2, #SA_SIGINFO
bl setsignal
mov w0, #SIGUSR1
adr x1, irritator_handler
mov w2, #SA_SIGINFO
orr w2, w2, #SA_NODEFER
bl setsignal
mov w0, #SIGUSR2
adr x1, tickle_handler
mov w2, #SA_SIGINFO
orr w2, w2, #SA_NODEFER
bl setsignal
#ifdef SSVE
puts "Streaming mode "
smstart_sm
@ -405,24 +438,6 @@ _start:
mov x0, x20
bl putdecn
mov x23, #0 // Irritation signal count
mov w0, #SIGINT
adr x1, terminate_handler
mov w2, #SA_SIGINFO
bl setsignal
mov w0, #SIGTERM
adr x1, terminate_handler
mov w2, #SA_SIGINFO
bl setsignal
mov w0, #SIGUSR1
adr x1, irritator_handler
mov w2, #SA_SIGINFO
orr w2, w2, #SA_NODEFER
bl setsignal
#ifdef SSVE
smstart_sm // syscalls will have exited streaming mode
#endif

View File

@ -167,6 +167,15 @@ function irritator_handler
ret
endfunction
function tickle_handler
// Increment the signal count (x23):
ldr x0, [x2, #ucontext_regs + 8 * 23]
add x0, x0, #1
str x0, [x2, #ucontext_regs + 8 * 23]
ret
endfunction
function terminate_handler
mov w21, w0
mov x20, x2
@ -223,6 +232,30 @@ endfunction
.globl _start
function _start
_start:
mov x23, #0 // signal count
mov w0, #SIGINT
adr x1, terminate_handler
mov w2, #SA_SIGINFO
bl setsignal
mov w0, #SIGTERM
adr x1, terminate_handler
mov w2, #SA_SIGINFO
bl setsignal
mov w0, #SIGUSR1
adr x1, irritator_handler
mov w2, #SA_SIGINFO
orr w2, w2, #SA_NODEFER
bl setsignal
mov w0, #SIGUSR2
adr x1, tickle_handler
mov w2, #SA_SIGINFO
orr w2, w2, #SA_NODEFER
bl setsignal
puts "Streaming mode "
smstart_za
@ -255,24 +288,6 @@ _start:
mov x0, x20
bl putdecn
mov x23, #0 // Irritation signal count
mov w0, #SIGINT
adr x1, terminate_handler
mov w2, #SA_SIGINFO
bl setsignal
mov w0, #SIGTERM
adr x1, terminate_handler
mov w2, #SA_SIGINFO
bl setsignal
mov w0, #SIGUSR1
adr x1, irritator_handler
mov w2, #SA_SIGINFO
orr w2, w2, #SA_NODEFER
bl setsignal
mov x22, #0 // generation number, increments per iteration
.Ltest_loop:
rdsvl 0, 8
@ -287,12 +302,7 @@ _start:
subs x21, x21, #1
b.ne 0b
and x8, x22, #127 // Every 128 interations...
cbz x8, 0f
mov x8, #__NR_getpid // (otherwise minimal syscall)
b 1f
0:
mov x8, #__NR_sched_yield // ...encourage preemption
mov x8, #__NR_sched_yield // encourage preemption
1:
svc #0

View File

@ -11,11 +11,8 @@ LDFLAGS += -pthread
SRCS := $(filter-out mte_common_util.c,$(wildcard *.c))
PROGS := $(patsubst %.c,%,$(SRCS))
#Add mte compiler option
CFLAGS += -march=armv8.5-a+memtag
#check if the compiler works well
mte_cc_support := $(shell if ($(CC) $(CFLAGS) -E -x c /dev/null -o /dev/null 2>&1) then echo "1"; fi)
mte_cc_support := $(shell if ($(CC) $(CFLAGS) -march=armv8.5-a+memtag -E -x c /dev/null -o /dev/null 2>&1) then echo "1"; fi)
ifeq ($(mte_cc_support),1)
# Generated binaries to be installed by top KSFT script

View File

@ -3,6 +3,8 @@
#include "mte_def.h"
.arch armv8.5-a+memtag
#define ENTRY(name) \
.globl name ;\
.p2align 2;\

View File

@ -165,15 +165,64 @@ static bool handle_signal_ok(struct tdescr *td,
}
static bool handle_signal_copyctx(struct tdescr *td,
siginfo_t *si, void *uc)
siginfo_t *si, void *uc_in)
{
ucontext_t *uc = uc_in;
struct _aarch64_ctx *head;
struct extra_context *extra, *copied_extra;
size_t offset = 0;
size_t to_copy;
ASSERT_GOOD_CONTEXT(uc);
/* Mangling PC to avoid loops on original BRK instr */
((ucontext_t *)uc)->uc_mcontext.pc += 4;
memcpy(td->live_uc, uc, td->live_sz);
ASSERT_GOOD_CONTEXT(td->live_uc);
uc->uc_mcontext.pc += 4;
/*
* Check for an preserve any extra data too with fixups.
*/
head = (struct _aarch64_ctx *)uc->uc_mcontext.__reserved;
head = get_header(head, EXTRA_MAGIC, td->live_sz, &offset);
if (head) {
extra = (struct extra_context *)head;
/*
* The extra buffer must be immediately after the
* extra_context and a 16 byte terminator. Include it
* in the copy, this was previously validated in
* ASSERT_GOOD_CONTEXT().
*/
to_copy = offset + sizeof(struct extra_context) + 16 +
extra->size;
copied_extra = (struct extra_context *)&(td->live_uc->uc_mcontext.__reserved[offset]);
} else {
copied_extra = NULL;
to_copy = sizeof(ucontext_t);
}
if (to_copy > td->live_sz) {
fprintf(stderr,
"Not enough space to grab context, %lu/%lu bytes\n",
td->live_sz, to_copy);
return false;
}
memcpy(td->live_uc, uc, to_copy);
/*
* If there was any EXTRA_CONTEXT fix up the size to be the
* struct extra_context and the following terminator record,
* this means that the rest of the code does not need to have
* special handling for the record and we don't need to fix up
* datap for the new location.
*/
if (copied_extra)
copied_extra->head.size = sizeof(*copied_extra) + 16;
td->live_uc_valid = 1;
fprintf(stderr,
"GOOD CONTEXT grabbed from sig_copyctx handler\n");
"%lu byte GOOD CONTEXT grabbed from sig_copyctx handler\n",
to_copy);
return true;
}

View File

@ -56,7 +56,8 @@ static inline bool feats_ok(struct tdescr *td)
* at sizeof(ucontext_t).
*/
static __always_inline bool get_current_context(struct tdescr *td,
ucontext_t *dest_uc)
ucontext_t *dest_uc,
size_t dest_sz)
{
static volatile bool seen_already;
@ -64,7 +65,7 @@ static __always_inline bool get_current_context(struct tdescr *td,
/* it's a genuine invocation..reinit */
seen_already = 0;
td->live_uc_valid = 0;
td->live_sz = sizeof(*dest_uc);
td->live_sz = dest_sz;
memset(dest_uc, 0x00, td->live_sz);
td->live_uc = dest_uc;
/*

View File

@ -21,7 +21,7 @@ static int fake_sigreturn_bad_magic_run(struct tdescr *td,
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))
if (!get_current_context(td, &sf.uc, sizeof(sf.uc)))
return 1;
/* need at least 2*HDR_SZ space: KSFT_BAD_MAGIC + terminator. */

View File

@ -24,7 +24,7 @@ static int fake_sigreturn_bad_size_run(struct tdescr *td,
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))
if (!get_current_context(td, &sf.uc, sizeof(sf.uc)))
return 1;
resv_sz = GET_SF_RESV_SIZE(sf);

View File

@ -21,7 +21,7 @@ static int fake_sigreturn_bad_size_for_magic0_run(struct tdescr *td,
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))
if (!get_current_context(td, &sf.uc, sizeof(sf.uc)))
return 1;
/* at least HDR_SZ for the badly sized terminator. */

View File

@ -21,7 +21,7 @@ static int fake_sigreturn_duplicated_fpsimd_run(struct tdescr *td,
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))
if (!get_current_context(td, &sf.uc, sizeof(sf.uc)))
return 1;
head = get_starting_head(shead, sizeof(struct fpsimd_context) + HDR_SZ,

View File

@ -19,7 +19,7 @@ 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))
if (!get_current_context(td, &sf.uc, sizeof(sf.uc)))
return 1;
/* Forcing sigframe on misaligned SP (16 + 3) */

View File

@ -23,7 +23,7 @@ static int fake_sigreturn_missing_fpsimd_run(struct tdescr *td,
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))
if (!get_current_context(td, &sf.uc, sizeof(sf.uc)))
return 1;
resv_sz = GET_SF_RESV_SIZE(sf);

View File

@ -54,7 +54,7 @@ static int fake_sigreturn_ssve_change_vl(struct tdescr *td,
struct sve_context *sve;
/* Get a signal context with a SME ZA frame in it */
if (!get_current_context(td, &sf.uc))
if (!get_current_context(td, &sf.uc, sizeof(sf.uc)))
return 1;
resv_sz = GET_SF_RESV_SIZE(sf);

View File

@ -56,7 +56,7 @@ static int fake_sigreturn_sve_change_vl(struct tdescr *td,
struct sve_context *sve;
/* Get a signal context with a SVE frame in it */
if (!get_current_context(td, &sf.uc))
if (!get_current_context(td, &sf.uc, sizeof(sf.uc)))
return 1;
resv_sz = GET_SF_RESV_SIZE(sf);

View File

@ -34,7 +34,7 @@ static int sme_vl(struct tdescr *td, siginfo_t *si, ucontext_t *uc)
struct za_context *za;
/* Get a signal context which should have a ZA frame in it */
if (!get_current_context(td, &sf.uc))
if (!get_current_context(td, &sf.uc, sizeof(sf.uc)))
return 1;
resv_sz = GET_SF_RESV_SIZE(sf);

View File

@ -13,7 +13,10 @@
#include "test_signals_utils.h"
#include "testcases.h"
struct fake_sigframe sf;
static union {
ucontext_t uc;
char buf[1024 * 64];
} context;
static unsigned int vls[SVE_VQ_MAX];
unsigned int nvls = 0;
@ -55,8 +58,8 @@ static void setup_ssve_regs(void)
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);
size_t offset;
struct _aarch64_ctx *head = GET_BUF_RESV_HEAD(context);
struct sve_context *ssve;
int ret;
@ -73,11 +76,11 @@ static int do_one_sme_vl(struct tdescr *td, siginfo_t *si, ucontext_t *uc,
* in it.
*/
setup_ssve_regs();
if (!get_current_context(td, &sf.uc))
if (!get_current_context(td, &context.uc, sizeof(context)))
return 1;
resv_sz = GET_SF_RESV_SIZE(sf);
head = get_header(head, SVE_MAGIC, resv_sz, &offset);
head = get_header(head, SVE_MAGIC, GET_BUF_RESV_SIZE(context),
&offset);
if (!head) {
fprintf(stderr, "No SVE context\n");
return 1;
@ -101,16 +104,6 @@ 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) {
printf("Skipping VL %u due to stack size\n", vls[i]);
continue;
}
if (do_one_sme_vl(td, si, uc, vls[i]))
return 1;
}

View File

@ -13,7 +13,10 @@
#include "test_signals_utils.h"
#include "testcases.h"
struct fake_sigframe sf;
static union {
ucontext_t uc;
char buf[1024 * 64];
} context;
static unsigned int vls[SVE_VQ_MAX];
unsigned int nvls = 0;
@ -55,8 +58,8 @@ static void setup_sve_regs(void)
static int do_one_sve_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);
size_t offset;
struct _aarch64_ctx *head = GET_BUF_RESV_HEAD(context);
struct sve_context *sve;
fprintf(stderr, "Testing VL %d\n", vl);
@ -71,11 +74,11 @@ static int do_one_sve_vl(struct tdescr *td, siginfo_t *si, ucontext_t *uc,
* in it.
*/
setup_sve_regs();
if (!get_current_context(td, &sf.uc))
if (!get_current_context(td, &context.uc, sizeof(context)))
return 1;
resv_sz = GET_SF_RESV_SIZE(sf);
head = get_header(head, SVE_MAGIC, resv_sz, &offset);
head = get_header(head, SVE_MAGIC, GET_BUF_RESV_SIZE(context),
&offset);
if (!head) {
fprintf(stderr, "No SVE context\n");
return 1;
@ -99,14 +102,6 @@ static int sve_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;
if (do_one_sve_vl(td, si, uc, vls[i]))
return 1;
}

View File

@ -34,7 +34,7 @@ static int sve_vl(struct tdescr *td, siginfo_t *si, ucontext_t *uc)
struct sve_context *sve;
/* Get a signal context which should have a SVE frame in it */
if (!get_current_context(td, &sf.uc))
if (!get_current_context(td, &sf.uc, sizeof(sf.uc)))
return 1;
resv_sz = GET_SF_RESV_SIZE(sf);

View File

@ -25,7 +25,8 @@ struct _aarch64_ctx *get_header(struct _aarch64_ctx *head, uint32_t magic,
return found;
}
bool validate_extra_context(struct extra_context *extra, char **err)
bool validate_extra_context(struct extra_context *extra, char **err,
void **extra_data, size_t *extra_size)
{
struct _aarch64_ctx *term;
@ -33,7 +34,7 @@ bool validate_extra_context(struct extra_context *extra, char **err)
return false;
fprintf(stderr, "Validating EXTRA...\n");
term = GET_RESV_NEXT_HEAD(extra);
term = GET_RESV_NEXT_HEAD(&extra->head);
if (!term || term->magic || term->size) {
*err = "Missing terminator after EXTRA context";
return false;
@ -42,11 +43,14 @@ bool validate_extra_context(struct extra_context *extra, char **err)
*err = "Extra DATAP misaligned";
else if (extra->size & 0x0fUL)
*err = "Extra SIZE misaligned";
else if (extra->datap != (uint64_t)term + sizeof(*term))
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;
}
@ -105,11 +109,14 @@ 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;
struct extra_context *extra = NULL;
struct sve_context *sve = NULL;
struct za_context *za = NULL;
struct _aarch64_ctx *head =
(struct _aarch64_ctx *)uc->uc_mcontext.__reserved;
void *extra_data = NULL;
size_t extra_sz = 0;
if (!err)
return false;
@ -120,12 +127,24 @@ bool validate_reserved(ucontext_t *uc, size_t resv_sz, char **err)
return false;
}
new_flags = 0;
switch (head->magic) {
case 0:
if (head->size)
if (head->size) {
*err = "Bad size for terminator";
else
} 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)
@ -133,7 +152,7 @@ bool validate_reserved(ucontext_t *uc, size_t resv_sz, char **err)
else if (head->size !=
sizeof(struct fpsimd_context))
*err = "Bad size for fpsimd_context";
flags |= FPSIMD_CTX;
new_flags |= FPSIMD_CTX;
break;
case ESR_MAGIC:
if (head->size != sizeof(struct esr_context))
@ -144,14 +163,14 @@ bool validate_reserved(ucontext_t *uc, size_t resv_sz, char **err)
*err = "Multiple SVE_MAGIC";
/* Size is validated in validate_sve_context() */
sve = (struct sve_context *)head;
flags |= SVE_CTX;
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;
flags |= ZA_CTX;
new_flags |= ZA_CTX;
break;
case EXTRA_MAGIC:
if (flags & EXTRA_CTX)
@ -159,7 +178,7 @@ bool validate_reserved(ucontext_t *uc, size_t resv_sz, char **err)
else if (head->size !=
sizeof(struct extra_context))
*err = "Bad size for extra_context";
flags |= EXTRA_CTX;
new_flags |= EXTRA_CTX;
extra = (struct extra_context *)head;
break;
case KSFT_BAD_MAGIC:
@ -192,16 +211,19 @@ bool validate_reserved(ucontext_t *uc, size_t resv_sz, char **err)
return false;
}
if (flags & EXTRA_CTX)
if (!validate_extra_context(extra, err))
if (new_flags & EXTRA_CTX)
if (!validate_extra_context(extra, err,
&extra_data, &extra_sz))
return false;
if (flags & SVE_CTX)
if (new_flags & SVE_CTX)
if (!validate_sve_context(sve, err))
return false;
if (flags & ZA_CTX)
if (new_flags & ZA_CTX)
if (!validate_za_context(za, err))
return false;
flags |= new_flags;
head = GET_RESV_NEXT_HEAD(head);
}

View File

@ -30,6 +30,13 @@
#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)
@ -79,8 +86,6 @@ struct fake_sigframe {
bool validate_reserved(ucontext_t *uc, size_t resv_sz, char **err);
bool validate_extra_context(struct extra_context *extra, char **err);
struct _aarch64_ctx *get_header(struct _aarch64_ctx *head, uint32_t magic,
size_t resv_sz, size_t *offset);

View File

@ -0,0 +1,119 @@
// 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 <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 SME_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;
/* 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 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,
};

View File

@ -13,7 +13,10 @@
#include "test_signals_utils.h"
#include "testcases.h"
struct fake_sigframe sf;
static union {
ucontext_t uc;
char buf[1024 * 128];
} context;
static unsigned int vls[SVE_VQ_MAX];
unsigned int nvls = 0;
@ -22,10 +25,10 @@ static bool sme_get_vls(struct tdescr *td)
int vq, vl;
/*
* Enumerate up to SVE_VQ_MAX vector lengths
* Enumerate up to SME_VQ_MAX vector lengths
*/
for (vq = SVE_VQ_MAX; vq > 0; --vq) {
vl = prctl(PR_SVE_SET_VL, vq * 16);
vl = prctl(PR_SME_SET_VL, vq * 16);
if (vl == -1)
return false;
@ -52,11 +55,13 @@ static void setup_za_regs(void)
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 resv_sz, offset;
struct _aarch64_ctx *head = GET_SF_RESV_HEAD(sf);
size_t offset;
struct _aarch64_ctx *head = GET_BUF_RESV_HEAD(context);
struct za_context *za;
fprintf(stderr, "Testing VL %d\n", vl);
@ -71,11 +76,10 @@ static int do_one_sme_vl(struct tdescr *td, siginfo_t *si, ucontext_t *uc,
* in it.
*/
setup_za_regs();
if (!get_current_context(td, &sf.uc))
if (!get_current_context(td, &context.uc, sizeof(context)))
return 1;
resv_sz = GET_SF_RESV_SIZE(sf);
head = get_header(head, ZA_MAGIC, resv_sz, &offset);
head = get_header(head, ZA_MAGIC, GET_BUF_RESV_SIZE(context), &offset);
if (!head) {
fprintf(stderr, "No ZA context\n");
return 1;
@ -87,10 +91,22 @@ static int do_one_sme_vl(struct tdescr *td, siginfo_t *si, ucontext_t *uc,
return 1;
}
/* The actual size validation is done in get_current_context() */
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;
}
return 0;
}
@ -99,16 +115,6 @@ 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] > 32) {
printf("Skipping VL %u due to stack size\n", vls[i]);
continue;
}
if (do_one_sme_vl(td, si, uc, vls[i]))
return 1;
}