xref: /kvm-unit-tests/riscv/sbi-fwft.c (revision 1b7dc511d891db54f5389ab591bb46f19976464c)
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