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