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 <stdlib.h> 9 #include <limits.h> 10 #include <asm/barrier.h> 11 #include <asm/csr.h> 12 #include <asm/delay.h> 13 #include <asm/io.h> 14 #include <asm/isa.h> 15 #include <asm/processor.h> 16 #include <asm/sbi.h> 17 #include <asm/smp.h> 18 #include <asm/timer.h> 19 20 static void help(void) 21 { 22 puts("Test SBI\n"); 23 puts("An environ must be provided where expected values are given.\n"); 24 } 25 26 static struct sbiret __base_sbi_ecall(int fid, unsigned long arg0) 27 { 28 return sbi_ecall(SBI_EXT_BASE, fid, arg0, 0, 0, 0, 0, 0); 29 } 30 31 static struct sbiret __time_sbi_ecall(unsigned long stime_value) 32 { 33 return sbi_ecall(SBI_EXT_TIME, SBI_EXT_TIME_SET_TIMER, stime_value, 0, 0, 0, 0, 0); 34 } 35 36 static struct sbiret __dbcn_sbi_ecall(int fid, unsigned long arg0, unsigned long arg1, unsigned long arg2) 37 { 38 return sbi_ecall(SBI_EXT_DBCN, fid, arg0, arg1, arg2, 0, 0, 0); 39 } 40 41 static void split_phys_addr(phys_addr_t paddr, unsigned long *hi, unsigned long *lo) 42 { 43 *lo = (unsigned long)paddr; 44 *hi = 0; 45 if (__riscv_xlen == 32) 46 *hi = (unsigned long)(paddr >> 32); 47 } 48 49 static bool env_or_skip(const char *env) 50 { 51 if (!getenv(env)) { 52 report_skip("missing %s environment variable", env); 53 return false; 54 } 55 56 return true; 57 } 58 59 static void gen_report(struct sbiret *ret, 60 long expected_error, long expected_value) 61 { 62 bool check_error = ret->error == expected_error; 63 bool check_value = ret->value == expected_value; 64 65 if (!check_error || !check_value) 66 report_info("expected (error: %ld, value: %ld), received: (error: %ld, value %ld)", 67 expected_error, expected_value, ret->error, ret->value); 68 69 report(check_error, "expected sbi.error"); 70 report(check_value, "expected sbi.value"); 71 } 72 73 static void check_base(void) 74 { 75 struct sbiret ret; 76 long expected; 77 78 report_prefix_push("base"); 79 80 ret = __base_sbi_ecall(SBI_EXT_BASE_GET_SPEC_VERSION, 0); 81 if (ret.error || ret.value < 2) { 82 report_skip("SBI spec version 0.2 or higher required"); 83 return; 84 } 85 86 report_prefix_push("spec_version"); 87 if (env_or_skip("SPEC_VERSION")) { 88 expected = strtol(getenv("SPEC_VERSION"), NULL, 0); 89 gen_report(&ret, 0, expected); 90 } 91 report_prefix_pop(); 92 93 report_prefix_push("impl_id"); 94 if (env_or_skip("IMPL_ID")) { 95 expected = strtol(getenv("IMPL_ID"), NULL, 0); 96 ret = __base_sbi_ecall(SBI_EXT_BASE_GET_IMP_ID, 0); 97 gen_report(&ret, 0, expected); 98 } 99 report_prefix_pop(); 100 101 report_prefix_push("impl_version"); 102 if (env_or_skip("IMPL_VERSION")) { 103 expected = strtol(getenv("IMPL_VERSION"), NULL, 0); 104 ret = __base_sbi_ecall(SBI_EXT_BASE_GET_IMP_VERSION, 0); 105 gen_report(&ret, 0, expected); 106 } 107 report_prefix_pop(); 108 109 report_prefix_push("probe_ext"); 110 expected = getenv("PROBE_EXT") ? strtol(getenv("PROBE_EXT"), NULL, 0) : 1; 111 ret = __base_sbi_ecall(SBI_EXT_BASE_PROBE_EXT, SBI_EXT_BASE); 112 gen_report(&ret, 0, expected); 113 report_prefix_push("unavailable"); 114 ret = __base_sbi_ecall(SBI_EXT_BASE_PROBE_EXT, 0xb000000); 115 gen_report(&ret, 0, 0); 116 report_prefix_pop(); 117 report_prefix_pop(); 118 119 report_prefix_push("mvendorid"); 120 if (env_or_skip("MVENDORID")) { 121 expected = strtol(getenv("MVENDORID"), NULL, 0); 122 ret = __base_sbi_ecall(SBI_EXT_BASE_GET_MVENDORID, 0); 123 gen_report(&ret, 0, expected); 124 } 125 report_prefix_pop(); 126 127 report_prefix_push("marchid"); 128 if (env_or_skip("MARCHID")) { 129 expected = strtol(getenv("MARCHID"), NULL, 0); 130 ret = __base_sbi_ecall(SBI_EXT_BASE_GET_MARCHID, 0); 131 gen_report(&ret, 0, expected); 132 } 133 report_prefix_pop(); 134 135 report_prefix_push("mimpid"); 136 if (env_or_skip("MIMPID")) { 137 expected = strtol(getenv("MIMPID"), NULL, 0); 138 ret = __base_sbi_ecall(SBI_EXT_BASE_GET_MIMPID, 0); 139 gen_report(&ret, 0, expected); 140 } 141 report_prefix_pop(); 142 143 report_prefix_pop(); 144 } 145 146 struct timer_info { 147 bool timer_works; 148 bool mask_timer_irq; 149 bool timer_irq_set; 150 bool timer_irq_cleared; 151 unsigned long timer_irq_count; 152 }; 153 154 static struct timer_info timer_info; 155 156 static bool timer_irq_pending(void) 157 { 158 return csr_read(CSR_SIP) & IP_TIP; 159 } 160 161 static void timer_irq_handler(struct pt_regs *regs) 162 { 163 timer_info.timer_works = true; 164 165 if (timer_info.timer_irq_count < ULONG_MAX) 166 ++timer_info.timer_irq_count; 167 168 if (timer_irq_pending()) 169 timer_info.timer_irq_set = true; 170 171 if (timer_info.mask_timer_irq) 172 timer_irq_disable(); 173 else 174 __time_sbi_ecall(ULONG_MAX); 175 176 if (!timer_irq_pending()) 177 timer_info.timer_irq_cleared = true; 178 } 179 180 static void timer_check_set_timer(bool mask_timer_irq) 181 { 182 struct sbiret ret; 183 unsigned long begin, end, duration; 184 const char *mask_test_str = mask_timer_irq ? " for mask irq test" : ""; 185 unsigned long d = getenv("TIMER_DELAY") ? strtol(getenv("TIMER_DELAY"), NULL, 0) : 200000; 186 unsigned long margin = getenv("TIMER_MARGIN") ? strtol(getenv("TIMER_MARGIN"), NULL, 0) : 200000; 187 188 d = usec_to_cycles(d); 189 margin = usec_to_cycles(margin); 190 191 timer_info = (struct timer_info){ .mask_timer_irq = mask_timer_irq }; 192 begin = timer_get_cycles(); 193 ret = __time_sbi_ecall(begin + d); 194 195 report(!ret.error, "set timer%s", mask_test_str); 196 if (ret.error) 197 report_info("set timer%s failed with %ld\n", mask_test_str, ret.error); 198 199 while ((end = timer_get_cycles()) <= (begin + d + margin) && !timer_info.timer_works) 200 cpu_relax(); 201 202 report(timer_info.timer_works, "timer interrupt received%s", mask_test_str); 203 report(timer_info.timer_irq_set, "pending timer interrupt bit set in irq handler%s", mask_test_str); 204 205 if (!mask_timer_irq) { 206 report(timer_info.timer_irq_set && timer_info.timer_irq_cleared, 207 "pending timer interrupt bit cleared by setting timer to -1"); 208 } 209 210 if (timer_info.timer_works) { 211 duration = end - begin; 212 report(duration >= d && duration <= (d + margin), "timer delay honored%s", mask_test_str); 213 } 214 215 report(timer_info.timer_irq_count == 1, "timer interrupt received exactly once%s", mask_test_str); 216 } 217 218 static void check_time(void) 219 { 220 bool pending; 221 222 report_prefix_push("time"); 223 224 if (!sbi_probe(SBI_EXT_TIME)) { 225 report_skip("time extension not available"); 226 report_prefix_pop(); 227 return; 228 } 229 230 report_prefix_push("set_timer"); 231 232 install_irq_handler(IRQ_S_TIMER, timer_irq_handler); 233 local_irq_enable(); 234 if (cpu_has_extension(smp_processor_id(), ISA_SSTC)) { 235 csr_write(CSR_STIMECMP, ULONG_MAX); 236 if (__riscv_xlen == 32) 237 csr_write(CSR_STIMECMPH, ULONG_MAX); 238 } 239 timer_irq_enable(); 240 241 timer_check_set_timer(false); 242 243 if (csr_read(CSR_SIE) & IE_TIE) 244 timer_check_set_timer(true); 245 else 246 report_skip("timer irq enable bit is not writable, skipping mask irq test"); 247 248 timer_irq_disable(); 249 __time_sbi_ecall(0); 250 pending = timer_irq_pending(); 251 report(pending, "timer immediately pending by setting timer to 0"); 252 __time_sbi_ecall(ULONG_MAX); 253 if (pending) 254 report(!timer_irq_pending(), "pending timer cleared while masked"); 255 else 256 report_skip("timer is not pending, skipping timer cleared while masked test"); 257 258 local_irq_disable(); 259 install_irq_handler(IRQ_S_TIMER, NULL); 260 261 report_prefix_pop(); 262 report_prefix_pop(); 263 } 264 265 #define DBCN_WRITE_TEST_STRING "DBCN_WRITE_TEST_STRING\n" 266 #define DBCN_WRITE_BYTE_TEST_BYTE (u8)'a' 267 268 /* 269 * Only the write functionality is tested here. There's no easy way to 270 * non-interactively test the read functionality. 271 */ 272 static void check_dbcn(void) 273 { 274 unsigned long num_bytes, base_addr_lo, base_addr_hi; 275 phys_addr_t paddr; 276 int num_calls = 0; 277 struct sbiret ret; 278 279 report_prefix_push("dbcn"); 280 281 ret = __base_sbi_ecall(SBI_EXT_BASE_PROBE_EXT, SBI_EXT_DBCN); 282 if (!ret.value) { 283 report_skip("DBCN extension unavailable"); 284 report_prefix_pop(); 285 return; 286 } 287 288 num_bytes = strlen(DBCN_WRITE_TEST_STRING); 289 paddr = virt_to_phys((void *)&DBCN_WRITE_TEST_STRING); 290 split_phys_addr(paddr, &base_addr_hi, &base_addr_lo); 291 292 report_prefix_push("write"); 293 294 do { 295 ret = __dbcn_sbi_ecall(SBI_EXT_DBCN_CONSOLE_WRITE, num_bytes, base_addr_lo, base_addr_hi); 296 num_bytes -= ret.value; 297 paddr += ret.value; 298 split_phys_addr(paddr, &base_addr_hi, &base_addr_lo); 299 num_calls++; 300 } while (num_bytes != 0 && ret.error == SBI_SUCCESS); 301 302 report(ret.error == SBI_SUCCESS, "write success"); 303 report_info("%d sbi calls made", num_calls); 304 305 /* Bytes are read from memory and written to the console */ 306 if (env_or_skip("INVALID_ADDR")) { 307 paddr = strtoull(getenv("INVALID_ADDR"), NULL, 0); 308 split_phys_addr(paddr, &base_addr_hi, &base_addr_lo); 309 ret = __dbcn_sbi_ecall(SBI_EXT_DBCN_CONSOLE_WRITE, 1, base_addr_lo, base_addr_hi); 310 report(ret.error == SBI_ERR_INVALID_PARAM, "invalid parameter: address"); 311 } 312 313 report_prefix_pop(); 314 315 report_prefix_push("write_byte"); 316 317 puts("DBCN_WRITE TEST CHAR: "); 318 ret = __dbcn_sbi_ecall(SBI_EXT_DBCN_CONSOLE_WRITE_BYTE, (u8)DBCN_WRITE_BYTE_TEST_BYTE, 0, 0); 319 puts("\n"); 320 report(ret.error == SBI_SUCCESS, "write success"); 321 report(ret.value == 0, "expected ret.value"); 322 323 report_prefix_pop(); 324 } 325 326 int main(int argc, char **argv) 327 { 328 if (argc > 1 && !strcmp(argv[1], "-h")) { 329 help(); 330 exit(0); 331 } 332 333 report_prefix_push("sbi"); 334 check_base(); 335 check_time(); 336 check_dbcn(); 337 338 return report_summary(); 339 } 340