10dca276aSMark Brown // SPDX-License-Identifier: GPL-2.0-only
20dca276aSMark Brown /*
3a1d71112SMark Brown  * Copyright (C) 2015-2021 ARM Limited.
40dca276aSMark Brown  * Original author: Dave Martin <Dave.Martin@arm.com>
50dca276aSMark Brown  */
60dca276aSMark Brown #include <errno.h>
7a1d71112SMark Brown #include <stdbool.h>
80dca276aSMark Brown #include <stddef.h>
90dca276aSMark Brown #include <stdio.h>
100dca276aSMark Brown #include <stdlib.h>
110dca276aSMark Brown #include <string.h>
120dca276aSMark Brown #include <unistd.h>
130dca276aSMark Brown #include <sys/auxv.h>
14a1d71112SMark Brown #include <sys/prctl.h>
150dca276aSMark Brown #include <sys/ptrace.h>
160dca276aSMark Brown #include <sys/types.h>
170dca276aSMark Brown #include <sys/uio.h>
180dca276aSMark Brown #include <sys/wait.h>
190dca276aSMark Brown #include <asm/sigcontext.h>
200dca276aSMark Brown #include <asm/ptrace.h>
210dca276aSMark Brown 
220dca276aSMark Brown #include "../../kselftest.h"
230dca276aSMark Brown 
240dca276aSMark Brown /* <linux/elf.h> and <sys/auxv.h> don't like each other, so: */
250dca276aSMark Brown #ifndef NT_ARM_SVE
260dca276aSMark Brown #define NT_ARM_SVE 0x405
270dca276aSMark Brown #endif
280dca276aSMark Brown 
29fa23100bSMark Brown #ifndef NT_ARM_SSVE
30fa23100bSMark Brown #define NT_ARM_SSVE 0x40b
31fa23100bSMark Brown #endif
32fa23100bSMark Brown 
33*89ff30b9SMark Brown /*
34*89ff30b9SMark Brown  * The architecture defines the maximum VQ as 16 but for extensibility
35*89ff30b9SMark Brown  * the kernel specifies the SVE_VQ_MAX as 512 resulting in us running
36*89ff30b9SMark Brown  * a *lot* more tests than are useful if we use it.  Until the
37*89ff30b9SMark Brown  * architecture is extended let's limit our coverage to what is
38*89ff30b9SMark Brown  * currently allowed, plus one extra to ensure we cover constraining
39*89ff30b9SMark Brown  * the VL as expected.
40*89ff30b9SMark Brown  */
41*89ff30b9SMark Brown #define TEST_VQ_MAX 17
42*89ff30b9SMark Brown 
4318edbb6bSMark Brown struct vec_type {
4418edbb6bSMark Brown 	const char *name;
4518edbb6bSMark Brown 	unsigned long hwcap_type;
4618edbb6bSMark Brown 	unsigned long hwcap;
4718edbb6bSMark Brown 	int regset;
4818edbb6bSMark Brown 	int prctl_set;
4918edbb6bSMark Brown };
5018edbb6bSMark Brown 
5118edbb6bSMark Brown static const struct vec_type vec_types[] = {
5218edbb6bSMark Brown 	{
5318edbb6bSMark Brown 		.name = "SVE",
5418edbb6bSMark Brown 		.hwcap_type = AT_HWCAP,
5518edbb6bSMark Brown 		.hwcap = HWCAP_SVE,
5618edbb6bSMark Brown 		.regset = NT_ARM_SVE,
5718edbb6bSMark Brown 		.prctl_set = PR_SVE_SET_VL,
5818edbb6bSMark Brown 	},
59fa23100bSMark Brown 	{
60fa23100bSMark Brown 		.name = "Streaming SVE",
61fa23100bSMark Brown 		.hwcap_type = AT_HWCAP2,
62fa23100bSMark Brown 		.hwcap = HWCAP2_SME,
63fa23100bSMark Brown 		.regset = NT_ARM_SSVE,
64fa23100bSMark Brown 		.prctl_set = PR_SME_SET_VL,
65fa23100bSMark Brown 	},
6618edbb6bSMark Brown };
6718edbb6bSMark Brown 
68*89ff30b9SMark Brown #define VL_TESTS (((TEST_VQ_MAX - SVE_VQ_MIN) + 1) * 4)
6918edbb6bSMark Brown #define FLAG_TESTS 2
701fb1e285SMark Brown #define FPSIMD_TESTS 2
7118edbb6bSMark Brown 
7218edbb6bSMark Brown #define EXPECTED_TESTS ((VL_TESTS + FLAG_TESTS + FPSIMD_TESTS) * ARRAY_SIZE(vec_types))
7318edbb6bSMark Brown 
fill_buf(char * buf,size_t size)74a1d71112SMark Brown static void fill_buf(char *buf, size_t size)
750dca276aSMark Brown {
76a1d71112SMark Brown 	int i;
770dca276aSMark Brown 
78a1d71112SMark Brown 	for (i = 0; i < size; i++)
79a1d71112SMark Brown 		buf[i] = random();
800dca276aSMark Brown }
810dca276aSMark Brown 
do_child(void)820dca276aSMark Brown static int do_child(void)
830dca276aSMark Brown {
840dca276aSMark Brown 	if (ptrace(PTRACE_TRACEME, -1, NULL, NULL))
850dca276aSMark Brown 		ksft_exit_fail_msg("PTRACE_TRACEME", strerror(errno));
860dca276aSMark Brown 
870dca276aSMark Brown 	if (raise(SIGSTOP))
880dca276aSMark Brown 		ksft_exit_fail_msg("raise(SIGSTOP)", strerror(errno));
890dca276aSMark Brown 
900dca276aSMark Brown 	return EXIT_SUCCESS;
910dca276aSMark Brown }
920dca276aSMark Brown 
get_fpsimd(pid_t pid,struct user_fpsimd_state * fpsimd)939f7d03a2SMark Brown static int get_fpsimd(pid_t pid, struct user_fpsimd_state *fpsimd)
949f7d03a2SMark Brown {
959f7d03a2SMark Brown 	struct iovec iov;
969f7d03a2SMark Brown 
979f7d03a2SMark Brown 	iov.iov_base = fpsimd;
989f7d03a2SMark Brown 	iov.iov_len = sizeof(*fpsimd);
999f7d03a2SMark Brown 	return ptrace(PTRACE_GETREGSET, pid, NT_PRFPREG, &iov);
1009f7d03a2SMark Brown }
1019f7d03a2SMark Brown 
set_fpsimd(pid_t pid,struct user_fpsimd_state * fpsimd)10282f97bcdSMark Brown static int set_fpsimd(pid_t pid, struct user_fpsimd_state *fpsimd)
10382f97bcdSMark Brown {
10482f97bcdSMark Brown 	struct iovec iov;
10582f97bcdSMark Brown 
10682f97bcdSMark Brown 	iov.iov_base = fpsimd;
10782f97bcdSMark Brown 	iov.iov_len = sizeof(*fpsimd);
10882f97bcdSMark Brown 	return ptrace(PTRACE_SETREGSET, pid, NT_PRFPREG, &iov);
10982f97bcdSMark Brown }
11082f97bcdSMark Brown 
get_sve(pid_t pid,const struct vec_type * type,void ** buf,size_t * size)11118edbb6bSMark Brown static struct user_sve_header *get_sve(pid_t pid, const struct vec_type *type,
11218edbb6bSMark Brown 				       void **buf, size_t *size)
1130dca276aSMark Brown {
1140dca276aSMark Brown 	struct user_sve_header *sve;
1150dca276aSMark Brown 	void *p;
1160dca276aSMark Brown 	size_t sz = sizeof *sve;
1170dca276aSMark Brown 	struct iovec iov;
1180dca276aSMark Brown 
1190dca276aSMark Brown 	while (1) {
1200dca276aSMark Brown 		if (*size < sz) {
1210dca276aSMark Brown 			p = realloc(*buf, sz);
1220dca276aSMark Brown 			if (!p) {
1230dca276aSMark Brown 				errno = ENOMEM;
1240dca276aSMark Brown 				goto error;
1250dca276aSMark Brown 			}
1260dca276aSMark Brown 
1270dca276aSMark Brown 			*buf = p;
1280dca276aSMark Brown 			*size = sz;
1290dca276aSMark Brown 		}
1300dca276aSMark Brown 
1310dca276aSMark Brown 		iov.iov_base = *buf;
1320dca276aSMark Brown 		iov.iov_len = sz;
13318edbb6bSMark Brown 		if (ptrace(PTRACE_GETREGSET, pid, type->regset, &iov))
1340dca276aSMark Brown 			goto error;
1350dca276aSMark Brown 
1360dca276aSMark Brown 		sve = *buf;
1370dca276aSMark Brown 		if (sve->size <= sz)
1380dca276aSMark Brown 			break;
1390dca276aSMark Brown 
1400dca276aSMark Brown 		sz = sve->size;
1410dca276aSMark Brown 	}
1420dca276aSMark Brown 
1430dca276aSMark Brown 	return sve;
1440dca276aSMark Brown 
1450dca276aSMark Brown error:
1460dca276aSMark Brown 	return NULL;
1470dca276aSMark Brown }
1480dca276aSMark Brown 
set_sve(pid_t pid,const struct vec_type * type,const struct user_sve_header * sve)14918edbb6bSMark Brown static int set_sve(pid_t pid, const struct vec_type *type,
15018edbb6bSMark Brown 		   const struct user_sve_header *sve)
1510dca276aSMark Brown {
1520dca276aSMark Brown 	struct iovec iov;
1530dca276aSMark Brown 
1540dca276aSMark Brown 	iov.iov_base = (void *)sve;
1550dca276aSMark Brown 	iov.iov_len = sve->size;
15618edbb6bSMark Brown 	return ptrace(PTRACE_SETREGSET, pid, type->regset, &iov);
1570dca276aSMark Brown }
1580dca276aSMark Brown 
1590ba1ce1eSMark Brown /* Validate setting and getting the inherit flag */
ptrace_set_get_inherit(pid_t child,const struct vec_type * type)16018edbb6bSMark Brown static void ptrace_set_get_inherit(pid_t child, const struct vec_type *type)
1610ba1ce1eSMark Brown {
1620ba1ce1eSMark Brown 	struct user_sve_header sve;
1630ba1ce1eSMark Brown 	struct user_sve_header *new_sve = NULL;
1640ba1ce1eSMark Brown 	size_t new_sve_size = 0;
1650ba1ce1eSMark Brown 	int ret;
1660ba1ce1eSMark Brown 
1670ba1ce1eSMark Brown 	/* First set the flag */
1680ba1ce1eSMark Brown 	memset(&sve, 0, sizeof(sve));
1690ba1ce1eSMark Brown 	sve.size = sizeof(sve);
1700ba1ce1eSMark Brown 	sve.vl = sve_vl_from_vq(SVE_VQ_MIN);
1710ba1ce1eSMark Brown 	sve.flags = SVE_PT_VL_INHERIT;
17218edbb6bSMark Brown 	ret = set_sve(child, type, &sve);
1730ba1ce1eSMark Brown 	if (ret != 0) {
17418edbb6bSMark Brown 		ksft_test_result_fail("Failed to set %s SVE_PT_VL_INHERIT\n",
17518edbb6bSMark Brown 				      type->name);
1760ba1ce1eSMark Brown 		return;
1770ba1ce1eSMark Brown 	}
1780ba1ce1eSMark Brown 
1790ba1ce1eSMark Brown 	/*
1800ba1ce1eSMark Brown 	 * Read back the new register state and verify that we have
1810ba1ce1eSMark Brown 	 * set the flags we expected.
1820ba1ce1eSMark Brown 	 */
18318edbb6bSMark Brown 	if (!get_sve(child, type, (void **)&new_sve, &new_sve_size)) {
18418edbb6bSMark Brown 		ksft_test_result_fail("Failed to read %s SVE flags\n",
18518edbb6bSMark Brown 				      type->name);
1860ba1ce1eSMark Brown 		return;
1870ba1ce1eSMark Brown 	}
1880ba1ce1eSMark Brown 
1890ba1ce1eSMark Brown 	ksft_test_result(new_sve->flags & SVE_PT_VL_INHERIT,
19018edbb6bSMark Brown 			 "%s SVE_PT_VL_INHERIT set\n", type->name);
1910ba1ce1eSMark Brown 
1920ba1ce1eSMark Brown 	/* Now clear */
1930ba1ce1eSMark Brown 	sve.flags &= ~SVE_PT_VL_INHERIT;
19418edbb6bSMark Brown 	ret = set_sve(child, type, &sve);
1950ba1ce1eSMark Brown 	if (ret != 0) {
19618edbb6bSMark Brown 		ksft_test_result_fail("Failed to clear %s SVE_PT_VL_INHERIT\n",
19718edbb6bSMark Brown 				      type->name);
1980ba1ce1eSMark Brown 		return;
1990ba1ce1eSMark Brown 	}
2000ba1ce1eSMark Brown 
20118edbb6bSMark Brown 	if (!get_sve(child, type, (void **)&new_sve, &new_sve_size)) {
20218edbb6bSMark Brown 		ksft_test_result_fail("Failed to read %s SVE flags\n",
20318edbb6bSMark Brown 				      type->name);
2040ba1ce1eSMark Brown 		return;
2050ba1ce1eSMark Brown 	}
2060ba1ce1eSMark Brown 
2070ba1ce1eSMark Brown 	ksft_test_result(!(new_sve->flags & SVE_PT_VL_INHERIT),
20818edbb6bSMark Brown 			 "%s SVE_PT_VL_INHERIT cleared\n", type->name);
2090ba1ce1eSMark Brown 
2100ba1ce1eSMark Brown 	free(new_sve);
2110ba1ce1eSMark Brown }
2120ba1ce1eSMark Brown 
213a1d71112SMark Brown /* Validate attempting to set the specfied VL via ptrace */
ptrace_set_get_vl(pid_t child,const struct vec_type * type,unsigned int vl,bool * supported)21418edbb6bSMark Brown static void ptrace_set_get_vl(pid_t child, const struct vec_type *type,
21518edbb6bSMark Brown 			      unsigned int vl, bool *supported)
2160dca276aSMark Brown {
217a1d71112SMark Brown 	struct user_sve_header sve;
218a1d71112SMark Brown 	struct user_sve_header *new_sve = NULL;
219a1d71112SMark Brown 	size_t new_sve_size = 0;
220a1d71112SMark Brown 	int ret, prctl_vl;
2210dca276aSMark Brown 
222a1d71112SMark Brown 	*supported = false;
2230dca276aSMark Brown 
224a1d71112SMark Brown 	/* Check if the VL is supported in this process */
22518edbb6bSMark Brown 	prctl_vl = prctl(type->prctl_set, vl);
226a1d71112SMark Brown 	if (prctl_vl == -1)
22718edbb6bSMark Brown 		ksft_exit_fail_msg("prctl(PR_%s_SET_VL) failed: %s (%d)\n",
22818edbb6bSMark Brown 				   type->name, strerror(errno), errno);
2290dca276aSMark Brown 
230a1d71112SMark Brown 	/* If the VL is not supported then a supported VL will be returned */
231a1d71112SMark Brown 	*supported = (prctl_vl == vl);
232a1d71112SMark Brown 
233a1d71112SMark Brown 	/* Set the VL by doing a set with no register payload */
234a1d71112SMark Brown 	memset(&sve, 0, sizeof(sve));
235a1d71112SMark Brown 	sve.size = sizeof(sve);
236a1d71112SMark Brown 	sve.vl = vl;
23718edbb6bSMark Brown 	ret = set_sve(child, type, &sve);
238a1d71112SMark Brown 	if (ret != 0) {
23918edbb6bSMark Brown 		ksft_test_result_fail("Failed to set %s VL %u\n",
24018edbb6bSMark Brown 				      type->name, vl);
241a1d71112SMark Brown 		return;
2420dca276aSMark Brown 	}
243a1d71112SMark Brown 
244a1d71112SMark Brown 	/*
245a1d71112SMark Brown 	 * Read back the new register state and verify that we have the
246a1d71112SMark Brown 	 * same VL that we got from prctl() on ourselves.
247a1d71112SMark Brown 	 */
24818edbb6bSMark Brown 	if (!get_sve(child, type, (void **)&new_sve, &new_sve_size)) {
24918edbb6bSMark Brown 		ksft_test_result_fail("Failed to read %s VL %u\n",
25018edbb6bSMark Brown 				      type->name, vl);
251a1d71112SMark Brown 		return;
252a1d71112SMark Brown 	}
253a1d71112SMark Brown 
25418edbb6bSMark Brown 	ksft_test_result(new_sve->vl = prctl_vl, "Set %s VL %u\n",
25518edbb6bSMark Brown 			 type->name, vl);
256a1d71112SMark Brown 
257a1d71112SMark Brown 	free(new_sve);
258a1d71112SMark Brown }
259a1d71112SMark Brown 
check_u32(unsigned int vl,const char * reg,uint32_t * in,uint32_t * out,int * errors)260a1d71112SMark Brown static void check_u32(unsigned int vl, const char *reg,
261a1d71112SMark Brown 		      uint32_t *in, uint32_t *out, int *errors)
262a1d71112SMark Brown {
263a1d71112SMark Brown 	if (*in != *out) {
264a1d71112SMark Brown 		printf("# VL %d %s wrote %x read %x\n",
265a1d71112SMark Brown 		       vl, reg, *in, *out);
266a1d71112SMark Brown 		(*errors)++;
267a1d71112SMark Brown 	}
268a1d71112SMark Brown }
269a1d71112SMark Brown 
27034785030SMark Brown /* Access the FPSIMD registers via the SVE regset */
ptrace_sve_fpsimd(pid_t child,const struct vec_type * type)27118edbb6bSMark Brown static void ptrace_sve_fpsimd(pid_t child, const struct vec_type *type)
27234785030SMark Brown {
2731fb1e285SMark Brown 	void *svebuf;
27434785030SMark Brown 	struct user_sve_header *sve;
27534785030SMark Brown 	struct user_fpsimd_state *fpsimd, new_fpsimd;
27634785030SMark Brown 	unsigned int i, j;
27734785030SMark Brown 	unsigned char *p;
2781fb1e285SMark Brown 	int ret;
27934785030SMark Brown 
2801fb1e285SMark Brown 	svebuf = malloc(SVE_PT_SIZE(0, SVE_PT_REGS_FPSIMD));
2811fb1e285SMark Brown 	if (!svebuf) {
2821fb1e285SMark Brown 		ksft_test_result_fail("Failed to allocate FPSIMD buffer\n");
28334785030SMark Brown 		return;
28434785030SMark Brown 	}
28534785030SMark Brown 
2861fb1e285SMark Brown 	memset(svebuf, 0, SVE_PT_SIZE(0, SVE_PT_REGS_FPSIMD));
2871fb1e285SMark Brown 	sve = svebuf;
2881fb1e285SMark Brown 	sve->flags = SVE_PT_REGS_FPSIMD;
2891fb1e285SMark Brown 	sve->size = SVE_PT_SIZE(0, SVE_PT_REGS_FPSIMD);
2901fb1e285SMark Brown 	sve->vl = 16;  /* We don't care what the VL is */
29134785030SMark Brown 
29234785030SMark Brown 	/* Try to set a known FPSIMD state via PT_REGS_SVE */
29334785030SMark Brown 	fpsimd = (struct user_fpsimd_state *)((char *)sve +
29434785030SMark Brown 					      SVE_PT_FPSIMD_OFFSET);
29534785030SMark Brown 	for (i = 0; i < 32; ++i) {
29634785030SMark Brown 		p = (unsigned char *)&fpsimd->vregs[i];
29734785030SMark Brown 
29834785030SMark Brown 		for (j = 0; j < sizeof(fpsimd->vregs[i]); ++j)
29934785030SMark Brown 			p[j] = j;
30034785030SMark Brown 	}
30134785030SMark Brown 
3021fb1e285SMark Brown 	ret = set_sve(child, type, sve);
3031fb1e285SMark Brown 	ksft_test_result(ret == 0, "%s FPSIMD set via SVE: %d\n",
3041fb1e285SMark Brown 			 type->name, ret);
3051fb1e285SMark Brown 	if (ret)
30634785030SMark Brown 		goto out;
30734785030SMark Brown 
30834785030SMark Brown 	/* Verify via the FPSIMD regset */
30934785030SMark Brown 	if (get_fpsimd(child, &new_fpsimd)) {
31034785030SMark Brown 		ksft_test_result_fail("get_fpsimd(): %s\n",
31134785030SMark Brown 				      strerror(errno));
31234785030SMark Brown 		goto out;
31334785030SMark Brown 	}
31434785030SMark Brown 	if (memcmp(fpsimd, &new_fpsimd, sizeof(*fpsimd)) == 0)
31518edbb6bSMark Brown 		ksft_test_result_pass("%s get_fpsimd() gave same state\n",
31618edbb6bSMark Brown 				      type->name);
31734785030SMark Brown 	else
31818edbb6bSMark Brown 		ksft_test_result_fail("%s get_fpsimd() gave different state\n",
31918edbb6bSMark Brown 				      type->name);
32034785030SMark Brown 
32134785030SMark Brown out:
32234785030SMark Brown 	free(svebuf);
32334785030SMark Brown }
32434785030SMark Brown 
325a1d71112SMark Brown /* Validate attempting to set SVE data and read SVE data */
ptrace_set_sve_get_sve_data(pid_t child,const struct vec_type * type,unsigned int vl)32618edbb6bSMark Brown static void ptrace_set_sve_get_sve_data(pid_t child,
32718edbb6bSMark Brown 					const struct vec_type *type,
32818edbb6bSMark Brown 					unsigned int vl)
329a1d71112SMark Brown {
330a1d71112SMark Brown 	void *write_buf;
331a1d71112SMark Brown 	void *read_buf = NULL;
332a1d71112SMark Brown 	struct user_sve_header *write_sve;
333a1d71112SMark Brown 	struct user_sve_header *read_sve;
334a1d71112SMark Brown 	size_t read_sve_size = 0;
335a1d71112SMark Brown 	unsigned int vq = sve_vq_from_vl(vl);
336a1d71112SMark Brown 	int ret, i;
337a1d71112SMark Brown 	size_t data_size;
338a1d71112SMark Brown 	int errors = 0;
339a1d71112SMark Brown 
340a1d71112SMark Brown 	data_size = SVE_PT_SVE_OFFSET + SVE_PT_SVE_SIZE(vq, SVE_PT_REGS_SVE);
341a1d71112SMark Brown 	write_buf = malloc(data_size);
342a1d71112SMark Brown 	if (!write_buf) {
34318edbb6bSMark Brown 		ksft_test_result_fail("Error allocating %d byte buffer for %s VL %u\n",
34418edbb6bSMark Brown 				      data_size, type->name, vl);
345a1d71112SMark Brown 		return;
346a1d71112SMark Brown 	}
347a1d71112SMark Brown 	write_sve = write_buf;
348a1d71112SMark Brown 
349a1d71112SMark Brown 	/* Set up some data and write it out */
350a1d71112SMark Brown 	memset(write_sve, 0, data_size);
351a1d71112SMark Brown 	write_sve->size = data_size;
352a1d71112SMark Brown 	write_sve->vl = vl;
353a1d71112SMark Brown 	write_sve->flags = SVE_PT_REGS_SVE;
354a1d71112SMark Brown 
355a1d71112SMark Brown 	for (i = 0; i < __SVE_NUM_ZREGS; i++)
356a1d71112SMark Brown 		fill_buf(write_buf + SVE_PT_SVE_ZREG_OFFSET(vq, i),
357a1d71112SMark Brown 			 SVE_PT_SVE_ZREG_SIZE(vq));
358a1d71112SMark Brown 
359a1d71112SMark Brown 	for (i = 0; i < __SVE_NUM_PREGS; i++)
360a1d71112SMark Brown 		fill_buf(write_buf + SVE_PT_SVE_PREG_OFFSET(vq, i),
361a1d71112SMark Brown 			 SVE_PT_SVE_PREG_SIZE(vq));
362a1d71112SMark Brown 
363a1d71112SMark Brown 	fill_buf(write_buf + SVE_PT_SVE_FPSR_OFFSET(vq), SVE_PT_SVE_FPSR_SIZE);
364a1d71112SMark Brown 	fill_buf(write_buf + SVE_PT_SVE_FPCR_OFFSET(vq), SVE_PT_SVE_FPCR_SIZE);
365a1d71112SMark Brown 
366a1d71112SMark Brown 	/* TODO: Generate a valid FFR pattern */
367a1d71112SMark Brown 
36818edbb6bSMark Brown 	ret = set_sve(child, type, write_sve);
369a1d71112SMark Brown 	if (ret != 0) {
37018edbb6bSMark Brown 		ksft_test_result_fail("Failed to set %s VL %u data\n",
37118edbb6bSMark Brown 				      type->name, vl);
372a1d71112SMark Brown 		goto out;
373a1d71112SMark Brown 	}
374a1d71112SMark Brown 
375a1d71112SMark Brown 	/* Read the data back */
37618edbb6bSMark Brown 	if (!get_sve(child, type, (void **)&read_buf, &read_sve_size)) {
37718edbb6bSMark Brown 		ksft_test_result_fail("Failed to read %s VL %u data\n",
37818edbb6bSMark Brown 				      type->name, vl);
379a1d71112SMark Brown 		goto out;
380a1d71112SMark Brown 	}
381a1d71112SMark Brown 	read_sve = read_buf;
382a1d71112SMark Brown 
383a1d71112SMark Brown 	/* We might read more data if there's extensions we don't know */
384a1d71112SMark Brown 	if (read_sve->size < write_sve->size) {
38518edbb6bSMark Brown 		ksft_test_result_fail("%s wrote %d bytes, only read %d\n",
38618edbb6bSMark Brown 				      type->name, write_sve->size,
38718edbb6bSMark Brown 				      read_sve->size);
388a1d71112SMark Brown 		goto out_read;
389a1d71112SMark Brown 	}
390a1d71112SMark Brown 
391a1d71112SMark Brown 	for (i = 0; i < __SVE_NUM_ZREGS; i++) {
392a1d71112SMark Brown 		if (memcmp(write_buf + SVE_PT_SVE_ZREG_OFFSET(vq, i),
393a1d71112SMark Brown 			   read_buf + SVE_PT_SVE_ZREG_OFFSET(vq, i),
394a1d71112SMark Brown 			   SVE_PT_SVE_ZREG_SIZE(vq)) != 0) {
395a1d71112SMark Brown 			printf("# Mismatch in %u Z%d\n", vl, i);
396a1d71112SMark Brown 			errors++;
397a1d71112SMark Brown 		}
398a1d71112SMark Brown 	}
399a1d71112SMark Brown 
400a1d71112SMark Brown 	for (i = 0; i < __SVE_NUM_PREGS; i++) {
401a1d71112SMark Brown 		if (memcmp(write_buf + SVE_PT_SVE_PREG_OFFSET(vq, i),
402a1d71112SMark Brown 			   read_buf + SVE_PT_SVE_PREG_OFFSET(vq, i),
403a1d71112SMark Brown 			   SVE_PT_SVE_PREG_SIZE(vq)) != 0) {
404a1d71112SMark Brown 			printf("# Mismatch in %u P%d\n", vl, i);
405a1d71112SMark Brown 			errors++;
406a1d71112SMark Brown 		}
407a1d71112SMark Brown 	}
408a1d71112SMark Brown 
409a1d71112SMark Brown 	check_u32(vl, "FPSR", write_buf + SVE_PT_SVE_FPSR_OFFSET(vq),
410a1d71112SMark Brown 		  read_buf + SVE_PT_SVE_FPSR_OFFSET(vq), &errors);
411a1d71112SMark Brown 	check_u32(vl, "FPCR", write_buf + SVE_PT_SVE_FPCR_OFFSET(vq),
412a1d71112SMark Brown 		  read_buf + SVE_PT_SVE_FPCR_OFFSET(vq), &errors);
413a1d71112SMark Brown 
41418edbb6bSMark Brown 	ksft_test_result(errors == 0, "Set and get %s data for VL %u\n",
41518edbb6bSMark Brown 			 type->name, vl);
416a1d71112SMark Brown 
417a1d71112SMark Brown out_read:
418a1d71112SMark Brown 	free(read_buf);
419a1d71112SMark Brown out:
420a1d71112SMark Brown 	free(write_buf);
421a1d71112SMark Brown }
422a1d71112SMark Brown 
423854f856fSMark Brown /* Validate attempting to set SVE data and read it via the FPSIMD regset */
ptrace_set_sve_get_fpsimd_data(pid_t child,const struct vec_type * type,unsigned int vl)42418edbb6bSMark Brown static void ptrace_set_sve_get_fpsimd_data(pid_t child,
42518edbb6bSMark Brown 					   const struct vec_type *type,
42618edbb6bSMark Brown 					   unsigned int vl)
427a1d71112SMark Brown {
428a1d71112SMark Brown 	void *write_buf;
429a1d71112SMark Brown 	struct user_sve_header *write_sve;
430a1d71112SMark Brown 	unsigned int vq = sve_vq_from_vl(vl);
431a1d71112SMark Brown 	struct user_fpsimd_state fpsimd_state;
432a1d71112SMark Brown 	int ret, i;
433a1d71112SMark Brown 	size_t data_size;
434a1d71112SMark Brown 	int errors = 0;
435a1d71112SMark Brown 
436a1d71112SMark Brown 	if (__BYTE_ORDER == __BIG_ENDIAN) {
437a1d71112SMark Brown 		ksft_test_result_skip("Big endian not supported\n");
438a1d71112SMark Brown 		return;
439a1d71112SMark Brown 	}
440a1d71112SMark Brown 
441a1d71112SMark Brown 	data_size = SVE_PT_SVE_OFFSET + SVE_PT_SVE_SIZE(vq, SVE_PT_REGS_SVE);
442a1d71112SMark Brown 	write_buf = malloc(data_size);
443a1d71112SMark Brown 	if (!write_buf) {
44418edbb6bSMark Brown 		ksft_test_result_fail("Error allocating %d byte buffer for %s VL %u\n",
44518edbb6bSMark Brown 				      data_size, type->name, vl);
446a1d71112SMark Brown 		return;
447a1d71112SMark Brown 	}
448a1d71112SMark Brown 	write_sve = write_buf;
449a1d71112SMark Brown 
450a1d71112SMark Brown 	/* Set up some data and write it out */
451a1d71112SMark Brown 	memset(write_sve, 0, data_size);
452a1d71112SMark Brown 	write_sve->size = data_size;
453a1d71112SMark Brown 	write_sve->vl = vl;
454a1d71112SMark Brown 	write_sve->flags = SVE_PT_REGS_SVE;
455a1d71112SMark Brown 
456a1d71112SMark Brown 	for (i = 0; i < __SVE_NUM_ZREGS; i++)
457a1d71112SMark Brown 		fill_buf(write_buf + SVE_PT_SVE_ZREG_OFFSET(vq, i),
458a1d71112SMark Brown 			 SVE_PT_SVE_ZREG_SIZE(vq));
459a1d71112SMark Brown 
460a1d71112SMark Brown 	fill_buf(write_buf + SVE_PT_SVE_FPSR_OFFSET(vq), SVE_PT_SVE_FPSR_SIZE);
461a1d71112SMark Brown 	fill_buf(write_buf + SVE_PT_SVE_FPCR_OFFSET(vq), SVE_PT_SVE_FPCR_SIZE);
462a1d71112SMark Brown 
46318edbb6bSMark Brown 	ret = set_sve(child, type, write_sve);
464a1d71112SMark Brown 	if (ret != 0) {
46518edbb6bSMark Brown 		ksft_test_result_fail("Failed to set %s VL %u data\n",
46618edbb6bSMark Brown 				      type->name, vl);
467a1d71112SMark Brown 		goto out;
468a1d71112SMark Brown 	}
469a1d71112SMark Brown 
470a1d71112SMark Brown 	/* Read the data back */
471a1d71112SMark Brown 	if (get_fpsimd(child, &fpsimd_state)) {
47218edbb6bSMark Brown 		ksft_test_result_fail("Failed to read %s VL %u FPSIMD data\n",
47318edbb6bSMark Brown 				      type->name, vl);
474a1d71112SMark Brown 		goto out;
475a1d71112SMark Brown 	}
476a1d71112SMark Brown 
477a1d71112SMark Brown 	for (i = 0; i < __SVE_NUM_ZREGS; i++) {
478a1d71112SMark Brown 		__uint128_t tmp = 0;
479a1d71112SMark Brown 
480a1d71112SMark Brown 		/*
481a1d71112SMark Brown 		 * Z regs are stored endianness invariant, this won't
482a1d71112SMark Brown 		 * work for big endian
483a1d71112SMark Brown 		 */
484a1d71112SMark Brown 		memcpy(&tmp, write_buf + SVE_PT_SVE_ZREG_OFFSET(vq, i),
485a1d71112SMark Brown 		       sizeof(tmp));
486a1d71112SMark Brown 
487a1d71112SMark Brown 		if (tmp != fpsimd_state.vregs[i]) {
48818edbb6bSMark Brown 			printf("# Mismatch in FPSIMD for %s VL %u Z%d\n",
48918edbb6bSMark Brown 			       type->name, vl, i);
490a1d71112SMark Brown 			errors++;
491a1d71112SMark Brown 		}
492a1d71112SMark Brown 	}
493a1d71112SMark Brown 
494a1d71112SMark Brown 	check_u32(vl, "FPSR", write_buf + SVE_PT_SVE_FPSR_OFFSET(vq),
495a1d71112SMark Brown 		  &fpsimd_state.fpsr, &errors);
496a1d71112SMark Brown 	check_u32(vl, "FPCR", write_buf + SVE_PT_SVE_FPCR_OFFSET(vq),
497a1d71112SMark Brown 		  &fpsimd_state.fpcr, &errors);
498a1d71112SMark Brown 
49918edbb6bSMark Brown 	ksft_test_result(errors == 0, "Set and get FPSIMD data for %s VL %u\n",
50018edbb6bSMark Brown 			 type->name, vl);
501a1d71112SMark Brown 
502a1d71112SMark Brown out:
503a1d71112SMark Brown 	free(write_buf);
5040dca276aSMark Brown }
5050dca276aSMark Brown 
50682f97bcdSMark Brown /* Validate attempting to set FPSIMD data and read it via the SVE regset */
ptrace_set_fpsimd_get_sve_data(pid_t child,const struct vec_type * type,unsigned int vl)50782f97bcdSMark Brown static void ptrace_set_fpsimd_get_sve_data(pid_t child,
50882f97bcdSMark Brown 					   const struct vec_type *type,
50982f97bcdSMark Brown 					   unsigned int vl)
51082f97bcdSMark Brown {
51182f97bcdSMark Brown 	void *read_buf = NULL;
51282f97bcdSMark Brown 	unsigned char *p;
51382f97bcdSMark Brown 	struct user_sve_header *read_sve;
51482f97bcdSMark Brown 	unsigned int vq = sve_vq_from_vl(vl);
51582f97bcdSMark Brown 	struct user_fpsimd_state write_fpsimd;
51682f97bcdSMark Brown 	int ret, i, j;
51782f97bcdSMark Brown 	size_t read_sve_size = 0;
51882f97bcdSMark Brown 	size_t expected_size;
51982f97bcdSMark Brown 	int errors = 0;
52082f97bcdSMark Brown 
52182f97bcdSMark Brown 	if (__BYTE_ORDER == __BIG_ENDIAN) {
52282f97bcdSMark Brown 		ksft_test_result_skip("Big endian not supported\n");
52382f97bcdSMark Brown 		return;
52482f97bcdSMark Brown 	}
52582f97bcdSMark Brown 
52682f97bcdSMark Brown 	for (i = 0; i < 32; ++i) {
52782f97bcdSMark Brown 		p = (unsigned char *)&write_fpsimd.vregs[i];
52882f97bcdSMark Brown 
52982f97bcdSMark Brown 		for (j = 0; j < sizeof(write_fpsimd.vregs[i]); ++j)
53082f97bcdSMark Brown 			p[j] = j;
53182f97bcdSMark Brown 	}
53282f97bcdSMark Brown 
53382f97bcdSMark Brown 	ret = set_fpsimd(child, &write_fpsimd);
53482f97bcdSMark Brown 	if (ret != 0) {
53582f97bcdSMark Brown 		ksft_test_result_fail("Failed to set FPSIMD state: %d\n)",
53682f97bcdSMark Brown 				      ret);
53782f97bcdSMark Brown 		return;
53882f97bcdSMark Brown 	}
53982f97bcdSMark Brown 
54082f97bcdSMark Brown 	if (!get_sve(child, type, (void **)&read_buf, &read_sve_size)) {
54182f97bcdSMark Brown 		ksft_test_result_fail("Failed to read %s VL %u data\n",
54282f97bcdSMark Brown 				      type->name, vl);
54382f97bcdSMark Brown 		return;
54482f97bcdSMark Brown 	}
54582f97bcdSMark Brown 	read_sve = read_buf;
54682f97bcdSMark Brown 
54782f97bcdSMark Brown 	if (read_sve->vl != vl) {
54882f97bcdSMark Brown 		ksft_test_result_fail("Child VL != expected VL %d\n",
54982f97bcdSMark Brown 				      read_sve->vl, vl);
55082f97bcdSMark Brown 		goto out;
55182f97bcdSMark Brown 	}
55282f97bcdSMark Brown 
55382f97bcdSMark Brown 	/* The kernel may return either SVE or FPSIMD format */
55482f97bcdSMark Brown 	switch (read_sve->flags & SVE_PT_REGS_MASK) {
55582f97bcdSMark Brown 	case SVE_PT_REGS_FPSIMD:
55682f97bcdSMark Brown 		expected_size = SVE_PT_FPSIMD_SIZE(vq, SVE_PT_REGS_FPSIMD);
55782f97bcdSMark Brown 		if (read_sve_size < expected_size) {
55882f97bcdSMark Brown 			ksft_test_result_fail("Read %d bytes, expected %d\n",
55982f97bcdSMark Brown 					      read_sve_size, expected_size);
56082f97bcdSMark Brown 			goto out;
56182f97bcdSMark Brown 		}
56282f97bcdSMark Brown 
56382f97bcdSMark Brown 		ret = memcmp(&write_fpsimd, read_buf + SVE_PT_FPSIMD_OFFSET,
56482f97bcdSMark Brown 			     sizeof(write_fpsimd));
56582f97bcdSMark Brown 		if (ret != 0) {
56682f97bcdSMark Brown 			ksft_print_msg("Read FPSIMD data mismatch\n");
56782f97bcdSMark Brown 			errors++;
56882f97bcdSMark Brown 		}
56982f97bcdSMark Brown 		break;
57082f97bcdSMark Brown 
57182f97bcdSMark Brown 	case SVE_PT_REGS_SVE:
57282f97bcdSMark Brown 		expected_size = SVE_PT_SVE_SIZE(vq, SVE_PT_REGS_SVE);
57382f97bcdSMark Brown 		if (read_sve_size < expected_size) {
57482f97bcdSMark Brown 			ksft_test_result_fail("Read %d bytes, expected %d\n",
57582f97bcdSMark Brown 					      read_sve_size, expected_size);
57682f97bcdSMark Brown 			goto out;
57782f97bcdSMark Brown 		}
57882f97bcdSMark Brown 
57982f97bcdSMark Brown 		for (i = 0; i < __SVE_NUM_ZREGS; i++) {
58082f97bcdSMark Brown 			__uint128_t tmp = 0;
58182f97bcdSMark Brown 
58282f97bcdSMark Brown 			/*
58382f97bcdSMark Brown 			 * Z regs are stored endianness invariant, this won't
58482f97bcdSMark Brown 			 * work for big endian
58582f97bcdSMark Brown 			 */
58682f97bcdSMark Brown 			memcpy(&tmp, read_buf + SVE_PT_SVE_ZREG_OFFSET(vq, i),
58782f97bcdSMark Brown 			       sizeof(tmp));
58882f97bcdSMark Brown 
58982f97bcdSMark Brown 			if (tmp != write_fpsimd.vregs[i]) {
59082f97bcdSMark Brown 				ksft_print_msg("Mismatch in FPSIMD for %s VL %u Z%d/V%d\n",
59182f97bcdSMark Brown 					       type->name, vl, i, i);
59282f97bcdSMark Brown 				errors++;
59382f97bcdSMark Brown 			}
59482f97bcdSMark Brown 		}
59582f97bcdSMark Brown 
59682f97bcdSMark Brown 		check_u32(vl, "FPSR", &write_fpsimd.fpsr,
59782f97bcdSMark Brown 			  read_buf + SVE_PT_SVE_FPSR_OFFSET(vq), &errors);
59882f97bcdSMark Brown 		check_u32(vl, "FPCR", &write_fpsimd.fpcr,
59982f97bcdSMark Brown 			  read_buf + SVE_PT_SVE_FPCR_OFFSET(vq), &errors);
60082f97bcdSMark Brown 		break;
60182f97bcdSMark Brown 	default:
60282f97bcdSMark Brown 		ksft_print_msg("Unexpected regs type %d\n",
60382f97bcdSMark Brown 			       read_sve->flags & SVE_PT_REGS_MASK);
60482f97bcdSMark Brown 		errors++;
60582f97bcdSMark Brown 		break;
60682f97bcdSMark Brown 	}
60782f97bcdSMark Brown 
60882f97bcdSMark Brown 	ksft_test_result(errors == 0, "Set FPSIMD, read via SVE for %s VL %u\n",
60982f97bcdSMark Brown 			 type->name, vl);
61082f97bcdSMark Brown 
61182f97bcdSMark Brown out:
61282f97bcdSMark Brown 	free(read_buf);
61382f97bcdSMark Brown }
61482f97bcdSMark Brown 
do_parent(pid_t child)6150dca276aSMark Brown static int do_parent(pid_t child)
6160dca276aSMark Brown {
6170dca276aSMark Brown 	int ret = EXIT_FAILURE;
6180dca276aSMark Brown 	pid_t pid;
61918edbb6bSMark Brown 	int status, i;
6200dca276aSMark Brown 	siginfo_t si;
621a1d71112SMark Brown 	unsigned int vq, vl;
622a1d71112SMark Brown 	bool vl_supported;
6230dca276aSMark Brown 
624e2dc49efSMark Brown 	ksft_print_msg("Parent is %d, child is %d\n", getpid(), child);
625e2dc49efSMark Brown 
6260dca276aSMark Brown 	/* Attach to the child */
6270dca276aSMark Brown 	while (1) {
6280dca276aSMark Brown 		int sig;
6290dca276aSMark Brown 
6300dca276aSMark Brown 		pid = wait(&status);
6310dca276aSMark Brown 		if (pid == -1) {
6320dca276aSMark Brown 			perror("wait");
6330dca276aSMark Brown 			goto error;
6340dca276aSMark Brown 		}
6350dca276aSMark Brown 
6360dca276aSMark Brown 		/*
6370dca276aSMark Brown 		 * This should never happen but it's hard to flag in
6380dca276aSMark Brown 		 * the framework.
6390dca276aSMark Brown 		 */
6400dca276aSMark Brown 		if (pid != child)
6410dca276aSMark Brown 			continue;
6420dca276aSMark Brown 
6430dca276aSMark Brown 		if (WIFEXITED(status) || WIFSIGNALED(status))
6440dca276aSMark Brown 			ksft_exit_fail_msg("Child died unexpectedly\n");
6450dca276aSMark Brown 
6460dca276aSMark Brown 		if (!WIFSTOPPED(status))
6470dca276aSMark Brown 			goto error;
6480dca276aSMark Brown 
6490dca276aSMark Brown 		sig = WSTOPSIG(status);
6500dca276aSMark Brown 
6510dca276aSMark Brown 		if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &si)) {
6520dca276aSMark Brown 			if (errno == ESRCH)
6530dca276aSMark Brown 				goto disappeared;
6540dca276aSMark Brown 
6550dca276aSMark Brown 			if (errno == EINVAL) {
6560dca276aSMark Brown 				sig = 0; /* bust group-stop */
6570dca276aSMark Brown 				goto cont;
6580dca276aSMark Brown 			}
6590dca276aSMark Brown 
6600dca276aSMark Brown 			ksft_test_result_fail("PTRACE_GETSIGINFO: %s\n",
6610dca276aSMark Brown 					      strerror(errno));
6620dca276aSMark Brown 			goto error;
6630dca276aSMark Brown 		}
6640dca276aSMark Brown 
6650dca276aSMark Brown 		if (sig == SIGSTOP && si.si_code == SI_TKILL &&
6660dca276aSMark Brown 		    si.si_pid == pid)
6670dca276aSMark Brown 			break;
6680dca276aSMark Brown 
6690dca276aSMark Brown 	cont:
6700dca276aSMark Brown 		if (ptrace(PTRACE_CONT, pid, NULL, sig)) {
6710dca276aSMark Brown 			if (errno == ESRCH)
6720dca276aSMark Brown 				goto disappeared;
6730dca276aSMark Brown 
6740dca276aSMark Brown 			ksft_test_result_fail("PTRACE_CONT: %s\n",
6750dca276aSMark Brown 					      strerror(errno));
6760dca276aSMark Brown 			goto error;
6770dca276aSMark Brown 		}
6780dca276aSMark Brown 	}
6790dca276aSMark Brown 
68018edbb6bSMark Brown 	for (i = 0; i < ARRAY_SIZE(vec_types); i++) {
68134785030SMark Brown 		/* FPSIMD via SVE regset */
68218edbb6bSMark Brown 		if (getauxval(vec_types[i].hwcap_type) & vec_types[i].hwcap) {
68318edbb6bSMark Brown 			ptrace_sve_fpsimd(child, &vec_types[i]);
68418edbb6bSMark Brown 		} else {
68518edbb6bSMark Brown 			ksft_test_result_skip("%s FPSIMD set via SVE\n",
68618edbb6bSMark Brown 					      vec_types[i].name);
6871fb1e285SMark Brown 			ksft_test_result_skip("%s FPSIMD read\n",
68818edbb6bSMark Brown 					      vec_types[i].name);
68918edbb6bSMark Brown 		}
6909f7d03a2SMark Brown 
6910ba1ce1eSMark Brown 		/* prctl() flags */
69250806fd9SMark Brown 		if (getauxval(vec_types[i].hwcap_type) & vec_types[i].hwcap) {
69318edbb6bSMark Brown 			ptrace_set_get_inherit(child, &vec_types[i]);
69450806fd9SMark Brown 		} else {
69550806fd9SMark Brown 			ksft_test_result_skip("%s SVE_PT_VL_INHERIT set\n",
69650806fd9SMark Brown 					      vec_types[i].name);
69750806fd9SMark Brown 			ksft_test_result_skip("%s SVE_PT_VL_INHERIT cleared\n",
69850806fd9SMark Brown 					      vec_types[i].name);
69950806fd9SMark Brown 		}
7000ba1ce1eSMark Brown 
701a1d71112SMark Brown 		/* Step through every possible VQ */
702*89ff30b9SMark Brown 		for (vq = SVE_VQ_MIN; vq <= TEST_VQ_MAX; vq++) {
703a1d71112SMark Brown 			vl = sve_vl_from_vq(vq);
7040dca276aSMark Brown 
705a1d71112SMark Brown 			/* First, try to set this vector length */
70618edbb6bSMark Brown 			if (getauxval(vec_types[i].hwcap_type) &
70718edbb6bSMark Brown 			    vec_types[i].hwcap) {
70818edbb6bSMark Brown 				ptrace_set_get_vl(child, &vec_types[i], vl,
70918edbb6bSMark Brown 						  &vl_supported);
71018edbb6bSMark Brown 			} else {
71118edbb6bSMark Brown 				ksft_test_result_skip("%s get/set VL %d\n",
71218edbb6bSMark Brown 						      vec_types[i].name, vl);
71318edbb6bSMark Brown 				vl_supported = false;
71418edbb6bSMark Brown 			}
715a1d71112SMark Brown 
716a1d71112SMark Brown 			/* If the VL is supported validate data set/get */
717a1d71112SMark Brown 			if (vl_supported) {
71818edbb6bSMark Brown 				ptrace_set_sve_get_sve_data(child, &vec_types[i], vl);
71918edbb6bSMark Brown 				ptrace_set_sve_get_fpsimd_data(child, &vec_types[i], vl);
72082f97bcdSMark Brown 				ptrace_set_fpsimd_get_sve_data(child, &vec_types[i], vl);
721a1d71112SMark Brown 			} else {
72218edbb6bSMark Brown 				ksft_test_result_skip("%s set SVE get SVE for VL %d\n",
72318edbb6bSMark Brown 						      vec_types[i].name, vl);
72418edbb6bSMark Brown 				ksft_test_result_skip("%s set SVE get FPSIMD for VL %d\n",
72518edbb6bSMark Brown 						      vec_types[i].name, vl);
72682f97bcdSMark Brown 				ksft_test_result_skip("%s set FPSIMD get SVE for VL %d\n",
72782f97bcdSMark Brown 						      vec_types[i].name, vl);
72818edbb6bSMark Brown 			}
7290dca276aSMark Brown 		}
7300dca276aSMark Brown 	}
7310dca276aSMark Brown 
7320dca276aSMark Brown 	ret = EXIT_SUCCESS;
7330dca276aSMark Brown 
7340dca276aSMark Brown error:
7350dca276aSMark Brown 	kill(child, SIGKILL);
7360dca276aSMark Brown 
7370dca276aSMark Brown disappeared:
7380dca276aSMark Brown 	return ret;
7390dca276aSMark Brown }
7400dca276aSMark Brown 
main(void)7410dca276aSMark Brown int main(void)
7420dca276aSMark Brown {
7430dca276aSMark Brown 	int ret = EXIT_SUCCESS;
7440dca276aSMark Brown 	pid_t child;
7450dca276aSMark Brown 
746a1d71112SMark Brown 	srandom(getpid());
747a1d71112SMark Brown 
7480dca276aSMark Brown 	ksft_print_header();
74978d2d816SMark Brown 	ksft_set_plan(EXPECTED_TESTS);
7500dca276aSMark Brown 
7510dca276aSMark Brown 	if (!(getauxval(AT_HWCAP) & HWCAP_SVE))
7520dca276aSMark Brown 		ksft_exit_skip("SVE not available\n");
7530dca276aSMark Brown 
7540dca276aSMark Brown 	child = fork();
7550dca276aSMark Brown 	if (!child)
7560dca276aSMark Brown 		return do_child();
7570dca276aSMark Brown 
7580dca276aSMark Brown 	if (do_parent(child))
7590dca276aSMark Brown 		ret = EXIT_FAILURE;
7600dca276aSMark Brown 
7610dca276aSMark Brown 	ksft_print_cnts();
7620dca276aSMark Brown 
76307e64488SMark Brown 	return ret;
7640dca276aSMark Brown }
765