/* * The architecture defines the maximum VQ as 16 but for extensibility * the kernel specifies the SVE_VQ_MAX as 512 resulting in us running * a *lot* more tests than are useful if we use it. Until the * architecture is extended let's limit our coverage to what is * currently allowed, plus one extra to ensure we cover constraining * the VL as expected.
*/ #define TEST_VQ_MAX 17
struct vec_type { constchar *name; unsignedlong hwcap_type; unsignedlong hwcap; int regset; int prctl_set;
};
/* Validate setting and getting the inherit flag */ staticvoid ptrace_set_get_inherit(pid_t child, conststruct vec_type *type)
{ struct user_sve_header sve; struct user_sve_header *new_sve = NULL;
size_t new_sve_size = 0; int ret;
/* First set the flag */
memset(&sve, 0, sizeof(sve));
sve.size = sizeof(sve);
sve.vl = sve_vl_from_vq(SVE_VQ_MIN);
sve.flags = SVE_PT_VL_INHERIT | SVE_PT_REGS_SVE;
ret = set_sve(child, type, &sve); if (ret != 0) {
ksft_test_result_fail("Failed to set %s SVE_PT_VL_INHERIT\n",
type->name); return;
}
/* * Read back the new register state and verify that we have * set the flags we expected.
*/ if (!get_sve(child, type, (void **)&new_sve, &new_sve_size)) {
ksft_test_result_fail("Failed to read %s SVE flags\n",
type->name); return;
}
/* Validate attempting to set the specfied VL via ptrace */ staticvoid ptrace_set_get_vl(pid_t child, conststruct vec_type *type, unsignedint vl, bool *supported)
{ struct user_sve_header sve; struct user_sve_header *new_sve = NULL;
size_t new_sve_size = 0; int ret, prctl_vl;
*supported = false;
/* Check if the VL is supported in this process */
prctl_vl = prctl(type->prctl_set, vl); if (prctl_vl == -1)
ksft_exit_fail_msg("prctl(PR_%s_SET_VL) failed: %s (%d)\n",
type->name, strerror(errno), errno);
/* If the VL is not supported then a supported VL will be returned */
*supported = (prctl_vl == vl);
/* Set the VL by doing a set with no register payload */
memset(&sve, 0, sizeof(sve));
sve.size = sizeof(sve);
sve.flags = SVE_PT_REGS_SVE;
sve.vl = vl;
ret = set_sve(child, type, &sve); if (ret != 0) {
ksft_test_result_fail("Failed to set %s VL %u\n",
type->name, vl); return;
}
/* * Read back the new register state and verify that we have the * same VL that we got from prctl() on ourselves.
*/ if (!get_sve(child, type, (void **)&new_sve, &new_sve_size)) {
ksft_test_result_fail("Failed to read %s VL %u\n",
type->name, vl); return;
}
/* Access the FPSIMD registers via the SVE regset */ staticvoid ptrace_sve_fpsimd(pid_t child, conststruct vec_type *type)
{ void *svebuf; struct user_sve_header *sve; struct user_fpsimd_state *fpsimd, new_fpsimd; unsignedint i, j; unsignedchar *p; int ret;
svebuf = malloc(SVE_PT_SIZE(0, SVE_PT_REGS_FPSIMD)); if (!svebuf) {
ksft_test_result_fail("Failed to allocate FPSIMD buffer\n"); return;
}
memset(svebuf, 0, SVE_PT_SIZE(0, SVE_PT_REGS_FPSIMD));
sve = svebuf;
sve->flags = SVE_PT_REGS_FPSIMD;
sve->size = SVE_PT_SIZE(0, SVE_PT_REGS_FPSIMD);
sve->vl = 16; /* We don't care what the VL is */
/* Try to set a known FPSIMD state via PT_REGS_SVE */
fpsimd = (struct user_fpsimd_state *)((char *)sve +
SVE_PT_FPSIMD_OFFSET); for (i = 0; i < 32; ++i) {
p = (unsignedchar *)&fpsimd->vregs[i];
/* This should only succeed for SVE */
ret = set_sve(child, type, sve);
ksft_test_result((type->regset == NT_ARM_SVE) == (ret == 0), "%s FPSIMD set via SVE: %d\n",
type->name, ret); if (ret) goto out;
/* Verify via the FPSIMD regset */ if (get_fpsimd(child, &new_fpsimd)) {
ksft_test_result_fail("get_fpsimd(): %s\n",
strerror(errno)); goto out;
} if (memcmp(fpsimd, &new_fpsimd, sizeof(*fpsimd)) == 0)
ksft_test_result_pass("%s get_fpsimd() gave same state\n",
type->name); else
ksft_test_result_fail("%s get_fpsimd() gave different state\n",
type->name);
out:
free(svebuf);
}
/* Validate attempting to set SVE data and read SVE data */ staticvoid ptrace_set_sve_get_sve_data(pid_t child, conststruct vec_type *type, unsignedint vl)
{ void *write_buf; void *read_buf = NULL; struct user_sve_header *write_sve; struct user_sve_header *read_sve;
size_t read_sve_size = 0; unsignedint vq = sve_vq_from_vl(vl); int ret, i;
size_t data_size; int errors = 0;
/* Set up some data and write it out */
memset(write_sve, 0, data_size);
write_sve->size = data_size;
write_sve->vl = vl;
write_sve->flags = SVE_PT_REGS_SVE;
for (i = 0; i < __SVE_NUM_ZREGS; i++)
fill_buf(write_buf + SVE_PT_SVE_ZREG_OFFSET(vq, i),
SVE_PT_SVE_ZREG_SIZE(vq));
for (i = 0; i < __SVE_NUM_PREGS; i++)
fill_buf(write_buf + SVE_PT_SVE_PREG_OFFSET(vq, i),
SVE_PT_SVE_PREG_SIZE(vq));
ret = set_sve(child, type, write_sve); if (ret != 0) {
ksft_test_result_fail("Failed to set %s VL %u data\n",
type->name, vl); goto out;
}
/* Read the data back */ if (!get_sve(child, type, (void **)&read_buf, &read_sve_size)) {
ksft_test_result_fail("Failed to read %s VL %u data\n",
type->name, vl); goto out;
}
read_sve = read_buf;
/* We might read more data if there's extensions we don't know */ if (read_sve->size < write_sve->size) {
ksft_test_result_fail("%s wrote %d bytes, only read %d\n",
type->name, write_sve->size,
read_sve->size); goto out_read;
}
for (i = 0; i < __SVE_NUM_ZREGS; i++) { if (memcmp(write_buf + SVE_PT_SVE_ZREG_OFFSET(vq, i),
read_buf + SVE_PT_SVE_ZREG_OFFSET(vq, i),
SVE_PT_SVE_ZREG_SIZE(vq)) != 0) {
printf("# Mismatch in %u Z%d\n", vl, i);
errors++;
}
}
for (i = 0; i < __SVE_NUM_PREGS; i++) { if (memcmp(write_buf + SVE_PT_SVE_PREG_OFFSET(vq, i),
read_buf + SVE_PT_SVE_PREG_OFFSET(vq, i),
SVE_PT_SVE_PREG_SIZE(vq)) != 0) {
printf("# Mismatch in %u P%d\n", vl, i);
errors++;
}
}
ksft_test_result(errors == 0, "Set and get %s data for VL %u\n",
type->name, vl);
out_read:
free(read_buf);
out:
free(write_buf);
}
/* Validate attempting to set SVE data and read it via the FPSIMD regset */ staticvoid ptrace_set_sve_get_fpsimd_data(pid_t child, conststruct vec_type *type, unsignedint vl)
{ void *write_buf; struct user_sve_header *write_sve; unsignedint vq = sve_vq_from_vl(vl); struct user_fpsimd_state fpsimd_state; int ret, i;
size_t data_size; int errors = 0;
if (__BYTE_ORDER == __BIG_ENDIAN) {
ksft_test_result_skip("Big endian not supported\n"); return;
}
/* Set up some data and write it out */
memset(write_sve, 0, data_size);
write_sve->size = data_size;
write_sve->vl = vl;
write_sve->flags = SVE_PT_REGS_SVE;
for (i = 0; i < __SVE_NUM_ZREGS; i++)
fill_buf(write_buf + SVE_PT_SVE_ZREG_OFFSET(vq, i),
SVE_PT_SVE_ZREG_SIZE(vq));
ret = set_sve(child, type, write_sve); if (ret != 0) {
ksft_test_result_fail("Failed to set %s VL %u data\n",
type->name, vl); goto out;
}
/* Read the data back */ if (get_fpsimd(child, &fpsimd_state)) {
ksft_test_result_fail("Failed to read %s VL %u FPSIMD data\n",
type->name, vl); goto out;
}
for (i = 0; i < __SVE_NUM_ZREGS; i++) {
__uint128_t tmp = 0;
/* * Z regs are stored endianness invariant, this won't * work for big endian
*/
memcpy(&tmp, write_buf + SVE_PT_SVE_ZREG_OFFSET(vq, i), sizeof(tmp));
if (tmp != fpsimd_state.vregs[i]) {
printf("# Mismatch in FPSIMD for %s VL %u Z%d\n",
type->name, vl, i);
errors++;
}
}
ksft_test_result(errors == 0, "Set and get FPSIMD data for %s VL %u\n",
type->name, vl);
out:
free(write_buf);
}
/* Validate attempting to set FPSIMD data and read it via the SVE regset */ staticvoid ptrace_set_fpsimd_get_sve_data(pid_t child, conststruct vec_type *type, unsignedint vl)
{ void *read_buf = NULL; unsignedchar *p; struct user_sve_header *read_sve; unsignedint vq = sve_vq_from_vl(vl); struct user_fpsimd_state write_fpsimd; int ret, i, j;
size_t read_sve_size = 0;
size_t expected_size; int errors = 0;
if (__BYTE_ORDER == __BIG_ENDIAN) {
ksft_test_result_skip("Big endian not supported\n"); return;
}
for (i = 0; i < 32; ++i) {
p = (unsignedchar *)&write_fpsimd.vregs[i];
/* The kernel may return either SVE or FPSIMD format */ switch (read_sve->flags & SVE_PT_REGS_MASK) { case SVE_PT_REGS_FPSIMD:
expected_size = SVE_PT_FPSIMD_SIZE(vq, SVE_PT_REGS_FPSIMD); if (read_sve_size < expected_size) {
ksft_test_result_fail("Read %ld bytes, expected %ld\n",
read_sve_size, expected_size); goto out;
}
ret = memcmp(&write_fpsimd, read_buf + SVE_PT_FPSIMD_OFFSET, sizeof(write_fpsimd)); if (ret != 0) {
ksft_print_msg("Read FPSIMD data mismatch\n");
errors++;
} break;
for (i = 0; i < ARRAY_SIZE(vec_types); i++) { /* FPSIMD via SVE regset */ if (getauxval(vec_types[i].hwcap_type) & vec_types[i].hwcap) {
ptrace_sve_fpsimd(child, &vec_types[i]);
} else {
ksft_test_result_skip("%s FPSIMD set via SVE\n",
vec_types[i].name);
ksft_test_result_skip("%s FPSIMD read\n",
vec_types[i].name);
}
/* Step through every possible VQ */ for (vq = SVE_VQ_MIN; vq <= TEST_VQ_MAX; vq++) {
vl = sve_vl_from_vq(vq);
/* First, try to set this vector length */ if (getauxval(vec_types[i].hwcap_type) &
vec_types[i].hwcap) {
ptrace_set_get_vl(child, &vec_types[i], vl,
&vl_supported);
} else {
ksft_test_result_skip("%s get/set VL %d\n",
vec_types[i].name, vl);
vl_supported = false;
}
/* If the VL is supported validate data set/get */ if (vl_supported) {
ptrace_set_sve_get_sve_data(child, &vec_types[i], vl);
ptrace_set_sve_get_fpsimd_data(child, &vec_types[i], vl);
ptrace_set_fpsimd_get_sve_data(child, &vec_types[i], vl);
} else {
ksft_test_result_skip("%s set SVE get SVE for VL %d\n",
vec_types[i].name, vl);
ksft_test_result_skip("%s set SVE get FPSIMD for VL %d\n",
vec_types[i].name, vl);
ksft_test_result_skip("%s set FPSIMD get SVE for VL %d\n",
vec_types[i].name, vl);
}
}
}
ret = EXIT_SUCCESS;
error:
kill(child, SIGKILL);
disappeared: return ret;
}
int main(void)
{ int ret = EXIT_SUCCESS;
pid_t child;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.