Path: blob/master/tools/testing/selftests/arm64/fp/zt-ptrace.c
26289 views
// SPDX-License-Identifier: GPL-2.0-only1/*2* Copyright (C) 2021 ARM Limited.3*/4#include <errno.h>5#include <stdbool.h>6#include <stddef.h>7#include <stdio.h>8#include <stdlib.h>9#include <string.h>10#include <unistd.h>11#include <sys/auxv.h>12#include <sys/prctl.h>13#include <sys/ptrace.h>14#include <sys/types.h>15#include <sys/uio.h>16#include <sys/wait.h>17#include <asm/sigcontext.h>18#include <asm/ptrace.h>1920#include "../../kselftest.h"2122/* <linux/elf.h> and <sys/auxv.h> don't like each other, so: */23#ifndef NT_ARM_ZA24#define NT_ARM_ZA 0x40c25#endif26#ifndef NT_ARM_ZT27#define NT_ARM_ZT 0x40d28#endif2930#define EXPECTED_TESTS 33132static int sme_vl;3334static void fill_buf(char *buf, size_t size)35{36int i;3738for (i = 0; i < size; i++)39buf[i] = random();40}4142static int do_child(void)43{44if (ptrace(PTRACE_TRACEME, -1, NULL, NULL))45ksft_exit_fail_msg("ptrace(PTRACE_TRACEME) failed: %s (%d)\n",46strerror(errno), errno);4748if (raise(SIGSTOP))49ksft_exit_fail_msg("raise(SIGSTOP) failed: %s (%d)\n",50strerror(errno), errno);5152return EXIT_SUCCESS;53}5455static struct user_za_header *get_za(pid_t pid, void **buf, size_t *size)56{57struct user_za_header *za;58void *p;59size_t sz = sizeof(*za);60struct iovec iov;6162while (1) {63if (*size < sz) {64p = realloc(*buf, sz);65if (!p) {66errno = ENOMEM;67goto error;68}6970*buf = p;71*size = sz;72}7374iov.iov_base = *buf;75iov.iov_len = sz;76if (ptrace(PTRACE_GETREGSET, pid, NT_ARM_ZA, &iov))77goto error;7879za = *buf;80if (za->size <= sz)81break;8283sz = za->size;84}8586return za;8788error:89return NULL;90}9192static int set_za(pid_t pid, const struct user_za_header *za)93{94struct iovec iov;9596iov.iov_base = (void *)za;97iov.iov_len = za->size;98return ptrace(PTRACE_SETREGSET, pid, NT_ARM_ZA, &iov);99}100101static int get_zt(pid_t pid, char zt[ZT_SIG_REG_BYTES])102{103struct iovec iov;104105iov.iov_base = zt;106iov.iov_len = ZT_SIG_REG_BYTES;107return ptrace(PTRACE_GETREGSET, pid, NT_ARM_ZT, &iov);108}109110111static int set_zt(pid_t pid, const char zt[ZT_SIG_REG_BYTES])112{113struct iovec iov;114115iov.iov_base = (void *)zt;116iov.iov_len = ZT_SIG_REG_BYTES;117return ptrace(PTRACE_SETREGSET, pid, NT_ARM_ZT, &iov);118}119120/* Reading with ZA disabled returns all zeros */121static void ptrace_za_disabled_read_zt(pid_t child)122{123struct user_za_header za;124char zt[ZT_SIG_REG_BYTES];125int ret, i;126bool fail = false;127128/* Disable PSTATE.ZA using the ZA interface */129memset(&za, 0, sizeof(za));130za.vl = sme_vl;131za.size = sizeof(za);132133ret = set_za(child, &za);134if (ret != 0) {135ksft_print_msg("Failed to disable ZA\n");136fail = true;137}138139/* Read back ZT */140ret = get_zt(child, zt);141if (ret != 0) {142ksft_print_msg("Failed to read ZT\n");143fail = true;144}145146for (i = 0; i < ARRAY_SIZE(zt); i++) {147if (zt[i]) {148ksft_print_msg("zt[%d]: 0x%x != 0\n", i, zt[i]);149fail = true;150}151}152153ksft_test_result(!fail, "ptrace_za_disabled_read_zt\n");154}155156/* Writing then reading ZT should return the data written */157static void ptrace_set_get_zt(pid_t child)158{159char zt_in[ZT_SIG_REG_BYTES];160char zt_out[ZT_SIG_REG_BYTES];161int ret, i;162bool fail = false;163164fill_buf(zt_in, sizeof(zt_in));165166ret = set_zt(child, zt_in);167if (ret != 0) {168ksft_print_msg("Failed to set ZT\n");169fail = true;170}171172ret = get_zt(child, zt_out);173if (ret != 0) {174ksft_print_msg("Failed to read ZT\n");175fail = true;176}177178for (i = 0; i < ARRAY_SIZE(zt_in); i++) {179if (zt_in[i] != zt_out[i]) {180ksft_print_msg("zt[%d]: 0x%x != 0x%x\n", i,181zt_in[i], zt_out[i]);182fail = true;183}184}185186ksft_test_result(!fail, "ptrace_set_get_zt\n");187}188189/* Writing ZT should set PSTATE.ZA */190static void ptrace_enable_za_via_zt(pid_t child)191{192struct user_za_header za_in;193struct user_za_header *za_out;194char zt[ZT_SIG_REG_BYTES];195char *za_data;196size_t za_out_size;197int ret, i, vq;198bool fail = false;199200/* Disable PSTATE.ZA using the ZA interface */201memset(&za_in, 0, sizeof(za_in));202za_in.vl = sme_vl;203za_in.size = sizeof(za_in);204205ret = set_za(child, &za_in);206if (ret != 0) {207ksft_print_msg("Failed to disable ZA\n");208fail = true;209}210211/* Write ZT */212fill_buf(zt, sizeof(zt));213ret = set_zt(child, zt);214if (ret != 0) {215ksft_print_msg("Failed to set ZT\n");216fail = true;217}218219/* Read back ZA and check for register data */220za_out = NULL;221za_out_size = 0;222if (get_za(child, (void **)&za_out, &za_out_size)) {223/* Should have an unchanged VL */224if (za_out->vl != sme_vl) {225ksft_print_msg("VL changed from %d to %d\n",226sme_vl, za_out->vl);227fail = true;228}229vq = __sve_vq_from_vl(za_out->vl);230za_data = (char *)za_out + ZA_PT_ZA_OFFSET;231232/* Should have register data */233if (za_out->size < ZA_PT_SIZE(vq)) {234ksft_print_msg("ZA data less than expected: %u < %u\n",235za_out->size, (unsigned int)ZA_PT_SIZE(vq));236fail = true;237vq = 0;238}239240/* That register data should be non-zero */241for (i = 0; i < ZA_PT_ZA_SIZE(vq); i++) {242if (za_data[i]) {243ksft_print_msg("ZA byte %d is %x\n",244i, za_data[i]);245fail = true;246}247}248} else {249ksft_print_msg("Failed to read ZA\n");250fail = true;251}252253ksft_test_result(!fail, "ptrace_enable_za_via_zt\n");254}255256static int do_parent(pid_t child)257{258int ret = EXIT_FAILURE;259pid_t pid;260int status;261siginfo_t si;262263/* Attach to the child */264while (1) {265int sig;266267pid = wait(&status);268if (pid == -1) {269perror("wait");270goto error;271}272273/*274* This should never happen but it's hard to flag in275* the framework.276*/277if (pid != child)278continue;279280if (WIFEXITED(status) || WIFSIGNALED(status))281ksft_exit_fail_msg("Child died unexpectedly\n");282283if (!WIFSTOPPED(status))284goto error;285286sig = WSTOPSIG(status);287288if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &si)) {289if (errno == ESRCH)290goto disappeared;291292if (errno == EINVAL) {293sig = 0; /* bust group-stop */294goto cont;295}296297ksft_test_result_fail("PTRACE_GETSIGINFO: %s\n",298strerror(errno));299goto error;300}301302if (sig == SIGSTOP && si.si_code == SI_TKILL &&303si.si_pid == pid)304break;305306cont:307if (ptrace(PTRACE_CONT, pid, NULL, sig)) {308if (errno == ESRCH)309goto disappeared;310311ksft_test_result_fail("PTRACE_CONT: %s\n",312strerror(errno));313goto error;314}315}316317ksft_print_msg("Parent is %d, child is %d\n", getpid(), child);318319ptrace_za_disabled_read_zt(child);320ptrace_set_get_zt(child);321ptrace_enable_za_via_zt(child);322323ret = EXIT_SUCCESS;324325error:326kill(child, SIGKILL);327328disappeared:329return ret;330}331332int main(void)333{334int ret = EXIT_SUCCESS;335pid_t child;336337srandom(getpid());338339ksft_print_header();340341if (!(getauxval(AT_HWCAP2) & HWCAP2_SME2)) {342ksft_set_plan(1);343ksft_exit_skip("SME2 not available\n");344}345346/* We need a valid SME VL to enable/disable ZA */347sme_vl = prctl(PR_SME_GET_VL);348if (sme_vl == -1) {349ksft_set_plan(1);350ksft_exit_skip("Failed to read SME VL: %d (%s)\n",351errno, strerror(errno));352}353354ksft_set_plan(EXPECTED_TESTS);355356child = fork();357if (!child)358return do_child();359360if (do_parent(child))361ret = EXIT_FAILURE;362363ksft_print_cnts();364365return ret;366}367368369