Merge tag 'arm64-upstream' of git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux

Pull arm64 updates from Catalin Marinas:

 - Support for 32-bit tasks on asymmetric AArch32 systems (on top of the
   scheduler changes merged via the tip tree).

 - More entry.S clean-ups and conversion to C.

 - MTE updates: allow a preferred tag checking mode to be set per CPU
   (the overhead of synchronous mode is smaller for some CPUs than
   others); optimisations for kernel entry/exit path; optionally disable
   MTE on the kernel command line.

 - Kselftest improvements for SVE and signal handling, PtrAuth.

 - Fix unlikely race where a TLBI could use stale ASID on an ASID
   roll-over (found by inspection).

 - Miscellaneous fixes: disable trapping of PMSNEVFR_EL1 to higher
   exception levels; drop unnecessary sigdelsetmask() call in the
   signal32 handling; remove BUG_ON when failing to allocate SVE state
   (just signal the process); SYM_CODE annotations.

 - Other trivial clean-ups: use macros instead of magic numbers, remove
   redundant returns, typos.

* tag 'arm64-upstream' of git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux: (56 commits)
  arm64: Do not trap PMSNEVFR_EL1
  arm64: mm: fix comment typo of pud_offset_phys()
  arm64: signal32: Drop pointless call to sigdelsetmask()
  arm64/sve: Better handle failure to allocate SVE register storage
  arm64: Document the requirement for SCR_EL3.HCE
  arm64: head: avoid over-mapping in map_memory
  arm64/sve: Add a comment documenting the binutils needed for SVE asm
  arm64/sve: Add some comments for sve_save/load_state()
  kselftest/arm64: signal: Add a TODO list for signal handling tests
  kselftest/arm64: signal: Add test case for SVE register state in signals
  kselftest/arm64: signal: Verify that signals can't change the SVE vector length
  kselftest/arm64: signal: Check SVE signal frame shows expected vector length
  kselftest/arm64: signal: Support signal frames with SVE register data
  kselftest/arm64: signal: Add SVE to the set of features we can check for
  arm64: replace in_irq() with in_hardirq()
  kselftest/arm64: pac: Fix skipping of tests on systems without PAC
  Documentation: arm64: describe asymmetric 32-bit support
  arm64: Remove logic to kill 32-bit tasks on 64-bit-only cores
  arm64: Hook up cmdline parameter to allow mismatched 32-bit EL0
  arm64: Advertise CPUs capable of running 32-bit applications in sysfs
  ...
This commit is contained in:
Linus Torvalds
2021-09-01 15:04:29 -07:00
69 changed files with 1842 additions and 463 deletions

View File

@@ -1,5 +1,7 @@
fpsimd-test
rdvl-sve
sve-probe-vls
sve-ptrace
sve-test
vec-syscfg
vlset

View File

@@ -1,17 +1,22 @@
# SPDX-License-Identifier: GPL-2.0
CFLAGS += -I../../../../../usr/include/
TEST_GEN_PROGS := sve-ptrace sve-probe-vls
TEST_PROGS_EXTENDED := fpsimd-test fpsimd-stress sve-test sve-stress vlset
TEST_GEN_PROGS := sve-ptrace sve-probe-vls vec-syscfg
TEST_PROGS_EXTENDED := fpsimd-test fpsimd-stress \
rdvl-sve \
sve-test sve-stress \
vlset
all: $(TEST_GEN_PROGS) $(TEST_PROGS_EXTENDED)
fpsimd-test: fpsimd-test.o
$(CC) -nostdlib $^ -o $@
rdvl-sve: rdvl-sve.o rdvl.o
sve-ptrace: sve-ptrace.o sve-ptrace-asm.o
sve-probe-vls: sve-probe-vls.o
sve-probe-vls: sve-probe-vls.o rdvl.o
sve-test: sve-test.o
$(CC) -nostdlib $^ -o $@
vec-syscfg: vec-syscfg.o rdvl.o
vlset: vlset.o
include ../../lib.mk

View File

@@ -0,0 +1,4 @@
- Test unsupported values in the ABIs.
- More coverage for ptrace (eg, vector length conversions).
- Coverage for signals.
- Test PR_SVE_VL_INHERITY after a double fork.

View File

@@ -0,0 +1,14 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <stdio.h>
#include "rdvl.h"
int main(void)
{
int vl = rdvl_sve();
printf("%d\n", vl);
return 0;
}

View File

@@ -0,0 +1,10 @@
// SPDX-License-Identifier: GPL-2.0-only
// Copyright (C) 2021 ARM Limited.
.arch_extension sve
.globl rdvl_sve
rdvl_sve:
hint 34 // BTI C
rdvl x0, #1
ret

View File

@@ -0,0 +1,8 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef RDVL_H
#define RDVL_H
int rdvl_sve(void);
#endif

View File

@@ -13,6 +13,7 @@
#include <asm/sigcontext.h>
#include "../../kselftest.h"
#include "rdvl.h"
int main(int argc, char **argv)
{
@@ -38,6 +39,10 @@ int main(int argc, char **argv)
vl &= PR_SVE_VL_LEN_MASK;
if (rdvl_sve() != vl)
ksft_exit_fail_msg("PR_SVE_SET_VL reports %d, RDVL %d\n",
vl, rdvl_sve());
if (!sve_vl_valid(vl))
ksft_exit_fail_msg("VL %d invalid\n", vl);
vq = sve_vq_from_vl(vl);

View File

@@ -0,0 +1,593 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2021 ARM Limited.
* Original author: Mark Brown <broonie@kernel.org>
*/
#include <assert.h>
#include <errno.h>
#include <fcntl.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 <sys/types.h>
#include <sys/wait.h>
#include <asm/sigcontext.h>
#include <asm/hwcap.h>
#include "../../kselftest.h"
#include "rdvl.h"
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
#define ARCH_MIN_VL SVE_VL_MIN
struct vec_data {
const char *name;
unsigned long hwcap_type;
unsigned long hwcap;
const char *rdvl_binary;
int (*rdvl)(void);
int prctl_get;
int prctl_set;
const char *default_vl_file;
int default_vl;
int min_vl;
int max_vl;
};
static struct vec_data vec_data[] = {
{
.name = "SVE",
.hwcap_type = AT_HWCAP,
.hwcap = HWCAP_SVE,
.rdvl = rdvl_sve,
.rdvl_binary = "./rdvl-sve",
.prctl_get = PR_SVE_GET_VL,
.prctl_set = PR_SVE_SET_VL,
.default_vl_file = "/proc/sys/abi/sve_default_vector_length",
},
};
static int stdio_read_integer(FILE *f, const char *what, int *val)
{
int n = 0;
int ret;
ret = fscanf(f, "%d%*1[\n]%n", val, &n);
if (ret < 1 || n < 1) {
ksft_print_msg("failed to parse integer from %s\n", what);
return -1;
}
return 0;
}
/* Start a new process and return the vector length it sees */
static int get_child_rdvl(struct vec_data *data)
{
FILE *out;
int pipefd[2];
pid_t pid, child;
int read_vl, ret;
ret = pipe(pipefd);
if (ret == -1) {
ksft_print_msg("pipe() failed: %d (%s)\n",
errno, strerror(errno));
return -1;
}
fflush(stdout);
child = fork();
if (child == -1) {
ksft_print_msg("fork() failed: %d (%s)\n",
errno, strerror(errno));
close(pipefd[0]);
close(pipefd[1]);
return -1;
}
/* Child: put vector length on the pipe */
if (child == 0) {
/*
* 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);
}
/* exec() a new binary which puts the VL on stdout */
ret = execl(data->rdvl_binary, data->rdvl_binary, NULL);
fprintf(stderr, "execl(%s) failed: %d\n",
data->rdvl_binary, errno, strerror(errno));
exit(EXIT_FAILURE);
}
close(pipefd[1]);
/* Parent; wait for the exit status from the child & verify it */
do {
pid = wait(&ret);
if (pid == -1) {
ksft_print_msg("wait() failed: %d (%s)\n",
errno, strerror(errno));
close(pipefd[0]);
return -1;
}
} while (pid != child);
assert(pid == child);
if (!WIFEXITED(ret)) {
ksft_print_msg("child exited abnormally\n");
close(pipefd[0]);
return -1;
}
if (WEXITSTATUS(ret) != 0) {
ksft_print_msg("child returned error %d\n",
WEXITSTATUS(ret));
close(pipefd[0]);
return -1;
}
out = fdopen(pipefd[0], "r");
if (!out) {
ksft_print_msg("failed to open child stdout\n");
close(pipefd[0]);
return -1;
}
ret = stdio_read_integer(out, "child", &read_vl);
fclose(out);
if (ret != 0)
return ret;
return read_vl;
}
static int file_read_integer(const char *name, int *val)
{
FILE *f;
int ret;
f = fopen(name, "r");
if (!f) {
ksft_test_result_fail("Unable to open %s: %d (%s)\n",
name, errno,
strerror(errno));
return -1;
}
ret = stdio_read_integer(f, name, val);
fclose(f);
return ret;
}
static int file_write_integer(const char *name, int val)
{
FILE *f;
int ret;
f = fopen(name, "w");
if (!f) {
ksft_test_result_fail("Unable to open %s: %d (%s)\n",
name, errno,
strerror(errno));
return -1;
}
fprintf(f, "%d", val);
fclose(f);
if (ret < 0) {
ksft_test_result_fail("Error writing %d to %s\n",
val, name);
return -1;
}
return 0;
}
/*
* Verify that we can read the default VL via proc, checking that it
* is set in a freshly spawned child.
*/
static void proc_read_default(struct vec_data *data)
{
int default_vl, child_vl, ret;
ret = file_read_integer(data->default_vl_file, &default_vl);
if (ret != 0)
return;
/* Is this the actual default seen by new processes? */
child_vl = get_child_rdvl(data);
if (child_vl != default_vl) {
ksft_test_result_fail("%s is %d but child VL is %d\n",
data->default_vl_file,
default_vl, child_vl);
return;
}
ksft_test_result_pass("%s default vector length %d\n", data->name,
default_vl);
data->default_vl = default_vl;
}
/* Verify that we can write a minimum value and have it take effect */
static void proc_write_min(struct vec_data *data)
{
int ret, new_default, child_vl;
if (geteuid() != 0) {
ksft_test_result_skip("Need to be root to write to /proc\n");
return;
}
ret = file_write_integer(data->default_vl_file, ARCH_MIN_VL);
if (ret != 0)
return;
/* What was the new value? */
ret = file_read_integer(data->default_vl_file, &new_default);
if (ret != 0)
return;
/* Did it take effect in a new process? */
child_vl = get_child_rdvl(data);
if (child_vl != new_default) {
ksft_test_result_fail("%s is %d but child VL is %d\n",
data->default_vl_file,
new_default, child_vl);
return;
}
ksft_test_result_pass("%s minimum vector length %d\n", data->name,
new_default);
data->min_vl = new_default;
file_write_integer(data->default_vl_file, data->default_vl);
}
/* Verify that we can write a maximum value and have it take effect */
static void proc_write_max(struct vec_data *data)
{
int ret, new_default, child_vl;
if (geteuid() != 0) {
ksft_test_result_skip("Need to be root to write to /proc\n");
return;
}
/* -1 is accepted by the /proc interface as the maximum VL */
ret = file_write_integer(data->default_vl_file, -1);
if (ret != 0)
return;
/* What was the new value? */
ret = file_read_integer(data->default_vl_file, &new_default);
if (ret != 0)
return;
/* Did it take effect in a new process? */
child_vl = get_child_rdvl(data);
if (child_vl != new_default) {
ksft_test_result_fail("%s is %d but child VL is %d\n",
data->default_vl_file,
new_default, child_vl);
return;
}
ksft_test_result_pass("%s maximum vector length %d\n", data->name,
new_default);
data->max_vl = new_default;
file_write_integer(data->default_vl_file, data->default_vl);
}
/* Can we read back a VL from prctl? */
static void prctl_get(struct vec_data *data)
{
int ret;
ret = prctl(data->prctl_get);
if (ret == -1) {
ksft_test_result_fail("%s prctl() read failed: %d (%s)\n",
data->name, errno, strerror(errno));
return;
}
/* Mask out any flags */
ret &= PR_SVE_VL_LEN_MASK;
/* Is that what we can read back directly? */
if (ret == data->rdvl())
ksft_test_result_pass("%s current VL is %d\n",
data->name, ret);
else
ksft_test_result_fail("%s prctl() VL %d but RDVL is %d\n",
data->name, ret, data->rdvl());
}
/* Does the prctl let us set the VL we already have? */
static void prctl_set_same(struct vec_data *data)
{
int cur_vl = data->rdvl();
int ret;
ret = prctl(data->prctl_set, cur_vl);
if (ret < 0) {
ksft_test_result_fail("%s prctl set failed: %d (%s)\n",
data->name, errno, strerror(errno));
return;
}
if (cur_vl != data->rdvl())
ksft_test_result_pass("%s current VL is %d\n",
data->name, ret);
else
ksft_test_result_fail("%s prctl() VL %d but RDVL is %d\n",
data->name, ret, data->rdvl());
}
/* Can we set a new VL for this process? */
static void prctl_set(struct vec_data *data)
{
int ret;
if (data->min_vl == data->max_vl) {
ksft_test_result_skip("%s only one VL supported\n",
data->name);
return;
}
/* Try to set the minimum VL */
ret = prctl(data->prctl_set, data->min_vl);
if (ret < 0) {
ksft_test_result_fail("%s prctl set failed for %d: %d (%s)\n",
data->name, data->min_vl,
errno, strerror(errno));
return;
}
if ((ret & PR_SVE_VL_LEN_MASK) != data->min_vl) {
ksft_test_result_fail("%s prctl set %d but return value is %d\n",
data->name, data->min_vl, data->rdvl());
return;
}
if (data->rdvl() != data->min_vl) {
ksft_test_result_fail("%s set %d but RDVL is %d\n",
data->name, data->min_vl, data->rdvl());
return;
}
/* Try to set the maximum VL */
ret = prctl(data->prctl_set, data->max_vl);
if (ret < 0) {
ksft_test_result_fail("%s prctl set failed for %d: %d (%s)\n",
data->name, data->max_vl,
errno, strerror(errno));
return;
}
if ((ret & PR_SVE_VL_LEN_MASK) != data->max_vl) {
ksft_test_result_fail("%s prctl() set %d but return value is %d\n",
data->name, data->max_vl, data->rdvl());
return;
}
/* The _INHERIT flag should not be present when we read the VL */
ret = prctl(data->prctl_get);
if (ret == -1) {
ksft_test_result_fail("%s prctl() read failed: %d (%s)\n",
data->name, errno, strerror(errno));
return;
}
if (ret & PR_SVE_VL_INHERIT) {
ksft_test_result_fail("%s prctl() reports _INHERIT\n",
data->name);
return;
}
ksft_test_result_pass("%s prctl() set min/max\n", data->name);
}
/* If we didn't request it a new VL shouldn't affect the child */
static void prctl_set_no_child(struct vec_data *data)
{
int ret, child_vl;
if (data->min_vl == data->max_vl) {
ksft_test_result_skip("%s only one VL supported\n",
data->name);
return;
}
ret = prctl(data->prctl_set, data->min_vl);
if (ret < 0) {
ksft_test_result_fail("%s prctl set failed for %d: %d (%s)\n",
data->name, data->min_vl,
errno, strerror(errno));
return;
}
/* Ensure the default VL is different */
ret = file_write_integer(data->default_vl_file, data->max_vl);
if (ret != 0)
return;
/* Check that the child has the default we just set */
child_vl = get_child_rdvl(data);
if (child_vl != data->max_vl) {
ksft_test_result_fail("%s is %d but child VL is %d\n",
data->default_vl_file,
data->max_vl, child_vl);
return;
}
ksft_test_result_pass("%s vector length used default\n", data->name);
file_write_integer(data->default_vl_file, data->default_vl);
}
/* If we didn't request it a new VL shouldn't affect the child */
static void prctl_set_for_child(struct vec_data *data)
{
int ret, child_vl;
if (data->min_vl == data->max_vl) {
ksft_test_result_skip("%s only one VL supported\n",
data->name);
return;
}
ret = prctl(data->prctl_set, data->min_vl | PR_SVE_VL_INHERIT);
if (ret < 0) {
ksft_test_result_fail("%s prctl set failed for %d: %d (%s)\n",
data->name, data->min_vl,
errno, strerror(errno));
return;
}
/* The _INHERIT flag should be present when we read the VL */
ret = prctl(data->prctl_get);
if (ret == -1) {
ksft_test_result_fail("%s prctl() read failed: %d (%s)\n",
data->name, errno, strerror(errno));
return;
}
if (!(ret & PR_SVE_VL_INHERIT)) {
ksft_test_result_fail("%s prctl() does not report _INHERIT\n",
data->name);
return;
}
/* Ensure the default VL is different */
ret = file_write_integer(data->default_vl_file, data->max_vl);
if (ret != 0)
return;
/* Check that the child inherited our VL */
child_vl = get_child_rdvl(data);
if (child_vl != data->min_vl) {
ksft_test_result_fail("%s is %d but child VL is %d\n",
data->default_vl_file,
data->min_vl, child_vl);
return;
}
ksft_test_result_pass("%s vector length was inherited\n", data->name);
file_write_integer(data->default_vl_file, data->default_vl);
}
/* _ONEXEC takes effect only in the child process */
static void prctl_set_onexec(struct vec_data *data)
{
int ret, child_vl;
if (data->min_vl == data->max_vl) {
ksft_test_result_skip("%s only one VL supported\n",
data->name);
return;
}
/* Set a known value for the default and our current VL */
ret = file_write_integer(data->default_vl_file, data->max_vl);
if (ret != 0)
return;
ret = prctl(data->prctl_set, data->max_vl);
if (ret < 0) {
ksft_test_result_fail("%s prctl set failed for %d: %d (%s)\n",
data->name, data->min_vl,
errno, strerror(errno));
return;
}
/* Set a different value for the child to have on exec */
ret = prctl(data->prctl_set, data->min_vl | PR_SVE_SET_VL_ONEXEC);
if (ret < 0) {
ksft_test_result_fail("%s prctl set failed for %d: %d (%s)\n",
data->name, data->min_vl,
errno, strerror(errno));
return;
}
/* Our current VL should stay the same */
if (data->rdvl() != data->max_vl) {
ksft_test_result_fail("%s VL changed by _ONEXEC prctl()\n",
data->name);
return;
}
/* Check that the child inherited our VL */
child_vl = get_child_rdvl(data);
if (child_vl != data->min_vl) {
ksft_test_result_fail("Set %d _ONEXEC but child VL is %d\n",
data->min_vl, child_vl);
return;
}
ksft_test_result_pass("%s vector length set on exec\n", data->name);
file_write_integer(data->default_vl_file, data->default_vl);
}
typedef void (*test_type)(struct vec_data *);
static const test_type tests[] = {
/*
* The default/min/max tests must be first and in this order
* to provide data for other tests.
*/
proc_read_default,
proc_write_min,
proc_write_max,
prctl_get,
prctl_set,
prctl_set_no_child,
prctl_set_for_child,
prctl_set_onexec,
};
int main(void)
{
int i, j;
ksft_print_header();
ksft_set_plan(ARRAY_SIZE(tests) * ARRAY_SIZE(vec_data));
for (i = 0; i < ARRAY_SIZE(vec_data); i++) {
struct vec_data *data = &vec_data[i];
unsigned long supported;
supported = getauxval(data->hwcap_type) & data->hwcap;
for (j = 0; j < ARRAY_SIZE(tests); j++) {
if (supported)
tests[j](data);
else
ksft_test_result_skip("%s not supported\n",
data->name);
}
}
ksft_exit_pass();
}

View File

@@ -1,4 +1,5 @@
check_buffer_fill
check_gcr_el1_cswitch
check_tags_inclusion
check_child_memory
check_mmap_options

View File

@@ -298,7 +298,7 @@ int mte_default_setup(void)
int ret;
if (!(hwcaps2 & HWCAP2_MTE)) {
ksft_print_msg("FAIL: MTE features unavailable\n");
ksft_print_msg("SKIP: MTE features unavailable\n");
return KSFT_SKIP;
}
/* Get current mte mode */

View File

@@ -25,13 +25,15 @@
do { \
unsigned long hwcaps = getauxval(AT_HWCAP); \
/* data key instructions are not in NOP space. This prevents a SIGILL */ \
ASSERT_NE(0, hwcaps & HWCAP_PACA) TH_LOG("PAUTH not enabled"); \
if (!(hwcaps & HWCAP_PACA)) \
SKIP(return, "PAUTH not enabled"); \
} while (0)
#define ASSERT_GENERIC_PAUTH_ENABLED() \
do { \
unsigned long hwcaps = getauxval(AT_HWCAP); \
/* generic key instructions are not in NOP space. This prevents a SIGILL */ \
ASSERT_NE(0, hwcaps & HWCAP_PACG) TH_LOG("Generic PAUTH not enabled"); \
if (!(hwcaps & HWCAP_PACG)) \
SKIP(return, "Generic PAUTH not enabled"); \
} while (0)
void sign_specific(struct signatures *sign, size_t val)
@@ -256,7 +258,7 @@ TEST(single_thread_different_keys)
unsigned long hwcaps = getauxval(AT_HWCAP);
/* generic and data key instructions are not in NOP space. This prevents a SIGILL */
ASSERT_NE(0, hwcaps & HWCAP_PACA) TH_LOG("PAUTH not enabled");
ASSERT_PAUTH_ENABLED();
if (!(hwcaps & HWCAP_PACG)) {
TH_LOG("WARNING: Generic PAUTH not enabled. Skipping generic key checks");
nkeys = NKEYS - 1;
@@ -299,7 +301,7 @@ TEST(exec_changed_keys)
unsigned long hwcaps = getauxval(AT_HWCAP);
/* generic and data key instructions are not in NOP space. This prevents a SIGILL */
ASSERT_NE(0, hwcaps & HWCAP_PACA) TH_LOG("PAUTH not enabled");
ASSERT_PAUTH_ENABLED();
if (!(hwcaps & HWCAP_PACG)) {
TH_LOG("WARNING: Generic PAUTH not enabled. Skipping generic key checks");
nkeys = NKEYS - 1;

View File

@@ -1,4 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
mangle_*
fake_sigreturn_*
sve_*
!*.[ch]

View File

@@ -33,10 +33,12 @@
*/
enum {
FSSBS_BIT,
FSVE_BIT,
FMAX_END
};
#define FEAT_SSBS (1UL << FSSBS_BIT)
#define FEAT_SVE (1UL << FSVE_BIT)
/*
* A descriptor used to describe and configure a test case.

View File

@@ -26,6 +26,7 @@ static int sig_copyctx = SIGTRAP;
static char const *const feats_names[FMAX_END] = {
" SSBS ",
" SVE ",
};
#define MAX_FEATS_SZ 128
@@ -263,6 +264,8 @@ int test_init(struct tdescr *td)
*/
if (getauxval(AT_HWCAP) & HWCAP_SSBS)
td->feats_supported |= FEAT_SSBS;
if (getauxval(AT_HWCAP) & HWCAP_SVE)
td->feats_supported |= FEAT_SVE;
if (feats_ok(td))
fprintf(stderr,
"Required Features: [%s] supported\n",

View File

@@ -0,0 +1,2 @@
- Validate that register contents are saved and restored as expected.
- Support and validate extra_context.

View File

@@ -0,0 +1,92 @@
// 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 <signal.h>
#include <ucontext.h>
#include <sys/prctl.h>
#include "test_signals_utils.h"
#include "testcases.h"
struct fake_sigframe sf;
static unsigned int vls[SVE_VQ_MAX];
unsigned int nvls = 0;
static bool sve_get_vls(struct tdescr *td)
{
int vq, vl;
/*
* Enumerate up to SVE_VQ_MAX vector lengths
*/
for (vq = SVE_VQ_MAX; vq > 0; --vq) {
vl = prctl(PR_SVE_SET_VL, vq * 16);
if (vl == -1)
return false;
vl &= PR_SVE_VL_LEN_MASK;
/* Skip missing VLs */
vq = sve_vq_from_vl(vl);
vls[nvls++] = vl;
}
/* We need at least two VLs */
if (nvls < 2) {
fprintf(stderr, "Only %d VL supported\n", nvls);
return false;
}
return true;
}
static int fake_sigreturn_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))
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,
};

View File

@@ -0,0 +1,126 @@
// 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 <signal.h>
#include <ucontext.h>
#include <sys/prctl.h>
#include "test_signals_utils.h"
#include "testcases.h"
struct fake_sigframe sf;
static unsigned int vls[SVE_VQ_MAX];
unsigned int nvls = 0;
static bool sve_get_vls(struct tdescr *td)
{
int vq, vl;
/*
* Enumerate up to SVE_VQ_MAX vector lengths
*/
for (vq = SVE_VQ_MAX; vq > 0; --vq) {
vl = prctl(PR_SVE_SET_VL, vq * 16);
if (vl == -1)
return false;
vl &= PR_SVE_VL_LEN_MASK;
/* Skip missing VLs */
vq = sve_vq_from_vl(vl);
vls[nvls++] = vl;
}
/* We need at least one VL */
if (nvls < 1) {
fprintf(stderr, "Only %d VL supported\n", nvls);
return false;
}
return true;
}
static void setup_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 resv_sz, offset;
struct _aarch64_ctx *head = GET_SF_RESV_HEAD(sf);
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, &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, "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++) {
/*
* 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;
}
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,
};

View File

@@ -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))
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,
};

View File

@@ -50,12 +50,38 @@ bool validate_extra_context(struct extra_context *extra, char **err)
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_reserved(ucontext_t *uc, size_t resv_sz, char **err)
{
bool terminated = false;
size_t offs = 0;
int flags = 0;
struct extra_context *extra = NULL;
struct sve_context *sve = NULL;
struct _aarch64_ctx *head =
(struct _aarch64_ctx *)uc->uc_mcontext.__reserved;
@@ -90,9 +116,8 @@ bool validate_reserved(ucontext_t *uc, size_t resv_sz, char **err)
case SVE_MAGIC:
if (flags & SVE_CTX)
*err = "Multiple SVE_MAGIC";
else if (head->size !=
sizeof(struct sve_context))
*err = "Bad size for sve_context";
/* Size is validated in validate_sve_context() */
sve = (struct sve_context *)head;
flags |= SVE_CTX;
break;
case EXTRA_MAGIC:
@@ -137,6 +162,9 @@ bool validate_reserved(ucontext_t *uc, size_t resv_sz, char **err)
if (flags & EXTRA_CTX)
if (!validate_extra_context(extra, err))
return false;
if (flags & SVE_CTX)
if (!validate_sve_context(sve, err))
return false;
head = GET_RESV_NEXT_HEAD(head);
}