162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2021 ARM Limited.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <errno.h>
762306a36Sopenharmony_ci#include <stdbool.h>
862306a36Sopenharmony_ci#include <stddef.h>
962306a36Sopenharmony_ci#include <stdio.h>
1062306a36Sopenharmony_ci#include <stdlib.h>
1162306a36Sopenharmony_ci#include <string.h>
1262306a36Sopenharmony_ci#include <unistd.h>
1362306a36Sopenharmony_ci#include <sys/auxv.h>
1462306a36Sopenharmony_ci#include <sys/prctl.h>
1562306a36Sopenharmony_ci#include <asm/hwcap.h>
1662306a36Sopenharmony_ci#include <asm/sigcontext.h>
1762306a36Sopenharmony_ci#include <asm/unistd.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include "../../kselftest.h"
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#include "syscall-abi.h"
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci/*
2462306a36Sopenharmony_ci * The kernel defines a much larger SVE_VQ_MAX than is expressable in
2562306a36Sopenharmony_ci * the architecture, this creates a *lot* of overhead filling the
2662306a36Sopenharmony_ci * buffers (especially ZA) on emulated platforms so use the actual
2762306a36Sopenharmony_ci * architectural maximum instead.
2862306a36Sopenharmony_ci */
2962306a36Sopenharmony_ci#define ARCH_SVE_VQ_MAX 16
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic int default_sme_vl;
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic int sve_vl_count;
3462306a36Sopenharmony_cistatic unsigned int sve_vls[ARCH_SVE_VQ_MAX];
3562306a36Sopenharmony_cistatic int sme_vl_count;
3662306a36Sopenharmony_cistatic unsigned int sme_vls[ARCH_SVE_VQ_MAX];
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ciextern void do_syscall(int sve_vl, int sme_vl);
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistatic void fill_random(void *buf, size_t size)
4162306a36Sopenharmony_ci{
4262306a36Sopenharmony_ci	int i;
4362306a36Sopenharmony_ci	uint32_t *lbuf = buf;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	/* random() returns a 32 bit number regardless of the size of long */
4662306a36Sopenharmony_ci	for (i = 0; i < size / sizeof(uint32_t); i++)
4762306a36Sopenharmony_ci		lbuf[i] = random();
4862306a36Sopenharmony_ci}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci/*
5162306a36Sopenharmony_ci * We also repeat the test for several syscalls to try to expose different
5262306a36Sopenharmony_ci * behaviour.
5362306a36Sopenharmony_ci */
5462306a36Sopenharmony_cistatic struct syscall_cfg {
5562306a36Sopenharmony_ci	int syscall_nr;
5662306a36Sopenharmony_ci	const char *name;
5762306a36Sopenharmony_ci} syscalls[] = {
5862306a36Sopenharmony_ci	{ __NR_getpid,		"getpid()" },
5962306a36Sopenharmony_ci	{ __NR_sched_yield,	"sched_yield()" },
6062306a36Sopenharmony_ci};
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci#define NUM_GPR 31
6362306a36Sopenharmony_ciuint64_t gpr_in[NUM_GPR];
6462306a36Sopenharmony_ciuint64_t gpr_out[NUM_GPR];
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_cistatic void setup_gpr(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
6762306a36Sopenharmony_ci		      uint64_t svcr)
6862306a36Sopenharmony_ci{
6962306a36Sopenharmony_ci	fill_random(gpr_in, sizeof(gpr_in));
7062306a36Sopenharmony_ci	gpr_in[8] = cfg->syscall_nr;
7162306a36Sopenharmony_ci	memset(gpr_out, 0, sizeof(gpr_out));
7262306a36Sopenharmony_ci}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistatic int check_gpr(struct syscall_cfg *cfg, int sve_vl, int sme_vl, uint64_t svcr)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	int errors = 0;
7762306a36Sopenharmony_ci	int i;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	/*
8062306a36Sopenharmony_ci	 * GPR x0-x7 may be clobbered, and all others should be preserved.
8162306a36Sopenharmony_ci	 */
8262306a36Sopenharmony_ci	for (i = 9; i < ARRAY_SIZE(gpr_in); i++) {
8362306a36Sopenharmony_ci		if (gpr_in[i] != gpr_out[i]) {
8462306a36Sopenharmony_ci			ksft_print_msg("%s SVE VL %d mismatch in GPR %d: %llx != %llx\n",
8562306a36Sopenharmony_ci				       cfg->name, sve_vl, i,
8662306a36Sopenharmony_ci				       gpr_in[i], gpr_out[i]);
8762306a36Sopenharmony_ci			errors++;
8862306a36Sopenharmony_ci		}
8962306a36Sopenharmony_ci	}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	return errors;
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci#define NUM_FPR 32
9562306a36Sopenharmony_ciuint64_t fpr_in[NUM_FPR * 2];
9662306a36Sopenharmony_ciuint64_t fpr_out[NUM_FPR * 2];
9762306a36Sopenharmony_ciuint64_t fpr_zero[NUM_FPR * 2];
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cistatic void setup_fpr(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
10062306a36Sopenharmony_ci		      uint64_t svcr)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	fill_random(fpr_in, sizeof(fpr_in));
10362306a36Sopenharmony_ci	memset(fpr_out, 0, sizeof(fpr_out));
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cistatic int check_fpr(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
10762306a36Sopenharmony_ci		     uint64_t svcr)
10862306a36Sopenharmony_ci{
10962306a36Sopenharmony_ci	int errors = 0;
11062306a36Sopenharmony_ci	int i;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	if (!sve_vl && !(svcr & SVCR_SM_MASK)) {
11362306a36Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(fpr_in); i++) {
11462306a36Sopenharmony_ci			if (fpr_in[i] != fpr_out[i]) {
11562306a36Sopenharmony_ci				ksft_print_msg("%s Q%d/%d mismatch %llx != %llx\n",
11662306a36Sopenharmony_ci					       cfg->name,
11762306a36Sopenharmony_ci					       i / 2, i % 2,
11862306a36Sopenharmony_ci					       fpr_in[i], fpr_out[i]);
11962306a36Sopenharmony_ci				errors++;
12062306a36Sopenharmony_ci			}
12162306a36Sopenharmony_ci		}
12262306a36Sopenharmony_ci	}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	/*
12562306a36Sopenharmony_ci	 * In streaming mode the whole register set should be cleared
12662306a36Sopenharmony_ci	 * by the transition out of streaming mode.
12762306a36Sopenharmony_ci	 */
12862306a36Sopenharmony_ci	if (svcr & SVCR_SM_MASK) {
12962306a36Sopenharmony_ci		if (memcmp(fpr_zero, fpr_out, sizeof(fpr_out)) != 0) {
13062306a36Sopenharmony_ci			ksft_print_msg("%s FPSIMD registers non-zero exiting SM\n",
13162306a36Sopenharmony_ci				       cfg->name);
13262306a36Sopenharmony_ci			errors++;
13362306a36Sopenharmony_ci		}
13462306a36Sopenharmony_ci	}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	return errors;
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci#define SVE_Z_SHARED_BYTES (128 / 8)
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_cistatic uint8_t z_zero[__SVE_ZREG_SIZE(ARCH_SVE_VQ_MAX)];
14262306a36Sopenharmony_ciuint8_t z_in[SVE_NUM_ZREGS * __SVE_ZREG_SIZE(ARCH_SVE_VQ_MAX)];
14362306a36Sopenharmony_ciuint8_t z_out[SVE_NUM_ZREGS * __SVE_ZREG_SIZE(ARCH_SVE_VQ_MAX)];
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cistatic void setup_z(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
14662306a36Sopenharmony_ci		    uint64_t svcr)
14762306a36Sopenharmony_ci{
14862306a36Sopenharmony_ci	fill_random(z_in, sizeof(z_in));
14962306a36Sopenharmony_ci	fill_random(z_out, sizeof(z_out));
15062306a36Sopenharmony_ci}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_cistatic int check_z(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
15362306a36Sopenharmony_ci		   uint64_t svcr)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	size_t reg_size = sve_vl;
15662306a36Sopenharmony_ci	int errors = 0;
15762306a36Sopenharmony_ci	int i;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	if (!sve_vl)
16062306a36Sopenharmony_ci		return 0;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	for (i = 0; i < SVE_NUM_ZREGS; i++) {
16362306a36Sopenharmony_ci		uint8_t *in = &z_in[reg_size * i];
16462306a36Sopenharmony_ci		uint8_t *out = &z_out[reg_size * i];
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci		if (svcr & SVCR_SM_MASK) {
16762306a36Sopenharmony_ci			/*
16862306a36Sopenharmony_ci			 * In streaming mode the whole register should
16962306a36Sopenharmony_ci			 * be cleared by the transition out of
17062306a36Sopenharmony_ci			 * streaming mode.
17162306a36Sopenharmony_ci			 */
17262306a36Sopenharmony_ci			if (memcmp(z_zero, out, reg_size) != 0) {
17362306a36Sopenharmony_ci				ksft_print_msg("%s SVE VL %d Z%d non-zero\n",
17462306a36Sopenharmony_ci					       cfg->name, sve_vl, i);
17562306a36Sopenharmony_ci				errors++;
17662306a36Sopenharmony_ci			}
17762306a36Sopenharmony_ci		} else {
17862306a36Sopenharmony_ci			/*
17962306a36Sopenharmony_ci			 * For standard SVE the low 128 bits should be
18062306a36Sopenharmony_ci			 * preserved and any additional bits cleared.
18162306a36Sopenharmony_ci			 */
18262306a36Sopenharmony_ci			if (memcmp(in, out, SVE_Z_SHARED_BYTES) != 0) {
18362306a36Sopenharmony_ci				ksft_print_msg("%s SVE VL %d Z%d low 128 bits changed\n",
18462306a36Sopenharmony_ci					       cfg->name, sve_vl, i);
18562306a36Sopenharmony_ci				errors++;
18662306a36Sopenharmony_ci			}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci			if (reg_size > SVE_Z_SHARED_BYTES &&
18962306a36Sopenharmony_ci			    (memcmp(z_zero, out + SVE_Z_SHARED_BYTES,
19062306a36Sopenharmony_ci				    reg_size - SVE_Z_SHARED_BYTES) != 0)) {
19162306a36Sopenharmony_ci				ksft_print_msg("%s SVE VL %d Z%d high bits non-zero\n",
19262306a36Sopenharmony_ci					       cfg->name, sve_vl, i);
19362306a36Sopenharmony_ci				errors++;
19462306a36Sopenharmony_ci			}
19562306a36Sopenharmony_ci		}
19662306a36Sopenharmony_ci	}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	return errors;
19962306a36Sopenharmony_ci}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ciuint8_t p_in[SVE_NUM_PREGS * __SVE_PREG_SIZE(ARCH_SVE_VQ_MAX)];
20262306a36Sopenharmony_ciuint8_t p_out[SVE_NUM_PREGS * __SVE_PREG_SIZE(ARCH_SVE_VQ_MAX)];
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_cistatic void setup_p(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
20562306a36Sopenharmony_ci		    uint64_t svcr)
20662306a36Sopenharmony_ci{
20762306a36Sopenharmony_ci	fill_random(p_in, sizeof(p_in));
20862306a36Sopenharmony_ci	fill_random(p_out, sizeof(p_out));
20962306a36Sopenharmony_ci}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_cistatic int check_p(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
21262306a36Sopenharmony_ci		   uint64_t svcr)
21362306a36Sopenharmony_ci{
21462306a36Sopenharmony_ci	size_t reg_size = sve_vq_from_vl(sve_vl) * 2; /* 1 bit per VL byte */
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	int errors = 0;
21762306a36Sopenharmony_ci	int i;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	if (!sve_vl)
22062306a36Sopenharmony_ci		return 0;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	/* After a syscall the P registers should be zeroed */
22362306a36Sopenharmony_ci	for (i = 0; i < SVE_NUM_PREGS * reg_size; i++)
22462306a36Sopenharmony_ci		if (p_out[i])
22562306a36Sopenharmony_ci			errors++;
22662306a36Sopenharmony_ci	if (errors)
22762306a36Sopenharmony_ci		ksft_print_msg("%s SVE VL %d predicate registers non-zero\n",
22862306a36Sopenharmony_ci			       cfg->name, sve_vl);
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	return errors;
23162306a36Sopenharmony_ci}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ciuint8_t ffr_in[__SVE_PREG_SIZE(ARCH_SVE_VQ_MAX)];
23462306a36Sopenharmony_ciuint8_t ffr_out[__SVE_PREG_SIZE(ARCH_SVE_VQ_MAX)];
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_cistatic void setup_ffr(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
23762306a36Sopenharmony_ci		      uint64_t svcr)
23862306a36Sopenharmony_ci{
23962306a36Sopenharmony_ci	/*
24062306a36Sopenharmony_ci	 * If we are in streaming mode and do not have FA64 then FFR
24162306a36Sopenharmony_ci	 * is unavailable.
24262306a36Sopenharmony_ci	 */
24362306a36Sopenharmony_ci	if ((svcr & SVCR_SM_MASK) &&
24462306a36Sopenharmony_ci	    !(getauxval(AT_HWCAP2) & HWCAP2_SME_FA64)) {
24562306a36Sopenharmony_ci		memset(&ffr_in, 0, sizeof(ffr_in));
24662306a36Sopenharmony_ci		return;
24762306a36Sopenharmony_ci	}
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	/*
25062306a36Sopenharmony_ci	 * It is only valid to set a contiguous set of bits starting
25162306a36Sopenharmony_ci	 * at 0.  For now since we're expecting this to be cleared by
25262306a36Sopenharmony_ci	 * a syscall just set all bits.
25362306a36Sopenharmony_ci	 */
25462306a36Sopenharmony_ci	memset(ffr_in, 0xff, sizeof(ffr_in));
25562306a36Sopenharmony_ci	fill_random(ffr_out, sizeof(ffr_out));
25662306a36Sopenharmony_ci}
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_cistatic int check_ffr(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
25962306a36Sopenharmony_ci		     uint64_t svcr)
26062306a36Sopenharmony_ci{
26162306a36Sopenharmony_ci	size_t reg_size = sve_vq_from_vl(sve_vl) * 2;  /* 1 bit per VL byte */
26262306a36Sopenharmony_ci	int errors = 0;
26362306a36Sopenharmony_ci	int i;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	if (!sve_vl)
26662306a36Sopenharmony_ci		return 0;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	if ((svcr & SVCR_SM_MASK) &&
26962306a36Sopenharmony_ci	    !(getauxval(AT_HWCAP2) & HWCAP2_SME_FA64))
27062306a36Sopenharmony_ci		return 0;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	/* After a syscall FFR should be zeroed */
27362306a36Sopenharmony_ci	for (i = 0; i < reg_size; i++)
27462306a36Sopenharmony_ci		if (ffr_out[i])
27562306a36Sopenharmony_ci			errors++;
27662306a36Sopenharmony_ci	if (errors)
27762306a36Sopenharmony_ci		ksft_print_msg("%s SVE VL %d FFR non-zero\n",
27862306a36Sopenharmony_ci			       cfg->name, sve_vl);
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	return errors;
28162306a36Sopenharmony_ci}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ciuint64_t svcr_in, svcr_out;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_cistatic void setup_svcr(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
28662306a36Sopenharmony_ci		    uint64_t svcr)
28762306a36Sopenharmony_ci{
28862306a36Sopenharmony_ci	svcr_in = svcr;
28962306a36Sopenharmony_ci}
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_cistatic int check_svcr(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
29262306a36Sopenharmony_ci		      uint64_t svcr)
29362306a36Sopenharmony_ci{
29462306a36Sopenharmony_ci	int errors = 0;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	if (svcr_out & SVCR_SM_MASK) {
29762306a36Sopenharmony_ci		ksft_print_msg("%s Still in SM, SVCR %llx\n",
29862306a36Sopenharmony_ci			       cfg->name, svcr_out);
29962306a36Sopenharmony_ci		errors++;
30062306a36Sopenharmony_ci	}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	if ((svcr_in & SVCR_ZA_MASK) != (svcr_out & SVCR_ZA_MASK)) {
30362306a36Sopenharmony_ci		ksft_print_msg("%s PSTATE.ZA changed, SVCR %llx != %llx\n",
30462306a36Sopenharmony_ci			       cfg->name, svcr_in, svcr_out);
30562306a36Sopenharmony_ci		errors++;
30662306a36Sopenharmony_ci	}
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	return errors;
30962306a36Sopenharmony_ci}
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ciuint8_t za_in[ZA_SIG_REGS_SIZE(ARCH_SVE_VQ_MAX)];
31262306a36Sopenharmony_ciuint8_t za_out[ZA_SIG_REGS_SIZE(ARCH_SVE_VQ_MAX)];
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_cistatic void setup_za(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
31562306a36Sopenharmony_ci		     uint64_t svcr)
31662306a36Sopenharmony_ci{
31762306a36Sopenharmony_ci	fill_random(za_in, sizeof(za_in));
31862306a36Sopenharmony_ci	memset(za_out, 0, sizeof(za_out));
31962306a36Sopenharmony_ci}
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_cistatic int check_za(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
32262306a36Sopenharmony_ci		    uint64_t svcr)
32362306a36Sopenharmony_ci{
32462306a36Sopenharmony_ci	size_t reg_size = sme_vl * sme_vl;
32562306a36Sopenharmony_ci	int errors = 0;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	if (!(svcr & SVCR_ZA_MASK))
32862306a36Sopenharmony_ci		return 0;
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	if (memcmp(za_in, za_out, reg_size) != 0) {
33162306a36Sopenharmony_ci		ksft_print_msg("SME VL %d ZA does not match\n", sme_vl);
33262306a36Sopenharmony_ci		errors++;
33362306a36Sopenharmony_ci	}
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	return errors;
33662306a36Sopenharmony_ci}
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ciuint8_t zt_in[ZT_SIG_REG_BYTES] __attribute__((aligned(16)));
33962306a36Sopenharmony_ciuint8_t zt_out[ZT_SIG_REG_BYTES] __attribute__((aligned(16)));
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_cistatic void setup_zt(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
34262306a36Sopenharmony_ci		     uint64_t svcr)
34362306a36Sopenharmony_ci{
34462306a36Sopenharmony_ci	fill_random(zt_in, sizeof(zt_in));
34562306a36Sopenharmony_ci	memset(zt_out, 0, sizeof(zt_out));
34662306a36Sopenharmony_ci}
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_cistatic int check_zt(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
34962306a36Sopenharmony_ci		    uint64_t svcr)
35062306a36Sopenharmony_ci{
35162306a36Sopenharmony_ci	int errors = 0;
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	if (!(getauxval(AT_HWCAP2) & HWCAP2_SME2))
35462306a36Sopenharmony_ci		return 0;
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	if (!(svcr & SVCR_ZA_MASK))
35762306a36Sopenharmony_ci		return 0;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	if (memcmp(zt_in, zt_out, sizeof(zt_in)) != 0) {
36062306a36Sopenharmony_ci		ksft_print_msg("SME VL %d ZT does not match\n", sme_vl);
36162306a36Sopenharmony_ci		errors++;
36262306a36Sopenharmony_ci	}
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	return errors;
36562306a36Sopenharmony_ci}
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_citypedef void (*setup_fn)(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
36862306a36Sopenharmony_ci			 uint64_t svcr);
36962306a36Sopenharmony_citypedef int (*check_fn)(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
37062306a36Sopenharmony_ci			uint64_t svcr);
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci/*
37362306a36Sopenharmony_ci * Each set of registers has a setup function which is called before
37462306a36Sopenharmony_ci * the syscall to fill values in a global variable for loading by the
37562306a36Sopenharmony_ci * test code and a check function which validates that the results are
37662306a36Sopenharmony_ci * as expected.  Vector lengths are passed everywhere, a vector length
37762306a36Sopenharmony_ci * of 0 should be treated as do not test.
37862306a36Sopenharmony_ci */
37962306a36Sopenharmony_cistatic struct {
38062306a36Sopenharmony_ci	setup_fn setup;
38162306a36Sopenharmony_ci	check_fn check;
38262306a36Sopenharmony_ci} regset[] = {
38362306a36Sopenharmony_ci	{ setup_gpr, check_gpr },
38462306a36Sopenharmony_ci	{ setup_fpr, check_fpr },
38562306a36Sopenharmony_ci	{ setup_z, check_z },
38662306a36Sopenharmony_ci	{ setup_p, check_p },
38762306a36Sopenharmony_ci	{ setup_ffr, check_ffr },
38862306a36Sopenharmony_ci	{ setup_svcr, check_svcr },
38962306a36Sopenharmony_ci	{ setup_za, check_za },
39062306a36Sopenharmony_ci	{ setup_zt, check_zt },
39162306a36Sopenharmony_ci};
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_cistatic bool do_test(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
39462306a36Sopenharmony_ci		    uint64_t svcr)
39562306a36Sopenharmony_ci{
39662306a36Sopenharmony_ci	int errors = 0;
39762306a36Sopenharmony_ci	int i;
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(regset); i++)
40062306a36Sopenharmony_ci		regset[i].setup(cfg, sve_vl, sme_vl, svcr);
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	do_syscall(sve_vl, sme_vl);
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(regset); i++)
40562306a36Sopenharmony_ci		errors += regset[i].check(cfg, sve_vl, sme_vl, svcr);
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	return errors == 0;
40862306a36Sopenharmony_ci}
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_cistatic void test_one_syscall(struct syscall_cfg *cfg)
41162306a36Sopenharmony_ci{
41262306a36Sopenharmony_ci	int sve, sme;
41362306a36Sopenharmony_ci	int ret;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	/* FPSIMD only case */
41662306a36Sopenharmony_ci	ksft_test_result(do_test(cfg, 0, default_sme_vl, 0),
41762306a36Sopenharmony_ci			 "%s FPSIMD\n", cfg->name);
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	for (sve = 0; sve < sve_vl_count; sve++) {
42062306a36Sopenharmony_ci		ret = prctl(PR_SVE_SET_VL, sve_vls[sve]);
42162306a36Sopenharmony_ci		if (ret == -1)
42262306a36Sopenharmony_ci			ksft_exit_fail_msg("PR_SVE_SET_VL failed: %s (%d)\n",
42362306a36Sopenharmony_ci					   strerror(errno), errno);
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci		ksft_test_result(do_test(cfg, sve_vls[sve], default_sme_vl, 0),
42662306a36Sopenharmony_ci				 "%s SVE VL %d\n", cfg->name, sve_vls[sve]);
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci		for (sme = 0; sme < sme_vl_count; sme++) {
42962306a36Sopenharmony_ci			ret = prctl(PR_SME_SET_VL, sme_vls[sme]);
43062306a36Sopenharmony_ci			if (ret == -1)
43162306a36Sopenharmony_ci				ksft_exit_fail_msg("PR_SME_SET_VL failed: %s (%d)\n",
43262306a36Sopenharmony_ci						   strerror(errno), errno);
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci			ksft_test_result(do_test(cfg, sve_vls[sve],
43562306a36Sopenharmony_ci						 sme_vls[sme],
43662306a36Sopenharmony_ci						 SVCR_ZA_MASK | SVCR_SM_MASK),
43762306a36Sopenharmony_ci					 "%s SVE VL %d/SME VL %d SM+ZA\n",
43862306a36Sopenharmony_ci					 cfg->name, sve_vls[sve],
43962306a36Sopenharmony_ci					 sme_vls[sme]);
44062306a36Sopenharmony_ci			ksft_test_result(do_test(cfg, sve_vls[sve],
44162306a36Sopenharmony_ci						 sme_vls[sme], SVCR_SM_MASK),
44262306a36Sopenharmony_ci					 "%s SVE VL %d/SME VL %d SM\n",
44362306a36Sopenharmony_ci					 cfg->name, sve_vls[sve],
44462306a36Sopenharmony_ci					 sme_vls[sme]);
44562306a36Sopenharmony_ci			ksft_test_result(do_test(cfg, sve_vls[sve],
44662306a36Sopenharmony_ci						 sme_vls[sme], SVCR_ZA_MASK),
44762306a36Sopenharmony_ci					 "%s SVE VL %d/SME VL %d ZA\n",
44862306a36Sopenharmony_ci					 cfg->name, sve_vls[sve],
44962306a36Sopenharmony_ci					 sme_vls[sme]);
45062306a36Sopenharmony_ci		}
45162306a36Sopenharmony_ci	}
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	for (sme = 0; sme < sme_vl_count; sme++) {
45462306a36Sopenharmony_ci		ret = prctl(PR_SME_SET_VL, sme_vls[sme]);
45562306a36Sopenharmony_ci		if (ret == -1)
45662306a36Sopenharmony_ci			ksft_exit_fail_msg("PR_SME_SET_VL failed: %s (%d)\n",
45762306a36Sopenharmony_ci						   strerror(errno), errno);
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci		ksft_test_result(do_test(cfg, 0, sme_vls[sme],
46062306a36Sopenharmony_ci					 SVCR_ZA_MASK | SVCR_SM_MASK),
46162306a36Sopenharmony_ci				 "%s SME VL %d SM+ZA\n",
46262306a36Sopenharmony_ci				 cfg->name, sme_vls[sme]);
46362306a36Sopenharmony_ci		ksft_test_result(do_test(cfg, 0, sme_vls[sme], SVCR_SM_MASK),
46462306a36Sopenharmony_ci				 "%s SME VL %d SM\n",
46562306a36Sopenharmony_ci				 cfg->name, sme_vls[sme]);
46662306a36Sopenharmony_ci		ksft_test_result(do_test(cfg, 0, sme_vls[sme], SVCR_ZA_MASK),
46762306a36Sopenharmony_ci				 "%s SME VL %d ZA\n",
46862306a36Sopenharmony_ci				 cfg->name, sme_vls[sme]);
46962306a36Sopenharmony_ci	}
47062306a36Sopenharmony_ci}
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_civoid sve_count_vls(void)
47362306a36Sopenharmony_ci{
47462306a36Sopenharmony_ci	unsigned int vq;
47562306a36Sopenharmony_ci	int vl;
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	if (!(getauxval(AT_HWCAP) & HWCAP_SVE))
47862306a36Sopenharmony_ci		return;
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	/*
48162306a36Sopenharmony_ci	 * Enumerate up to ARCH_SVE_VQ_MAX vector lengths
48262306a36Sopenharmony_ci	 */
48362306a36Sopenharmony_ci	for (vq = ARCH_SVE_VQ_MAX; vq > 0; vq /= 2) {
48462306a36Sopenharmony_ci		vl = prctl(PR_SVE_SET_VL, vq * 16);
48562306a36Sopenharmony_ci		if (vl == -1)
48662306a36Sopenharmony_ci			ksft_exit_fail_msg("PR_SVE_SET_VL failed: %s (%d)\n",
48762306a36Sopenharmony_ci					   strerror(errno), errno);
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci		vl &= PR_SVE_VL_LEN_MASK;
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci		if (vq != sve_vq_from_vl(vl))
49262306a36Sopenharmony_ci			vq = sve_vq_from_vl(vl);
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci		sve_vls[sve_vl_count++] = vl;
49562306a36Sopenharmony_ci	}
49662306a36Sopenharmony_ci}
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_civoid sme_count_vls(void)
49962306a36Sopenharmony_ci{
50062306a36Sopenharmony_ci	unsigned int vq;
50162306a36Sopenharmony_ci	int vl;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	if (!(getauxval(AT_HWCAP2) & HWCAP2_SME))
50462306a36Sopenharmony_ci		return;
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	/*
50762306a36Sopenharmony_ci	 * Enumerate up to ARCH_SVE_VQ_MAX vector lengths
50862306a36Sopenharmony_ci	 */
50962306a36Sopenharmony_ci	for (vq = ARCH_SVE_VQ_MAX; vq > 0; vq /= 2) {
51062306a36Sopenharmony_ci		vl = prctl(PR_SME_SET_VL, vq * 16);
51162306a36Sopenharmony_ci		if (vl == -1)
51262306a36Sopenharmony_ci			ksft_exit_fail_msg("PR_SME_SET_VL failed: %s (%d)\n",
51362306a36Sopenharmony_ci					   strerror(errno), errno);
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci		vl &= PR_SME_VL_LEN_MASK;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci		/* Found lowest VL */
51862306a36Sopenharmony_ci		if (sve_vq_from_vl(vl) > vq)
51962306a36Sopenharmony_ci			break;
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci		if (vq != sve_vq_from_vl(vl))
52262306a36Sopenharmony_ci			vq = sve_vq_from_vl(vl);
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci		sme_vls[sme_vl_count++] = vl;
52562306a36Sopenharmony_ci	}
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	/* Ensure we configure a SME VL, used to flag if SVCR is set */
52862306a36Sopenharmony_ci	default_sme_vl = sme_vls[0];
52962306a36Sopenharmony_ci}
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ciint main(void)
53262306a36Sopenharmony_ci{
53362306a36Sopenharmony_ci	int i;
53462306a36Sopenharmony_ci	int tests = 1;  /* FPSIMD */
53562306a36Sopenharmony_ci	int sme_ver;
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	srandom(getpid());
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	ksft_print_header();
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	sve_count_vls();
54262306a36Sopenharmony_ci	sme_count_vls();
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	tests += sve_vl_count;
54562306a36Sopenharmony_ci	tests += sme_vl_count * 3;
54662306a36Sopenharmony_ci	tests += (sve_vl_count * sme_vl_count) * 3;
54762306a36Sopenharmony_ci	ksft_set_plan(ARRAY_SIZE(syscalls) * tests);
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	if (getauxval(AT_HWCAP2) & HWCAP2_SME2)
55062306a36Sopenharmony_ci		sme_ver = 2;
55162306a36Sopenharmony_ci	else
55262306a36Sopenharmony_ci		sme_ver = 1;
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	if (getauxval(AT_HWCAP2) & HWCAP2_SME_FA64)
55562306a36Sopenharmony_ci		ksft_print_msg("SME%d with FA64\n", sme_ver);
55662306a36Sopenharmony_ci	else if (getauxval(AT_HWCAP2) & HWCAP2_SME)
55762306a36Sopenharmony_ci		ksft_print_msg("SME%d without FA64\n", sme_ver);
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(syscalls); i++)
56062306a36Sopenharmony_ci		test_one_syscall(&syscalls[i]);
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	ksft_print_cnts();
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	return 0;
56562306a36Sopenharmony_ci}
566