1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Test Timebase 4 * 5 * Copyright 2024 Nicholas Piggin, IBM Corp. 6 * 7 * This contains tests of timebase facility, TB, DEC, etc. 8 */ 9 #include <libcflat.h> 10 #include <util.h> 11 #include <migrate.h> 12 #include <alloc.h> 13 #include <asm/handlers.h> 14 #include <devicetree.h> 15 #include <asm/hcall.h> 16 #include <asm/processor.h> 17 #include <asm/time.h> 18 #include <asm/barrier.h> 19 20 static int dec_bits = 0; 21 22 static void cpu_dec_bits(int fdtnode, u64 regval __unused, void *arg __unused) 23 { 24 const struct fdt_property *prop; 25 int plen; 26 27 prop = fdt_get_property(dt_fdt(), fdtnode, "ibm,dec-bits", &plen); 28 if (!prop) { 29 dec_bits = 32; 30 return; 31 } 32 33 /* Sanity check for the property layout (first two bytes are header) */ 34 assert(plen == 4); 35 36 /* Check all CPU nodes have the same value of dec-bits */ 37 if (dec_bits) 38 assert(dec_bits == fdt32_to_cpu(*(uint32_t *)prop->data)); 39 else 40 dec_bits = fdt32_to_cpu(*(uint32_t *)prop->data); 41 } 42 43 /* Check amount of CPUs nodes that have the TM flag */ 44 static int find_dec_bits(void) 45 { 46 int ret; 47 48 ret = dt_for_each_cpu_node(cpu_dec_bits, NULL); 49 if (ret < 0) 50 return ret; 51 52 return dec_bits; 53 } 54 55 56 static bool do_migrate = false; 57 static volatile bool got_interrupt; 58 static volatile struct pt_regs recorded_regs; 59 60 static uint64_t dec_max; 61 static uint64_t dec_min; 62 63 static void test_tb(int argc, char **argv) 64 { 65 uint64_t tb; 66 int i; 67 68 tb = get_tb(); 69 report(get_tb() >= tb, "timebase is not going backwards"); 70 if (do_migrate) { 71 tb = get_tb(); 72 migrate(); 73 report(get_tb() >= tb, 74 "timebase is not going backwards over migration"); 75 } 76 77 for (i = 0; i < 100; i++) { 78 if (get_tb() > tb) 79 break; 80 } 81 report(get_tb() > tb, "timebase is incrementing"); 82 } 83 84 static void dec_stop_handler(struct pt_regs *regs, void *data) 85 { 86 mtspr(SPR_DEC, dec_max); 87 } 88 89 static void dec_handler(struct pt_regs *regs, void *data) 90 { 91 got_interrupt = true; 92 memcpy((void *)&recorded_regs, regs, sizeof(struct pt_regs)); 93 regs->msr &= ~MSR_EE; 94 } 95 96 static void test_dec(int argc, char **argv) 97 { 98 uint64_t tb1, tb2, dec; 99 int i; 100 101 handle_exception(0x900, &dec_handler, NULL); 102 103 for (i = 0; i < 100; i++) { 104 tb1 = get_tb(); 105 mtspr(SPR_DEC, dec_max); 106 dec = mfspr(SPR_DEC); 107 tb2 = get_tb(); 108 if (tb2 - tb1 < dec_max - dec) 109 break; 110 } 111 /* POWER CPUs can have a slight (few ticks) variation here */ 112 report_kfail(!host_is_tcg, tb2 - tb1 >= dec_max - dec, 113 "decrementer remains within TB after mtDEC"); 114 115 tb1 = get_tb(); 116 mtspr(SPR_DEC, dec_max); 117 mdelay(1000); 118 dec = mfspr(SPR_DEC); 119 tb2 = get_tb(); 120 report(tb2 - tb1 >= dec_max - dec, 121 "decrementer remains within TB after 1s"); 122 123 mtspr(SPR_DEC, dec_max); 124 local_irq_enable(); 125 local_irq_disable(); 126 if (mfspr(SPR_DEC) <= dec_max) { 127 report(!got_interrupt, 128 "no interrupt on decrementer positive"); 129 } 130 got_interrupt = false; 131 132 mtspr(SPR_DEC, 1); 133 mdelay(100); /* Give the timer a chance to run */ 134 if (do_migrate) 135 migrate(); 136 local_irq_enable(); 137 local_irq_disable(); 138 report(got_interrupt, "interrupt on decrementer underflow"); 139 got_interrupt = false; 140 141 if (do_migrate) 142 migrate(); 143 local_irq_enable(); 144 local_irq_disable(); 145 report(got_interrupt, "interrupt on decrementer still underflown"); 146 got_interrupt = false; 147 148 mtspr(SPR_DEC, 0); 149 mdelay(100); /* Give the timer a chance to run */ 150 if (do_migrate) 151 migrate(); 152 local_irq_enable(); 153 local_irq_disable(); 154 report(got_interrupt, "DEC deal with set to 0"); 155 got_interrupt = false; 156 157 /* Test for level-triggered decrementer */ 158 mtspr(SPR_DEC, -1ULL); 159 if (do_migrate) 160 migrate(); 161 local_irq_enable(); 162 local_irq_disable(); 163 report(got_interrupt, "interrupt on decrementer write MSB"); 164 got_interrupt = false; 165 166 mtspr(SPR_DEC, dec_max); 167 local_irq_enable(); 168 if (do_migrate) 169 migrate(); 170 mtspr(SPR_DEC, -1); 171 local_irq_disable(); 172 report(got_interrupt, "interrupt on decrementer write MSB with irqs on"); 173 got_interrupt = false; 174 175 mtspr(SPR_DEC, dec_min + 1); 176 mdelay(100); 177 local_irq_enable(); 178 local_irq_disable(); 179 /* TCG does not model this correctly */ 180 report_kfail(host_is_tcg, !got_interrupt, 181 "no interrupt after wrap to positive"); 182 got_interrupt = false; 183 184 handle_exception(0x900, NULL, NULL); 185 } 186 187 static void test_hdec(int argc, char **argv) 188 { 189 uint64_t tb1, tb2, hdec; 190 191 if (!machine_is_powernv()) { 192 report_skip("test reqiures powernv machine"); 193 return; 194 } 195 196 handle_exception(0x900, &dec_stop_handler, NULL); 197 handle_exception(0x980, &dec_handler, NULL); 198 199 mtspr(SPR_HDEC, dec_max); 200 mtspr(SPR_LPCR, mfspr(SPR_LPCR) | LPCR_HDICE); 201 202 tb1 = get_tb(); 203 mtspr(SPR_HDEC, dec_max); 204 hdec = mfspr(SPR_HDEC); 205 tb2 = get_tb(); 206 report(tb2 - tb1 >= dec_max - hdec, "hdecrementer remains within TB"); 207 208 tb1 = get_tb(); 209 mtspr(SPR_HDEC, dec_max); 210 mdelay(1000); 211 hdec = mfspr(SPR_HDEC); 212 tb2 = get_tb(); 213 report(tb2 - tb1 >= dec_max - hdec, "hdecrementer remains within TB after 1s"); 214 215 mtspr(SPR_HDEC, dec_max); 216 local_irq_enable(); 217 local_irq_disable(); 218 if (mfspr(SPR_HDEC) <= dec_max) { 219 report(!got_interrupt, "no interrupt on decrementer positive"); 220 } 221 got_interrupt = false; 222 223 mtspr(SPR_HDEC, 1); 224 mdelay(100); /* Give the timer a chance to run */ 225 if (do_migrate) 226 migrate(); 227 /* HDEC is edge triggered so ensure it still fires */ 228 mtspr(SPR_HDEC, dec_max); 229 local_irq_enable(); 230 local_irq_disable(); 231 report(got_interrupt, "interrupt on hdecrementer underflow"); 232 got_interrupt = false; 233 234 if (do_migrate) 235 migrate(); 236 local_irq_enable(); 237 local_irq_disable(); 238 report(!got_interrupt, "no interrupt on hdecrementer still underflown"); 239 got_interrupt = false; 240 241 mtspr(SPR_HDEC, -1ULL); 242 if (do_migrate) 243 migrate(); 244 local_irq_enable(); 245 local_irq_disable(); 246 report(got_interrupt, "no interrupt on hdecrementer underflown write MSB"); 247 got_interrupt = false; 248 249 mtspr(SPR_HDEC, 0); 250 mdelay(100); /* Give the timer a chance to run */ 251 if (do_migrate) 252 migrate(); 253 /* HDEC is edge triggered so ensure it still fires */ 254 mtspr(SPR_HDEC, dec_max); 255 local_irq_enable(); 256 local_irq_disable(); 257 report(got_interrupt, "HDEC deal with set to 0"); 258 got_interrupt = false; 259 260 mtspr(SPR_HDEC, dec_max); 261 local_irq_enable(); 262 if (do_migrate) 263 migrate(); 264 mtspr(SPR_HDEC, -1ULL); 265 local_irq_disable(); 266 report(got_interrupt, "interrupt on hdecrementer write MSB with irqs on"); 267 got_interrupt = false; 268 269 mtspr(SPR_HDEC, dec_max); 270 got_interrupt = false; 271 mtspr(SPR_HDEC, dec_min + 1); 272 if (do_migrate) 273 migrate(); 274 mdelay(100); 275 local_irq_enable(); 276 local_irq_disable(); 277 report(got_interrupt, "got interrupt after wrap to positive"); 278 got_interrupt = false; 279 280 mtspr(SPR_HDEC, -1ULL); 281 local_irq_enable(); 282 local_irq_disable(); 283 got_interrupt = false; 284 mtspr(SPR_HDEC, dec_min + 1000000); 285 if (do_migrate) 286 migrate(); 287 mdelay(100); 288 mtspr(SPR_HDEC, -1ULL); 289 local_irq_enable(); 290 local_irq_disable(); 291 report(got_interrupt, "edge re-armed after wrap to positive"); 292 got_interrupt = false; 293 294 mtspr(SPR_LPCR, mfspr(SPR_LPCR) & ~LPCR_HDICE); 295 296 handle_exception(0x900, NULL, NULL); 297 handle_exception(0x980, NULL, NULL); 298 } 299 300 struct { 301 const char *name; 302 void (*func)(int argc, char **argv); 303 } hctests[] = { 304 { "tb", test_tb }, 305 { "dec", test_dec }, 306 { "hdec", test_hdec }, 307 { NULL, NULL } 308 }; 309 310 int main(int argc, char **argv) 311 { 312 bool all; 313 int i; 314 315 all = argc == 1 || !strcmp(argv[1], "all"); 316 317 for (i = 1; i < argc; i++) { 318 if (!strcmp(argv[i], "-w")) { 319 do_migrate = true; 320 if (!all && argc == 2) 321 all = true; 322 } 323 } 324 325 find_dec_bits(); 326 dec_max = (1ULL << (dec_bits - 1)) - 1; 327 dec_min = (1ULL << (dec_bits - 1)); 328 329 if (machine_is_powernv() && dec_bits > 32) { 330 mtspr(SPR_LPCR, mfspr(SPR_LPCR) | LPCR_LD); 331 } 332 333 report_prefix_push("timebase"); 334 335 for (i = 0; hctests[i].name != NULL; i++) { 336 if (all || strcmp(argv[1], hctests[i].name) == 0) { 337 report_prefix_push(hctests[i].name); 338 hctests[i].func(argc, argv); 339 report_prefix_pop(); 340 } 341 } 342 343 report_prefix_pop(); 344 345 if (machine_is_powernv() && dec_bits > 32) { 346 mtspr(SPR_LPCR, mfspr(SPR_LPCR) & ~LPCR_LD); 347 } 348 349 return report_summary(); 350 } 351