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