xref: /kvm-unit-tests/lib/s390x/interrupt.c (revision c2c1663a96d9e3f9539913d5fd12344b4699ed87)
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 <sclp.h>
13 #include <interrupt.h>
14 #include <sie.h>
15 #include <fault.h>
16 #include <asm/page.h>
17 
18 static bool pgm_int_expected;
19 static bool ext_int_expected;
20 static void (*pgm_cleanup_func)(void);
21 
22 void expect_pgm_int(void)
23 {
24 	pgm_int_expected = true;
25 	lowcore.pgm_int_code = 0;
26 	lowcore.trans_exc_id = 0;
27 	mb();
28 }
29 
30 void expect_ext_int(void)
31 {
32 	ext_int_expected = true;
33 	lowcore.ext_int_code = 0;
34 	mb();
35 }
36 
37 uint16_t clear_pgm_int(void)
38 {
39 	uint16_t code;
40 
41 	mb();
42 	code = lowcore.pgm_int_code;
43 	lowcore.pgm_int_code = 0;
44 	lowcore.trans_exc_id = 0;
45 	pgm_int_expected = false;
46 	return code;
47 }
48 
49 void check_pgm_int_code(uint16_t code)
50 {
51 	mb();
52 	report(code == lowcore.pgm_int_code,
53 	       "Program interrupt: expected(%d) == received(%d)", code,
54 	       lowcore.pgm_int_code);
55 }
56 
57 void register_pgm_cleanup_func(void (*f)(void))
58 {
59 	pgm_cleanup_func = f;
60 }
61 
62 static void fixup_pgm_int(struct stack_frame_int *stack)
63 {
64 	/* If we have an error on SIE we directly move to sie_exit */
65 	if (lowcore.pgm_old_psw.addr >= (uint64_t)&sie_entry &&
66 	    lowcore.pgm_old_psw.addr <= (uint64_t)&sie_exit) {
67 		lowcore.pgm_old_psw.addr = (uint64_t)&sie_exit;
68 	}
69 
70 	switch (lowcore.pgm_int_code) {
71 	case PGM_INT_CODE_PRIVILEGED_OPERATION:
72 		/* Normal operation is in supervisor state, so this exception
73 		 * was produced intentionally and we should return to the
74 		 * supervisor state.
75 		 */
76 		lowcore.pgm_old_psw.mask &= ~PSW_MASK_PSTATE;
77 		break;
78 	case PGM_INT_CODE_PROTECTION:
79 		/* Handling for iep.c test case. */
80 		if (prot_is_iep((union teid) { .val = lowcore.trans_exc_id }))
81 			/*
82 			 * We branched to the instruction that caused
83 			 * the exception so we can use the return
84 			 * address in GR14 to jump back and continue
85 			 * executing test code.
86 			 */
87 			lowcore.pgm_old_psw.addr = stack->grs0[12];
88 		break;
89 	case PGM_INT_CODE_SEGMENT_TRANSLATION:
90 	case PGM_INT_CODE_PAGE_TRANSLATION:
91 	case PGM_INT_CODE_TRACE_TABLE:
92 	case PGM_INT_CODE_AFX_TRANSLATION:
93 	case PGM_INT_CODE_ASX_TRANSLATION:
94 	case PGM_INT_CODE_LX_TRANSLATION:
95 	case PGM_INT_CODE_EX_TRANSLATION:
96 	case PGM_INT_CODE_PRIMARY_AUTHORITY:
97 	case PGM_INT_CODE_SECONDARY_AUTHORITY:
98 	case PGM_INT_CODE_LFX_TRANSLATION:
99 	case PGM_INT_CODE_LSX_TRANSLATION:
100 	case PGM_INT_CODE_ALEN_TRANSLATION:
101 	case PGM_INT_CODE_ALE_SEQUENCE:
102 	case PGM_INT_CODE_ASTE_VALIDITY:
103 	case PGM_INT_CODE_ASTE_SEQUENCE:
104 	case PGM_INT_CODE_EXTENDED_AUTHORITY:
105 	case PGM_INT_CODE_LSTE_SEQUENCE:
106 	case PGM_INT_CODE_ASTE_INSTANCE:
107 	case PGM_INT_CODE_STACK_FULL:
108 	case PGM_INT_CODE_STACK_EMPTY:
109 	case PGM_INT_CODE_STACK_SPECIFICATION:
110 	case PGM_INT_CODE_STACK_TYPE:
111 	case PGM_INT_CODE_STACK_OPERATION:
112 	case PGM_INT_CODE_ASCE_TYPE:
113 	case PGM_INT_CODE_REGION_FIRST_TRANS:
114 	case PGM_INT_CODE_REGION_SECOND_TRANS:
115 	case PGM_INT_CODE_REGION_THIRD_TRANS:
116 	case PGM_INT_CODE_PER:
117 	case PGM_INT_CODE_CRYPTO_OPERATION:
118 	case PGM_INT_CODE_SECURE_STOR_ACCESS:
119 	case PGM_INT_CODE_NON_SECURE_STOR_ACCESS:
120 	case PGM_INT_CODE_SECURE_STOR_VIOLATION:
121 		/* The interrupt was nullified, the old PSW points at the
122 		 * responsible instruction. Forward the PSW so we don't loop.
123 		 */
124 		lowcore.pgm_old_psw.addr += lowcore.pgm_int_id;
125 	}
126 	/* suppressed/terminated/completed point already at the next address */
127 }
128 
129 static void print_storage_exception_information(void)
130 {
131 	switch (lowcore.pgm_int_code) {
132 	case PGM_INT_CODE_PROTECTION:
133 	case PGM_INT_CODE_PAGE_TRANSLATION:
134 	case PGM_INT_CODE_SEGMENT_TRANSLATION:
135 	case PGM_INT_CODE_ASCE_TYPE:
136 	case PGM_INT_CODE_REGION_FIRST_TRANS:
137 	case PGM_INT_CODE_REGION_SECOND_TRANS:
138 	case PGM_INT_CODE_REGION_THIRD_TRANS:
139 	case PGM_INT_CODE_SECURE_STOR_ACCESS:
140 	case PGM_INT_CODE_NON_SECURE_STOR_ACCESS:
141 	case PGM_INT_CODE_SECURE_STOR_VIOLATION:
142 		print_decode_teid(lowcore.trans_exc_id);
143 		break;
144 	}
145 }
146 
147 static void print_int_regs(struct stack_frame_int *stack)
148 {
149 	printf("\n");
150 	printf("GPRS:\n");
151 	printf("%016lx %016lx %016lx %016lx\n",
152 	       stack->grs1[0], stack->grs1[1], stack->grs0[0], stack->grs0[1]);
153 	printf("%016lx %016lx %016lx %016lx\n",
154 	       stack->grs0[2], stack->grs0[3], stack->grs0[4], stack->grs0[5]);
155 	printf("%016lx %016lx %016lx %016lx\n",
156 	       stack->grs0[6], stack->grs0[7], stack->grs0[8], stack->grs0[9]);
157 	printf("%016lx %016lx %016lx %016lx\n",
158 	       stack->grs0[10], stack->grs0[11], stack->grs0[12], stack->grs0[13]);
159 	printf("\n");
160 }
161 
162 static void print_pgm_info(struct stack_frame_int *stack)
163 
164 {
165 	bool in_sie;
166 
167 	in_sie = (lowcore.pgm_old_psw.addr >= (uintptr_t)sie_entry &&
168 		  lowcore.pgm_old_psw.addr <= (uintptr_t)sie_exit);
169 
170 	printf("\n");
171 	printf("Unexpected program interrupt %s: %#x on cpu %d at %#lx, ilen %d\n",
172 	       in_sie ? "in SIE" : "",
173 	       lowcore.pgm_int_code, stap(), lowcore.pgm_old_psw.addr, lowcore.pgm_int_id);
174 	print_int_regs(stack);
175 	dump_stack();
176 
177 	/* Dump stack doesn't end with a \n so we add it here instead */
178 	printf("\n");
179 	print_storage_exception_information();
180 	report_summary();
181 	abort();
182 }
183 
184 void handle_pgm_int(struct stack_frame_int *stack)
185 {
186 	if (!pgm_int_expected) {
187 		/* Force sclp_busy to false, otherwise we will loop forever */
188 		sclp_handle_ext();
189 		print_pgm_info(stack);
190 	}
191 
192 	pgm_int_expected = false;
193 
194 	if (pgm_cleanup_func)
195 		(*pgm_cleanup_func)();
196 	else
197 		fixup_pgm_int(stack);
198 }
199 
200 void handle_ext_int(struct stack_frame_int *stack)
201 {
202 	if (!ext_int_expected &&
203 	    lowcore.ext_int_code != EXT_IRQ_SERVICE_SIG) {
204 		report_abort("Unexpected external call interrupt (code %#x): on cpu %d at %#lx",
205 			     lowcore.ext_int_code, stap(), lowcore.ext_old_psw.addr);
206 		return;
207 	}
208 
209 	if (lowcore.ext_int_code == EXT_IRQ_SERVICE_SIG) {
210 		stack->crs[0] &= ~(1UL << 9);
211 		sclp_handle_ext();
212 	} else {
213 		ext_int_expected = false;
214 	}
215 
216 	if (!(stack->crs[0] & CR0_EXTM_MASK))
217 		lowcore.ext_old_psw.mask &= ~PSW_MASK_EXT;
218 }
219 
220 void handle_mcck_int(void)
221 {
222 	report_abort("Unexpected machine check interrupt: on cpu %d at %#lx",
223 		     stap(), lowcore.mcck_old_psw.addr);
224 }
225 
226 static void (*io_int_func)(void);
227 
228 void handle_io_int(void)
229 {
230 	if (io_int_func)
231 		return io_int_func();
232 
233 	report_abort("Unexpected io interrupt: on cpu %d at %#lx",
234 		     stap(), lowcore.io_old_psw.addr);
235 }
236 
237 int register_io_int_func(void (*f)(void))
238 {
239 	if (io_int_func)
240 		return -1;
241 	io_int_func = f;
242 	return 0;
243 }
244 
245 int unregister_io_int_func(void (*f)(void))
246 {
247 	if (io_int_func != f)
248 		return -1;
249 	io_int_func = NULL;
250 	return 0;
251 }
252 
253 void handle_svc_int(void)
254 {
255 	uint16_t code = lowcore.svc_int_code;
256 
257 	switch (code) {
258 	case SVC_LEAVE_PSTATE:
259 		lowcore.svc_old_psw.mask &= ~PSW_MASK_PSTATE;
260 		break;
261 	default:
262 		report_abort("Unexpected supervisor call interrupt: code %#x on cpu %d at %#lx",
263 			      code, stap(), lowcore.svc_old_psw.addr);
264 	}
265 }
266