Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Linux/tools/testing/selftests/arm64/abi/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 12 kB image not shown  

Quelle  syscall-abi.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (C) 2021 ARM Limited.
 */


#include <errno.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/auxv.h>
#include <sys/prctl.h>
#include <asm/hwcap.h>
#include <asm/sigcontext.h>
#include <asm/unistd.h>

#include "../../kselftest.h"

#include "syscall-abi.h"

/*
 * The kernel defines a much larger SVE_VQ_MAX than is expressable in
 * the architecture, this creates a *lot* of overhead filling the
 * buffers (especially ZA) on emulated platforms so use the actual
 * architectural maximum instead.
 */

#define ARCH_SVE_VQ_MAX 16

static int default_sme_vl;

static int sve_vl_count;
static unsigned int sve_vls[ARCH_SVE_VQ_MAX];
static int sme_vl_count;
static unsigned int sme_vls[ARCH_SVE_VQ_MAX];

extern void do_syscall(int sve_vl, int sme_vl);

static void fill_random(void *buf, size_t size)
{
 int i;
 uint32_t *lbuf = buf;

 /* random() returns a 32 bit number regardless of the size of long */
 for (i = 0; i < size / sizeof(uint32_t); i++)
  lbuf[i] = random();
}

/*
 * We also repeat the test for several syscalls to try to expose different
 * behaviour.
 */

static struct syscall_cfg {
 int syscall_nr;
 const char *name;
} syscalls[] = {
 { __NR_getpid,  "getpid()" },
 { __NR_sched_yield, "sched_yield()" },
};

#define NUM_GPR 31
uint64_t gpr_in[NUM_GPR];
uint64_t gpr_out[NUM_GPR];

static void setup_gpr(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
        uint64_t svcr)
{
 fill_random(gpr_in, sizeof(gpr_in));
 gpr_in[8] = cfg->syscall_nr;
 memset(gpr_out, 0, sizeof(gpr_out));
}

static int check_gpr(struct syscall_cfg *cfg, int sve_vl, int sme_vl, uint64_t svcr)
{
 int errors = 0;
 int i;

 /*
 * GPR x0-x7 may be clobbered, and all others should be preserved.
 */

 for (i = 9; i < ARRAY_SIZE(gpr_in); i++) {
  if (gpr_in[i] != gpr_out[i]) {
   ksft_print_msg("%s SVE VL %d mismatch in GPR %d: %lx != %lx\n",
           cfg->name, sve_vl, i,
           gpr_in[i], gpr_out[i]);
   errors++;
  }
 }

 return errors;
}

#define NUM_FPR 32
uint64_t fpr_in[NUM_FPR * 2];
uint64_t fpr_out[NUM_FPR * 2];
uint64_t fpr_zero[NUM_FPR * 2];

static void setup_fpr(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
        uint64_t svcr)
{
 fill_random(fpr_in, sizeof(fpr_in));
 memset(fpr_out, 0, sizeof(fpr_out));
}

static int check_fpr(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
       uint64_t svcr)
{
 int errors = 0;
 int i;

 if (!sve_vl && !(svcr & SVCR_SM_MASK)) {
  for (i = 0; i < ARRAY_SIZE(fpr_in); i++) {
   if (fpr_in[i] != fpr_out[i]) {
    ksft_print_msg("%s Q%d/%d mismatch %lx != %lx\n",
            cfg->name,
            i / 2, i % 2,
            fpr_in[i], fpr_out[i]);
    errors++;
   }
  }
 }

 /*
 * In streaming mode the whole register set should be cleared
 * by the transition out of streaming mode.
 */

 if (svcr & SVCR_SM_MASK) {
  if (memcmp(fpr_zero, fpr_out, sizeof(fpr_out)) != 0) {
   ksft_print_msg("%s FPSIMD registers non-zero exiting SM\n",
           cfg->name);
   errors++;
  }
 }

 return errors;
}

#define SVE_Z_SHARED_BYTES (128 / 8)

static uint8_t z_zero[__SVE_ZREG_SIZE(ARCH_SVE_VQ_MAX)];
uint8_t z_in[SVE_NUM_ZREGS * __SVE_ZREG_SIZE(ARCH_SVE_VQ_MAX)];
uint8_t z_out[SVE_NUM_ZREGS * __SVE_ZREG_SIZE(ARCH_SVE_VQ_MAX)];

static void setup_z(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
      uint64_t svcr)
{
 fill_random(z_in, sizeof(z_in));
 fill_random(z_out, sizeof(z_out));
}

static int check_z(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
     uint64_t svcr)
{
 size_t reg_size = sve_vl;
 int errors = 0;
 int i;

 if (!sve_vl)
  return 0;

 for (i = 0; i < SVE_NUM_ZREGS; i++) {
  uint8_t *in = &z_in[reg_size * i];
  uint8_t *out = &z_out[reg_size * i];

  if (svcr & SVCR_SM_MASK) {
   /*
 * In streaming mode the whole register should
 * be cleared by the transition out of
 * streaming mode.
 */

   if (memcmp(z_zero, out, reg_size) != 0) {
    ksft_print_msg("%s SVE VL %d Z%d non-zero\n",
            cfg->name, sve_vl, i);
    errors++;
   }
  } else {
   /*
 * For standard SVE the low 128 bits should be
 * preserved and any additional bits cleared.
 */

   if (memcmp(in, out, SVE_Z_SHARED_BYTES) != 0) {
    ksft_print_msg("%s SVE VL %d Z%d low 128 bits changed\n",
            cfg->name, sve_vl, i);
    errors++;
   }

   if (reg_size > SVE_Z_SHARED_BYTES &&
       (memcmp(z_zero, out + SVE_Z_SHARED_BYTES,
        reg_size - SVE_Z_SHARED_BYTES) != 0)) {
    ksft_print_msg("%s SVE VL %d Z%d high bits non-zero\n",
            cfg->name, sve_vl, i);
    errors++;
   }
  }
 }

 return errors;
}

uint8_t p_in[SVE_NUM_PREGS * __SVE_PREG_SIZE(ARCH_SVE_VQ_MAX)];
uint8_t p_out[SVE_NUM_PREGS * __SVE_PREG_SIZE(ARCH_SVE_VQ_MAX)];

static void setup_p(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
      uint64_t svcr)
{
 fill_random(p_in, sizeof(p_in));
 fill_random(p_out, sizeof(p_out));
}

static int check_p(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
     uint64_t svcr)
{
 size_t reg_size = sve_vq_from_vl(sve_vl) * 2; /* 1 bit per VL byte */

 int errors = 0;
 int i;

 if (!sve_vl)
  return 0;

 /* After a syscall the P registers should be zeroed */
 for (i = 0; i < SVE_NUM_PREGS * reg_size; i++)
  if (p_out[i])
   errors++;
 if (errors)
  ksft_print_msg("%s SVE VL %d predicate registers non-zero\n",
          cfg->name, sve_vl);

 return errors;
}

uint8_t ffr_in[__SVE_PREG_SIZE(ARCH_SVE_VQ_MAX)];
uint8_t ffr_out[__SVE_PREG_SIZE(ARCH_SVE_VQ_MAX)];

static void setup_ffr(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
        uint64_t svcr)
{
 /*
 * If we are in streaming mode and do not have FA64 then FFR
 * is unavailable.
 */

 if ((svcr & SVCR_SM_MASK) &&
     !(getauxval(AT_HWCAP2) & HWCAP2_SME_FA64)) {
  memset(&ffr_in, 0, sizeof(ffr_in));
  return;
 }

 /*
 * It is only valid to set a contiguous set of bits starting
 * at 0.  For now since we're expecting this to be cleared by
 * a syscall just set all bits.
 */

 memset(ffr_in, 0xff, sizeof(ffr_in));
 fill_random(ffr_out, sizeof(ffr_out));
}

static int check_ffr(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
       uint64_t svcr)
{
 size_t reg_size = sve_vq_from_vl(sve_vl) * 2;  /* 1 bit per VL byte */
 int errors = 0;
 int i;

 if (!sve_vl)
  return 0;

 if ((svcr & SVCR_SM_MASK) &&
     !(getauxval(AT_HWCAP2) & HWCAP2_SME_FA64))
  return 0;

 /* After a syscall FFR should be zeroed */
 for (i = 0; i < reg_size; i++)
  if (ffr_out[i])
   errors++;
 if (errors)
  ksft_print_msg("%s SVE VL %d FFR non-zero\n",
          cfg->name, sve_vl);

 return errors;
}

uint64_t svcr_in, svcr_out;

static void setup_svcr(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
      uint64_t svcr)
{
 svcr_in = svcr;
}

static int check_svcr(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
        uint64_t svcr)
{
 int errors = 0;

 if (svcr_out & SVCR_SM_MASK) {
  ksft_print_msg("%s Still in SM, SVCR %lx\n",
          cfg->name, svcr_out);
  errors++;
 }

 if ((svcr_in & SVCR_ZA_MASK) != (svcr_out & SVCR_ZA_MASK)) {
  ksft_print_msg("%s PSTATE.ZA changed, SVCR %lx != %lx\n",
          cfg->name, svcr_in, svcr_out);
  errors++;
 }

 return errors;
}

uint8_t za_in[ZA_SIG_REGS_SIZE(ARCH_SVE_VQ_MAX)];
uint8_t za_out[ZA_SIG_REGS_SIZE(ARCH_SVE_VQ_MAX)];

static void setup_za(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
       uint64_t svcr)
{
 fill_random(za_in, sizeof(za_in));
 memset(za_out, 0, sizeof(za_out));
}

static int check_za(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
      uint64_t svcr)
{
 size_t reg_size = sme_vl * sme_vl;
 int errors = 0;

 if (!(svcr & SVCR_ZA_MASK))
  return 0;

 if (memcmp(za_in, za_out, reg_size) != 0) {
  ksft_print_msg("SME VL %d ZA does not match\n", sme_vl);
  errors++;
 }

 return errors;
}

uint8_t zt_in[ZT_SIG_REG_BYTES] __attribute__((aligned(16)));
uint8_t zt_out[ZT_SIG_REG_BYTES] __attribute__((aligned(16)));

static void setup_zt(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
       uint64_t svcr)
{
 fill_random(zt_in, sizeof(zt_in));
 memset(zt_out, 0, sizeof(zt_out));
}

static int check_zt(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
      uint64_t svcr)
{
 int errors = 0;

 if (!(getauxval(AT_HWCAP2) & HWCAP2_SME2))
  return 0;

 if (!(svcr & SVCR_ZA_MASK))
  return 0;

 if (memcmp(zt_in, zt_out, sizeof(zt_in)) != 0) {
  ksft_print_msg("SME VL %d ZT does not match\n", sme_vl);
  errors++;
 }

 return errors;
}

typedef void (*setup_fn)(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
    uint64_t svcr);
typedef int (*check_fn)(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
   uint64_t svcr);

/*
 * Each set of registers has a setup function which is called before
 * the syscall to fill values in a global variable for loading by the
 * test code and a check function which validates that the results are
 * as expected.  Vector lengths are passed everywhere, a vector length
 * of 0 should be treated as do not test.
 */

static struct {
 setup_fn setup;
 check_fn check;
} regset[] = {
 { setup_gpr, check_gpr },
 { setup_fpr, check_fpr },
 { setup_z, check_z },
 { setup_p, check_p },
 { setup_ffr, check_ffr },
 { setup_svcr, check_svcr },
 { setup_za, check_za },
 { setup_zt, check_zt },
};

static bool do_test(struct syscall_cfg *cfg, int sve_vl, int sme_vl,
      uint64_t svcr)
{
 int errors = 0;
 int i;

 for (i = 0; i < ARRAY_SIZE(regset); i++)
  regset[i].setup(cfg, sve_vl, sme_vl, svcr);

 do_syscall(sve_vl, sme_vl);

 for (i = 0; i < ARRAY_SIZE(regset); i++)
  errors += regset[i].check(cfg, sve_vl, sme_vl, svcr);

 return errors == 0;
}

static void test_one_syscall(struct syscall_cfg *cfg)
{
 int sve, sme;
 int ret;

 /* FPSIMD only case */
 ksft_test_result(do_test(cfg, 0, default_sme_vl, 0),
    "%s FPSIMD\n", cfg->name);

 for (sve = 0; sve < sve_vl_count; sve++) {
  ret = prctl(PR_SVE_SET_VL, sve_vls[sve]);
  if (ret == -1)
   ksft_exit_fail_msg("PR_SVE_SET_VL failed: %s (%d)\n",
        strerror(errno), errno);

  ksft_test_result(do_test(cfg, sve_vls[sve], default_sme_vl, 0),
     "%s SVE VL %d\n", cfg->name, sve_vls[sve]);

  for (sme = 0; sme < sme_vl_count; sme++) {
   ret = prctl(PR_SME_SET_VL, sme_vls[sme]);
   if (ret == -1)
    ksft_exit_fail_msg("PR_SME_SET_VL failed: %s (%d)\n",
         strerror(errno), errno);

   ksft_test_result(do_test(cfg, sve_vls[sve],
       sme_vls[sme],
       SVCR_ZA_MASK | SVCR_SM_MASK),
      "%s SVE VL %d/SME VL %d SM+ZA\n",
      cfg->name, sve_vls[sve],
      sme_vls[sme]);
   ksft_test_result(do_test(cfg, sve_vls[sve],
       sme_vls[sme], SVCR_SM_MASK),
      "%s SVE VL %d/SME VL %d SM\n",
      cfg->name, sve_vls[sve],
      sme_vls[sme]);
   ksft_test_result(do_test(cfg, sve_vls[sve],
       sme_vls[sme], SVCR_ZA_MASK),
      "%s SVE VL %d/SME VL %d ZA\n",
      cfg->name, sve_vls[sve],
      sme_vls[sme]);
  }
 }

 for (sme = 0; sme < sme_vl_count; sme++) {
  ret = prctl(PR_SME_SET_VL, sme_vls[sme]);
  if (ret == -1)
   ksft_exit_fail_msg("PR_SME_SET_VL failed: %s (%d)\n",
         strerror(errno), errno);

  ksft_test_result(do_test(cfg, 0, sme_vls[sme],
      SVCR_ZA_MASK | SVCR_SM_MASK),
     "%s SME VL %d SM+ZA\n",
     cfg->name, sme_vls[sme]);
  ksft_test_result(do_test(cfg, 0, sme_vls[sme], SVCR_SM_MASK),
     "%s SME VL %d SM\n",
     cfg->name, sme_vls[sme]);
  ksft_test_result(do_test(cfg, 0, sme_vls[sme], SVCR_ZA_MASK),
     "%s SME VL %d ZA\n",
     cfg->name, sme_vls[sme]);
 }
}

void sve_count_vls(void)
{
 unsigned int vq;
 int vl;

 if (!(getauxval(AT_HWCAP) & HWCAP_SVE))
  return;

 /*
 * Enumerate up to ARCH_SVE_VQ_MAX vector lengths
 */

 for (vq = ARCH_SVE_VQ_MAX; vq > 0; vq /= 2) {
  vl = prctl(PR_SVE_SET_VL, vq * 16);
  if (vl == -1)
   ksft_exit_fail_msg("PR_SVE_SET_VL failed: %s (%d)\n",
        strerror(errno), errno);

  vl &= PR_SVE_VL_LEN_MASK;

  if (vq != sve_vq_from_vl(vl))
   vq = sve_vq_from_vl(vl);

  sve_vls[sve_vl_count++] = vl;
 }
}

void sme_count_vls(void)
{
 unsigned int vq;
 int vl;

 if (!(getauxval(AT_HWCAP2) & HWCAP2_SME))
  return;

 /*
 * Enumerate up to ARCH_SVE_VQ_MAX vector lengths
 */

 for (vq = ARCH_SVE_VQ_MAX; vq > 0; vq /= 2) {
  vl = prctl(PR_SME_SET_VL, vq * 16);
  if (vl == -1)
   ksft_exit_fail_msg("PR_SME_SET_VL failed: %s (%d)\n",
        strerror(errno), errno);

  vl &= PR_SME_VL_LEN_MASK;

  /* Found lowest VL */
  if (sve_vq_from_vl(vl) > vq)
   break;

  if (vq != sve_vq_from_vl(vl))
   vq = sve_vq_from_vl(vl);

  sme_vls[sme_vl_count++] = vl;
 }

 /* Ensure we configure a SME VL, used to flag if SVCR is set */
 default_sme_vl = sme_vls[0];
}

int main(void)
{
 int i;
 int tests = 1;  /* FPSIMD */
 int sme_ver;

 srandom(getpid());

 ksft_print_header();

 sve_count_vls();
 sme_count_vls();

 tests += sve_vl_count;
 tests += sme_vl_count * 3;
 tests += (sve_vl_count * sme_vl_count) * 3;
 ksft_set_plan(ARRAY_SIZE(syscalls) * tests);

 if (getauxval(AT_HWCAP2) & HWCAP2_SME2)
  sme_ver = 2;
 else
  sme_ver = 1;

 if (getauxval(AT_HWCAP2) & HWCAP2_SME_FA64)
  ksft_print_msg("SME%d with FA64\n", sme_ver);
 else if (getauxval(AT_HWCAP2) & HWCAP2_SME)
  ksft_print_msg("SME%d without FA64\n", sme_ver);

 for (i = 0; i < ARRAY_SIZE(syscalls); i++)
  test_one_syscall(&syscalls[i]);

 ksft_print_cnts();

 return 0;
}

Messung V0.5
C=94 H=94 G=93

¤ Dauer der Verarbeitung: 0.25 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

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.