Path: blob/master/tools/testing/selftests/arm64/fp/za-ptrace.c
26295 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#endif2627/*28* The architecture defines the maximum VQ as 16 but for extensibility29* the kernel specifies the SVE_VQ_MAX as 512 resulting in us running30* a *lot* more tests than are useful if we use it. Until the31* architecture is extended let's limit our coverage to what is32* currently allowed, plus one extra to ensure we cover constraining33* the VL as expected.34*/35#define TEST_VQ_MAX 173637#define EXPECTED_TESTS (((TEST_VQ_MAX - SVE_VQ_MIN) + 1) * 3)3839static void fill_buf(char *buf, size_t size)40{41int i;4243for (i = 0; i < size; i++)44buf[i] = random();45}4647static int do_child(void)48{49if (ptrace(PTRACE_TRACEME, -1, NULL, NULL))50ksft_exit_fail_msg("ptrace(PTRACE_TRACEME) failed: %s (%d)",51strerror(errno), errno);5253if (raise(SIGSTOP))54ksft_exit_fail_msg("raise(SIGSTOP) failed: %s (%d)\n",55strerror(errno), errno);5657return EXIT_SUCCESS;58}5960static struct user_za_header *get_za(pid_t pid, void **buf, size_t *size)61{62struct user_za_header *za;63void *p;64size_t sz = sizeof(*za);65struct iovec iov;6667while (1) {68if (*size < sz) {69p = realloc(*buf, sz);70if (!p) {71errno = ENOMEM;72goto error;73}7475*buf = p;76*size = sz;77}7879iov.iov_base = *buf;80iov.iov_len = sz;81if (ptrace(PTRACE_GETREGSET, pid, NT_ARM_ZA, &iov))82goto error;8384za = *buf;85if (za->size <= sz)86break;8788sz = za->size;89}9091return za;9293error:94return NULL;95}9697static int set_za(pid_t pid, const struct user_za_header *za)98{99struct iovec iov;100101iov.iov_base = (void *)za;102iov.iov_len = za->size;103return ptrace(PTRACE_SETREGSET, pid, NT_ARM_ZA, &iov);104}105106/* Validate attempting to set the specfied VL via ptrace */107static void ptrace_set_get_vl(pid_t child, unsigned int vl, bool *supported)108{109struct user_za_header za;110struct user_za_header *new_za = NULL;111size_t new_za_size = 0;112int ret, prctl_vl;113114*supported = false;115116/* Check if the VL is supported in this process */117prctl_vl = prctl(PR_SME_SET_VL, vl);118if (prctl_vl == -1)119ksft_exit_fail_msg("prctl(PR_SME_SET_VL) failed: %s (%d)\n",120strerror(errno), errno);121122/* If the VL is not supported then a supported VL will be returned */123*supported = (prctl_vl == vl);124125/* Set the VL by doing a set with no register payload */126memset(&za, 0, sizeof(za));127za.size = sizeof(za);128za.vl = vl;129ret = set_za(child, &za);130if (ret != 0) {131ksft_test_result_fail("Failed to set VL %u\n", vl);132return;133}134135/*136* Read back the new register state and verify that we have the137* same VL that we got from prctl() on ourselves.138*/139if (!get_za(child, (void **)&new_za, &new_za_size)) {140ksft_test_result_fail("Failed to read VL %u\n", vl);141return;142}143144ksft_test_result(new_za->vl = prctl_vl, "Set VL %u\n", vl);145146free(new_za);147}148149/* Validate attempting to set no ZA data and read it back */150static void ptrace_set_no_data(pid_t child, unsigned int vl)151{152void *read_buf = NULL;153struct user_za_header write_za;154struct user_za_header *read_za;155size_t read_za_size = 0;156int ret;157158/* Set up some data and write it out */159memset(&write_za, 0, sizeof(write_za));160write_za.size = ZA_PT_ZA_OFFSET;161write_za.vl = vl;162163ret = set_za(child, &write_za);164if (ret != 0) {165ksft_test_result_fail("Failed to set VL %u no data\n", vl);166return;167}168169/* Read the data back */170if (!get_za(child, (void **)&read_buf, &read_za_size)) {171ksft_test_result_fail("Failed to read VL %u no data\n", vl);172return;173}174read_za = read_buf;175176/* We might read more data if there's extensions we don't know */177if (read_za->size < write_za.size) {178ksft_test_result_fail("VL %u wrote %d bytes, only read %d\n",179vl, write_za.size, read_za->size);180goto out_read;181}182183ksft_test_result(read_za->size == write_za.size,184"Disabled ZA for VL %u\n", vl);185186out_read:187free(read_buf);188}189190/* Validate attempting to set data and read it back */191static void ptrace_set_get_data(pid_t child, unsigned int vl)192{193void *write_buf;194void *read_buf = NULL;195struct user_za_header *write_za;196struct user_za_header *read_za;197size_t read_za_size = 0;198unsigned int vq = sve_vq_from_vl(vl);199int ret;200size_t data_size;201202data_size = ZA_PT_SIZE(vq);203write_buf = malloc(data_size);204if (!write_buf) {205ksft_test_result_fail("Error allocating %ld byte buffer for VL %u\n",206data_size, vl);207return;208}209write_za = write_buf;210211/* Set up some data and write it out */212memset(write_za, 0, data_size);213write_za->size = data_size;214write_za->vl = vl;215216fill_buf(write_buf + ZA_PT_ZA_OFFSET, ZA_PT_ZA_SIZE(vq));217218ret = set_za(child, write_za);219if (ret != 0) {220ksft_test_result_fail("Failed to set VL %u data\n", vl);221goto out;222}223224/* Read the data back */225if (!get_za(child, (void **)&read_buf, &read_za_size)) {226ksft_test_result_fail("Failed to read VL %u data\n", vl);227goto out;228}229read_za = read_buf;230231/* We might read more data if there's extensions we don't know */232if (read_za->size < write_za->size) {233ksft_test_result_fail("VL %u wrote %d bytes, only read %d\n",234vl, write_za->size, read_za->size);235goto out_read;236}237238ksft_test_result(memcmp(write_buf + ZA_PT_ZA_OFFSET,239read_buf + ZA_PT_ZA_OFFSET,240ZA_PT_ZA_SIZE(vq)) == 0,241"Data match for VL %u\n", vl);242243out_read:244free(read_buf);245out:246free(write_buf);247}248249static int do_parent(pid_t child)250{251int ret = EXIT_FAILURE;252pid_t pid;253int status;254siginfo_t si;255unsigned int vq, vl;256bool vl_supported;257258/* Attach to the child */259while (1) {260int sig;261262pid = wait(&status);263if (pid == -1) {264perror("wait");265goto error;266}267268/*269* This should never happen but it's hard to flag in270* the framework.271*/272if (pid != child)273continue;274275if (WIFEXITED(status) || WIFSIGNALED(status))276ksft_exit_fail_msg("Child died unexpectedly\n");277278if (!WIFSTOPPED(status))279goto error;280281sig = WSTOPSIG(status);282283if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &si)) {284if (errno == ESRCH)285goto disappeared;286287if (errno == EINVAL) {288sig = 0; /* bust group-stop */289goto cont;290}291292ksft_test_result_fail("PTRACE_GETSIGINFO: %s\n",293strerror(errno));294goto error;295}296297if (sig == SIGSTOP && si.si_code == SI_TKILL &&298si.si_pid == pid)299break;300301cont:302if (ptrace(PTRACE_CONT, pid, NULL, sig)) {303if (errno == ESRCH)304goto disappeared;305306ksft_test_result_fail("PTRACE_CONT: %s\n",307strerror(errno));308goto error;309}310}311312ksft_print_msg("Parent is %d, child is %d\n", getpid(), child);313314/* Step through every possible VQ */315for (vq = SVE_VQ_MIN; vq <= TEST_VQ_MAX; vq++) {316vl = sve_vl_from_vq(vq);317318/* First, try to set this vector length */319ptrace_set_get_vl(child, vl, &vl_supported);320321/* If the VL is supported validate data set/get */322if (vl_supported) {323ptrace_set_no_data(child, vl);324ptrace_set_get_data(child, vl);325} else {326ksft_test_result_skip("Disabled ZA for VL %u\n", vl);327ksft_test_result_skip("Get and set data for VL %u\n",328vl);329}330}331332ret = EXIT_SUCCESS;333334error:335kill(child, SIGKILL);336337disappeared:338return ret;339}340341int main(void)342{343int ret = EXIT_SUCCESS;344pid_t child;345346srandom(getpid());347348ksft_print_header();349350if (!(getauxval(AT_HWCAP2) & HWCAP2_SME)) {351ksft_set_plan(1);352ksft_exit_skip("SME not available\n");353}354355ksft_set_plan(EXPECTED_TESTS);356357child = fork();358if (!child)359return do_child();360361if (do_parent(child))362ret = EXIT_FAILURE;363364ksft_print_cnts();365366return ret;367}368369370