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