]>
Commit | Line | Data |
---|---|---|
000a1a38 CB |
1 | /* |
2 | * QEMU S/390 Interrupt support | |
3 | * | |
79afc36d | 4 | * Copyright IBM Corp. 2012, 2014 |
000a1a38 CB |
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 | ||
9615495a | 10 | #include "qemu/osdep.h" |
e3cfd926 | 11 | #include "qemu/log.h" |
000a1a38 | 12 | #include "cpu.h" |
f16bbb9b | 13 | #include "kvm_s390x.h" |
4e58b838 | 14 | #include "internal.h" |
e3cfd926 | 15 | #include "exec/exec-all.h" |
9c17d615 | 16 | #include "sysemu/kvm.h" |
bd3f16ac PB |
17 | #include "hw/s390x/ioinst.h" |
18 | ||
e3cfd926 TH |
19 | /* Ensure to exit the TB after this call! */ |
20 | void trigger_pgm_exception(CPUS390XState *env, uint32_t code, uint32_t ilen) | |
21 | { | |
22 | CPUState *cs = CPU(s390_env_get_cpu(env)); | |
23 | ||
24 | cs->exception_index = EXCP_PGM; | |
25 | env->int_pgm_code = code; | |
26 | env->int_pgm_ilen = ilen; | |
27 | } | |
28 | ||
29 | static void tcg_s390_program_interrupt(CPUS390XState *env, uint32_t code, | |
51dcdbd3 | 30 | int ilen, uintptr_t ra) |
e3cfd926 TH |
31 | { |
32 | #ifdef CONFIG_TCG | |
33 | trigger_pgm_exception(env, code, ilen); | |
51dcdbd3 | 34 | cpu_loop_exit_restore(CPU(s390_env_get_cpu(env)), ra); |
e3cfd926 TH |
35 | #else |
36 | g_assert_not_reached(); | |
37 | #endif | |
38 | } | |
39 | ||
51dcdbd3 DH |
40 | void s390_program_interrupt(CPUS390XState *env, uint32_t code, int ilen, |
41 | uintptr_t ra) | |
e3cfd926 TH |
42 | { |
43 | S390CPU *cpu = s390_env_get_cpu(env); | |
44 | ||
45 | qemu_log_mask(CPU_LOG_INT, "program interrupt at %#" PRIx64 "\n", | |
46 | env->psw.addr); | |
47 | ||
48 | if (kvm_enabled()) { | |
49 | kvm_s390_program_interrupt(cpu, code); | |
50 | } else if (tcg_enabled()) { | |
51dcdbd3 | 51 | tcg_s390_program_interrupt(env, code, ilen, ra); |
e3cfd926 TH |
52 | } else { |
53 | g_assert_not_reached(); | |
54 | } | |
55 | } | |
56 | ||
bd3f16ac | 57 | #if !defined(CONFIG_USER_ONLY) |
d516f74c | 58 | static void cpu_inject_service(S390CPU *cpu, uint32_t param) |
bd3f16ac PB |
59 | { |
60 | CPUS390XState *env = &cpu->env; | |
61 | ||
d516f74c DH |
62 | /* multiplexing is good enough for sclp - kvm does it internally as well*/ |
63 | env->service_param |= param; | |
bd3f16ac | 64 | |
6482b0ff DH |
65 | env->pending_int |= INTERRUPT_EXT_SERVICE; |
66 | cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); | |
67 | } | |
68 | ||
69 | void cpu_inject_clock_comparator(S390CPU *cpu) | |
70 | { | |
71 | CPUS390XState *env = &cpu->env; | |
72 | ||
73 | env->pending_int |= INTERRUPT_EXT_CLOCK_COMPARATOR; | |
74 | cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); | |
75 | } | |
76 | ||
77 | void cpu_inject_cpu_timer(S390CPU *cpu) | |
78 | { | |
79 | CPUS390XState *env = &cpu->env; | |
80 | ||
81 | env->pending_int |= INTERRUPT_EXT_CPU_TIMER; | |
bd3f16ac PB |
82 | cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); |
83 | } | |
84 | ||
14ca122e DH |
85 | void cpu_inject_emergency_signal(S390CPU *cpu, uint16_t src_cpu_addr) |
86 | { | |
87 | CPUS390XState *env = &cpu->env; | |
88 | ||
89 | g_assert(src_cpu_addr < S390_MAX_CPUS); | |
90 | set_bit(src_cpu_addr, env->emergency_signals); | |
91 | ||
92 | env->pending_int |= INTERRUPT_EMERGENCY_SIGNAL; | |
93 | cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); | |
94 | } | |
95 | ||
96 | int cpu_inject_external_call(S390CPU *cpu, uint16_t src_cpu_addr) | |
97 | { | |
98 | CPUS390XState *env = &cpu->env; | |
99 | ||
100 | g_assert(src_cpu_addr < S390_MAX_CPUS); | |
101 | if (env->pending_int & INTERRUPT_EXTERNAL_CALL) { | |
102 | return -EBUSY; | |
103 | } | |
104 | env->external_call_addr = src_cpu_addr; | |
105 | ||
106 | env->pending_int |= INTERRUPT_EXTERNAL_CALL; | |
107 | cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); | |
108 | return 0; | |
109 | } | |
110 | ||
eabcea18 DH |
111 | void cpu_inject_restart(S390CPU *cpu) |
112 | { | |
b1ab5f60 DH |
113 | CPUS390XState *env = &cpu->env; |
114 | ||
eabcea18 DH |
115 | if (kvm_enabled()) { |
116 | kvm_s390_restart_interrupt(cpu); | |
117 | return; | |
118 | } | |
b1ab5f60 DH |
119 | |
120 | env->pending_int |= INTERRUPT_RESTART; | |
121 | cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); | |
eabcea18 DH |
122 | } |
123 | ||
124 | void cpu_inject_stop(S390CPU *cpu) | |
125 | { | |
b1ab5f60 DH |
126 | CPUS390XState *env = &cpu->env; |
127 | ||
eabcea18 DH |
128 | if (kvm_enabled()) { |
129 | kvm_s390_stop_interrupt(cpu); | |
130 | return; | |
131 | } | |
b1ab5f60 DH |
132 | |
133 | env->pending_int |= INTERRUPT_STOP; | |
134 | cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); | |
eabcea18 DH |
135 | } |
136 | ||
bd3f16ac PB |
137 | static void cpu_inject_io(S390CPU *cpu, uint16_t subchannel_id, |
138 | uint16_t subchannel_number, | |
139 | uint32_t io_int_parm, uint32_t io_int_word) | |
140 | { | |
141 | CPUS390XState *env = &cpu->env; | |
142 | int isc = IO_INT_WORD_ISC(io_int_word); | |
143 | ||
144 | if (env->io_index[isc] == MAX_IO_QUEUE - 1) { | |
145 | /* ugh - can't queue anymore. Let's drop. */ | |
146 | return; | |
147 | } | |
148 | ||
149 | env->io_index[isc]++; | |
150 | assert(env->io_index[isc] < MAX_IO_QUEUE); | |
151 | ||
152 | env->io_queue[env->io_index[isc]][isc].id = subchannel_id; | |
153 | env->io_queue[env->io_index[isc]][isc].nr = subchannel_number; | |
154 | env->io_queue[env->io_index[isc]][isc].parm = io_int_parm; | |
155 | env->io_queue[env->io_index[isc]][isc].word = io_int_word; | |
156 | ||
157 | env->pending_int |= INTERRUPT_IO; | |
158 | cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); | |
159 | } | |
160 | ||
161 | static void cpu_inject_crw_mchk(S390CPU *cpu) | |
162 | { | |
163 | CPUS390XState *env = &cpu->env; | |
164 | ||
165 | if (env->mchk_index == MAX_MCHK_QUEUE - 1) { | |
166 | /* ugh - can't queue anymore. Let's drop. */ | |
167 | return; | |
168 | } | |
169 | ||
170 | env->mchk_index++; | |
171 | assert(env->mchk_index < MAX_MCHK_QUEUE); | |
172 | ||
173 | env->mchk_queue[env->mchk_index].type = 1; | |
174 | ||
175 | env->pending_int |= INTERRUPT_MCHK; | |
176 | cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); | |
177 | } | |
000a1a38 | 178 | |
79afc36d CH |
179 | /* |
180 | * All of the following interrupts are floating, i.e. not per-vcpu. | |
de13d216 CH |
181 | * We just need a dummy cpustate in order to be able to inject in the |
182 | * non-kvm case. | |
79afc36d | 183 | */ |
000a1a38 CB |
184 | void s390_sclp_extint(uint32_t parm) |
185 | { | |
000a1a38 | 186 | if (kvm_enabled()) { |
de13d216 | 187 | kvm_s390_service_interrupt(parm); |
000a1a38 | 188 | } else { |
de13d216 | 189 | S390CPU *dummy_cpu = s390_cpu_addr2state(0); |
de13d216 | 190 | |
d516f74c | 191 | cpu_inject_service(dummy_cpu, parm); |
000a1a38 CB |
192 | } |
193 | } | |
79afc36d | 194 | |
de13d216 CH |
195 | void s390_io_interrupt(uint16_t subchannel_id, uint16_t subchannel_nr, |
196 | uint32_t io_int_parm, uint32_t io_int_word) | |
79afc36d CH |
197 | { |
198 | if (kvm_enabled()) { | |
de13d216 | 199 | kvm_s390_io_interrupt(subchannel_id, subchannel_nr, io_int_parm, |
79afc36d CH |
200 | io_int_word); |
201 | } else { | |
de13d216 CH |
202 | S390CPU *dummy_cpu = s390_cpu_addr2state(0); |
203 | ||
204 | cpu_inject_io(dummy_cpu, subchannel_id, subchannel_nr, io_int_parm, | |
79afc36d CH |
205 | io_int_word); |
206 | } | |
207 | } | |
208 | ||
de13d216 | 209 | void s390_crw_mchk(void) |
79afc36d CH |
210 | { |
211 | if (kvm_enabled()) { | |
de13d216 | 212 | kvm_s390_crw_mchk(); |
79afc36d | 213 | } else { |
de13d216 CH |
214 | S390CPU *dummy_cpu = s390_cpu_addr2state(0); |
215 | ||
216 | cpu_inject_crw_mchk(dummy_cpu); | |
79afc36d CH |
217 | } |
218 | } | |
219 | ||
8417f904 DH |
220 | bool s390_cpu_has_mcck_int(S390CPU *cpu) |
221 | { | |
222 | CPUS390XState *env = &cpu->env; | |
223 | ||
224 | if (!(env->psw.mask & PSW_MASK_MCHECK)) { | |
225 | return false; | |
226 | } | |
227 | ||
228 | return env->pending_int & INTERRUPT_MCHK; | |
229 | } | |
230 | ||
231 | bool s390_cpu_has_ext_int(S390CPU *cpu) | |
232 | { | |
233 | CPUS390XState *env = &cpu->env; | |
234 | ||
235 | if (!(env->psw.mask & PSW_MASK_EXT)) { | |
236 | return false; | |
237 | } | |
238 | ||
9dec2388 DH |
239 | if ((env->pending_int & INTERRUPT_EMERGENCY_SIGNAL) && |
240 | (env->cregs[0] & CR0_EMERGENCY_SIGNAL_SC)) { | |
241 | return true; | |
242 | } | |
243 | ||
244 | if ((env->pending_int & INTERRUPT_EXTERNAL_CALL) && | |
245 | (env->cregs[0] & CR0_EXTERNAL_CALL_SC)) { | |
246 | return true; | |
247 | } | |
248 | ||
249 | if ((env->pending_int & INTERRUPT_EXTERNAL_CALL) && | |
250 | (env->cregs[0] & CR0_EXTERNAL_CALL_SC)) { | |
251 | return true; | |
252 | } | |
253 | ||
254 | if ((env->pending_int & INTERRUPT_EXT_CLOCK_COMPARATOR) && | |
255 | (env->cregs[0] & CR0_CKC_SC)) { | |
256 | return true; | |
257 | } | |
258 | ||
259 | if ((env->pending_int & INTERRUPT_EXT_CPU_TIMER) && | |
260 | (env->cregs[0] & CR0_CPU_TIMER_SC)) { | |
261 | return true; | |
262 | } | |
263 | ||
264 | if ((env->pending_int & INTERRUPT_EXT_SERVICE) && | |
265 | (env->cregs[0] & CR0_SERVICE_SC)) { | |
266 | return true; | |
267 | } | |
268 | ||
269 | return false; | |
8417f904 DH |
270 | } |
271 | ||
272 | bool s390_cpu_has_io_int(S390CPU *cpu) | |
273 | { | |
274 | CPUS390XState *env = &cpu->env; | |
275 | ||
276 | if (!(env->psw.mask & PSW_MASK_IO)) { | |
277 | return false; | |
278 | } | |
279 | ||
280 | return env->pending_int & INTERRUPT_IO; | |
281 | } | |
b1ab5f60 DH |
282 | |
283 | bool s390_cpu_has_restart_int(S390CPU *cpu) | |
284 | { | |
285 | CPUS390XState *env = &cpu->env; | |
286 | ||
287 | return env->pending_int & INTERRUPT_RESTART; | |
288 | } | |
289 | ||
290 | bool s390_cpu_has_stop_int(S390CPU *cpu) | |
291 | { | |
292 | CPUS390XState *env = &cpu->env; | |
293 | ||
294 | return env->pending_int & INTERRUPT_STOP; | |
295 | } | |
000a1a38 | 296 | #endif |
8417f904 DH |
297 | |
298 | bool s390_cpu_has_int(S390CPU *cpu) | |
299 | { | |
300 | #ifndef CONFIG_USER_ONLY | |
301 | if (!tcg_enabled()) { | |
302 | return false; | |
303 | } | |
304 | return s390_cpu_has_mcck_int(cpu) || | |
305 | s390_cpu_has_ext_int(cpu) || | |
b1ab5f60 DH |
306 | s390_cpu_has_io_int(cpu) || |
307 | s390_cpu_has_restart_int(cpu) || | |
308 | s390_cpu_has_stop_int(cpu); | |
8417f904 DH |
309 | #else |
310 | return false; | |
311 | #endif | |
312 | } |