Path: blob/master/tools/testing/selftests/arm64/abi/syscall-abi.c
26289 views
// SPDX-License-Identifier: GPL-2.0-only1/*2* Copyright (C) 2021 ARM Limited.3*/45#include <errno.h>6#include <stdbool.h>7#include <stddef.h>8#include <stdio.h>9#include <stdlib.h>10#include <string.h>11#include <unistd.h>12#include <sys/auxv.h>13#include <sys/prctl.h>14#include <asm/hwcap.h>15#include <asm/sigcontext.h>16#include <asm/unistd.h>1718#include "../../kselftest.h"1920#include "syscall-abi.h"2122/*23* The kernel defines a much larger SVE_VQ_MAX than is expressable in24* the architecture, this creates a *lot* of overhead filling the25* buffers (especially ZA) on emulated platforms so use the actual26* architectural maximum instead.27*/28#define ARCH_SVE_VQ_MAX 162930static int default_sme_vl;3132static int sve_vl_count;33static unsigned int sve_vls[ARCH_SVE_VQ_MAX];34static int sme_vl_count;35static unsigned int sme_vls[ARCH_SVE_VQ_MAX];3637extern void do_syscall(int sve_vl, int sme_vl);3839static void fill_random(void *buf, size_t size)40{41int i;42uint32_t *lbuf = buf;4344/* random() returns a 32 bit number regardless of the size of long */45for (i = 0; i < size / sizeof(uint32_t); i++)46lbuf[i] = random();47}4849/*50* We also repeat the test for several syscalls to try to expose different51* behaviour.52*/53static struct syscall_cfg {54int syscall_nr;55const char *name;56} syscalls[] = {57{ __NR_getpid, "getpid()" },58{ __NR_sched_yield, "sched_yield()" },59};6061#define NUM_GPR 3162uint64_t gpr_in[NUM_GPR];63uint64_t gpr_out[NUM_GPR];6465static void setup_gpr(struct syscall_cfg *cfg, int sve_vl, int sme_vl,66uint64_t svcr)67{68fill_random(gpr_in, sizeof(gpr_in));69gpr_in[8] = cfg->syscall_nr;70memset(gpr_out, 0, sizeof(gpr_out));71}7273static int check_gpr(struct syscall_cfg *cfg, int sve_vl, int sme_vl, uint64_t svcr)74{75int errors = 0;76int i;7778/*79* GPR x0-x7 may be clobbered, and all others should be preserved.80*/81for (i = 9; i < ARRAY_SIZE(gpr_in); i++) {82if (gpr_in[i] != gpr_out[i]) {83ksft_print_msg("%s SVE VL %d mismatch in GPR %d: %lx != %lx\n",84cfg->name, sve_vl, i,85gpr_in[i], gpr_out[i]);86errors++;87}88}8990return errors;91}9293#define NUM_FPR 3294uint64_t fpr_in[NUM_FPR * 2];95uint64_t fpr_out[NUM_FPR * 2];96uint64_t fpr_zero[NUM_FPR * 2];9798static void setup_fpr(struct syscall_cfg *cfg, int sve_vl, int sme_vl,99uint64_t svcr)100{101fill_random(fpr_in, sizeof(fpr_in));102memset(fpr_out, 0, sizeof(fpr_out));103}104105static int check_fpr(struct syscall_cfg *cfg, int sve_vl, int sme_vl,106uint64_t svcr)107{108int errors = 0;109int i;110111if (!sve_vl && !(svcr & SVCR_SM_MASK)) {112for (i = 0; i < ARRAY_SIZE(fpr_in); i++) {113if (fpr_in[i] != fpr_out[i]) {114ksft_print_msg("%s Q%d/%d mismatch %lx != %lx\n",115cfg->name,116i / 2, i % 2,117fpr_in[i], fpr_out[i]);118errors++;119}120}121}122123/*124* In streaming mode the whole register set should be cleared125* by the transition out of streaming mode.126*/127if (svcr & SVCR_SM_MASK) {128if (memcmp(fpr_zero, fpr_out, sizeof(fpr_out)) != 0) {129ksft_print_msg("%s FPSIMD registers non-zero exiting SM\n",130cfg->name);131errors++;132}133}134135return errors;136}137138#define SVE_Z_SHARED_BYTES (128 / 8)139140static uint8_t z_zero[__SVE_ZREG_SIZE(ARCH_SVE_VQ_MAX)];141uint8_t z_in[SVE_NUM_ZREGS * __SVE_ZREG_SIZE(ARCH_SVE_VQ_MAX)];142uint8_t z_out[SVE_NUM_ZREGS * __SVE_ZREG_SIZE(ARCH_SVE_VQ_MAX)];143144static void setup_z(struct syscall_cfg *cfg, int sve_vl, int sme_vl,145uint64_t svcr)146{147fill_random(z_in, sizeof(z_in));148fill_random(z_out, sizeof(z_out));149}150151static int check_z(struct syscall_cfg *cfg, int sve_vl, int sme_vl,152uint64_t svcr)153{154size_t reg_size = sve_vl;155int errors = 0;156int i;157158if (!sve_vl)159return 0;160161for (i = 0; i < SVE_NUM_ZREGS; i++) {162uint8_t *in = &z_in[reg_size * i];163uint8_t *out = &z_out[reg_size * i];164165if (svcr & SVCR_SM_MASK) {166/*167* In streaming mode the whole register should168* be cleared by the transition out of169* streaming mode.170*/171if (memcmp(z_zero, out, reg_size) != 0) {172ksft_print_msg("%s SVE VL %d Z%d non-zero\n",173cfg->name, sve_vl, i);174errors++;175}176} else {177/*178* For standard SVE the low 128 bits should be179* preserved and any additional bits cleared.180*/181if (memcmp(in, out, SVE_Z_SHARED_BYTES) != 0) {182ksft_print_msg("%s SVE VL %d Z%d low 128 bits changed\n",183cfg->name, sve_vl, i);184errors++;185}186187if (reg_size > SVE_Z_SHARED_BYTES &&188(memcmp(z_zero, out + SVE_Z_SHARED_BYTES,189reg_size - SVE_Z_SHARED_BYTES) != 0)) {190ksft_print_msg("%s SVE VL %d Z%d high bits non-zero\n",191cfg->name, sve_vl, i);192errors++;193}194}195}196197return errors;198}199200uint8_t p_in[SVE_NUM_PREGS * __SVE_PREG_SIZE(ARCH_SVE_VQ_MAX)];201uint8_t p_out[SVE_NUM_PREGS * __SVE_PREG_SIZE(ARCH_SVE_VQ_MAX)];202203static void setup_p(struct syscall_cfg *cfg, int sve_vl, int sme_vl,204uint64_t svcr)205{206fill_random(p_in, sizeof(p_in));207fill_random(p_out, sizeof(p_out));208}209210static int check_p(struct syscall_cfg *cfg, int sve_vl, int sme_vl,211uint64_t svcr)212{213size_t reg_size = sve_vq_from_vl(sve_vl) * 2; /* 1 bit per VL byte */214215int errors = 0;216int i;217218if (!sve_vl)219return 0;220221/* After a syscall the P registers should be zeroed */222for (i = 0; i < SVE_NUM_PREGS * reg_size; i++)223if (p_out[i])224errors++;225if (errors)226ksft_print_msg("%s SVE VL %d predicate registers non-zero\n",227cfg->name, sve_vl);228229return errors;230}231232uint8_t ffr_in[__SVE_PREG_SIZE(ARCH_SVE_VQ_MAX)];233uint8_t ffr_out[__SVE_PREG_SIZE(ARCH_SVE_VQ_MAX)];234235static void setup_ffr(struct syscall_cfg *cfg, int sve_vl, int sme_vl,236uint64_t svcr)237{238/*239* If we are in streaming mode and do not have FA64 then FFR240* is unavailable.241*/242if ((svcr & SVCR_SM_MASK) &&243!(getauxval(AT_HWCAP2) & HWCAP2_SME_FA64)) {244memset(&ffr_in, 0, sizeof(ffr_in));245return;246}247248/*249* It is only valid to set a contiguous set of bits starting250* at 0. For now since we're expecting this to be cleared by251* a syscall just set all bits.252*/253memset(ffr_in, 0xff, sizeof(ffr_in));254fill_random(ffr_out, sizeof(ffr_out));255}256257static int check_ffr(struct syscall_cfg *cfg, int sve_vl, int sme_vl,258uint64_t svcr)259{260size_t reg_size = sve_vq_from_vl(sve_vl) * 2; /* 1 bit per VL byte */261int errors = 0;262int i;263264if (!sve_vl)265return 0;266267if ((svcr & SVCR_SM_MASK) &&268!(getauxval(AT_HWCAP2) & HWCAP2_SME_FA64))269return 0;270271/* After a syscall FFR should be zeroed */272for (i = 0; i < reg_size; i++)273if (ffr_out[i])274errors++;275if (errors)276ksft_print_msg("%s SVE VL %d FFR non-zero\n",277cfg->name, sve_vl);278279return errors;280}281282uint64_t svcr_in, svcr_out;283284static void setup_svcr(struct syscall_cfg *cfg, int sve_vl, int sme_vl,285uint64_t svcr)286{287svcr_in = svcr;288}289290static int check_svcr(struct syscall_cfg *cfg, int sve_vl, int sme_vl,291uint64_t svcr)292{293int errors = 0;294295if (svcr_out & SVCR_SM_MASK) {296ksft_print_msg("%s Still in SM, SVCR %lx\n",297cfg->name, svcr_out);298errors++;299}300301if ((svcr_in & SVCR_ZA_MASK) != (svcr_out & SVCR_ZA_MASK)) {302ksft_print_msg("%s PSTATE.ZA changed, SVCR %lx != %lx\n",303cfg->name, svcr_in, svcr_out);304errors++;305}306307return errors;308}309310uint8_t za_in[ZA_SIG_REGS_SIZE(ARCH_SVE_VQ_MAX)];311uint8_t za_out[ZA_SIG_REGS_SIZE(ARCH_SVE_VQ_MAX)];312313static void setup_za(struct syscall_cfg *cfg, int sve_vl, int sme_vl,314uint64_t svcr)315{316fill_random(za_in, sizeof(za_in));317memset(za_out, 0, sizeof(za_out));318}319320static int check_za(struct syscall_cfg *cfg, int sve_vl, int sme_vl,321uint64_t svcr)322{323size_t reg_size = sme_vl * sme_vl;324int errors = 0;325326if (!(svcr & SVCR_ZA_MASK))327return 0;328329if (memcmp(za_in, za_out, reg_size) != 0) {330ksft_print_msg("SME VL %d ZA does not match\n", sme_vl);331errors++;332}333334return errors;335}336337uint8_t zt_in[ZT_SIG_REG_BYTES] __attribute__((aligned(16)));338uint8_t zt_out[ZT_SIG_REG_BYTES] __attribute__((aligned(16)));339340static void setup_zt(struct syscall_cfg *cfg, int sve_vl, int sme_vl,341uint64_t svcr)342{343fill_random(zt_in, sizeof(zt_in));344memset(zt_out, 0, sizeof(zt_out));345}346347static int check_zt(struct syscall_cfg *cfg, int sve_vl, int sme_vl,348uint64_t svcr)349{350int errors = 0;351352if (!(getauxval(AT_HWCAP2) & HWCAP2_SME2))353return 0;354355if (!(svcr & SVCR_ZA_MASK))356return 0;357358if (memcmp(zt_in, zt_out, sizeof(zt_in)) != 0) {359ksft_print_msg("SME VL %d ZT does not match\n", sme_vl);360errors++;361}362363return errors;364}365366typedef void (*setup_fn)(struct syscall_cfg *cfg, int sve_vl, int sme_vl,367uint64_t svcr);368typedef int (*check_fn)(struct syscall_cfg *cfg, int sve_vl, int sme_vl,369uint64_t svcr);370371/*372* Each set of registers has a setup function which is called before373* the syscall to fill values in a global variable for loading by the374* test code and a check function which validates that the results are375* as expected. Vector lengths are passed everywhere, a vector length376* of 0 should be treated as do not test.377*/378static struct {379setup_fn setup;380check_fn check;381} regset[] = {382{ setup_gpr, check_gpr },383{ setup_fpr, check_fpr },384{ setup_z, check_z },385{ setup_p, check_p },386{ setup_ffr, check_ffr },387{ setup_svcr, check_svcr },388{ setup_za, check_za },389{ setup_zt, check_zt },390};391392static bool do_test(struct syscall_cfg *cfg, int sve_vl, int sme_vl,393uint64_t svcr)394{395int errors = 0;396int i;397398for (i = 0; i < ARRAY_SIZE(regset); i++)399regset[i].setup(cfg, sve_vl, sme_vl, svcr);400401do_syscall(sve_vl, sme_vl);402403for (i = 0; i < ARRAY_SIZE(regset); i++)404errors += regset[i].check(cfg, sve_vl, sme_vl, svcr);405406return errors == 0;407}408409static void test_one_syscall(struct syscall_cfg *cfg)410{411int sve, sme;412int ret;413414/* FPSIMD only case */415ksft_test_result(do_test(cfg, 0, default_sme_vl, 0),416"%s FPSIMD\n", cfg->name);417418for (sve = 0; sve < sve_vl_count; sve++) {419ret = prctl(PR_SVE_SET_VL, sve_vls[sve]);420if (ret == -1)421ksft_exit_fail_msg("PR_SVE_SET_VL failed: %s (%d)\n",422strerror(errno), errno);423424ksft_test_result(do_test(cfg, sve_vls[sve], default_sme_vl, 0),425"%s SVE VL %d\n", cfg->name, sve_vls[sve]);426427for (sme = 0; sme < sme_vl_count; sme++) {428ret = prctl(PR_SME_SET_VL, sme_vls[sme]);429if (ret == -1)430ksft_exit_fail_msg("PR_SME_SET_VL failed: %s (%d)\n",431strerror(errno), errno);432433ksft_test_result(do_test(cfg, sve_vls[sve],434sme_vls[sme],435SVCR_ZA_MASK | SVCR_SM_MASK),436"%s SVE VL %d/SME VL %d SM+ZA\n",437cfg->name, sve_vls[sve],438sme_vls[sme]);439ksft_test_result(do_test(cfg, sve_vls[sve],440sme_vls[sme], SVCR_SM_MASK),441"%s SVE VL %d/SME VL %d SM\n",442cfg->name, sve_vls[sve],443sme_vls[sme]);444ksft_test_result(do_test(cfg, sve_vls[sve],445sme_vls[sme], SVCR_ZA_MASK),446"%s SVE VL %d/SME VL %d ZA\n",447cfg->name, sve_vls[sve],448sme_vls[sme]);449}450}451452for (sme = 0; sme < sme_vl_count; sme++) {453ret = prctl(PR_SME_SET_VL, sme_vls[sme]);454if (ret == -1)455ksft_exit_fail_msg("PR_SME_SET_VL failed: %s (%d)\n",456strerror(errno), errno);457458ksft_test_result(do_test(cfg, 0, sme_vls[sme],459SVCR_ZA_MASK | SVCR_SM_MASK),460"%s SME VL %d SM+ZA\n",461cfg->name, sme_vls[sme]);462ksft_test_result(do_test(cfg, 0, sme_vls[sme], SVCR_SM_MASK),463"%s SME VL %d SM\n",464cfg->name, sme_vls[sme]);465ksft_test_result(do_test(cfg, 0, sme_vls[sme], SVCR_ZA_MASK),466"%s SME VL %d ZA\n",467cfg->name, sme_vls[sme]);468}469}470471void sve_count_vls(void)472{473unsigned int vq;474int vl;475476if (!(getauxval(AT_HWCAP) & HWCAP_SVE))477return;478479/*480* Enumerate up to ARCH_SVE_VQ_MAX vector lengths481*/482for (vq = ARCH_SVE_VQ_MAX; vq > 0; vq /= 2) {483vl = prctl(PR_SVE_SET_VL, vq * 16);484if (vl == -1)485ksft_exit_fail_msg("PR_SVE_SET_VL failed: %s (%d)\n",486strerror(errno), errno);487488vl &= PR_SVE_VL_LEN_MASK;489490if (vq != sve_vq_from_vl(vl))491vq = sve_vq_from_vl(vl);492493sve_vls[sve_vl_count++] = vl;494}495}496497void sme_count_vls(void)498{499unsigned int vq;500int vl;501502if (!(getauxval(AT_HWCAP2) & HWCAP2_SME))503return;504505/*506* Enumerate up to ARCH_SVE_VQ_MAX vector lengths507*/508for (vq = ARCH_SVE_VQ_MAX; vq > 0; vq /= 2) {509vl = prctl(PR_SME_SET_VL, vq * 16);510if (vl == -1)511ksft_exit_fail_msg("PR_SME_SET_VL failed: %s (%d)\n",512strerror(errno), errno);513514vl &= PR_SME_VL_LEN_MASK;515516/* Found lowest VL */517if (sve_vq_from_vl(vl) > vq)518break;519520if (vq != sve_vq_from_vl(vl))521vq = sve_vq_from_vl(vl);522523sme_vls[sme_vl_count++] = vl;524}525526/* Ensure we configure a SME VL, used to flag if SVCR is set */527default_sme_vl = sme_vls[0];528}529530int main(void)531{532int i;533int tests = 1; /* FPSIMD */534int sme_ver;535536srandom(getpid());537538ksft_print_header();539540sve_count_vls();541sme_count_vls();542543tests += sve_vl_count;544tests += sme_vl_count * 3;545tests += (sve_vl_count * sme_vl_count) * 3;546ksft_set_plan(ARRAY_SIZE(syscalls) * tests);547548if (getauxval(AT_HWCAP2) & HWCAP2_SME2)549sme_ver = 2;550else551sme_ver = 1;552553if (getauxval(AT_HWCAP2) & HWCAP2_SME_FA64)554ksft_print_msg("SME%d with FA64\n", sme_ver);555else if (getauxval(AT_HWCAP2) & HWCAP2_SME)556ksft_print_msg("SME%d without FA64\n", sme_ver);557558for (i = 0; i < ARRAY_SIZE(syscalls); i++)559test_one_syscall(&syscalls[i]);560561ksft_print_cnts();562563return 0;564}565566567