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