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