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 18 static volatile bool got_interrupt; 19 static volatile struct pt_regs recorded_regs; 20 21 static void mce_handler(struct pt_regs *regs, void *opaque) 22 { 23 bool *is_fetch = opaque; 24 25 got_interrupt = true; 26 memcpy((void *)&recorded_regs, regs, sizeof(struct pt_regs)); 27 if (*is_fetch) 28 regs->nip = regs->link; 29 else 30 regs_advance_insn(regs); 31 } 32 33 static void fault_handler(struct pt_regs *regs, void *opaque) 34 { 35 memcpy((void *)&recorded_regs, regs, sizeof(struct pt_regs)); 36 if (regs->trap == 0x400 || regs->trap == 0x480) 37 regs->nip = regs->link; 38 else 39 regs_advance_insn(regs); 40 } 41 42 static void test_mce(void) 43 { 44 unsigned long addr = -4ULL; 45 uint8_t tmp; 46 bool is_fetch; 47 48 report_prefix_push("mce"); 49 50 handle_exception(0x200, mce_handler, &is_fetch); 51 handle_exception(0x300, fault_handler, NULL); 52 handle_exception(0x380, fault_handler, NULL); 53 handle_exception(0x400, fault_handler, NULL); 54 handle_exception(0x480, fault_handler, NULL); 55 56 if (machine_is_powernv()) { 57 enable_mcheck(); 58 } else { 59 report(mfmsr() & MSR_ME, "pseries machine has MSR[ME]=1"); 60 if (!(mfmsr() & MSR_ME)) { /* try to fix it */ 61 enable_mcheck(); 62 } 63 if (mfmsr() & MSR_ME) { 64 disable_mcheck(); 65 report(mfmsr() & MSR_ME, "pseries is unable to change MSR[ME]"); 66 if (!(mfmsr() & MSR_ME)) { /* try to fix it */ 67 enable_mcheck(); 68 } 69 } 70 } 71 72 is_fetch = false; 73 asm volatile("lbz %0,0(%1)" : "=r"(tmp) : "r"(addr)); 74 75 /* KVM does not MCE on access outside partition scope */ 76 report_kfail(host_is_kvm, got_interrupt, "MCE on access to invalid real address"); 77 if (got_interrupt) { 78 report(mfspr(SPR_DAR) == addr, "MCE sets DAR correctly"); 79 if (cpu_has_power_mce) 80 report(recorded_regs.msr & (1ULL << 21), "d-side MCE sets SRR1[42]"); 81 got_interrupt = false; 82 } 83 84 is_fetch = true; 85 asm volatile("mtctr %0 ; bctrl" :: "r"(addr) : "ctr", "lr"); 86 /* KVM does not MCE on access outside partition scope */ 87 report_kfail(host_is_kvm, got_interrupt, "MCE on fetch from invalid real address"); 88 if (got_interrupt) { 89 report(recorded_regs.nip == addr, "MCE sets SRR0 correctly"); 90 if (cpu_has_power_mce) 91 report(!(recorded_regs.msr & (1ULL << 21)), "i-side MCE clears SRR1[42]"); 92 got_interrupt = false; 93 } 94 95 handle_exception(0x200, NULL, NULL); 96 handle_exception(0x300, NULL, NULL); 97 handle_exception(0x380, NULL, NULL); 98 handle_exception(0x400, NULL, NULL); 99 handle_exception(0x480, NULL, NULL); 100 101 report_prefix_pop(); 102 } 103 104 static void dseg_handler(struct pt_regs *regs, void *data) 105 { 106 got_interrupt = true; 107 memcpy((void *)&recorded_regs, regs, sizeof(struct pt_regs)); 108 regs_advance_insn(regs); 109 regs->msr &= ~MSR_DR; 110 } 111 112 static void test_dseg(void) 113 { 114 uint64_t msr, tmp; 115 116 report_prefix_push("data segment"); 117 118 /* Some HV start in radix mode and need 0x300 */ 119 handle_exception(0x300, &dseg_handler, NULL); 120 handle_exception(0x380, &dseg_handler, NULL); 121 122 asm volatile( 123 " mfmsr %0 \n \ 124 ori %0,%0,%2 \n \ 125 mtmsrd %0 \n \ 126 lbz %1,0(0) " 127 : "=r"(msr), "=r"(tmp) : "i"(MSR_DR): "memory"); 128 129 report(got_interrupt, "interrupt on NULL dereference"); 130 got_interrupt = false; 131 132 handle_exception(0x300, NULL, NULL); 133 handle_exception(0x380, NULL, NULL); 134 135 report_prefix_pop(); 136 } 137 138 static void dec_handler(struct pt_regs *regs, void *data) 139 { 140 got_interrupt = true; 141 memcpy((void *)&recorded_regs, regs, sizeof(struct pt_regs)); 142 regs->msr &= ~MSR_EE; 143 } 144 145 static void test_dec(void) 146 { 147 uint64_t msr; 148 uint64_t tb; 149 150 report_prefix_push("decrementer"); 151 152 handle_exception(0x900, &dec_handler, NULL); 153 154 asm volatile( 155 " mtdec %1 \n \ 156 mfmsr %0 \n \ 157 ori %0,%0,%2 \n \ 158 mtmsrd %0,1 " 159 : "=r"(msr) : "r"(10000), "i"(MSR_EE): "memory"); 160 161 tb = get_tb(); 162 while (!got_interrupt) { 163 if (get_tb() - tb > tb_hz * 5) 164 break; /* timeout 5s */ 165 } 166 167 report(got_interrupt, "interrupt on decrementer underflow"); 168 got_interrupt = false; 169 170 handle_exception(0x900, NULL, NULL); 171 172 if (!machine_is_powernv()) 173 goto done; /* Skip HV tests */ 174 175 handle_exception(0x980, &dec_handler, NULL); 176 177 mtspr(SPR_LPCR, mfspr(SPR_LPCR) | LPCR_HDICE); 178 asm volatile( 179 " mtspr 0x136,%1 \n \ 180 mtdec %3 \n \ 181 mfmsr %0 \n \ 182 ori %0,%0,%2 \n \ 183 mtmsrd %0,1 " 184 : "=r"(msr) : "r"(10000), "i"(MSR_EE), "r"(0x7fffffff): "memory"); 185 186 tb = get_tb(); 187 while (!got_interrupt) { 188 if (get_tb() - tb > tb_hz * 5) 189 break; /* timeout 5s */ 190 } 191 192 mtspr(SPR_LPCR, mfspr(SPR_LPCR) & ~LPCR_HDICE); 193 194 report(got_interrupt, "interrupt on hdecrementer underflow"); 195 got_interrupt = false; 196 197 handle_exception(0x980, NULL, NULL); 198 199 done: 200 report_prefix_pop(); 201 } 202 203 204 static volatile uint64_t recorded_heir; 205 206 static void heai_handler(struct pt_regs *regs, void *data) 207 { 208 got_interrupt = true; 209 memcpy((void *)&recorded_regs, regs, sizeof(struct pt_regs)); 210 regs_advance_insn(regs); 211 if (cpu_has_heai) 212 recorded_heir = mfspr(SPR_HEIR); 213 } 214 215 static void program_handler(struct pt_regs *regs, void *data) 216 { 217 got_interrupt = true; 218 memcpy((void *)&recorded_regs, regs, sizeof(struct pt_regs)); 219 regs_advance_insn(regs); 220 } 221 222 /* 223 * This tests invalid instruction handling. powernv (HV) should take an 224 * HEAI interrupt with the HEIR SPR set to the instruction image. pseries 225 * (guest) should take a program interrupt. CPUs which support prefix 226 * should report prefix instruction in (H)SRR1[34]. 227 */ 228 static void test_illegal(void) 229 { 230 report_prefix_push("illegal instruction"); 231 232 if (machine_is_powernv()) { 233 handle_exception(0xe40, &heai_handler, NULL); 234 } else { 235 handle_exception(0x700, &program_handler, NULL); 236 } 237 238 asm volatile(".long 0x12345678" ::: "memory"); 239 report(got_interrupt, "interrupt on invalid instruction"); 240 got_interrupt = false; 241 if (cpu_has_heai) 242 report(recorded_heir == 0x12345678, "HEIR: 0x%08lx", recorded_heir); 243 report(!regs_is_prefix(&recorded_regs), "(H)SRR1 prefix bit clear"); 244 245 if (cpu_has_prefix) { 246 asm volatile(".balign 8 ; .long 0x04000123; .long 0x00badc0d"); 247 report(got_interrupt, "interrupt on invalid prefix instruction"); 248 got_interrupt = false; 249 if (cpu_has_heai) 250 report(recorded_heir == 0x0400012300badc0d, "HEIR: 0x%08lx", recorded_heir); 251 report(regs_is_prefix(&recorded_regs), "(H)SRR1 prefix bit set"); 252 } 253 254 handle_exception(0xe40, NULL, NULL); 255 handle_exception(0x700, NULL, NULL); 256 257 report_prefix_pop(); 258 } 259 260 static void sc_handler(struct pt_regs *regs, void *data) 261 { 262 got_interrupt = true; 263 memcpy((void *)&recorded_regs, regs, sizeof(struct pt_regs)); 264 } 265 266 static void test_sc(void) 267 { 268 report_prefix_push("syscall"); 269 270 handle_exception(0xc00, &sc_handler, NULL); 271 272 asm volatile("sc 0" ::: "memory"); 273 274 report(got_interrupt, "interrupt on sc 0 instruction"); 275 got_interrupt = false; 276 if (cpu_has_sc_lev) 277 report(((recorded_regs.msr >> 20) & 0x3) == 0, "SRR1 set LEV=0"); 278 if (machine_is_powernv()) { 279 asm volatile("sc 1" ::: "memory"); 280 281 report(got_interrupt, "interrupt on sc 1 instruction"); 282 got_interrupt = false; 283 if (cpu_has_sc_lev) 284 report(((recorded_regs.msr >> 20) & 0x3) == 1, "SRR1 set LEV=1"); 285 } 286 287 handle_exception(0xc00, NULL, NULL); 288 289 report_prefix_pop(); 290 } 291 292 293 static void trace_handler(struct pt_regs *regs, void *data) 294 { 295 got_interrupt = true; 296 memcpy((void *)&recorded_regs, regs, sizeof(struct pt_regs)); 297 regs->msr &= ~(MSR_SE | MSR_BE); 298 } 299 300 static void program_trace_handler(struct pt_regs *regs, void *data) 301 { 302 regs->msr &= ~(MSR_SE | MSR_BE); 303 regs->nip += 4; 304 } 305 306 extern char trace_insn[]; 307 extern char trace_insn2[]; 308 extern char trace_insn3[]; 309 extern char trace_rfid[]; 310 311 static void test_trace(void) 312 { 313 unsigned long msr; 314 315 report_prefix_push("trace"); 316 317 handle_exception(0xd00, &trace_handler, NULL); 318 319 msr = mfmsr() | MSR_SE; 320 asm volatile( 321 " mtmsr %0 \n" 322 ".global trace_insn \n" 323 "trace_insn: \n" 324 " nop \n" 325 : : "r"(msr) : "memory"); 326 327 report(got_interrupt, "interrupt on single step"); 328 got_interrupt = false; 329 report(recorded_regs.nip == (unsigned long)trace_insn + 4, 330 "single step interrupt at the correct address"); 331 if (cpu_has_siar) 332 report(mfspr(SPR_SIAR) == (unsigned long)trace_insn, 333 "single step recorded SIAR at the correct address"); 334 335 msr = mfmsr() | MSR_SE; 336 asm volatile( 337 " mtmsr %0 \n" 338 ".global trace_insn2 \n" 339 "trace_insn2: \n" 340 " b 1f \n" 341 " nop \n" 342 "1: \n" 343 : : "r"(msr) : "memory"); 344 345 report(got_interrupt, "interrupt on single step branch"); 346 got_interrupt = false; 347 report(recorded_regs.nip == (unsigned long)trace_insn2 + 8, 348 "single step interrupt at the correct address"); 349 if (cpu_has_siar) 350 report(mfspr(SPR_SIAR) == (unsigned long)trace_insn2, 351 "single step recorded SIAR at the correct address"); 352 353 msr = mfmsr() | MSR_BE; 354 asm volatile( 355 " mtmsr %0 \n" 356 ".global trace_insn3 \n" 357 "trace_insn3: \n" 358 " nop \n" 359 " b 1f \n" 360 " nop \n" 361 "1: \n" 362 : : "r"(msr) : "memory"); 363 364 report(got_interrupt, "interrupt on branch trace"); 365 got_interrupt = false; 366 report(recorded_regs.nip == (unsigned long)trace_insn3 + 12, 367 "branch trace interrupt at the correct address"); 368 if (cpu_has_siar) 369 report(mfspr(SPR_SIAR) == (unsigned long)trace_insn3 + 4, 370 "branch trace recorded SIAR at the correct address"); 371 372 handle_exception(0x700, &program_trace_handler, NULL); 373 msr = mfmsr() | MSR_SE; 374 asm volatile( 375 " mtmsr %0 \n" 376 " trap \n" 377 : : "r"(msr) : "memory"); 378 379 report(!got_interrupt, "no interrupt on single step trap"); 380 got_interrupt = false; 381 handle_exception(0x700, NULL, NULL); 382 383 msr = mfmsr() | MSR_SE; 384 mtspr(SPR_SRR0, (unsigned long)trace_rfid); 385 mtspr(SPR_SRR1, mfmsr()); 386 asm volatile( 387 " mtmsr %0 \n" 388 " rfid \n" 389 ".global trace_rfid \n" 390 "trace_rfid: \n" 391 : : "r"(msr) : "memory"); 392 393 report(!got_interrupt, "no interrupt on single step rfid"); 394 got_interrupt = false; 395 handle_exception(0xd00, NULL, NULL); 396 397 report_prefix_pop(); 398 } 399 400 401 int main(int argc, char **argv) 402 { 403 report_prefix_push("interrupts"); 404 405 if (cpu_has_power_mce) 406 test_mce(); 407 test_dseg(); 408 test_illegal(); 409 test_dec(); 410 test_sc(); 411 test_trace(); 412 413 report_prefix_pop(); 414 415 return report_summary(); 416 } 417