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