1b77e995eSMark Brown // SPDX-License-Identifier: GPL-2.0-only
2b77e995eSMark Brown /*
3b77e995eSMark Brown  * Copyright (C) 2021 ARM Limited.
4b77e995eSMark Brown  */
5b77e995eSMark Brown 
6b77e995eSMark Brown #include <errno.h>
7b77e995eSMark Brown #include <stdbool.h>
8b77e995eSMark Brown #include <stddef.h>
9b77e995eSMark Brown #include <stdio.h>
10b77e995eSMark Brown #include <stdlib.h>
11b77e995eSMark Brown #include <string.h>
12b77e995eSMark Brown #include <unistd.h>
13b77e995eSMark Brown #include <sys/auxv.h>
14b77e995eSMark Brown #include <sys/prctl.h>
15b77e995eSMark Brown #include <asm/hwcap.h>
16b77e995eSMark Brown #include <asm/sigcontext.h>
17b77e995eSMark Brown #include <asm/unistd.h>
18b77e995eSMark Brown 
19b77e995eSMark Brown #include "../../kselftest.h"
20b77e995eSMark Brown 
2143e3f855SMark Brown #include "syscall-abi.h"
2243e3f855SMark Brown 
23*358b763eSMark Brown /*
24*358b763eSMark Brown  * The kernel defines a much larger SVE_VQ_MAX than is expressable in
25*358b763eSMark Brown  * the architecture, this creates a *lot* of overhead filling the
26*358b763eSMark Brown  * buffers (especially ZA) on emulated platforms so use the actual
27*358b763eSMark Brown  * architectural maximum instead.
28*358b763eSMark Brown  */
29*358b763eSMark Brown #define ARCH_SVE_VQ_MAX 16
30*358b763eSMark Brown 
3143e3f855SMark Brown static int default_sme_vl;
3243e3f855SMark Brown 
33fae491e5SMark Brown static int sve_vl_count;
34*358b763eSMark Brown static unsigned int sve_vls[ARCH_SVE_VQ_MAX];
35fae491e5SMark Brown static int sme_vl_count;
36*358b763eSMark Brown static unsigned int sme_vls[ARCH_SVE_VQ_MAX];
37fae491e5SMark Brown 
3843e3f855SMark Brown extern void do_syscall(int sve_vl, int sme_vl);
39b77e995eSMark Brown 
fill_random(void * buf,size_t size)40b77e995eSMark Brown static void fill_random(void *buf, size_t size)
41b77e995eSMark Brown {
42b77e995eSMark Brown 	int i;
43b77e995eSMark Brown 	uint32_t *lbuf = buf;
44b77e995eSMark Brown 
45b77e995eSMark Brown 	/* random() returns a 32 bit number regardless of the size of long */
46b77e995eSMark Brown 	for (i = 0; i < size / sizeof(uint32_t); i++)
47b77e995eSMark Brown 		lbuf[i] = random();
48b77e995eSMark Brown }
49b77e995eSMark Brown 
50b77e995eSMark Brown /*
51b77e995eSMark Brown  * We also repeat the test for several syscalls to try to expose different
52b77e995eSMark Brown  * behaviour.
53b77e995eSMark Brown  */
54b77e995eSMark Brown static struct syscall_cfg {
55b77e995eSMark Brown 	int syscall_nr;
56b77e995eSMark Brown 	const char *name;
57b77e995eSMark Brown } syscalls[] = {
58b77e995eSMark Brown 	{ __NR_getpid,		"getpid()" },
59b77e995eSMark Brown 	{ __NR_sched_yield,	"sched_yield()" },
60b77e995eSMark Brown };
61b77e995eSMark Brown 
62b77e995eSMark Brown #define NUM_GPR 31
63b77e995eSMark Brown uint64_t gpr_in[NUM_GPR];
64b77e995eSMark Brown uint64_t gpr_out[NUM_GPR];
65b77e995eSMark Brown 
setup_gpr(struct syscall_cfg * cfg,int sve_vl,int sme_vl,uint64_t svcr)6643e3f855SMark Brown static void setup_gpr(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
6743e3f855SMark Brown 		      uint64_t svcr)
68b77e995eSMark Brown {
69b77e995eSMark Brown 	fill_random(gpr_in, sizeof(gpr_in));
70b77e995eSMark Brown 	gpr_in[8] = cfg->syscall_nr;
71b77e995eSMark Brown 	memset(gpr_out, 0, sizeof(gpr_out));
72b77e995eSMark Brown }
73b77e995eSMark Brown 
check_gpr(struct syscall_cfg * cfg,int sve_vl,int sme_vl,uint64_t svcr)7443e3f855SMark Brown static int check_gpr(struct syscall_cfg *cfg, int sve_vl, int sme_vl, uint64_t svcr)
75b77e995eSMark Brown {
76b77e995eSMark Brown 	int errors = 0;
77b77e995eSMark Brown 	int i;
78b77e995eSMark Brown 
79b77e995eSMark Brown 	/*
80b77e995eSMark Brown 	 * GPR x0-x7 may be clobbered, and all others should be preserved.
81b77e995eSMark Brown 	 */
82b77e995eSMark Brown 	for (i = 9; i < ARRAY_SIZE(gpr_in); i++) {
83b77e995eSMark Brown 		if (gpr_in[i] != gpr_out[i]) {
84b77e995eSMark Brown 			ksft_print_msg("%s SVE VL %d mismatch in GPR %d: %llx != %llx\n",
85b77e995eSMark Brown 				       cfg->name, sve_vl, i,
86b77e995eSMark Brown 				       gpr_in[i], gpr_out[i]);
87b77e995eSMark Brown 			errors++;
88b77e995eSMark Brown 		}
89b77e995eSMark Brown 	}
90b77e995eSMark Brown 
91b77e995eSMark Brown 	return errors;
92b77e995eSMark Brown }
93b77e995eSMark Brown 
94b77e995eSMark Brown #define NUM_FPR 32
95b77e995eSMark Brown uint64_t fpr_in[NUM_FPR * 2];
96b77e995eSMark Brown uint64_t fpr_out[NUM_FPR * 2];
97024e4a15SMark Brown uint64_t fpr_zero[NUM_FPR * 2];
98b77e995eSMark Brown 
setup_fpr(struct syscall_cfg * cfg,int sve_vl,int sme_vl,uint64_t svcr)9943e3f855SMark Brown static void setup_fpr(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
10043e3f855SMark Brown 		      uint64_t svcr)
101b77e995eSMark Brown {
102b77e995eSMark Brown 	fill_random(fpr_in, sizeof(fpr_in));
103b77e995eSMark Brown 	memset(fpr_out, 0, sizeof(fpr_out));
104b77e995eSMark Brown }
105b77e995eSMark Brown 
check_fpr(struct syscall_cfg * cfg,int sve_vl,int sme_vl,uint64_t svcr)10643e3f855SMark Brown static int check_fpr(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
10743e3f855SMark Brown 		     uint64_t svcr)
108b77e995eSMark Brown {
109b77e995eSMark Brown 	int errors = 0;
110b77e995eSMark Brown 	int i;
111b77e995eSMark Brown 
112024e4a15SMark Brown 	if (!sve_vl && !(svcr & SVCR_SM_MASK)) {
113b77e995eSMark Brown 		for (i = 0; i < ARRAY_SIZE(fpr_in); i++) {
114b77e995eSMark Brown 			if (fpr_in[i] != fpr_out[i]) {
115b77e995eSMark Brown 				ksft_print_msg("%s Q%d/%d mismatch %llx != %llx\n",
116b77e995eSMark Brown 					       cfg->name,
117b77e995eSMark Brown 					       i / 2, i % 2,
118b77e995eSMark Brown 					       fpr_in[i], fpr_out[i]);
119b77e995eSMark Brown 				errors++;
120b77e995eSMark Brown 			}
121b77e995eSMark Brown 		}
122b77e995eSMark Brown 	}
123b77e995eSMark Brown 
124024e4a15SMark Brown 	/*
125024e4a15SMark Brown 	 * In streaming mode the whole register set should be cleared
126024e4a15SMark Brown 	 * by the transition out of streaming mode.
127024e4a15SMark Brown 	 */
128024e4a15SMark Brown 	if (svcr & SVCR_SM_MASK) {
129024e4a15SMark Brown 		if (memcmp(fpr_zero, fpr_out, sizeof(fpr_out)) != 0) {
130024e4a15SMark Brown 			ksft_print_msg("%s FPSIMD registers non-zero exiting SM\n",
131024e4a15SMark Brown 				       cfg->name);
132024e4a15SMark Brown 			errors++;
133024e4a15SMark Brown 		}
134024e4a15SMark Brown 	}
135024e4a15SMark Brown 
136b77e995eSMark Brown 	return errors;
137b77e995eSMark Brown }
138b77e995eSMark Brown 
139af3ce550SMark Brown #define SVE_Z_SHARED_BYTES (128 / 8)
140af3ce550SMark Brown 
141*358b763eSMark Brown static uint8_t z_zero[__SVE_ZREG_SIZE(ARCH_SVE_VQ_MAX)];
142*358b763eSMark Brown uint8_t z_in[SVE_NUM_ZREGS * __SVE_ZREG_SIZE(ARCH_SVE_VQ_MAX)];
143*358b763eSMark Brown uint8_t z_out[SVE_NUM_ZREGS * __SVE_ZREG_SIZE(ARCH_SVE_VQ_MAX)];
144b77e995eSMark Brown 
setup_z(struct syscall_cfg * cfg,int sve_vl,int sme_vl,uint64_t svcr)14543e3f855SMark Brown static void setup_z(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
14643e3f855SMark Brown 		    uint64_t svcr)
147b77e995eSMark Brown {
148b77e995eSMark Brown 	fill_random(z_in, sizeof(z_in));
149b77e995eSMark Brown 	fill_random(z_out, sizeof(z_out));
150b77e995eSMark Brown }
151b77e995eSMark Brown 
check_z(struct syscall_cfg * cfg,int sve_vl,int sme_vl,uint64_t svcr)15243e3f855SMark Brown static int check_z(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
15343e3f855SMark Brown 		   uint64_t svcr)
154b77e995eSMark Brown {
155b77e995eSMark Brown 	size_t reg_size = sve_vl;
156b77e995eSMark Brown 	int errors = 0;
157b77e995eSMark Brown 	int i;
158b77e995eSMark Brown 
159b77e995eSMark Brown 	if (!sve_vl)
160b77e995eSMark Brown 		return 0;
161b77e995eSMark Brown 
162b77e995eSMark Brown 	for (i = 0; i < SVE_NUM_ZREGS; i++) {
163af3ce550SMark Brown 		uint8_t *in = &z_in[reg_size * i];
164af3ce550SMark Brown 		uint8_t *out = &z_out[reg_size * i];
165b77e995eSMark Brown 
166af3ce550SMark Brown 		if (svcr & SVCR_SM_MASK) {
167af3ce550SMark Brown 			/*
168af3ce550SMark Brown 			 * In streaming mode the whole register should
169af3ce550SMark Brown 			 * be cleared by the transition out of
170af3ce550SMark Brown 			 * streaming mode.
171af3ce550SMark Brown 			 */
172af3ce550SMark Brown 			if (memcmp(z_zero, out, reg_size) != 0) {
173af3ce550SMark Brown 				ksft_print_msg("%s SVE VL %d Z%d non-zero\n",
174af3ce550SMark Brown 					       cfg->name, sve_vl, i);
175af3ce550SMark Brown 				errors++;
176af3ce550SMark Brown 			}
177af3ce550SMark Brown 		} else {
178af3ce550SMark Brown 			/*
179af3ce550SMark Brown 			 * For standard SVE the low 128 bits should be
180af3ce550SMark Brown 			 * preserved and any additional bits cleared.
181af3ce550SMark Brown 			 */
182af3ce550SMark Brown 			if (memcmp(in, out, SVE_Z_SHARED_BYTES) != 0) {
183b77e995eSMark Brown 				ksft_print_msg("%s SVE VL %d Z%d low 128 bits changed\n",
184b77e995eSMark Brown 					       cfg->name, sve_vl, i);
185b77e995eSMark Brown 				errors++;
186b77e995eSMark Brown 			}
187af3ce550SMark Brown 
188af3ce550SMark Brown 			if (reg_size > SVE_Z_SHARED_BYTES &&
189af3ce550SMark Brown 			    (memcmp(z_zero, out + SVE_Z_SHARED_BYTES,
190af3ce550SMark Brown 				    reg_size - SVE_Z_SHARED_BYTES) != 0)) {
191af3ce550SMark Brown 				ksft_print_msg("%s SVE VL %d Z%d high bits non-zero\n",
192af3ce550SMark Brown 					       cfg->name, sve_vl, i);
193af3ce550SMark Brown 				errors++;
194af3ce550SMark Brown 			}
195af3ce550SMark Brown 		}
196b77e995eSMark Brown 	}
197b77e995eSMark Brown 
198b77e995eSMark Brown 	return errors;
199b77e995eSMark Brown }
200b77e995eSMark Brown 
201*358b763eSMark Brown uint8_t p_in[SVE_NUM_PREGS * __SVE_PREG_SIZE(ARCH_SVE_VQ_MAX)];
202*358b763eSMark Brown uint8_t p_out[SVE_NUM_PREGS * __SVE_PREG_SIZE(ARCH_SVE_VQ_MAX)];
203b77e995eSMark Brown 
setup_p(struct syscall_cfg * cfg,int sve_vl,int sme_vl,uint64_t svcr)20443e3f855SMark Brown static void setup_p(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
20543e3f855SMark Brown 		    uint64_t svcr)
206b77e995eSMark Brown {
207b77e995eSMark Brown 	fill_random(p_in, sizeof(p_in));
208b77e995eSMark Brown 	fill_random(p_out, sizeof(p_out));
209b77e995eSMark Brown }
210b77e995eSMark Brown 
check_p(struct syscall_cfg * cfg,int sve_vl,int sme_vl,uint64_t svcr)21143e3f855SMark Brown static int check_p(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
21243e3f855SMark Brown 		   uint64_t svcr)
213b77e995eSMark Brown {
214b77e995eSMark Brown 	size_t reg_size = sve_vq_from_vl(sve_vl) * 2; /* 1 bit per VL byte */
215b77e995eSMark Brown 
216b77e995eSMark Brown 	int errors = 0;
217b77e995eSMark Brown 	int i;
218b77e995eSMark Brown 
219b77e995eSMark Brown 	if (!sve_vl)
220b77e995eSMark Brown 		return 0;
221b77e995eSMark Brown 
222af3ce550SMark Brown 	/* After a syscall the P registers should be zeroed */
223b77e995eSMark Brown 	for (i = 0; i < SVE_NUM_PREGS * reg_size; i++)
224af3ce550SMark Brown 		if (p_out[i])
225b77e995eSMark Brown 			errors++;
226b77e995eSMark Brown 	if (errors)
227b77e995eSMark Brown 		ksft_print_msg("%s SVE VL %d predicate registers non-zero\n",
228b77e995eSMark Brown 			       cfg->name, sve_vl);
229b77e995eSMark Brown 
230b77e995eSMark Brown 	return errors;
231b77e995eSMark Brown }
232b77e995eSMark Brown 
233*358b763eSMark Brown uint8_t ffr_in[__SVE_PREG_SIZE(ARCH_SVE_VQ_MAX)];
234*358b763eSMark Brown uint8_t ffr_out[__SVE_PREG_SIZE(ARCH_SVE_VQ_MAX)];
235b77e995eSMark Brown 
setup_ffr(struct syscall_cfg * cfg,int sve_vl,int sme_vl,uint64_t svcr)23643e3f855SMark Brown static void setup_ffr(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
23743e3f855SMark Brown 		      uint64_t svcr)
238b77e995eSMark Brown {
239b77e995eSMark Brown 	/*
24043e3f855SMark Brown 	 * If we are in streaming mode and do not have FA64 then FFR
24143e3f855SMark Brown 	 * is unavailable.
24243e3f855SMark Brown 	 */
24343e3f855SMark Brown 	if ((svcr & SVCR_SM_MASK) &&
24443e3f855SMark Brown 	    !(getauxval(AT_HWCAP2) & HWCAP2_SME_FA64)) {
24543e3f855SMark Brown 		memset(&ffr_in, 0, sizeof(ffr_in));
24643e3f855SMark Brown 		return;
24743e3f855SMark Brown 	}
24843e3f855SMark Brown 
24943e3f855SMark Brown 	/*
250b77e995eSMark Brown 	 * It is only valid to set a contiguous set of bits starting
251b77e995eSMark Brown 	 * at 0.  For now since we're expecting this to be cleared by
252b77e995eSMark Brown 	 * a syscall just set all bits.
253b77e995eSMark Brown 	 */
254b77e995eSMark Brown 	memset(ffr_in, 0xff, sizeof(ffr_in));
255b77e995eSMark Brown 	fill_random(ffr_out, sizeof(ffr_out));
256b77e995eSMark Brown }
257b77e995eSMark Brown 
check_ffr(struct syscall_cfg * cfg,int sve_vl,int sme_vl,uint64_t svcr)25843e3f855SMark Brown static int check_ffr(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
25943e3f855SMark Brown 		     uint64_t svcr)
260b77e995eSMark Brown {
261b77e995eSMark Brown 	size_t reg_size = sve_vq_from_vl(sve_vl) * 2;  /* 1 bit per VL byte */
262b77e995eSMark Brown 	int errors = 0;
263b77e995eSMark Brown 	int i;
264b77e995eSMark Brown 
265b77e995eSMark Brown 	if (!sve_vl)
266b77e995eSMark Brown 		return 0;
267b77e995eSMark Brown 
26843e3f855SMark Brown 	if ((svcr & SVCR_SM_MASK) &&
26943e3f855SMark Brown 	    !(getauxval(AT_HWCAP2) & HWCAP2_SME_FA64))
27043e3f855SMark Brown 		return 0;
27143e3f855SMark Brown 
272af3ce550SMark Brown 	/* After a syscall FFR should be zeroed */
273b77e995eSMark Brown 	for (i = 0; i < reg_size; i++)
274af3ce550SMark Brown 		if (ffr_out[i])
275b77e995eSMark Brown 			errors++;
276b77e995eSMark Brown 	if (errors)
277b77e995eSMark Brown 		ksft_print_msg("%s SVE VL %d FFR non-zero\n",
278b77e995eSMark Brown 			       cfg->name, sve_vl);
279b77e995eSMark Brown 
280b77e995eSMark Brown 	return errors;
281b77e995eSMark Brown }
282b77e995eSMark Brown 
28343e3f855SMark Brown uint64_t svcr_in, svcr_out;
28443e3f855SMark Brown 
setup_svcr(struct syscall_cfg * cfg,int sve_vl,int sme_vl,uint64_t svcr)28543e3f855SMark Brown static void setup_svcr(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
28643e3f855SMark Brown 		    uint64_t svcr)
28743e3f855SMark Brown {
28843e3f855SMark Brown 	svcr_in = svcr;
28943e3f855SMark Brown }
29043e3f855SMark Brown 
check_svcr(struct syscall_cfg * cfg,int sve_vl,int sme_vl,uint64_t svcr)29143e3f855SMark Brown static int check_svcr(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
29243e3f855SMark Brown 		      uint64_t svcr)
29343e3f855SMark Brown {
29443e3f855SMark Brown 	int errors = 0;
29543e3f855SMark Brown 
29643e3f855SMark Brown 	if (svcr_out & SVCR_SM_MASK) {
29743e3f855SMark Brown 		ksft_print_msg("%s Still in SM, SVCR %llx\n",
29843e3f855SMark Brown 			       cfg->name, svcr_out);
29943e3f855SMark Brown 		errors++;
30043e3f855SMark Brown 	}
30143e3f855SMark Brown 
30243e3f855SMark Brown 	if ((svcr_in & SVCR_ZA_MASK) != (svcr_out & SVCR_ZA_MASK)) {
30343e3f855SMark Brown 		ksft_print_msg("%s PSTATE.ZA changed, SVCR %llx != %llx\n",
30443e3f855SMark Brown 			       cfg->name, svcr_in, svcr_out);
30543e3f855SMark Brown 		errors++;
30643e3f855SMark Brown 	}
30743e3f855SMark Brown 
30843e3f855SMark Brown 	return errors;
30943e3f855SMark Brown }
31043e3f855SMark Brown 
311*358b763eSMark Brown uint8_t za_in[ZA_SIG_REGS_SIZE(ARCH_SVE_VQ_MAX)];
312*358b763eSMark Brown uint8_t za_out[ZA_SIG_REGS_SIZE(ARCH_SVE_VQ_MAX)];
31343e3f855SMark Brown 
setup_za(struct syscall_cfg * cfg,int sve_vl,int sme_vl,uint64_t svcr)31443e3f855SMark Brown static void setup_za(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
31543e3f855SMark Brown 		     uint64_t svcr)
31643e3f855SMark Brown {
31743e3f855SMark Brown 	fill_random(za_in, sizeof(za_in));
31843e3f855SMark Brown 	memset(za_out, 0, sizeof(za_out));
31943e3f855SMark Brown }
32043e3f855SMark Brown 
check_za(struct syscall_cfg * cfg,int sve_vl,int sme_vl,uint64_t svcr)32143e3f855SMark Brown static int check_za(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
32243e3f855SMark Brown 		    uint64_t svcr)
32343e3f855SMark Brown {
32443e3f855SMark Brown 	size_t reg_size = sme_vl * sme_vl;
32543e3f855SMark Brown 	int errors = 0;
32643e3f855SMark Brown 
32743e3f855SMark Brown 	if (!(svcr & SVCR_ZA_MASK))
32843e3f855SMark Brown 		return 0;
32943e3f855SMark Brown 
33043e3f855SMark Brown 	if (memcmp(za_in, za_out, reg_size) != 0) {
33143e3f855SMark Brown 		ksft_print_msg("SME VL %d ZA does not match\n", sme_vl);
33243e3f855SMark Brown 		errors++;
33343e3f855SMark Brown 	}
33443e3f855SMark Brown 
33543e3f855SMark Brown 	return errors;
33643e3f855SMark Brown }
33743e3f855SMark Brown 
33849886aa9SMark Brown uint8_t zt_in[ZT_SIG_REG_BYTES] __attribute__((aligned(16)));
33949886aa9SMark Brown uint8_t zt_out[ZT_SIG_REG_BYTES] __attribute__((aligned(16)));
34049886aa9SMark Brown 
setup_zt(struct syscall_cfg * cfg,int sve_vl,int sme_vl,uint64_t svcr)34149886aa9SMark Brown static void setup_zt(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
34249886aa9SMark Brown 		     uint64_t svcr)
34349886aa9SMark Brown {
34449886aa9SMark Brown 	fill_random(zt_in, sizeof(zt_in));
34549886aa9SMark Brown 	memset(zt_out, 0, sizeof(zt_out));
34649886aa9SMark Brown }
34749886aa9SMark Brown 
check_zt(struct syscall_cfg * cfg,int sve_vl,int sme_vl,uint64_t svcr)34849886aa9SMark Brown static int check_zt(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
34949886aa9SMark Brown 		    uint64_t svcr)
35049886aa9SMark Brown {
35149886aa9SMark Brown 	int errors = 0;
35249886aa9SMark Brown 
35349886aa9SMark Brown 	if (!(getauxval(AT_HWCAP2) & HWCAP2_SME2))
35449886aa9SMark Brown 		return 0;
35549886aa9SMark Brown 
35649886aa9SMark Brown 	if (!(svcr & SVCR_ZA_MASK))
35749886aa9SMark Brown 		return 0;
35849886aa9SMark Brown 
35949886aa9SMark Brown 	if (memcmp(zt_in, zt_out, sizeof(zt_in)) != 0) {
36049886aa9SMark Brown 		ksft_print_msg("SME VL %d ZT does not match\n", sme_vl);
36149886aa9SMark Brown 		errors++;
36249886aa9SMark Brown 	}
36349886aa9SMark Brown 
36449886aa9SMark Brown 	return errors;
36549886aa9SMark Brown }
36649886aa9SMark Brown 
36743e3f855SMark Brown typedef void (*setup_fn)(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
36843e3f855SMark Brown 			 uint64_t svcr);
36943e3f855SMark Brown typedef int (*check_fn)(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
37043e3f855SMark Brown 			uint64_t svcr);
371b77e995eSMark Brown 
372b77e995eSMark Brown /*
373b77e995eSMark Brown  * Each set of registers has a setup function which is called before
374b77e995eSMark Brown  * the syscall to fill values in a global variable for loading by the
375b77e995eSMark Brown  * test code and a check function which validates that the results are
376b77e995eSMark Brown  * as expected.  Vector lengths are passed everywhere, a vector length
377b77e995eSMark Brown  * of 0 should be treated as do not test.
378b77e995eSMark Brown  */
379b77e995eSMark Brown static struct {
380b77e995eSMark Brown 	setup_fn setup;
381b77e995eSMark Brown 	check_fn check;
382b77e995eSMark Brown } regset[] = {
383b77e995eSMark Brown 	{ setup_gpr, check_gpr },
384b77e995eSMark Brown 	{ setup_fpr, check_fpr },
385b77e995eSMark Brown 	{ setup_z, check_z },
386b77e995eSMark Brown 	{ setup_p, check_p },
387b77e995eSMark Brown 	{ setup_ffr, check_ffr },
38843e3f855SMark Brown 	{ setup_svcr, check_svcr },
38943e3f855SMark Brown 	{ setup_za, check_za },
39049886aa9SMark Brown 	{ setup_zt, check_zt },
391b77e995eSMark Brown };
392b77e995eSMark Brown 
do_test(struct syscall_cfg * cfg,int sve_vl,int sme_vl,uint64_t svcr)39343e3f855SMark Brown static bool do_test(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
39443e3f855SMark Brown 		    uint64_t svcr)
395b77e995eSMark Brown {
396b77e995eSMark Brown 	int errors = 0;
397b77e995eSMark Brown 	int i;
398b77e995eSMark Brown 
399b77e995eSMark Brown 	for (i = 0; i < ARRAY_SIZE(regset); i++)
40043e3f855SMark Brown 		regset[i].setup(cfg, sve_vl, sme_vl, svcr);
401b77e995eSMark Brown 
40243e3f855SMark Brown 	do_syscall(sve_vl, sme_vl);
403b77e995eSMark Brown 
404b77e995eSMark Brown 	for (i = 0; i < ARRAY_SIZE(regset); i++)
40543e3f855SMark Brown 		errors += regset[i].check(cfg, sve_vl, sme_vl, svcr);
406b77e995eSMark Brown 
407b77e995eSMark Brown 	return errors == 0;
408b77e995eSMark Brown }
409b77e995eSMark Brown 
test_one_syscall(struct syscall_cfg * cfg)410b77e995eSMark Brown static void test_one_syscall(struct syscall_cfg *cfg)
411b77e995eSMark Brown {
412fae491e5SMark Brown 	int sve, sme;
413fae491e5SMark Brown 	int ret;
414b77e995eSMark Brown 
415b77e995eSMark Brown 	/* FPSIMD only case */
41643e3f855SMark Brown 	ksft_test_result(do_test(cfg, 0, default_sme_vl, 0),
417b77e995eSMark Brown 			 "%s FPSIMD\n", cfg->name);
418b77e995eSMark Brown 
419fae491e5SMark Brown 	for (sve = 0; sve < sve_vl_count; sve++) {
420fae491e5SMark Brown 		ret = prctl(PR_SVE_SET_VL, sve_vls[sve]);
421fae491e5SMark Brown 		if (ret == -1)
422b77e995eSMark Brown 			ksft_exit_fail_msg("PR_SVE_SET_VL failed: %s (%d)\n",
423b77e995eSMark Brown 					   strerror(errno), errno);
424b77e995eSMark Brown 
425fae491e5SMark Brown 		ksft_test_result(do_test(cfg, sve_vls[sve], default_sme_vl, 0),
426fae491e5SMark Brown 				 "%s SVE VL %d\n", cfg->name, sve_vls[sve]);
427b77e995eSMark Brown 
428fae491e5SMark Brown 		for (sme = 0; sme < sme_vl_count; sme++) {
429fae491e5SMark Brown 			ret = prctl(PR_SME_SET_VL, sme_vls[sme]);
430fae491e5SMark Brown 			if (ret == -1)
43143e3f855SMark Brown 				ksft_exit_fail_msg("PR_SME_SET_VL failed: %s (%d)\n",
43243e3f855SMark Brown 						   strerror(errno), errno);
43343e3f855SMark Brown 
434fae491e5SMark Brown 			ksft_test_result(do_test(cfg, sve_vls[sve],
435fae491e5SMark Brown 						 sme_vls[sme],
43643e3f855SMark Brown 						 SVCR_ZA_MASK | SVCR_SM_MASK),
43743e3f855SMark Brown 					 "%s SVE VL %d/SME VL %d SM+ZA\n",
438fae491e5SMark Brown 					 cfg->name, sve_vls[sve],
439fae491e5SMark Brown 					 sme_vls[sme]);
440fae491e5SMark Brown 			ksft_test_result(do_test(cfg, sve_vls[sve],
441fae491e5SMark Brown 						 sme_vls[sme], SVCR_SM_MASK),
44243e3f855SMark Brown 					 "%s SVE VL %d/SME VL %d SM\n",
443fae491e5SMark Brown 					 cfg->name, sve_vls[sve],
444fae491e5SMark Brown 					 sme_vls[sme]);
445fae491e5SMark Brown 			ksft_test_result(do_test(cfg, sve_vls[sve],
446fae491e5SMark Brown 						 sme_vls[sme], SVCR_ZA_MASK),
44743e3f855SMark Brown 					 "%s SVE VL %d/SME VL %d ZA\n",
448fae491e5SMark Brown 					 cfg->name, sve_vls[sve],
449fae491e5SMark Brown 					 sme_vls[sme]);
450b77e995eSMark Brown 		}
451b77e995eSMark Brown 	}
452b77e995eSMark Brown 
453024e4a15SMark Brown 	for (sme = 0; sme < sme_vl_count; sme++) {
454024e4a15SMark Brown 		ret = prctl(PR_SME_SET_VL, sme_vls[sme]);
455024e4a15SMark Brown 		if (ret == -1)
456024e4a15SMark Brown 			ksft_exit_fail_msg("PR_SME_SET_VL failed: %s (%d)\n",
457024e4a15SMark Brown 						   strerror(errno), errno);
458024e4a15SMark Brown 
459024e4a15SMark Brown 		ksft_test_result(do_test(cfg, 0, sme_vls[sme],
460024e4a15SMark Brown 					 SVCR_ZA_MASK | SVCR_SM_MASK),
461024e4a15SMark Brown 				 "%s SME VL %d SM+ZA\n",
462024e4a15SMark Brown 				 cfg->name, sme_vls[sme]);
463024e4a15SMark Brown 		ksft_test_result(do_test(cfg, 0, sme_vls[sme], SVCR_SM_MASK),
464024e4a15SMark Brown 				 "%s SME VL %d SM\n",
465024e4a15SMark Brown 				 cfg->name, sme_vls[sme]);
466024e4a15SMark Brown 		ksft_test_result(do_test(cfg, 0, sme_vls[sme], SVCR_ZA_MASK),
467024e4a15SMark Brown 				 "%s SME VL %d ZA\n",
468024e4a15SMark Brown 				 cfg->name, sme_vls[sme]);
469024e4a15SMark Brown 	}
470b77e995eSMark Brown }
471b77e995eSMark Brown 
sve_count_vls(void)472fae491e5SMark Brown void sve_count_vls(void)
473b77e995eSMark Brown {
474b77e995eSMark Brown 	unsigned int vq;
475b77e995eSMark Brown 	int vl;
476b77e995eSMark Brown 
477b77e995eSMark Brown 	if (!(getauxval(AT_HWCAP) & HWCAP_SVE))
478fae491e5SMark Brown 		return;
479b77e995eSMark Brown 
480b77e995eSMark Brown 	/*
481*358b763eSMark Brown 	 * Enumerate up to ARCH_SVE_VQ_MAX vector lengths
482b77e995eSMark Brown 	 */
483*358b763eSMark Brown 	for (vq = ARCH_SVE_VQ_MAX; vq > 0; vq /= 2) {
484b77e995eSMark Brown 		vl = prctl(PR_SVE_SET_VL, vq * 16);
485b77e995eSMark Brown 		if (vl == -1)
486b77e995eSMark Brown 			ksft_exit_fail_msg("PR_SVE_SET_VL failed: %s (%d)\n",
487b77e995eSMark Brown 					   strerror(errno), errno);
488b77e995eSMark Brown 
489b77e995eSMark Brown 		vl &= PR_SVE_VL_LEN_MASK;
490b77e995eSMark Brown 
491b77e995eSMark Brown 		if (vq != sve_vq_from_vl(vl))
492b77e995eSMark Brown 			vq = sve_vq_from_vl(vl);
493b77e995eSMark Brown 
494fae491e5SMark Brown 		sve_vls[sve_vl_count++] = vl;
495fae491e5SMark Brown 	}
496b77e995eSMark Brown }
497b77e995eSMark Brown 
sme_count_vls(void)498fae491e5SMark Brown void sme_count_vls(void)
49943e3f855SMark Brown {
50043e3f855SMark Brown 	unsigned int vq;
50143e3f855SMark Brown 	int vl;
50243e3f855SMark Brown 
50343e3f855SMark Brown 	if (!(getauxval(AT_HWCAP2) & HWCAP2_SME))
504fae491e5SMark Brown 		return;
50543e3f855SMark Brown 
50643e3f855SMark Brown 	/*
507*358b763eSMark Brown 	 * Enumerate up to ARCH_SVE_VQ_MAX vector lengths
50843e3f855SMark Brown 	 */
509*358b763eSMark Brown 	for (vq = ARCH_SVE_VQ_MAX; vq > 0; vq /= 2) {
51043e3f855SMark Brown 		vl = prctl(PR_SME_SET_VL, vq * 16);
51143e3f855SMark Brown 		if (vl == -1)
51243e3f855SMark Brown 			ksft_exit_fail_msg("PR_SME_SET_VL failed: %s (%d)\n",
51343e3f855SMark Brown 					   strerror(errno), errno);
51443e3f855SMark Brown 
51543e3f855SMark Brown 		vl &= PR_SME_VL_LEN_MASK;
51643e3f855SMark Brown 
51797ec597bSMark Brown 		/* Found lowest VL */
51897ec597bSMark Brown 		if (sve_vq_from_vl(vl) > vq)
51997ec597bSMark Brown 			break;
52097ec597bSMark Brown 
52143e3f855SMark Brown 		if (vq != sve_vq_from_vl(vl))
52243e3f855SMark Brown 			vq = sve_vq_from_vl(vl);
52343e3f855SMark Brown 
524fae491e5SMark Brown 		sme_vls[sme_vl_count++] = vl;
52543e3f855SMark Brown 	}
52643e3f855SMark Brown 
527fae491e5SMark Brown 	/* Ensure we configure a SME VL, used to flag if SVCR is set */
528fae491e5SMark Brown 	default_sme_vl = sme_vls[0];
52943e3f855SMark Brown }
53043e3f855SMark Brown 
main(void)531b77e995eSMark Brown int main(void)
532b77e995eSMark Brown {
533b77e995eSMark Brown 	int i;
53443e3f855SMark Brown 	int tests = 1;  /* FPSIMD */
53549886aa9SMark Brown 	int sme_ver;
536b77e995eSMark Brown 
537b77e995eSMark Brown 	srandom(getpid());
538b77e995eSMark Brown 
539b77e995eSMark Brown 	ksft_print_header();
540fae491e5SMark Brown 
541fae491e5SMark Brown 	sve_count_vls();
542fae491e5SMark Brown 	sme_count_vls();
543fae491e5SMark Brown 
544fae491e5SMark Brown 	tests += sve_vl_count;
545024e4a15SMark Brown 	tests += sme_vl_count * 3;
546fae491e5SMark Brown 	tests += (sve_vl_count * sme_vl_count) * 3;
54743e3f855SMark Brown 	ksft_set_plan(ARRAY_SIZE(syscalls) * tests);
54843e3f855SMark Brown 
54949886aa9SMark Brown 	if (getauxval(AT_HWCAP2) & HWCAP2_SME2)
55049886aa9SMark Brown 		sme_ver = 2;
55149886aa9SMark Brown 	else
55249886aa9SMark Brown 		sme_ver = 1;
55349886aa9SMark Brown 
55443e3f855SMark Brown 	if (getauxval(AT_HWCAP2) & HWCAP2_SME_FA64)
55549886aa9SMark Brown 		ksft_print_msg("SME%d with FA64\n", sme_ver);
55643e3f855SMark Brown 	else if (getauxval(AT_HWCAP2) & HWCAP2_SME)
55749886aa9SMark Brown 		ksft_print_msg("SME%d without FA64\n", sme_ver);
558b77e995eSMark Brown 
559b77e995eSMark Brown 	for (i = 0; i < ARRAY_SIZE(syscalls); i++)
560b77e995eSMark Brown 		test_one_syscall(&syscalls[i]);
561b77e995eSMark Brown 
562b77e995eSMark Brown 	ksft_print_cnts();
563b77e995eSMark Brown 
564b77e995eSMark Brown 	return 0;
565b77e995eSMark Brown }
566