brintos

brintos / linux-shallow public Read only

0
0
Text · 6.9 KiB · 996d961 Raw
366 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2021 ARM Limited.4 */5#include <errno.h>6#include <stdbool.h>7#include <stddef.h>8#include <stdio.h>9#include <stdlib.h>10#include <string.h>11#include <unistd.h>12#include <sys/auxv.h>13#include <sys/prctl.h>14#include <sys/ptrace.h>15#include <sys/types.h>16#include <sys/uio.h>17#include <sys/wait.h>18#include <asm/sigcontext.h>19#include <asm/ptrace.h>20 21#include "../../kselftest.h"22 23/* <linux/elf.h> and <sys/auxv.h> don't like each other, so: */24#ifndef NT_ARM_ZA25#define NT_ARM_ZA 0x40c26#endif27#ifndef NT_ARM_ZT28#define NT_ARM_ZT 0x40d29#endif30 31#define EXPECTED_TESTS 332 33static int sme_vl;34 35static void fill_buf(char *buf, size_t size)36{37	int i;38 39	for (i = 0; i < size; i++)40		buf[i] = random();41}42 43static int do_child(void)44{45	if (ptrace(PTRACE_TRACEME, -1, NULL, NULL))46		ksft_exit_fail_msg("PTRACE_TRACEME", strerror(errno));47 48	if (raise(SIGSTOP))49		ksft_exit_fail_msg("raise(SIGSTOP)", strerror(errno));50 51	return EXIT_SUCCESS;52}53 54static struct user_za_header *get_za(pid_t pid, void **buf, size_t *size)55{56	struct user_za_header *za;57	void *p;58	size_t sz = sizeof(*za);59	struct iovec iov;60 61	while (1) {62		if (*size < sz) {63			p = realloc(*buf, sz);64			if (!p) {65				errno = ENOMEM;66				goto error;67			}68 69			*buf = p;70			*size = sz;71		}72 73		iov.iov_base = *buf;74		iov.iov_len = sz;75		if (ptrace(PTRACE_GETREGSET, pid, NT_ARM_ZA, &iov))76			goto error;77 78		za = *buf;79		if (za->size <= sz)80			break;81 82		sz = za->size;83	}84 85	return za;86 87error:88	return NULL;89}90 91static int set_za(pid_t pid, const struct user_za_header *za)92{93	struct iovec iov;94 95	iov.iov_base = (void *)za;96	iov.iov_len = za->size;97	return ptrace(PTRACE_SETREGSET, pid, NT_ARM_ZA, &iov);98}99 100static int get_zt(pid_t pid, char zt[ZT_SIG_REG_BYTES])101{102	struct iovec iov;103 104	iov.iov_base = zt;105	iov.iov_len = ZT_SIG_REG_BYTES;106	return ptrace(PTRACE_GETREGSET, pid, NT_ARM_ZT, &iov);107}108 109 110static int set_zt(pid_t pid, const char zt[ZT_SIG_REG_BYTES])111{112	struct iovec iov;113 114	iov.iov_base = (void *)zt;115	iov.iov_len = ZT_SIG_REG_BYTES;116	return ptrace(PTRACE_SETREGSET, pid, NT_ARM_ZT, &iov);117}118 119/* Reading with ZA disabled returns all zeros */120static void ptrace_za_disabled_read_zt(pid_t child)121{122	struct user_za_header za;123	char zt[ZT_SIG_REG_BYTES];124	int ret, i;125	bool fail = false;126 127	/* Disable PSTATE.ZA using the ZA interface */128	memset(&za, 0, sizeof(za));129	za.vl = sme_vl;130	za.size = sizeof(za);131 132	ret = set_za(child, &za);133	if (ret != 0) {134		ksft_print_msg("Failed to disable ZA\n");135		fail = true;136	}137 138	/* Read back ZT */139	ret = get_zt(child, zt);140	if (ret != 0) {141		ksft_print_msg("Failed to read ZT\n");142		fail = true;143	}144 145	for (i = 0; i < ARRAY_SIZE(zt); i++) {146		if (zt[i]) {147			ksft_print_msg("zt[%d]: 0x%x != 0\n", i, zt[i]);148			fail = true;149		}150	}151 152	ksft_test_result(!fail, "ptrace_za_disabled_read_zt\n");153}154 155/* Writing then reading ZT should return the data written */156static void ptrace_set_get_zt(pid_t child)157{158	char zt_in[ZT_SIG_REG_BYTES];159	char zt_out[ZT_SIG_REG_BYTES];160	int ret, i;161	bool fail = false;162 163	fill_buf(zt_in, sizeof(zt_in));164 165	ret = set_zt(child, zt_in);166	if (ret != 0) {167		ksft_print_msg("Failed to set ZT\n");168		fail = true;169	}170 171	ret = get_zt(child, zt_out);172	if (ret != 0) {173		ksft_print_msg("Failed to read ZT\n");174		fail = true;175	}176 177	for (i = 0; i < ARRAY_SIZE(zt_in); i++) {178		if (zt_in[i] != zt_out[i]) {179			ksft_print_msg("zt[%d]: 0x%x != 0x%x\n", i, 180				       zt_in[i], zt_out[i]);181			fail = true;182		}183	}184 185	ksft_test_result(!fail, "ptrace_set_get_zt\n");186}187 188/* Writing ZT should set PSTATE.ZA */189static void ptrace_enable_za_via_zt(pid_t child)190{191	struct user_za_header za_in;192	struct user_za_header *za_out;193	char zt[ZT_SIG_REG_BYTES];194	char *za_data;195	size_t za_out_size;196	int ret, i, vq;197	bool fail = false;198 199	/* Disable PSTATE.ZA using the ZA interface */200	memset(&za_in, 0, sizeof(za_in));201	za_in.vl = sme_vl;202	za_in.size = sizeof(za_in);203 204	ret = set_za(child, &za_in);205	if (ret != 0) {206		ksft_print_msg("Failed to disable ZA\n");207		fail = true;208	}209 210	/* Write ZT */211	fill_buf(zt, sizeof(zt));212	ret = set_zt(child, zt);213	if (ret != 0) {214		ksft_print_msg("Failed to set ZT\n");215		fail = true;216	}217 218	/* Read back ZA and check for register data */219	za_out = NULL;220	za_out_size = 0;221	if (get_za(child, (void **)&za_out, &za_out_size)) {222		/* Should have an unchanged VL */223		if (za_out->vl != sme_vl) {224			ksft_print_msg("VL changed from %d to %d\n",225				       sme_vl, za_out->vl);226			fail = true;227		}228		vq = __sve_vq_from_vl(za_out->vl);229		za_data = (char *)za_out + ZA_PT_ZA_OFFSET;230 231		/* Should have register data */232		if (za_out->size < ZA_PT_SIZE(vq)) {233			ksft_print_msg("ZA data less than expected: %u < %u\n",234				       za_out->size, ZA_PT_SIZE(vq));235			fail = true;236			vq = 0;237		}238 239		/* That register data should be non-zero */240		for (i = 0; i < ZA_PT_ZA_SIZE(vq); i++) {241			if (za_data[i]) {242				ksft_print_msg("ZA byte %d is %x\n",243					       i, za_data[i]);244				fail = true;245			}246		}247	} else {248		ksft_print_msg("Failed to read ZA\n");249		fail = true;250	}251 252	ksft_test_result(!fail, "ptrace_enable_za_via_zt\n");253}254 255static int do_parent(pid_t child)256{257	int ret = EXIT_FAILURE;258	pid_t pid;259	int status;260	siginfo_t si;261 262	/* Attach to the child */263	while (1) {264		int sig;265 266		pid = wait(&status);267		if (pid == -1) {268			perror("wait");269			goto error;270		}271 272		/*273		 * This should never happen but it's hard to flag in274		 * the framework.275		 */276		if (pid != child)277			continue;278 279		if (WIFEXITED(status) || WIFSIGNALED(status))280			ksft_exit_fail_msg("Child died unexpectedly\n");281 282		if (!WIFSTOPPED(status))283			goto error;284 285		sig = WSTOPSIG(status);286 287		if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &si)) {288			if (errno == ESRCH)289				goto disappeared;290 291			if (errno == EINVAL) {292				sig = 0; /* bust group-stop */293				goto cont;294			}295 296			ksft_test_result_fail("PTRACE_GETSIGINFO: %s\n",297					      strerror(errno));298			goto error;299		}300 301		if (sig == SIGSTOP && si.si_code == SI_TKILL &&302		    si.si_pid == pid)303			break;304 305	cont:306		if (ptrace(PTRACE_CONT, pid, NULL, sig)) {307			if (errno == ESRCH)308				goto disappeared;309 310			ksft_test_result_fail("PTRACE_CONT: %s\n",311					      strerror(errno));312			goto error;313		}314	}315 316	ksft_print_msg("Parent is %d, child is %d\n", getpid(), child);317 318	ptrace_za_disabled_read_zt(child);319	ptrace_set_get_zt(child);320	ptrace_enable_za_via_zt(child);321 322	ret = EXIT_SUCCESS;323 324error:325	kill(child, SIGKILL);326 327disappeared:328	return ret;329}330 331int main(void)332{333	int ret = EXIT_SUCCESS;334	pid_t child;335 336	srandom(getpid());337 338	ksft_print_header();339 340	if (!(getauxval(AT_HWCAP2) & HWCAP2_SME2)) {341		ksft_set_plan(1);342		ksft_exit_skip("SME2 not available\n");343	}344 345	/* We need a valid SME VL to enable/disable ZA */346	sme_vl = prctl(PR_SME_GET_VL);347	if (sme_vl == -1) {348		ksft_set_plan(1);349		ksft_exit_skip("Failed to read SME VL: %d (%s)\n",350			       errno, strerror(errno));351	}352 353	ksft_set_plan(EXPECTED_TESTS);354 355	child = fork();356	if (!child)357		return do_child();358 359	if (do_parent(child))360		ret = EXIT_FAILURE;361 362	ksft_print_cnts();363 364	return ret;365}366