xref: /kvm-unit-tests/lib/s390x/interrupt.c (revision cdc9bd7b375b865cfca1b7a7550a075527e41b44)
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 /*
3  * s390x interrupt handling
4  *
5  * Copyright (c) 2017 Red Hat Inc
6  *
7  * Authors:
8  *  David Hildenbrand <david@redhat.com>
9  */
10 #include <libcflat.h>
11 #include <asm/barrier.h>
12 #include <asm/asm-offsets.h>
13 #include <sclp.h>
14 #include <interrupt.h>
15 #include <sie.h>
16 #include <fault.h>
17 #include <asm/page.h>
18 #include "smp.h"
19 
20 /**
21  * expect_pgm_int - Expect a program interrupt on the current CPU.
22  */
23 void expect_pgm_int(void)
24 {
25 	THIS_CPU->pgm_int_expected = true;
26 	lowcore.pgm_int_code = 0;
27 	lowcore.trans_exc_id = 0;
28 	mb();
29 }
30 
31 /**
32  * expect_ext_int - Expect an external interrupt on the current CPU.
33  */
34 void expect_ext_int(void)
35 {
36 	THIS_CPU->ext_int_expected = true;
37 	lowcore.ext_int_code = 0;
38 	mb();
39 }
40 
41 /**
42  * clear_pgm_int - Clear program interrupt information
43  *
44  * Clear program interrupt information, including the expected program
45  * interrupt flag.
46  * No program interrupts are expected after calling this function.
47  *
48  * Return: the program interrupt code before clearing
49  */
50 uint16_t clear_pgm_int(void)
51 {
52 	uint16_t code;
53 
54 	mb();
55 	code = lowcore.pgm_int_code;
56 	lowcore.pgm_int_code = 0;
57 	lowcore.trans_exc_id = 0;
58 	THIS_CPU->pgm_int_expected = false;
59 	return code;
60 }
61 
62 /**
63  * check_pgm_int_code - Check the program interrupt code on the current CPU.
64  * @code the expected program interrupt code on the current CPU
65  *
66  * Check and report if the program interrupt on the current CPU matches the
67  * expected one.
68  */
69 void check_pgm_int_code(uint16_t code)
70 {
71 	mb();
72 	report(code == lowcore.pgm_int_code,
73 	       "Program interrupt: expected(%d) == received(%d)", code,
74 	       lowcore.pgm_int_code);
75 }
76 
77 /**
78  * register_pgm_cleanup_func - Register a cleanup function for progam
79  * interrupts for the current CPU.
80  * @f the cleanup function to be registered on the current CPU
81  *
82  * Register a cleanup function to be called at the end of the normal
83  * interrupt handling for program interrupts for this CPU.
84  *
85  * Pass NULL to unregister a previously registered cleanup function.
86  */
87 void register_pgm_cleanup_func(void (*f)(struct stack_frame_int *))
88 {
89 	THIS_CPU->pgm_cleanup_func = f;
90 }
91 
92 /**
93  * register_ext_cleanup_func - Register a cleanup function for external
94  * interrupts for the current CPU.
95  * @f the cleanup function to be registered on the current CPU
96  *
97  * Register a cleanup function to be called at the end of the normal
98  * interrupt handling for external interrupts for this CPU.
99  *
100  * Pass NULL to unregister a previously registered cleanup function.
101  */
102 void register_ext_cleanup_func(void (*f)(struct stack_frame_int *))
103 {
104 	THIS_CPU->ext_cleanup_func = f;
105 }
106 
107 static void fixup_pgm_int(struct stack_frame_int *stack)
108 {
109 	/* If we have an error on SIE we directly move to sie_exit */
110 	if (lowcore.pgm_old_psw.addr >= (uint64_t)&sie_entry &&
111 	    lowcore.pgm_old_psw.addr <= (uint64_t)&sie_exit) {
112 		lowcore.pgm_old_psw.addr = (uint64_t)&sie_exit;
113 	}
114 
115 	switch (lowcore.pgm_int_code) {
116 	case PGM_INT_CODE_PRIVILEGED_OPERATION:
117 		/* Normal operation is in supervisor state, so this exception
118 		 * was produced intentionally and we should return to the
119 		 * supervisor state.
120 		 */
121 		lowcore.pgm_old_psw.mask &= ~PSW_MASK_PSTATE;
122 		break;
123 	case PGM_INT_CODE_PROTECTION:
124 		/* Handling for iep.c test case. */
125 		if (prot_is_iep((union teid) { .val = lowcore.trans_exc_id }))
126 			/*
127 			 * We branched to the instruction that caused
128 			 * the exception so we can use the return
129 			 * address in GR14 to jump back and continue
130 			 * executing test code.
131 			 */
132 			lowcore.pgm_old_psw.addr = stack->grs0[12];
133 		break;
134 	case PGM_INT_CODE_SEGMENT_TRANSLATION:
135 	case PGM_INT_CODE_PAGE_TRANSLATION:
136 	case PGM_INT_CODE_TRACE_TABLE:
137 	case PGM_INT_CODE_AFX_TRANSLATION:
138 	case PGM_INT_CODE_ASX_TRANSLATION:
139 	case PGM_INT_CODE_LX_TRANSLATION:
140 	case PGM_INT_CODE_EX_TRANSLATION:
141 	case PGM_INT_CODE_PRIMARY_AUTHORITY:
142 	case PGM_INT_CODE_SECONDARY_AUTHORITY:
143 	case PGM_INT_CODE_LFX_TRANSLATION:
144 	case PGM_INT_CODE_LSX_TRANSLATION:
145 	case PGM_INT_CODE_ALEN_TRANSLATION:
146 	case PGM_INT_CODE_ALE_SEQUENCE:
147 	case PGM_INT_CODE_ASTE_VALIDITY:
148 	case PGM_INT_CODE_ASTE_SEQUENCE:
149 	case PGM_INT_CODE_EXTENDED_AUTHORITY:
150 	case PGM_INT_CODE_LSTE_SEQUENCE:
151 	case PGM_INT_CODE_ASTE_INSTANCE:
152 	case PGM_INT_CODE_STACK_FULL:
153 	case PGM_INT_CODE_STACK_EMPTY:
154 	case PGM_INT_CODE_STACK_SPECIFICATION:
155 	case PGM_INT_CODE_STACK_TYPE:
156 	case PGM_INT_CODE_STACK_OPERATION:
157 	case PGM_INT_CODE_ASCE_TYPE:
158 	case PGM_INT_CODE_REGION_FIRST_TRANS:
159 	case PGM_INT_CODE_REGION_SECOND_TRANS:
160 	case PGM_INT_CODE_REGION_THIRD_TRANS:
161 	case PGM_INT_CODE_PER:
162 	case PGM_INT_CODE_CRYPTO_OPERATION:
163 	case PGM_INT_CODE_SECURE_STOR_ACCESS:
164 	case PGM_INT_CODE_NON_SECURE_STOR_ACCESS:
165 	case PGM_INT_CODE_SECURE_STOR_VIOLATION:
166 		/* The interrupt was nullified, the old PSW points at the
167 		 * responsible instruction. Forward the PSW so we don't loop.
168 		 */
169 		lowcore.pgm_old_psw.addr += lowcore.pgm_int_id;
170 	}
171 	/* suppressed/terminated/completed point already at the next address */
172 }
173 
174 static void print_storage_exception_information(void)
175 {
176 	switch (lowcore.pgm_int_code) {
177 	case PGM_INT_CODE_PROTECTION:
178 	case PGM_INT_CODE_PAGE_TRANSLATION:
179 	case PGM_INT_CODE_SEGMENT_TRANSLATION:
180 	case PGM_INT_CODE_ASCE_TYPE:
181 	case PGM_INT_CODE_REGION_FIRST_TRANS:
182 	case PGM_INT_CODE_REGION_SECOND_TRANS:
183 	case PGM_INT_CODE_REGION_THIRD_TRANS:
184 	case PGM_INT_CODE_SECURE_STOR_ACCESS:
185 	case PGM_INT_CODE_NON_SECURE_STOR_ACCESS:
186 	case PGM_INT_CODE_SECURE_STOR_VIOLATION:
187 		print_decode_teid(lowcore.trans_exc_id);
188 		break;
189 	}
190 }
191 
192 static void print_int_regs(struct stack_frame_int *stack, bool sie)
193 {
194 	struct kvm_s390_sie_block *sblk;
195 
196 	printf("\n");
197 	printf("%s\n", sie ? "Guest registers:" : "Host registers:");
198 	printf("GPRS:\n");
199 	printf("%016lx %016lx %016lx %016lx\n",
200 	       stack->grs1[0], stack->grs1[1], stack->grs0[0], stack->grs0[1]);
201 	printf("%016lx %016lx %016lx %016lx\n",
202 	       stack->grs0[2], stack->grs0[3], stack->grs0[4], stack->grs0[5]);
203 	printf("%016lx %016lx %016lx %016lx\n",
204 	       stack->grs0[6], stack->grs0[7], stack->grs0[8], stack->grs0[9]);
205 
206 	if (sie) {
207 		sblk = (struct kvm_s390_sie_block *)stack->grs0[12];
208 		printf("%016lx %016lx %016lx %016lx\n",
209 		       stack->grs0[10], stack->grs0[11], sblk->gg14, sblk->gg15);
210 	} else {
211 		printf("%016lx %016lx %016lx %016lx\n",
212 		       stack->grs0[10], stack->grs0[11], stack->grs0[12], stack->grs0[13]);
213 	}
214 
215 	printf("\n");
216 }
217 
218 static void print_pgm_info(struct stack_frame_int *stack)
219 
220 {
221 	bool in_sie, in_sie_gregs;
222 	struct vm_save_area *vregs;
223 
224 	in_sie = (lowcore.pgm_old_psw.addr >= (uintptr_t)sie_entry &&
225 		  lowcore.pgm_old_psw.addr <= (uintptr_t)sie_exit);
226 	in_sie_gregs = (lowcore.pgm_old_psw.addr >= (uintptr_t)sie_entry_gregs &&
227 			lowcore.pgm_old_psw.addr < (uintptr_t)sie_exit_gregs);
228 
229 	printf("\n");
230 	printf("Unexpected program interrupt %s: %#x on cpu %d at %#lx, ilen %d\n",
231 	       in_sie ? "in SIE" : "",
232 	       lowcore.pgm_int_code, stap(), lowcore.pgm_old_psw.addr, lowcore.pgm_int_id);
233 
234 	/*
235 	 * If we fall out of SIE before loading the host registers,
236 	 * then we need to do it here so we print the host registers
237 	 * and not the guest registers.
238 	 *
239 	 * Back tracing is actually not a problem since SIE restores gr15.
240 	 */
241 	if (in_sie_gregs) {
242 		print_int_regs(stack, true);
243 		vregs = *((struct vm_save_area **)(stack->grs0[13] + __SF_SIE_SAVEAREA));
244 
245 		/*
246 		 * The grs are not linear on the interrupt stack frame.
247 		 * We copy 0 and 1 here and 2 - 15 with the memcopy below.
248 		 */
249 		stack->grs1[0] = vregs->host.grs[0];
250 		stack->grs1[1] = vregs->host.grs[1];
251 		/*  2 - 15 */
252 		memcpy(stack->grs0, &vregs->host.grs[2], sizeof(stack->grs0) - 8);
253 	}
254 	print_int_regs(stack, false);
255 	dump_stack();
256 
257 	/* Dump stack doesn't end with a \n so we add it here instead */
258 	printf("\n");
259 	print_storage_exception_information();
260 	report_summary();
261 	abort();
262 }
263 
264 void handle_pgm_int(struct stack_frame_int *stack)
265 {
266 	if (THIS_CPU->in_interrupt_handler) {
267 		/* Something went very wrong, stop everything now without printing anything */
268 		smp_teardown();
269 		disabled_wait(0xfa12edbad21);
270 	}
271 	if (!THIS_CPU->pgm_int_expected) {
272 		/* Force sclp_busy to false, otherwise we will loop forever */
273 		sclp_handle_ext();
274 		print_pgm_info(stack);
275 	}
276 
277 	THIS_CPU->pgm_int_expected = false;
278 	THIS_CPU->in_interrupt_handler = true;
279 
280 	if (THIS_CPU->pgm_cleanup_func)
281 		THIS_CPU->pgm_cleanup_func(stack);
282 	else
283 		fixup_pgm_int(stack);
284 	THIS_CPU->in_interrupt_handler = false;
285 }
286 
287 void handle_ext_int(struct stack_frame_int *stack)
288 {
289 	THIS_CPU->in_interrupt_handler = true;
290 	if (!THIS_CPU->ext_int_expected && lowcore.ext_int_code != EXT_IRQ_SERVICE_SIG) {
291 		report_abort("Unexpected external call interrupt (code %#x): on cpu %d at %#lx",
292 			     lowcore.ext_int_code, stap(), lowcore.ext_old_psw.addr);
293 		return;
294 	}
295 
296 	if (lowcore.ext_int_code == EXT_IRQ_SERVICE_SIG) {
297 		stack->crs[0] &= ~(1UL << 9);
298 		sclp_handle_ext();
299 	} else {
300 		THIS_CPU->ext_int_expected = false;
301 	}
302 
303 	if (!(stack->crs[0] & CR0_EXTM_MASK))
304 		lowcore.ext_old_psw.mask &= ~PSW_MASK_EXT;
305 
306 	if (THIS_CPU->ext_cleanup_func)
307 		THIS_CPU->ext_cleanup_func(stack);
308 	THIS_CPU->in_interrupt_handler = false;
309 }
310 
311 void handle_mcck_int(void)
312 {
313 	report_abort("Unexpected machine check interrupt: on cpu %d at %#lx",
314 		     stap(), lowcore.mcck_old_psw.addr);
315 }
316 
317 static void (*io_int_func)(void);
318 
319 void handle_io_int(void)
320 {
321 	THIS_CPU->in_interrupt_handler = true;
322 	if (io_int_func)
323 		io_int_func();
324 	else
325 		report_abort("Unexpected io interrupt: on cpu %d at %#lx",
326 			     stap(), lowcore.io_old_psw.addr);
327 	THIS_CPU->in_interrupt_handler = false;
328 }
329 
330 int register_io_int_func(void (*f)(void))
331 {
332 	if (io_int_func)
333 		return -1;
334 	io_int_func = f;
335 	return 0;
336 }
337 
338 int unregister_io_int_func(void (*f)(void))
339 {
340 	if (io_int_func != f)
341 		return -1;
342 	io_int_func = NULL;
343 	return 0;
344 }
345 
346 void handle_svc_int(void)
347 {
348 	uint16_t code = lowcore.svc_int_code;
349 
350 	switch (code) {
351 	case SVC_LEAVE_PSTATE:
352 		lowcore.svc_old_psw.mask &= ~PSW_MASK_PSTATE;
353 		break;
354 	default:
355 		report_abort("Unexpected supervisor call interrupt: code %#x on cpu %d at %#lx",
356 			      code, stap(), lowcore.svc_old_psw.addr);
357 	}
358 }
359