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