1515#include <linux/compiler.h>
1616#include <linux/kernel.h>
1717#include <asm/ucontext.h>
18+ #include <getopt.h>
1819
1920#include "hwprobe.h"
2021#include "../../kselftest.h"
2122
2223#define MK_CBO (fn ) le32_bswap((uint32_t)(fn) << 20 | 10 << 15 | 2 << 12 | 0 << 7 | 15)
24+ #define MK_PREFETCH (fn ) \
25+ le32_bswap(0 << 25 | (uint32_t)(fn) << 20 | 10 << 15 | 6 << 12 | 0 << 7 | 19)
2326
2427static char mem [4096 ] __aligned (4096 ) = { [0 ... 4095 ] = 0xa5 };
2528
26- static bool illegal_insn ;
29+ static bool got_fault ;
2730
28- static void sigill_handler (int sig , siginfo_t * info , void * context )
31+ static void fault_handler (int sig , siginfo_t * info , void * context )
2932{
3033 unsigned long * regs = (unsigned long * )& ((ucontext_t * )context )-> uc_mcontext ;
3134 uint32_t insn = * (uint32_t * )regs [0 ];
3235
33- assert (insn == MK_CBO (regs [11 ]));
36+ if (sig == SIGILL )
37+ assert (insn == MK_CBO (regs [11 ]));
3438
35- illegal_insn = true;
39+ if (sig == SIGSEGV || sig == SIGBUS )
40+ assert (insn == MK_PREFETCH (regs [11 ]));
41+
42+ got_fault = true;
3643 regs [0 ] += 4 ;
3744}
3845
@@ -45,46 +52,103 @@ static void sigill_handler(int sig, siginfo_t *info, void *context)
4552 : : "r" (base), "i" (fn), "i" (MK_CBO(fn)) : "a0", "a1", "memory"); \
4653})
4754
55+ #define prefetch_insn (base , fn ) \
56+ ({ \
57+ asm volatile( \
58+ "mv a0, %0\n" \
59+ "li a1, %1\n" \
60+ ".4byte %2\n" \
61+ : : "r" (base), "i" (fn), "i" (MK_PREFETCH(fn)) : "a0", "a1"); \
62+ })
63+
4864static void cbo_inval (char * base ) { cbo_insn (base , 0 ); }
4965static void cbo_clean (char * base ) { cbo_insn (base , 1 ); }
5066static void cbo_flush (char * base ) { cbo_insn (base , 2 ); }
5167static void cbo_zero (char * base ) { cbo_insn (base , 4 ); }
68+ static void prefetch_i (char * base ) { prefetch_insn (base , 0 ); }
69+ static void prefetch_r (char * base ) { prefetch_insn (base , 1 ); }
70+ static void prefetch_w (char * base ) { prefetch_insn (base , 3 ); }
5271
5372static void test_no_cbo_inval (void * arg )
5473{
5574 ksft_print_msg ("Testing cbo.inval instruction remain privileged\n" );
56- illegal_insn = false;
75+ got_fault = false;
5776 cbo_inval (& mem [0 ]);
58- ksft_test_result (illegal_insn , "No cbo.inval\n" );
77+ ksft_test_result (got_fault , "No cbo.inval\n" );
5978}
6079
6180static void test_no_zicbom (void * arg )
6281{
6382 ksft_print_msg ("Testing Zicbom instructions remain privileged\n" );
6483
65- illegal_insn = false;
84+ got_fault = false;
6685 cbo_clean (& mem [0 ]);
67- ksft_test_result (illegal_insn , "No cbo.clean\n" );
86+ ksft_test_result (got_fault , "No cbo.clean\n" );
6887
69- illegal_insn = false;
88+ got_fault = false;
7089 cbo_flush (& mem [0 ]);
71- ksft_test_result (illegal_insn , "No cbo.flush\n" );
90+ ksft_test_result (got_fault , "No cbo.flush\n" );
7291}
7392
7493static void test_no_zicboz (void * arg )
7594{
7695 ksft_print_msg ("No Zicboz, testing cbo.zero remains privileged\n" );
7796
78- illegal_insn = false;
97+ got_fault = false;
7998 cbo_zero (& mem [0 ]);
80- ksft_test_result (illegal_insn , "No cbo.zero\n" );
99+ ksft_test_result (got_fault , "No cbo.zero\n" );
81100}
82101
83102static bool is_power_of_2 (__u64 n )
84103{
85104 return n != 0 && (n & (n - 1 )) == 0 ;
86105}
87106
107+ static void test_zicbop (void * arg )
108+ {
109+ struct riscv_hwprobe pair = {
110+ .key = RISCV_HWPROBE_KEY_ZICBOP_BLOCK_SIZE ,
111+ };
112+ struct sigaction act = {
113+ .sa_sigaction = & fault_handler ,
114+ .sa_flags = SA_SIGINFO
115+ };
116+ struct sigaction dfl = {
117+ .sa_handler = SIG_DFL
118+ };
119+ cpu_set_t * cpus = (cpu_set_t * )arg ;
120+ __u64 block_size ;
121+ long rc ;
122+
123+ rc = sigaction (SIGSEGV , & act , NULL );
124+ assert (rc == 0 );
125+ rc = sigaction (SIGBUS , & act , NULL );
126+ assert (rc == 0 );
127+
128+ rc = riscv_hwprobe (& pair , 1 , sizeof (cpu_set_t ), (unsigned long * )cpus , 0 );
129+ block_size = pair .value ;
130+ ksft_test_result (rc == 0 && pair .key == RISCV_HWPROBE_KEY_ZICBOP_BLOCK_SIZE &&
131+ is_power_of_2 (block_size ), "Zicbop block size\n" );
132+ ksft_print_msg ("Zicbop block size: %llu\n" , block_size );
133+
134+ got_fault = false;
135+ prefetch_i (& mem [0 ]);
136+ prefetch_r (& mem [0 ]);
137+ prefetch_w (& mem [0 ]);
138+ ksft_test_result (!got_fault , "Zicbop prefetch.* on valid address\n" );
139+
140+ got_fault = false;
141+ prefetch_i (NULL );
142+ prefetch_r (NULL );
143+ prefetch_w (NULL );
144+ ksft_test_result (!got_fault , "Zicbop prefetch.* on NULL\n" );
145+
146+ rc = sigaction (SIGBUS , & dfl , NULL );
147+ assert (rc == 0 );
148+ rc = sigaction (SIGSEGV , & dfl , NULL );
149+ assert (rc == 0 );
150+ }
151+
88152static void test_zicbom (void * arg )
89153{
90154 struct riscv_hwprobe pair = {
@@ -100,13 +164,13 @@ static void test_zicbom(void *arg)
100164 is_power_of_2 (block_size ), "Zicbom block size\n" );
101165 ksft_print_msg ("Zicbom block size: %llu\n" , block_size );
102166
103- illegal_insn = false;
167+ got_fault = false;
104168 cbo_clean (& mem [block_size ]);
105- ksft_test_result (!illegal_insn , "cbo.clean\n" );
169+ ksft_test_result (!got_fault , "cbo.clean\n" );
106170
107- illegal_insn = false;
171+ got_fault = false;
108172 cbo_flush (& mem [block_size ]);
109- ksft_test_result (!illegal_insn , "cbo.flush\n" );
173+ ksft_test_result (!got_fault , "cbo.flush\n" );
110174}
111175
112176static void test_zicboz (void * arg )
@@ -125,11 +189,11 @@ static void test_zicboz(void *arg)
125189 is_power_of_2 (block_size ), "Zicboz block size\n" );
126190 ksft_print_msg ("Zicboz block size: %llu\n" , block_size );
127191
128- illegal_insn = false;
192+ got_fault = false;
129193 cbo_zero (& mem [block_size ]);
130- ksft_test_result (!illegal_insn , "cbo.zero\n" );
194+ ksft_test_result (!got_fault , "cbo.zero\n" );
131195
132- if (illegal_insn || !is_power_of_2 (block_size )) {
196+ if (got_fault || !is_power_of_2 (block_size )) {
133197 ksft_test_result_skip ("cbo.zero check\n" );
134198 return ;
135199 }
@@ -177,7 +241,19 @@ static void check_no_zicbo_cpus(cpu_set_t *cpus, __u64 cbo)
177241 rc = riscv_hwprobe (& pair , 1 , sizeof (cpu_set_t ), (unsigned long * )& one_cpu , 0 );
178242 assert (rc == 0 && pair .key == RISCV_HWPROBE_KEY_IMA_EXT_0 );
179243
180- cbostr = cbo == RISCV_HWPROBE_EXT_ZICBOZ ? "Zicboz" : "Zicbom" ;
244+ switch (cbo ) {
245+ case RISCV_HWPROBE_EXT_ZICBOZ :
246+ cbostr = "Zicboz" ;
247+ break ;
248+ case RISCV_HWPROBE_EXT_ZICBOM :
249+ cbostr = "Zicbom" ;
250+ break ;
251+ case RISCV_HWPROBE_EXT_ZICBOP :
252+ cbostr = "Zicbop" ;
253+ break ;
254+ default :
255+ ksft_exit_fail_msg ("Internal error: invalid cbo %llu\n" , cbo );
256+ }
181257
182258 if (pair .value & cbo )
183259 ksft_exit_fail_msg ("%s is only present on a subset of harts.\n"
@@ -194,6 +270,7 @@ enum {
194270 TEST_ZICBOM ,
195271 TEST_NO_ZICBOM ,
196272 TEST_NO_CBO_INVAL ,
273+ TEST_ZICBOP ,
197274};
198275
199276static struct test_info {
@@ -206,26 +283,51 @@ static struct test_info {
206283 [TEST_ZICBOM ] = { .nr_tests = 3 , test_zicbom },
207284 [TEST_NO_ZICBOM ] = { .nr_tests = 2 , test_no_zicbom },
208285 [TEST_NO_CBO_INVAL ] = { .nr_tests = 1 , test_no_cbo_inval },
286+ [TEST_ZICBOP ] = { .nr_tests = 3 , test_zicbop },
287+ };
288+
289+ static const struct option long_opts [] = {
290+ {"zicbom-raises-sigill" , no_argument , 0 , 'm' },
291+ {"zicboz-raises-sigill" , no_argument , 0 , 'z' },
292+ {0 , 0 , 0 , 0 }
209293};
210294
211295int main (int argc , char * * argv )
212296{
213297 struct sigaction act = {
214- .sa_sigaction = & sigill_handler ,
298+ .sa_sigaction = & fault_handler ,
215299 .sa_flags = SA_SIGINFO ,
216300 };
217301 struct riscv_hwprobe pair ;
218302 unsigned int plan = 0 ;
219303 cpu_set_t cpus ;
220304 long rc ;
221- int i ;
222-
223- if (argc > 1 && !strcmp (argv [1 ], "--sigill" )) {
224- rc = sigaction (SIGILL , & act , NULL );
225- assert (rc == 0 );
226- tests [TEST_NO_ZICBOZ ].enabled = true;
227- tests [TEST_NO_ZICBOM ].enabled = true;
228- tests [TEST_NO_CBO_INVAL ].enabled = true;
305+ int i , opt , long_index ;
306+
307+ long_index = 0 ;
308+
309+ while ((opt = getopt_long (argc , argv , "mz" , long_opts , & long_index )) != -1 ) {
310+ switch (opt ) {
311+ case 'm' :
312+ tests [TEST_NO_ZICBOM ].enabled = true;
313+ tests [TEST_NO_CBO_INVAL ].enabled = true;
314+ rc = sigaction (SIGILL , & act , NULL );
315+ assert (rc == 0 );
316+ break ;
317+ case 'z' :
318+ tests [TEST_NO_ZICBOZ ].enabled = true;
319+ tests [TEST_NO_CBO_INVAL ].enabled = true;
320+ rc = sigaction (SIGILL , & act , NULL );
321+ assert (rc == 0 );
322+ break ;
323+ case '?' :
324+ fprintf (stderr ,
325+ "Usage: %s [--zicbom-raises-sigill|-m] [--zicboz-raises-sigill|-z]\n" ,
326+ argv [0 ]);
327+ exit (1 );
328+ default :
329+ break ;
330+ }
229331 }
230332
231333 rc = sched_getaffinity (0 , sizeof (cpu_set_t ), & cpus );
@@ -253,6 +355,11 @@ int main(int argc, char **argv)
253355 check_no_zicbo_cpus (& cpus , RISCV_HWPROBE_EXT_ZICBOM );
254356 }
255357
358+ if (pair .value & RISCV_HWPROBE_EXT_ZICBOP )
359+ tests [TEST_ZICBOP ].enabled = true;
360+ else
361+ check_no_zicbo_cpus (& cpus , RISCV_HWPROBE_EXT_ZICBOP );
362+
256363 for (i = 0 ; i < ARRAY_SIZE (tests ); ++ i )
257364 plan += tests [i ].enabled ? tests [i ].nr_tests : 0 ;
258365
0 commit comments