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