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