xref: /kvm-unit-tests/powerpc/interrupts.c (revision 93c847c1e5cbe266496ee66dc83dcfa24c401c96)
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 
mce_handler(struct pt_regs * regs,void * opaque)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 
fault_handler(struct pt_regs * regs,void * opaque)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 
test_mce(void)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 
dside_handler(struct pt_regs * regs,void * data)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 
iside_handler(struct pt_regs * regs,void * data)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 
test_dseg_nommu(void)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 
test_mmu(void)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 
dec_handler(struct pt_regs * regs,void * data)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 
test_dec(void)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 
heai_handler(struct pt_regs * regs,void * data)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 
program_handler(struct pt_regs * regs,void * data)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  */
test_illegal(void)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 
dec_ignore_handler(struct pt_regs * regs,void * data)332 static void dec_ignore_handler(struct pt_regs *regs, void *data)
333 {
334 	mtspr(SPR_DEC, 0x7fffffff);
335 }
336 
test_privileged(void)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 
sc_handler(struct pt_regs * regs,void * data)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 
test_sc(void)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 
trace_handler(struct pt_regs * regs,void * data)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 
program_trace_handler(struct pt_regs * regs,void * data)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 
test_trace(void)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 
main(int argc,char ** argv)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