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