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