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