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

* for-next/selftests:
  kselftest/arm64: Add lsfe to the hwcaps test
  kselftest/arm64: Check that unsupported regsets fail in sve-ptrace
  kselftest/arm64: Verify that we reject out of bounds VLs in sve-ptrace
  kselftest/arm64/gcs/basic-gcs: Respect parent directory CFLAGS
  selftests/arm64: Fix grammatical error in string literals
  kselftest/arm64: Add parentheses around sizeof for clarity
  kselftest/arm64: Supress warning and improve readability
  kselftest/arm64: Remove extra blank line
  kselftest/arm64/gcs: Use nolibc's getauxval()
  kselftest/arm64/gcs: Correctly check return value when disabling GCS
  selftests: arm64: Fix -Waddress warning in tpidr2 test
  kselftest/arm64: Log error codes in sve-ptrace
  selftests: arm64: Check fread return value in exec_target
This commit is contained in:
Will Deacon 2025-09-24 16:34:56 +01:00
commit 712f4ee70a
14 changed files with 144 additions and 32 deletions

View File

@ -17,6 +17,8 @@
#include <asm/sigcontext.h>
#include <asm/unistd.h>
#include <linux/auxvec.h>
#include "../../kselftest.h"
#define TESTS_PER_HWCAP 3
@ -55,7 +57,6 @@ static void cmpbr_sigill(void)
/* Not implemented, too complicated and unreliable anyway */
}
static void crc32_sigill(void)
{
/* CRC32W W0, W0, W1 */
@ -169,6 +170,18 @@ static void lse128_sigill(void)
: "cc", "memory");
}
static void lsfe_sigill(void)
{
float __attribute__ ((aligned (16))) mem;
register float *memp asm ("x0") = &mem;
/* STFADD H0, [X0] */
asm volatile(".inst 0x7c20801f"
: "+r" (memp)
:
: "memory");
}
static void lut_sigill(void)
{
/* LUTI2 V0.16B, { V0.16B }, V[0] */
@ -762,6 +775,13 @@ static const struct hwcap_data {
.cpuinfo = "lse128",
.sigill_fn = lse128_sigill,
},
{
.name = "LSFE",
.at_hwcap = AT_HWCAP3,
.hwcap_bit = HWCAP3_LSFE,
.cpuinfo = "lsfe",
.sigill_fn = lsfe_sigill,
},
{
.name = "LUT",
.at_hwcap = AT_HWCAP2,

View File

@ -227,10 +227,10 @@ int main(int argc, char **argv)
ret = open("/proc/sys/abi/sme_default_vector_length", O_RDONLY, 0);
if (ret >= 0) {
ksft_test_result(default_value(), "default_value\n");
ksft_test_result(write_read, "write_read\n");
ksft_test_result(write_sleep_read, "write_sleep_read\n");
ksft_test_result(write_fork_read, "write_fork_read\n");
ksft_test_result(write_clone_read, "write_clone_read\n");
ksft_test_result(write_read(), "write_read\n");
ksft_test_result(write_sleep_read(), "write_sleep_read\n");
ksft_test_result(write_fork_read(), "write_fork_read\n");
ksft_test_result(write_clone_read(), "write_clone_read\n");
} else {
ksft_print_msg("SME support not present\n");

View File

@ -14,7 +14,6 @@
#define GNU_PROPERTY_AARCH64_FEATURE_1_BTI (1U << 0)
#define GNU_PROPERTY_AARCH64_FEATURE_1_PAC (1U << 1)
.macro startfn name:req
.globl \name
\name:

View File

@ -1568,7 +1568,6 @@ static void run_sve_tests(void)
&test_config);
}
}
}
static void run_sme_tests(void)

View File

@ -106,7 +106,7 @@ static void child_start(struct child_data *child, const char *program)
/*
* Read from the startup pipe, there should be no data
* and we should block until it is closed. We just
* carry on on error since this isn't super critical.
* carry-on on error since this isn't super critical.
*/
ret = read(3, &i, sizeof(i));
if (ret < 0)
@ -549,7 +549,7 @@ int main(int argc, char **argv)
evs = calloc(tests, sizeof(*evs));
if (!evs)
ksft_exit_fail_msg("Failed to allocated %d epoll events\n",
ksft_exit_fail_msg("Failed to allocate %d epoll events\n",
tests);
for (i = 0; i < cpus; i++) {

View File

@ -188,13 +188,13 @@ static bool create_socket(void)
ref = malloc(digest_len);
if (!ref) {
printf("Failed to allocated %d byte reference\n", digest_len);
printf("Failed to allocate %d byte reference\n", digest_len);
return false;
}
digest = malloc(digest_len);
if (!digest) {
printf("Failed to allocated %d byte digest\n", digest_len);
printf("Failed to allocate %d byte digest\n", digest_len);
return false;
}

View File

@ -66,7 +66,7 @@ static const struct vec_type vec_types[] = {
};
#define VL_TESTS (((TEST_VQ_MAX - SVE_VQ_MIN) + 1) * 4)
#define FLAG_TESTS 2
#define FLAG_TESTS 4
#define FPSIMD_TESTS 2
#define EXPECTED_TESTS ((VL_TESTS + FLAG_TESTS + FPSIMD_TESTS) * ARRAY_SIZE(vec_types))
@ -95,19 +95,27 @@ static int do_child(void)
static int get_fpsimd(pid_t pid, struct user_fpsimd_state *fpsimd)
{
struct iovec iov;
int ret;
iov.iov_base = fpsimd;
iov.iov_len = sizeof(*fpsimd);
return ptrace(PTRACE_GETREGSET, pid, NT_PRFPREG, &iov);
ret = ptrace(PTRACE_GETREGSET, pid, NT_PRFPREG, &iov);
if (ret == -1)
ksft_perror("ptrace(PTRACE_GETREGSET)");
return ret;
}
static int set_fpsimd(pid_t pid, struct user_fpsimd_state *fpsimd)
{
struct iovec iov;
int ret;
iov.iov_base = fpsimd;
iov.iov_len = sizeof(*fpsimd);
return ptrace(PTRACE_SETREGSET, pid, NT_PRFPREG, &iov);
ret = ptrace(PTRACE_SETREGSET, pid, NT_PRFPREG, &iov);
if (ret == -1)
ksft_perror("ptrace(PTRACE_SETREGSET)");
return ret;
}
static struct user_sve_header *get_sve(pid_t pid, const struct vec_type *type,
@ -115,8 +123,9 @@ static struct user_sve_header *get_sve(pid_t pid, const struct vec_type *type,
{
struct user_sve_header *sve;
void *p;
size_t sz = sizeof *sve;
size_t sz = sizeof(*sve);
struct iovec iov;
int ret;
while (1) {
if (*size < sz) {
@ -132,8 +141,11 @@ static struct user_sve_header *get_sve(pid_t pid, const struct vec_type *type,
iov.iov_base = *buf;
iov.iov_len = sz;
if (ptrace(PTRACE_GETREGSET, pid, type->regset, &iov))
ret = ptrace(PTRACE_GETREGSET, pid, type->regset, &iov);
if (ret) {
ksft_perror("ptrace(PTRACE_GETREGSET)");
goto error;
}
sve = *buf;
if (sve->size <= sz)
@ -152,10 +164,46 @@ static int set_sve(pid_t pid, const struct vec_type *type,
const struct user_sve_header *sve)
{
struct iovec iov;
int ret;
iov.iov_base = (void *)sve;
iov.iov_len = sve->size;
return ptrace(PTRACE_SETREGSET, pid, type->regset, &iov);
ret = ptrace(PTRACE_SETREGSET, pid, type->regset, &iov);
if (ret == -1)
ksft_perror("ptrace(PTRACE_SETREGSET)");
return ret;
}
/* A read operation fails */
static void read_fails(pid_t child, const struct vec_type *type)
{
struct user_sve_header *new_sve = NULL;
size_t new_sve_size = 0;
void *ret;
ret = get_sve(child, type, (void **)&new_sve, &new_sve_size);
ksft_test_result(ret == NULL, "%s unsupported read fails\n",
type->name);
free(new_sve);
}
/* A write operation fails */
static void write_fails(pid_t child, const struct vec_type *type)
{
struct user_sve_header sve;
int ret;
/* Just the header, no data */
memset(&sve, 0, sizeof(sve));
sve.size = sizeof(sve);
sve.flags = SVE_PT_REGS_SVE;
sve.vl = SVE_VL_MIN;
ret = set_sve(child, type, &sve);
ksft_test_result(ret != 0, "%s unsupported write fails\n",
type->name);
}
/* Validate setting and getting the inherit flag */
@ -270,6 +318,25 @@ static void check_u32(unsigned int vl, const char *reg,
}
}
/* Set out of range VLs */
static void ptrace_set_vl_ranges(pid_t child, const struct vec_type *type)
{
struct user_sve_header sve;
int ret;
memset(&sve, 0, sizeof(sve));
sve.flags = SVE_PT_REGS_SVE;
sve.size = sizeof(sve);
ret = set_sve(child, type, &sve);
ksft_test_result(ret != 0, "%s Set invalid VL 0\n", type->name);
sve.vl = SVE_VL_MAX + SVE_VQ_BYTES;
ret = set_sve(child, type, &sve);
ksft_test_result(ret != 0, "%s Set invalid VL %d\n", type->name,
SVE_VL_MAX + SVE_VQ_BYTES);
}
/* Access the FPSIMD registers via the SVE regset */
static void ptrace_sve_fpsimd(pid_t child, const struct vec_type *type)
{
@ -683,6 +750,20 @@ static int do_parent(pid_t child)
}
for (i = 0; i < ARRAY_SIZE(vec_types); i++) {
/*
* If the vector type isn't supported reads and writes
* should fail.
*/
if (!(getauxval(vec_types[i].hwcap_type) & vec_types[i].hwcap)) {
read_fails(child, &vec_types[i]);
write_fails(child, &vec_types[i]);
} else {
ksft_test_result_skip("%s unsupported read fails\n",
vec_types[i].name);
ksft_test_result_skip("%s unsupported write fails\n",
vec_types[i].name);
}
/* FPSIMD via SVE regset */
if (getauxval(vec_types[i].hwcap_type) & vec_types[i].hwcap) {
ptrace_sve_fpsimd(child, &vec_types[i]);
@ -703,6 +784,17 @@ static int do_parent(pid_t child)
vec_types[i].name);
}
/* Setting out of bounds VLs should fail */
if (getauxval(vec_types[i].hwcap_type) & vec_types[i].hwcap) {
ptrace_set_vl_ranges(child, &vec_types[i]);
} else {
ksft_test_result_skip("%s Set invalid VL 0\n",
vec_types[i].name);
ksft_test_result_skip("%s Set invalid VL %d\n",
vec_types[i].name,
SVE_VL_MAX + SVE_VQ_BYTES);
}
/* Step through every possible VQ */
for (vq = SVE_VQ_MIN; vq <= TEST_VQ_MAX; vq++) {
vl = sve_vl_from_vq(vq);

View File

@ -690,7 +690,6 @@ 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.

View File

@ -108,7 +108,6 @@ static int get_zt(pid_t pid, char zt[ZT_SIG_REG_BYTES])
return ptrace(PTRACE_GETREGSET, pid, NT_ARM_ZT, &iov);
}
static int set_zt(pid_t pid, const char zt[ZT_SIG_REG_BYTES])
{
struct iovec iov;

View File

@ -14,11 +14,11 @@ LDLIBS+=-lpthread
include ../../lib.mk
$(OUTPUT)/basic-gcs: basic-gcs.c
$(CC) -g -fno-asynchronous-unwind-tables -fno-ident -s -Os -nostdlib \
-static -include ../../../../include/nolibc/nolibc.h \
$(CC) $(CFLAGS) -fno-asynchronous-unwind-tables -fno-ident -s -nostdlib -nostdinc \
-static -I../../../../include/nolibc -include ../../../../include/nolibc/nolibc.h \
-I../../../../../usr/include \
-std=gnu99 -I../.. -g \
-ffreestanding -Wall $^ -o $@ -lgcc
-ffreestanding $^ -o $@ -lgcc
$(OUTPUT)/gcs-stress-thread: gcs-stress-thread.S
$(CC) -nostdlib $^ -o $@

View File

@ -10,6 +10,7 @@
#include <sys/mman.h>
#include <asm/mman.h>
#include <asm/hwcap.h>
#include <linux/sched.h>
#include "kselftest.h"
@ -386,14 +387,13 @@ int main(void)
ksft_print_header();
/*
* We don't have getauxval() with nolibc so treat a failure to
* read GCS state as a lack of support and skip.
*/
if (!(getauxval(AT_HWCAP) & HWCAP_GCS))
ksft_exit_skip("SKIP GCS not supported\n");
ret = my_syscall5(__NR_prctl, PR_GET_SHADOW_STACK_STATUS,
&gcs_mode, 0, 0, 0);
if (ret != 0)
ksft_exit_skip("Failed to read GCS state: %d\n", ret);
ksft_exit_fail_msg("Failed to read GCS state: %d\n", ret);
if (!(gcs_mode & PR_SHADOW_STACK_ENABLE)) {
gcs_mode = PR_SHADOW_STACK_ENABLE;
@ -410,7 +410,7 @@ int main(void)
}
/* One last test: disable GCS, we can do this one time */
my_syscall5(__NR_prctl, PR_SET_SHADOW_STACK_STATUS, 0, 0, 0, 0);
ret = my_syscall5(__NR_prctl, PR_SET_SHADOW_STACK_STATUS, 0, 0, 0, 0);
if (ret != 0)
ksft_print_msg("Failed to disable GCS: %d\n", ret);

View File

@ -165,7 +165,6 @@ TEST_F(valid_modes, lock_enable_disable_others)
ASSERT_EQ(ret, 0);
ASSERT_EQ(mode, PR_SHADOW_STACK_ALL_MODES);
ret = my_syscall2(__NR_prctl, PR_SET_SHADOW_STACK_STATUS,
variant->mode);
ASSERT_EQ(ret, 0);

View File

@ -433,7 +433,7 @@ int main(int argc, char **argv)
evs = calloc(tests, sizeof(*evs));
if (!evs)
ksft_exit_fail_msg("Failed to allocated %d epoll events\n",
ksft_exit_fail_msg("Failed to allocate %d epoll events\n",
tests);
for (i = 0; i < gcs_threads; i++)

View File

@ -13,7 +13,12 @@ int main(void)
unsigned long hwcaps;
size_t val;
fread(&val, sizeof(size_t), 1, stdin);
size_t size = fread(&val, sizeof(size_t), 1, stdin);
if (size != 1) {
fprintf(stderr, "Could not read input from stdin\n");
return EXIT_FAILURE;
}
/* don't try to execute illegal (unimplemented) instructions) caller
* should have checked this and keep worker simple