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 mb(); 26 } 27 28 void expect_ext_int(void) 29 { 30 ext_int_expected = true; 31 lc->ext_int_code = 0; 32 mb(); 33 } 34 35 uint16_t clear_pgm_int(void) 36 { 37 uint16_t code; 38 39 mb(); 40 code = lc->pgm_int_code; 41 lc->pgm_int_code = 0; 42 pgm_int_expected = false; 43 return code; 44 } 45 46 void check_pgm_int_code(uint16_t code) 47 { 48 mb(); 49 report(code == lc->pgm_int_code, 50 "Program interrupt: expected(%d) == received(%d)", code, 51 lc->pgm_int_code); 52 } 53 54 void register_pgm_cleanup_func(void (*f)(void)) 55 { 56 pgm_cleanup_func = f; 57 } 58 59 static void fixup_pgm_int(struct stack_frame_int *stack) 60 { 61 /* If we have an error on SIE we directly move to sie_exit */ 62 if (lc->pgm_old_psw.addr >= (uint64_t)&sie_entry && 63 lc->pgm_old_psw.addr <= (uint64_t)&sie_exit) { 64 lc->pgm_old_psw.addr = (uint64_t)&sie_exit; 65 } 66 67 switch (lc->pgm_int_code) { 68 case PGM_INT_CODE_PRIVILEGED_OPERATION: 69 /* Normal operation is in supervisor state, so this exception 70 * was produced intentionally and we should return to the 71 * supervisor state. 72 */ 73 lc->pgm_old_psw.mask &= ~PSW_MASK_PSTATE; 74 break; 75 case PGM_INT_CODE_PROTECTION: 76 /* Handling for iep.c test case. */ 77 if (lc->trans_exc_id & 0x80UL && lc->trans_exc_id & 0x04UL && 78 !(lc->trans_exc_id & 0x08UL)) 79 /* 80 * We branched to the instruction that caused 81 * the exception so we can use the return 82 * address in GR14 to jump back and continue 83 * executing test code. 84 */ 85 lc->pgm_old_psw.addr = stack->grs0[12]; 86 break; 87 case PGM_INT_CODE_SEGMENT_TRANSLATION: 88 case PGM_INT_CODE_PAGE_TRANSLATION: 89 case PGM_INT_CODE_TRACE_TABLE: 90 case PGM_INT_CODE_AFX_TRANSLATION: 91 case PGM_INT_CODE_ASX_TRANSLATION: 92 case PGM_INT_CODE_LX_TRANSLATION: 93 case PGM_INT_CODE_EX_TRANSLATION: 94 case PGM_INT_CODE_PRIMARY_AUTHORITY: 95 case PGM_INT_CODE_SECONDARY_AUTHORITY: 96 case PGM_INT_CODE_LFX_TRANSLATION: 97 case PGM_INT_CODE_LSX_TRANSLATION: 98 case PGM_INT_CODE_ALEN_TRANSLATION: 99 case PGM_INT_CODE_ALE_SEQUENCE: 100 case PGM_INT_CODE_ASTE_VALIDITY: 101 case PGM_INT_CODE_ASTE_SEQUENCE: 102 case PGM_INT_CODE_EXTENDED_AUTHORITY: 103 case PGM_INT_CODE_LSTE_SEQUENCE: 104 case PGM_INT_CODE_ASTE_INSTANCE: 105 case PGM_INT_CODE_STACK_FULL: 106 case PGM_INT_CODE_STACK_EMPTY: 107 case PGM_INT_CODE_STACK_SPECIFICATION: 108 case PGM_INT_CODE_STACK_TYPE: 109 case PGM_INT_CODE_STACK_OPERATION: 110 case PGM_INT_CODE_ASCE_TYPE: 111 case PGM_INT_CODE_REGION_FIRST_TRANS: 112 case PGM_INT_CODE_REGION_SECOND_TRANS: 113 case PGM_INT_CODE_REGION_THIRD_TRANS: 114 case PGM_INT_CODE_PER: 115 case PGM_INT_CODE_CRYPTO_OPERATION: 116 /* The interrupt was nullified, the old PSW points at the 117 * responsible instruction. Forward the PSW so we don't loop. 118 */ 119 lc->pgm_old_psw.addr += lc->pgm_int_id; 120 } 121 /* suppressed/terminated/completed point already at the next address */ 122 } 123 124 static void print_int_regs(struct stack_frame_int *stack) 125 { 126 printf("\n"); 127 printf("GPRS:\n"); 128 printf("%016lx %016lx %016lx %016lx\n", 129 stack->grs1[0], stack->grs1[1], stack->grs0[0], stack->grs0[1]); 130 printf("%016lx %016lx %016lx %016lx\n", 131 stack->grs0[2], stack->grs0[3], stack->grs0[4], stack->grs0[5]); 132 printf("%016lx %016lx %016lx %016lx\n", 133 stack->grs0[6], stack->grs0[7], stack->grs0[8], stack->grs0[9]); 134 printf("%016lx %016lx %016lx %016lx\n", 135 stack->grs0[10], stack->grs0[11], stack->grs0[12], stack->grs0[13]); 136 printf("\n"); 137 } 138 139 static void print_pgm_info(struct stack_frame_int *stack) 140 141 { 142 printf("\n"); 143 printf("Unexpected program interrupt: %d on cpu %d at %#lx, ilen %d\n", 144 lc->pgm_int_code, stap(), lc->pgm_old_psw.addr, 145 lc->pgm_int_id); 146 print_int_regs(stack); 147 dump_stack(); 148 report_summary(); 149 abort(); 150 } 151 152 void handle_pgm_int(struct stack_frame_int *stack) 153 { 154 if (!pgm_int_expected) { 155 /* Force sclp_busy to false, otherwise we will loop forever */ 156 sclp_handle_ext(); 157 print_pgm_info(stack); 158 report_abort("Unexpected program interrupt: %d on cpu %d at %#lx, ilen %d\n", 159 lc->pgm_int_code, stap(), lc->pgm_old_psw.addr, 160 lc->pgm_int_id); 161 } 162 163 pgm_int_expected = false; 164 165 if (pgm_cleanup_func) 166 (*pgm_cleanup_func)(); 167 else 168 fixup_pgm_int(stack); 169 } 170 171 void handle_ext_int(struct stack_frame_int *stack) 172 { 173 if (!ext_int_expected && 174 lc->ext_int_code != EXT_IRQ_SERVICE_SIG) { 175 report_abort("Unexpected external call interrupt (code %#x): on cpu %d at %#lx", 176 lc->ext_int_code, stap(), lc->ext_old_psw.addr); 177 return; 178 } 179 180 if (lc->ext_int_code == EXT_IRQ_SERVICE_SIG) { 181 stack->crs[0] &= ~(1UL << 9); 182 sclp_handle_ext(); 183 } else { 184 ext_int_expected = false; 185 } 186 187 if (!(stack->crs[0] & CR0_EXTM_MASK)) 188 lc->ext_old_psw.mask &= ~PSW_MASK_EXT; 189 } 190 191 void handle_mcck_int(void) 192 { 193 report_abort("Unexpected machine check interrupt: on cpu %d at %#lx", 194 stap(), lc->mcck_old_psw.addr); 195 } 196 197 static void (*io_int_func)(void); 198 199 void handle_io_int(void) 200 { 201 if (io_int_func) 202 return io_int_func(); 203 204 report_abort("Unexpected io interrupt: on cpu %d at %#lx", 205 stap(), lc->io_old_psw.addr); 206 } 207 208 int register_io_int_func(void (*f)(void)) 209 { 210 if (io_int_func) 211 return -1; 212 io_int_func = f; 213 return 0; 214 } 215 216 int unregister_io_int_func(void (*f)(void)) 217 { 218 if (io_int_func != f) 219 return -1; 220 io_int_func = NULL; 221 return 0; 222 } 223 224 void handle_svc_int(void) 225 { 226 uint16_t code = lc->svc_int_code; 227 228 switch (code) { 229 case SVC_LEAVE_PSTATE: 230 lc->svc_old_psw.mask &= ~PSW_MASK_PSTATE; 231 break; 232 default: 233 report_abort("Unexpected supervisor call interrupt: code %#x on cpu %d at %#lx", 234 code, stap(), lc->svc_old_psw.addr); 235 } 236 } 237