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