linux/tools/testing/selftests/arm64/abi/syscall-abi.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (C) 2021 ARM Limited.
   4 */
   5
   6#include <errno.h>
   7#include <stdbool.h>
   8#include <stddef.h>
   9#include <stdio.h>
  10#include <stdlib.h>
  11#include <string.h>
  12#include <unistd.h>
  13#include <sys/auxv.h>
  14#include <sys/prctl.h>
  15#include <asm/hwcap.h>
  16#include <asm/sigcontext.h>
  17#include <asm/unistd.h>
  18
  19#include "../../kselftest.h"
  20
  21#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
  22#define NUM_VL ((SVE_VQ_MAX - SVE_VQ_MIN) + 1)
  23
  24extern void do_syscall(int sve_vl);
  25
  26static void fill_random(void *buf, size_t size)
  27{
  28        int i;
  29        uint32_t *lbuf = buf;
  30
  31        /* random() returns a 32 bit number regardless of the size of long */
  32        for (i = 0; i < size / sizeof(uint32_t); i++)
  33                lbuf[i] = random();
  34}
  35
  36/*
  37 * We also repeat the test for several syscalls to try to expose different
  38 * behaviour.
  39 */
  40static struct syscall_cfg {
  41        int syscall_nr;
  42        const char *name;
  43} syscalls[] = {
  44        { __NR_getpid,          "getpid()" },
  45        { __NR_sched_yield,     "sched_yield()" },
  46};
  47
  48#define NUM_GPR 31
  49uint64_t gpr_in[NUM_GPR];
  50uint64_t gpr_out[NUM_GPR];
  51
  52static void setup_gpr(struct syscall_cfg *cfg, int sve_vl)
  53{
  54        fill_random(gpr_in, sizeof(gpr_in));
  55        gpr_in[8] = cfg->syscall_nr;
  56        memset(gpr_out, 0, sizeof(gpr_out));
  57}
  58
  59static int check_gpr(struct syscall_cfg *cfg, int sve_vl)
  60{
  61        int errors = 0;
  62        int i;
  63
  64        /*
  65         * GPR x0-x7 may be clobbered, and all others should be preserved.
  66         */
  67        for (i = 9; i < ARRAY_SIZE(gpr_in); i++) {
  68                if (gpr_in[i] != gpr_out[i]) {
  69                        ksft_print_msg("%s SVE VL %d mismatch in GPR %d: %llx != %llx\n",
  70                                       cfg->name, sve_vl, i,
  71                                       gpr_in[i], gpr_out[i]);
  72                        errors++;
  73                }
  74        }
  75
  76        return errors;
  77}
  78
  79#define NUM_FPR 32
  80uint64_t fpr_in[NUM_FPR * 2];
  81uint64_t fpr_out[NUM_FPR * 2];
  82
  83static void setup_fpr(struct syscall_cfg *cfg, int sve_vl)
  84{
  85        fill_random(fpr_in, sizeof(fpr_in));
  86        memset(fpr_out, 0, sizeof(fpr_out));
  87}
  88
  89static int check_fpr(struct syscall_cfg *cfg, int sve_vl)
  90{
  91        int errors = 0;
  92        int i;
  93
  94        if (!sve_vl) {
  95                for (i = 0; i < ARRAY_SIZE(fpr_in); i++) {
  96                        if (fpr_in[i] != fpr_out[i]) {
  97                                ksft_print_msg("%s Q%d/%d mismatch %llx != %llx\n",
  98                                               cfg->name,
  99                                               i / 2, i % 2,
 100                                               fpr_in[i], fpr_out[i]);
 101                                errors++;
 102                        }
 103                }
 104        }
 105
 106        return errors;
 107}
 108
 109static uint8_t z_zero[__SVE_ZREG_SIZE(SVE_VQ_MAX)];
 110uint8_t z_in[SVE_NUM_PREGS * __SVE_ZREG_SIZE(SVE_VQ_MAX)];
 111uint8_t z_out[SVE_NUM_PREGS * __SVE_ZREG_SIZE(SVE_VQ_MAX)];
 112
 113static void setup_z(struct syscall_cfg *cfg, int sve_vl)
 114{
 115        fill_random(z_in, sizeof(z_in));
 116        fill_random(z_out, sizeof(z_out));
 117}
 118
 119static int check_z(struct syscall_cfg *cfg, int sve_vl)
 120{
 121        size_t reg_size = sve_vl;
 122        int errors = 0;
 123        int i;
 124
 125        if (!sve_vl)
 126                return 0;
 127
 128        /*
 129         * After a syscall the low 128 bits of the Z registers should
 130         * be preserved and the rest be zeroed or preserved.
 131         */
 132        for (i = 0; i < SVE_NUM_ZREGS; i++) {
 133                void *in = &z_in[reg_size * i];
 134                void *out = &z_out[reg_size * i];
 135
 136                if (memcmp(in, out, SVE_VQ_BYTES) != 0) {
 137                        ksft_print_msg("%s SVE VL %d Z%d low 128 bits changed\n",
 138                                       cfg->name, sve_vl, i);
 139                        errors++;
 140                }
 141        }
 142
 143        return errors;
 144}
 145
 146uint8_t p_in[SVE_NUM_PREGS * __SVE_PREG_SIZE(SVE_VQ_MAX)];
 147uint8_t p_out[SVE_NUM_PREGS * __SVE_PREG_SIZE(SVE_VQ_MAX)];
 148
 149static void setup_p(struct syscall_cfg *cfg, int sve_vl)
 150{
 151        fill_random(p_in, sizeof(p_in));
 152        fill_random(p_out, sizeof(p_out));
 153}
 154
 155static int check_p(struct syscall_cfg *cfg, int sve_vl)
 156{
 157        size_t reg_size = sve_vq_from_vl(sve_vl) * 2; /* 1 bit per VL byte */
 158
 159        int errors = 0;
 160        int i;
 161
 162        if (!sve_vl)
 163                return 0;
 164
 165        /* After a syscall the P registers should be preserved or zeroed */
 166        for (i = 0; i < SVE_NUM_PREGS * reg_size; i++)
 167                if (p_out[i] && (p_in[i] != p_out[i]))
 168                        errors++;
 169        if (errors)
 170                ksft_print_msg("%s SVE VL %d predicate registers non-zero\n",
 171                               cfg->name, sve_vl);
 172
 173        return errors;
 174}
 175
 176uint8_t ffr_in[__SVE_PREG_SIZE(SVE_VQ_MAX)];
 177uint8_t ffr_out[__SVE_PREG_SIZE(SVE_VQ_MAX)];
 178
 179static void setup_ffr(struct syscall_cfg *cfg, int sve_vl)
 180{
 181        /*
 182         * It is only valid to set a contiguous set of bits starting
 183         * at 0.  For now since we're expecting this to be cleared by
 184         * a syscall just set all bits.
 185         */
 186        memset(ffr_in, 0xff, sizeof(ffr_in));
 187        fill_random(ffr_out, sizeof(ffr_out));
 188}
 189
 190static int check_ffr(struct syscall_cfg *cfg, int sve_vl)
 191{
 192        size_t reg_size = sve_vq_from_vl(sve_vl) * 2;  /* 1 bit per VL byte */
 193        int errors = 0;
 194        int i;
 195
 196        if (!sve_vl)
 197                return 0;
 198
 199        /* After a syscall the P registers should be preserved or zeroed */
 200        for (i = 0; i < reg_size; i++)
 201                if (ffr_out[i] && (ffr_in[i] != ffr_out[i]))
 202                        errors++;
 203        if (errors)
 204                ksft_print_msg("%s SVE VL %d FFR non-zero\n",
 205                               cfg->name, sve_vl);
 206
 207        return errors;
 208}
 209
 210typedef void (*setup_fn)(struct syscall_cfg *cfg, int sve_vl);
 211typedef int (*check_fn)(struct syscall_cfg *cfg, int sve_vl);
 212
 213/*
 214 * Each set of registers has a setup function which is called before
 215 * the syscall to fill values in a global variable for loading by the
 216 * test code and a check function which validates that the results are
 217 * as expected.  Vector lengths are passed everywhere, a vector length
 218 * of 0 should be treated as do not test.
 219 */
 220static struct {
 221        setup_fn setup;
 222        check_fn check;
 223} regset[] = {
 224        { setup_gpr, check_gpr },
 225        { setup_fpr, check_fpr },
 226        { setup_z, check_z },
 227        { setup_p, check_p },
 228        { setup_ffr, check_ffr },
 229};
 230
 231static bool do_test(struct syscall_cfg *cfg, int sve_vl)
 232{
 233        int errors = 0;
 234        int i;
 235
 236        for (i = 0; i < ARRAY_SIZE(regset); i++)
 237                regset[i].setup(cfg, sve_vl);
 238
 239        do_syscall(sve_vl);
 240
 241        for (i = 0; i < ARRAY_SIZE(regset); i++)
 242                errors += regset[i].check(cfg, sve_vl);
 243
 244        return errors == 0;
 245}
 246
 247static void test_one_syscall(struct syscall_cfg *cfg)
 248{
 249        int sve_vq, sve_vl;
 250
 251        /* FPSIMD only case */
 252        ksft_test_result(do_test(cfg, 0),
 253                         "%s FPSIMD\n", cfg->name);
 254
 255        if (!(getauxval(AT_HWCAP) & HWCAP_SVE))
 256                return;
 257
 258        for (sve_vq = SVE_VQ_MAX; sve_vq > 0; --sve_vq) {
 259                sve_vl = prctl(PR_SVE_SET_VL, sve_vq * 16);
 260                if (sve_vl == -1)
 261                        ksft_exit_fail_msg("PR_SVE_SET_VL failed: %s (%d)\n",
 262                                           strerror(errno), errno);
 263
 264                sve_vl &= PR_SVE_VL_LEN_MASK;
 265
 266                if (sve_vq != sve_vq_from_vl(sve_vl))
 267                        sve_vq = sve_vq_from_vl(sve_vl);
 268
 269                ksft_test_result(do_test(cfg, sve_vl),
 270                                 "%s SVE VL %d\n", cfg->name, sve_vl);
 271        }
 272}
 273
 274int sve_count_vls(void)
 275{
 276        unsigned int vq;
 277        int vl_count = 0;
 278        int vl;
 279
 280        if (!(getauxval(AT_HWCAP) & HWCAP_SVE))
 281                return 0;
 282
 283        /*
 284         * Enumerate up to SVE_VQ_MAX vector lengths
 285         */
 286        for (vq = SVE_VQ_MAX; vq > 0; --vq) {
 287                vl = prctl(PR_SVE_SET_VL, vq * 16);
 288                if (vl == -1)
 289                        ksft_exit_fail_msg("PR_SVE_SET_VL failed: %s (%d)\n",
 290                                           strerror(errno), errno);
 291
 292                vl &= PR_SVE_VL_LEN_MASK;
 293
 294                if (vq != sve_vq_from_vl(vl))
 295                        vq = sve_vq_from_vl(vl);
 296
 297                vl_count++;
 298        }
 299
 300        return vl_count;
 301}
 302
 303int main(void)
 304{
 305        int i;
 306
 307        srandom(getpid());
 308
 309        ksft_print_header();
 310        ksft_set_plan(ARRAY_SIZE(syscalls) * (sve_count_vls() + 1));
 311
 312        for (i = 0; i < ARRAY_SIZE(syscalls); i++)
 313                test_one_syscall(&syscalls[i]);
 314
 315        ksft_print_cnts();
 316
 317        return 0;
 318}
 319