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 | |