123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * Copyright (C) 2021 ARM Limited.
- * Original author: Mark Brown <[email protected]>
- */
- #include <assert.h>
- #include <errno.h>
- #include <fcntl.h>
- #include <stddef.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <unistd.h>
- #include <sys/auxv.h>
- #include <sys/prctl.h>
- #include <sys/types.h>
- #include <sys/wait.h>
- #include <asm/sigcontext.h>
- #include <asm/hwcap.h>
- #include "../../kselftest.h"
- #include "rdvl.h"
- #define ARCH_MIN_VL SVE_VL_MIN
- struct vec_data {
- const char *name;
- unsigned long hwcap_type;
- unsigned long hwcap;
- const char *rdvl_binary;
- int (*rdvl)(void);
- int prctl_get;
- int prctl_set;
- const char *default_vl_file;
- int default_vl;
- int min_vl;
- int max_vl;
- };
- static struct vec_data vec_data[] = {
- {
- .name = "SVE",
- .hwcap_type = AT_HWCAP,
- .hwcap = HWCAP_SVE,
- .rdvl = rdvl_sve,
- .rdvl_binary = "./rdvl-sve",
- .prctl_get = PR_SVE_GET_VL,
- .prctl_set = PR_SVE_SET_VL,
- .default_vl_file = "/proc/sys/abi/sve_default_vector_length",
- },
- {
- .name = "SME",
- .hwcap_type = AT_HWCAP2,
- .hwcap = HWCAP2_SME,
- .rdvl = rdvl_sme,
- .rdvl_binary = "./rdvl-sme",
- .prctl_get = PR_SME_GET_VL,
- .prctl_set = PR_SME_SET_VL,
- .default_vl_file = "/proc/sys/abi/sme_default_vector_length",
- },
- };
- static int stdio_read_integer(FILE *f, const char *what, int *val)
- {
- int n = 0;
- int ret;
- ret = fscanf(f, "%d%*1[\n]%n", val, &n);
- if (ret < 1 || n < 1) {
- ksft_print_msg("failed to parse integer from %s\n", what);
- return -1;
- }
- return 0;
- }
- /* Start a new process and return the vector length it sees */
- static int get_child_rdvl(struct vec_data *data)
- {
- FILE *out;
- int pipefd[2];
- pid_t pid, child;
- int read_vl, ret;
- ret = pipe(pipefd);
- if (ret == -1) {
- ksft_print_msg("pipe() failed: %d (%s)\n",
- errno, strerror(errno));
- return -1;
- }
- fflush(stdout);
- child = fork();
- if (child == -1) {
- ksft_print_msg("fork() failed: %d (%s)\n",
- errno, strerror(errno));
- close(pipefd[0]);
- close(pipefd[1]);
- return -1;
- }
- /* Child: put vector length on the pipe */
- if (child == 0) {
- /*
- * Replace stdout with the pipe, errors to stderr from
- * here as kselftest prints to stdout.
- */
- ret = dup2(pipefd[1], 1);
- if (ret == -1) {
- fprintf(stderr, "dup2() %d\n", errno);
- exit(EXIT_FAILURE);
- }
- /* exec() a new binary which puts the VL on stdout */
- ret = execl(data->rdvl_binary, data->rdvl_binary, NULL);
- fprintf(stderr, "execl(%s) failed: %d (%s)\n",
- data->rdvl_binary, errno, strerror(errno));
- exit(EXIT_FAILURE);
- }
- close(pipefd[1]);
- /* Parent; wait for the exit status from the child & verify it */
- do {
- pid = wait(&ret);
- if (pid == -1) {
- ksft_print_msg("wait() failed: %d (%s)\n",
- errno, strerror(errno));
- close(pipefd[0]);
- return -1;
- }
- } while (pid != child);
- assert(pid == child);
- if (!WIFEXITED(ret)) {
- ksft_print_msg("child exited abnormally\n");
- close(pipefd[0]);
- return -1;
- }
- if (WEXITSTATUS(ret) != 0) {
- ksft_print_msg("child returned error %d\n",
- WEXITSTATUS(ret));
- close(pipefd[0]);
- return -1;
- }
- out = fdopen(pipefd[0], "r");
- if (!out) {
- ksft_print_msg("failed to open child stdout\n");
- close(pipefd[0]);
- return -1;
- }
- ret = stdio_read_integer(out, "child", &read_vl);
- fclose(out);
- if (ret != 0)
- return ret;
- return read_vl;
- }
- static int file_read_integer(const char *name, int *val)
- {
- FILE *f;
- int ret;
- f = fopen(name, "r");
- if (!f) {
- ksft_test_result_fail("Unable to open %s: %d (%s)\n",
- name, errno,
- strerror(errno));
- return -1;
- }
- ret = stdio_read_integer(f, name, val);
- fclose(f);
- return ret;
- }
- static int file_write_integer(const char *name, int val)
- {
- FILE *f;
- f = fopen(name, "w");
- if (!f) {
- ksft_test_result_fail("Unable to open %s: %d (%s)\n",
- name, errno,
- strerror(errno));
- return -1;
- }
- fprintf(f, "%d", val);
- fclose(f);
- return 0;
- }
- /*
- * Verify that we can read the default VL via proc, checking that it
- * is set in a freshly spawned child.
- */
- static void proc_read_default(struct vec_data *data)
- {
- int default_vl, child_vl, ret;
- ret = file_read_integer(data->default_vl_file, &default_vl);
- if (ret != 0)
- return;
- /* Is this the actual default seen by new processes? */
- child_vl = get_child_rdvl(data);
- if (child_vl != default_vl) {
- ksft_test_result_fail("%s is %d but child VL is %d\n",
- data->default_vl_file,
- default_vl, child_vl);
- return;
- }
- ksft_test_result_pass("%s default vector length %d\n", data->name,
- default_vl);
- data->default_vl = default_vl;
- }
- /* Verify that we can write a minimum value and have it take effect */
- static void proc_write_min(struct vec_data *data)
- {
- int ret, new_default, child_vl;
- if (geteuid() != 0) {
- ksft_test_result_skip("Need to be root to write to /proc\n");
- return;
- }
- ret = file_write_integer(data->default_vl_file, ARCH_MIN_VL);
- if (ret != 0)
- return;
- /* What was the new value? */
- ret = file_read_integer(data->default_vl_file, &new_default);
- if (ret != 0)
- return;
- /* Did it take effect in a new process? */
- child_vl = get_child_rdvl(data);
- if (child_vl != new_default) {
- ksft_test_result_fail("%s is %d but child VL is %d\n",
- data->default_vl_file,
- new_default, child_vl);
- return;
- }
- ksft_test_result_pass("%s minimum vector length %d\n", data->name,
- new_default);
- data->min_vl = new_default;
- file_write_integer(data->default_vl_file, data->default_vl);
- }
- /* Verify that we can write a maximum value and have it take effect */
- static void proc_write_max(struct vec_data *data)
- {
- int ret, new_default, child_vl;
- if (geteuid() != 0) {
- ksft_test_result_skip("Need to be root to write to /proc\n");
- return;
- }
- /* -1 is accepted by the /proc interface as the maximum VL */
- ret = file_write_integer(data->default_vl_file, -1);
- if (ret != 0)
- return;
- /* What was the new value? */
- ret = file_read_integer(data->default_vl_file, &new_default);
- if (ret != 0)
- return;
- /* Did it take effect in a new process? */
- child_vl = get_child_rdvl(data);
- if (child_vl != new_default) {
- ksft_test_result_fail("%s is %d but child VL is %d\n",
- data->default_vl_file,
- new_default, child_vl);
- return;
- }
- ksft_test_result_pass("%s maximum vector length %d\n", data->name,
- new_default);
- data->max_vl = new_default;
- file_write_integer(data->default_vl_file, data->default_vl);
- }
- /* Can we read back a VL from prctl? */
- static void prctl_get(struct vec_data *data)
- {
- int ret;
- ret = prctl(data->prctl_get);
- if (ret == -1) {
- ksft_test_result_fail("%s prctl() read failed: %d (%s)\n",
- data->name, errno, strerror(errno));
- return;
- }
- /* Mask out any flags */
- ret &= PR_SVE_VL_LEN_MASK;
- /* Is that what we can read back directly? */
- if (ret == data->rdvl())
- ksft_test_result_pass("%s current VL is %d\n",
- data->name, ret);
- else
- ksft_test_result_fail("%s prctl() VL %d but RDVL is %d\n",
- data->name, ret, data->rdvl());
- }
- /* Does the prctl let us set the VL we already have? */
- static void prctl_set_same(struct vec_data *data)
- {
- int cur_vl = data->rdvl();
- int ret;
- ret = prctl(data->prctl_set, cur_vl);
- if (ret < 0) {
- ksft_test_result_fail("%s prctl set failed: %d (%s)\n",
- data->name, errno, strerror(errno));
- return;
- }
- ksft_test_result(cur_vl == data->rdvl(),
- "%s set VL %d and have VL %d\n",
- data->name, cur_vl, data->rdvl());
- }
- /* Can we set a new VL for this process? */
- static void prctl_set(struct vec_data *data)
- {
- int ret;
- if (data->min_vl == data->max_vl) {
- ksft_test_result_skip("%s only one VL supported\n",
- data->name);
- return;
- }
- /* Try to set the minimum VL */
- ret = prctl(data->prctl_set, data->min_vl);
- if (ret < 0) {
- ksft_test_result_fail("%s prctl set failed for %d: %d (%s)\n",
- data->name, data->min_vl,
- errno, strerror(errno));
- return;
- }
- if ((ret & PR_SVE_VL_LEN_MASK) != data->min_vl) {
- ksft_test_result_fail("%s prctl set %d but return value is %d\n",
- data->name, data->min_vl, data->rdvl());
- return;
- }
- if (data->rdvl() != data->min_vl) {
- ksft_test_result_fail("%s set %d but RDVL is %d\n",
- data->name, data->min_vl, data->rdvl());
- return;
- }
- /* Try to set the maximum VL */
- ret = prctl(data->prctl_set, data->max_vl);
- if (ret < 0) {
- ksft_test_result_fail("%s prctl set failed for %d: %d (%s)\n",
- data->name, data->max_vl,
- errno, strerror(errno));
- return;
- }
- if ((ret & PR_SVE_VL_LEN_MASK) != data->max_vl) {
- ksft_test_result_fail("%s prctl() set %d but return value is %d\n",
- data->name, data->max_vl, data->rdvl());
- return;
- }
- /* The _INHERIT flag should not be present when we read the VL */
- ret = prctl(data->prctl_get);
- if (ret == -1) {
- ksft_test_result_fail("%s prctl() read failed: %d (%s)\n",
- data->name, errno, strerror(errno));
- return;
- }
- if (ret & PR_SVE_VL_INHERIT) {
- ksft_test_result_fail("%s prctl() reports _INHERIT\n",
- data->name);
- return;
- }
- ksft_test_result_pass("%s prctl() set min/max\n", data->name);
- }
- /* If we didn't request it a new VL shouldn't affect the child */
- static void prctl_set_no_child(struct vec_data *data)
- {
- int ret, child_vl;
- if (data->min_vl == data->max_vl) {
- ksft_test_result_skip("%s only one VL supported\n",
- data->name);
- return;
- }
- ret = prctl(data->prctl_set, data->min_vl);
- if (ret < 0) {
- ksft_test_result_fail("%s prctl set failed for %d: %d (%s)\n",
- data->name, data->min_vl,
- errno, strerror(errno));
- return;
- }
- /* Ensure the default VL is different */
- ret = file_write_integer(data->default_vl_file, data->max_vl);
- if (ret != 0)
- return;
- /* Check that the child has the default we just set */
- child_vl = get_child_rdvl(data);
- if (child_vl != data->max_vl) {
- ksft_test_result_fail("%s is %d but child VL is %d\n",
- data->default_vl_file,
- data->max_vl, child_vl);
- return;
- }
- ksft_test_result_pass("%s vector length used default\n", data->name);
- file_write_integer(data->default_vl_file, data->default_vl);
- }
- /* If we didn't request it a new VL shouldn't affect the child */
- static void prctl_set_for_child(struct vec_data *data)
- {
- int ret, child_vl;
- if (data->min_vl == data->max_vl) {
- ksft_test_result_skip("%s only one VL supported\n",
- data->name);
- return;
- }
- ret = prctl(data->prctl_set, data->min_vl | PR_SVE_VL_INHERIT);
- if (ret < 0) {
- ksft_test_result_fail("%s prctl set failed for %d: %d (%s)\n",
- data->name, data->min_vl,
- errno, strerror(errno));
- return;
- }
- /* The _INHERIT flag should be present when we read the VL */
- ret = prctl(data->prctl_get);
- if (ret == -1) {
- ksft_test_result_fail("%s prctl() read failed: %d (%s)\n",
- data->name, errno, strerror(errno));
- return;
- }
- if (!(ret & PR_SVE_VL_INHERIT)) {
- ksft_test_result_fail("%s prctl() does not report _INHERIT\n",
- data->name);
- return;
- }
- /* Ensure the default VL is different */
- ret = file_write_integer(data->default_vl_file, data->max_vl);
- if (ret != 0)
- return;
- /* Check that the child inherited our VL */
- child_vl = get_child_rdvl(data);
- if (child_vl != data->min_vl) {
- ksft_test_result_fail("%s is %d but child VL is %d\n",
- data->default_vl_file,
- data->min_vl, child_vl);
- return;
- }
- ksft_test_result_pass("%s vector length was inherited\n", data->name);
- file_write_integer(data->default_vl_file, data->default_vl);
- }
- /* _ONEXEC takes effect only in the child process */
- static void prctl_set_onexec(struct vec_data *data)
- {
- int ret, child_vl;
- if (data->min_vl == data->max_vl) {
- ksft_test_result_skip("%s only one VL supported\n",
- data->name);
- return;
- }
- /* Set a known value for the default and our current VL */
- ret = file_write_integer(data->default_vl_file, data->max_vl);
- if (ret != 0)
- return;
- ret = prctl(data->prctl_set, data->max_vl);
- if (ret < 0) {
- ksft_test_result_fail("%s prctl set failed for %d: %d (%s)\n",
- data->name, data->min_vl,
- errno, strerror(errno));
- return;
- }
- /* Set a different value for the child to have on exec */
- ret = prctl(data->prctl_set, data->min_vl | PR_SVE_SET_VL_ONEXEC);
- if (ret < 0) {
- ksft_test_result_fail("%s prctl set failed for %d: %d (%s)\n",
- data->name, data->min_vl,
- errno, strerror(errno));
- return;
- }
- /* Our current VL should stay the same */
- if (data->rdvl() != data->max_vl) {
- ksft_test_result_fail("%s VL changed by _ONEXEC prctl()\n",
- data->name);
- return;
- }
- /* Check that the child inherited our VL */
- child_vl = get_child_rdvl(data);
- if (child_vl != data->min_vl) {
- ksft_test_result_fail("Set %d _ONEXEC but child VL is %d\n",
- data->min_vl, child_vl);
- return;
- }
- ksft_test_result_pass("%s vector length set on exec\n", data->name);
- file_write_integer(data->default_vl_file, data->default_vl);
- }
- /* For each VQ verify that setting via prctl() does the right thing */
- static void prctl_set_all_vqs(struct vec_data *data)
- {
- int ret, vq, vl, new_vl;
- int errors = 0;
- if (!data->min_vl || !data->max_vl) {
- ksft_test_result_skip("%s Failed to enumerate VLs, not testing VL setting\n",
- data->name);
- return;
- }
- for (vq = SVE_VQ_MIN; vq <= SVE_VQ_MAX; vq++) {
- vl = sve_vl_from_vq(vq);
- /* Attempt to set the VL */
- ret = prctl(data->prctl_set, vl);
- if (ret < 0) {
- errors++;
- ksft_print_msg("%s prctl set failed for %d: %d (%s)\n",
- data->name, vl,
- errno, strerror(errno));
- continue;
- }
- new_vl = ret & PR_SVE_VL_LEN_MASK;
- /* Check that we actually have the reported new VL */
- if (data->rdvl() != new_vl) {
- ksft_print_msg("Set %s VL %d but RDVL reports %d\n",
- data->name, new_vl, data->rdvl());
- errors++;
- }
- /* Was that the VL we asked for? */
- if (new_vl == vl)
- continue;
- /* Should round up to the minimum VL if below it */
- if (vl < data->min_vl) {
- if (new_vl != data->min_vl) {
- ksft_print_msg("%s VL %d returned %d not minimum %d\n",
- data->name, vl, new_vl,
- data->min_vl);
- errors++;
- }
- continue;
- }
- /* Should round down to maximum VL if above it */
- if (vl > data->max_vl) {
- if (new_vl != data->max_vl) {
- ksft_print_msg("%s VL %d returned %d not maximum %d\n",
- data->name, vl, new_vl,
- data->max_vl);
- errors++;
- }
- continue;
- }
- /* Otherwise we should've rounded down */
- if (!(new_vl < vl)) {
- ksft_print_msg("%s VL %d returned %d, did not round down\n",
- data->name, vl, new_vl);
- errors++;
- continue;
- }
- }
- ksft_test_result(errors == 0, "%s prctl() set all VLs, %d errors\n",
- data->name, errors);
- }
- typedef void (*test_type)(struct vec_data *);
- static const test_type tests[] = {
- /*
- * The default/min/max tests must be first and in this order
- * to provide data for other tests.
- */
- proc_read_default,
- proc_write_min,
- proc_write_max,
- prctl_get,
- prctl_set_same,
- prctl_set,
- prctl_set_no_child,
- prctl_set_for_child,
- prctl_set_onexec,
- prctl_set_all_vqs,
- };
- int main(void)
- {
- int i, j;
- ksft_print_header();
- ksft_set_plan(ARRAY_SIZE(tests) * ARRAY_SIZE(vec_data));
- for (i = 0; i < ARRAY_SIZE(vec_data); i++) {
- struct vec_data *data = &vec_data[i];
- unsigned long supported;
- supported = getauxval(data->hwcap_type) & data->hwcap;
- for (j = 0; j < ARRAY_SIZE(tests); j++) {
- if (supported)
- tests[j](data);
- else
- ksft_test_result_skip("%s not supported\n",
- data->name);
- }
- }
- ksft_exit_pass();
- }
|