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

* for-next/selftests: (22 commits)
  kselftest/arm64: Fix hwcaps selftest build
  kselftest/arm64: add jscvt feature to hwcap test
  kselftest/arm64: add pmull feature to hwcap test
  kselftest/arm64: add AES feature check to hwcap test
  kselftest/arm64: add SHA1 and related features to hwcap test
  kselftest/arm64: build BTI tests in output directory
  kselftest/arm64: fix a memleak in zt_regs_run()
  kselftest/arm64: Size sycall-abi buffers for the actual maximum VL
  kselftest/arm64: add lse and lse2 features to hwcap test
  kselftest/arm64: add test item that support to capturing the SIGBUS signal
  kselftest/arm64: add DEF_SIGHANDLER_FUNC() and DEF_INST_RAISE_SIG() helpers
  kselftest/arm64: add crc32 feature to hwcap test
  kselftest/arm64: add float-point feature to hwcap test
  kselftest/arm64: Use the tools/include compiler.h rather than our own
  kselftest/arm64: Use shared OPTIMZER_HIDE_VAR() definiton
  kselftest/arm64: Make the tools/include headers available
  tools include: Add some common function attributes
  tools compiler.h: Add OPTIMIZER_HIDE_VAR()
  kselftest/arm64: Exit streaming mode after collecting signal context
  kselftest/arm64: add RCpc load-acquire to hwcap test
  ...
This commit is contained in:
Will Deacon 2023-08-25 12:36:57 +01:00
commit e1df272139
13 changed files with 471 additions and 125 deletions

View file

@ -42,6 +42,18 @@
# define __always_inline inline __attribute__((always_inline))
#endif
#ifndef __always_unused
#define __always_unused __attribute__((__unused__))
#endif
#ifndef __noreturn
#define __noreturn __attribute__((__noreturn__))
#endif
#ifndef unreachable
#define unreachable() __builtin_unreachable()
#endif
#ifndef noinline
#define noinline
#endif
@ -190,4 +202,10 @@ static __always_inline void __write_once_size(volatile void *p, void *res, int s
#define ___PASTE(a, b) a##b
#define __PASTE(a, b) ___PASTE(a, b)
#ifndef OPTIMIZER_HIDE_VAR
/* Make the optimizer believe the variable can be manipulated arbitrarily. */
#define OPTIMIZER_HIDE_VAR(var) \
__asm__ ("" : "=r" (var) : "0" (var))
#endif
#endif /* _TOOLS_LINUX_COMPILER_H */

View file

@ -19,6 +19,8 @@ CFLAGS += -I$(top_srcdir)/tools/testing/selftests/
CFLAGS += $(KHDR_INCLUDES)
CFLAGS += -I$(top_srcdir)/tools/include
export CFLAGS
export top_srcdir

View file

@ -19,19 +19,38 @@
#include "../../kselftest.h"
#define TESTS_PER_HWCAP 2
#define TESTS_PER_HWCAP 3
/*
* 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.
* Function expected to generate exception when the feature is not
* supported and return when it is supported. If the specific exception
* 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);
typedef void (*sig_fn)(void);
static void aes_sigill(void)
{
/* AESE V0.16B, V0.16B */
asm volatile(".inst 0x4e284800" : : : );
}
static void atomics_sigill(void)
{
/* STADD W0, [SP] */
asm volatile(".inst 0xb82003ff" : : : );
}
static void crc32_sigill(void)
{
/* CRC32W W0, W0, W1 */
asm volatile(".inst 0x1ac14800" : : : );
}
static void cssc_sigill(void)
{
@ -39,6 +58,29 @@ static void cssc_sigill(void)
asm volatile(".inst 0xdac01c00" : : : "x0");
}
static void fp_sigill(void)
{
asm volatile("fmov s0, #1");
}
static void ilrcpc_sigill(void)
{
/* LDAPUR W0, [SP, #8] */
asm volatile(".inst 0x994083e0" : : : );
}
static void jscvt_sigill(void)
{
/* FJCVTZS W0, D0 */
asm volatile(".inst 0x1e7e0000" : : : );
}
static void lrcpc_sigill(void)
{
/* LDAPR W0, [SP, #0] */
asm volatile(".inst 0xb8bfc3e0" : : : );
}
static void mops_sigill(void)
{
char dst[1], src[1];
@ -53,11 +95,35 @@ static void mops_sigill(void)
: "cc", "memory");
}
static void pmull_sigill(void)
{
/* PMULL V0.1Q, V0.1D, V0.1D */
asm volatile(".inst 0x0ee0e000" : : : );
}
static void rng_sigill(void)
{
asm volatile("mrs x0, S3_3_C2_C4_0" : : : "x0");
}
static void sha1_sigill(void)
{
/* SHA1H S0, S0 */
asm volatile(".inst 0x5e280800" : : : );
}
static void sha2_sigill(void)
{
/* SHA256H Q0, Q0, V0.4S */
asm volatile(".inst 0x5e004000" : : : );
}
static void sha512_sigill(void)
{
/* SHA512H Q0, Q0, V0.2D */
asm volatile(".inst 0xce608000" : : : );
}
static void sme_sigill(void)
{
/* RDSVL x0, #0 */
@ -215,14 +281,38 @@ static void hbc_sigill(void)
".inst 0x54000030" : : : "cc");
}
static void uscat_sigbus(void)
{
/* unaligned atomic access */
asm volatile("ADD x1, sp, #2" : : : );
/* STADD W0, [X1] */
asm volatile(".inst 0xb820003f" : : : );
}
static const struct hwcap_data {
const char *name;
unsigned long at_hwcap;
unsigned long hwcap_bit;
const char *cpuinfo;
sigill_fn sigill_fn;
sig_fn sigill_fn;
bool sigill_reliable;
sig_fn sigbus_fn;
bool sigbus_reliable;
} hwcaps[] = {
{
.name = "AES",
.at_hwcap = AT_HWCAP,
.hwcap_bit = HWCAP_AES,
.cpuinfo = "aes",
.sigill_fn = aes_sigill,
},
{
.name = "CRC32",
.at_hwcap = AT_HWCAP,
.hwcap_bit = HWCAP_CRC32,
.cpuinfo = "crc32",
.sigill_fn = crc32_sigill,
},
{
.name = "CSSC",
.at_hwcap = AT_HWCAP2,
@ -230,6 +320,50 @@ static const struct hwcap_data {
.cpuinfo = "cssc",
.sigill_fn = cssc_sigill,
},
{
.name = "FP",
.at_hwcap = AT_HWCAP,
.hwcap_bit = HWCAP_FP,
.cpuinfo = "fp",
.sigill_fn = fp_sigill,
},
{
.name = "JSCVT",
.at_hwcap = AT_HWCAP,
.hwcap_bit = HWCAP_JSCVT,
.cpuinfo = "jscvt",
.sigill_fn = jscvt_sigill,
},
{
.name = "LRCPC",
.at_hwcap = AT_HWCAP,
.hwcap_bit = HWCAP_LRCPC,
.cpuinfo = "lrcpc",
.sigill_fn = lrcpc_sigill,
},
{
.name = "LRCPC2",
.at_hwcap = AT_HWCAP,
.hwcap_bit = HWCAP_ILRCPC,
.cpuinfo = "ilrcpc",
.sigill_fn = ilrcpc_sigill,
},
{
.name = "LSE",
.at_hwcap = AT_HWCAP,
.hwcap_bit = HWCAP_ATOMICS,
.cpuinfo = "atomics",
.sigill_fn = atomics_sigill,
},
{
.name = "LSE2",
.at_hwcap = AT_HWCAP,
.hwcap_bit = HWCAP_USCAT,
.cpuinfo = "uscat",
.sigill_fn = atomics_sigill,
.sigbus_fn = uscat_sigbus,
.sigbus_reliable = true,
},
{
.name = "MOPS",
.at_hwcap = AT_HWCAP2,
@ -238,6 +372,13 @@ static const struct hwcap_data {
.sigill_fn = mops_sigill,
.sigill_reliable = true,
},
{
.name = "PMULL",
.at_hwcap = AT_HWCAP,
.hwcap_bit = HWCAP_PMULL,
.cpuinfo = "pmull",
.sigill_fn = pmull_sigill,
},
{
.name = "RNG",
.at_hwcap = AT_HWCAP2,
@ -251,6 +392,27 @@ static const struct hwcap_data {
.hwcap_bit = HWCAP2_RPRFM,
.cpuinfo = "rprfm",
},
{
.name = "SHA1",
.at_hwcap = AT_HWCAP,
.hwcap_bit = HWCAP_SHA1,
.cpuinfo = "sha1",
.sigill_fn = sha1_sigill,
},
{
.name = "SHA2",
.at_hwcap = AT_HWCAP,
.hwcap_bit = HWCAP_SHA2,
.cpuinfo = "sha2",
.sigill_fn = sha2_sigill,
},
{
.name = "SHA512",
.at_hwcap = AT_HWCAP,
.hwcap_bit = HWCAP_SHA512,
.cpuinfo = "sha512",
.sigill_fn = sha512_sigill,
},
{
.name = "SME",
.at_hwcap = AT_HWCAP2,
@ -403,18 +565,22 @@ static const struct hwcap_data {
},
};
static bool seen_sigill;
typedef void (*sighandler_fn)(int, siginfo_t *, void *);
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;
#define DEF_SIGHANDLER_FUNC(SIG, NUM) \
static bool seen_##SIG; \
static void handle_##SIG(int sig, siginfo_t *info, void *context) \
{ \
ucontext_t *uc = context; \
\
seen_##SIG = true; \
/* Skip over the offending instruction */ \
uc->uc_mcontext.pc += 4; \
}
DEF_SIGHANDLER_FUNC(sigill, SIGILL);
DEF_SIGHANDLER_FUNC(sigbus, SIGBUS);
bool cpuinfo_present(const char *name)
{
FILE *f;
@ -457,25 +623,78 @@ bool cpuinfo_present(const char *name)
return false;
}
static int install_sigaction(int signum, sighandler_fn handler)
{
int ret;
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_sigaction = handler;
sa.sa_flags = SA_RESTART | SA_SIGINFO;
sigemptyset(&sa.sa_mask);
ret = sigaction(signum, &sa, NULL);
if (ret < 0)
ksft_exit_fail_msg("Failed to install SIGNAL handler: %s (%d)\n",
strerror(errno), errno);
return ret;
}
static void uninstall_sigaction(int signum)
{
if (sigaction(signum, NULL, NULL) < 0)
ksft_exit_fail_msg("Failed to uninstall SIGNAL handler: %s (%d)\n",
strerror(errno), errno);
}
#define DEF_INST_RAISE_SIG(SIG, NUM) \
static bool inst_raise_##SIG(const struct hwcap_data *hwcap, \
bool have_hwcap) \
{ \
if (!hwcap->SIG##_fn) { \
ksft_test_result_skip(#SIG"_%s\n", hwcap->name); \
/* assume that it would raise exception in default */ \
return true; \
} \
\
install_sigaction(NUM, handle_##SIG); \
\
seen_##SIG = false; \
hwcap->SIG##_fn(); \
\
if (have_hwcap) { \
/* Should be able to use the extension */ \
ksft_test_result(!seen_##SIG, \
#SIG"_%s\n", hwcap->name); \
} else if (hwcap->SIG##_reliable) { \
/* Guaranteed a SIGNAL */ \
ksft_test_result(seen_##SIG, \
#SIG"_%s\n", hwcap->name); \
} else { \
/* Missing SIGNAL might be fine */ \
ksft_print_msg(#SIG"_%sreported for %s\n", \
seen_##SIG ? "" : "not ", \
hwcap->name); \
ksft_test_result_skip(#SIG"_%s\n", \
hwcap->name); \
} \
\
uninstall_sigaction(NUM); \
return seen_##SIG; \
}
DEF_INST_RAISE_SIG(sigill, SIGILL);
DEF_INST_RAISE_SIG(sigbus, SIGBUS);
int main(void)
{
int i;
const struct hwcap_data *hwcap;
int i, ret;
bool have_cpuinfo, have_hwcap;
struct sigaction sa;
bool have_cpuinfo, have_hwcap, raise_sigill;
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];
@ -488,30 +707,15 @@ int main(void)
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);
}
/*
* Testing for SIGBUS only makes sense after make sure
* that the instruction does not cause a SIGILL signal.
*/
raise_sigill = inst_raise_sigill(hwcap, have_hwcap);
if (!raise_sigill)
inst_raise_sigbus(hwcap, have_hwcap);
else
ksft_test_result_skip("sigbus_%s\n", hwcap->name);
}
ksft_print_cnts();

View file

@ -20,12 +20,20 @@
#include "syscall-abi.h"
/*
* The kernel defines a much larger SVE_VQ_MAX than is expressable in
* the architecture, this creates a *lot* of overhead filling the
* buffers (especially ZA) on emulated platforms so use the actual
* architectural maximum instead.
*/
#define ARCH_SVE_VQ_MAX 16
static int default_sme_vl;
static int sve_vl_count;
static unsigned int sve_vls[SVE_VQ_MAX];
static unsigned int sve_vls[ARCH_SVE_VQ_MAX];
static int sme_vl_count;
static unsigned int sme_vls[SVE_VQ_MAX];
static unsigned int sme_vls[ARCH_SVE_VQ_MAX];
extern void do_syscall(int sve_vl, int sme_vl);
@ -130,9 +138,9 @@ static int check_fpr(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
#define SVE_Z_SHARED_BYTES (128 / 8)
static uint8_t z_zero[__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 uint8_t z_zero[__SVE_ZREG_SIZE(ARCH_SVE_VQ_MAX)];
uint8_t z_in[SVE_NUM_ZREGS * __SVE_ZREG_SIZE(ARCH_SVE_VQ_MAX)];
uint8_t z_out[SVE_NUM_ZREGS * __SVE_ZREG_SIZE(ARCH_SVE_VQ_MAX)];
static void setup_z(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
uint64_t svcr)
@ -190,8 +198,8 @@ static int check_z(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
return errors;
}
uint8_t p_in[SVE_NUM_PREGS * __SVE_PREG_SIZE(SVE_VQ_MAX)];
uint8_t p_out[SVE_NUM_PREGS * __SVE_PREG_SIZE(SVE_VQ_MAX)];
uint8_t p_in[SVE_NUM_PREGS * __SVE_PREG_SIZE(ARCH_SVE_VQ_MAX)];
uint8_t p_out[SVE_NUM_PREGS * __SVE_PREG_SIZE(ARCH_SVE_VQ_MAX)];
static void setup_p(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
uint64_t svcr)
@ -222,8 +230,8 @@ static int check_p(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
return errors;
}
uint8_t ffr_in[__SVE_PREG_SIZE(SVE_VQ_MAX)];
uint8_t ffr_out[__SVE_PREG_SIZE(SVE_VQ_MAX)];
uint8_t ffr_in[__SVE_PREG_SIZE(ARCH_SVE_VQ_MAX)];
uint8_t ffr_out[__SVE_PREG_SIZE(ARCH_SVE_VQ_MAX)];
static void setup_ffr(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
uint64_t svcr)
@ -300,8 +308,8 @@ static int check_svcr(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
return errors;
}
uint8_t za_in[ZA_SIG_REGS_SIZE(SVE_VQ_MAX)];
uint8_t za_out[ZA_SIG_REGS_SIZE(SVE_VQ_MAX)];
uint8_t za_in[ZA_SIG_REGS_SIZE(ARCH_SVE_VQ_MAX)];
uint8_t za_out[ZA_SIG_REGS_SIZE(ARCH_SVE_VQ_MAX)];
static void setup_za(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
uint64_t svcr)
@ -470,9 +478,9 @@ void sve_count_vls(void)
return;
/*
* Enumerate up to SVE_VQ_MAX vector lengths
* Enumerate up to ARCH_SVE_VQ_MAX vector lengths
*/
for (vq = SVE_VQ_MAX; vq > 0; vq /= 2) {
for (vq = ARCH_SVE_VQ_MAX; vq > 0; vq /= 2) {
vl = prctl(PR_SVE_SET_VL, vq * 16);
if (vl == -1)
ksft_exit_fail_msg("PR_SVE_SET_VL failed: %s (%d)\n",
@ -496,9 +504,9 @@ void sme_count_vls(void)
return;
/*
* Enumerate up to SVE_VQ_MAX vector lengths
* Enumerate up to ARCH_SVE_VQ_MAX vector lengths
*/
for (vq = SVE_VQ_MAX; vq > 0; vq /= 2) {
for (vq = ARCH_SVE_VQ_MAX; vq > 0; vq /= 2) {
vl = prctl(PR_SME_SET_VL, vq * 16);
if (vl == -1)
ksft_exit_fail_msg("PR_SME_SET_VL failed: %s (%d)\n",

View file

@ -2,8 +2,6 @@
TEST_GEN_PROGS := btitest nobtitest
PROGS := $(patsubst %,gen/%,$(TEST_GEN_PROGS))
# These tests are built as freestanding binaries since otherwise BTI
# support in ld.so is required which is not currently widespread; when
# it is available it will still be useful to test this separately as the
@ -18,44 +16,41 @@ CFLAGS_COMMON = -ffreestanding -Wall -Wextra $(CFLAGS)
BTI_CC_COMMAND = $(CC) $(CFLAGS_BTI) $(CFLAGS_COMMON) -c -o $@ $<
NOBTI_CC_COMMAND = $(CC) $(CFLAGS_NOBTI) $(CFLAGS_COMMON) -c -o $@ $<
%-bti.o: %.c
$(OUTPUT)/%-bti.o: %.c
$(BTI_CC_COMMAND)
%-bti.o: %.S
$(OUTPUT)/%-bti.o: %.S
$(BTI_CC_COMMAND)
%-nobti.o: %.c
$(OUTPUT)/%-nobti.o: %.c
$(NOBTI_CC_COMMAND)
%-nobti.o: %.S
$(OUTPUT)/%-nobti.o: %.S
$(NOBTI_CC_COMMAND)
BTI_OBJS = \
test-bti.o \
signal-bti.o \
start-bti.o \
syscall-bti.o \
system-bti.o \
teststubs-bti.o \
trampoline-bti.o
gen/btitest: $(BTI_OBJS)
$(OUTPUT)/test-bti.o \
$(OUTPUT)/signal-bti.o \
$(OUTPUT)/start-bti.o \
$(OUTPUT)/syscall-bti.o \
$(OUTPUT)/system-bti.o \
$(OUTPUT)/teststubs-bti.o \
$(OUTPUT)/trampoline-bti.o
$(OUTPUT)/btitest: $(BTI_OBJS)
$(CC) $(CFLAGS_BTI) $(CFLAGS_COMMON) -nostdlib -static -o $@ $^
NOBTI_OBJS = \
test-nobti.o \
signal-nobti.o \
start-nobti.o \
syscall-nobti.o \
system-nobti.o \
teststubs-nobti.o \
trampoline-nobti.o
gen/nobtitest: $(NOBTI_OBJS)
$(OUTPUT)/test-nobti.o \
$(OUTPUT)/signal-nobti.o \
$(OUTPUT)/start-nobti.o \
$(OUTPUT)/syscall-nobti.o \
$(OUTPUT)/system-nobti.o \
$(OUTPUT)/teststubs-nobti.o \
$(OUTPUT)/trampoline-nobti.o
$(OUTPUT)/nobtitest: $(NOBTI_OBJS)
$(CC) $(CFLAGS_BTI) $(CFLAGS_COMMON) -nostdlib -static -o $@ $^
# Including KSFT lib.mk here will also mangle the TEST_GEN_PROGS list
# to account for any OUTPUT target-dirs optionally provided by
# the toplevel makefile
include ../../lib.mk
$(TEST_GEN_PROGS): $(PROGS)
cp $(PROGS) $(OUTPUT)/

View file

@ -1,21 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2019 Arm Limited
* Original author: Dave Martin <Dave.Martin@arm.com>
*/
#ifndef COMPILER_H
#define COMPILER_H
#define __always_unused __attribute__((__unused__))
#define __noreturn __attribute__((__noreturn__))
#define __unreachable() __builtin_unreachable()
/* curse(e) has value e, but the compiler cannot assume so */
#define curse(e) ({ \
__typeof__(e) __curse_e = (e); \
asm ("" : "+r" (__curse_e)); \
__curse_e; \
})
#endif /* ! COMPILER_H */

View file

@ -1,2 +0,0 @@
btitest
nobtitest

View file

@ -8,12 +8,10 @@
#include <asm/unistd.h>
#include "compiler.h"
void __noreturn exit(int n)
{
syscall(__NR_exit, n);
__unreachable();
unreachable();
}
ssize_t write(int fd, const void *buf, size_t size)

View file

@ -14,12 +14,12 @@ typedef __kernel_size_t size_t;
typedef __kernel_ssize_t ssize_t;
#include <linux/errno.h>
#include <linux/compiler.h>
#include <asm/hwcap.h>
#include <asm/ptrace.h>
#include <asm/unistd.h>
#include "compiler.h"
long syscall(int nr, ...);
void __noreturn exit(int n);

View file

@ -17,7 +17,6 @@
typedef struct ucontext ucontext_t;
#include "btitest.h"
#include "compiler.h"
#include "signal.h"
#define EXPECTED_TESTS 18

View file

@ -6,6 +6,7 @@
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
@ -39,9 +40,11 @@ struct vec_data {
int max_vl;
};
#define VEC_SVE 0
#define VEC_SME 1
static struct vec_data vec_data[] = {
{
[VEC_SVE] = {
.name = "SVE",
.hwcap_type = AT_HWCAP,
.hwcap = HWCAP_SVE,
@ -51,7 +54,7 @@ static struct vec_data vec_data[] = {
.prctl_set = PR_SVE_SET_VL,
.default_vl_file = "/proc/sys/abi/sve_default_vector_length",
},
{
[VEC_SME] = {
.name = "SME",
.hwcap_type = AT_HWCAP2,
.hwcap = HWCAP2_SME,
@ -551,7 +554,8 @@ static void prctl_set_onexec(struct vec_data *data)
/* For each VQ verify that setting via prctl() does the right thing */
static void prctl_set_all_vqs(struct vec_data *data)
{
int ret, vq, vl, new_vl;
int ret, vq, vl, new_vl, i;
int orig_vls[ARRAY_SIZE(vec_data)];
int errors = 0;
if (!data->min_vl || !data->max_vl) {
@ -560,6 +564,9 @@ static void prctl_set_all_vqs(struct vec_data *data)
return;
}
for (i = 0; i < ARRAY_SIZE(vec_data); i++)
orig_vls[i] = vec_data[i].rdvl();
for (vq = SVE_VQ_MIN; vq <= SVE_VQ_MAX; vq++) {
vl = sve_vl_from_vq(vq);
@ -582,6 +589,22 @@ static void prctl_set_all_vqs(struct vec_data *data)
errors++;
}
/* Did any other VLs change? */
for (i = 0; i < ARRAY_SIZE(vec_data); i++) {
if (&vec_data[i] == data)
continue;
if (!(getauxval(vec_data[i].hwcap_type) & vec_data[i].hwcap))
continue;
if (vec_data[i].rdvl() != orig_vls[i]) {
ksft_print_msg("%s VL changed from %d to %d\n",
vec_data[i].name, orig_vls[i],
vec_data[i].rdvl());
errors++;
}
}
/* Was that the VL we asked for? */
if (new_vl == vl)
continue;
@ -644,18 +667,107 @@ static const test_type tests[] = {
prctl_set_all_vqs,
};
static inline void smstart(void)
{
asm volatile("msr S0_3_C4_C7_3, xzr");
}
static inline void smstart_sm(void)
{
asm volatile("msr S0_3_C4_C3_3, xzr");
}
static inline void smstop(void)
{
asm volatile("msr S0_3_C4_C6_3, xzr");
}
/*
* Verify we can change the SVE vector length while SME is active and
* continue to use SME afterwards.
*/
static void change_sve_with_za(void)
{
struct vec_data *sve_data = &vec_data[VEC_SVE];
bool pass = true;
int ret, i;
if (sve_data->min_vl == sve_data->max_vl) {
ksft_print_msg("Only one SVE VL supported, can't change\n");
ksft_test_result_skip("change_sve_while_sme\n");
return;
}
/* Ensure we will trigger a change when we set the maximum */
ret = prctl(sve_data->prctl_set, sve_data->min_vl);
if (ret != sve_data->min_vl) {
ksft_print_msg("Failed to set SVE VL %d: %d\n",
sve_data->min_vl, ret);
pass = false;
}
/* Enable SM and ZA */
smstart();
/* Trigger another VL change */
ret = prctl(sve_data->prctl_set, sve_data->max_vl);
if (ret != sve_data->max_vl) {
ksft_print_msg("Failed to set SVE VL %d: %d\n",
sve_data->max_vl, ret);
pass = false;
}
/*
* Spin for a bit with SM enabled to try to trigger another
* save/restore. We can't use syscalls without exiting
* streaming mode.
*/
for (i = 0; i < 100000000; i++)
smstart_sm();
/*
* TODO: Verify that ZA was preserved over the VL change and
* spin.
*/
/* Clean up after ourselves */
smstop();
ret = prctl(sve_data->prctl_set, sve_data->default_vl);
if (ret != sve_data->default_vl) {
ksft_print_msg("Failed to restore SVE VL %d: %d\n",
sve_data->default_vl, ret);
pass = false;
}
ksft_test_result(pass, "change_sve_with_za\n");
}
typedef void (*test_all_type)(void);
static const struct {
const char *name;
test_all_type test;
} all_types_tests[] = {
{ "change_sve_with_za", change_sve_with_za },
};
int main(void)
{
bool all_supported = true;
int i, j;
ksft_print_header();
ksft_set_plan(ARRAY_SIZE(tests) * ARRAY_SIZE(vec_data));
ksft_set_plan(ARRAY_SIZE(tests) * ARRAY_SIZE(vec_data) +
ARRAY_SIZE(all_types_tests));
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;
if (!supported)
all_supported = false;
for (j = 0; j < ARRAY_SIZE(tests); j++) {
if (supported)
@ -666,5 +778,12 @@ int main(void)
}
}
for (i = 0; i < ARRAY_SIZE(all_types_tests); i++) {
if (all_supported)
all_types_tests[i].test();
else
ksft_test_result_skip("%s\n", all_types_tests[i].name);
}
ksft_exit_pass();
}

View file

@ -8,6 +8,8 @@
#include <stdio.h>
#include <string.h>
#include <linux/compiler.h>
#include "test_signals.h"
int test_init(struct tdescr *td);
@ -60,13 +62,25 @@ static __always_inline bool get_current_context(struct tdescr *td,
size_t dest_sz)
{
static volatile bool seen_already;
int i;
char *uc = (char *)dest_uc;
assert(td && dest_uc);
/* it's a genuine invocation..reinit */
seen_already = 0;
td->live_uc_valid = 0;
td->live_sz = dest_sz;
memset(dest_uc, 0x00, td->live_sz);
/*
* This is a memset() but we don't want the compiler to
* optimise it into either instructions or a library call
* which might be incompatible with streaming mode.
*/
for (i = 0; i < td->live_sz; i++) {
uc[i] = 0;
OPTIMIZER_HIDE_VAR(uc[0]);
}
td->live_uc = dest_uc;
/*
* Grab ucontext_t triggering a SIGTRAP.
@ -103,6 +117,17 @@ static __always_inline bool get_current_context(struct tdescr *td,
:
: "memory");
/*
* If we were grabbing a streaming mode context then we may
* have entered streaming mode behind the system's back and
* libc or compiler generated code might decide to do
* something invalid in streaming mode, or potentially even
* the state of ZA. Issue a SMSTOP to exit both now we have
* grabbed the state.
*/
if (td->feats_supported & FEAT_SME)
asm volatile("msr S0_3_C4_C6_3, xzr");
/*
* If we get here with seen_already==1 it implies the td->live_uc
* context has been used to get back here....this probably means

View file

@ -65,6 +65,7 @@ int zt_regs_run(struct tdescr *td, siginfo_t *si, ucontext_t *uc)
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;
}