1
2
3
4
5
6
7
8
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 "exec/exec-all.h"
18#include "sysemu/sysemu.h"
19#include "trace.h"
20#include "qapi/qapi-types-misc.h"
21
22QemuMutex qemu_sigp_mutex;
23
24typedef struct SigpInfo {
25 uint64_t param;
26 int cc;
27 uint64_t *status_reg;
28} SigpInfo;
29
30static void set_sigp_status(SigpInfo *si, uint64_t status)
31{
32 *si->status_reg &= 0xffffffff00000000ULL;
33 *si->status_reg |= status;
34 si->cc = SIGP_CC_STATUS_STORED;
35}
36
37static void sigp_sense(S390CPU *dst_cpu, SigpInfo *si)
38{
39 uint8_t state = s390_cpu_get_state(dst_cpu);
40 bool ext_call = dst_cpu->env.pending_int & INTERRUPT_EXTERNAL_CALL;
41 uint64_t status = 0;
42
43 if (!tcg_enabled()) {
44
45 set_sigp_status(si, SIGP_STAT_INVALID_ORDER);
46 return;
47 }
48
49
50 if (state != S390_CPU_STATE_STOPPED && !ext_call) {
51 si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
52 } else {
53 if (ext_call) {
54 status |= SIGP_STAT_EXT_CALL_PENDING;
55 }
56 if (state == S390_CPU_STATE_STOPPED) {
57 status |= SIGP_STAT_STOPPED;
58 }
59 set_sigp_status(si, status);
60 }
61}
62
63static void sigp_external_call(S390CPU *src_cpu, S390CPU *dst_cpu, SigpInfo *si)
64{
65 int ret;
66
67 if (!tcg_enabled()) {
68
69 set_sigp_status(si, SIGP_STAT_INVALID_ORDER);
70 return;
71 }
72
73 ret = cpu_inject_external_call(dst_cpu, src_cpu->env.core_id);
74 if (!ret) {
75 si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
76 } else {
77 set_sigp_status(si, SIGP_STAT_EXT_CALL_PENDING);
78 }
79}
80
81static void sigp_emergency(S390CPU *src_cpu, S390CPU *dst_cpu, SigpInfo *si)
82{
83 if (!tcg_enabled()) {
84
85 set_sigp_status(si, SIGP_STAT_INVALID_ORDER);
86 return;
87 }
88
89 cpu_inject_emergency_signal(dst_cpu, src_cpu->env.core_id);
90 si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
91}
92
93static void sigp_start(CPUState *cs, run_on_cpu_data arg)
94{
95 S390CPU *cpu = S390_CPU(cs);
96 SigpInfo *si = arg.host_ptr;
97
98 if (s390_cpu_get_state(cpu) != S390_CPU_STATE_STOPPED) {
99 si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
100 return;
101 }
102
103 s390_cpu_set_state(S390_CPU_STATE_OPERATING, cpu);
104 si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
105}
106
107static void sigp_stop(CPUState *cs, run_on_cpu_data arg)
108{
109 S390CPU *cpu = S390_CPU(cs);
110 SigpInfo *si = arg.host_ptr;
111
112 if (s390_cpu_get_state(cpu) != S390_CPU_STATE_OPERATING) {
113 si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
114 return;
115 }
116
117
118 if (cs->halted) {
119 s390_cpu_set_state(S390_CPU_STATE_STOPPED, cpu);
120 } else {
121
122 cpu->env.sigp_order = SIGP_STOP;
123 cpu_inject_stop(cpu);
124 }
125 si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
126}
127
128static void sigp_stop_and_store_status(CPUState *cs, run_on_cpu_data arg)
129{
130 S390CPU *cpu = S390_CPU(cs);
131 SigpInfo *si = arg.host_ptr;
132
133
134 if (s390_cpu_get_state(cpu) == S390_CPU_STATE_OPERATING && cs->halted) {
135 s390_cpu_set_state(S390_CPU_STATE_STOPPED, cpu);
136 }
137
138 switch (s390_cpu_get_state(cpu)) {
139 case S390_CPU_STATE_OPERATING:
140 cpu->env.sigp_order = SIGP_STOP_STORE_STATUS;
141 cpu_inject_stop(cpu);
142
143 break;
144 case S390_CPU_STATE_STOPPED:
145
146 cpu_synchronize_state(cs);
147 s390_store_status(cpu, S390_STORE_STATUS_DEF_ADDR, true);
148 break;
149 }
150 si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
151}
152
153static void sigp_store_status_at_address(CPUState *cs, run_on_cpu_data arg)
154{
155 S390CPU *cpu = S390_CPU(cs);
156 SigpInfo *si = arg.host_ptr;
157 uint32_t address = si->param & 0x7ffffe00u;
158
159
160 if (s390_cpu_get_state(cpu) != S390_CPU_STATE_STOPPED) {
161 set_sigp_status(si, SIGP_STAT_INCORRECT_STATE);
162 return;
163 }
164
165 cpu_synchronize_state(cs);
166
167 if (s390_store_status(cpu, address, false)) {
168 set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER);
169 return;
170 }
171 si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
172}
173
174#define ADTL_SAVE_LC_MASK 0xfUL
175static void sigp_store_adtl_status(CPUState *cs, run_on_cpu_data arg)
176{
177 S390CPU *cpu = S390_CPU(cs);
178 SigpInfo *si = arg.host_ptr;
179 uint8_t lc = si->param & ADTL_SAVE_LC_MASK;
180 hwaddr addr = si->param & ~ADTL_SAVE_LC_MASK;
181 hwaddr len = 1UL << (lc ? lc : 10);
182
183 if (!s390_has_feat(S390_FEAT_VECTOR) &&
184 !s390_has_feat(S390_FEAT_GUARDED_STORAGE)) {
185 set_sigp_status(si, SIGP_STAT_INVALID_ORDER);
186 return;
187 }
188
189
190 if (s390_cpu_get_state(cpu) != S390_CPU_STATE_STOPPED) {
191 set_sigp_status(si, SIGP_STAT_INCORRECT_STATE);
192 return;
193 }
194
195
196 if (addr & (len - 1)) {
197 set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER);
198 return;
199 }
200
201
202 if (!s390_has_feat(S390_FEAT_GUARDED_STORAGE) &&
203 lc != 0) {
204 set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER);
205 return;
206 }
207
208
209 if (s390_has_feat(S390_FEAT_GUARDED_STORAGE) &&
210 lc != 0 &&
211 lc != 10 &&
212 lc != 11 &&
213 lc != 12) {
214 set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER);
215 return;
216 }
217
218 cpu_synchronize_state(cs);
219
220 if (s390_store_adtl_status(cpu, addr, len)) {
221 set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER);
222 return;
223 }
224 si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
225}
226
227static void sigp_restart(CPUState *cs, run_on_cpu_data arg)
228{
229 S390CPU *cpu = S390_CPU(cs);
230 SigpInfo *si = arg.host_ptr;
231
232 switch (s390_cpu_get_state(cpu)) {
233 case S390_CPU_STATE_STOPPED:
234
235 cpu_synchronize_state(cs);
236
237
238
239
240 s390_cpu_set_state(S390_CPU_STATE_OPERATING, cpu);
241 do_restart_interrupt(&cpu->env);
242 break;
243 case S390_CPU_STATE_OPERATING:
244 cpu_inject_restart(cpu);
245 break;
246 }
247 si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
248}
249
250static void sigp_initial_cpu_reset(CPUState *cs, run_on_cpu_data arg)
251{
252 S390CPU *cpu = S390_CPU(cs);
253 S390CPUClass *scc = S390_CPU_GET_CLASS(cpu);
254 SigpInfo *si = arg.host_ptr;
255
256 cpu_synchronize_state(cs);
257 scc->initial_cpu_reset(cs);
258 cpu_synchronize_post_reset(cs);
259 si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
260}
261
262static void sigp_cpu_reset(CPUState *cs, run_on_cpu_data arg)
263{
264 S390CPU *cpu = S390_CPU(cs);
265 S390CPUClass *scc = S390_CPU_GET_CLASS(cpu);
266 SigpInfo *si = arg.host_ptr;
267
268 cpu_synchronize_state(cs);
269 scc->cpu_reset(cs);
270 cpu_synchronize_post_reset(cs);
271 si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
272}
273
274static void sigp_set_prefix(CPUState *cs, run_on_cpu_data arg)
275{
276 S390CPU *cpu = S390_CPU(cs);
277 SigpInfo *si = arg.host_ptr;
278 uint32_t addr = si->param & 0x7fffe000u;
279
280 cpu_synchronize_state(cs);
281
282 if (!address_space_access_valid(&address_space_memory, addr,
283 sizeof(struct LowCore), false)) {
284 set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER);
285 return;
286 }
287
288
289 if (s390_cpu_get_state(cpu) != S390_CPU_STATE_STOPPED) {
290 set_sigp_status(si, SIGP_STAT_INCORRECT_STATE);
291 return;
292 }
293
294 cpu->env.psa = addr;
295 tlb_flush(cs);
296 cpu_synchronize_post_init(cs);
297 si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
298}
299
300static void sigp_cond_emergency(S390CPU *src_cpu, S390CPU *dst_cpu,
301 SigpInfo *si)
302{
303 const uint64_t psw_int_mask = PSW_MASK_IO | PSW_MASK_EXT;
304 uint16_t p_asn, s_asn, asn;
305 uint64_t psw_addr, psw_mask;
306 bool idle;
307
308 if (!tcg_enabled()) {
309
310 set_sigp_status(si, SIGP_STAT_INVALID_ORDER);
311 return;
312 }
313
314
315 idle = CPU(dst_cpu)->halted;
316 psw_addr = dst_cpu->env.psw.addr;
317 psw_mask = dst_cpu->env.psw.mask;
318 asn = si->param;
319 p_asn = dst_cpu->env.cregs[4] & 0xffff;
320 s_asn = dst_cpu->env.cregs[3] & 0xffff;
321
322 if (s390_cpu_get_state(dst_cpu) != S390_CPU_STATE_STOPPED ||
323 (psw_mask & psw_int_mask) != psw_int_mask ||
324 (idle && psw_addr != 0) ||
325 (!idle && (asn == p_asn || asn == s_asn))) {
326 cpu_inject_emergency_signal(dst_cpu, src_cpu->env.core_id);
327 } else {
328 set_sigp_status(si, SIGP_STAT_INCORRECT_STATE);
329 }
330
331 si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
332}
333
334static void sigp_sense_running(S390CPU *dst_cpu, SigpInfo *si)
335{
336 if (!tcg_enabled()) {
337
338 set_sigp_status(si, SIGP_STAT_INVALID_ORDER);
339 return;
340 }
341
342
343 if (!s390_has_feat(S390_FEAT_SENSE_RUNNING_STATUS)) {
344 set_sigp_status(si, SIGP_STAT_INVALID_ORDER);
345 return;
346 }
347
348
349 if (CPU(dst_cpu)->halted) {
350 si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
351 } else {
352 set_sigp_status(si, SIGP_STAT_NOT_RUNNING);
353 }
354}
355
356static int handle_sigp_single_dst(S390CPU *cpu, S390CPU *dst_cpu, uint8_t order,
357 uint64_t param, uint64_t *status_reg)
358{
359 SigpInfo si = {
360 .param = param,
361 .status_reg = status_reg,
362 };
363
364
365 if (dst_cpu == NULL) {
366 return SIGP_CC_NOT_OPERATIONAL;
367 }
368
369
370 if (dst_cpu->env.sigp_order != 0 &&
371 order != SIGP_CPU_RESET &&
372 order != SIGP_INITIAL_CPU_RESET) {
373 return SIGP_CC_BUSY;
374 }
375
376 switch (order) {
377 case SIGP_SENSE:
378 sigp_sense(dst_cpu, &si);
379 break;
380 case SIGP_EXTERNAL_CALL:
381 sigp_external_call(cpu, dst_cpu, &si);
382 break;
383 case SIGP_EMERGENCY:
384 sigp_emergency(cpu, dst_cpu, &si);
385 break;
386 case SIGP_START:
387 run_on_cpu(CPU(dst_cpu), sigp_start, RUN_ON_CPU_HOST_PTR(&si));
388 break;
389 case SIGP_STOP:
390 run_on_cpu(CPU(dst_cpu), sigp_stop, RUN_ON_CPU_HOST_PTR(&si));
391 break;
392 case SIGP_RESTART:
393 run_on_cpu(CPU(dst_cpu), sigp_restart, RUN_ON_CPU_HOST_PTR(&si));
394 break;
395 case SIGP_STOP_STORE_STATUS:
396 run_on_cpu(CPU(dst_cpu), sigp_stop_and_store_status, RUN_ON_CPU_HOST_PTR(&si));
397 break;
398 case SIGP_STORE_STATUS_ADDR:
399 run_on_cpu(CPU(dst_cpu), sigp_store_status_at_address, RUN_ON_CPU_HOST_PTR(&si));
400 break;
401 case SIGP_STORE_ADTL_STATUS:
402 run_on_cpu(CPU(dst_cpu), sigp_store_adtl_status, RUN_ON_CPU_HOST_PTR(&si));
403 break;
404 case SIGP_SET_PREFIX:
405 run_on_cpu(CPU(dst_cpu), sigp_set_prefix, RUN_ON_CPU_HOST_PTR(&si));
406 break;
407 case SIGP_INITIAL_CPU_RESET:
408 run_on_cpu(CPU(dst_cpu), sigp_initial_cpu_reset, RUN_ON_CPU_HOST_PTR(&si));
409 break;
410 case SIGP_CPU_RESET:
411 run_on_cpu(CPU(dst_cpu), sigp_cpu_reset, RUN_ON_CPU_HOST_PTR(&si));
412 break;
413 case SIGP_COND_EMERGENCY:
414 sigp_cond_emergency(cpu, dst_cpu, &si);
415 break;
416 case SIGP_SENSE_RUNNING:
417 sigp_sense_running(dst_cpu, &si);
418 break;
419 default:
420 set_sigp_status(&si, SIGP_STAT_INVALID_ORDER);
421 }
422
423 return si.cc;
424}
425
426static int sigp_set_architecture(S390CPU *cpu, uint32_t param,
427 uint64_t *status_reg)
428{
429 CPUState *cur_cs;
430 S390CPU *cur_cpu;
431 bool all_stopped = true;
432
433 CPU_FOREACH(cur_cs) {
434 cur_cpu = S390_CPU(cur_cs);
435
436 if (cur_cpu == cpu) {
437 continue;
438 }
439 if (s390_cpu_get_state(cur_cpu) != S390_CPU_STATE_STOPPED) {
440 all_stopped = false;
441 }
442 }
443
444 *status_reg &= 0xffffffff00000000ULL;
445
446
447 *status_reg |= (all_stopped ? SIGP_STAT_INVALID_PARAMETER :
448 SIGP_STAT_INCORRECT_STATE);
449 return SIGP_CC_STATUS_STORED;
450}
451
452int handle_sigp(CPUS390XState *env, uint8_t order, uint64_t r1, uint64_t r3)
453{
454 uint64_t *status_reg = &env->regs[r1];
455 uint64_t param = (r1 % 2) ? env->regs[r1] : env->regs[r1 + 1];
456 S390CPU *cpu = s390_env_get_cpu(env);
457 S390CPU *dst_cpu = NULL;
458 int ret;
459
460 if (qemu_mutex_trylock(&qemu_sigp_mutex)) {
461 ret = SIGP_CC_BUSY;
462 goto out;
463 }
464
465 switch (order) {
466 case SIGP_SET_ARCH:
467 ret = sigp_set_architecture(cpu, param, status_reg);
468 break;
469 default:
470
471 dst_cpu = s390_cpu_addr2state(env->regs[r3]);
472 ret = handle_sigp_single_dst(cpu, dst_cpu, order, param, status_reg);
473 }
474 qemu_mutex_unlock(&qemu_sigp_mutex);
475
476out:
477 trace_sigp_finished(order, CPU(cpu)->cpu_index,
478 dst_cpu ? CPU(dst_cpu)->cpu_index : -1, ret);
479 g_assert(ret >= 0);
480
481 return ret;
482}
483
484int s390_cpu_restart(S390CPU *cpu)
485{
486 SigpInfo si = {};
487
488 run_on_cpu(CPU(cpu), sigp_restart, RUN_ON_CPU_HOST_PTR(&si));
489 return 0;
490}
491
492void do_stop_interrupt(CPUS390XState *env)
493{
494 S390CPU *cpu = s390_env_get_cpu(env);
495
496 if (s390_cpu_set_state(S390_CPU_STATE_STOPPED, cpu) == 0) {
497 qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
498 }
499 if (cpu->env.sigp_order == SIGP_STOP_STORE_STATUS) {
500 s390_store_status(cpu, S390_STORE_STATUS_DEF_ADDR, true);
501 }
502 env->sigp_order = 0;
503 env->pending_int &= ~INTERRUPT_STOP;
504}
505
506void s390_init_sigp(void)
507{
508 qemu_mutex_init(&qemu_sigp_mutex);
509}
510