337 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			337 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
// 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;
 | 
						|
}
 |