vec-syscfg.c (15711B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Copyright (C) 2021 ARM Limited. 4 * Original author: Mark Brown <broonie@kernel.org> 5 */ 6#include <assert.h> 7#include <errno.h> 8#include <fcntl.h> 9#include <stddef.h> 10#include <stdio.h> 11#include <stdlib.h> 12#include <string.h> 13#include <unistd.h> 14#include <sys/auxv.h> 15#include <sys/prctl.h> 16#include <sys/types.h> 17#include <sys/wait.h> 18#include <asm/sigcontext.h> 19#include <asm/hwcap.h> 20 21#include "../../kselftest.h" 22#include "rdvl.h" 23 24#define ARCH_MIN_VL SVE_VL_MIN 25 26struct vec_data { 27 const char *name; 28 unsigned long hwcap_type; 29 unsigned long hwcap; 30 const char *rdvl_binary; 31 int (*rdvl)(void); 32 33 int prctl_get; 34 int prctl_set; 35 const char *default_vl_file; 36 37 int default_vl; 38 int min_vl; 39 int max_vl; 40}; 41 42 43static struct vec_data vec_data[] = { 44 { 45 .name = "SVE", 46 .hwcap_type = AT_HWCAP, 47 .hwcap = HWCAP_SVE, 48 .rdvl = rdvl_sve, 49 .rdvl_binary = "./rdvl-sve", 50 .prctl_get = PR_SVE_GET_VL, 51 .prctl_set = PR_SVE_SET_VL, 52 .default_vl_file = "/proc/sys/abi/sve_default_vector_length", 53 }, 54 { 55 .name = "SME", 56 .hwcap_type = AT_HWCAP2, 57 .hwcap = HWCAP2_SME, 58 .rdvl = rdvl_sme, 59 .rdvl_binary = "./rdvl-sme", 60 .prctl_get = PR_SME_GET_VL, 61 .prctl_set = PR_SME_SET_VL, 62 .default_vl_file = "/proc/sys/abi/sme_default_vector_length", 63 }, 64}; 65 66static int stdio_read_integer(FILE *f, const char *what, int *val) 67{ 68 int n = 0; 69 int ret; 70 71 ret = fscanf(f, "%d%*1[\n]%n", val, &n); 72 if (ret < 1 || n < 1) { 73 ksft_print_msg("failed to parse integer from %s\n", what); 74 return -1; 75 } 76 77 return 0; 78} 79 80/* Start a new process and return the vector length it sees */ 81static int get_child_rdvl(struct vec_data *data) 82{ 83 FILE *out; 84 int pipefd[2]; 85 pid_t pid, child; 86 int read_vl, ret; 87 88 ret = pipe(pipefd); 89 if (ret == -1) { 90 ksft_print_msg("pipe() failed: %d (%s)\n", 91 errno, strerror(errno)); 92 return -1; 93 } 94 95 fflush(stdout); 96 97 child = fork(); 98 if (child == -1) { 99 ksft_print_msg("fork() failed: %d (%s)\n", 100 errno, strerror(errno)); 101 close(pipefd[0]); 102 close(pipefd[1]); 103 return -1; 104 } 105 106 /* Child: put vector length on the pipe */ 107 if (child == 0) { 108 /* 109 * Replace stdout with the pipe, errors to stderr from 110 * here as kselftest prints to stdout. 111 */ 112 ret = dup2(pipefd[1], 1); 113 if (ret == -1) { 114 fprintf(stderr, "dup2() %d\n", errno); 115 exit(EXIT_FAILURE); 116 } 117 118 /* exec() a new binary which puts the VL on stdout */ 119 ret = execl(data->rdvl_binary, data->rdvl_binary, NULL); 120 fprintf(stderr, "execl(%s) failed: %d (%s)\n", 121 data->rdvl_binary, errno, strerror(errno)); 122 123 exit(EXIT_FAILURE); 124 } 125 126 close(pipefd[1]); 127 128 /* Parent; wait for the exit status from the child & verify it */ 129 do { 130 pid = wait(&ret); 131 if (pid == -1) { 132 ksft_print_msg("wait() failed: %d (%s)\n", 133 errno, strerror(errno)); 134 close(pipefd[0]); 135 return -1; 136 } 137 } while (pid != child); 138 139 assert(pid == child); 140 141 if (!WIFEXITED(ret)) { 142 ksft_print_msg("child exited abnormally\n"); 143 close(pipefd[0]); 144 return -1; 145 } 146 147 if (WEXITSTATUS(ret) != 0) { 148 ksft_print_msg("child returned error %d\n", 149 WEXITSTATUS(ret)); 150 close(pipefd[0]); 151 return -1; 152 } 153 154 out = fdopen(pipefd[0], "r"); 155 if (!out) { 156 ksft_print_msg("failed to open child stdout\n"); 157 close(pipefd[0]); 158 return -1; 159 } 160 161 ret = stdio_read_integer(out, "child", &read_vl); 162 fclose(out); 163 if (ret != 0) 164 return ret; 165 166 return read_vl; 167} 168 169static int file_read_integer(const char *name, int *val) 170{ 171 FILE *f; 172 int ret; 173 174 f = fopen(name, "r"); 175 if (!f) { 176 ksft_test_result_fail("Unable to open %s: %d (%s)\n", 177 name, errno, 178 strerror(errno)); 179 return -1; 180 } 181 182 ret = stdio_read_integer(f, name, val); 183 fclose(f); 184 185 return ret; 186} 187 188static int file_write_integer(const char *name, int val) 189{ 190 FILE *f; 191 192 f = fopen(name, "w"); 193 if (!f) { 194 ksft_test_result_fail("Unable to open %s: %d (%s)\n", 195 name, errno, 196 strerror(errno)); 197 return -1; 198 } 199 200 fprintf(f, "%d", val); 201 fclose(f); 202 203 return 0; 204} 205 206/* 207 * Verify that we can read the default VL via proc, checking that it 208 * is set in a freshly spawned child. 209 */ 210static void proc_read_default(struct vec_data *data) 211{ 212 int default_vl, child_vl, ret; 213 214 ret = file_read_integer(data->default_vl_file, &default_vl); 215 if (ret != 0) 216 return; 217 218 /* Is this the actual default seen by new processes? */ 219 child_vl = get_child_rdvl(data); 220 if (child_vl != default_vl) { 221 ksft_test_result_fail("%s is %d but child VL is %d\n", 222 data->default_vl_file, 223 default_vl, child_vl); 224 return; 225 } 226 227 ksft_test_result_pass("%s default vector length %d\n", data->name, 228 default_vl); 229 data->default_vl = default_vl; 230} 231 232/* Verify that we can write a minimum value and have it take effect */ 233static void proc_write_min(struct vec_data *data) 234{ 235 int ret, new_default, child_vl; 236 237 if (geteuid() != 0) { 238 ksft_test_result_skip("Need to be root to write to /proc\n"); 239 return; 240 } 241 242 ret = file_write_integer(data->default_vl_file, ARCH_MIN_VL); 243 if (ret != 0) 244 return; 245 246 /* What was the new value? */ 247 ret = file_read_integer(data->default_vl_file, &new_default); 248 if (ret != 0) 249 return; 250 251 /* Did it take effect in a new process? */ 252 child_vl = get_child_rdvl(data); 253 if (child_vl != new_default) { 254 ksft_test_result_fail("%s is %d but child VL is %d\n", 255 data->default_vl_file, 256 new_default, child_vl); 257 return; 258 } 259 260 ksft_test_result_pass("%s minimum vector length %d\n", data->name, 261 new_default); 262 data->min_vl = new_default; 263 264 file_write_integer(data->default_vl_file, data->default_vl); 265} 266 267/* Verify that we can write a maximum value and have it take effect */ 268static void proc_write_max(struct vec_data *data) 269{ 270 int ret, new_default, child_vl; 271 272 if (geteuid() != 0) { 273 ksft_test_result_skip("Need to be root to write to /proc\n"); 274 return; 275 } 276 277 /* -1 is accepted by the /proc interface as the maximum VL */ 278 ret = file_write_integer(data->default_vl_file, -1); 279 if (ret != 0) 280 return; 281 282 /* What was the new value? */ 283 ret = file_read_integer(data->default_vl_file, &new_default); 284 if (ret != 0) 285 return; 286 287 /* Did it take effect in a new process? */ 288 child_vl = get_child_rdvl(data); 289 if (child_vl != new_default) { 290 ksft_test_result_fail("%s is %d but child VL is %d\n", 291 data->default_vl_file, 292 new_default, child_vl); 293 return; 294 } 295 296 ksft_test_result_pass("%s maximum vector length %d\n", data->name, 297 new_default); 298 data->max_vl = new_default; 299 300 file_write_integer(data->default_vl_file, data->default_vl); 301} 302 303/* Can we read back a VL from prctl? */ 304static void prctl_get(struct vec_data *data) 305{ 306 int ret; 307 308 ret = prctl(data->prctl_get); 309 if (ret == -1) { 310 ksft_test_result_fail("%s prctl() read failed: %d (%s)\n", 311 data->name, errno, strerror(errno)); 312 return; 313 } 314 315 /* Mask out any flags */ 316 ret &= PR_SVE_VL_LEN_MASK; 317 318 /* Is that what we can read back directly? */ 319 if (ret == data->rdvl()) 320 ksft_test_result_pass("%s current VL is %d\n", 321 data->name, ret); 322 else 323 ksft_test_result_fail("%s prctl() VL %d but RDVL is %d\n", 324 data->name, ret, data->rdvl()); 325} 326 327/* Does the prctl let us set the VL we already have? */ 328static void prctl_set_same(struct vec_data *data) 329{ 330 int cur_vl = data->rdvl(); 331 int ret; 332 333 ret = prctl(data->prctl_set, cur_vl); 334 if (ret < 0) { 335 ksft_test_result_fail("%s prctl set failed: %d (%s)\n", 336 data->name, errno, strerror(errno)); 337 return; 338 } 339 340 ksft_test_result(cur_vl == data->rdvl(), 341 "%s set VL %d and have VL %d\n", 342 data->name, cur_vl, data->rdvl()); 343} 344 345/* Can we set a new VL for this process? */ 346static void prctl_set(struct vec_data *data) 347{ 348 int ret; 349 350 if (data->min_vl == data->max_vl) { 351 ksft_test_result_skip("%s only one VL supported\n", 352 data->name); 353 return; 354 } 355 356 /* Try to set the minimum VL */ 357 ret = prctl(data->prctl_set, data->min_vl); 358 if (ret < 0) { 359 ksft_test_result_fail("%s prctl set failed for %d: %d (%s)\n", 360 data->name, data->min_vl, 361 errno, strerror(errno)); 362 return; 363 } 364 365 if ((ret & PR_SVE_VL_LEN_MASK) != data->min_vl) { 366 ksft_test_result_fail("%s prctl set %d but return value is %d\n", 367 data->name, data->min_vl, data->rdvl()); 368 return; 369 } 370 371 if (data->rdvl() != data->min_vl) { 372 ksft_test_result_fail("%s set %d but RDVL is %d\n", 373 data->name, data->min_vl, data->rdvl()); 374 return; 375 } 376 377 /* Try to set the maximum VL */ 378 ret = prctl(data->prctl_set, data->max_vl); 379 if (ret < 0) { 380 ksft_test_result_fail("%s prctl set failed for %d: %d (%s)\n", 381 data->name, data->max_vl, 382 errno, strerror(errno)); 383 return; 384 } 385 386 if ((ret & PR_SVE_VL_LEN_MASK) != data->max_vl) { 387 ksft_test_result_fail("%s prctl() set %d but return value is %d\n", 388 data->name, data->max_vl, data->rdvl()); 389 return; 390 } 391 392 /* The _INHERIT flag should not be present when we read the VL */ 393 ret = prctl(data->prctl_get); 394 if (ret == -1) { 395 ksft_test_result_fail("%s prctl() read failed: %d (%s)\n", 396 data->name, errno, strerror(errno)); 397 return; 398 } 399 400 if (ret & PR_SVE_VL_INHERIT) { 401 ksft_test_result_fail("%s prctl() reports _INHERIT\n", 402 data->name); 403 return; 404 } 405 406 ksft_test_result_pass("%s prctl() set min/max\n", data->name); 407} 408 409/* If we didn't request it a new VL shouldn't affect the child */ 410static void prctl_set_no_child(struct vec_data *data) 411{ 412 int ret, child_vl; 413 414 if (data->min_vl == data->max_vl) { 415 ksft_test_result_skip("%s only one VL supported\n", 416 data->name); 417 return; 418 } 419 420 ret = prctl(data->prctl_set, data->min_vl); 421 if (ret < 0) { 422 ksft_test_result_fail("%s prctl set failed for %d: %d (%s)\n", 423 data->name, data->min_vl, 424 errno, strerror(errno)); 425 return; 426 } 427 428 /* Ensure the default VL is different */ 429 ret = file_write_integer(data->default_vl_file, data->max_vl); 430 if (ret != 0) 431 return; 432 433 /* Check that the child has the default we just set */ 434 child_vl = get_child_rdvl(data); 435 if (child_vl != data->max_vl) { 436 ksft_test_result_fail("%s is %d but child VL is %d\n", 437 data->default_vl_file, 438 data->max_vl, child_vl); 439 return; 440 } 441 442 ksft_test_result_pass("%s vector length used default\n", data->name); 443 444 file_write_integer(data->default_vl_file, data->default_vl); 445} 446 447/* If we didn't request it a new VL shouldn't affect the child */ 448static void prctl_set_for_child(struct vec_data *data) 449{ 450 int ret, child_vl; 451 452 if (data->min_vl == data->max_vl) { 453 ksft_test_result_skip("%s only one VL supported\n", 454 data->name); 455 return; 456 } 457 458 ret = prctl(data->prctl_set, data->min_vl | PR_SVE_VL_INHERIT); 459 if (ret < 0) { 460 ksft_test_result_fail("%s prctl set failed for %d: %d (%s)\n", 461 data->name, data->min_vl, 462 errno, strerror(errno)); 463 return; 464 } 465 466 /* The _INHERIT flag should be present when we read the VL */ 467 ret = prctl(data->prctl_get); 468 if (ret == -1) { 469 ksft_test_result_fail("%s prctl() read failed: %d (%s)\n", 470 data->name, errno, strerror(errno)); 471 return; 472 } 473 if (!(ret & PR_SVE_VL_INHERIT)) { 474 ksft_test_result_fail("%s prctl() does not report _INHERIT\n", 475 data->name); 476 return; 477 } 478 479 /* Ensure the default VL is different */ 480 ret = file_write_integer(data->default_vl_file, data->max_vl); 481 if (ret != 0) 482 return; 483 484 /* Check that the child inherited our VL */ 485 child_vl = get_child_rdvl(data); 486 if (child_vl != data->min_vl) { 487 ksft_test_result_fail("%s is %d but child VL is %d\n", 488 data->default_vl_file, 489 data->min_vl, child_vl); 490 return; 491 } 492 493 ksft_test_result_pass("%s vector length was inherited\n", data->name); 494 495 file_write_integer(data->default_vl_file, data->default_vl); 496} 497 498/* _ONEXEC takes effect only in the child process */ 499static void prctl_set_onexec(struct vec_data *data) 500{ 501 int ret, child_vl; 502 503 if (data->min_vl == data->max_vl) { 504 ksft_test_result_skip("%s only one VL supported\n", 505 data->name); 506 return; 507 } 508 509 /* Set a known value for the default and our current VL */ 510 ret = file_write_integer(data->default_vl_file, data->max_vl); 511 if (ret != 0) 512 return; 513 514 ret = prctl(data->prctl_set, data->max_vl); 515 if (ret < 0) { 516 ksft_test_result_fail("%s prctl set failed for %d: %d (%s)\n", 517 data->name, data->min_vl, 518 errno, strerror(errno)); 519 return; 520 } 521 522 /* Set a different value for the child to have on exec */ 523 ret = prctl(data->prctl_set, data->min_vl | PR_SVE_SET_VL_ONEXEC); 524 if (ret < 0) { 525 ksft_test_result_fail("%s prctl set failed for %d: %d (%s)\n", 526 data->name, data->min_vl, 527 errno, strerror(errno)); 528 return; 529 } 530 531 /* Our current VL should stay the same */ 532 if (data->rdvl() != data->max_vl) { 533 ksft_test_result_fail("%s VL changed by _ONEXEC prctl()\n", 534 data->name); 535 return; 536 } 537 538 /* Check that the child inherited our VL */ 539 child_vl = get_child_rdvl(data); 540 if (child_vl != data->min_vl) { 541 ksft_test_result_fail("Set %d _ONEXEC but child VL is %d\n", 542 data->min_vl, child_vl); 543 return; 544 } 545 546 ksft_test_result_pass("%s vector length set on exec\n", data->name); 547 548 file_write_integer(data->default_vl_file, data->default_vl); 549} 550 551/* For each VQ verify that setting via prctl() does the right thing */ 552static void prctl_set_all_vqs(struct vec_data *data) 553{ 554 int ret, vq, vl, new_vl; 555 int errors = 0; 556 557 if (!data->min_vl || !data->max_vl) { 558 ksft_test_result_skip("%s Failed to enumerate VLs, not testing VL setting\n", 559 data->name); 560 return; 561 } 562 563 for (vq = SVE_VQ_MIN; vq <= SVE_VQ_MAX; vq++) { 564 vl = sve_vl_from_vq(vq); 565 566 /* Attempt to set the VL */ 567 ret = prctl(data->prctl_set, vl); 568 if (ret < 0) { 569 errors++; 570 ksft_print_msg("%s prctl set failed for %d: %d (%s)\n", 571 data->name, vl, 572 errno, strerror(errno)); 573 continue; 574 } 575 576 new_vl = ret & PR_SVE_VL_LEN_MASK; 577 578 /* Check that we actually have the reported new VL */ 579 if (data->rdvl() != new_vl) { 580 ksft_print_msg("Set %s VL %d but RDVL reports %d\n", 581 data->name, new_vl, data->rdvl()); 582 errors++; 583 } 584 585 /* Was that the VL we asked for? */ 586 if (new_vl == vl) 587 continue; 588 589 /* Should round up to the minimum VL if below it */ 590 if (vl < data->min_vl) { 591 if (new_vl != data->min_vl) { 592 ksft_print_msg("%s VL %d returned %d not minimum %d\n", 593 data->name, vl, new_vl, 594 data->min_vl); 595 errors++; 596 } 597 598 continue; 599 } 600 601 /* Should round down to maximum VL if above it */ 602 if (vl > data->max_vl) { 603 if (new_vl != data->max_vl) { 604 ksft_print_msg("%s VL %d returned %d not maximum %d\n", 605 data->name, vl, new_vl, 606 data->max_vl); 607 errors++; 608 } 609 610 continue; 611 } 612 613 /* Otherwise we should've rounded down */ 614 if (!(new_vl < vl)) { 615 ksft_print_msg("%s VL %d returned %d, did not round down\n", 616 data->name, vl, new_vl); 617 errors++; 618 619 continue; 620 } 621 } 622 623 ksft_test_result(errors == 0, "%s prctl() set all VLs, %d errors\n", 624 data->name, errors); 625} 626 627typedef void (*test_type)(struct vec_data *); 628 629static const test_type tests[] = { 630 /* 631 * The default/min/max tests must be first and in this order 632 * to provide data for other tests. 633 */ 634 proc_read_default, 635 proc_write_min, 636 proc_write_max, 637 638 prctl_get, 639 prctl_set_same, 640 prctl_set, 641 prctl_set_no_child, 642 prctl_set_for_child, 643 prctl_set_onexec, 644 prctl_set_all_vqs, 645}; 646 647int main(void) 648{ 649 int i, j; 650 651 ksft_print_header(); 652 ksft_set_plan(ARRAY_SIZE(tests) * ARRAY_SIZE(vec_data)); 653 654 for (i = 0; i < ARRAY_SIZE(vec_data); i++) { 655 struct vec_data *data = &vec_data[i]; 656 unsigned long supported; 657 658 supported = getauxval(data->hwcap_type) & data->hwcap; 659 660 for (j = 0; j < ARRAY_SIZE(tests); j++) { 661 if (supported) 662 tests[j](data); 663 else 664 ksft_test_result_skip("%s not supported\n", 665 data->name); 666 } 667 } 668 669 ksft_exit_pass(); 670}