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