162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2021 ARM Limited. 462306a36Sopenharmony_ci * Original author: Mark Brown <broonie@kernel.org> 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci#include <assert.h> 762306a36Sopenharmony_ci#include <errno.h> 862306a36Sopenharmony_ci#include <fcntl.h> 962306a36Sopenharmony_ci#include <stdbool.h> 1062306a36Sopenharmony_ci#include <stddef.h> 1162306a36Sopenharmony_ci#include <stdio.h> 1262306a36Sopenharmony_ci#include <stdlib.h> 1362306a36Sopenharmony_ci#include <string.h> 1462306a36Sopenharmony_ci#include <unistd.h> 1562306a36Sopenharmony_ci#include <sys/auxv.h> 1662306a36Sopenharmony_ci#include <sys/prctl.h> 1762306a36Sopenharmony_ci#include <sys/types.h> 1862306a36Sopenharmony_ci#include <sys/wait.h> 1962306a36Sopenharmony_ci#include <asm/sigcontext.h> 2062306a36Sopenharmony_ci#include <asm/hwcap.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include "../../kselftest.h" 2362306a36Sopenharmony_ci#include "rdvl.h" 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#define ARCH_MIN_VL SVE_VL_MIN 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistruct vec_data { 2862306a36Sopenharmony_ci const char *name; 2962306a36Sopenharmony_ci unsigned long hwcap_type; 3062306a36Sopenharmony_ci unsigned long hwcap; 3162306a36Sopenharmony_ci const char *rdvl_binary; 3262306a36Sopenharmony_ci int (*rdvl)(void); 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci int prctl_get; 3562306a36Sopenharmony_ci int prctl_set; 3662306a36Sopenharmony_ci const char *default_vl_file; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci int default_vl; 3962306a36Sopenharmony_ci int min_vl; 4062306a36Sopenharmony_ci int max_vl; 4162306a36Sopenharmony_ci}; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci#define VEC_SVE 0 4462306a36Sopenharmony_ci#define VEC_SME 1 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic struct vec_data vec_data[] = { 4762306a36Sopenharmony_ci [VEC_SVE] = { 4862306a36Sopenharmony_ci .name = "SVE", 4962306a36Sopenharmony_ci .hwcap_type = AT_HWCAP, 5062306a36Sopenharmony_ci .hwcap = HWCAP_SVE, 5162306a36Sopenharmony_ci .rdvl = rdvl_sve, 5262306a36Sopenharmony_ci .rdvl_binary = "./rdvl-sve", 5362306a36Sopenharmony_ci .prctl_get = PR_SVE_GET_VL, 5462306a36Sopenharmony_ci .prctl_set = PR_SVE_SET_VL, 5562306a36Sopenharmony_ci .default_vl_file = "/proc/sys/abi/sve_default_vector_length", 5662306a36Sopenharmony_ci }, 5762306a36Sopenharmony_ci [VEC_SME] = { 5862306a36Sopenharmony_ci .name = "SME", 5962306a36Sopenharmony_ci .hwcap_type = AT_HWCAP2, 6062306a36Sopenharmony_ci .hwcap = HWCAP2_SME, 6162306a36Sopenharmony_ci .rdvl = rdvl_sme, 6262306a36Sopenharmony_ci .rdvl_binary = "./rdvl-sme", 6362306a36Sopenharmony_ci .prctl_get = PR_SME_GET_VL, 6462306a36Sopenharmony_ci .prctl_set = PR_SME_SET_VL, 6562306a36Sopenharmony_ci .default_vl_file = "/proc/sys/abi/sme_default_vector_length", 6662306a36Sopenharmony_ci }, 6762306a36Sopenharmony_ci}; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic int stdio_read_integer(FILE *f, const char *what, int *val) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci int n = 0; 7262306a36Sopenharmony_ci int ret; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci ret = fscanf(f, "%d%*1[\n]%n", val, &n); 7562306a36Sopenharmony_ci if (ret < 1 || n < 1) { 7662306a36Sopenharmony_ci ksft_print_msg("failed to parse integer from %s\n", what); 7762306a36Sopenharmony_ci return -1; 7862306a36Sopenharmony_ci } 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci return 0; 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci/* Start a new process and return the vector length it sees */ 8462306a36Sopenharmony_cistatic int get_child_rdvl(struct vec_data *data) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci FILE *out; 8762306a36Sopenharmony_ci int pipefd[2]; 8862306a36Sopenharmony_ci pid_t pid, child; 8962306a36Sopenharmony_ci int read_vl, ret; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci ret = pipe(pipefd); 9262306a36Sopenharmony_ci if (ret == -1) { 9362306a36Sopenharmony_ci ksft_print_msg("pipe() failed: %d (%s)\n", 9462306a36Sopenharmony_ci errno, strerror(errno)); 9562306a36Sopenharmony_ci return -1; 9662306a36Sopenharmony_ci } 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci fflush(stdout); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci child = fork(); 10162306a36Sopenharmony_ci if (child == -1) { 10262306a36Sopenharmony_ci ksft_print_msg("fork() failed: %d (%s)\n", 10362306a36Sopenharmony_ci errno, strerror(errno)); 10462306a36Sopenharmony_ci close(pipefd[0]); 10562306a36Sopenharmony_ci close(pipefd[1]); 10662306a36Sopenharmony_ci return -1; 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci /* Child: put vector length on the pipe */ 11062306a36Sopenharmony_ci if (child == 0) { 11162306a36Sopenharmony_ci /* 11262306a36Sopenharmony_ci * Replace stdout with the pipe, errors to stderr from 11362306a36Sopenharmony_ci * here as kselftest prints to stdout. 11462306a36Sopenharmony_ci */ 11562306a36Sopenharmony_ci ret = dup2(pipefd[1], 1); 11662306a36Sopenharmony_ci if (ret == -1) { 11762306a36Sopenharmony_ci fprintf(stderr, "dup2() %d\n", errno); 11862306a36Sopenharmony_ci exit(EXIT_FAILURE); 11962306a36Sopenharmony_ci } 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci /* exec() a new binary which puts the VL on stdout */ 12262306a36Sopenharmony_ci ret = execl(data->rdvl_binary, data->rdvl_binary, NULL); 12362306a36Sopenharmony_ci fprintf(stderr, "execl(%s) failed: %d (%s)\n", 12462306a36Sopenharmony_ci data->rdvl_binary, errno, strerror(errno)); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci exit(EXIT_FAILURE); 12762306a36Sopenharmony_ci } 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci close(pipefd[1]); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci /* Parent; wait for the exit status from the child & verify it */ 13262306a36Sopenharmony_ci do { 13362306a36Sopenharmony_ci pid = wait(&ret); 13462306a36Sopenharmony_ci if (pid == -1) { 13562306a36Sopenharmony_ci ksft_print_msg("wait() failed: %d (%s)\n", 13662306a36Sopenharmony_ci errno, strerror(errno)); 13762306a36Sopenharmony_ci close(pipefd[0]); 13862306a36Sopenharmony_ci return -1; 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci } while (pid != child); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci assert(pid == child); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci if (!WIFEXITED(ret)) { 14562306a36Sopenharmony_ci ksft_print_msg("child exited abnormally\n"); 14662306a36Sopenharmony_ci close(pipefd[0]); 14762306a36Sopenharmony_ci return -1; 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci if (WEXITSTATUS(ret) != 0) { 15162306a36Sopenharmony_ci ksft_print_msg("child returned error %d\n", 15262306a36Sopenharmony_ci WEXITSTATUS(ret)); 15362306a36Sopenharmony_ci close(pipefd[0]); 15462306a36Sopenharmony_ci return -1; 15562306a36Sopenharmony_ci } 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci out = fdopen(pipefd[0], "r"); 15862306a36Sopenharmony_ci if (!out) { 15962306a36Sopenharmony_ci ksft_print_msg("failed to open child stdout\n"); 16062306a36Sopenharmony_ci close(pipefd[0]); 16162306a36Sopenharmony_ci return -1; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci ret = stdio_read_integer(out, "child", &read_vl); 16562306a36Sopenharmony_ci fclose(out); 16662306a36Sopenharmony_ci if (ret != 0) 16762306a36Sopenharmony_ci return ret; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci return read_vl; 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic int file_read_integer(const char *name, int *val) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci FILE *f; 17562306a36Sopenharmony_ci int ret; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci f = fopen(name, "r"); 17862306a36Sopenharmony_ci if (!f) { 17962306a36Sopenharmony_ci ksft_test_result_fail("Unable to open %s: %d (%s)\n", 18062306a36Sopenharmony_ci name, errno, 18162306a36Sopenharmony_ci strerror(errno)); 18262306a36Sopenharmony_ci return -1; 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci ret = stdio_read_integer(f, name, val); 18662306a36Sopenharmony_ci fclose(f); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci return ret; 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cistatic int file_write_integer(const char *name, int val) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci FILE *f; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci f = fopen(name, "w"); 19662306a36Sopenharmony_ci if (!f) { 19762306a36Sopenharmony_ci ksft_test_result_fail("Unable to open %s: %d (%s)\n", 19862306a36Sopenharmony_ci name, errno, 19962306a36Sopenharmony_ci strerror(errno)); 20062306a36Sopenharmony_ci return -1; 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci fprintf(f, "%d", val); 20462306a36Sopenharmony_ci fclose(f); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci return 0; 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci/* 21062306a36Sopenharmony_ci * Verify that we can read the default VL via proc, checking that it 21162306a36Sopenharmony_ci * is set in a freshly spawned child. 21262306a36Sopenharmony_ci */ 21362306a36Sopenharmony_cistatic void proc_read_default(struct vec_data *data) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci int default_vl, child_vl, ret; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci ret = file_read_integer(data->default_vl_file, &default_vl); 21862306a36Sopenharmony_ci if (ret != 0) 21962306a36Sopenharmony_ci return; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci /* Is this the actual default seen by new processes? */ 22262306a36Sopenharmony_ci child_vl = get_child_rdvl(data); 22362306a36Sopenharmony_ci if (child_vl != default_vl) { 22462306a36Sopenharmony_ci ksft_test_result_fail("%s is %d but child VL is %d\n", 22562306a36Sopenharmony_ci data->default_vl_file, 22662306a36Sopenharmony_ci default_vl, child_vl); 22762306a36Sopenharmony_ci return; 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci ksft_test_result_pass("%s default vector length %d\n", data->name, 23162306a36Sopenharmony_ci default_vl); 23262306a36Sopenharmony_ci data->default_vl = default_vl; 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci/* Verify that we can write a minimum value and have it take effect */ 23662306a36Sopenharmony_cistatic void proc_write_min(struct vec_data *data) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci int ret, new_default, child_vl; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci if (geteuid() != 0) { 24162306a36Sopenharmony_ci ksft_test_result_skip("Need to be root to write to /proc\n"); 24262306a36Sopenharmony_ci return; 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci ret = file_write_integer(data->default_vl_file, ARCH_MIN_VL); 24662306a36Sopenharmony_ci if (ret != 0) 24762306a36Sopenharmony_ci return; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci /* What was the new value? */ 25062306a36Sopenharmony_ci ret = file_read_integer(data->default_vl_file, &new_default); 25162306a36Sopenharmony_ci if (ret != 0) 25262306a36Sopenharmony_ci return; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci /* Did it take effect in a new process? */ 25562306a36Sopenharmony_ci child_vl = get_child_rdvl(data); 25662306a36Sopenharmony_ci if (child_vl != new_default) { 25762306a36Sopenharmony_ci ksft_test_result_fail("%s is %d but child VL is %d\n", 25862306a36Sopenharmony_ci data->default_vl_file, 25962306a36Sopenharmony_ci new_default, child_vl); 26062306a36Sopenharmony_ci return; 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci ksft_test_result_pass("%s minimum vector length %d\n", data->name, 26462306a36Sopenharmony_ci new_default); 26562306a36Sopenharmony_ci data->min_vl = new_default; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci file_write_integer(data->default_vl_file, data->default_vl); 26862306a36Sopenharmony_ci} 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci/* Verify that we can write a maximum value and have it take effect */ 27162306a36Sopenharmony_cistatic void proc_write_max(struct vec_data *data) 27262306a36Sopenharmony_ci{ 27362306a36Sopenharmony_ci int ret, new_default, child_vl; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci if (geteuid() != 0) { 27662306a36Sopenharmony_ci ksft_test_result_skip("Need to be root to write to /proc\n"); 27762306a36Sopenharmony_ci return; 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci /* -1 is accepted by the /proc interface as the maximum VL */ 28162306a36Sopenharmony_ci ret = file_write_integer(data->default_vl_file, -1); 28262306a36Sopenharmony_ci if (ret != 0) 28362306a36Sopenharmony_ci return; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci /* What was the new value? */ 28662306a36Sopenharmony_ci ret = file_read_integer(data->default_vl_file, &new_default); 28762306a36Sopenharmony_ci if (ret != 0) 28862306a36Sopenharmony_ci return; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci /* Did it take effect in a new process? */ 29162306a36Sopenharmony_ci child_vl = get_child_rdvl(data); 29262306a36Sopenharmony_ci if (child_vl != new_default) { 29362306a36Sopenharmony_ci ksft_test_result_fail("%s is %d but child VL is %d\n", 29462306a36Sopenharmony_ci data->default_vl_file, 29562306a36Sopenharmony_ci new_default, child_vl); 29662306a36Sopenharmony_ci return; 29762306a36Sopenharmony_ci } 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci ksft_test_result_pass("%s maximum vector length %d\n", data->name, 30062306a36Sopenharmony_ci new_default); 30162306a36Sopenharmony_ci data->max_vl = new_default; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci file_write_integer(data->default_vl_file, data->default_vl); 30462306a36Sopenharmony_ci} 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci/* Can we read back a VL from prctl? */ 30762306a36Sopenharmony_cistatic void prctl_get(struct vec_data *data) 30862306a36Sopenharmony_ci{ 30962306a36Sopenharmony_ci int ret; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci ret = prctl(data->prctl_get); 31262306a36Sopenharmony_ci if (ret == -1) { 31362306a36Sopenharmony_ci ksft_test_result_fail("%s prctl() read failed: %d (%s)\n", 31462306a36Sopenharmony_ci data->name, errno, strerror(errno)); 31562306a36Sopenharmony_ci return; 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci /* Mask out any flags */ 31962306a36Sopenharmony_ci ret &= PR_SVE_VL_LEN_MASK; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci /* Is that what we can read back directly? */ 32262306a36Sopenharmony_ci if (ret == data->rdvl()) 32362306a36Sopenharmony_ci ksft_test_result_pass("%s current VL is %d\n", 32462306a36Sopenharmony_ci data->name, ret); 32562306a36Sopenharmony_ci else 32662306a36Sopenharmony_ci ksft_test_result_fail("%s prctl() VL %d but RDVL is %d\n", 32762306a36Sopenharmony_ci data->name, ret, data->rdvl()); 32862306a36Sopenharmony_ci} 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci/* Does the prctl let us set the VL we already have? */ 33162306a36Sopenharmony_cistatic void prctl_set_same(struct vec_data *data) 33262306a36Sopenharmony_ci{ 33362306a36Sopenharmony_ci int cur_vl = data->rdvl(); 33462306a36Sopenharmony_ci int ret; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci ret = prctl(data->prctl_set, cur_vl); 33762306a36Sopenharmony_ci if (ret < 0) { 33862306a36Sopenharmony_ci ksft_test_result_fail("%s prctl set failed: %d (%s)\n", 33962306a36Sopenharmony_ci data->name, errno, strerror(errno)); 34062306a36Sopenharmony_ci return; 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci ksft_test_result(cur_vl == data->rdvl(), 34462306a36Sopenharmony_ci "%s set VL %d and have VL %d\n", 34562306a36Sopenharmony_ci data->name, cur_vl, data->rdvl()); 34662306a36Sopenharmony_ci} 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci/* Can we set a new VL for this process? */ 34962306a36Sopenharmony_cistatic void prctl_set(struct vec_data *data) 35062306a36Sopenharmony_ci{ 35162306a36Sopenharmony_ci int ret; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci if (data->min_vl == data->max_vl) { 35462306a36Sopenharmony_ci ksft_test_result_skip("%s only one VL supported\n", 35562306a36Sopenharmony_ci data->name); 35662306a36Sopenharmony_ci return; 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci /* Try to set the minimum VL */ 36062306a36Sopenharmony_ci ret = prctl(data->prctl_set, data->min_vl); 36162306a36Sopenharmony_ci if (ret < 0) { 36262306a36Sopenharmony_ci ksft_test_result_fail("%s prctl set failed for %d: %d (%s)\n", 36362306a36Sopenharmony_ci data->name, data->min_vl, 36462306a36Sopenharmony_ci errno, strerror(errno)); 36562306a36Sopenharmony_ci return; 36662306a36Sopenharmony_ci } 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci if ((ret & PR_SVE_VL_LEN_MASK) != data->min_vl) { 36962306a36Sopenharmony_ci ksft_test_result_fail("%s prctl set %d but return value is %d\n", 37062306a36Sopenharmony_ci data->name, data->min_vl, data->rdvl()); 37162306a36Sopenharmony_ci return; 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci if (data->rdvl() != data->min_vl) { 37562306a36Sopenharmony_ci ksft_test_result_fail("%s set %d but RDVL is %d\n", 37662306a36Sopenharmony_ci data->name, data->min_vl, data->rdvl()); 37762306a36Sopenharmony_ci return; 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci /* Try to set the maximum VL */ 38162306a36Sopenharmony_ci ret = prctl(data->prctl_set, data->max_vl); 38262306a36Sopenharmony_ci if (ret < 0) { 38362306a36Sopenharmony_ci ksft_test_result_fail("%s prctl set failed for %d: %d (%s)\n", 38462306a36Sopenharmony_ci data->name, data->max_vl, 38562306a36Sopenharmony_ci errno, strerror(errno)); 38662306a36Sopenharmony_ci return; 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci if ((ret & PR_SVE_VL_LEN_MASK) != data->max_vl) { 39062306a36Sopenharmony_ci ksft_test_result_fail("%s prctl() set %d but return value is %d\n", 39162306a36Sopenharmony_ci data->name, data->max_vl, data->rdvl()); 39262306a36Sopenharmony_ci return; 39362306a36Sopenharmony_ci } 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci /* The _INHERIT flag should not be present when we read the VL */ 39662306a36Sopenharmony_ci ret = prctl(data->prctl_get); 39762306a36Sopenharmony_ci if (ret == -1) { 39862306a36Sopenharmony_ci ksft_test_result_fail("%s prctl() read failed: %d (%s)\n", 39962306a36Sopenharmony_ci data->name, errno, strerror(errno)); 40062306a36Sopenharmony_ci return; 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci if (ret & PR_SVE_VL_INHERIT) { 40462306a36Sopenharmony_ci ksft_test_result_fail("%s prctl() reports _INHERIT\n", 40562306a36Sopenharmony_ci data->name); 40662306a36Sopenharmony_ci return; 40762306a36Sopenharmony_ci } 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci ksft_test_result_pass("%s prctl() set min/max\n", data->name); 41062306a36Sopenharmony_ci} 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci/* If we didn't request it a new VL shouldn't affect the child */ 41362306a36Sopenharmony_cistatic void prctl_set_no_child(struct vec_data *data) 41462306a36Sopenharmony_ci{ 41562306a36Sopenharmony_ci int ret, child_vl; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci if (data->min_vl == data->max_vl) { 41862306a36Sopenharmony_ci ksft_test_result_skip("%s only one VL supported\n", 41962306a36Sopenharmony_ci data->name); 42062306a36Sopenharmony_ci return; 42162306a36Sopenharmony_ci } 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci ret = prctl(data->prctl_set, data->min_vl); 42462306a36Sopenharmony_ci if (ret < 0) { 42562306a36Sopenharmony_ci ksft_test_result_fail("%s prctl set failed for %d: %d (%s)\n", 42662306a36Sopenharmony_ci data->name, data->min_vl, 42762306a36Sopenharmony_ci errno, strerror(errno)); 42862306a36Sopenharmony_ci return; 42962306a36Sopenharmony_ci } 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci /* Ensure the default VL is different */ 43262306a36Sopenharmony_ci ret = file_write_integer(data->default_vl_file, data->max_vl); 43362306a36Sopenharmony_ci if (ret != 0) 43462306a36Sopenharmony_ci return; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci /* Check that the child has the default we just set */ 43762306a36Sopenharmony_ci child_vl = get_child_rdvl(data); 43862306a36Sopenharmony_ci if (child_vl != data->max_vl) { 43962306a36Sopenharmony_ci ksft_test_result_fail("%s is %d but child VL is %d\n", 44062306a36Sopenharmony_ci data->default_vl_file, 44162306a36Sopenharmony_ci data->max_vl, child_vl); 44262306a36Sopenharmony_ci return; 44362306a36Sopenharmony_ci } 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci ksft_test_result_pass("%s vector length used default\n", data->name); 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci file_write_integer(data->default_vl_file, data->default_vl); 44862306a36Sopenharmony_ci} 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci/* If we didn't request it a new VL shouldn't affect the child */ 45162306a36Sopenharmony_cistatic void prctl_set_for_child(struct vec_data *data) 45262306a36Sopenharmony_ci{ 45362306a36Sopenharmony_ci int ret, child_vl; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci if (data->min_vl == data->max_vl) { 45662306a36Sopenharmony_ci ksft_test_result_skip("%s only one VL supported\n", 45762306a36Sopenharmony_ci data->name); 45862306a36Sopenharmony_ci return; 45962306a36Sopenharmony_ci } 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci ret = prctl(data->prctl_set, data->min_vl | PR_SVE_VL_INHERIT); 46262306a36Sopenharmony_ci if (ret < 0) { 46362306a36Sopenharmony_ci ksft_test_result_fail("%s prctl set failed for %d: %d (%s)\n", 46462306a36Sopenharmony_ci data->name, data->min_vl, 46562306a36Sopenharmony_ci errno, strerror(errno)); 46662306a36Sopenharmony_ci return; 46762306a36Sopenharmony_ci } 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci /* The _INHERIT flag should be present when we read the VL */ 47062306a36Sopenharmony_ci ret = prctl(data->prctl_get); 47162306a36Sopenharmony_ci if (ret == -1) { 47262306a36Sopenharmony_ci ksft_test_result_fail("%s prctl() read failed: %d (%s)\n", 47362306a36Sopenharmony_ci data->name, errno, strerror(errno)); 47462306a36Sopenharmony_ci return; 47562306a36Sopenharmony_ci } 47662306a36Sopenharmony_ci if (!(ret & PR_SVE_VL_INHERIT)) { 47762306a36Sopenharmony_ci ksft_test_result_fail("%s prctl() does not report _INHERIT\n", 47862306a36Sopenharmony_ci data->name); 47962306a36Sopenharmony_ci return; 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci /* Ensure the default VL is different */ 48362306a36Sopenharmony_ci ret = file_write_integer(data->default_vl_file, data->max_vl); 48462306a36Sopenharmony_ci if (ret != 0) 48562306a36Sopenharmony_ci return; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci /* Check that the child inherited our VL */ 48862306a36Sopenharmony_ci child_vl = get_child_rdvl(data); 48962306a36Sopenharmony_ci if (child_vl != data->min_vl) { 49062306a36Sopenharmony_ci ksft_test_result_fail("%s is %d but child VL is %d\n", 49162306a36Sopenharmony_ci data->default_vl_file, 49262306a36Sopenharmony_ci data->min_vl, child_vl); 49362306a36Sopenharmony_ci return; 49462306a36Sopenharmony_ci } 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci ksft_test_result_pass("%s vector length was inherited\n", data->name); 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci file_write_integer(data->default_vl_file, data->default_vl); 49962306a36Sopenharmony_ci} 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci/* _ONEXEC takes effect only in the child process */ 50262306a36Sopenharmony_cistatic void prctl_set_onexec(struct vec_data *data) 50362306a36Sopenharmony_ci{ 50462306a36Sopenharmony_ci int ret, child_vl; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci if (data->min_vl == data->max_vl) { 50762306a36Sopenharmony_ci ksft_test_result_skip("%s only one VL supported\n", 50862306a36Sopenharmony_ci data->name); 50962306a36Sopenharmony_ci return; 51062306a36Sopenharmony_ci } 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci /* Set a known value for the default and our current VL */ 51362306a36Sopenharmony_ci ret = file_write_integer(data->default_vl_file, data->max_vl); 51462306a36Sopenharmony_ci if (ret != 0) 51562306a36Sopenharmony_ci return; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci ret = prctl(data->prctl_set, data->max_vl); 51862306a36Sopenharmony_ci if (ret < 0) { 51962306a36Sopenharmony_ci ksft_test_result_fail("%s prctl set failed for %d: %d (%s)\n", 52062306a36Sopenharmony_ci data->name, data->min_vl, 52162306a36Sopenharmony_ci errno, strerror(errno)); 52262306a36Sopenharmony_ci return; 52362306a36Sopenharmony_ci } 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci /* Set a different value for the child to have on exec */ 52662306a36Sopenharmony_ci ret = prctl(data->prctl_set, data->min_vl | PR_SVE_SET_VL_ONEXEC); 52762306a36Sopenharmony_ci if (ret < 0) { 52862306a36Sopenharmony_ci ksft_test_result_fail("%s prctl set failed for %d: %d (%s)\n", 52962306a36Sopenharmony_ci data->name, data->min_vl, 53062306a36Sopenharmony_ci errno, strerror(errno)); 53162306a36Sopenharmony_ci return; 53262306a36Sopenharmony_ci } 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci /* Our current VL should stay the same */ 53562306a36Sopenharmony_ci if (data->rdvl() != data->max_vl) { 53662306a36Sopenharmony_ci ksft_test_result_fail("%s VL changed by _ONEXEC prctl()\n", 53762306a36Sopenharmony_ci data->name); 53862306a36Sopenharmony_ci return; 53962306a36Sopenharmony_ci } 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci /* Check that the child inherited our VL */ 54262306a36Sopenharmony_ci child_vl = get_child_rdvl(data); 54362306a36Sopenharmony_ci if (child_vl != data->min_vl) { 54462306a36Sopenharmony_ci ksft_test_result_fail("Set %d _ONEXEC but child VL is %d\n", 54562306a36Sopenharmony_ci data->min_vl, child_vl); 54662306a36Sopenharmony_ci return; 54762306a36Sopenharmony_ci } 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci ksft_test_result_pass("%s vector length set on exec\n", data->name); 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci file_write_integer(data->default_vl_file, data->default_vl); 55262306a36Sopenharmony_ci} 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci/* For each VQ verify that setting via prctl() does the right thing */ 55562306a36Sopenharmony_cistatic void prctl_set_all_vqs(struct vec_data *data) 55662306a36Sopenharmony_ci{ 55762306a36Sopenharmony_ci int ret, vq, vl, new_vl, i; 55862306a36Sopenharmony_ci int orig_vls[ARRAY_SIZE(vec_data)]; 55962306a36Sopenharmony_ci int errors = 0; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci if (!data->min_vl || !data->max_vl) { 56262306a36Sopenharmony_ci ksft_test_result_skip("%s Failed to enumerate VLs, not testing VL setting\n", 56362306a36Sopenharmony_ci data->name); 56462306a36Sopenharmony_ci return; 56562306a36Sopenharmony_ci } 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(vec_data); i++) 56862306a36Sopenharmony_ci orig_vls[i] = vec_data[i].rdvl(); 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci for (vq = SVE_VQ_MIN; vq <= SVE_VQ_MAX; vq++) { 57162306a36Sopenharmony_ci vl = sve_vl_from_vq(vq); 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci /* Attempt to set the VL */ 57462306a36Sopenharmony_ci ret = prctl(data->prctl_set, vl); 57562306a36Sopenharmony_ci if (ret < 0) { 57662306a36Sopenharmony_ci errors++; 57762306a36Sopenharmony_ci ksft_print_msg("%s prctl set failed for %d: %d (%s)\n", 57862306a36Sopenharmony_ci data->name, vl, 57962306a36Sopenharmony_ci errno, strerror(errno)); 58062306a36Sopenharmony_ci continue; 58162306a36Sopenharmony_ci } 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci new_vl = ret & PR_SVE_VL_LEN_MASK; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci /* Check that we actually have the reported new VL */ 58662306a36Sopenharmony_ci if (data->rdvl() != new_vl) { 58762306a36Sopenharmony_ci ksft_print_msg("Set %s VL %d but RDVL reports %d\n", 58862306a36Sopenharmony_ci data->name, new_vl, data->rdvl()); 58962306a36Sopenharmony_ci errors++; 59062306a36Sopenharmony_ci } 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci /* Did any other VLs change? */ 59362306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(vec_data); i++) { 59462306a36Sopenharmony_ci if (&vec_data[i] == data) 59562306a36Sopenharmony_ci continue; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci if (!(getauxval(vec_data[i].hwcap_type) & vec_data[i].hwcap)) 59862306a36Sopenharmony_ci continue; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci if (vec_data[i].rdvl() != orig_vls[i]) { 60162306a36Sopenharmony_ci ksft_print_msg("%s VL changed from %d to %d\n", 60262306a36Sopenharmony_ci vec_data[i].name, orig_vls[i], 60362306a36Sopenharmony_ci vec_data[i].rdvl()); 60462306a36Sopenharmony_ci errors++; 60562306a36Sopenharmony_ci } 60662306a36Sopenharmony_ci } 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci /* Was that the VL we asked for? */ 60962306a36Sopenharmony_ci if (new_vl == vl) 61062306a36Sopenharmony_ci continue; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci /* Should round up to the minimum VL if below it */ 61362306a36Sopenharmony_ci if (vl < data->min_vl) { 61462306a36Sopenharmony_ci if (new_vl != data->min_vl) { 61562306a36Sopenharmony_ci ksft_print_msg("%s VL %d returned %d not minimum %d\n", 61662306a36Sopenharmony_ci data->name, vl, new_vl, 61762306a36Sopenharmony_ci data->min_vl); 61862306a36Sopenharmony_ci errors++; 61962306a36Sopenharmony_ci } 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci continue; 62262306a36Sopenharmony_ci } 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci /* Should round down to maximum VL if above it */ 62562306a36Sopenharmony_ci if (vl > data->max_vl) { 62662306a36Sopenharmony_ci if (new_vl != data->max_vl) { 62762306a36Sopenharmony_ci ksft_print_msg("%s VL %d returned %d not maximum %d\n", 62862306a36Sopenharmony_ci data->name, vl, new_vl, 62962306a36Sopenharmony_ci data->max_vl); 63062306a36Sopenharmony_ci errors++; 63162306a36Sopenharmony_ci } 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci continue; 63462306a36Sopenharmony_ci } 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci /* Otherwise we should've rounded down */ 63762306a36Sopenharmony_ci if (!(new_vl < vl)) { 63862306a36Sopenharmony_ci ksft_print_msg("%s VL %d returned %d, did not round down\n", 63962306a36Sopenharmony_ci data->name, vl, new_vl); 64062306a36Sopenharmony_ci errors++; 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci continue; 64362306a36Sopenharmony_ci } 64462306a36Sopenharmony_ci } 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci ksft_test_result(errors == 0, "%s prctl() set all VLs, %d errors\n", 64762306a36Sopenharmony_ci data->name, errors); 64862306a36Sopenharmony_ci} 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_citypedef void (*test_type)(struct vec_data *); 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_cistatic const test_type tests[] = { 65362306a36Sopenharmony_ci /* 65462306a36Sopenharmony_ci * The default/min/max tests must be first and in this order 65562306a36Sopenharmony_ci * to provide data for other tests. 65662306a36Sopenharmony_ci */ 65762306a36Sopenharmony_ci proc_read_default, 65862306a36Sopenharmony_ci proc_write_min, 65962306a36Sopenharmony_ci proc_write_max, 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci prctl_get, 66262306a36Sopenharmony_ci prctl_set_same, 66362306a36Sopenharmony_ci prctl_set, 66462306a36Sopenharmony_ci prctl_set_no_child, 66562306a36Sopenharmony_ci prctl_set_for_child, 66662306a36Sopenharmony_ci prctl_set_onexec, 66762306a36Sopenharmony_ci prctl_set_all_vqs, 66862306a36Sopenharmony_ci}; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_cistatic inline void smstart(void) 67162306a36Sopenharmony_ci{ 67262306a36Sopenharmony_ci asm volatile("msr S0_3_C4_C7_3, xzr"); 67362306a36Sopenharmony_ci} 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_cistatic inline void smstart_sm(void) 67662306a36Sopenharmony_ci{ 67762306a36Sopenharmony_ci asm volatile("msr S0_3_C4_C3_3, xzr"); 67862306a36Sopenharmony_ci} 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_cistatic inline void smstop(void) 68162306a36Sopenharmony_ci{ 68262306a36Sopenharmony_ci asm volatile("msr S0_3_C4_C6_3, xzr"); 68362306a36Sopenharmony_ci} 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci/* 68762306a36Sopenharmony_ci * Verify we can change the SVE vector length while SME is active and 68862306a36Sopenharmony_ci * continue to use SME afterwards. 68962306a36Sopenharmony_ci */ 69062306a36Sopenharmony_cistatic void change_sve_with_za(void) 69162306a36Sopenharmony_ci{ 69262306a36Sopenharmony_ci struct vec_data *sve_data = &vec_data[VEC_SVE]; 69362306a36Sopenharmony_ci bool pass = true; 69462306a36Sopenharmony_ci int ret, i; 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci if (sve_data->min_vl == sve_data->max_vl) { 69762306a36Sopenharmony_ci ksft_print_msg("Only one SVE VL supported, can't change\n"); 69862306a36Sopenharmony_ci ksft_test_result_skip("change_sve_while_sme\n"); 69962306a36Sopenharmony_ci return; 70062306a36Sopenharmony_ci } 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci /* Ensure we will trigger a change when we set the maximum */ 70362306a36Sopenharmony_ci ret = prctl(sve_data->prctl_set, sve_data->min_vl); 70462306a36Sopenharmony_ci if (ret != sve_data->min_vl) { 70562306a36Sopenharmony_ci ksft_print_msg("Failed to set SVE VL %d: %d\n", 70662306a36Sopenharmony_ci sve_data->min_vl, ret); 70762306a36Sopenharmony_ci pass = false; 70862306a36Sopenharmony_ci } 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci /* Enable SM and ZA */ 71162306a36Sopenharmony_ci smstart(); 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci /* Trigger another VL change */ 71462306a36Sopenharmony_ci ret = prctl(sve_data->prctl_set, sve_data->max_vl); 71562306a36Sopenharmony_ci if (ret != sve_data->max_vl) { 71662306a36Sopenharmony_ci ksft_print_msg("Failed to set SVE VL %d: %d\n", 71762306a36Sopenharmony_ci sve_data->max_vl, ret); 71862306a36Sopenharmony_ci pass = false; 71962306a36Sopenharmony_ci } 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci /* 72262306a36Sopenharmony_ci * Spin for a bit with SM enabled to try to trigger another 72362306a36Sopenharmony_ci * save/restore. We can't use syscalls without exiting 72462306a36Sopenharmony_ci * streaming mode. 72562306a36Sopenharmony_ci */ 72662306a36Sopenharmony_ci for (i = 0; i < 100000000; i++) 72762306a36Sopenharmony_ci smstart_sm(); 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci /* 73062306a36Sopenharmony_ci * TODO: Verify that ZA was preserved over the VL change and 73162306a36Sopenharmony_ci * spin. 73262306a36Sopenharmony_ci */ 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci /* Clean up after ourselves */ 73562306a36Sopenharmony_ci smstop(); 73662306a36Sopenharmony_ci ret = prctl(sve_data->prctl_set, sve_data->default_vl); 73762306a36Sopenharmony_ci if (ret != sve_data->default_vl) { 73862306a36Sopenharmony_ci ksft_print_msg("Failed to restore SVE VL %d: %d\n", 73962306a36Sopenharmony_ci sve_data->default_vl, ret); 74062306a36Sopenharmony_ci pass = false; 74162306a36Sopenharmony_ci } 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci ksft_test_result(pass, "change_sve_with_za\n"); 74462306a36Sopenharmony_ci} 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_citypedef void (*test_all_type)(void); 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_cistatic const struct { 74962306a36Sopenharmony_ci const char *name; 75062306a36Sopenharmony_ci test_all_type test; 75162306a36Sopenharmony_ci} all_types_tests[] = { 75262306a36Sopenharmony_ci { "change_sve_with_za", change_sve_with_za }, 75362306a36Sopenharmony_ci}; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ciint main(void) 75662306a36Sopenharmony_ci{ 75762306a36Sopenharmony_ci bool all_supported = true; 75862306a36Sopenharmony_ci int i, j; 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci ksft_print_header(); 76162306a36Sopenharmony_ci ksft_set_plan(ARRAY_SIZE(tests) * ARRAY_SIZE(vec_data) + 76262306a36Sopenharmony_ci ARRAY_SIZE(all_types_tests)); 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(vec_data); i++) { 76562306a36Sopenharmony_ci struct vec_data *data = &vec_data[i]; 76662306a36Sopenharmony_ci unsigned long supported; 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci supported = getauxval(data->hwcap_type) & data->hwcap; 76962306a36Sopenharmony_ci if (!supported) 77062306a36Sopenharmony_ci all_supported = false; 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci for (j = 0; j < ARRAY_SIZE(tests); j++) { 77362306a36Sopenharmony_ci if (supported) 77462306a36Sopenharmony_ci tests[j](data); 77562306a36Sopenharmony_ci else 77662306a36Sopenharmony_ci ksft_test_result_skip("%s not supported\n", 77762306a36Sopenharmony_ci data->name); 77862306a36Sopenharmony_ci } 77962306a36Sopenharmony_ci } 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(all_types_tests); i++) { 78262306a36Sopenharmony_ci if (all_supported) 78362306a36Sopenharmony_ci all_types_tests[i].test(); 78462306a36Sopenharmony_ci else 78562306a36Sopenharmony_ci ksft_test_result_skip("%s\n", all_types_tests[i].name); 78662306a36Sopenharmony_ci } 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci ksft_exit_pass(); 78962306a36Sopenharmony_ci} 790