]>
Commit | Line | Data |
---|---|---|
74b4c74d DH |
1 | /* |
2 | * s390x SIGP instruction handling | |
3 | * | |
4 | * Copyright (c) 2009 Alexander Graf <agraf@suse.de> | |
5 | * Copyright IBM Corp. 2012 | |
6 | * | |
7 | * This work is licensed under the terms of the GNU GPL, version 2 or later. | |
8 | * See the COPYING file in the top-level directory. | |
9 | */ | |
10 | ||
11 | #include "qemu/osdep.h" | |
12 | #include "qemu-common.h" | |
13 | #include "cpu.h" | |
14 | #include "internal.h" | |
15 | #include "sysemu/hw_accel.h" | |
16 | #include "exec/address-spaces.h" | |
17 | #include "sysemu/sysemu.h" | |
18 | #include "trace.h" | |
19 | ||
20 | QemuMutex qemu_sigp_mutex; | |
21 | ||
22 | typedef struct SigpInfo { | |
23 | uint64_t param; | |
24 | int cc; | |
25 | uint64_t *status_reg; | |
26 | } SigpInfo; | |
27 | ||
28 | static void set_sigp_status(SigpInfo *si, uint64_t status) | |
29 | { | |
30 | *si->status_reg &= 0xffffffff00000000ULL; | |
31 | *si->status_reg |= status; | |
32 | si->cc = SIGP_CC_STATUS_STORED; | |
33 | } | |
34 | ||
35 | static void sigp_start(CPUState *cs, run_on_cpu_data arg) | |
36 | { | |
37 | S390CPU *cpu = S390_CPU(cs); | |
38 | SigpInfo *si = arg.host_ptr; | |
39 | ||
40 | if (s390_cpu_get_state(cpu) != CPU_STATE_STOPPED) { | |
41 | si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; | |
42 | return; | |
43 | } | |
44 | ||
45 | s390_cpu_set_state(CPU_STATE_OPERATING, cpu); | |
46 | si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; | |
47 | } | |
48 | ||
49 | static void sigp_stop(CPUState *cs, run_on_cpu_data arg) | |
50 | { | |
51 | S390CPU *cpu = S390_CPU(cs); | |
52 | SigpInfo *si = arg.host_ptr; | |
53 | ||
54 | if (s390_cpu_get_state(cpu) != CPU_STATE_OPERATING) { | |
55 | si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; | |
56 | return; | |
57 | } | |
58 | ||
59 | /* disabled wait - sleeping in user space */ | |
60 | if (cs->halted) { | |
61 | s390_cpu_set_state(CPU_STATE_STOPPED, cpu); | |
62 | } else { | |
63 | /* execute the stop function */ | |
64 | cpu->env.sigp_order = SIGP_STOP; | |
65 | cpu_inject_stop(cpu); | |
66 | } | |
67 | si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; | |
68 | } | |
69 | ||
70 | static void sigp_stop_and_store_status(CPUState *cs, run_on_cpu_data arg) | |
71 | { | |
72 | S390CPU *cpu = S390_CPU(cs); | |
73 | SigpInfo *si = arg.host_ptr; | |
74 | ||
75 | /* disabled wait - sleeping in user space */ | |
76 | if (s390_cpu_get_state(cpu) == CPU_STATE_OPERATING && cs->halted) { | |
77 | s390_cpu_set_state(CPU_STATE_STOPPED, cpu); | |
78 | } | |
79 | ||
80 | switch (s390_cpu_get_state(cpu)) { | |
81 | case CPU_STATE_OPERATING: | |
82 | cpu->env.sigp_order = SIGP_STOP_STORE_STATUS; | |
83 | cpu_inject_stop(cpu); | |
3047f8b5 | 84 | /* store will be performed in do_stop_interrup() */ |
74b4c74d DH |
85 | break; |
86 | case CPU_STATE_STOPPED: | |
87 | /* already stopped, just store the status */ | |
88 | cpu_synchronize_state(cs); | |
89 | s390_store_status(cpu, S390_STORE_STATUS_DEF_ADDR, true); | |
90 | break; | |
91 | } | |
92 | si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; | |
93 | } | |
94 | ||
95 | static void sigp_store_status_at_address(CPUState *cs, run_on_cpu_data arg) | |
96 | { | |
97 | S390CPU *cpu = S390_CPU(cs); | |
98 | SigpInfo *si = arg.host_ptr; | |
99 | uint32_t address = si->param & 0x7ffffe00u; | |
100 | ||
101 | /* cpu has to be stopped */ | |
102 | if (s390_cpu_get_state(cpu) != CPU_STATE_STOPPED) { | |
103 | set_sigp_status(si, SIGP_STAT_INCORRECT_STATE); | |
104 | return; | |
105 | } | |
106 | ||
107 | cpu_synchronize_state(cs); | |
108 | ||
109 | if (s390_store_status(cpu, address, false)) { | |
110 | set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER); | |
111 | return; | |
112 | } | |
113 | si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; | |
114 | } | |
115 | ||
116 | #define ADTL_SAVE_LC_MASK 0xfUL | |
117 | static void sigp_store_adtl_status(CPUState *cs, run_on_cpu_data arg) | |
118 | { | |
119 | S390CPU *cpu = S390_CPU(cs); | |
120 | SigpInfo *si = arg.host_ptr; | |
121 | uint8_t lc = si->param & ADTL_SAVE_LC_MASK; | |
122 | hwaddr addr = si->param & ~ADTL_SAVE_LC_MASK; | |
123 | hwaddr len = 1UL << (lc ? lc : 10); | |
124 | ||
125 | if (!s390_has_feat(S390_FEAT_VECTOR) && | |
126 | !s390_has_feat(S390_FEAT_GUARDED_STORAGE)) { | |
127 | set_sigp_status(si, SIGP_STAT_INVALID_ORDER); | |
128 | return; | |
129 | } | |
130 | ||
131 | /* cpu has to be stopped */ | |
132 | if (s390_cpu_get_state(cpu) != CPU_STATE_STOPPED) { | |
133 | set_sigp_status(si, SIGP_STAT_INCORRECT_STATE); | |
134 | return; | |
135 | } | |
136 | ||
137 | /* address must be aligned to length */ | |
138 | if (addr & (len - 1)) { | |
139 | set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER); | |
140 | return; | |
141 | } | |
142 | ||
143 | /* no GS: only lc == 0 is valid */ | |
144 | if (!s390_has_feat(S390_FEAT_GUARDED_STORAGE) && | |
145 | lc != 0) { | |
146 | set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER); | |
147 | return; | |
148 | } | |
149 | ||
150 | /* GS: 0, 10, 11, 12 are valid */ | |
151 | if (s390_has_feat(S390_FEAT_GUARDED_STORAGE) && | |
152 | lc != 0 && | |
153 | lc != 10 && | |
154 | lc != 11 && | |
155 | lc != 12) { | |
156 | set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER); | |
157 | return; | |
158 | } | |
159 | ||
160 | cpu_synchronize_state(cs); | |
161 | ||
162 | if (s390_store_adtl_status(cpu, addr, len)) { | |
163 | set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER); | |
164 | return; | |
165 | } | |
166 | si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; | |
167 | } | |
168 | ||
169 | static void sigp_restart(CPUState *cs, run_on_cpu_data arg) | |
170 | { | |
171 | S390CPU *cpu = S390_CPU(cs); | |
172 | SigpInfo *si = arg.host_ptr; | |
173 | ||
174 | switch (s390_cpu_get_state(cpu)) { | |
175 | case CPU_STATE_STOPPED: | |
176 | /* the restart irq has to be delivered prior to any other pending irq */ | |
177 | cpu_synchronize_state(cs); | |
178 | do_restart_interrupt(&cpu->env); | |
179 | s390_cpu_set_state(CPU_STATE_OPERATING, cpu); | |
180 | break; | |
181 | case CPU_STATE_OPERATING: | |
182 | cpu_inject_restart(cpu); | |
183 | break; | |
184 | } | |
185 | si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; | |
186 | } | |
187 | ||
188 | static void sigp_initial_cpu_reset(CPUState *cs, run_on_cpu_data arg) | |
189 | { | |
190 | S390CPU *cpu = S390_CPU(cs); | |
191 | S390CPUClass *scc = S390_CPU_GET_CLASS(cpu); | |
192 | SigpInfo *si = arg.host_ptr; | |
193 | ||
194 | cpu_synchronize_state(cs); | |
195 | scc->initial_cpu_reset(cs); | |
196 | cpu_synchronize_post_reset(cs); | |
197 | si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; | |
198 | } | |
199 | ||
200 | static void sigp_cpu_reset(CPUState *cs, run_on_cpu_data arg) | |
201 | { | |
202 | S390CPU *cpu = S390_CPU(cs); | |
203 | S390CPUClass *scc = S390_CPU_GET_CLASS(cpu); | |
204 | SigpInfo *si = arg.host_ptr; | |
205 | ||
206 | cpu_synchronize_state(cs); | |
207 | scc->cpu_reset(cs); | |
208 | cpu_synchronize_post_reset(cs); | |
209 | si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; | |
210 | } | |
211 | ||
212 | static void sigp_set_prefix(CPUState *cs, run_on_cpu_data arg) | |
213 | { | |
214 | S390CPU *cpu = S390_CPU(cs); | |
215 | SigpInfo *si = arg.host_ptr; | |
216 | uint32_t addr = si->param & 0x7fffe000u; | |
217 | ||
218 | cpu_synchronize_state(cs); | |
219 | ||
220 | if (!address_space_access_valid(&address_space_memory, addr, | |
221 | sizeof(struct LowCore), false)) { | |
222 | set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER); | |
223 | return; | |
224 | } | |
225 | ||
226 | /* cpu has to be stopped */ | |
227 | if (s390_cpu_get_state(cpu) != CPU_STATE_STOPPED) { | |
228 | set_sigp_status(si, SIGP_STAT_INCORRECT_STATE); | |
229 | return; | |
230 | } | |
231 | ||
232 | cpu->env.psa = addr; | |
233 | cpu_synchronize_post_init(cs); | |
234 | si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; | |
235 | } | |
236 | ||
d1b468bc DH |
237 | static void sigp_sense_running(S390CPU *dst_cpu, SigpInfo *si) |
238 | { | |
239 | if (!tcg_enabled()) { | |
240 | /* handled in KVM */ | |
241 | set_sigp_status(si, SIGP_STAT_INVALID_ORDER); | |
242 | return; | |
243 | } | |
244 | ||
245 | /* sensing without locks is racy, but it's the same for real hw */ | |
246 | if (!s390_has_feat(S390_FEAT_SENSE_RUNNING_STATUS)) { | |
247 | set_sigp_status(si, SIGP_STAT_INVALID_ORDER); | |
248 | return; | |
249 | } | |
250 | ||
251 | /* If halted (which includes also STOPPED), it is not running */ | |
252 | if (CPU(dst_cpu)->halted) { | |
253 | si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; | |
254 | } else { | |
255 | set_sigp_status(si, SIGP_STAT_NOT_RUNNING); | |
256 | } | |
257 | } | |
258 | ||
74b4c74d DH |
259 | static int handle_sigp_single_dst(S390CPU *dst_cpu, uint8_t order, |
260 | uint64_t param, uint64_t *status_reg) | |
261 | { | |
262 | SigpInfo si = { | |
263 | .param = param, | |
264 | .status_reg = status_reg, | |
265 | }; | |
266 | ||
267 | /* cpu available? */ | |
268 | if (dst_cpu == NULL) { | |
269 | return SIGP_CC_NOT_OPERATIONAL; | |
270 | } | |
271 | ||
272 | /* only resets can break pending orders */ | |
273 | if (dst_cpu->env.sigp_order != 0 && | |
274 | order != SIGP_CPU_RESET && | |
275 | order != SIGP_INITIAL_CPU_RESET) { | |
276 | return SIGP_CC_BUSY; | |
277 | } | |
278 | ||
279 | switch (order) { | |
280 | case SIGP_START: | |
281 | run_on_cpu(CPU(dst_cpu), sigp_start, RUN_ON_CPU_HOST_PTR(&si)); | |
282 | break; | |
283 | case SIGP_STOP: | |
284 | run_on_cpu(CPU(dst_cpu), sigp_stop, RUN_ON_CPU_HOST_PTR(&si)); | |
285 | break; | |
286 | case SIGP_RESTART: | |
287 | run_on_cpu(CPU(dst_cpu), sigp_restart, RUN_ON_CPU_HOST_PTR(&si)); | |
288 | break; | |
289 | case SIGP_STOP_STORE_STATUS: | |
290 | run_on_cpu(CPU(dst_cpu), sigp_stop_and_store_status, RUN_ON_CPU_HOST_PTR(&si)); | |
291 | break; | |
292 | case SIGP_STORE_STATUS_ADDR: | |
293 | run_on_cpu(CPU(dst_cpu), sigp_store_status_at_address, RUN_ON_CPU_HOST_PTR(&si)); | |
294 | break; | |
295 | case SIGP_STORE_ADTL_STATUS: | |
296 | run_on_cpu(CPU(dst_cpu), sigp_store_adtl_status, RUN_ON_CPU_HOST_PTR(&si)); | |
297 | break; | |
298 | case SIGP_SET_PREFIX: | |
299 | run_on_cpu(CPU(dst_cpu), sigp_set_prefix, RUN_ON_CPU_HOST_PTR(&si)); | |
300 | break; | |
301 | case SIGP_INITIAL_CPU_RESET: | |
302 | run_on_cpu(CPU(dst_cpu), sigp_initial_cpu_reset, RUN_ON_CPU_HOST_PTR(&si)); | |
303 | break; | |
304 | case SIGP_CPU_RESET: | |
305 | run_on_cpu(CPU(dst_cpu), sigp_cpu_reset, RUN_ON_CPU_HOST_PTR(&si)); | |
306 | break; | |
d1b468bc DH |
307 | case SIGP_SENSE_RUNNING: |
308 | sigp_sense_running(dst_cpu, &si); | |
309 | break; | |
74b4c74d DH |
310 | default: |
311 | set_sigp_status(&si, SIGP_STAT_INVALID_ORDER); | |
312 | } | |
313 | ||
314 | return si.cc; | |
315 | } | |
316 | ||
317 | static int sigp_set_architecture(S390CPU *cpu, uint32_t param, | |
318 | uint64_t *status_reg) | |
319 | { | |
320 | CPUState *cur_cs; | |
321 | S390CPU *cur_cpu; | |
322 | bool all_stopped = true; | |
323 | ||
324 | CPU_FOREACH(cur_cs) { | |
325 | cur_cpu = S390_CPU(cur_cs); | |
326 | ||
327 | if (cur_cpu == cpu) { | |
328 | continue; | |
329 | } | |
330 | if (s390_cpu_get_state(cur_cpu) != CPU_STATE_STOPPED) { | |
331 | all_stopped = false; | |
332 | } | |
333 | } | |
334 | ||
335 | *status_reg &= 0xffffffff00000000ULL; | |
336 | ||
337 | /* Reject set arch order, with czam we're always in z/Arch mode. */ | |
338 | *status_reg |= (all_stopped ? SIGP_STAT_INVALID_PARAMETER : | |
339 | SIGP_STAT_INCORRECT_STATE); | |
340 | return SIGP_CC_STATUS_STORED; | |
341 | } | |
342 | ||
343 | int handle_sigp(CPUS390XState *env, uint8_t order, uint64_t r1, uint64_t r3) | |
344 | { | |
345 | uint64_t *status_reg = &env->regs[r1]; | |
346 | uint64_t param = (r1 % 2) ? env->regs[r1] : env->regs[r1 + 1]; | |
347 | S390CPU *cpu = s390_env_get_cpu(env); | |
348 | S390CPU *dst_cpu = NULL; | |
349 | int ret; | |
350 | ||
351 | if (qemu_mutex_trylock(&qemu_sigp_mutex)) { | |
352 | ret = SIGP_CC_BUSY; | |
353 | goto out; | |
354 | } | |
355 | ||
356 | switch (order) { | |
357 | case SIGP_SET_ARCH: | |
358 | ret = sigp_set_architecture(cpu, param, status_reg); | |
359 | break; | |
360 | default: | |
361 | /* all other sigp orders target a single vcpu */ | |
362 | dst_cpu = s390_cpu_addr2state(env->regs[r3]); | |
363 | ret = handle_sigp_single_dst(dst_cpu, order, param, status_reg); | |
364 | } | |
365 | qemu_mutex_unlock(&qemu_sigp_mutex); | |
366 | ||
367 | out: | |
368 | trace_sigp_finished(order, CPU(cpu)->cpu_index, | |
369 | dst_cpu ? CPU(dst_cpu)->cpu_index : -1, ret); | |
370 | g_assert(ret >= 0); | |
371 | ||
372 | return ret; | |
373 | } | |
374 | ||
375 | int s390_cpu_restart(S390CPU *cpu) | |
376 | { | |
377 | SigpInfo si = {}; | |
378 | ||
379 | if (tcg_enabled()) { | |
380 | /* FIXME TCG */ | |
381 | return -ENOSYS; | |
382 | } | |
383 | ||
384 | run_on_cpu(CPU(cpu), sigp_restart, RUN_ON_CPU_HOST_PTR(&si)); | |
385 | return 0; | |
386 | } | |
387 | ||
3047f8b5 DH |
388 | void do_stop_interrupt(CPUS390XState *env) |
389 | { | |
390 | S390CPU *cpu = s390_env_get_cpu(env); | |
391 | ||
392 | if (s390_cpu_set_state(CPU_STATE_STOPPED, cpu) == 0) { | |
393 | qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); | |
394 | } | |
395 | if (cpu->env.sigp_order == SIGP_STOP_STORE_STATUS) { | |
396 | s390_store_status(cpu, S390_STORE_STATUS_DEF_ADDR, true); | |
397 | } | |
398 | env->sigp_order = 0; | |
399 | } | |
400 | ||
74b4c74d DH |
401 | void s390_init_sigp(void) |
402 | { | |
403 | qemu_mutex_init(&qemu_sigp_mutex); | |
404 | } |