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