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 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 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 */ 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 */ 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 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 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 105 static void misaligned_handler(struct pt_regs *regs) 106 { 107 misaligned_handled = true; 108 regs->epc += 4; 109 } 110 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 116 static struct sbiret fwft_misaligned_exc_get(void) 117 { 118 return sbi_fwft_get(SBI_FWFT_MISALIGNED_EXC_DELEG); 119 } 120 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 arch,-c\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 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 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 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 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 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 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 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 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 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