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