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/isa.h> 14 #include <asm/processor.h> 15 #include <asm/sbi.h> 16 #include <asm/smp.h> 17 #include <asm/timer.h> 18 19 static void help(void) 20 { 21 puts("Test SBI\n"); 22 puts("An environ must be provided where expected values are given.\n"); 23 } 24 25 static struct sbiret __base_sbi_ecall(int fid, unsigned long arg0) 26 { 27 return sbi_ecall(SBI_EXT_BASE, fid, arg0, 0, 0, 0, 0, 0); 28 } 29 30 static struct sbiret __time_sbi_ecall(unsigned long stime_value) 31 { 32 return sbi_ecall(SBI_EXT_TIME, SBI_EXT_TIME_SET_TIMER, stime_value, 0, 0, 0, 0, 0); 33 } 34 35 static bool env_or_skip(const char *env) 36 { 37 if (!getenv(env)) { 38 report_skip("missing %s environment variable", env); 39 return false; 40 } 41 42 return true; 43 } 44 45 static void gen_report(struct sbiret *ret, 46 long expected_error, long expected_value) 47 { 48 bool check_error = ret->error == expected_error; 49 bool check_value = ret->value == expected_value; 50 51 if (!check_error || !check_value) 52 report_info("expected (error: %ld, value: %ld), received: (error: %ld, value %ld)", 53 expected_error, expected_value, ret->error, ret->value); 54 55 report(check_error, "expected sbi.error"); 56 report(check_value, "expected sbi.value"); 57 } 58 59 static void check_base(void) 60 { 61 struct sbiret ret; 62 long expected; 63 64 report_prefix_push("base"); 65 66 ret = __base_sbi_ecall(SBI_EXT_BASE_GET_SPEC_VERSION, 0); 67 if (ret.error || ret.value < 2) { 68 report_skip("SBI spec version 0.2 or higher required"); 69 return; 70 } 71 72 report_prefix_push("spec_version"); 73 if (env_or_skip("SPEC_VERSION")) { 74 expected = strtol(getenv("SPEC_VERSION"), NULL, 0); 75 gen_report(&ret, 0, expected); 76 } 77 report_prefix_pop(); 78 79 report_prefix_push("impl_id"); 80 if (env_or_skip("IMPL_ID")) { 81 expected = strtol(getenv("IMPL_ID"), NULL, 0); 82 ret = __base_sbi_ecall(SBI_EXT_BASE_GET_IMP_ID, 0); 83 gen_report(&ret, 0, expected); 84 } 85 report_prefix_pop(); 86 87 report_prefix_push("impl_version"); 88 if (env_or_skip("IMPL_VERSION")) { 89 expected = strtol(getenv("IMPL_VERSION"), NULL, 0); 90 ret = __base_sbi_ecall(SBI_EXT_BASE_GET_IMP_VERSION, 0); 91 gen_report(&ret, 0, expected); 92 } 93 report_prefix_pop(); 94 95 report_prefix_push("probe_ext"); 96 expected = getenv("PROBE_EXT") ? strtol(getenv("PROBE_EXT"), NULL, 0) : 1; 97 ret = __base_sbi_ecall(SBI_EXT_BASE_PROBE_EXT, SBI_EXT_BASE); 98 gen_report(&ret, 0, expected); 99 report_prefix_push("unavailable"); 100 ret = __base_sbi_ecall(SBI_EXT_BASE_PROBE_EXT, 0xb000000); 101 gen_report(&ret, 0, 0); 102 report_prefix_pop(); 103 report_prefix_pop(); 104 105 report_prefix_push("mvendorid"); 106 if (env_or_skip("MVENDORID")) { 107 expected = strtol(getenv("MVENDORID"), NULL, 0); 108 ret = __base_sbi_ecall(SBI_EXT_BASE_GET_MVENDORID, 0); 109 gen_report(&ret, 0, expected); 110 } 111 report_prefix_pop(); 112 113 report_prefix_push("marchid"); 114 if (env_or_skip("MARCHID")) { 115 expected = strtol(getenv("MARCHID"), NULL, 0); 116 ret = __base_sbi_ecall(SBI_EXT_BASE_GET_MARCHID, 0); 117 gen_report(&ret, 0, expected); 118 } 119 report_prefix_pop(); 120 121 report_prefix_push("mimpid"); 122 if (env_or_skip("MIMPID")) { 123 expected = strtol(getenv("MIMPID"), NULL, 0); 124 ret = __base_sbi_ecall(SBI_EXT_BASE_GET_MIMPID, 0); 125 gen_report(&ret, 0, expected); 126 } 127 report_prefix_pop(); 128 129 report_prefix_pop(); 130 } 131 132 struct timer_info { 133 bool timer_works; 134 bool mask_timer_irq; 135 bool timer_irq_set; 136 bool timer_irq_cleared; 137 unsigned long timer_irq_count; 138 }; 139 140 static struct timer_info timer_info; 141 142 static bool timer_irq_pending(void) 143 { 144 return csr_read(CSR_SIP) & IP_TIP; 145 } 146 147 static void timer_irq_handler(struct pt_regs *regs) 148 { 149 timer_info.timer_works = true; 150 151 if (timer_info.timer_irq_count < ULONG_MAX) 152 ++timer_info.timer_irq_count; 153 154 if (timer_irq_pending()) 155 timer_info.timer_irq_set = true; 156 157 if (timer_info.mask_timer_irq) 158 timer_irq_disable(); 159 else 160 __time_sbi_ecall(ULONG_MAX); 161 162 if (!timer_irq_pending()) 163 timer_info.timer_irq_cleared = true; 164 } 165 166 static void timer_check_set_timer(bool mask_timer_irq) 167 { 168 struct sbiret ret; 169 unsigned long begin, end, duration; 170 const char *mask_test_str = mask_timer_irq ? " for mask irq test" : ""; 171 unsigned long d = getenv("TIMER_DELAY") ? strtol(getenv("TIMER_DELAY"), NULL, 0) : 200000; 172 unsigned long margin = getenv("TIMER_MARGIN") ? strtol(getenv("TIMER_MARGIN"), NULL, 0) : 200000; 173 174 d = usec_to_cycles(d); 175 margin = usec_to_cycles(margin); 176 177 timer_info = (struct timer_info){ .mask_timer_irq = mask_timer_irq }; 178 begin = timer_get_cycles(); 179 ret = __time_sbi_ecall(begin + d); 180 181 report(!ret.error, "set timer%s", mask_test_str); 182 if (ret.error) 183 report_info("set timer%s failed with %ld\n", mask_test_str, ret.error); 184 185 while ((end = timer_get_cycles()) <= (begin + d + margin) && !timer_info.timer_works) 186 cpu_relax(); 187 188 report(timer_info.timer_works, "timer interrupt received%s", mask_test_str); 189 report(timer_info.timer_irq_set, "pending timer interrupt bit set in irq handler%s", mask_test_str); 190 191 if (!mask_timer_irq) { 192 report(timer_info.timer_irq_set && timer_info.timer_irq_cleared, 193 "pending timer interrupt bit cleared by setting timer to -1"); 194 } 195 196 if (timer_info.timer_works) { 197 duration = end - begin; 198 report(duration >= d && duration <= (d + margin), "timer delay honored%s", mask_test_str); 199 } 200 201 report(timer_info.timer_irq_count == 1, "timer interrupt received exactly once%s", mask_test_str); 202 } 203 204 static void check_time(void) 205 { 206 bool pending; 207 208 report_prefix_push("time"); 209 210 if (!sbi_probe(SBI_EXT_TIME)) { 211 report_skip("time extension not available"); 212 report_prefix_pop(); 213 return; 214 } 215 216 report_prefix_push("set_timer"); 217 218 install_irq_handler(IRQ_S_TIMER, timer_irq_handler); 219 local_irq_enable(); 220 if (cpu_has_extension(smp_processor_id(), ISA_SSTC)) { 221 csr_write(CSR_STIMECMP, ULONG_MAX); 222 if (__riscv_xlen == 32) 223 csr_write(CSR_STIMECMPH, ULONG_MAX); 224 } 225 timer_irq_enable(); 226 227 timer_check_set_timer(false); 228 229 if (csr_read(CSR_SIE) & IE_TIE) 230 timer_check_set_timer(true); 231 else 232 report_skip("timer irq enable bit is not writable, skipping mask irq test"); 233 234 timer_irq_disable(); 235 __time_sbi_ecall(0); 236 pending = timer_irq_pending(); 237 report(pending, "timer immediately pending by setting timer to 0"); 238 __time_sbi_ecall(ULONG_MAX); 239 if (pending) 240 report(!timer_irq_pending(), "pending timer cleared while masked"); 241 else 242 report_skip("timer is not pending, skipping timer cleared while masked test"); 243 244 local_irq_disable(); 245 install_irq_handler(IRQ_S_TIMER, NULL); 246 247 report_prefix_pop(); 248 report_prefix_pop(); 249 } 250 251 int main(int argc, char **argv) 252 { 253 254 if (argc > 1 && !strcmp(argv[1], "-h")) { 255 help(); 256 exit(0); 257 } 258 259 report_prefix_push("sbi"); 260 check_base(); 261 check_time(); 262 263 return report_summary(); 264 } 265