1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * SBI verification 4 * 5 * Copyright (C) 2023, Ventana Micro Systems Inc., Andrew Jones <ajones@ventanamicro.com> 6 */ 7 #include <libcflat.h> 8 #include <alloc_page.h> 9 #include <stdlib.h> 10 #include <string.h> 11 #include <limits.h> 12 #include <vmalloc.h> 13 #include <memregions.h> 14 #include <asm/barrier.h> 15 #include <asm/csr.h> 16 #include <asm/delay.h> 17 #include <asm/io.h> 18 #include <asm/mmu.h> 19 #include <asm/processor.h> 20 #include <asm/sbi.h> 21 #include <asm/smp.h> 22 #include <asm/timer.h> 23 24 #define HIGH_ADDR_BOUNDARY ((phys_addr_t)1 << 32) 25 26 static void help(void) 27 { 28 puts("Test SBI\n"); 29 puts("An environ must be provided where expected values are given.\n"); 30 } 31 32 static struct sbiret __base_sbi_ecall(int fid, unsigned long arg0) 33 { 34 return sbi_ecall(SBI_EXT_BASE, fid, arg0, 0, 0, 0, 0, 0); 35 } 36 37 static struct sbiret __dbcn_sbi_ecall(int fid, unsigned long arg0, unsigned long arg1, unsigned long arg2) 38 { 39 return sbi_ecall(SBI_EXT_DBCN, fid, arg0, arg1, arg2, 0, 0, 0); 40 } 41 42 static void split_phys_addr(phys_addr_t paddr, unsigned long *hi, unsigned long *lo) 43 { 44 *lo = (unsigned long)paddr; 45 *hi = 0; 46 if (__riscv_xlen == 32) 47 *hi = (unsigned long)(paddr >> 32); 48 } 49 50 static bool check_addr(phys_addr_t start, phys_addr_t size) 51 { 52 struct mem_region *r = memregions_find(start); 53 return r && r->end - start >= size && r->flags == MR_F_UNUSED; 54 } 55 56 static phys_addr_t get_highest_addr(void) 57 { 58 phys_addr_t highest_end = 0; 59 struct mem_region *r; 60 61 for (r = mem_regions; r->end; ++r) { 62 if (r->end > highest_end) 63 highest_end = r->end; 64 } 65 66 return highest_end - 1; 67 } 68 69 static bool env_or_skip(const char *env) 70 { 71 if (!getenv(env)) { 72 report_skip("missing %s environment variable", env); 73 return false; 74 } 75 76 return true; 77 } 78 79 static void gen_report(struct sbiret *ret, 80 long expected_error, long expected_value) 81 { 82 bool check_error = ret->error == expected_error; 83 bool check_value = ret->value == expected_value; 84 85 if (!check_error || !check_value) 86 report_info("expected (error: %ld, value: %ld), received: (error: %ld, value %ld)", 87 expected_error, expected_value, ret->error, ret->value); 88 89 report(check_error, "expected sbi.error"); 90 report(check_value, "expected sbi.value"); 91 } 92 93 static void check_base(void) 94 { 95 struct sbiret ret; 96 long expected; 97 98 report_prefix_push("base"); 99 100 ret = __base_sbi_ecall(SBI_EXT_BASE_GET_SPEC_VERSION, 0); 101 if (ret.error || ret.value < 2) { 102 report_skip("SBI spec version 0.2 or higher required"); 103 return; 104 } 105 106 report_prefix_push("spec_version"); 107 if (env_or_skip("SBI_SPEC_VERSION")) { 108 expected = (long)strtoul(getenv("SBI_SPEC_VERSION"), NULL, 0); 109 gen_report(&ret, 0, expected); 110 } 111 report_prefix_pop(); 112 113 report_prefix_push("impl_id"); 114 if (env_or_skip("SBI_IMPL_ID")) { 115 expected = (long)strtoul(getenv("SBI_IMPL_ID"), NULL, 0); 116 ret = __base_sbi_ecall(SBI_EXT_BASE_GET_IMP_ID, 0); 117 gen_report(&ret, 0, expected); 118 } 119 report_prefix_pop(); 120 121 report_prefix_push("impl_version"); 122 if (env_or_skip("SBI_IMPL_VERSION")) { 123 expected = (long)strtoul(getenv("SBI_IMPL_VERSION"), NULL, 0); 124 ret = __base_sbi_ecall(SBI_EXT_BASE_GET_IMP_VERSION, 0); 125 gen_report(&ret, 0, expected); 126 } 127 report_prefix_pop(); 128 129 report_prefix_push("probe_ext"); 130 expected = getenv("SBI_PROBE_EXT") ? (long)strtoul(getenv("SBI_PROBE_EXT"), NULL, 0) : 1; 131 ret = __base_sbi_ecall(SBI_EXT_BASE_PROBE_EXT, SBI_EXT_BASE); 132 gen_report(&ret, 0, expected); 133 report_prefix_push("unavailable"); 134 ret = __base_sbi_ecall(SBI_EXT_BASE_PROBE_EXT, 0xb000000); 135 gen_report(&ret, 0, 0); 136 report_prefix_pop(); 137 report_prefix_pop(); 138 139 report_prefix_push("mvendorid"); 140 if (env_or_skip("MVENDORID")) { 141 expected = (long)strtoul(getenv("MVENDORID"), NULL, 0); 142 assert(__riscv_xlen == 32 || !(expected >> 32)); 143 ret = __base_sbi_ecall(SBI_EXT_BASE_GET_MVENDORID, 0); 144 gen_report(&ret, 0, expected); 145 } 146 report_prefix_pop(); 147 148 report_prefix_push("marchid"); 149 if (env_or_skip("MARCHID")) { 150 expected = (long)strtoul(getenv("MARCHID"), NULL, 0); 151 ret = __base_sbi_ecall(SBI_EXT_BASE_GET_MARCHID, 0); 152 gen_report(&ret, 0, expected); 153 } 154 report_prefix_pop(); 155 156 report_prefix_push("mimpid"); 157 if (env_or_skip("MIMPID")) { 158 expected = (long)strtoul(getenv("MIMPID"), NULL, 0); 159 ret = __base_sbi_ecall(SBI_EXT_BASE_GET_MIMPID, 0); 160 gen_report(&ret, 0, expected); 161 } 162 report_prefix_pop(); 163 164 report_prefix_pop(); 165 } 166 167 struct timer_info { 168 bool timer_works; 169 bool mask_timer_irq; 170 bool timer_irq_set; 171 bool timer_irq_cleared; 172 unsigned long timer_irq_count; 173 }; 174 175 static struct timer_info timer_info; 176 177 static bool timer_irq_pending(void) 178 { 179 return csr_read(CSR_SIP) & IP_TIP; 180 } 181 182 static void timer_irq_handler(struct pt_regs *regs) 183 { 184 timer_info.timer_works = true; 185 186 if (timer_info.timer_irq_count < ULONG_MAX) 187 ++timer_info.timer_irq_count; 188 189 if (timer_irq_pending()) 190 timer_info.timer_irq_set = true; 191 192 if (timer_info.mask_timer_irq) 193 timer_irq_disable(); 194 else 195 sbi_set_timer(ULONG_MAX); 196 197 if (!timer_irq_pending()) 198 timer_info.timer_irq_cleared = true; 199 } 200 201 static void timer_check_set_timer(bool mask_timer_irq) 202 { 203 struct sbiret ret; 204 unsigned long begin, end, duration; 205 const char *mask_test_str = mask_timer_irq ? " for mask irq test" : ""; 206 unsigned long d = getenv("SBI_TIMER_DELAY") ? strtol(getenv("SBI_TIMER_DELAY"), NULL, 0) : 200000; 207 unsigned long margin = getenv("SBI_TIMER_MARGIN") ? strtol(getenv("SBI_TIMER_MARGIN"), NULL, 0) : 200000; 208 209 d = usec_to_cycles(d); 210 margin = usec_to_cycles(margin); 211 212 timer_info = (struct timer_info){ .mask_timer_irq = mask_timer_irq }; 213 begin = timer_get_cycles(); 214 ret = sbi_set_timer(begin + d); 215 216 report(!ret.error, "set timer%s", mask_test_str); 217 if (ret.error) 218 report_info("set timer%s failed with %ld\n", mask_test_str, ret.error); 219 220 while ((end = timer_get_cycles()) <= (begin + d + margin) && !timer_info.timer_works) 221 cpu_relax(); 222 223 report(timer_info.timer_works, "timer interrupt received%s", mask_test_str); 224 report(timer_info.timer_irq_set, "pending timer interrupt bit set in irq handler%s", mask_test_str); 225 226 if (!mask_timer_irq) { 227 report(timer_info.timer_irq_set && timer_info.timer_irq_cleared, 228 "pending timer interrupt bit cleared by setting timer to -1"); 229 } 230 231 if (timer_info.timer_works) { 232 duration = end - begin; 233 report(duration >= d && duration <= (d + margin), "timer delay honored%s", mask_test_str); 234 } 235 236 report(timer_info.timer_irq_count == 1, "timer interrupt received exactly once%s", mask_test_str); 237 } 238 239 static void check_time(void) 240 { 241 bool pending; 242 243 report_prefix_push("time"); 244 245 if (!sbi_probe(SBI_EXT_TIME)) { 246 report_skip("time extension not available"); 247 report_prefix_pop(); 248 return; 249 } 250 251 report_prefix_push("set_timer"); 252 253 install_irq_handler(IRQ_S_TIMER, timer_irq_handler); 254 local_irq_enable(); 255 timer_irq_enable(); 256 257 timer_check_set_timer(false); 258 259 if (csr_read(CSR_SIE) & IE_TIE) 260 timer_check_set_timer(true); 261 else 262 report_skip("timer irq enable bit is not writable, skipping mask irq test"); 263 264 timer_irq_disable(); 265 sbi_set_timer(0); 266 pending = timer_irq_pending(); 267 report(pending, "timer immediately pending by setting timer to 0"); 268 sbi_set_timer(ULONG_MAX); 269 if (pending) 270 report(!timer_irq_pending(), "pending timer cleared while masked"); 271 else 272 report_skip("timer is not pending, skipping timer cleared while masked test"); 273 274 local_irq_disable(); 275 install_irq_handler(IRQ_S_TIMER, NULL); 276 277 report_prefix_pop(); 278 report_prefix_pop(); 279 } 280 281 #define DBCN_WRITE_TEST_STRING "DBCN_WRITE_TEST_STRING\n" 282 #define DBCN_WRITE_BYTE_TEST_BYTE ((u8)'a') 283 284 static void dbcn_write_test(const char *s, unsigned long num_bytes) 285 { 286 unsigned long base_addr_lo, base_addr_hi; 287 phys_addr_t paddr = virt_to_phys((void *)s); 288 int num_calls = 0; 289 struct sbiret ret; 290 291 split_phys_addr(paddr, &base_addr_hi, &base_addr_lo); 292 293 do { 294 ret = __dbcn_sbi_ecall(SBI_EXT_DBCN_CONSOLE_WRITE, num_bytes, base_addr_lo, base_addr_hi); 295 num_bytes -= ret.value; 296 paddr += ret.value; 297 split_phys_addr(paddr, &base_addr_hi, &base_addr_lo); 298 num_calls++; 299 } while (num_bytes != 0 && ret.error == SBI_SUCCESS); 300 301 report(ret.error == SBI_SUCCESS, "write success (error=%ld)", ret.error); 302 report_info("%d sbi calls made", num_calls); 303 } 304 305 static void dbcn_high_write_test(const char *s, unsigned long num_bytes, 306 phys_addr_t page_addr, size_t page_offset) 307 { 308 int nr_pages = page_offset ? 2 : 1; 309 void *vaddr; 310 311 if (page_addr != PAGE_ALIGN(page_addr) || page_addr + PAGE_SIZE < HIGH_ADDR_BOUNDARY || 312 !check_addr(page_addr, nr_pages * PAGE_SIZE)) { 313 report_skip("Memory above 4G required"); 314 return; 315 } 316 317 vaddr = alloc_vpages(nr_pages); 318 319 for (int i = 0; i < nr_pages; ++i) 320 install_page(current_pgtable(), page_addr + i * PAGE_SIZE, vaddr + i * PAGE_SIZE); 321 memcpy(vaddr + page_offset, DBCN_WRITE_TEST_STRING, num_bytes); 322 dbcn_write_test(vaddr + page_offset, num_bytes); 323 } 324 325 /* 326 * Only the write functionality is tested here. There's no easy way to 327 * non-interactively test the read functionality. 328 */ 329 static void check_dbcn(void) 330 { 331 unsigned long num_bytes = strlen(DBCN_WRITE_TEST_STRING); 332 unsigned long base_addr_lo, base_addr_hi; 333 bool do_invalid_addr = false; 334 phys_addr_t paddr; 335 struct sbiret ret; 336 const char *tmp; 337 char *buf; 338 339 report_prefix_push("dbcn"); 340 341 ret = __base_sbi_ecall(SBI_EXT_BASE_PROBE_EXT, SBI_EXT_DBCN); 342 if (!ret.value) { 343 report_skip("DBCN extension unavailable"); 344 report_prefix_pop(); 345 return; 346 } 347 348 report_prefix_push("write"); 349 350 dbcn_write_test(DBCN_WRITE_TEST_STRING, num_bytes); 351 352 assert(num_bytes < PAGE_SIZE); 353 354 report_prefix_push("page boundary"); 355 buf = alloc_pages(1); 356 memcpy(&buf[PAGE_SIZE - num_bytes / 2], DBCN_WRITE_TEST_STRING, num_bytes); 357 dbcn_write_test(&buf[PAGE_SIZE - num_bytes / 2], num_bytes); 358 report_prefix_pop(); 359 360 report_prefix_push("high boundary"); 361 tmp = getenv("SBI_DBCN_SKIP_HIGH_BOUNDARY"); 362 if (!tmp || atol(tmp) == 0) 363 dbcn_high_write_test(DBCN_WRITE_TEST_STRING, num_bytes, 364 HIGH_ADDR_BOUNDARY - PAGE_SIZE, PAGE_SIZE - num_bytes / 2); 365 else 366 report_skip("user disabled"); 367 report_prefix_pop(); 368 369 report_prefix_push("high page"); 370 tmp = getenv("SBI_DBCN_SKIP_HIGH_PAGE"); 371 if (!tmp || atol(tmp) == 0) { 372 paddr = HIGH_ADDR_BOUNDARY; 373 tmp = getenv("HIGH_PAGE"); 374 if (tmp) 375 paddr = strtoull(tmp, NULL, 0); 376 dbcn_high_write_test(DBCN_WRITE_TEST_STRING, num_bytes, paddr, 0); 377 } else { 378 report_skip("user disabled"); 379 } 380 report_prefix_pop(); 381 382 /* Bytes are read from memory and written to the console */ 383 report_prefix_push("invalid parameter"); 384 tmp = getenv("INVALID_ADDR_AUTO"); 385 if (tmp && atol(tmp) == 1) { 386 paddr = get_highest_addr() + 1; 387 do_invalid_addr = true; 388 } else if (env_or_skip("INVALID_ADDR")) { 389 paddr = strtoull(getenv("INVALID_ADDR"), NULL, 0); 390 do_invalid_addr = true; 391 } 392 393 if (do_invalid_addr) { 394 split_phys_addr(paddr, &base_addr_hi, &base_addr_lo); 395 ret = __dbcn_sbi_ecall(SBI_EXT_DBCN_CONSOLE_WRITE, 1, base_addr_lo, base_addr_hi); 396 report(ret.error == SBI_ERR_INVALID_PARAM, "address (error=%ld)", ret.error); 397 } 398 report_prefix_pop(); 399 400 report_prefix_pop(); 401 report_prefix_push("write_byte"); 402 403 puts("DBCN_WRITE TEST CHAR: "); 404 ret = __dbcn_sbi_ecall(SBI_EXT_DBCN_CONSOLE_WRITE_BYTE, (u8)DBCN_WRITE_BYTE_TEST_BYTE, 0, 0); 405 puts("\n"); 406 report(ret.error == SBI_SUCCESS, "write success (error=%ld)", ret.error); 407 report(ret.value == 0, "expected ret.value (%ld)", ret.value); 408 409 report_prefix_pop(); 410 report_prefix_pop(); 411 } 412 413 int main(int argc, char **argv) 414 { 415 if (argc > 1 && !strcmp(argv[1], "-h")) { 416 help(); 417 exit(0); 418 } 419 420 report_prefix_push("sbi"); 421 check_base(); 422 check_time(); 423 check_dbcn(); 424 425 return report_summary(); 426 } 427