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