xref: /openbmc/linux/tools/testing/selftests/arm64/fp/za-ptrace.c (revision 9a87ffc99ec8eb8d35eed7c4f816d75f5cc9662e)
186c8888fSMark Brown // SPDX-License-Identifier: GPL-2.0-only
286c8888fSMark Brown /*
386c8888fSMark Brown  * Copyright (C) 2021 ARM Limited.
486c8888fSMark Brown  */
586c8888fSMark Brown #include <errno.h>
686c8888fSMark Brown #include <stdbool.h>
786c8888fSMark Brown #include <stddef.h>
886c8888fSMark Brown #include <stdio.h>
986c8888fSMark Brown #include <stdlib.h>
1086c8888fSMark Brown #include <string.h>
1186c8888fSMark Brown #include <unistd.h>
1286c8888fSMark Brown #include <sys/auxv.h>
1386c8888fSMark Brown #include <sys/prctl.h>
1486c8888fSMark Brown #include <sys/ptrace.h>
1586c8888fSMark Brown #include <sys/types.h>
1686c8888fSMark Brown #include <sys/uio.h>
1786c8888fSMark Brown #include <sys/wait.h>
1886c8888fSMark Brown #include <asm/sigcontext.h>
1986c8888fSMark Brown #include <asm/ptrace.h>
2086c8888fSMark Brown 
2186c8888fSMark Brown #include "../../kselftest.h"
2286c8888fSMark Brown 
2386c8888fSMark Brown /* <linux/elf.h> and <sys/auxv.h> don't like each other, so: */
2486c8888fSMark Brown #ifndef NT_ARM_ZA
2586c8888fSMark Brown #define NT_ARM_ZA 0x40c
2686c8888fSMark Brown #endif
2786c8888fSMark Brown 
28*89ff30b9SMark Brown /*
29*89ff30b9SMark Brown  * The architecture defines the maximum VQ as 16 but for extensibility
30*89ff30b9SMark Brown  * the kernel specifies the SVE_VQ_MAX as 512 resulting in us running
31*89ff30b9SMark Brown  * a *lot* more tests than are useful if we use it.  Until the
32*89ff30b9SMark Brown  * architecture is extended let's limit our coverage to what is
33*89ff30b9SMark Brown  * currently allowed, plus one extra to ensure we cover constraining
34*89ff30b9SMark Brown  * the VL as expected.
35*89ff30b9SMark Brown  */
36*89ff30b9SMark Brown #define TEST_VQ_MAX 17
37*89ff30b9SMark Brown 
38*89ff30b9SMark Brown #define EXPECTED_TESTS (((TEST_VQ_MAX - SVE_VQ_MIN) + 1) * 3)
3986c8888fSMark Brown 
fill_buf(char * buf,size_t size)4086c8888fSMark Brown static void fill_buf(char *buf, size_t size)
4186c8888fSMark Brown {
4286c8888fSMark Brown 	int i;
4386c8888fSMark Brown 
4486c8888fSMark Brown 	for (i = 0; i < size; i++)
4586c8888fSMark Brown 		buf[i] = random();
4686c8888fSMark Brown }
4786c8888fSMark Brown 
do_child(void)4886c8888fSMark Brown static int do_child(void)
4986c8888fSMark Brown {
5086c8888fSMark Brown 	if (ptrace(PTRACE_TRACEME, -1, NULL, NULL))
5186c8888fSMark Brown 		ksft_exit_fail_msg("PTRACE_TRACEME", strerror(errno));
5286c8888fSMark Brown 
5386c8888fSMark Brown 	if (raise(SIGSTOP))
5486c8888fSMark Brown 		ksft_exit_fail_msg("raise(SIGSTOP)", strerror(errno));
5586c8888fSMark Brown 
5686c8888fSMark Brown 	return EXIT_SUCCESS;
5786c8888fSMark Brown }
5886c8888fSMark Brown 
get_za(pid_t pid,void ** buf,size_t * size)5986c8888fSMark Brown static struct user_za_header *get_za(pid_t pid, void **buf, size_t *size)
6086c8888fSMark Brown {
6186c8888fSMark Brown 	struct user_za_header *za;
6286c8888fSMark Brown 	void *p;
6386c8888fSMark Brown 	size_t sz = sizeof(*za);
6486c8888fSMark Brown 	struct iovec iov;
6586c8888fSMark Brown 
6686c8888fSMark Brown 	while (1) {
6786c8888fSMark Brown 		if (*size < sz) {
6886c8888fSMark Brown 			p = realloc(*buf, sz);
6986c8888fSMark Brown 			if (!p) {
7086c8888fSMark Brown 				errno = ENOMEM;
7186c8888fSMark Brown 				goto error;
7286c8888fSMark Brown 			}
7386c8888fSMark Brown 
7486c8888fSMark Brown 			*buf = p;
7586c8888fSMark Brown 			*size = sz;
7686c8888fSMark Brown 		}
7786c8888fSMark Brown 
7886c8888fSMark Brown 		iov.iov_base = *buf;
7986c8888fSMark Brown 		iov.iov_len = sz;
8086c8888fSMark Brown 		if (ptrace(PTRACE_GETREGSET, pid, NT_ARM_ZA, &iov))
8186c8888fSMark Brown 			goto error;
8286c8888fSMark Brown 
8386c8888fSMark Brown 		za = *buf;
8486c8888fSMark Brown 		if (za->size <= sz)
8586c8888fSMark Brown 			break;
8686c8888fSMark Brown 
8786c8888fSMark Brown 		sz = za->size;
8886c8888fSMark Brown 	}
8986c8888fSMark Brown 
9086c8888fSMark Brown 	return za;
9186c8888fSMark Brown 
9286c8888fSMark Brown error:
9386c8888fSMark Brown 	return NULL;
9486c8888fSMark Brown }
9586c8888fSMark Brown 
set_za(pid_t pid,const struct user_za_header * za)9686c8888fSMark Brown static int set_za(pid_t pid, const struct user_za_header *za)
9786c8888fSMark Brown {
9886c8888fSMark Brown 	struct iovec iov;
9986c8888fSMark Brown 
10086c8888fSMark Brown 	iov.iov_base = (void *)za;
10186c8888fSMark Brown 	iov.iov_len = za->size;
10286c8888fSMark Brown 	return ptrace(PTRACE_SETREGSET, pid, NT_ARM_ZA, &iov);
10386c8888fSMark Brown }
10486c8888fSMark Brown 
10586c8888fSMark Brown /* Validate attempting to set the specfied VL via ptrace */
ptrace_set_get_vl(pid_t child,unsigned int vl,bool * supported)10686c8888fSMark Brown static void ptrace_set_get_vl(pid_t child, unsigned int vl, bool *supported)
10786c8888fSMark Brown {
10886c8888fSMark Brown 	struct user_za_header za;
10986c8888fSMark Brown 	struct user_za_header *new_za = NULL;
11086c8888fSMark Brown 	size_t new_za_size = 0;
11186c8888fSMark Brown 	int ret, prctl_vl;
11286c8888fSMark Brown 
11386c8888fSMark Brown 	*supported = false;
11486c8888fSMark Brown 
11586c8888fSMark Brown 	/* Check if the VL is supported in this process */
11686c8888fSMark Brown 	prctl_vl = prctl(PR_SME_SET_VL, vl);
11786c8888fSMark Brown 	if (prctl_vl == -1)
11886c8888fSMark Brown 		ksft_exit_fail_msg("prctl(PR_SME_SET_VL) failed: %s (%d)\n",
11986c8888fSMark Brown 				   strerror(errno), errno);
12086c8888fSMark Brown 
12186c8888fSMark Brown 	/* If the VL is not supported then a supported VL will be returned */
12286c8888fSMark Brown 	*supported = (prctl_vl == vl);
12386c8888fSMark Brown 
12486c8888fSMark Brown 	/* Set the VL by doing a set with no register payload */
12586c8888fSMark Brown 	memset(&za, 0, sizeof(za));
12686c8888fSMark Brown 	za.size = sizeof(za);
12786c8888fSMark Brown 	za.vl = vl;
12886c8888fSMark Brown 	ret = set_za(child, &za);
12986c8888fSMark Brown 	if (ret != 0) {
13086c8888fSMark Brown 		ksft_test_result_fail("Failed to set VL %u\n", vl);
13186c8888fSMark Brown 		return;
13286c8888fSMark Brown 	}
13386c8888fSMark Brown 
13486c8888fSMark Brown 	/*
13586c8888fSMark Brown 	 * Read back the new register state and verify that we have the
13686c8888fSMark Brown 	 * same VL that we got from prctl() on ourselves.
13786c8888fSMark Brown 	 */
13886c8888fSMark Brown 	if (!get_za(child, (void **)&new_za, &new_za_size)) {
13986c8888fSMark Brown 		ksft_test_result_fail("Failed to read VL %u\n", vl);
14086c8888fSMark Brown 		return;
14186c8888fSMark Brown 	}
14286c8888fSMark Brown 
14386c8888fSMark Brown 	ksft_test_result(new_za->vl = prctl_vl, "Set VL %u\n", vl);
14486c8888fSMark Brown 
14586c8888fSMark Brown 	free(new_za);
14686c8888fSMark Brown }
14786c8888fSMark Brown 
14886c8888fSMark Brown /* Validate attempting to set no ZA data and read it back */
ptrace_set_no_data(pid_t child,unsigned int vl)14986c8888fSMark Brown static void ptrace_set_no_data(pid_t child, unsigned int vl)
15086c8888fSMark Brown {
15186c8888fSMark Brown 	void *read_buf = NULL;
15286c8888fSMark Brown 	struct user_za_header write_za;
15386c8888fSMark Brown 	struct user_za_header *read_za;
15486c8888fSMark Brown 	size_t read_za_size = 0;
15586c8888fSMark Brown 	int ret;
15686c8888fSMark Brown 
15786c8888fSMark Brown 	/* Set up some data and write it out */
15886c8888fSMark Brown 	memset(&write_za, 0, sizeof(write_za));
15986c8888fSMark Brown 	write_za.size = ZA_PT_ZA_OFFSET;
16086c8888fSMark Brown 	write_za.vl = vl;
16186c8888fSMark Brown 
16286c8888fSMark Brown 	ret = set_za(child, &write_za);
16386c8888fSMark Brown 	if (ret != 0) {
16486c8888fSMark Brown 		ksft_test_result_fail("Failed to set VL %u no data\n", vl);
16586c8888fSMark Brown 		return;
16686c8888fSMark Brown 	}
16786c8888fSMark Brown 
16886c8888fSMark Brown 	/* Read the data back */
16986c8888fSMark Brown 	if (!get_za(child, (void **)&read_buf, &read_za_size)) {
17086c8888fSMark Brown 		ksft_test_result_fail("Failed to read VL %u no data\n", vl);
17186c8888fSMark Brown 		return;
17286c8888fSMark Brown 	}
17386c8888fSMark Brown 	read_za = read_buf;
17486c8888fSMark Brown 
17586c8888fSMark Brown 	/* We might read more data if there's extensions we don't know */
17686c8888fSMark Brown 	if (read_za->size < write_za.size) {
17786c8888fSMark Brown 		ksft_test_result_fail("VL %u wrote %d bytes, only read %d\n",
17886c8888fSMark Brown 				      vl, write_za.size, read_za->size);
17986c8888fSMark Brown 		goto out_read;
18086c8888fSMark Brown 	}
18186c8888fSMark Brown 
18286c8888fSMark Brown 	ksft_test_result(read_za->size == write_za.size,
18386c8888fSMark Brown 			 "Disabled ZA for VL %u\n", vl);
18486c8888fSMark Brown 
18586c8888fSMark Brown out_read:
18686c8888fSMark Brown 	free(read_buf);
18786c8888fSMark Brown }
18886c8888fSMark Brown 
18986c8888fSMark Brown /* Validate attempting to set data and read it back */
ptrace_set_get_data(pid_t child,unsigned int vl)19086c8888fSMark Brown static void ptrace_set_get_data(pid_t child, unsigned int vl)
19186c8888fSMark Brown {
19286c8888fSMark Brown 	void *write_buf;
19386c8888fSMark Brown 	void *read_buf = NULL;
19486c8888fSMark Brown 	struct user_za_header *write_za;
19586c8888fSMark Brown 	struct user_za_header *read_za;
19686c8888fSMark Brown 	size_t read_za_size = 0;
19786c8888fSMark Brown 	unsigned int vq = sve_vq_from_vl(vl);
19886c8888fSMark Brown 	int ret;
19986c8888fSMark Brown 	size_t data_size;
20086c8888fSMark Brown 
20186c8888fSMark Brown 	data_size = ZA_PT_SIZE(vq);
20286c8888fSMark Brown 	write_buf = malloc(data_size);
20386c8888fSMark Brown 	if (!write_buf) {
20486c8888fSMark Brown 		ksft_test_result_fail("Error allocating %d byte buffer for VL %u\n",
20586c8888fSMark Brown 				      data_size, vl);
20686c8888fSMark Brown 		return;
20786c8888fSMark Brown 	}
20886c8888fSMark Brown 	write_za = write_buf;
20986c8888fSMark Brown 
21086c8888fSMark Brown 	/* Set up some data and write it out */
21186c8888fSMark Brown 	memset(write_za, 0, data_size);
21286c8888fSMark Brown 	write_za->size = data_size;
21386c8888fSMark Brown 	write_za->vl = vl;
21486c8888fSMark Brown 
21586c8888fSMark Brown 	fill_buf(write_buf + ZA_PT_ZA_OFFSET, ZA_PT_ZA_SIZE(vq));
21686c8888fSMark Brown 
21786c8888fSMark Brown 	ret = set_za(child, write_za);
21886c8888fSMark Brown 	if (ret != 0) {
21986c8888fSMark Brown 		ksft_test_result_fail("Failed to set VL %u data\n", vl);
22086c8888fSMark Brown 		goto out;
22186c8888fSMark Brown 	}
22286c8888fSMark Brown 
22386c8888fSMark Brown 	/* Read the data back */
22486c8888fSMark Brown 	if (!get_za(child, (void **)&read_buf, &read_za_size)) {
22586c8888fSMark Brown 		ksft_test_result_fail("Failed to read VL %u data\n", vl);
22686c8888fSMark Brown 		goto out;
22786c8888fSMark Brown 	}
22886c8888fSMark Brown 	read_za = read_buf;
22986c8888fSMark Brown 
23086c8888fSMark Brown 	/* We might read more data if there's extensions we don't know */
23186c8888fSMark Brown 	if (read_za->size < write_za->size) {
23286c8888fSMark Brown 		ksft_test_result_fail("VL %u wrote %d bytes, only read %d\n",
23386c8888fSMark Brown 				      vl, write_za->size, read_za->size);
23486c8888fSMark Brown 		goto out_read;
23586c8888fSMark Brown 	}
23686c8888fSMark Brown 
23786c8888fSMark Brown 	ksft_test_result(memcmp(write_buf + ZA_PT_ZA_OFFSET,
23886c8888fSMark Brown 				read_buf + ZA_PT_ZA_OFFSET,
23986c8888fSMark Brown 				ZA_PT_ZA_SIZE(vq)) == 0,
24086c8888fSMark Brown 			 "Data match for VL %u\n", vl);
24186c8888fSMark Brown 
24286c8888fSMark Brown out_read:
24386c8888fSMark Brown 	free(read_buf);
24486c8888fSMark Brown out:
24586c8888fSMark Brown 	free(write_buf);
24686c8888fSMark Brown }
24786c8888fSMark Brown 
do_parent(pid_t child)24886c8888fSMark Brown static int do_parent(pid_t child)
24986c8888fSMark Brown {
25086c8888fSMark Brown 	int ret = EXIT_FAILURE;
25186c8888fSMark Brown 	pid_t pid;
25286c8888fSMark Brown 	int status;
25386c8888fSMark Brown 	siginfo_t si;
25486c8888fSMark Brown 	unsigned int vq, vl;
25586c8888fSMark Brown 	bool vl_supported;
25686c8888fSMark Brown 
25786c8888fSMark Brown 	/* Attach to the child */
25886c8888fSMark Brown 	while (1) {
25986c8888fSMark Brown 		int sig;
26086c8888fSMark Brown 
26186c8888fSMark Brown 		pid = wait(&status);
26286c8888fSMark Brown 		if (pid == -1) {
26386c8888fSMark Brown 			perror("wait");
26486c8888fSMark Brown 			goto error;
26586c8888fSMark Brown 		}
26686c8888fSMark Brown 
26786c8888fSMark Brown 		/*
26886c8888fSMark Brown 		 * This should never happen but it's hard to flag in
26986c8888fSMark Brown 		 * the framework.
27086c8888fSMark Brown 		 */
27186c8888fSMark Brown 		if (pid != child)
27286c8888fSMark Brown 			continue;
27386c8888fSMark Brown 
27486c8888fSMark Brown 		if (WIFEXITED(status) || WIFSIGNALED(status))
27586c8888fSMark Brown 			ksft_exit_fail_msg("Child died unexpectedly\n");
27686c8888fSMark Brown 
27786c8888fSMark Brown 		if (!WIFSTOPPED(status))
27886c8888fSMark Brown 			goto error;
27986c8888fSMark Brown 
28086c8888fSMark Brown 		sig = WSTOPSIG(status);
28186c8888fSMark Brown 
28286c8888fSMark Brown 		if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &si)) {
28386c8888fSMark Brown 			if (errno == ESRCH)
28486c8888fSMark Brown 				goto disappeared;
28586c8888fSMark Brown 
28686c8888fSMark Brown 			if (errno == EINVAL) {
28786c8888fSMark Brown 				sig = 0; /* bust group-stop */
28886c8888fSMark Brown 				goto cont;
28986c8888fSMark Brown 			}
29086c8888fSMark Brown 
29186c8888fSMark Brown 			ksft_test_result_fail("PTRACE_GETSIGINFO: %s\n",
29286c8888fSMark Brown 					      strerror(errno));
29386c8888fSMark Brown 			goto error;
29486c8888fSMark Brown 		}
29586c8888fSMark Brown 
29686c8888fSMark Brown 		if (sig == SIGSTOP && si.si_code == SI_TKILL &&
29786c8888fSMark Brown 		    si.si_pid == pid)
29886c8888fSMark Brown 			break;
29986c8888fSMark Brown 
30086c8888fSMark Brown 	cont:
30186c8888fSMark Brown 		if (ptrace(PTRACE_CONT, pid, NULL, sig)) {
30286c8888fSMark Brown 			if (errno == ESRCH)
30386c8888fSMark Brown 				goto disappeared;
30486c8888fSMark Brown 
30586c8888fSMark Brown 			ksft_test_result_fail("PTRACE_CONT: %s\n",
30686c8888fSMark Brown 					      strerror(errno));
30786c8888fSMark Brown 			goto error;
30886c8888fSMark Brown 		}
30986c8888fSMark Brown 	}
31086c8888fSMark Brown 
31186c8888fSMark Brown 	ksft_print_msg("Parent is %d, child is %d\n", getpid(), child);
31286c8888fSMark Brown 
31386c8888fSMark Brown 	/* Step through every possible VQ */
314*89ff30b9SMark Brown 	for (vq = SVE_VQ_MIN; vq <= TEST_VQ_MAX; vq++) {
31586c8888fSMark Brown 		vl = sve_vl_from_vq(vq);
31686c8888fSMark Brown 
31786c8888fSMark Brown 		/* First, try to set this vector length */
31886c8888fSMark Brown 		ptrace_set_get_vl(child, vl, &vl_supported);
31986c8888fSMark Brown 
32086c8888fSMark Brown 		/* If the VL is supported validate data set/get */
32186c8888fSMark Brown 		if (vl_supported) {
32286c8888fSMark Brown 			ptrace_set_no_data(child, vl);
32386c8888fSMark Brown 			ptrace_set_get_data(child, vl);
32486c8888fSMark Brown 		} else {
32586c8888fSMark Brown 			ksft_test_result_skip("Disabled ZA for VL %u\n", vl);
32686c8888fSMark Brown 			ksft_test_result_skip("Get and set data for VL %u\n",
32786c8888fSMark Brown 					      vl);
32886c8888fSMark Brown 		}
32986c8888fSMark Brown 	}
33086c8888fSMark Brown 
33186c8888fSMark Brown 	ret = EXIT_SUCCESS;
33286c8888fSMark Brown 
33386c8888fSMark Brown error:
33486c8888fSMark Brown 	kill(child, SIGKILL);
33586c8888fSMark Brown 
33686c8888fSMark Brown disappeared:
33786c8888fSMark Brown 	return ret;
33886c8888fSMark Brown }
33986c8888fSMark Brown 
main(void)34086c8888fSMark Brown int main(void)
34186c8888fSMark Brown {
34286c8888fSMark Brown 	int ret = EXIT_SUCCESS;
34386c8888fSMark Brown 	pid_t child;
34486c8888fSMark Brown 
34586c8888fSMark Brown 	srandom(getpid());
34686c8888fSMark Brown 
34786c8888fSMark Brown 	ksft_print_header();
34886c8888fSMark Brown 
34986c8888fSMark Brown 	if (!(getauxval(AT_HWCAP2) & HWCAP2_SME)) {
35086c8888fSMark Brown 		ksft_set_plan(1);
35186c8888fSMark Brown 		ksft_exit_skip("SME not available\n");
35286c8888fSMark Brown 	}
35386c8888fSMark Brown 
35486c8888fSMark Brown 	ksft_set_plan(EXPECTED_TESTS);
35586c8888fSMark Brown 
35686c8888fSMark Brown 	child = fork();
35786c8888fSMark Brown 	if (!child)
35886c8888fSMark Brown 		return do_child();
35986c8888fSMark Brown 
36086c8888fSMark Brown 	if (do_parent(child))
36186c8888fSMark Brown 		ret = EXIT_FAILURE;
36286c8888fSMark Brown 
36386c8888fSMark Brown 	ksft_print_cnts();
36486c8888fSMark Brown 
36586c8888fSMark Brown 	return ret;
36686c8888fSMark Brown }
367