1 /* 2 * QEMU S/390 Interrupt support 3 * 4 * Copyright IBM Corp. 2012, 2014 5 * 6 * This work is licensed under the terms of the GNU GPL, version 2 or (at your 7 * option) any later version. See the COPYING file in the top-level directory. 8 */ 9 10 #include "qemu/osdep.h" 11 #include "qemu/log.h" 12 #include "cpu.h" 13 #include "kvm_s390x.h" 14 #include "internal.h" 15 #include "exec/exec-all.h" 16 #include "sysemu/kvm.h" 17 #include "hw/s390x/ioinst.h" 18 #if !defined(CONFIG_USER_ONLY) 19 #include "hw/s390x/s390_flic.h" 20 #endif 21 22 /* Ensure to exit the TB after this call! */ 23 void trigger_pgm_exception(CPUS390XState *env, uint32_t code, uint32_t ilen) 24 { 25 CPUState *cs = CPU(s390_env_get_cpu(env)); 26 27 cs->exception_index = EXCP_PGM; 28 env->int_pgm_code = code; 29 env->int_pgm_ilen = ilen; 30 } 31 32 static void tcg_s390_program_interrupt(CPUS390XState *env, uint32_t code, 33 int ilen, uintptr_t ra) 34 { 35 #ifdef CONFIG_TCG 36 trigger_pgm_exception(env, code, ilen); 37 cpu_loop_exit_restore(CPU(s390_env_get_cpu(env)), ra); 38 #else 39 g_assert_not_reached(); 40 #endif 41 } 42 43 void s390_program_interrupt(CPUS390XState *env, uint32_t code, int ilen, 44 uintptr_t ra) 45 { 46 S390CPU *cpu = s390_env_get_cpu(env); 47 48 qemu_log_mask(CPU_LOG_INT, "program interrupt at %#" PRIx64 "\n", 49 env->psw.addr); 50 51 if (kvm_enabled()) { 52 kvm_s390_program_interrupt(cpu, code); 53 } else if (tcg_enabled()) { 54 tcg_s390_program_interrupt(env, code, ilen, ra); 55 } else { 56 g_assert_not_reached(); 57 } 58 } 59 60 #if !defined(CONFIG_USER_ONLY) 61 void cpu_inject_service(S390CPU *cpu, uint32_t param) 62 { 63 CPUS390XState *env = &cpu->env; 64 65 /* multiplexing is good enough for sclp - kvm does it internally as well*/ 66 env->service_param |= param; 67 68 env->pending_int |= INTERRUPT_EXT_SERVICE; 69 cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); 70 } 71 72 void cpu_inject_clock_comparator(S390CPU *cpu) 73 { 74 CPUS390XState *env = &cpu->env; 75 76 env->pending_int |= INTERRUPT_EXT_CLOCK_COMPARATOR; 77 cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); 78 } 79 80 void cpu_inject_cpu_timer(S390CPU *cpu) 81 { 82 CPUS390XState *env = &cpu->env; 83 84 env->pending_int |= INTERRUPT_EXT_CPU_TIMER; 85 cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); 86 } 87 88 void cpu_inject_emergency_signal(S390CPU *cpu, uint16_t src_cpu_addr) 89 { 90 CPUS390XState *env = &cpu->env; 91 92 g_assert(src_cpu_addr < S390_MAX_CPUS); 93 set_bit(src_cpu_addr, env->emergency_signals); 94 95 env->pending_int |= INTERRUPT_EMERGENCY_SIGNAL; 96 cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); 97 } 98 99 int cpu_inject_external_call(S390CPU *cpu, uint16_t src_cpu_addr) 100 { 101 CPUS390XState *env = &cpu->env; 102 103 g_assert(src_cpu_addr < S390_MAX_CPUS); 104 if (env->pending_int & INTERRUPT_EXTERNAL_CALL) { 105 return -EBUSY; 106 } 107 env->external_call_addr = src_cpu_addr; 108 109 env->pending_int |= INTERRUPT_EXTERNAL_CALL; 110 cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); 111 return 0; 112 } 113 114 void cpu_inject_restart(S390CPU *cpu) 115 { 116 CPUS390XState *env = &cpu->env; 117 118 if (kvm_enabled()) { 119 kvm_s390_restart_interrupt(cpu); 120 return; 121 } 122 123 env->pending_int |= INTERRUPT_RESTART; 124 cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); 125 } 126 127 void cpu_inject_stop(S390CPU *cpu) 128 { 129 CPUS390XState *env = &cpu->env; 130 131 if (kvm_enabled()) { 132 kvm_s390_stop_interrupt(cpu); 133 return; 134 } 135 136 env->pending_int |= INTERRUPT_STOP; 137 cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); 138 } 139 140 void cpu_inject_io(S390CPU *cpu, uint16_t subchannel_id, 141 uint16_t subchannel_number, uint32_t io_int_parm, 142 uint32_t io_int_word) 143 { 144 CPUS390XState *env = &cpu->env; 145 int isc = IO_INT_WORD_ISC(io_int_word); 146 147 if (env->io_index[isc] == MAX_IO_QUEUE - 1) { 148 /* ugh - can't queue anymore. Let's drop. */ 149 return; 150 } 151 152 env->io_index[isc]++; 153 assert(env->io_index[isc] < MAX_IO_QUEUE); 154 155 env->io_queue[env->io_index[isc]][isc].id = subchannel_id; 156 env->io_queue[env->io_index[isc]][isc].nr = subchannel_number; 157 env->io_queue[env->io_index[isc]][isc].parm = io_int_parm; 158 env->io_queue[env->io_index[isc]][isc].word = io_int_word; 159 160 env->pending_int |= INTERRUPT_IO; 161 cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); 162 } 163 164 void cpu_inject_crw_mchk(S390CPU *cpu) 165 { 166 CPUS390XState *env = &cpu->env; 167 168 env->pending_int |= INTERRUPT_MCHK; 169 cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); 170 } 171 172 /* 173 * All of the following interrupts are floating, i.e. not per-vcpu. 174 * We just need a dummy cpustate in order to be able to inject in the 175 * non-kvm case. 176 */ 177 void s390_sclp_extint(uint32_t parm) 178 { 179 S390FLICState *fs = s390_get_flic(); 180 S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs); 181 182 fsc->inject_service(fs, parm); 183 } 184 185 void s390_io_interrupt(uint16_t subchannel_id, uint16_t subchannel_nr, 186 uint32_t io_int_parm, uint32_t io_int_word) 187 { 188 S390FLICState *fs = s390_get_flic(); 189 S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs); 190 191 fsc->inject_io(fs, subchannel_id, subchannel_nr, io_int_parm, io_int_word); 192 } 193 194 void s390_crw_mchk(void) 195 { 196 S390FLICState *fs = s390_get_flic(); 197 S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs); 198 199 fsc->inject_crw_mchk(fs); 200 } 201 202 bool s390_cpu_has_mcck_int(S390CPU *cpu) 203 { 204 CPUS390XState *env = &cpu->env; 205 206 if (!(env->psw.mask & PSW_MASK_MCHECK)) { 207 return false; 208 } 209 210 /* for now we only support channel report machine checks (floating) */ 211 if ((env->pending_int & INTERRUPT_MCHK) && 212 (env->cregs[14] & CR14_CHANNEL_REPORT_SC)) { 213 return true; 214 } 215 216 return false; 217 } 218 219 bool s390_cpu_has_ext_int(S390CPU *cpu) 220 { 221 CPUS390XState *env = &cpu->env; 222 223 if (!(env->psw.mask & PSW_MASK_EXT)) { 224 return false; 225 } 226 227 if ((env->pending_int & INTERRUPT_EMERGENCY_SIGNAL) && 228 (env->cregs[0] & CR0_EMERGENCY_SIGNAL_SC)) { 229 return true; 230 } 231 232 if ((env->pending_int & INTERRUPT_EXTERNAL_CALL) && 233 (env->cregs[0] & CR0_EXTERNAL_CALL_SC)) { 234 return true; 235 } 236 237 if ((env->pending_int & INTERRUPT_EXTERNAL_CALL) && 238 (env->cregs[0] & CR0_EXTERNAL_CALL_SC)) { 239 return true; 240 } 241 242 if ((env->pending_int & INTERRUPT_EXT_CLOCK_COMPARATOR) && 243 (env->cregs[0] & CR0_CKC_SC)) { 244 return true; 245 } 246 247 if ((env->pending_int & INTERRUPT_EXT_CPU_TIMER) && 248 (env->cregs[0] & CR0_CPU_TIMER_SC)) { 249 return true; 250 } 251 252 if ((env->pending_int & INTERRUPT_EXT_SERVICE) && 253 (env->cregs[0] & CR0_SERVICE_SC)) { 254 return true; 255 } 256 257 return false; 258 } 259 260 bool s390_cpu_has_io_int(S390CPU *cpu) 261 { 262 CPUS390XState *env = &cpu->env; 263 264 if (!(env->psw.mask & PSW_MASK_IO)) { 265 return false; 266 } 267 268 return env->pending_int & INTERRUPT_IO; 269 } 270 271 bool s390_cpu_has_restart_int(S390CPU *cpu) 272 { 273 CPUS390XState *env = &cpu->env; 274 275 return env->pending_int & INTERRUPT_RESTART; 276 } 277 278 bool s390_cpu_has_stop_int(S390CPU *cpu) 279 { 280 CPUS390XState *env = &cpu->env; 281 282 return env->pending_int & INTERRUPT_STOP; 283 } 284 #endif 285 286 bool s390_cpu_has_int(S390CPU *cpu) 287 { 288 #ifndef CONFIG_USER_ONLY 289 if (!tcg_enabled()) { 290 return false; 291 } 292 return s390_cpu_has_mcck_int(cpu) || 293 s390_cpu_has_ext_int(cpu) || 294 s390_cpu_has_io_int(cpu) || 295 s390_cpu_has_restart_int(cpu) || 296 s390_cpu_has_stop_int(cpu); 297 #else 298 return false; 299 #endif 300 } 301