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