Commit c3976232 authored by Catalin Marinas's avatar Catalin Marinas
Browse files

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

* for-next/kselftest: (28 commits)
  : Kselftest updates for arm64
  kselftest/arm64: Handle EINTR while reading data from children
  kselftest/arm64: Flag fp-stress as exiting when we begin finishing up
  kselftest/arm64: Don't repeat termination handler for fp-stress
  kselftest/arm64: Don't enable v8.5 for MTE selftest builds
  kselftest/arm64: Fix typo in hwcap check
  kselftest/arm64: Add hwcap test for RNG
  kselftest/arm64: Add SVE 2 to the tested hwcaps
  kselftest/arm64: Add missing newline in hwcap output
  kselftest/arm64: Fix spelling misakes of signal names
  kselftest/arm64: Enforce actual ABI for SVE syscalls
  kselftest/arm64: Correct buffer allocation for SVE Z registers
  kselftest/arm64: Include larger SVE and SME VLs in signal tests
  kselftest/arm64: Allow larger buffers in get_signal_context()
  kselftest/arm64: Preserve any EXTRA_CONTEXT in handle_signal_copyctx()
  kselftest/arm64: Validate contents of EXTRA_CONTEXT blocks
  kselftest/arm64: Only validate each signal context once
  kselftest/arm64: Remove unneeded protype for validate_extra_context()
  kselftest/arm64: Fix validation of EXTRA_CONTEXT signal context location
  kselftest/arm64: Fix validatation termination record after EXTRA_CONTEXT
  kselftest/arm64: Validate signal ucontext in place
  ...
parents b23ec74c a7119874
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
hwcap
ptrace
syscall-abi
tpidr2
+1 −1
Original line number Diff line number Diff line
# SPDX-License-Identifier: GPL-2.0
# Copyright (C) 2021 ARM Limited

TEST_GEN_PROGS := ptrace syscall-abi tpidr2
TEST_GEN_PROGS := hwcap ptrace syscall-abi tpidr2

include ../../lib.mk

+336 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (C) 2022 ARM Limited.
 */

#include <errno.h>
#include <signal.h>
#include <stdbool.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 <asm/hwcap.h>
#include <asm/sigcontext.h>
#include <asm/unistd.h>

#include "../../kselftest.h"

#define TESTS_PER_HWCAP 2

/*
 * 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.
 *
 * 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);

static void rng_sigill(void)
{
	asm volatile("mrs x0, S3_3_C2_C4_0" : : : "x0");
}

static void sme_sigill(void)
{
	/* RDSVL x0, #0 */
	asm volatile(".inst 0x04bf5800" : : : "x0");
}

static void sve_sigill(void)
{
	/* RDVL x0, #0 */
	asm volatile(".inst 0x04bf5000" : : : "x0");
}

static void sve2_sigill(void)
{
	/* SQABS Z0.b, P0/M, Z0.B */
	asm volatile(".inst 0x4408A000" : : : "z0");
}

static void sveaes_sigill(void)
{
	/* AESD z0.b, z0.b, z0.b */
	asm volatile(".inst 0x4522e400" : : : "z0");
}

static void svepmull_sigill(void)
{
	/* PMULLB Z0.Q, Z0.D, Z0.D */
	asm volatile(".inst 0x45006800" : : : "z0");
}

static void svebitperm_sigill(void)
{
	/* BDEP Z0.B, Z0.B, Z0.B */
	asm volatile(".inst 0x4500b400" : : : "z0");
}

static void svesha3_sigill(void)
{
	/* EOR3 Z0.D, Z0.D, Z0.D, Z0.D */
	asm volatile(".inst 0x4203800" : : : "z0");
}

static void svesm4_sigill(void)
{
	/* SM4E Z0.S, Z0.S, Z0.S */
	asm volatile(".inst 0x4523e000" : : : "z0");
}

static void svei8mm_sigill(void)
{
	/* USDOT Z0.S, Z0.B, Z0.B[0] */
	asm volatile(".inst 0x44a01800" : : : "z0");
}

static void svef32mm_sigill(void)
{
	/* FMMLA Z0.S, Z0.S, Z0.S */
	asm volatile(".inst 0x64a0e400" : : : "z0");
}

static void svef64mm_sigill(void)
{
	/* FMMLA Z0.D, Z0.D, Z0.D */
	asm volatile(".inst 0x64e0e400" : : : "z0");
}

static void svebf16_sigill(void)
{
	/* BFCVT Z0.H, P0/M, Z0.S */
	asm volatile(".inst 0x658aa000" : : : "z0");
}

static const struct hwcap_data {
	const char *name;
	unsigned long at_hwcap;
	unsigned long hwcap_bit;
	const char *cpuinfo;
	sigill_fn sigill_fn;
	bool sigill_reliable;
} hwcaps[] = {
	{
		.name = "RNG",
		.at_hwcap = AT_HWCAP2,
		.hwcap_bit = HWCAP2_RNG,
		.cpuinfo = "rng",
		.sigill_fn = rng_sigill,
	},
	{
		.name = "SME",
		.at_hwcap = AT_HWCAP2,
		.hwcap_bit = HWCAP2_SME,
		.cpuinfo = "sme",
		.sigill_fn = sme_sigill,
		.sigill_reliable = true,
	},
	{
		.name = "SVE",
		.at_hwcap = AT_HWCAP,
		.hwcap_bit = HWCAP_SVE,
		.cpuinfo = "sve",
		.sigill_fn = sve_sigill,
		.sigill_reliable = true,
	},
	{
		.name = "SVE 2",
		.at_hwcap = AT_HWCAP2,
		.hwcap_bit = HWCAP2_SVE2,
		.cpuinfo = "sve2",
		.sigill_fn = sve2_sigill,
	},
	{
		.name = "SVE AES",
		.at_hwcap = AT_HWCAP2,
		.hwcap_bit = HWCAP2_SVEAES,
		.cpuinfo = "sveaes",
		.sigill_fn = sveaes_sigill,
	},
	{
		.name = "SVE2 PMULL",
		.at_hwcap = AT_HWCAP2,
		.hwcap_bit = HWCAP2_SVEPMULL,
		.cpuinfo = "svepmull",
		.sigill_fn = svepmull_sigill,
	},
	{
		.name = "SVE2 BITPERM",
		.at_hwcap = AT_HWCAP2,
		.hwcap_bit = HWCAP2_SVEBITPERM,
		.cpuinfo = "svebitperm",
		.sigill_fn = svebitperm_sigill,
	},
	{
		.name = "SVE2 SHA3",
		.at_hwcap = AT_HWCAP2,
		.hwcap_bit = HWCAP2_SVESHA3,
		.cpuinfo = "svesha3",
		.sigill_fn = svesha3_sigill,
	},
	{
		.name = "SVE2 SM4",
		.at_hwcap = AT_HWCAP2,
		.hwcap_bit = HWCAP2_SVESM4,
		.cpuinfo = "svesm4",
		.sigill_fn = svesm4_sigill,
	},
	{
		.name = "SVE2 I8MM",
		.at_hwcap = AT_HWCAP2,
		.hwcap_bit = HWCAP2_SVEI8MM,
		.cpuinfo = "svei8mm",
		.sigill_fn = svei8mm_sigill,
	},
	{
		.name = "SVE2 F32MM",
		.at_hwcap = AT_HWCAP2,
		.hwcap_bit = HWCAP2_SVEF32MM,
		.cpuinfo = "svef32mm",
		.sigill_fn = svef32mm_sigill,
	},
	{
		.name = "SVE2 F64MM",
		.at_hwcap = AT_HWCAP2,
		.hwcap_bit = HWCAP2_SVEF64MM,
		.cpuinfo = "svef64mm",
		.sigill_fn = svef64mm_sigill,
	},
	{
		.name = "SVE2 BF16",
		.at_hwcap = AT_HWCAP2,
		.hwcap_bit = HWCAP2_SVEBF16,
		.cpuinfo = "svebf16",
		.sigill_fn = svebf16_sigill,
	},
	{
		.name = "SVE2 EBF16",
		.at_hwcap = AT_HWCAP2,
		.hwcap_bit = HWCAP2_SVE_EBF16,
		.cpuinfo = "sveebf16",
	},
};

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

bool cpuinfo_present(const char *name)
{
	FILE *f;
	char buf[2048], name_space[30], name_newline[30];
	char *s;

	/*
	 * The feature should appear with a leading space and either a
	 * trailing space or a newline.
	 */
	snprintf(name_space, sizeof(name_space), " %s ", name);
	snprintf(name_newline, sizeof(name_newline), " %s\n", name);

	f = fopen("/proc/cpuinfo", "r");
	if (!f) {
		ksft_print_msg("Failed to open /proc/cpuinfo\n");
		return false;
	}

	while (fgets(buf, sizeof(buf), f)) {
		/* Features: line? */
		if (strncmp(buf, "Features\t:", strlen("Features\t:")) != 0)
			continue;

		/* All CPUs should be symmetric, don't read any more */
		fclose(f);

		s = strstr(buf, name_space);
		if (s)
			return true;
		s = strstr(buf, name_newline);
		if (s)
			return true;

		return false;
	}

	ksft_print_msg("Failed to find Features in /proc/cpuinfo\n");
	fclose(f);
	return false;
}

int main(void)
{
	const struct hwcap_data *hwcap;
	int i, ret;
	bool have_cpuinfo, have_hwcap;
	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_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];

		have_hwcap = getauxval(hwcap->at_hwcap) & hwcap->hwcap_bit;
		have_cpuinfo = cpuinfo_present(hwcap->cpuinfo);

		if (have_hwcap)
			ksft_print_msg("%s present\n", hwcap->name);

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

	ksft_print_cnts();

	return 0;
}
+40 −21
Original line number Diff line number Diff line
@@ -112,9 +112,11 @@ static int check_fpr(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
	return errors;
}

#define SVE_Z_SHARED_BYTES (128 / 8)

static uint8_t z_zero[__SVE_ZREG_SIZE(SVE_VQ_MAX)];
uint8_t z_in[SVE_NUM_PREGS * __SVE_ZREG_SIZE(SVE_VQ_MAX)];
uint8_t z_out[SVE_NUM_PREGS * __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 void setup_z(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
		    uint64_t svcr)
@@ -133,23 +135,40 @@ static int check_z(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
	if (!sve_vl)
		return 0;

	/*
	 * After a syscall the low 128 bits of the Z registers should
	 * be preserved and the rest be zeroed or preserved, except if
	 * we were in streaming mode in which case the low 128 bits may
	 * also be cleared by the transition out of streaming mode.
	 */
	for (i = 0; i < SVE_NUM_ZREGS; i++) {
		void *in = &z_in[reg_size * i];
		void *out = &z_out[reg_size * i];
		uint8_t *in = &z_in[reg_size * i];
		uint8_t *out = &z_out[reg_size * i];

		if ((memcmp(in, out, SVE_VQ_BYTES) != 0) &&
		    !((svcr & SVCR_SM_MASK) &&
		      memcmp(z_zero, out, SVE_VQ_BYTES) == 0)) {
		if (svcr & SVCR_SM_MASK) {
			/*
			 * In streaming mode the whole register should
			 * be cleared by the transition out of
			 * streaming mode.
			 */
			if (memcmp(z_zero, out, reg_size) != 0) {
				ksft_print_msg("%s SVE VL %d Z%d non-zero\n",
					       cfg->name, sve_vl, i);
				errors++;
			}
		} else {
			/*
			 * For standard SVE the low 128 bits should be
			 * preserved and any additional bits cleared.
			 */
			if (memcmp(in, out, SVE_Z_SHARED_BYTES) != 0) {
				ksft_print_msg("%s SVE VL %d Z%d low 128 bits changed\n",
					       cfg->name, sve_vl, i);
				errors++;
			}

			if (reg_size > SVE_Z_SHARED_BYTES &&
			    (memcmp(z_zero, out + SVE_Z_SHARED_BYTES,
				    reg_size - SVE_Z_SHARED_BYTES) != 0)) {
				ksft_print_msg("%s SVE VL %d Z%d high bits non-zero\n",
					       cfg->name, sve_vl, i);
				errors++;
			}
		}
	}

	return errors;
@@ -176,9 +195,9 @@ static int check_p(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
	if (!sve_vl)
		return 0;

	/* After a syscall the P registers should be preserved or zeroed */
	/* After a syscall the P registers should be zeroed */
	for (i = 0; i < SVE_NUM_PREGS * reg_size; i++)
		if (p_out[i] && (p_in[i] != p_out[i]))
		if (p_out[i])
			errors++;
	if (errors)
		ksft_print_msg("%s SVE VL %d predicate registers non-zero\n",
@@ -226,9 +245,9 @@ static int check_ffr(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
	    !(getauxval(AT_HWCAP2) & HWCAP2_SME_FA64))
		return 0;

	/* After a syscall the P registers should be preserved or zeroed */
	/* After a syscall FFR should be zeroed */
	for (i = 0; i < reg_size; i++)
		if (ffr_out[i] && (ffr_in[i] != ffr_out[i]))
		if (ffr_out[i])
			errors++;
	if (errors)
		ksft_print_msg("%s SVE VL %d FFR non-zero\n",
+1 −0
Original line number Diff line number Diff line
fp-pidbench
fp-stress
fpsimd-test
rdvl-sme
rdvl-sve
Loading