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