xref: /qemu/target/s390x/interrupt.c (revision c87ff4d108efce2546150be057721cb41ca1f74d)
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