Path: blob/master/tools/testing/selftests/arm64/fp/sve-ptrace.c
26292 views
// SPDX-License-Identifier: GPL-2.0-only1/*2* Copyright (C) 2015-2021 ARM Limited.3* Original author: Dave Martin <[email protected]>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>2021#include "../../kselftest.h"2223/* <linux/elf.h> and <sys/auxv.h> don't like each other, so: */24#ifndef NT_ARM_SVE25#define NT_ARM_SVE 0x40526#endif2728#ifndef NT_ARM_SSVE29#define NT_ARM_SSVE 0x40b30#endif3132/*33* The architecture defines the maximum VQ as 16 but for extensibility34* the kernel specifies the SVE_VQ_MAX as 512 resulting in us running35* a *lot* more tests than are useful if we use it. Until the36* architecture is extended let's limit our coverage to what is37* currently allowed, plus one extra to ensure we cover constraining38* the VL as expected.39*/40#define TEST_VQ_MAX 174142struct vec_type {43const char *name;44unsigned long hwcap_type;45unsigned long hwcap;46int regset;47int prctl_set;48};4950static const struct vec_type vec_types[] = {51{52.name = "SVE",53.hwcap_type = AT_HWCAP,54.hwcap = HWCAP_SVE,55.regset = NT_ARM_SVE,56.prctl_set = PR_SVE_SET_VL,57},58{59.name = "Streaming SVE",60.hwcap_type = AT_HWCAP2,61.hwcap = HWCAP2_SME,62.regset = NT_ARM_SSVE,63.prctl_set = PR_SME_SET_VL,64},65};6667#define VL_TESTS (((TEST_VQ_MAX - SVE_VQ_MIN) + 1) * 4)68#define FLAG_TESTS 269#define FPSIMD_TESTS 27071#define EXPECTED_TESTS ((VL_TESTS + FLAG_TESTS + FPSIMD_TESTS) * ARRAY_SIZE(vec_types))7273static void fill_buf(char *buf, size_t size)74{75int i;7677for (i = 0; i < size; i++)78buf[i] = random();79}8081static int do_child(void)82{83if (ptrace(PTRACE_TRACEME, -1, NULL, NULL))84ksft_exit_fail_msg("ptrace(PTRACE_TRACEME) failed: %s (%d)\n",85strerror(errno), errno);8687if (raise(SIGSTOP))88ksft_exit_fail_msg("raise(SIGSTOP) failed: %s (%d)\n",89strerror(errno), errno);9091return EXIT_SUCCESS;92}9394static int get_fpsimd(pid_t pid, struct user_fpsimd_state *fpsimd)95{96struct iovec iov;9798iov.iov_base = fpsimd;99iov.iov_len = sizeof(*fpsimd);100return ptrace(PTRACE_GETREGSET, pid, NT_PRFPREG, &iov);101}102103static int set_fpsimd(pid_t pid, struct user_fpsimd_state *fpsimd)104{105struct iovec iov;106107iov.iov_base = fpsimd;108iov.iov_len = sizeof(*fpsimd);109return ptrace(PTRACE_SETREGSET, pid, NT_PRFPREG, &iov);110}111112static struct user_sve_header *get_sve(pid_t pid, const struct vec_type *type,113void **buf, size_t *size)114{115struct user_sve_header *sve;116void *p;117size_t sz = sizeof *sve;118struct iovec iov;119120while (1) {121if (*size < sz) {122p = realloc(*buf, sz);123if (!p) {124errno = ENOMEM;125goto error;126}127128*buf = p;129*size = sz;130}131132iov.iov_base = *buf;133iov.iov_len = sz;134if (ptrace(PTRACE_GETREGSET, pid, type->regset, &iov))135goto error;136137sve = *buf;138if (sve->size <= sz)139break;140141sz = sve->size;142}143144return sve;145146error:147return NULL;148}149150static int set_sve(pid_t pid, const struct vec_type *type,151const struct user_sve_header *sve)152{153struct iovec iov;154155iov.iov_base = (void *)sve;156iov.iov_len = sve->size;157return ptrace(PTRACE_SETREGSET, pid, type->regset, &iov);158}159160/* Validate setting and getting the inherit flag */161static void ptrace_set_get_inherit(pid_t child, const struct vec_type *type)162{163struct user_sve_header sve;164struct user_sve_header *new_sve = NULL;165size_t new_sve_size = 0;166int ret;167168/* First set the flag */169memset(&sve, 0, sizeof(sve));170sve.size = sizeof(sve);171sve.vl = sve_vl_from_vq(SVE_VQ_MIN);172sve.flags = SVE_PT_VL_INHERIT | SVE_PT_REGS_SVE;173ret = set_sve(child, type, &sve);174if (ret != 0) {175ksft_test_result_fail("Failed to set %s SVE_PT_VL_INHERIT\n",176type->name);177return;178}179180/*181* Read back the new register state and verify that we have182* set the flags we expected.183*/184if (!get_sve(child, type, (void **)&new_sve, &new_sve_size)) {185ksft_test_result_fail("Failed to read %s SVE flags\n",186type->name);187return;188}189190ksft_test_result(new_sve->flags & SVE_PT_VL_INHERIT,191"%s SVE_PT_VL_INHERIT set\n", type->name);192193/* Now clear */194sve.flags &= ~SVE_PT_VL_INHERIT;195ret = set_sve(child, type, &sve);196if (ret != 0) {197ksft_test_result_fail("Failed to clear %s SVE_PT_VL_INHERIT\n",198type->name);199return;200}201202if (!get_sve(child, type, (void **)&new_sve, &new_sve_size)) {203ksft_test_result_fail("Failed to read %s SVE flags\n",204type->name);205return;206}207208ksft_test_result(!(new_sve->flags & SVE_PT_VL_INHERIT),209"%s SVE_PT_VL_INHERIT cleared\n", type->name);210211free(new_sve);212}213214/* Validate attempting to set the specfied VL via ptrace */215static void ptrace_set_get_vl(pid_t child, const struct vec_type *type,216unsigned int vl, bool *supported)217{218struct user_sve_header sve;219struct user_sve_header *new_sve = NULL;220size_t new_sve_size = 0;221int ret, prctl_vl;222223*supported = false;224225/* Check if the VL is supported in this process */226prctl_vl = prctl(type->prctl_set, vl);227if (prctl_vl == -1)228ksft_exit_fail_msg("prctl(PR_%s_SET_VL) failed: %s (%d)\n",229type->name, strerror(errno), errno);230231/* If the VL is not supported then a supported VL will be returned */232*supported = (prctl_vl == vl);233234/* Set the VL by doing a set with no register payload */235memset(&sve, 0, sizeof(sve));236sve.size = sizeof(sve);237sve.flags = SVE_PT_REGS_SVE;238sve.vl = vl;239ret = set_sve(child, type, &sve);240if (ret != 0) {241ksft_test_result_fail("Failed to set %s VL %u\n",242type->name, vl);243return;244}245246/*247* Read back the new register state and verify that we have the248* same VL that we got from prctl() on ourselves.249*/250if (!get_sve(child, type, (void **)&new_sve, &new_sve_size)) {251ksft_test_result_fail("Failed to read %s VL %u\n",252type->name, vl);253return;254}255256ksft_test_result(new_sve->vl == prctl_vl, "Set %s VL %u\n",257type->name, vl);258259free(new_sve);260}261262static void check_u32(unsigned int vl, const char *reg,263uint32_t *in, uint32_t *out, int *errors)264{265if (*in != *out) {266printf("# VL %d %s wrote %x read %x\n",267vl, reg, *in, *out);268(*errors)++;269}270}271272/* Access the FPSIMD registers via the SVE regset */273static void ptrace_sve_fpsimd(pid_t child, const struct vec_type *type)274{275void *svebuf;276struct user_sve_header *sve;277struct user_fpsimd_state *fpsimd, new_fpsimd;278unsigned int i, j;279unsigned char *p;280int ret;281282svebuf = malloc(SVE_PT_SIZE(0, SVE_PT_REGS_FPSIMD));283if (!svebuf) {284ksft_test_result_fail("Failed to allocate FPSIMD buffer\n");285return;286}287288memset(svebuf, 0, SVE_PT_SIZE(0, SVE_PT_REGS_FPSIMD));289sve = svebuf;290sve->flags = SVE_PT_REGS_FPSIMD;291sve->size = SVE_PT_SIZE(0, SVE_PT_REGS_FPSIMD);292sve->vl = 16; /* We don't care what the VL is */293294/* Try to set a known FPSIMD state via PT_REGS_SVE */295fpsimd = (struct user_fpsimd_state *)((char *)sve +296SVE_PT_FPSIMD_OFFSET);297for (i = 0; i < 32; ++i) {298p = (unsigned char *)&fpsimd->vregs[i];299300for (j = 0; j < sizeof(fpsimd->vregs[i]); ++j)301p[j] = j;302}303304/* This should only succeed for SVE */305ret = set_sve(child, type, sve);306ksft_test_result((type->regset == NT_ARM_SVE) == (ret == 0),307"%s FPSIMD set via SVE: %d\n",308type->name, ret);309if (ret)310goto out;311312/* Verify via the FPSIMD regset */313if (get_fpsimd(child, &new_fpsimd)) {314ksft_test_result_fail("get_fpsimd(): %s\n",315strerror(errno));316goto out;317}318if (memcmp(fpsimd, &new_fpsimd, sizeof(*fpsimd)) == 0)319ksft_test_result_pass("%s get_fpsimd() gave same state\n",320type->name);321else322ksft_test_result_fail("%s get_fpsimd() gave different state\n",323type->name);324325out:326free(svebuf);327}328329/* Validate attempting to set SVE data and read SVE data */330static void ptrace_set_sve_get_sve_data(pid_t child,331const struct vec_type *type,332unsigned int vl)333{334void *write_buf;335void *read_buf = NULL;336struct user_sve_header *write_sve;337struct user_sve_header *read_sve;338size_t read_sve_size = 0;339unsigned int vq = sve_vq_from_vl(vl);340int ret, i;341size_t data_size;342int errors = 0;343344data_size = SVE_PT_SVE_OFFSET + SVE_PT_SVE_SIZE(vq, SVE_PT_REGS_SVE);345write_buf = malloc(data_size);346if (!write_buf) {347ksft_test_result_fail("Error allocating %ld byte buffer for %s VL %u\n",348data_size, type->name, vl);349return;350}351write_sve = write_buf;352353/* Set up some data and write it out */354memset(write_sve, 0, data_size);355write_sve->size = data_size;356write_sve->vl = vl;357write_sve->flags = SVE_PT_REGS_SVE;358359for (i = 0; i < __SVE_NUM_ZREGS; i++)360fill_buf(write_buf + SVE_PT_SVE_ZREG_OFFSET(vq, i),361SVE_PT_SVE_ZREG_SIZE(vq));362363for (i = 0; i < __SVE_NUM_PREGS; i++)364fill_buf(write_buf + SVE_PT_SVE_PREG_OFFSET(vq, i),365SVE_PT_SVE_PREG_SIZE(vq));366367fill_buf(write_buf + SVE_PT_SVE_FPSR_OFFSET(vq), SVE_PT_SVE_FPSR_SIZE);368fill_buf(write_buf + SVE_PT_SVE_FPCR_OFFSET(vq), SVE_PT_SVE_FPCR_SIZE);369370/* TODO: Generate a valid FFR pattern */371372ret = set_sve(child, type, write_sve);373if (ret != 0) {374ksft_test_result_fail("Failed to set %s VL %u data\n",375type->name, vl);376goto out;377}378379/* Read the data back */380if (!get_sve(child, type, (void **)&read_buf, &read_sve_size)) {381ksft_test_result_fail("Failed to read %s VL %u data\n",382type->name, vl);383goto out;384}385read_sve = read_buf;386387/* We might read more data if there's extensions we don't know */388if (read_sve->size < write_sve->size) {389ksft_test_result_fail("%s wrote %d bytes, only read %d\n",390type->name, write_sve->size,391read_sve->size);392goto out_read;393}394395for (i = 0; i < __SVE_NUM_ZREGS; i++) {396if (memcmp(write_buf + SVE_PT_SVE_ZREG_OFFSET(vq, i),397read_buf + SVE_PT_SVE_ZREG_OFFSET(vq, i),398SVE_PT_SVE_ZREG_SIZE(vq)) != 0) {399printf("# Mismatch in %u Z%d\n", vl, i);400errors++;401}402}403404for (i = 0; i < __SVE_NUM_PREGS; i++) {405if (memcmp(write_buf + SVE_PT_SVE_PREG_OFFSET(vq, i),406read_buf + SVE_PT_SVE_PREG_OFFSET(vq, i),407SVE_PT_SVE_PREG_SIZE(vq)) != 0) {408printf("# Mismatch in %u P%d\n", vl, i);409errors++;410}411}412413check_u32(vl, "FPSR", write_buf + SVE_PT_SVE_FPSR_OFFSET(vq),414read_buf + SVE_PT_SVE_FPSR_OFFSET(vq), &errors);415check_u32(vl, "FPCR", write_buf + SVE_PT_SVE_FPCR_OFFSET(vq),416read_buf + SVE_PT_SVE_FPCR_OFFSET(vq), &errors);417418ksft_test_result(errors == 0, "Set and get %s data for VL %u\n",419type->name, vl);420421out_read:422free(read_buf);423out:424free(write_buf);425}426427/* Validate attempting to set SVE data and read it via the FPSIMD regset */428static void ptrace_set_sve_get_fpsimd_data(pid_t child,429const struct vec_type *type,430unsigned int vl)431{432void *write_buf;433struct user_sve_header *write_sve;434unsigned int vq = sve_vq_from_vl(vl);435struct user_fpsimd_state fpsimd_state;436int ret, i;437size_t data_size;438int errors = 0;439440if (__BYTE_ORDER == __BIG_ENDIAN) {441ksft_test_result_skip("Big endian not supported\n");442return;443}444445data_size = SVE_PT_SVE_OFFSET + SVE_PT_SVE_SIZE(vq, SVE_PT_REGS_SVE);446write_buf = malloc(data_size);447if (!write_buf) {448ksft_test_result_fail("Error allocating %ld byte buffer for %s VL %u\n",449data_size, type->name, vl);450return;451}452write_sve = write_buf;453454/* Set up some data and write it out */455memset(write_sve, 0, data_size);456write_sve->size = data_size;457write_sve->vl = vl;458write_sve->flags = SVE_PT_REGS_SVE;459460for (i = 0; i < __SVE_NUM_ZREGS; i++)461fill_buf(write_buf + SVE_PT_SVE_ZREG_OFFSET(vq, i),462SVE_PT_SVE_ZREG_SIZE(vq));463464fill_buf(write_buf + SVE_PT_SVE_FPSR_OFFSET(vq), SVE_PT_SVE_FPSR_SIZE);465fill_buf(write_buf + SVE_PT_SVE_FPCR_OFFSET(vq), SVE_PT_SVE_FPCR_SIZE);466467ret = set_sve(child, type, write_sve);468if (ret != 0) {469ksft_test_result_fail("Failed to set %s VL %u data\n",470type->name, vl);471goto out;472}473474/* Read the data back */475if (get_fpsimd(child, &fpsimd_state)) {476ksft_test_result_fail("Failed to read %s VL %u FPSIMD data\n",477type->name, vl);478goto out;479}480481for (i = 0; i < __SVE_NUM_ZREGS; i++) {482__uint128_t tmp = 0;483484/*485* Z regs are stored endianness invariant, this won't486* work for big endian487*/488memcpy(&tmp, write_buf + SVE_PT_SVE_ZREG_OFFSET(vq, i),489sizeof(tmp));490491if (tmp != fpsimd_state.vregs[i]) {492printf("# Mismatch in FPSIMD for %s VL %u Z%d\n",493type->name, vl, i);494errors++;495}496}497498check_u32(vl, "FPSR", write_buf + SVE_PT_SVE_FPSR_OFFSET(vq),499&fpsimd_state.fpsr, &errors);500check_u32(vl, "FPCR", write_buf + SVE_PT_SVE_FPCR_OFFSET(vq),501&fpsimd_state.fpcr, &errors);502503ksft_test_result(errors == 0, "Set and get FPSIMD data for %s VL %u\n",504type->name, vl);505506out:507free(write_buf);508}509510/* Validate attempting to set FPSIMD data and read it via the SVE regset */511static void ptrace_set_fpsimd_get_sve_data(pid_t child,512const struct vec_type *type,513unsigned int vl)514{515void *read_buf = NULL;516unsigned char *p;517struct user_sve_header *read_sve;518unsigned int vq = sve_vq_from_vl(vl);519struct user_fpsimd_state write_fpsimd;520int ret, i, j;521size_t read_sve_size = 0;522size_t expected_size;523int errors = 0;524525if (__BYTE_ORDER == __BIG_ENDIAN) {526ksft_test_result_skip("Big endian not supported\n");527return;528}529530for (i = 0; i < 32; ++i) {531p = (unsigned char *)&write_fpsimd.vregs[i];532533for (j = 0; j < sizeof(write_fpsimd.vregs[i]); ++j)534p[j] = j;535}536537ret = set_fpsimd(child, &write_fpsimd);538if (ret != 0) {539ksft_test_result_fail("Failed to set FPSIMD state: %d\n)",540ret);541return;542}543544if (!get_sve(child, type, (void **)&read_buf, &read_sve_size)) {545ksft_test_result_fail("Failed to read %s VL %u data\n",546type->name, vl);547return;548}549read_sve = read_buf;550551if (read_sve->vl != vl) {552ksft_test_result_fail("Child VL != expected VL: %u != %u\n",553read_sve->vl, vl);554goto out;555}556557/* The kernel may return either SVE or FPSIMD format */558switch (read_sve->flags & SVE_PT_REGS_MASK) {559case SVE_PT_REGS_FPSIMD:560expected_size = SVE_PT_FPSIMD_SIZE(vq, SVE_PT_REGS_FPSIMD);561if (read_sve_size < expected_size) {562ksft_test_result_fail("Read %ld bytes, expected %ld\n",563read_sve_size, expected_size);564goto out;565}566567ret = memcmp(&write_fpsimd, read_buf + SVE_PT_FPSIMD_OFFSET,568sizeof(write_fpsimd));569if (ret != 0) {570ksft_print_msg("Read FPSIMD data mismatch\n");571errors++;572}573break;574575case SVE_PT_REGS_SVE:576expected_size = SVE_PT_SVE_SIZE(vq, SVE_PT_REGS_SVE);577if (read_sve_size < expected_size) {578ksft_test_result_fail("Read %ld bytes, expected %ld\n",579read_sve_size, expected_size);580goto out;581}582583for (i = 0; i < __SVE_NUM_ZREGS; i++) {584__uint128_t tmp = 0;585586/*587* Z regs are stored endianness invariant, this won't588* work for big endian589*/590memcpy(&tmp, read_buf + SVE_PT_SVE_ZREG_OFFSET(vq, i),591sizeof(tmp));592593if (tmp != write_fpsimd.vregs[i]) {594ksft_print_msg("Mismatch in FPSIMD for %s VL %u Z%d/V%d\n",595type->name, vl, i, i);596errors++;597}598}599600check_u32(vl, "FPSR", &write_fpsimd.fpsr,601read_buf + SVE_PT_SVE_FPSR_OFFSET(vq), &errors);602check_u32(vl, "FPCR", &write_fpsimd.fpcr,603read_buf + SVE_PT_SVE_FPCR_OFFSET(vq), &errors);604break;605default:606ksft_print_msg("Unexpected regs type %d\n",607read_sve->flags & SVE_PT_REGS_MASK);608errors++;609break;610}611612ksft_test_result(errors == 0, "Set FPSIMD, read via SVE for %s VL %u\n",613type->name, vl);614615out:616free(read_buf);617}618619static int do_parent(pid_t child)620{621int ret = EXIT_FAILURE;622pid_t pid;623int status, i;624siginfo_t si;625unsigned int vq, vl;626bool vl_supported;627628ksft_print_msg("Parent is %d, child is %d\n", getpid(), child);629630/* Attach to the child */631while (1) {632int sig;633634pid = wait(&status);635if (pid == -1) {636perror("wait");637goto error;638}639640/*641* This should never happen but it's hard to flag in642* the framework.643*/644if (pid != child)645continue;646647if (WIFEXITED(status) || WIFSIGNALED(status))648ksft_exit_fail_msg("Child died unexpectedly\n");649650if (!WIFSTOPPED(status))651goto error;652653sig = WSTOPSIG(status);654655if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &si)) {656if (errno == ESRCH)657goto disappeared;658659if (errno == EINVAL) {660sig = 0; /* bust group-stop */661goto cont;662}663664ksft_test_result_fail("PTRACE_GETSIGINFO: %s\n",665strerror(errno));666goto error;667}668669if (sig == SIGSTOP && si.si_code == SI_TKILL &&670si.si_pid == pid)671break;672673cont:674if (ptrace(PTRACE_CONT, pid, NULL, sig)) {675if (errno == ESRCH)676goto disappeared;677678ksft_test_result_fail("PTRACE_CONT: %s\n",679strerror(errno));680goto error;681}682}683684for (i = 0; i < ARRAY_SIZE(vec_types); i++) {685/* FPSIMD via SVE regset */686if (getauxval(vec_types[i].hwcap_type) & vec_types[i].hwcap) {687ptrace_sve_fpsimd(child, &vec_types[i]);688} else {689ksft_test_result_skip("%s FPSIMD set via SVE\n",690vec_types[i].name);691ksft_test_result_skip("%s FPSIMD read\n",692vec_types[i].name);693}694695/* prctl() flags */696if (getauxval(vec_types[i].hwcap_type) & vec_types[i].hwcap) {697ptrace_set_get_inherit(child, &vec_types[i]);698} else {699ksft_test_result_skip("%s SVE_PT_VL_INHERIT set\n",700vec_types[i].name);701ksft_test_result_skip("%s SVE_PT_VL_INHERIT cleared\n",702vec_types[i].name);703}704705/* Step through every possible VQ */706for (vq = SVE_VQ_MIN; vq <= TEST_VQ_MAX; vq++) {707vl = sve_vl_from_vq(vq);708709/* First, try to set this vector length */710if (getauxval(vec_types[i].hwcap_type) &711vec_types[i].hwcap) {712ptrace_set_get_vl(child, &vec_types[i], vl,713&vl_supported);714} else {715ksft_test_result_skip("%s get/set VL %d\n",716vec_types[i].name, vl);717vl_supported = false;718}719720/* If the VL is supported validate data set/get */721if (vl_supported) {722ptrace_set_sve_get_sve_data(child, &vec_types[i], vl);723ptrace_set_sve_get_fpsimd_data(child, &vec_types[i], vl);724ptrace_set_fpsimd_get_sve_data(child, &vec_types[i], vl);725} else {726ksft_test_result_skip("%s set SVE get SVE for VL %d\n",727vec_types[i].name, vl);728ksft_test_result_skip("%s set SVE get FPSIMD for VL %d\n",729vec_types[i].name, vl);730ksft_test_result_skip("%s set FPSIMD get SVE for VL %d\n",731vec_types[i].name, vl);732}733}734}735736ret = EXIT_SUCCESS;737738error:739kill(child, SIGKILL);740741disappeared:742return ret;743}744745int main(void)746{747int ret = EXIT_SUCCESS;748pid_t child;749750srandom(getpid());751752ksft_print_header();753ksft_set_plan(EXPECTED_TESTS);754755child = fork();756if (!child)757return do_child();758759if (do_parent(child))760ret = EXIT_FAILURE;761762ksft_print_cnts();763764return ret;765}766767768