linux/tools/testing/selftests/arm64/fp/vec-syscfg.c
<<
>>
Prefs
   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}
 671