Commit e1df2721 authored by Will Deacon's avatar Will Deacon
Browse files

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
  ...
parents f8f62118 94f23ac3
Loading
Loading
Loading
Loading
+18 −0
Original line number Diff line number Diff line
@@ -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 */
+2 −0
Original line number Diff line number Diff line
@@ -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

+254 −50
Original line number Diff line number Diff line
@@ -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;

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;
typedef void (*sighandler_fn)(int, siginfo_t *, void *);

#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;
}

int main(void)
static int install_sigaction(int signum, sighandler_fn handler)
{
	const struct hwcap_data *hwcap;
	int i, ret;
	bool have_cpuinfo, have_hwcap;
	int ret;
	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_sigaction = handler;
	sa.sa_flags = SA_RESTART | SA_SIGINFO;
	sigemptyset(&sa.sa_mask);
	ret = sigaction(SIGILL, &sa, NULL);
	ret = sigaction(signum, &sa, NULL);
	if (ret < 0)
		ksft_exit_fail_msg("Failed to install SIGILL handler: %s (%d)\n",
		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;
	bool have_cpuinfo, have_hwcap, raise_sigill;

	ksft_print_header();
	ksft_set_plan(ARRAY_SIZE(hwcaps) * TESTS_PER_HWCAP);

	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();
+23 −15
Original line number Diff line number Diff line
@@ -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",
+20 −25
Original line number Diff line number Diff line
@@ -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)/
Loading