xref: /kvm-unit-tests/lib/s390x/interrupt.c (revision a991ed2fb5eef30af4e402cc79bcbae067d43e6b)
1 /*
2  * s390x interrupt handling
3  *
4  * Copyright (c) 2017 Red Hat Inc
5  *
6  * Authors:
7  *  David Hildenbrand <david@redhat.com>
8  *
9  * This code is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU Library General Public License version 2.
11  */
12 #include <libcflat.h>
13 #include <asm/barrier.h>
14 #include <sclp.h>
15 #include <interrupt.h>
16 
17 static bool pgm_int_expected;
18 static bool ext_int_expected;
19 static void (*pgm_cleanup_func)(void);
20 static struct lowcore *lc;
21 
22 void expect_pgm_int(void)
23 {
24 	pgm_int_expected = true;
25 	lc->pgm_int_code = 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 	pgm_int_expected = false;
44 	return code;
45 }
46 
47 void check_pgm_int_code(uint16_t code)
48 {
49 	mb();
50 	report(code == lc->pgm_int_code,
51 	       "Program interrupt: expected(%d) == received(%d)", code,
52 	       lc->pgm_int_code);
53 }
54 
55 void register_pgm_cleanup_func(void (*f)(void))
56 {
57 	pgm_cleanup_func = f;
58 }
59 
60 static void fixup_pgm_int(void)
61 {
62 	switch (lc->pgm_int_code) {
63 	case PGM_INT_CODE_PRIVILEGED_OPERATION:
64 		/* Normal operation is in supervisor state, so this exception
65 		 * was produced intentionally and we should return to the
66 		 * supervisor state.
67 		 */
68 		lc->pgm_old_psw.mask &= ~PSW_MASK_PSTATE;
69 		break;
70 	case PGM_INT_CODE_PROTECTION:
71 		/* Handling for iep.c test case. */
72 		if (lc->trans_exc_id & 0x80UL && lc->trans_exc_id & 0x04UL &&
73 		    !(lc->trans_exc_id & 0x08UL))
74 			lc->pgm_old_psw.addr = lc->sw_int_grs[14];
75 		break;
76 	case PGM_INT_CODE_SEGMENT_TRANSLATION:
77 	case PGM_INT_CODE_PAGE_TRANSLATION:
78 	case PGM_INT_CODE_TRACE_TABLE:
79 	case PGM_INT_CODE_AFX_TRANSLATION:
80 	case PGM_INT_CODE_ASX_TRANSLATION:
81 	case PGM_INT_CODE_LX_TRANSLATION:
82 	case PGM_INT_CODE_EX_TRANSLATION:
83 	case PGM_INT_CODE_PRIMARY_AUTHORITY:
84 	case PGM_INT_CODE_SECONDARY_AUTHORITY:
85 	case PGM_INT_CODE_LFX_TRANSLATION:
86 	case PGM_INT_CODE_LSX_TRANSLATION:
87 	case PGM_INT_CODE_ALEN_TRANSLATION:
88 	case PGM_INT_CODE_ALE_SEQUENCE:
89 	case PGM_INT_CODE_ASTE_VALIDITY:
90 	case PGM_INT_CODE_ASTE_SEQUENCE:
91 	case PGM_INT_CODE_EXTENDED_AUTHORITY:
92 	case PGM_INT_CODE_LSTE_SEQUENCE:
93 	case PGM_INT_CODE_ASTE_INSTANCE:
94 	case PGM_INT_CODE_STACK_FULL:
95 	case PGM_INT_CODE_STACK_EMPTY:
96 	case PGM_INT_CODE_STACK_SPECIFICATION:
97 	case PGM_INT_CODE_STACK_TYPE:
98 	case PGM_INT_CODE_STACK_OPERATION:
99 	case PGM_INT_CODE_ASCE_TYPE:
100 	case PGM_INT_CODE_REGION_FIRST_TRANS:
101 	case PGM_INT_CODE_REGION_SECOND_TRANS:
102 	case PGM_INT_CODE_REGION_THIRD_TRANS:
103 	case PGM_INT_CODE_PER:
104 	case PGM_INT_CODE_CRYPTO_OPERATION:
105 		/* The interrupt was nullified, the old PSW points at the
106 		 * responsible instruction. Forward the PSW so we don't loop.
107 		 */
108 		lc->pgm_old_psw.addr += lc->pgm_int_id;
109 	}
110 	/* suppressed/terminated/completed point already at the next address */
111 }
112 
113 void handle_pgm_int(void)
114 {
115 	if (!pgm_int_expected) {
116 		/* Force sclp_busy to false, otherwise we will loop forever */
117 		sclp_handle_ext();
118 		report_abort("Unexpected program interrupt: %d on cpu %d at %#lx, ilen %d\n",
119 			     lc->pgm_int_code, stap(), lc->pgm_old_psw.addr,
120 			     lc->pgm_int_id);
121 	}
122 
123 	pgm_int_expected = false;
124 
125 	if (pgm_cleanup_func)
126 		(*pgm_cleanup_func)();
127 	else
128 		fixup_pgm_int();
129 }
130 
131 void handle_ext_int(void)
132 {
133 	if (!ext_int_expected &&
134 	    lc->ext_int_code != EXT_IRQ_SERVICE_SIG) {
135 		report_abort("Unexpected external call interrupt (code %#x): on cpu %d at %#lx",
136 			     lc->ext_int_code, stap(), lc->ext_old_psw.addr);
137 		return;
138 	}
139 
140 	if (lc->ext_int_code == EXT_IRQ_SERVICE_SIG) {
141 		lc->sw_int_crs[0] &= ~(1UL << 9);
142 		sclp_handle_ext();
143 	} else {
144 		ext_int_expected = false;
145 	}
146 
147 	if (!(lc->sw_int_crs[0] & CR0_EXTM_MASK))
148 		lc->ext_old_psw.mask &= ~PSW_MASK_EXT;
149 }
150 
151 void handle_mcck_int(void)
152 {
153 	report_abort("Unexpected machine check interrupt: on cpu %d at %#lx",
154 		     stap(), lc->mcck_old_psw.addr);
155 }
156 
157 static void (*io_int_func)(void);
158 
159 void handle_io_int(void)
160 {
161 	if (io_int_func)
162 		return io_int_func();
163 
164 	report_abort("Unexpected io interrupt: on cpu %d at %#lx",
165 		     stap(), lc->io_old_psw.addr);
166 }
167 
168 int register_io_int_func(void (*f)(void))
169 {
170 	if (io_int_func)
171 		return -1;
172 	io_int_func = f;
173 	return 0;
174 }
175 
176 int unregister_io_int_func(void (*f)(void))
177 {
178 	if (io_int_func != f)
179 		return -1;
180 	io_int_func = NULL;
181 	return 0;
182 }
183 
184 void handle_svc_int(void)
185 {
186 	report_abort("Unexpected supervisor call interrupt: on cpu %d at %#lx",
187 		     stap(), lc->svc_old_psw.addr);
188 }
189