765 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2015-2021 ARM Limited.4 * Original author: Dave Martin <Dave.Martin@arm.com>5 */6#include <errno.h>7#include <stdbool.h>8#include <stddef.h>9#include <stdio.h>10#include <stdlib.h>11#include <string.h>12#include <unistd.h>13#include <sys/auxv.h>14#include <sys/prctl.h>15#include <sys/ptrace.h>16#include <sys/types.h>17#include <sys/uio.h>18#include <sys/wait.h>19#include <asm/sigcontext.h>20#include <asm/ptrace.h>21 22#include "../../kselftest.h"23 24/* <linux/elf.h> and <sys/auxv.h> don't like each other, so: */25#ifndef NT_ARM_SVE26#define NT_ARM_SVE 0x40527#endif28 29#ifndef NT_ARM_SSVE30#define NT_ARM_SSVE 0x40b31#endif32 33/*34 * The architecture defines the maximum VQ as 16 but for extensibility35 * the kernel specifies the SVE_VQ_MAX as 512 resulting in us running36 * a *lot* more tests than are useful if we use it. Until the37 * architecture is extended let's limit our coverage to what is38 * currently allowed, plus one extra to ensure we cover constraining39 * the VL as expected.40 */41#define TEST_VQ_MAX 1742 43struct vec_type {44 const char *name;45 unsigned long hwcap_type;46 unsigned long hwcap;47 int regset;48 int prctl_set;49};50 51static const struct vec_type vec_types[] = {52 {53 .name = "SVE",54 .hwcap_type = AT_HWCAP,55 .hwcap = HWCAP_SVE,56 .regset = NT_ARM_SVE,57 .prctl_set = PR_SVE_SET_VL,58 },59 {60 .name = "Streaming SVE",61 .hwcap_type = AT_HWCAP2,62 .hwcap = HWCAP2_SME,63 .regset = NT_ARM_SSVE,64 .prctl_set = PR_SME_SET_VL,65 },66};67 68#define VL_TESTS (((TEST_VQ_MAX - SVE_VQ_MIN) + 1) * 4)69#define FLAG_TESTS 270#define FPSIMD_TESTS 271 72#define EXPECTED_TESTS ((VL_TESTS + FLAG_TESTS + FPSIMD_TESTS) * ARRAY_SIZE(vec_types))73 74static void fill_buf(char *buf, size_t size)75{76 int i;77 78 for (i = 0; i < size; i++)79 buf[i] = random();80}81 82static int do_child(void)83{84 if (ptrace(PTRACE_TRACEME, -1, NULL, NULL))85 ksft_exit_fail_msg("PTRACE_TRACEME", strerror(errno));86 87 if (raise(SIGSTOP))88 ksft_exit_fail_msg("raise(SIGSTOP)", strerror(errno));89 90 return EXIT_SUCCESS;91}92 93static int get_fpsimd(pid_t pid, struct user_fpsimd_state *fpsimd)94{95 struct iovec iov;96 97 iov.iov_base = fpsimd;98 iov.iov_len = sizeof(*fpsimd);99 return ptrace(PTRACE_GETREGSET, pid, NT_PRFPREG, &iov);100}101 102static int set_fpsimd(pid_t pid, struct user_fpsimd_state *fpsimd)103{104 struct iovec iov;105 106 iov.iov_base = fpsimd;107 iov.iov_len = sizeof(*fpsimd);108 return ptrace(PTRACE_SETREGSET, pid, NT_PRFPREG, &iov);109}110 111static struct user_sve_header *get_sve(pid_t pid, const struct vec_type *type,112 void **buf, size_t *size)113{114 struct user_sve_header *sve;115 void *p;116 size_t sz = sizeof *sve;117 struct iovec iov;118 119 while (1) {120 if (*size < sz) {121 p = realloc(*buf, sz);122 if (!p) {123 errno = ENOMEM;124 goto error;125 }126 127 *buf = p;128 *size = sz;129 }130 131 iov.iov_base = *buf;132 iov.iov_len = sz;133 if (ptrace(PTRACE_GETREGSET, pid, type->regset, &iov))134 goto error;135 136 sve = *buf;137 if (sve->size <= sz)138 break;139 140 sz = sve->size;141 }142 143 return sve;144 145error:146 return NULL;147}148 149static int set_sve(pid_t pid, const struct vec_type *type,150 const struct user_sve_header *sve)151{152 struct iovec iov;153 154 iov.iov_base = (void *)sve;155 iov.iov_len = sve->size;156 return ptrace(PTRACE_SETREGSET, pid, type->regset, &iov);157}158 159/* Validate setting and getting the inherit flag */160static void ptrace_set_get_inherit(pid_t child, const struct vec_type *type)161{162 struct user_sve_header sve;163 struct user_sve_header *new_sve = NULL;164 size_t new_sve_size = 0;165 int ret;166 167 /* First set the flag */168 memset(&sve, 0, sizeof(sve));169 sve.size = sizeof(sve);170 sve.vl = sve_vl_from_vq(SVE_VQ_MIN);171 sve.flags = SVE_PT_VL_INHERIT;172 ret = set_sve(child, type, &sve);173 if (ret != 0) {174 ksft_test_result_fail("Failed to set %s SVE_PT_VL_INHERIT\n",175 type->name);176 return;177 }178 179 /*180 * Read back the new register state and verify that we have181 * set the flags we expected.182 */183 if (!get_sve(child, type, (void **)&new_sve, &new_sve_size)) {184 ksft_test_result_fail("Failed to read %s SVE flags\n",185 type->name);186 return;187 }188 189 ksft_test_result(new_sve->flags & SVE_PT_VL_INHERIT,190 "%s SVE_PT_VL_INHERIT set\n", type->name);191 192 /* Now clear */193 sve.flags &= ~SVE_PT_VL_INHERIT;194 ret = set_sve(child, type, &sve);195 if (ret != 0) {196 ksft_test_result_fail("Failed to clear %s SVE_PT_VL_INHERIT\n",197 type->name);198 return;199 }200 201 if (!get_sve(child, type, (void **)&new_sve, &new_sve_size)) {202 ksft_test_result_fail("Failed to read %s SVE flags\n",203 type->name);204 return;205 }206 207 ksft_test_result(!(new_sve->flags & SVE_PT_VL_INHERIT),208 "%s SVE_PT_VL_INHERIT cleared\n", type->name);209 210 free(new_sve);211}212 213/* Validate attempting to set the specfied VL via ptrace */214static void ptrace_set_get_vl(pid_t child, const struct vec_type *type,215 unsigned int vl, bool *supported)216{217 struct user_sve_header sve;218 struct user_sve_header *new_sve = NULL;219 size_t new_sve_size = 0;220 int ret, prctl_vl;221 222 *supported = false;223 224 /* Check if the VL is supported in this process */225 prctl_vl = prctl(type->prctl_set, vl);226 if (prctl_vl == -1)227 ksft_exit_fail_msg("prctl(PR_%s_SET_VL) failed: %s (%d)\n",228 type->name, strerror(errno), errno);229 230 /* If the VL is not supported then a supported VL will be returned */231 *supported = (prctl_vl == vl);232 233 /* Set the VL by doing a set with no register payload */234 memset(&sve, 0, sizeof(sve));235 sve.size = sizeof(sve);236 sve.vl = vl;237 ret = set_sve(child, type, &sve);238 if (ret != 0) {239 ksft_test_result_fail("Failed to set %s VL %u\n",240 type->name, vl);241 return;242 }243 244 /*245 * Read back the new register state and verify that we have the246 * same VL that we got from prctl() on ourselves.247 */248 if (!get_sve(child, type, (void **)&new_sve, &new_sve_size)) {249 ksft_test_result_fail("Failed to read %s VL %u\n",250 type->name, vl);251 return;252 }253 254 ksft_test_result(new_sve->vl = prctl_vl, "Set %s VL %u\n",255 type->name, vl);256 257 free(new_sve);258}259 260static void check_u32(unsigned int vl, const char *reg,261 uint32_t *in, uint32_t *out, int *errors)262{263 if (*in != *out) {264 printf("# VL %d %s wrote %x read %x\n",265 vl, reg, *in, *out);266 (*errors)++;267 }268}269 270/* Access the FPSIMD registers via the SVE regset */271static void ptrace_sve_fpsimd(pid_t child, const struct vec_type *type)272{273 void *svebuf;274 struct user_sve_header *sve;275 struct user_fpsimd_state *fpsimd, new_fpsimd;276 unsigned int i, j;277 unsigned char *p;278 int ret;279 280 svebuf = malloc(SVE_PT_SIZE(0, SVE_PT_REGS_FPSIMD));281 if (!svebuf) {282 ksft_test_result_fail("Failed to allocate FPSIMD buffer\n");283 return;284 }285 286 memset(svebuf, 0, SVE_PT_SIZE(0, SVE_PT_REGS_FPSIMD));287 sve = svebuf;288 sve->flags = SVE_PT_REGS_FPSIMD;289 sve->size = SVE_PT_SIZE(0, SVE_PT_REGS_FPSIMD);290 sve->vl = 16; /* We don't care what the VL is */291 292 /* Try to set a known FPSIMD state via PT_REGS_SVE */293 fpsimd = (struct user_fpsimd_state *)((char *)sve +294 SVE_PT_FPSIMD_OFFSET);295 for (i = 0; i < 32; ++i) {296 p = (unsigned char *)&fpsimd->vregs[i];297 298 for (j = 0; j < sizeof(fpsimd->vregs[i]); ++j)299 p[j] = j;300 }301 302 ret = set_sve(child, type, sve);303 ksft_test_result(ret == 0, "%s FPSIMD set via SVE: %d\n",304 type->name, ret);305 if (ret)306 goto out;307 308 /* Verify via the FPSIMD regset */309 if (get_fpsimd(child, &new_fpsimd)) {310 ksft_test_result_fail("get_fpsimd(): %s\n",311 strerror(errno));312 goto out;313 }314 if (memcmp(fpsimd, &new_fpsimd, sizeof(*fpsimd)) == 0)315 ksft_test_result_pass("%s get_fpsimd() gave same state\n",316 type->name);317 else318 ksft_test_result_fail("%s get_fpsimd() gave different state\n",319 type->name);320 321out:322 free(svebuf);323}324 325/* Validate attempting to set SVE data and read SVE data */326static void ptrace_set_sve_get_sve_data(pid_t child,327 const struct vec_type *type,328 unsigned int vl)329{330 void *write_buf;331 void *read_buf = NULL;332 struct user_sve_header *write_sve;333 struct user_sve_header *read_sve;334 size_t read_sve_size = 0;335 unsigned int vq = sve_vq_from_vl(vl);336 int ret, i;337 size_t data_size;338 int errors = 0;339 340 data_size = SVE_PT_SVE_OFFSET + SVE_PT_SVE_SIZE(vq, SVE_PT_REGS_SVE);341 write_buf = malloc(data_size);342 if (!write_buf) {343 ksft_test_result_fail("Error allocating %d byte buffer for %s VL %u\n",344 data_size, type->name, vl);345 return;346 }347 write_sve = write_buf;348 349 /* Set up some data and write it out */350 memset(write_sve, 0, data_size);351 write_sve->size = data_size;352 write_sve->vl = vl;353 write_sve->flags = SVE_PT_REGS_SVE;354 355 for (i = 0; i < __SVE_NUM_ZREGS; i++)356 fill_buf(write_buf + SVE_PT_SVE_ZREG_OFFSET(vq, i),357 SVE_PT_SVE_ZREG_SIZE(vq));358 359 for (i = 0; i < __SVE_NUM_PREGS; i++)360 fill_buf(write_buf + SVE_PT_SVE_PREG_OFFSET(vq, i),361 SVE_PT_SVE_PREG_SIZE(vq));362 363 fill_buf(write_buf + SVE_PT_SVE_FPSR_OFFSET(vq), SVE_PT_SVE_FPSR_SIZE);364 fill_buf(write_buf + SVE_PT_SVE_FPCR_OFFSET(vq), SVE_PT_SVE_FPCR_SIZE);365 366 /* TODO: Generate a valid FFR pattern */367 368 ret = set_sve(child, type, write_sve);369 if (ret != 0) {370 ksft_test_result_fail("Failed to set %s VL %u data\n",371 type->name, vl);372 goto out;373 }374 375 /* Read the data back */376 if (!get_sve(child, type, (void **)&read_buf, &read_sve_size)) {377 ksft_test_result_fail("Failed to read %s VL %u data\n",378 type->name, vl);379 goto out;380 }381 read_sve = read_buf;382 383 /* We might read more data if there's extensions we don't know */384 if (read_sve->size < write_sve->size) {385 ksft_test_result_fail("%s wrote %d bytes, only read %d\n",386 type->name, write_sve->size,387 read_sve->size);388 goto out_read;389 }390 391 for (i = 0; i < __SVE_NUM_ZREGS; i++) {392 if (memcmp(write_buf + SVE_PT_SVE_ZREG_OFFSET(vq, i),393 read_buf + SVE_PT_SVE_ZREG_OFFSET(vq, i),394 SVE_PT_SVE_ZREG_SIZE(vq)) != 0) {395 printf("# Mismatch in %u Z%d\n", vl, i);396 errors++;397 }398 }399 400 for (i = 0; i < __SVE_NUM_PREGS; i++) {401 if (memcmp(write_buf + SVE_PT_SVE_PREG_OFFSET(vq, i),402 read_buf + SVE_PT_SVE_PREG_OFFSET(vq, i),403 SVE_PT_SVE_PREG_SIZE(vq)) != 0) {404 printf("# Mismatch in %u P%d\n", vl, i);405 errors++;406 }407 }408 409 check_u32(vl, "FPSR", write_buf + SVE_PT_SVE_FPSR_OFFSET(vq),410 read_buf + SVE_PT_SVE_FPSR_OFFSET(vq), &errors);411 check_u32(vl, "FPCR", write_buf + SVE_PT_SVE_FPCR_OFFSET(vq),412 read_buf + SVE_PT_SVE_FPCR_OFFSET(vq), &errors);413 414 ksft_test_result(errors == 0, "Set and get %s data for VL %u\n",415 type->name, vl);416 417out_read:418 free(read_buf);419out:420 free(write_buf);421}422 423/* Validate attempting to set SVE data and read it via the FPSIMD regset */424static void ptrace_set_sve_get_fpsimd_data(pid_t child,425 const struct vec_type *type,426 unsigned int vl)427{428 void *write_buf;429 struct user_sve_header *write_sve;430 unsigned int vq = sve_vq_from_vl(vl);431 struct user_fpsimd_state fpsimd_state;432 int ret, i;433 size_t data_size;434 int errors = 0;435 436 if (__BYTE_ORDER == __BIG_ENDIAN) {437 ksft_test_result_skip("Big endian not supported\n");438 return;439 }440 441 data_size = SVE_PT_SVE_OFFSET + SVE_PT_SVE_SIZE(vq, SVE_PT_REGS_SVE);442 write_buf = malloc(data_size);443 if (!write_buf) {444 ksft_test_result_fail("Error allocating %d byte buffer for %s VL %u\n",445 data_size, type->name, vl);446 return;447 }448 write_sve = write_buf;449 450 /* Set up some data and write it out */451 memset(write_sve, 0, data_size);452 write_sve->size = data_size;453 write_sve->vl = vl;454 write_sve->flags = SVE_PT_REGS_SVE;455 456 for (i = 0; i < __SVE_NUM_ZREGS; i++)457 fill_buf(write_buf + SVE_PT_SVE_ZREG_OFFSET(vq, i),458 SVE_PT_SVE_ZREG_SIZE(vq));459 460 fill_buf(write_buf + SVE_PT_SVE_FPSR_OFFSET(vq), SVE_PT_SVE_FPSR_SIZE);461 fill_buf(write_buf + SVE_PT_SVE_FPCR_OFFSET(vq), SVE_PT_SVE_FPCR_SIZE);462 463 ret = set_sve(child, type, write_sve);464 if (ret != 0) {465 ksft_test_result_fail("Failed to set %s VL %u data\n",466 type->name, vl);467 goto out;468 }469 470 /* Read the data back */471 if (get_fpsimd(child, &fpsimd_state)) {472 ksft_test_result_fail("Failed to read %s VL %u FPSIMD data\n",473 type->name, vl);474 goto out;475 }476 477 for (i = 0; i < __SVE_NUM_ZREGS; i++) {478 __uint128_t tmp = 0;479 480 /*481 * Z regs are stored endianness invariant, this won't482 * work for big endian483 */484 memcpy(&tmp, write_buf + SVE_PT_SVE_ZREG_OFFSET(vq, i),485 sizeof(tmp));486 487 if (tmp != fpsimd_state.vregs[i]) {488 printf("# Mismatch in FPSIMD for %s VL %u Z%d\n",489 type->name, vl, i);490 errors++;491 }492 }493 494 check_u32(vl, "FPSR", write_buf + SVE_PT_SVE_FPSR_OFFSET(vq),495 &fpsimd_state.fpsr, &errors);496 check_u32(vl, "FPCR", write_buf + SVE_PT_SVE_FPCR_OFFSET(vq),497 &fpsimd_state.fpcr, &errors);498 499 ksft_test_result(errors == 0, "Set and get FPSIMD data for %s VL %u\n",500 type->name, vl);501 502out:503 free(write_buf);504}505 506/* Validate attempting to set FPSIMD data and read it via the SVE regset */507static void ptrace_set_fpsimd_get_sve_data(pid_t child,508 const struct vec_type *type,509 unsigned int vl)510{511 void *read_buf = NULL;512 unsigned char *p;513 struct user_sve_header *read_sve;514 unsigned int vq = sve_vq_from_vl(vl);515 struct user_fpsimd_state write_fpsimd;516 int ret, i, j;517 size_t read_sve_size = 0;518 size_t expected_size;519 int errors = 0;520 521 if (__BYTE_ORDER == __BIG_ENDIAN) {522 ksft_test_result_skip("Big endian not supported\n");523 return;524 }525 526 for (i = 0; i < 32; ++i) {527 p = (unsigned char *)&write_fpsimd.vregs[i];528 529 for (j = 0; j < sizeof(write_fpsimd.vregs[i]); ++j)530 p[j] = j;531 }532 533 ret = set_fpsimd(child, &write_fpsimd);534 if (ret != 0) {535 ksft_test_result_fail("Failed to set FPSIMD state: %d\n)",536 ret);537 return;538 }539 540 if (!get_sve(child, type, (void **)&read_buf, &read_sve_size)) {541 ksft_test_result_fail("Failed to read %s VL %u data\n",542 type->name, vl);543 return;544 }545 read_sve = read_buf;546 547 if (read_sve->vl != vl) {548 ksft_test_result_fail("Child VL != expected VL %d\n",549 read_sve->vl, vl);550 goto out;551 }552 553 /* The kernel may return either SVE or FPSIMD format */554 switch (read_sve->flags & SVE_PT_REGS_MASK) {555 case SVE_PT_REGS_FPSIMD:556 expected_size = SVE_PT_FPSIMD_SIZE(vq, SVE_PT_REGS_FPSIMD);557 if (read_sve_size < expected_size) {558 ksft_test_result_fail("Read %d bytes, expected %d\n",559 read_sve_size, expected_size);560 goto out;561 }562 563 ret = memcmp(&write_fpsimd, read_buf + SVE_PT_FPSIMD_OFFSET,564 sizeof(write_fpsimd));565 if (ret != 0) {566 ksft_print_msg("Read FPSIMD data mismatch\n");567 errors++;568 }569 break;570 571 case SVE_PT_REGS_SVE:572 expected_size = SVE_PT_SVE_SIZE(vq, SVE_PT_REGS_SVE);573 if (read_sve_size < expected_size) {574 ksft_test_result_fail("Read %d bytes, expected %d\n",575 read_sve_size, expected_size);576 goto out;577 }578 579 for (i = 0; i < __SVE_NUM_ZREGS; i++) {580 __uint128_t tmp = 0;581 582 /*583 * Z regs are stored endianness invariant, this won't584 * work for big endian585 */586 memcpy(&tmp, read_buf + SVE_PT_SVE_ZREG_OFFSET(vq, i),587 sizeof(tmp));588 589 if (tmp != write_fpsimd.vregs[i]) {590 ksft_print_msg("Mismatch in FPSIMD for %s VL %u Z%d/V%d\n",591 type->name, vl, i, i);592 errors++;593 }594 }595 596 check_u32(vl, "FPSR", &write_fpsimd.fpsr,597 read_buf + SVE_PT_SVE_FPSR_OFFSET(vq), &errors);598 check_u32(vl, "FPCR", &write_fpsimd.fpcr,599 read_buf + SVE_PT_SVE_FPCR_OFFSET(vq), &errors);600 break;601 default:602 ksft_print_msg("Unexpected regs type %d\n",603 read_sve->flags & SVE_PT_REGS_MASK);604 errors++;605 break;606 }607 608 ksft_test_result(errors == 0, "Set FPSIMD, read via SVE for %s VL %u\n",609 type->name, vl);610 611out:612 free(read_buf);613}614 615static int do_parent(pid_t child)616{617 int ret = EXIT_FAILURE;618 pid_t pid;619 int status, i;620 siginfo_t si;621 unsigned int vq, vl;622 bool vl_supported;623 624 ksft_print_msg("Parent is %d, child is %d\n", getpid(), child);625 626 /* Attach to the child */627 while (1) {628 int sig;629 630 pid = wait(&status);631 if (pid == -1) {632 perror("wait");633 goto error;634 }635 636 /*637 * This should never happen but it's hard to flag in638 * the framework.639 */640 if (pid != child)641 continue;642 643 if (WIFEXITED(status) || WIFSIGNALED(status))644 ksft_exit_fail_msg("Child died unexpectedly\n");645 646 if (!WIFSTOPPED(status))647 goto error;648 649 sig = WSTOPSIG(status);650 651 if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &si)) {652 if (errno == ESRCH)653 goto disappeared;654 655 if (errno == EINVAL) {656 sig = 0; /* bust group-stop */657 goto cont;658 }659 660 ksft_test_result_fail("PTRACE_GETSIGINFO: %s\n",661 strerror(errno));662 goto error;663 }664 665 if (sig == SIGSTOP && si.si_code == SI_TKILL &&666 si.si_pid == pid)667 break;668 669 cont:670 if (ptrace(PTRACE_CONT, pid, NULL, sig)) {671 if (errno == ESRCH)672 goto disappeared;673 674 ksft_test_result_fail("PTRACE_CONT: %s\n",675 strerror(errno));676 goto error;677 }678 }679 680 for (i = 0; i < ARRAY_SIZE(vec_types); i++) {681 /* FPSIMD via SVE regset */682 if (getauxval(vec_types[i].hwcap_type) & vec_types[i].hwcap) {683 ptrace_sve_fpsimd(child, &vec_types[i]);684 } else {685 ksft_test_result_skip("%s FPSIMD set via SVE\n",686 vec_types[i].name);687 ksft_test_result_skip("%s FPSIMD read\n",688 vec_types[i].name);689 }690 691 /* prctl() flags */692 if (getauxval(vec_types[i].hwcap_type) & vec_types[i].hwcap) {693 ptrace_set_get_inherit(child, &vec_types[i]);694 } else {695 ksft_test_result_skip("%s SVE_PT_VL_INHERIT set\n",696 vec_types[i].name);697 ksft_test_result_skip("%s SVE_PT_VL_INHERIT cleared\n",698 vec_types[i].name);699 }700 701 /* Step through every possible VQ */702 for (vq = SVE_VQ_MIN; vq <= TEST_VQ_MAX; vq++) {703 vl = sve_vl_from_vq(vq);704 705 /* First, try to set this vector length */706 if (getauxval(vec_types[i].hwcap_type) &707 vec_types[i].hwcap) {708 ptrace_set_get_vl(child, &vec_types[i], vl,709 &vl_supported);710 } else {711 ksft_test_result_skip("%s get/set VL %d\n",712 vec_types[i].name, vl);713 vl_supported = false;714 }715 716 /* If the VL is supported validate data set/get */717 if (vl_supported) {718 ptrace_set_sve_get_sve_data(child, &vec_types[i], vl);719 ptrace_set_sve_get_fpsimd_data(child, &vec_types[i], vl);720 ptrace_set_fpsimd_get_sve_data(child, &vec_types[i], vl);721 } else {722 ksft_test_result_skip("%s set SVE get SVE for VL %d\n",723 vec_types[i].name, vl);724 ksft_test_result_skip("%s set SVE get FPSIMD for VL %d\n",725 vec_types[i].name, vl);726 ksft_test_result_skip("%s set FPSIMD get SVE for VL %d\n",727 vec_types[i].name, vl);728 }729 }730 }731 732 ret = EXIT_SUCCESS;733 734error:735 kill(child, SIGKILL);736 737disappeared:738 return ret;739}740 741int main(void)742{743 int ret = EXIT_SUCCESS;744 pid_t child;745 746 srandom(getpid());747 748 ksft_print_header();749 ksft_set_plan(EXPECTED_TESTS);750 751 if (!(getauxval(AT_HWCAP) & HWCAP_SVE))752 ksft_exit_skip("SVE not available\n");753 754 child = fork();755 if (!child)756 return do_child();757 758 if (do_parent(child))759 ret = EXIT_FAILURE;760 761 ksft_print_cnts();762 763 return ret;764}765