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