1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * SBI verification
4 *
5 * Copyright (C) 2024, Rivos Inc., Clément Léger <cleger@rivosinc.com>
6 */
7 #include <libcflat.h>
8 #include <alloc.h>
9 #include <stdlib.h>
10
11 #include <asm/csr.h>
12 #include <asm/io.h>
13 #include <asm/mmu.h>
14 #include <asm/processor.h>
15 #include <asm/ptrace.h>
16 #include <asm/sbi.h>
17
18 #include "sbi-tests.h"
19
20 void check_fwft(void);
21
fwft_set_and_check_raw(const char * str,unsigned long feature,unsigned long value,unsigned long flags)22 static struct sbiret fwft_set_and_check_raw(const char *str, unsigned long feature,
23 unsigned long value, unsigned long flags)
24 {
25 struct sbiret ret;
26
27 ret = sbi_fwft_set_raw(feature, value, flags);
28 if (!sbiret_report_error(&ret, SBI_SUCCESS, "set to %ld%s", value, str))
29 return ret;
30
31 ret = sbi_fwft_get_raw(feature);
32 sbiret_report(&ret, SBI_SUCCESS, value, "get %ld after set%s", value, str);
33
34 return ret;
35 }
36
fwft_check_reserved(unsigned long id)37 static void fwft_check_reserved(unsigned long id)
38 {
39 struct sbiret ret;
40
41 ret = sbi_fwft_get(id);
42 sbiret_report_error(&ret, SBI_ERR_DENIED, "get reserved feature 0x%lx", id);
43
44 ret = sbi_fwft_set(id, 1, 0);
45 sbiret_report_error(&ret, SBI_ERR_DENIED, "set reserved feature 0x%lx", id);
46 }
47
48 /* Must be called before any sbi_fwft_set() call is made for @feature */
fwft_check_reset(uint32_t feature,unsigned long reset)49 static void fwft_check_reset(uint32_t feature, unsigned long reset)
50 {
51 struct sbiret ret = sbi_fwft_get(feature);
52
53 sbiret_report(&ret, SBI_SUCCESS, reset, "resets to %lu", reset);
54 }
55
56 /* Must be called after locking the feature using SBI_FWFT_SET_FLAG_LOCK */
fwft_feature_lock_test_values(uint32_t feature,size_t nr_values,unsigned long test_values[],unsigned long locked_value)57 static void fwft_feature_lock_test_values(uint32_t feature, size_t nr_values,
58 unsigned long test_values[],
59 unsigned long locked_value)
60 {
61 struct sbiret ret;
62
63 report_prefix_push("locked");
64
65 bool kfail = __sbi_get_imp_id() == SBI_IMPL_OPENSBI &&
66 __sbi_get_imp_version() < sbi_impl_opensbi_mk_version(1, 7);
67
68 for (int i = 0; i < nr_values; ++i) {
69 ret = sbi_fwft_set(feature, test_values[i], 0);
70 sbiret_kfail_error(kfail, &ret, SBI_ERR_DENIED_LOCKED,
71 "Set to %lu without lock flag", test_values[i]);
72
73 ret = sbi_fwft_set(feature, test_values[i], SBI_FWFT_SET_FLAG_LOCK);
74 sbiret_kfail_error(kfail, &ret, SBI_ERR_DENIED_LOCKED,
75 "Set to %lu with lock flag", test_values[i]);
76 }
77
78 ret = sbi_fwft_get(feature);
79 sbiret_report(&ret, SBI_SUCCESS, locked_value, "Get value %lu", locked_value);
80
81 report_prefix_pop();
82 }
83
fwft_feature_lock_test(uint32_t feature,unsigned long locked_value)84 static void fwft_feature_lock_test(uint32_t feature, unsigned long locked_value)
85 {
86 unsigned long values[] = {0, 1};
87
88 fwft_feature_lock_test_values(feature, 2, values, locked_value);
89 }
90
fwft_check_base(void)91 static void fwft_check_base(void)
92 {
93 report_prefix_push("base");
94
95 fwft_check_reserved(SBI_FWFT_LOCAL_RESERVED_START);
96 fwft_check_reserved(SBI_FWFT_LOCAL_RESERVED_END);
97 fwft_check_reserved(SBI_FWFT_GLOBAL_RESERVED_START);
98 fwft_check_reserved(SBI_FWFT_GLOBAL_RESERVED_END);
99
100 report_prefix_pop();
101 }
102
103 static bool misaligned_handled;
104
misaligned_handler(struct pt_regs * regs)105 static void misaligned_handler(struct pt_regs *regs)
106 {
107 misaligned_handled = true;
108 regs->epc += 4;
109 }
110
fwft_misaligned_exc_set(unsigned long value,unsigned long flags)111 static struct sbiret fwft_misaligned_exc_set(unsigned long value, unsigned long flags)
112 {
113 return sbi_fwft_set(SBI_FWFT_MISALIGNED_EXC_DELEG, value, flags);
114 }
115
fwft_misaligned_exc_get(void)116 static struct sbiret fwft_misaligned_exc_get(void)
117 {
118 return sbi_fwft_get(SBI_FWFT_MISALIGNED_EXC_DELEG);
119 }
120
fwft_check_misaligned_exc_deleg(void)121 static void fwft_check_misaligned_exc_deleg(void)
122 {
123 struct sbiret ret;
124 unsigned long expected;
125
126 report_prefix_push("misaligned_exc_deleg");
127
128 ret = fwft_misaligned_exc_get();
129 if (ret.error != SBI_SUCCESS) {
130 if (env_enabled("SBI_HAVE_FWFT_MISALIGNED_EXC_DELEG")) {
131 sbiret_report_error(&ret, SBI_SUCCESS, "supported");
132 return;
133 }
134 report_skip("not supported by platform");
135 return;
136 }
137
138 if (!sbiret_report_error(&ret, SBI_SUCCESS, "Get misaligned deleg feature"))
139 return;
140
141 if (env_or_skip("MISALIGNED_EXC_DELEG_RESET")) {
142 expected = strtoul(getenv("MISALIGNED_EXC_DELEG_RESET"), NULL, 0);
143 fwft_check_reset(SBI_FWFT_MISALIGNED_EXC_DELEG, expected);
144 }
145
146 ret = fwft_misaligned_exc_set(2, 0);
147 sbiret_report_error(&ret, SBI_ERR_INVALID_PARAM,
148 "Set misaligned deleg feature invalid value 2");
149 ret = fwft_misaligned_exc_set(0xFFFFFFFF, 0);
150 sbiret_report_error(&ret, SBI_ERR_INVALID_PARAM,
151 "Set misaligned deleg feature invalid value 0xFFFFFFFF");
152
153 #if __riscv_xlen > 32
154 ret = fwft_misaligned_exc_set(BIT(32), 0);
155 sbiret_report_error(&ret, SBI_ERR_INVALID_PARAM,
156 "Set misaligned deleg with invalid value > 32bits");
157
158 ret = fwft_misaligned_exc_set(0, BIT(32));
159 sbiret_report_error(&ret, SBI_ERR_INVALID_PARAM,
160 "Set misaligned deleg with invalid flag > 32bits");
161 #endif
162
163 /* Set to 0 and check after with get */
164 fwft_set_and_check_raw("", SBI_FWFT_MISALIGNED_EXC_DELEG, 0, 0);
165
166 /* Set to 1 and check after with get */
167 fwft_set_and_check_raw("", SBI_FWFT_MISALIGNED_EXC_DELEG, 1, 0);
168
169 install_exception_handler(EXC_LOAD_MISALIGNED, misaligned_handler);
170
171 asm volatile (
172 ".option push\n"
173 /*
174 * Disable compression so the lw takes exactly 4 bytes and thus
175 * can be skipped reliably from the exception handler.
176 */
177 ".option norvc\n"
178 "lw %[val], 1(%[val_addr])\n"
179 ".option pop\n"
180 : [val] "+r" (ret.value)
181 : [val_addr] "r" (&ret.value)
182 : "memory");
183
184 /*
185 * Even though the SBI delegated the misaligned exception to S-mode, it might not trap on
186 * misaligned load/store access, report that during tests.
187 */
188 if (!misaligned_handled)
189 report_skip("Misaligned load exception does not trap in S-mode");
190 else
191 report_pass("Misaligned load exception trap in S-mode");
192
193 install_exception_handler(EXC_LOAD_MISALIGNED, NULL);
194
195 /* Lock the feature */
196 ret = fwft_misaligned_exc_set(0, SBI_FWFT_SET_FLAG_LOCK);
197 sbiret_report_error(&ret, SBI_SUCCESS, "Set misaligned deleg feature value 0 and lock");
198
199 fwft_feature_lock_test(SBI_FWFT_MISALIGNED_EXC_DELEG, 0);
200
201 report_prefix_pop();
202 }
203
204 static bool adue_triggered_read, adue_triggered_write;
205
adue_set_ad(unsigned long addr,pteval_t prot)206 static void adue_set_ad(unsigned long addr, pteval_t prot)
207 {
208 pte_t *ptep = get_pte(current_pgtable(), addr);
209 *ptep = __pte(pte_val(*ptep) | prot);
210 local_flush_tlb_page(addr);
211 }
212
adue_read_handler(struct pt_regs * regs)213 static void adue_read_handler(struct pt_regs *regs)
214 {
215 adue_triggered_read = true;
216 adue_set_ad(regs->badaddr, _PAGE_ACCESSED);
217 }
218
adue_write_handler(struct pt_regs * regs)219 static void adue_write_handler(struct pt_regs *regs)
220 {
221 adue_triggered_write = true;
222 adue_set_ad(regs->badaddr, _PAGE_ACCESSED | _PAGE_DIRTY);
223 }
224
adue_check_pte(pteval_t pte,bool write)225 static bool adue_check_pte(pteval_t pte, bool write)
226 {
227 return (pte & (_PAGE_ACCESSED | _PAGE_DIRTY)) == (_PAGE_ACCESSED | (write ? _PAGE_DIRTY : 0));
228 }
229
adue_check(bool hw_updating_enabled,bool write)230 static void adue_check(bool hw_updating_enabled, bool write)
231 {
232 unsigned long *ptr = malloc(sizeof(unsigned long));
233 pte_t *ptep = get_pte(current_pgtable(), (uintptr_t)ptr);
234 bool *triggered;
235 const char *op;
236
237 WRITE_ONCE(adue_triggered_read, false);
238 WRITE_ONCE(adue_triggered_write, false);
239
240 *ptep = __pte(pte_val(*ptep) & ~(_PAGE_ACCESSED | _PAGE_DIRTY));
241 local_flush_tlb_page((uintptr_t)ptr);
242
243 if (write) {
244 op = "write";
245 triggered = &adue_triggered_write;
246 writel(0xdeadbeef, ptr);
247 } else {
248 op = "read";
249 triggered = &adue_triggered_read;
250 readl(ptr);
251 }
252
253 report(hw_updating_enabled != *triggered &&
254 adue_check_pte(pte_val(*ptep), write), "hw updating %s %s",
255 hw_updating_enabled ? "enabled" : "disabled", op);
256
257 free(ptr);
258 }
259
adue_toggle_and_check_raw(const char * str,unsigned long feature,unsigned long value,unsigned long flags)260 static bool adue_toggle_and_check_raw(const char *str, unsigned long feature, unsigned long value,
261 unsigned long flags)
262 {
263 struct sbiret ret = fwft_set_and_check_raw(str, feature, value, flags);
264
265 if (!ret.error) {
266 adue_check(value, false);
267 adue_check(value, true);
268 return true;
269 }
270
271 return false;
272 }
273
adue_toggle_and_check(const char * str,unsigned long value,unsigned long flags)274 static bool adue_toggle_and_check(const char *str, unsigned long value, unsigned long flags)
275 {
276 return adue_toggle_and_check_raw(str, SBI_FWFT_PTE_AD_HW_UPDATING, value, flags);
277 }
278
fwft_check_pte_ad_hw_updating(void)279 static void fwft_check_pte_ad_hw_updating(void)
280 {
281 struct sbiret ret;
282 bool enabled;
283
284 report_prefix_push("pte_ad_hw_updating");
285
286 ret = sbi_fwft_get(SBI_FWFT_PTE_AD_HW_UPDATING);
287 if (ret.error != SBI_SUCCESS) {
288 if (env_enabled("SBI_HAVE_FWFT_PTE_AD_HW_UPDATING")) {
289 sbiret_report_error(&ret, SBI_SUCCESS, "supported");
290 return;
291 }
292 report_skip("not supported by platform");
293 return;
294 } else if (!sbiret_report_error(&ret, SBI_SUCCESS, "get")) {
295 /* Not much we can do without a working get... */
296 return;
297 }
298
299 report(ret.value == 0 || ret.value == 1, "first get value is 0/1");
300
301 enabled = ret.value;
302
303 bool kfail = __sbi_get_imp_id() == SBI_IMPL_OPENSBI &&
304 __sbi_get_imp_version() < sbi_impl_opensbi_mk_version(1, 7);
305 report_kfail(kfail, !enabled, "resets to 0");
306
307 install_exception_handler(EXC_LOAD_PAGE_FAULT, adue_read_handler);
308 install_exception_handler(EXC_STORE_PAGE_FAULT, adue_write_handler);
309
310 adue_check(enabled, false);
311 adue_check(enabled, true);
312
313 if (!adue_toggle_and_check("", !enabled, 0))
314 goto adue_inval_tests;
315 else
316 enabled = !enabled;
317
318 if (!adue_toggle_and_check(" again", !enabled, 0))
319 goto adue_inval_tests;
320 else
321 enabled = !enabled;
322
323 #if __riscv_xlen > 32
324 if (!adue_toggle_and_check_raw(" with high feature bits set",
325 BIT(32) | SBI_FWFT_PTE_AD_HW_UPDATING, !enabled, 0))
326 goto adue_inval_tests;
327 else
328 enabled = !enabled;
329 #endif
330
331 adue_inval_tests:
332 ret = sbi_fwft_set(SBI_FWFT_PTE_AD_HW_UPDATING, 2, 0);
333 sbiret_report_error(&ret, SBI_ERR_INVALID_PARAM, "set to 2");
334
335 ret = sbi_fwft_set(SBI_FWFT_PTE_AD_HW_UPDATING, !enabled, 2);
336 sbiret_report_error(&ret, SBI_ERR_INVALID_PARAM, "set to %d with flags=2", !enabled);
337
338 if (!adue_toggle_and_check(" with lock", !enabled, 1))
339 goto adue_done;
340 else
341 enabled = !enabled;
342
343 fwft_feature_lock_test(SBI_FWFT_PTE_AD_HW_UPDATING, enabled);
344
345 adue_done:
346 install_exception_handler(EXC_LOAD_PAGE_FAULT, NULL);
347 install_exception_handler(EXC_STORE_PAGE_FAULT, NULL);
348
349 report_prefix_pop();
350 }
351
check_fwft(void)352 void check_fwft(void)
353 {
354 report_prefix_push("fwft");
355
356 if (!sbi_probe(SBI_EXT_FWFT)) {
357 report_skip("FWFT extension not available");
358 report_prefix_pop();
359 return;
360 }
361
362 sbi_bad_fid(SBI_EXT_FWFT);
363
364 fwft_check_base();
365 fwft_check_misaligned_exc_deleg();
366 fwft_check_pte_ad_hw_updating();
367
368 report_prefix_pop();
369 }
370