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 "sysemu/tcg.h" 18 #include "hw/s390x/ioinst.h" 19 #include "tcg_s390x.h" 20 #if !defined(CONFIG_USER_ONLY) 21 #include "hw/s390x/s390_flic.h" 22 #endif 23 24 /* Ensure to exit the TB after this call! */ 25 void trigger_pgm_exception(CPUS390XState *env, uint32_t code, uint32_t ilen) 26 { 27 CPUState *cs = env_cpu(env); 28 29 cs->exception_index = EXCP_PGM; 30 env->int_pgm_code = code; 31 /* If ILEN_UNWIND, int_pgm_ilen already has the correct value. */ 32 if (ilen != ILEN_UNWIND) { 33 env->int_pgm_ilen = ilen; 34 } 35 } 36 37 void s390_program_interrupt(CPUS390XState *env, uint32_t code, int ilen, 38 uintptr_t ra) 39 { 40 if (kvm_enabled()) { 41 kvm_s390_program_interrupt(env_archcpu(env), code); 42 } else if (tcg_enabled()) { 43 tcg_s390_program_interrupt(env, code, ilen, ra); 44 } else { 45 g_assert_not_reached(); 46 } 47 } 48 49 #if !defined(CONFIG_USER_ONLY) 50 void cpu_inject_clock_comparator(S390CPU *cpu) 51 { 52 CPUS390XState *env = &cpu->env; 53 54 env->pending_int |= INTERRUPT_EXT_CLOCK_COMPARATOR; 55 cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); 56 } 57 58 void cpu_inject_cpu_timer(S390CPU *cpu) 59 { 60 CPUS390XState *env = &cpu->env; 61 62 env->pending_int |= INTERRUPT_EXT_CPU_TIMER; 63 cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); 64 } 65 66 void cpu_inject_emergency_signal(S390CPU *cpu, uint16_t src_cpu_addr) 67 { 68 CPUS390XState *env = &cpu->env; 69 70 g_assert(src_cpu_addr < S390_MAX_CPUS); 71 set_bit(src_cpu_addr, env->emergency_signals); 72 73 env->pending_int |= INTERRUPT_EMERGENCY_SIGNAL; 74 cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); 75 } 76 77 int cpu_inject_external_call(S390CPU *cpu, uint16_t src_cpu_addr) 78 { 79 CPUS390XState *env = &cpu->env; 80 81 g_assert(src_cpu_addr < S390_MAX_CPUS); 82 if (env->pending_int & INTERRUPT_EXTERNAL_CALL) { 83 return -EBUSY; 84 } 85 env->external_call_addr = src_cpu_addr; 86 87 env->pending_int |= INTERRUPT_EXTERNAL_CALL; 88 cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); 89 return 0; 90 } 91 92 void cpu_inject_restart(S390CPU *cpu) 93 { 94 CPUS390XState *env = &cpu->env; 95 96 if (kvm_enabled()) { 97 kvm_s390_restart_interrupt(cpu); 98 return; 99 } 100 101 env->pending_int |= INTERRUPT_RESTART; 102 cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); 103 } 104 105 void cpu_inject_stop(S390CPU *cpu) 106 { 107 CPUS390XState *env = &cpu->env; 108 109 if (kvm_enabled()) { 110 kvm_s390_stop_interrupt(cpu); 111 return; 112 } 113 114 env->pending_int |= INTERRUPT_STOP; 115 cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); 116 } 117 118 /* 119 * All of the following interrupts are floating, i.e. not per-vcpu. 120 * We just need a dummy cpustate in order to be able to inject in the 121 * non-kvm case. 122 */ 123 void s390_sclp_extint(uint32_t parm) 124 { 125 S390FLICState *fs = s390_get_flic(); 126 S390FLICStateClass *fsc = s390_get_flic_class(fs); 127 128 fsc->inject_service(fs, parm); 129 } 130 131 void s390_io_interrupt(uint16_t subchannel_id, uint16_t subchannel_nr, 132 uint32_t io_int_parm, uint32_t io_int_word) 133 { 134 S390FLICState *fs = s390_get_flic(); 135 S390FLICStateClass *fsc = s390_get_flic_class(fs); 136 137 fsc->inject_io(fs, subchannel_id, subchannel_nr, io_int_parm, io_int_word); 138 } 139 140 void s390_crw_mchk(void) 141 { 142 S390FLICState *fs = s390_get_flic(); 143 S390FLICStateClass *fsc = s390_get_flic_class(fs); 144 145 fsc->inject_crw_mchk(fs); 146 } 147 148 bool s390_cpu_has_mcck_int(S390CPU *cpu) 149 { 150 QEMUS390FLICState *flic = s390_get_qemu_flic(s390_get_flic()); 151 CPUS390XState *env = &cpu->env; 152 153 if (!(env->psw.mask & PSW_MASK_MCHECK)) { 154 return false; 155 } 156 157 /* for now we only support channel report machine checks (floating) */ 158 if (qemu_s390_flic_has_crw_mchk(flic) && 159 (env->cregs[14] & CR14_CHANNEL_REPORT_SC)) { 160 return true; 161 } 162 163 return false; 164 } 165 166 bool s390_cpu_has_ext_int(S390CPU *cpu) 167 { 168 QEMUS390FLICState *flic = s390_get_qemu_flic(s390_get_flic()); 169 CPUS390XState *env = &cpu->env; 170 171 if (!(env->psw.mask & PSW_MASK_EXT)) { 172 return false; 173 } 174 175 if ((env->pending_int & INTERRUPT_EMERGENCY_SIGNAL) && 176 (env->cregs[0] & CR0_EMERGENCY_SIGNAL_SC)) { 177 return true; 178 } 179 180 if ((env->pending_int & INTERRUPT_EXTERNAL_CALL) && 181 (env->cregs[0] & CR0_EXTERNAL_CALL_SC)) { 182 return true; 183 } 184 185 if ((env->pending_int & INTERRUPT_EXTERNAL_CALL) && 186 (env->cregs[0] & CR0_EXTERNAL_CALL_SC)) { 187 return true; 188 } 189 190 if ((env->pending_int & INTERRUPT_EXT_CLOCK_COMPARATOR) && 191 (env->cregs[0] & CR0_CKC_SC)) { 192 return true; 193 } 194 195 if ((env->pending_int & INTERRUPT_EXT_CPU_TIMER) && 196 (env->cregs[0] & CR0_CPU_TIMER_SC)) { 197 return true; 198 } 199 200 if (qemu_s390_flic_has_service(flic) && 201 (env->cregs[0] & CR0_SERVICE_SC)) { 202 return true; 203 } 204 205 return false; 206 } 207 208 bool s390_cpu_has_io_int(S390CPU *cpu) 209 { 210 QEMUS390FLICState *flic = s390_get_qemu_flic(s390_get_flic()); 211 CPUS390XState *env = &cpu->env; 212 213 if (!(env->psw.mask & PSW_MASK_IO)) { 214 return false; 215 } 216 217 return qemu_s390_flic_has_io(flic, env->cregs[6]); 218 } 219 220 bool s390_cpu_has_restart_int(S390CPU *cpu) 221 { 222 CPUS390XState *env = &cpu->env; 223 224 return env->pending_int & INTERRUPT_RESTART; 225 } 226 227 bool s390_cpu_has_stop_int(S390CPU *cpu) 228 { 229 CPUS390XState *env = &cpu->env; 230 231 return env->pending_int & INTERRUPT_STOP; 232 } 233 #endif 234 235 bool s390_cpu_has_int(S390CPU *cpu) 236 { 237 #ifndef CONFIG_USER_ONLY 238 if (!tcg_enabled()) { 239 return false; 240 } 241 return s390_cpu_has_mcck_int(cpu) || 242 s390_cpu_has_ext_int(cpu) || 243 s390_cpu_has_io_int(cpu) || 244 s390_cpu_has_restart_int(cpu) || 245 s390_cpu_has_stop_int(cpu); 246 #else 247 return false; 248 #endif 249 } 250