227 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (c) 2023 Ventana Micro Systems Inc.4 *5 * Run with 'taskset -c <cpu-list> cbo' to only execute hwprobe on a6 * subset of cpus, as well as only executing the tests on those cpus.7 */8#define _GNU_SOURCE9#include <stdbool.h>10#include <stdint.h>11#include <string.h>12#include <sched.h>13#include <signal.h>14#include <assert.h>15#include <linux/compiler.h>16#include <linux/kernel.h>17#include <asm/ucontext.h>18 19#include "hwprobe.h"20#include "../../kselftest.h"21 22#define MK_CBO(fn) le32_bswap((uint32_t)(fn) << 20 | 10 << 15 | 2 << 12 | 0 << 7 | 15)23 24static char mem[4096] __aligned(4096) = { [0 ... 4095] = 0xa5 };25 26static bool illegal_insn;27 28static void sigill_handler(int sig, siginfo_t *info, void *context)29{30 unsigned long *regs = (unsigned long *)&((ucontext_t *)context)->uc_mcontext;31 uint32_t insn = *(uint32_t *)regs[0];32 33 assert(insn == MK_CBO(regs[11]));34 35 illegal_insn = true;36 regs[0] += 4;37}38 39#define cbo_insn(base, fn) \40({ \41 asm volatile( \42 "mv a0, %0\n" \43 "li a1, %1\n" \44 ".4byte %2\n" \45 : : "r" (base), "i" (fn), "i" (MK_CBO(fn)) : "a0", "a1", "memory"); \46})47 48static void cbo_inval(char *base) { cbo_insn(base, 0); }49static void cbo_clean(char *base) { cbo_insn(base, 1); }50static void cbo_flush(char *base) { cbo_insn(base, 2); }51static void cbo_zero(char *base) { cbo_insn(base, 4); }52 53static void test_no_zicbom(void *arg)54{55 ksft_print_msg("Testing Zicbom instructions remain privileged\n");56 57 illegal_insn = false;58 cbo_clean(&mem[0]);59 ksft_test_result(illegal_insn, "No cbo.clean\n");60 61 illegal_insn = false;62 cbo_flush(&mem[0]);63 ksft_test_result(illegal_insn, "No cbo.flush\n");64 65 illegal_insn = false;66 cbo_inval(&mem[0]);67 ksft_test_result(illegal_insn, "No cbo.inval\n");68}69 70static void test_no_zicboz(void *arg)71{72 ksft_print_msg("No Zicboz, testing cbo.zero remains privileged\n");73 74 illegal_insn = false;75 cbo_zero(&mem[0]);76 ksft_test_result(illegal_insn, "No cbo.zero\n");77}78 79static bool is_power_of_2(__u64 n)80{81 return n != 0 && (n & (n - 1)) == 0;82}83 84static void test_zicboz(void *arg)85{86 struct riscv_hwprobe pair = {87 .key = RISCV_HWPROBE_KEY_ZICBOZ_BLOCK_SIZE,88 };89 cpu_set_t *cpus = (cpu_set_t *)arg;90 __u64 block_size;91 int i, j;92 long rc;93 94 rc = riscv_hwprobe(&pair, 1, sizeof(cpu_set_t), (unsigned long *)cpus, 0);95 block_size = pair.value;96 ksft_test_result(rc == 0 && pair.key == RISCV_HWPROBE_KEY_ZICBOZ_BLOCK_SIZE &&97 is_power_of_2(block_size), "Zicboz block size\n");98 ksft_print_msg("Zicboz block size: %llu\n", block_size);99 100 illegal_insn = false;101 cbo_zero(&mem[block_size]);102 ksft_test_result(!illegal_insn, "cbo.zero\n");103 104 if (illegal_insn || !is_power_of_2(block_size)) {105 ksft_test_result_skip("cbo.zero check\n");106 return;107 }108 109 assert(block_size <= 1024);110 111 for (i = 0; i < 4096 / block_size; ++i) {112 if (i % 2)113 cbo_zero(&mem[i * block_size]);114 }115 116 for (i = 0; i < 4096 / block_size; ++i) {117 char expected = i % 2 ? 0x0 : 0xa5;118 119 for (j = 0; j < block_size; ++j) {120 if (mem[i * block_size + j] != expected) {121 ksft_test_result_fail("cbo.zero check\n");122 ksft_print_msg("cbo.zero check: mem[%llu] != 0x%x\n",123 i * block_size + j, expected);124 return;125 }126 }127 }128 129 ksft_test_result_pass("cbo.zero check\n");130}131 132static void check_no_zicboz_cpus(cpu_set_t *cpus)133{134 struct riscv_hwprobe pair = {135 .key = RISCV_HWPROBE_KEY_IMA_EXT_0,136 };137 cpu_set_t one_cpu;138 int i = 0, c = 0;139 long rc;140 141 while (i++ < CPU_COUNT(cpus)) {142 while (!CPU_ISSET(c, cpus))143 ++c;144 145 CPU_ZERO(&one_cpu);146 CPU_SET(c, &one_cpu);147 148 rc = riscv_hwprobe(&pair, 1, sizeof(cpu_set_t), (unsigned long *)&one_cpu, 0);149 assert(rc == 0 && pair.key == RISCV_HWPROBE_KEY_IMA_EXT_0);150 151 if (pair.value & RISCV_HWPROBE_EXT_ZICBOZ)152 ksft_exit_fail_msg("Zicboz is only present on a subset of harts.\n"153 "Use taskset to select a set of harts where Zicboz\n"154 "presence (present or not) is consistent for each hart\n");155 ++c;156 }157}158 159enum {160 TEST_ZICBOZ,161 TEST_NO_ZICBOZ,162 TEST_NO_ZICBOM,163};164 165static struct test_info {166 bool enabled;167 unsigned int nr_tests;168 void (*test_fn)(void *arg);169} tests[] = {170 [TEST_ZICBOZ] = { .nr_tests = 3, test_zicboz },171 [TEST_NO_ZICBOZ] = { .nr_tests = 1, test_no_zicboz },172 [TEST_NO_ZICBOM] = { .nr_tests = 3, test_no_zicbom },173};174 175int main(int argc, char **argv)176{177 struct sigaction act = {178 .sa_sigaction = &sigill_handler,179 .sa_flags = SA_SIGINFO,180 };181 struct riscv_hwprobe pair;182 unsigned int plan = 0;183 cpu_set_t cpus;184 long rc;185 int i;186 187 if (argc > 1 && !strcmp(argv[1], "--sigill")) {188 rc = sigaction(SIGILL, &act, NULL);189 assert(rc == 0);190 tests[TEST_NO_ZICBOZ].enabled = true;191 tests[TEST_NO_ZICBOM].enabled = true;192 }193 194 rc = sched_getaffinity(0, sizeof(cpu_set_t), &cpus);195 assert(rc == 0);196 197 ksft_print_header();198 199 pair.key = RISCV_HWPROBE_KEY_IMA_EXT_0;200 rc = riscv_hwprobe(&pair, 1, sizeof(cpu_set_t), (unsigned long *)&cpus, 0);201 if (rc < 0)202 ksft_exit_fail_msg("hwprobe() failed with %ld\n", rc);203 assert(rc == 0 && pair.key == RISCV_HWPROBE_KEY_IMA_EXT_0);204 205 if (pair.value & RISCV_HWPROBE_EXT_ZICBOZ) {206 tests[TEST_ZICBOZ].enabled = true;207 tests[TEST_NO_ZICBOZ].enabled = false;208 } else {209 check_no_zicboz_cpus(&cpus);210 }211 212 for (i = 0; i < ARRAY_SIZE(tests); ++i)213 plan += tests[i].enabled ? tests[i].nr_tests : 0;214 215 if (plan == 0)216 ksft_print_msg("No tests enabled.\n");217 else218 ksft_set_plan(plan);219 220 for (i = 0; i < ARRAY_SIZE(tests); ++i) {221 if (tests[i].enabled)222 tests[i].test_fn(&cpus);223 }224 225 ksft_finished();226}227