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/sigcontext.h>
#include <asm/unistd.h> #include <asm/unistd.h>
#include <linux/auxvec.h>
#include "../../kselftest.h" #include "../../kselftest.h"
#define TESTS_PER_HWCAP 3 #define TESTS_PER_HWCAP 3
@ -55,7 +57,6 @@ static void cmpbr_sigill(void)
/* Not implemented, too complicated and unreliable anyway */ /* Not implemented, too complicated and unreliable anyway */
} }
static void crc32_sigill(void) static void crc32_sigill(void)
{ {
/* CRC32W W0, W0, W1 */ /* CRC32W W0, W0, W1 */
@ -169,6 +170,18 @@ static void lse128_sigill(void)
: "cc", "memory"); : "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) static void lut_sigill(void)
{ {
/* LUTI2 V0.16B, { V0.16B }, V[0] */ /* LUTI2 V0.16B, { V0.16B }, V[0] */
@ -762,6 +775,13 @@ static const struct hwcap_data {
.cpuinfo = "lse128", .cpuinfo = "lse128",
.sigill_fn = lse128_sigill, .sigill_fn = lse128_sigill,
}, },
{
.name = "LSFE",
.at_hwcap = AT_HWCAP3,
.hwcap_bit = HWCAP3_LSFE,
.cpuinfo = "lsfe",
.sigill_fn = lsfe_sigill,
},
{ {
.name = "LUT", .name = "LUT",
.at_hwcap = AT_HWCAP2, .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); ret = open("/proc/sys/abi/sme_default_vector_length", O_RDONLY, 0);
if (ret >= 0) { if (ret >= 0) {
ksft_test_result(default_value(), "default_value\n"); ksft_test_result(default_value(), "default_value\n");
ksft_test_result(write_read, "write_read\n"); ksft_test_result(write_read(), "write_read\n");
ksft_test_result(write_sleep_read, "write_sleep_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_fork_read(), "write_fork_read\n");
ksft_test_result(write_clone_read, "write_clone_read\n"); ksft_test_result(write_clone_read(), "write_clone_read\n");
} else { } else {
ksft_print_msg("SME support not present\n"); 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_BTI (1U << 0)
#define GNU_PROPERTY_AARCH64_FEATURE_1_PAC (1U << 1) #define GNU_PROPERTY_AARCH64_FEATURE_1_PAC (1U << 1)
.macro startfn name:req .macro startfn name:req
.globl \name .globl \name
\name: \name:

View File

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

View File

@ -105,8 +105,8 @@ static void child_start(struct child_data *child, const char *program)
/* /*
* Read from the startup pipe, there should be no data * Read from the startup pipe, there should be no data
* and we should block until it is closed. We just * 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)); ret = read(3, &i, sizeof(i));
if (ret < 0) if (ret < 0)
@ -549,7 +549,7 @@ int main(int argc, char **argv)
evs = calloc(tests, sizeof(*evs)); evs = calloc(tests, sizeof(*evs));
if (!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); tests);
for (i = 0; i < cpus; i++) { for (i = 0; i < cpus; i++) {

View File

@ -188,13 +188,13 @@ static bool create_socket(void)
ref = malloc(digest_len); ref = malloc(digest_len);
if (!ref) { if (!ref) {
printf("Failed to allocated %d byte reference\n", digest_len); printf("Failed to allocate %d byte reference\n", digest_len);
return false; return false;
} }
digest = malloc(digest_len); digest = malloc(digest_len);
if (!digest) { if (!digest) {
printf("Failed to allocated %d byte digest\n", digest_len); printf("Failed to allocate %d byte digest\n", digest_len);
return false; 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 VL_TESTS (((TEST_VQ_MAX - SVE_VQ_MIN) + 1) * 4)
#define FLAG_TESTS 2 #define FLAG_TESTS 4
#define FPSIMD_TESTS 2 #define FPSIMD_TESTS 2
#define EXPECTED_TESTS ((VL_TESTS + FLAG_TESTS + FPSIMD_TESTS) * ARRAY_SIZE(vec_types)) #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) static int get_fpsimd(pid_t pid, struct user_fpsimd_state *fpsimd)
{ {
struct iovec iov; struct iovec iov;
int ret;
iov.iov_base = fpsimd; iov.iov_base = fpsimd;
iov.iov_len = sizeof(*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) static int set_fpsimd(pid_t pid, struct user_fpsimd_state *fpsimd)
{ {
struct iovec iov; struct iovec iov;
int ret;
iov.iov_base = fpsimd; iov.iov_base = fpsimd;
iov.iov_len = sizeof(*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, 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; struct user_sve_header *sve;
void *p; void *p;
size_t sz = sizeof *sve; size_t sz = sizeof(*sve);
struct iovec iov; struct iovec iov;
int ret;
while (1) { while (1) {
if (*size < sz) { 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_base = *buf;
iov.iov_len = sz; 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; goto error;
}
sve = *buf; sve = *buf;
if (sve->size <= sz) 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) const struct user_sve_header *sve)
{ {
struct iovec iov; struct iovec iov;
int ret;
iov.iov_base = (void *)sve; iov.iov_base = (void *)sve;
iov.iov_len = sve->size; 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 */ /* 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 */ /* Access the FPSIMD registers via the SVE regset */
static void ptrace_sve_fpsimd(pid_t child, const struct vec_type *type) 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++) { 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 */ /* FPSIMD via SVE regset */
if (getauxval(vec_types[i].hwcap_type) & vec_types[i].hwcap) { if (getauxval(vec_types[i].hwcap_type) & vec_types[i].hwcap) {
ptrace_sve_fpsimd(child, &vec_types[i]); ptrace_sve_fpsimd(child, &vec_types[i]);
@ -703,6 +784,17 @@ static int do_parent(pid_t child)
vec_types[i].name); 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 */ /* Step through every possible VQ */
for (vq = SVE_VQ_MIN; vq <= TEST_VQ_MAX; vq++) { for (vq = SVE_VQ_MIN; vq <= TEST_VQ_MAX; vq++) {
vl = sve_vl_from_vq(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"); asm volatile("msr S0_3_C4_C6_3, xzr");
} }
/* /*
* Verify we can change the SVE vector length while SME is active and * Verify we can change the SVE vector length while SME is active and
* continue to use SME afterwards. * 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); return ptrace(PTRACE_GETREGSET, pid, NT_ARM_ZT, &iov);
} }
static int set_zt(pid_t pid, const char zt[ZT_SIG_REG_BYTES]) static int set_zt(pid_t pid, const char zt[ZT_SIG_REG_BYTES])
{ {
struct iovec iov; struct iovec iov;

View File

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

View File

@ -10,6 +10,7 @@
#include <sys/mman.h> #include <sys/mman.h>
#include <asm/mman.h> #include <asm/mman.h>
#include <asm/hwcap.h>
#include <linux/sched.h> #include <linux/sched.h>
#include "kselftest.h" #include "kselftest.h"
@ -386,14 +387,13 @@ int main(void)
ksft_print_header(); ksft_print_header();
/* if (!(getauxval(AT_HWCAP) & HWCAP_GCS))
* We don't have getauxval() with nolibc so treat a failure to ksft_exit_skip("SKIP GCS not supported\n");
* read GCS state as a lack of support and skip.
*/
ret = my_syscall5(__NR_prctl, PR_GET_SHADOW_STACK_STATUS, ret = my_syscall5(__NR_prctl, PR_GET_SHADOW_STACK_STATUS,
&gcs_mode, 0, 0, 0); &gcs_mode, 0, 0, 0);
if (ret != 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)) { if (!(gcs_mode & PR_SHADOW_STACK_ENABLE)) {
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 */ /* 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) if (ret != 0)
ksft_print_msg("Failed to disable GCS: %d\n", ret); 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(ret, 0);
ASSERT_EQ(mode, PR_SHADOW_STACK_ALL_MODES); ASSERT_EQ(mode, PR_SHADOW_STACK_ALL_MODES);
ret = my_syscall2(__NR_prctl, PR_SET_SHADOW_STACK_STATUS, ret = my_syscall2(__NR_prctl, PR_SET_SHADOW_STACK_STATUS,
variant->mode); variant->mode);
ASSERT_EQ(ret, 0); ASSERT_EQ(ret, 0);

View File

@ -433,7 +433,7 @@ int main(int argc, char **argv)
evs = calloc(tests, sizeof(*evs)); evs = calloc(tests, sizeof(*evs));
if (!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); tests);
for (i = 0; i < gcs_threads; i++) for (i = 0; i < gcs_threads; i++)

View File

@ -13,7 +13,12 @@ int main(void)
unsigned long hwcaps; unsigned long hwcaps;
size_t val; 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 /* don't try to execute illegal (unimplemented) instructions) caller
* should have checked this and keep worker simple * should have checked this and keep worker simple