1 /* SPDX-License-Identifier: LGPL-2.0-only */ 2 /* 3 * Test interrupts 4 * 5 * Copyright 2024 Nicholas Piggin, IBM Corp. 6 */ 7 #include <libcflat.h> 8 #include <util.h> 9 #include <migrate.h> 10 #include <alloc.h> 11 #include <asm/setup.h> 12 #include <asm/handlers.h> 13 #include <asm/hcall.h> 14 #include <asm/processor.h> 15 #include <asm/time.h> 16 #include <asm/barrier.h> 17 #include <asm/mmu.h> 18 #include "alloc_phys.h" 19 #include "vmalloc.h" 20 21 static volatile bool got_interrupt; 22 static volatile struct pt_regs recorded_regs; 23 24 static void mce_handler(struct pt_regs *regs, void *opaque) 25 { 26 bool *is_fetch = opaque; 27 28 got_interrupt = true; 29 memcpy((void *)&recorded_regs, regs, sizeof(struct pt_regs)); 30 if (*is_fetch) 31 regs->nip = regs->link; 32 else 33 regs_advance_insn(regs); 34 } 35 36 static void fault_handler(struct pt_regs *regs, void *opaque) 37 { 38 memcpy((void *)&recorded_regs, regs, sizeof(struct pt_regs)); 39 if (regs->trap == 0x400 || regs->trap == 0x480) 40 regs->nip = regs->link; 41 else 42 regs_advance_insn(regs); 43 } 44 45 static void test_mce(void) 46 { 47 unsigned long addr = -4ULL; 48 uint8_t tmp; 49 bool is_fetch; 50 bool mmu = mmu_enabled(); 51 52 report_prefix_push("mce"); 53 54 handle_exception(0x200, mce_handler, &is_fetch); 55 handle_exception(0x300, fault_handler, NULL); 56 handle_exception(0x380, fault_handler, NULL); 57 handle_exception(0x400, fault_handler, NULL); 58 handle_exception(0x480, fault_handler, NULL); 59 60 if (mmu) 61 mmu_disable(); 62 63 if (machine_is_powernv()) { 64 enable_mcheck(); 65 } else { 66 report(mfmsr() & MSR_ME, "pseries machine has MSR[ME]=1"); 67 if (!(mfmsr() & MSR_ME)) { /* try to fix it */ 68 enable_mcheck(); 69 } 70 if (mfmsr() & MSR_ME) { 71 disable_mcheck(); 72 report(mfmsr() & MSR_ME, "pseries is unable to change MSR[ME]"); 73 if (!(mfmsr() & MSR_ME)) { /* try to fix it */ 74 enable_mcheck(); 75 } 76 } 77 } 78 79 is_fetch = false; 80 asm volatile("lbz %0,0(%1)" : "=r"(tmp) : "r"(addr)); 81 82 /* KVM does not MCE on access outside partition scope */ 83 report_kfail(host_is_kvm, got_interrupt, "MCE on access to invalid real address"); 84 if (got_interrupt) { 85 report(mfspr(SPR_DAR) == addr, "MCE sets DAR correctly"); 86 if (cpu_has_power_mce) 87 report(recorded_regs.msr & (1ULL << 21), "d-side MCE sets SRR1[42]"); 88 got_interrupt = false; 89 } 90 91 is_fetch = true; 92 asm volatile("mtctr %0 ; bctrl" :: "r"(addr) : "ctr", "lr"); 93 /* KVM does not MCE on access outside partition scope */ 94 report_kfail(host_is_kvm, got_interrupt, "MCE on fetch from invalid real address"); 95 if (got_interrupt) { 96 report(recorded_regs.nip == addr, "MCE sets SRR0 correctly"); 97 if (cpu_has_power_mce) 98 report(!(recorded_regs.msr & (1ULL << 21)), "i-side MCE clears SRR1[42]"); 99 got_interrupt = false; 100 } 101 102 if (mmu) 103 mmu_enable(NULL); 104 105 handle_exception(0x200, NULL, NULL); 106 handle_exception(0x300, NULL, NULL); 107 handle_exception(0x380, NULL, NULL); 108 handle_exception(0x400, NULL, NULL); 109 handle_exception(0x480, NULL, NULL); 110 111 report_prefix_pop(); 112 } 113 114 static void dside_handler(struct pt_regs *regs, void *data) 115 { 116 got_interrupt = true; 117 memcpy((void *)&recorded_regs, regs, sizeof(struct pt_regs)); 118 regs_advance_insn(regs); 119 } 120 121 static void iside_handler(struct pt_regs *regs, void *data) 122 { 123 got_interrupt = true; 124 memcpy((void *)&recorded_regs, regs, sizeof(struct pt_regs)); 125 regs->nip = regs->link; 126 } 127 128 static void test_dseg_nommu(void) 129 { 130 uint64_t msr, tmp; 131 132 report_prefix_push("dseg"); 133 134 /* Some HV start in radix mode and need 0x300 */ 135 handle_exception(0x300, &dside_handler, NULL); 136 handle_exception(0x380, &dside_handler, NULL); 137 138 asm volatile( 139 " mfmsr %0 \n \ 140 ori %1,%0,%2 \n \ 141 mtmsrd %1 \n \ 142 lbz %1,0(0) \n \ 143 mtmsrd %0 " 144 : "=r"(msr), "=r"(tmp) : "i"(MSR_DR): "memory"); 145 146 report(got_interrupt, "interrupt on NULL dereference"); 147 got_interrupt = false; 148 149 handle_exception(0x300, NULL, NULL); 150 handle_exception(0x380, NULL, NULL); 151 152 report_prefix_pop(); 153 } 154 155 static void test_mmu(void) 156 { 157 uint64_t tmp, addr; 158 phys_addr_t base, top; 159 160 if (!mmu_enabled()) { 161 test_dseg_nommu(); 162 return; 163 } 164 165 phys_alloc_get_unused(&base, &top); 166 167 report_prefix_push("dsi"); 168 addr = top + PAGE_SIZE; 169 handle_exception(0x300, &dside_handler, NULL); 170 asm volatile("lbz %0,0(%1)" : "=r"(tmp) : "r"(addr)); 171 report(got_interrupt, "dsi on out of range dereference"); 172 report(mfspr(SPR_DAR) == addr, "DAR set correctly"); 173 report(mfspr(SPR_DSISR) & (1ULL << 30), "DSISR set correctly"); 174 got_interrupt = false; 175 handle_exception(0x300, NULL, NULL); 176 report_prefix_pop(); 177 178 report_prefix_push("dseg"); 179 addr = -4ULL; 180 handle_exception(0x380, &dside_handler, NULL); 181 asm volatile("lbz %0,0(%1)" : "=r"(tmp) : "r"(addr)); 182 report(got_interrupt, "dseg on out of range dereference"); 183 report(mfspr(SPR_DAR) == addr, "DAR set correctly"); 184 got_interrupt = false; 185 handle_exception(0x380, NULL, NULL); 186 report_prefix_pop(); 187 188 report_prefix_push("isi"); 189 addr = top + PAGE_SIZE; 190 handle_exception(0x400, &iside_handler, NULL); 191 asm volatile("mtctr %0 ; bctrl" :: "r"(addr) : "ctr", "lr"); 192 report(got_interrupt, "isi on out of range fetch"); 193 report(recorded_regs.nip == addr, "SRR0 set correctly"); 194 report(recorded_regs.msr & (1ULL << 30), "SRR1 set correctly"); 195 got_interrupt = false; 196 handle_exception(0x400, NULL, NULL); 197 report_prefix_pop(); 198 199 report_prefix_push("iseg"); 200 addr = -4ULL; 201 handle_exception(0x480, &iside_handler, NULL); 202 asm volatile("mtctr %0 ; bctrl" :: "r"(addr) : "ctr", "lr"); 203 report(got_interrupt, "isi on out of range fetch"); 204 report(recorded_regs.nip == addr, "SRR0 set correctly"); 205 got_interrupt = false; 206 handle_exception(0x480, NULL, NULL); 207 report_prefix_pop(); 208 } 209 210 static void dec_handler(struct pt_regs *regs, void *data) 211 { 212 got_interrupt = true; 213 memcpy((void *)&recorded_regs, regs, sizeof(struct pt_regs)); 214 regs->msr &= ~MSR_EE; 215 } 216 217 static void test_dec(void) 218 { 219 uint64_t msr; 220 uint64_t tb; 221 222 report_prefix_push("decrementer"); 223 224 handle_exception(0x900, &dec_handler, NULL); 225 226 asm volatile( 227 " mtdec %1 \n \ 228 mfmsr %0 \n \ 229 ori %0,%0,%2 \n \ 230 mtmsrd %0,1 " 231 : "=r"(msr) : "r"(10000), "i"(MSR_EE): "memory"); 232 233 tb = get_tb(); 234 while (!got_interrupt) { 235 if (get_tb() - tb > tb_hz * 5) 236 break; /* timeout 5s */ 237 } 238 239 report(got_interrupt, "interrupt on decrementer underflow"); 240 got_interrupt = false; 241 242 handle_exception(0x900, NULL, NULL); 243 244 if (!machine_is_powernv()) 245 goto done; /* Skip HV tests */ 246 247 handle_exception(0x980, &dec_handler, NULL); 248 249 mtspr(SPR_LPCR, mfspr(SPR_LPCR) | LPCR_HDICE); 250 asm volatile( 251 " mtspr 0x136,%1 \n \ 252 mtdec %3 \n \ 253 mfmsr %0 \n \ 254 ori %0,%0,%2 \n \ 255 mtmsrd %0,1 " 256 : "=r"(msr) : "r"(10000), "i"(MSR_EE), "r"(0x7fffffff): "memory"); 257 258 tb = get_tb(); 259 while (!got_interrupt) { 260 if (get_tb() - tb > tb_hz * 5) 261 break; /* timeout 5s */ 262 } 263 264 mtspr(SPR_LPCR, mfspr(SPR_LPCR) & ~LPCR_HDICE); 265 266 report(got_interrupt, "interrupt on hdecrementer underflow"); 267 got_interrupt = false; 268 269 handle_exception(0x980, NULL, NULL); 270 271 done: 272 report_prefix_pop(); 273 } 274 275 276 static volatile uint64_t recorded_heir; 277 278 static void heai_handler(struct pt_regs *regs, void *data) 279 { 280 got_interrupt = true; 281 memcpy((void *)&recorded_regs, regs, sizeof(struct pt_regs)); 282 regs_advance_insn(regs); 283 if (cpu_has_heai) 284 recorded_heir = mfspr(SPR_HEIR); 285 } 286 287 static void program_handler(struct pt_regs *regs, void *data) 288 { 289 got_interrupt = true; 290 memcpy((void *)&recorded_regs, regs, sizeof(struct pt_regs)); 291 regs_advance_insn(regs); 292 } 293 294 /* 295 * This tests invalid instruction handling. powernv (HV) should take an 296 * HEAI interrupt with the HEIR SPR set to the instruction image. pseries 297 * (guest) should take a program interrupt. CPUs which support prefix 298 * should report prefix instruction in (H)SRR1[34]. 299 */ 300 static void test_illegal(void) 301 { 302 report_prefix_push("illegal instruction"); 303 304 if (machine_is_powernv()) { 305 handle_exception(0xe40, &heai_handler, NULL); 306 } else { 307 handle_exception(0x700, &program_handler, NULL); 308 } 309 310 asm volatile(".long 0x12345678" ::: "memory"); 311 report(got_interrupt, "interrupt on invalid instruction"); 312 got_interrupt = false; 313 if (cpu_has_heai) 314 report(recorded_heir == 0x12345678, "HEIR: 0x%08lx", recorded_heir); 315 report(!regs_is_prefix(&recorded_regs), "(H)SRR1 prefix bit clear"); 316 317 if (cpu_has_prefix) { 318 asm volatile(".balign 8 ; .long 0x04000123; .long 0x00badc0d"); 319 report(got_interrupt, "interrupt on invalid prefix instruction"); 320 got_interrupt = false; 321 if (cpu_has_heai) 322 report(recorded_heir == 0x0400012300badc0d, "HEIR: 0x%08lx", recorded_heir); 323 report(regs_is_prefix(&recorded_regs), "(H)SRR1 prefix bit set"); 324 } 325 326 handle_exception(0xe40, NULL, NULL); 327 handle_exception(0x700, NULL, NULL); 328 329 report_prefix_pop(); 330 } 331 332 static void dec_ignore_handler(struct pt_regs *regs, void *data) 333 { 334 mtspr(SPR_DEC, 0x7fffffff); 335 } 336 337 static void test_privileged(void) 338 { 339 unsigned long msr; 340 341 if (!mmu_enabled()) 342 return; 343 344 report_prefix_push("privileged instruction"); 345 346 handle_exception(0x700, &program_handler, NULL); 347 handle_exception(0x900, &dec_ignore_handler, NULL); 348 enter_usermode(); 349 asm volatile("mfmsr %0" : "=r"(msr) :: "memory"); 350 exit_usermode(); 351 report(got_interrupt, "interrupt on privileged instruction"); 352 got_interrupt = false; 353 handle_exception(0x900, NULL, NULL); 354 handle_exception(0x700, NULL, NULL); 355 356 report_prefix_pop(); 357 } 358 359 static void sc_handler(struct pt_regs *regs, void *data) 360 { 361 got_interrupt = true; 362 memcpy((void *)&recorded_regs, regs, sizeof(struct pt_regs)); 363 } 364 365 static void test_sc(void) 366 { 367 report_prefix_push("syscall"); 368 369 handle_exception(0xc00, &sc_handler, NULL); 370 371 asm volatile("sc 0" ::: "memory"); 372 373 report(got_interrupt, "interrupt on sc 0 instruction"); 374 got_interrupt = false; 375 if (cpu_has_sc_lev) 376 report(((recorded_regs.msr >> 20) & 0x3) == 0, "SRR1 set LEV=0"); 377 if (machine_is_powernv()) { 378 asm volatile("sc 1" ::: "memory"); 379 380 report(got_interrupt, "interrupt on sc 1 instruction"); 381 got_interrupt = false; 382 if (cpu_has_sc_lev) 383 report(((recorded_regs.msr >> 20) & 0x3) == 1, "SRR1 set LEV=1"); 384 } 385 386 handle_exception(0xc00, NULL, NULL); 387 388 report_prefix_pop(); 389 } 390 391 392 static void trace_handler(struct pt_regs *regs, void *data) 393 { 394 got_interrupt = true; 395 memcpy((void *)&recorded_regs, regs, sizeof(struct pt_regs)); 396 regs->msr &= ~(MSR_SE | MSR_BE); 397 } 398 399 static void program_trace_handler(struct pt_regs *regs, void *data) 400 { 401 regs->msr &= ~(MSR_SE | MSR_BE); 402 regs->nip += 4; 403 } 404 405 extern char trace_insn[]; 406 extern char trace_insn2[]; 407 extern char trace_insn3[]; 408 extern char trace_rfid[]; 409 410 static void test_trace(void) 411 { 412 unsigned long msr; 413 414 report_prefix_push("trace"); 415 416 handle_exception(0xd00, &trace_handler, NULL); 417 418 msr = mfmsr() | MSR_SE; 419 asm volatile( 420 " mtmsr %0 \n" 421 ".global trace_insn \n" 422 "trace_insn: \n" 423 " nop \n" 424 : : "r"(msr) : "memory"); 425 426 report(got_interrupt, "interrupt on single step"); 427 got_interrupt = false; 428 report(recorded_regs.nip == (unsigned long)trace_insn + 4, 429 "single step interrupt at the correct address"); 430 if (cpu_has_siar) 431 report(mfspr(SPR_SIAR) == (unsigned long)trace_insn, 432 "single step recorded SIAR at the correct address"); 433 434 msr = mfmsr() | MSR_SE; 435 asm volatile( 436 " mtmsr %0 \n" 437 ".global trace_insn2 \n" 438 "trace_insn2: \n" 439 " b 1f \n" 440 " nop \n" 441 "1: \n" 442 : : "r"(msr) : "memory"); 443 444 report(got_interrupt, "interrupt on single step branch"); 445 got_interrupt = false; 446 report(recorded_regs.nip == (unsigned long)trace_insn2 + 8, 447 "single step interrupt at the correct address"); 448 if (cpu_has_siar) 449 report(mfspr(SPR_SIAR) == (unsigned long)trace_insn2, 450 "single step recorded SIAR at the correct address"); 451 452 msr = mfmsr() | MSR_BE; 453 asm volatile( 454 " mtmsr %0 \n" 455 ".global trace_insn3 \n" 456 "trace_insn3: \n" 457 " nop \n" 458 " b 1f \n" 459 " nop \n" 460 "1: \n" 461 : : "r"(msr) : "memory"); 462 463 report(got_interrupt, "interrupt on branch trace"); 464 got_interrupt = false; 465 report(recorded_regs.nip == (unsigned long)trace_insn3 + 12, 466 "branch trace interrupt at the correct address"); 467 if (cpu_has_siar) 468 report(mfspr(SPR_SIAR) == (unsigned long)trace_insn3 + 4, 469 "branch trace recorded SIAR at the correct address"); 470 471 handle_exception(0x700, &program_trace_handler, NULL); 472 msr = mfmsr() | MSR_SE; 473 asm volatile( 474 " mtmsr %0 \n" 475 " trap \n" 476 : : "r"(msr) : "memory"); 477 478 report(!got_interrupt, "no interrupt on single step trap"); 479 got_interrupt = false; 480 handle_exception(0x700, NULL, NULL); 481 482 msr = mfmsr() | MSR_SE; 483 mtspr(SPR_SRR0, (unsigned long)trace_rfid); 484 mtspr(SPR_SRR1, mfmsr()); 485 asm volatile( 486 " mtmsr %0 \n" 487 " rfid \n" 488 ".global trace_rfid \n" 489 "trace_rfid: \n" 490 : : "r"(msr) : "memory"); 491 492 report(!got_interrupt, "no interrupt on single step rfid"); 493 got_interrupt = false; 494 handle_exception(0xd00, NULL, NULL); 495 496 report_prefix_pop(); 497 } 498 499 500 int main(int argc, char **argv) 501 { 502 report_prefix_push("interrupts"); 503 504 if (vm_available()) 505 setup_vm(); 506 507 if (cpu_has_power_mce) 508 test_mce(); 509 test_mmu(); 510 test_illegal(); 511 test_privileged(); 512 test_dec(); 513 test_sc(); 514 test_trace(); 515 516 report_prefix_pop(); 517 518 return report_summary(); 519 } 520