Skip to content

Commit a29e2a4

Browse files
jones-drewpalmer-dabbelt
authored andcommitted
RISC-V: selftests: Add CBO tests
Add hwprobe test for Zicboz and its block size. Also, when Zicboz is present, test that cbo.zero may be issued and works. Additionally provide a command line option that enables testing that the Zicbom instructions cause SIGILL and also that cbo.zero causes SIGILL when Zicboz it's not present. The SIGILL tests require "opt-in" with a command line option because the RISC-V ISA does not require unimplemented standard opcodes to issue illegal-instruction exceptions (but hopefully most platforms do). Pinning the test to a subset of cpus with taskset will also restrict the hwprobe calls to that set. Signed-off-by: Andrew Jones <[email protected]> Reviewed-by: Xiao Wang <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Palmer Dabbelt <[email protected]>
1 parent 2f248e0 commit a29e2a4

File tree

4 files changed

+250
-12
lines changed

4 files changed

+250
-12
lines changed

tools/testing/selftests/riscv/hwprobe/Makefile

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,14 @@
22
# Copyright (C) 2021 ARM Limited
33
# Originally tools/testing/arm64/abi/Makefile
44

5-
TEST_GEN_PROGS := hwprobe
5+
CFLAGS += -I$(top_srcdir)/tools/include
6+
7+
TEST_GEN_PROGS := hwprobe cbo
68

79
include ../../lib.mk
810

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

tools/testing/selftests/riscv/hwprobe/hwprobe.c

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,7 @@
11
// SPDX-License-Identifier: GPL-2.0-only
2-
#include <stddef.h>
3-
#include <asm/hwprobe.h>
4-
2+
#include "hwprobe.h"
53
#include "../../kselftest.h"
64

7-
/*
8-
* Rather than relying on having a new enough libc to define this, just do it
9-
* ourselves. This way we don't need to be coupled to a new-enough libc to
10-
* contain the call.
11-
*/
12-
long riscv_hwprobe(struct riscv_hwprobe *pairs, size_t pair_count,
13-
size_t cpu_count, unsigned long *cpus, unsigned int flags);
14-
155
int main(int argc, char **argv)
166
{
177
struct riscv_hwprobe pairs[8];
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/* SPDX-License-Identifier: GPL-2.0-only */
2+
#ifndef SELFTEST_RISCV_HWPROBE_H
3+
#define SELFTEST_RISCV_HWPROBE_H
4+
#include <stddef.h>
5+
#include <asm/hwprobe.h>
6+
7+
/*
8+
* Rather than relying on having a new enough libc to define this, just do it
9+
* ourselves. This way we don't need to be coupled to a new-enough libc to
10+
* contain the call.
11+
*/
12+
long riscv_hwprobe(struct riscv_hwprobe *pairs, size_t pair_count,
13+
size_t cpu_count, unsigned long *cpus, unsigned int flags);
14+
15+
#endif

0 commit comments

Comments
 (0)