xref: /kvm-unit-tests/lib/s390x/interrupt.c (revision 68721b97806e9c1f766573a85d0e4b7ee27c9576)
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 
16 static bool pgm_int_expected;
17 static bool ext_int_expected;
18 static void (*pgm_cleanup_func)(void);
19 static struct lowcore *lc;
20 
21 void expect_pgm_int(void)
22 {
23 	pgm_int_expected = true;
24 	lc->pgm_int_code = 0;
25 	lc->trans_exc_id = 0;
26 	mb();
27 }
28 
29 void expect_ext_int(void)
30 {
31 	ext_int_expected = true;
32 	lc->ext_int_code = 0;
33 	mb();
34 }
35 
36 uint16_t clear_pgm_int(void)
37 {
38 	uint16_t code;
39 
40 	mb();
41 	code = lc->pgm_int_code;
42 	lc->pgm_int_code = 0;
43 	lc->trans_exc_id = 0;
44 	pgm_int_expected = false;
45 	return code;
46 }
47 
48 void check_pgm_int_code(uint16_t code)
49 {
50 	mb();
51 	report(code == lc->pgm_int_code,
52 	       "Program interrupt: expected(%d) == received(%d)", code,
53 	       lc->pgm_int_code);
54 }
55 
56 void register_pgm_cleanup_func(void (*f)(void))
57 {
58 	pgm_cleanup_func = f;
59 }
60 
61 static void fixup_pgm_int(struct stack_frame_int *stack)
62 {
63 	/* If we have an error on SIE we directly move to sie_exit */
64 	if (lc->pgm_old_psw.addr >= (uint64_t)&sie_entry &&
65 	    lc->pgm_old_psw.addr <= (uint64_t)&sie_exit) {
66 		lc->pgm_old_psw.addr = (uint64_t)&sie_exit;
67 	}
68 
69 	switch (lc->pgm_int_code) {
70 	case PGM_INT_CODE_PRIVILEGED_OPERATION:
71 		/* Normal operation is in supervisor state, so this exception
72 		 * was produced intentionally and we should return to the
73 		 * supervisor state.
74 		 */
75 		lc->pgm_old_psw.mask &= ~PSW_MASK_PSTATE;
76 		break;
77 	case PGM_INT_CODE_PROTECTION:
78 		/* Handling for iep.c test case. */
79 		if (lc->trans_exc_id & 0x80UL && lc->trans_exc_id & 0x04UL &&
80 		    !(lc->trans_exc_id & 0x08UL))
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 			lc->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 		lc->pgm_old_psw.addr += lc->pgm_int_id;
125 	}
126 	/* suppressed/terminated/completed point already at the next address */
127 }
128 
129 static void print_int_regs(struct stack_frame_int *stack)
130 {
131 	printf("\n");
132 	printf("GPRS:\n");
133 	printf("%016lx %016lx %016lx %016lx\n",
134 	       stack->grs1[0], stack->grs1[1], stack->grs0[0], stack->grs0[1]);
135 	printf("%016lx %016lx %016lx %016lx\n",
136 	       stack->grs0[2], stack->grs0[3], stack->grs0[4], stack->grs0[5]);
137 	printf("%016lx %016lx %016lx %016lx\n",
138 	       stack->grs0[6], stack->grs0[7], stack->grs0[8], stack->grs0[9]);
139 	printf("%016lx %016lx %016lx %016lx\n",
140 	       stack->grs0[10], stack->grs0[11], stack->grs0[12], stack->grs0[13]);
141 	printf("\n");
142 }
143 
144 static void print_pgm_info(struct stack_frame_int *stack)
145 
146 {
147 	bool in_sie;
148 
149 	in_sie = (lc->pgm_old_psw.addr >= (uintptr_t)sie_entry &&
150 		  lc->pgm_old_psw.addr <= (uintptr_t)sie_exit);
151 
152 	printf("\n");
153 	printf("Unexpected program interrupt %s: %d on cpu %d at %#lx, ilen %d\n",
154 	       in_sie ? "in SIE" : "",
155 	       lc->pgm_int_code, stap(), lc->pgm_old_psw.addr, lc->pgm_int_id);
156 	print_int_regs(stack);
157 	dump_stack();
158 	report_summary();
159 	abort();
160 }
161 
162 void handle_pgm_int(struct stack_frame_int *stack)
163 {
164 	if (!pgm_int_expected) {
165 		/* Force sclp_busy to false, otherwise we will loop forever */
166 		sclp_handle_ext();
167 		print_pgm_info(stack);
168 	}
169 
170 	pgm_int_expected = false;
171 
172 	if (pgm_cleanup_func)
173 		(*pgm_cleanup_func)();
174 	else
175 		fixup_pgm_int(stack);
176 }
177 
178 void handle_ext_int(struct stack_frame_int *stack)
179 {
180 	if (!ext_int_expected &&
181 	    lc->ext_int_code != EXT_IRQ_SERVICE_SIG) {
182 		report_abort("Unexpected external call interrupt (code %#x): on cpu %d at %#lx",
183 			     lc->ext_int_code, stap(), lc->ext_old_psw.addr);
184 		return;
185 	}
186 
187 	if (lc->ext_int_code == EXT_IRQ_SERVICE_SIG) {
188 		stack->crs[0] &= ~(1UL << 9);
189 		sclp_handle_ext();
190 	} else {
191 		ext_int_expected = false;
192 	}
193 
194 	if (!(stack->crs[0] & CR0_EXTM_MASK))
195 		lc->ext_old_psw.mask &= ~PSW_MASK_EXT;
196 }
197 
198 void handle_mcck_int(void)
199 {
200 	report_abort("Unexpected machine check interrupt: on cpu %d at %#lx",
201 		     stap(), lc->mcck_old_psw.addr);
202 }
203 
204 static void (*io_int_func)(void);
205 
206 void handle_io_int(void)
207 {
208 	if (io_int_func)
209 		return io_int_func();
210 
211 	report_abort("Unexpected io interrupt: on cpu %d at %#lx",
212 		     stap(), lc->io_old_psw.addr);
213 }
214 
215 int register_io_int_func(void (*f)(void))
216 {
217 	if (io_int_func)
218 		return -1;
219 	io_int_func = f;
220 	return 0;
221 }
222 
223 int unregister_io_int_func(void (*f)(void))
224 {
225 	if (io_int_func != f)
226 		return -1;
227 	io_int_func = NULL;
228 	return 0;
229 }
230 
231 void handle_svc_int(void)
232 {
233 	uint16_t code = lc->svc_int_code;
234 
235 	switch (code) {
236 	case SVC_LEAVE_PSTATE:
237 		lc->svc_old_psw.mask &= ~PSW_MASK_PSTATE;
238 		break;
239 	default:
240 		report_abort("Unexpected supervisor call interrupt: code %#x on cpu %d at %#lx",
241 			      code, stap(), lc->svc_old_psw.addr);
242 	}
243 }
244