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