xref: /qemu/target/s390x/sigp.c (revision 302230fc449a5778dfb703e7fc2f2bdfe71dcd9d)
174b4c74dSDavid Hildenbrand /*
274b4c74dSDavid Hildenbrand  * s390x SIGP instruction handling
374b4c74dSDavid Hildenbrand  *
474b4c74dSDavid Hildenbrand  * Copyright (c) 2009 Alexander Graf <agraf@suse.de>
574b4c74dSDavid Hildenbrand  * Copyright IBM Corp. 2012
674b4c74dSDavid Hildenbrand  *
774b4c74dSDavid Hildenbrand  * This work is licensed under the terms of the GNU GPL, version 2 or later.
874b4c74dSDavid Hildenbrand  * See the COPYING file in the top-level directory.
974b4c74dSDavid Hildenbrand  */
1074b4c74dSDavid Hildenbrand 
1174b4c74dSDavid Hildenbrand #include "qemu/osdep.h"
1274b4c74dSDavid Hildenbrand #include "qemu-common.h"
1374b4c74dSDavid Hildenbrand #include "cpu.h"
1474b4c74dSDavid Hildenbrand #include "internal.h"
1574b4c74dSDavid Hildenbrand #include "sysemu/hw_accel.h"
1674b4c74dSDavid Hildenbrand #include "exec/address-spaces.h"
1774b4c74dSDavid Hildenbrand #include "sysemu/sysemu.h"
1874b4c74dSDavid Hildenbrand #include "trace.h"
1974b4c74dSDavid Hildenbrand 
2074b4c74dSDavid Hildenbrand QemuMutex qemu_sigp_mutex;
2174b4c74dSDavid Hildenbrand 
2274b4c74dSDavid Hildenbrand typedef struct SigpInfo {
2374b4c74dSDavid Hildenbrand     uint64_t param;
2474b4c74dSDavid Hildenbrand     int cc;
2574b4c74dSDavid Hildenbrand     uint64_t *status_reg;
2674b4c74dSDavid Hildenbrand } SigpInfo;
2774b4c74dSDavid Hildenbrand 
2874b4c74dSDavid Hildenbrand static void set_sigp_status(SigpInfo *si, uint64_t status)
2974b4c74dSDavid Hildenbrand {
3074b4c74dSDavid Hildenbrand     *si->status_reg &= 0xffffffff00000000ULL;
3174b4c74dSDavid Hildenbrand     *si->status_reg |= status;
3274b4c74dSDavid Hildenbrand     si->cc = SIGP_CC_STATUS_STORED;
3374b4c74dSDavid Hildenbrand }
3474b4c74dSDavid Hildenbrand 
35*302230fcSDavid Hildenbrand static void sigp_sense(S390CPU *dst_cpu, SigpInfo *si)
36*302230fcSDavid Hildenbrand {
37*302230fcSDavid Hildenbrand     uint8_t state = s390_cpu_get_state(dst_cpu);
38*302230fcSDavid Hildenbrand     bool ext_call = dst_cpu->env.pending_int & INTERRUPT_EXTERNAL_CALL;
39*302230fcSDavid Hildenbrand     uint64_t status = 0;
40*302230fcSDavid Hildenbrand 
41*302230fcSDavid Hildenbrand     if (!tcg_enabled()) {
42*302230fcSDavid Hildenbrand         /* handled in KVM */
43*302230fcSDavid Hildenbrand         set_sigp_status(si, SIGP_STAT_INVALID_ORDER);
44*302230fcSDavid Hildenbrand         return;
45*302230fcSDavid Hildenbrand     }
46*302230fcSDavid Hildenbrand 
47*302230fcSDavid Hildenbrand     /* sensing without locks is racy, but it's the same for real hw */
48*302230fcSDavid Hildenbrand     if (state != CPU_STATE_STOPPED && !ext_call) {
49*302230fcSDavid Hildenbrand         si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
50*302230fcSDavid Hildenbrand     } else {
51*302230fcSDavid Hildenbrand         if (ext_call) {
52*302230fcSDavid Hildenbrand             status |= SIGP_STAT_EXT_CALL_PENDING;
53*302230fcSDavid Hildenbrand         }
54*302230fcSDavid Hildenbrand         if (state == CPU_STATE_STOPPED) {
55*302230fcSDavid Hildenbrand             status |= SIGP_STAT_STOPPED;
56*302230fcSDavid Hildenbrand         }
57*302230fcSDavid Hildenbrand         set_sigp_status(si, status);
58*302230fcSDavid Hildenbrand     }
59*302230fcSDavid Hildenbrand }
60*302230fcSDavid Hildenbrand 
6174b4c74dSDavid Hildenbrand static void sigp_start(CPUState *cs, run_on_cpu_data arg)
6274b4c74dSDavid Hildenbrand {
6374b4c74dSDavid Hildenbrand     S390CPU *cpu = S390_CPU(cs);
6474b4c74dSDavid Hildenbrand     SigpInfo *si = arg.host_ptr;
6574b4c74dSDavid Hildenbrand 
6674b4c74dSDavid Hildenbrand     if (s390_cpu_get_state(cpu) != CPU_STATE_STOPPED) {
6774b4c74dSDavid Hildenbrand         si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
6874b4c74dSDavid Hildenbrand         return;
6974b4c74dSDavid Hildenbrand     }
7074b4c74dSDavid Hildenbrand 
7174b4c74dSDavid Hildenbrand     s390_cpu_set_state(CPU_STATE_OPERATING, cpu);
7274b4c74dSDavid Hildenbrand     si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
7374b4c74dSDavid Hildenbrand }
7474b4c74dSDavid Hildenbrand 
7574b4c74dSDavid Hildenbrand static void sigp_stop(CPUState *cs, run_on_cpu_data arg)
7674b4c74dSDavid Hildenbrand {
7774b4c74dSDavid Hildenbrand     S390CPU *cpu = S390_CPU(cs);
7874b4c74dSDavid Hildenbrand     SigpInfo *si = arg.host_ptr;
7974b4c74dSDavid Hildenbrand 
8074b4c74dSDavid Hildenbrand     if (s390_cpu_get_state(cpu) != CPU_STATE_OPERATING) {
8174b4c74dSDavid Hildenbrand         si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
8274b4c74dSDavid Hildenbrand         return;
8374b4c74dSDavid Hildenbrand     }
8474b4c74dSDavid Hildenbrand 
8574b4c74dSDavid Hildenbrand     /* disabled wait - sleeping in user space */
8674b4c74dSDavid Hildenbrand     if (cs->halted) {
8774b4c74dSDavid Hildenbrand         s390_cpu_set_state(CPU_STATE_STOPPED, cpu);
8874b4c74dSDavid Hildenbrand     } else {
8974b4c74dSDavid Hildenbrand         /* execute the stop function */
9074b4c74dSDavid Hildenbrand         cpu->env.sigp_order = SIGP_STOP;
9174b4c74dSDavid Hildenbrand         cpu_inject_stop(cpu);
9274b4c74dSDavid Hildenbrand     }
9374b4c74dSDavid Hildenbrand     si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
9474b4c74dSDavid Hildenbrand }
9574b4c74dSDavid Hildenbrand 
9674b4c74dSDavid Hildenbrand static void sigp_stop_and_store_status(CPUState *cs, run_on_cpu_data arg)
9774b4c74dSDavid Hildenbrand {
9874b4c74dSDavid Hildenbrand     S390CPU *cpu = S390_CPU(cs);
9974b4c74dSDavid Hildenbrand     SigpInfo *si = arg.host_ptr;
10074b4c74dSDavid Hildenbrand 
10174b4c74dSDavid Hildenbrand     /* disabled wait - sleeping in user space */
10274b4c74dSDavid Hildenbrand     if (s390_cpu_get_state(cpu) == CPU_STATE_OPERATING && cs->halted) {
10374b4c74dSDavid Hildenbrand         s390_cpu_set_state(CPU_STATE_STOPPED, cpu);
10474b4c74dSDavid Hildenbrand     }
10574b4c74dSDavid Hildenbrand 
10674b4c74dSDavid Hildenbrand     switch (s390_cpu_get_state(cpu)) {
10774b4c74dSDavid Hildenbrand     case CPU_STATE_OPERATING:
10874b4c74dSDavid Hildenbrand         cpu->env.sigp_order = SIGP_STOP_STORE_STATUS;
10974b4c74dSDavid Hildenbrand         cpu_inject_stop(cpu);
1103047f8b5SDavid Hildenbrand         /* store will be performed in do_stop_interrup() */
11174b4c74dSDavid Hildenbrand         break;
11274b4c74dSDavid Hildenbrand     case CPU_STATE_STOPPED:
11374b4c74dSDavid Hildenbrand         /* already stopped, just store the status */
11474b4c74dSDavid Hildenbrand         cpu_synchronize_state(cs);
11574b4c74dSDavid Hildenbrand         s390_store_status(cpu, S390_STORE_STATUS_DEF_ADDR, true);
11674b4c74dSDavid Hildenbrand         break;
11774b4c74dSDavid Hildenbrand     }
11874b4c74dSDavid Hildenbrand     si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
11974b4c74dSDavid Hildenbrand }
12074b4c74dSDavid Hildenbrand 
12174b4c74dSDavid Hildenbrand static void sigp_store_status_at_address(CPUState *cs, run_on_cpu_data arg)
12274b4c74dSDavid Hildenbrand {
12374b4c74dSDavid Hildenbrand     S390CPU *cpu = S390_CPU(cs);
12474b4c74dSDavid Hildenbrand     SigpInfo *si = arg.host_ptr;
12574b4c74dSDavid Hildenbrand     uint32_t address = si->param & 0x7ffffe00u;
12674b4c74dSDavid Hildenbrand 
12774b4c74dSDavid Hildenbrand     /* cpu has to be stopped */
12874b4c74dSDavid Hildenbrand     if (s390_cpu_get_state(cpu) != CPU_STATE_STOPPED) {
12974b4c74dSDavid Hildenbrand         set_sigp_status(si, SIGP_STAT_INCORRECT_STATE);
13074b4c74dSDavid Hildenbrand         return;
13174b4c74dSDavid Hildenbrand     }
13274b4c74dSDavid Hildenbrand 
13374b4c74dSDavid Hildenbrand     cpu_synchronize_state(cs);
13474b4c74dSDavid Hildenbrand 
13574b4c74dSDavid Hildenbrand     if (s390_store_status(cpu, address, false)) {
13674b4c74dSDavid Hildenbrand         set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER);
13774b4c74dSDavid Hildenbrand         return;
13874b4c74dSDavid Hildenbrand     }
13974b4c74dSDavid Hildenbrand     si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
14074b4c74dSDavid Hildenbrand }
14174b4c74dSDavid Hildenbrand 
14274b4c74dSDavid Hildenbrand #define ADTL_SAVE_LC_MASK  0xfUL
14374b4c74dSDavid Hildenbrand static void sigp_store_adtl_status(CPUState *cs, run_on_cpu_data arg)
14474b4c74dSDavid Hildenbrand {
14574b4c74dSDavid Hildenbrand     S390CPU *cpu = S390_CPU(cs);
14674b4c74dSDavid Hildenbrand     SigpInfo *si = arg.host_ptr;
14774b4c74dSDavid Hildenbrand     uint8_t lc = si->param & ADTL_SAVE_LC_MASK;
14874b4c74dSDavid Hildenbrand     hwaddr addr = si->param & ~ADTL_SAVE_LC_MASK;
14974b4c74dSDavid Hildenbrand     hwaddr len = 1UL << (lc ? lc : 10);
15074b4c74dSDavid Hildenbrand 
15174b4c74dSDavid Hildenbrand     if (!s390_has_feat(S390_FEAT_VECTOR) &&
15274b4c74dSDavid Hildenbrand         !s390_has_feat(S390_FEAT_GUARDED_STORAGE)) {
15374b4c74dSDavid Hildenbrand         set_sigp_status(si, SIGP_STAT_INVALID_ORDER);
15474b4c74dSDavid Hildenbrand         return;
15574b4c74dSDavid Hildenbrand     }
15674b4c74dSDavid Hildenbrand 
15774b4c74dSDavid Hildenbrand     /* cpu has to be stopped */
15874b4c74dSDavid Hildenbrand     if (s390_cpu_get_state(cpu) != CPU_STATE_STOPPED) {
15974b4c74dSDavid Hildenbrand         set_sigp_status(si, SIGP_STAT_INCORRECT_STATE);
16074b4c74dSDavid Hildenbrand         return;
16174b4c74dSDavid Hildenbrand     }
16274b4c74dSDavid Hildenbrand 
16374b4c74dSDavid Hildenbrand     /* address must be aligned to length */
16474b4c74dSDavid Hildenbrand     if (addr & (len - 1)) {
16574b4c74dSDavid Hildenbrand         set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER);
16674b4c74dSDavid Hildenbrand         return;
16774b4c74dSDavid Hildenbrand     }
16874b4c74dSDavid Hildenbrand 
16974b4c74dSDavid Hildenbrand     /* no GS: only lc == 0 is valid */
17074b4c74dSDavid Hildenbrand     if (!s390_has_feat(S390_FEAT_GUARDED_STORAGE) &&
17174b4c74dSDavid Hildenbrand         lc != 0) {
17274b4c74dSDavid Hildenbrand         set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER);
17374b4c74dSDavid Hildenbrand         return;
17474b4c74dSDavid Hildenbrand     }
17574b4c74dSDavid Hildenbrand 
17674b4c74dSDavid Hildenbrand     /* GS: 0, 10, 11, 12 are valid */
17774b4c74dSDavid Hildenbrand     if (s390_has_feat(S390_FEAT_GUARDED_STORAGE) &&
17874b4c74dSDavid Hildenbrand         lc != 0 &&
17974b4c74dSDavid Hildenbrand         lc != 10 &&
18074b4c74dSDavid Hildenbrand         lc != 11 &&
18174b4c74dSDavid Hildenbrand         lc != 12) {
18274b4c74dSDavid Hildenbrand         set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER);
18374b4c74dSDavid Hildenbrand         return;
18474b4c74dSDavid Hildenbrand     }
18574b4c74dSDavid Hildenbrand 
18674b4c74dSDavid Hildenbrand     cpu_synchronize_state(cs);
18774b4c74dSDavid Hildenbrand 
18874b4c74dSDavid Hildenbrand     if (s390_store_adtl_status(cpu, addr, len)) {
18974b4c74dSDavid Hildenbrand         set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER);
19074b4c74dSDavid Hildenbrand         return;
19174b4c74dSDavid Hildenbrand     }
19274b4c74dSDavid Hildenbrand     si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
19374b4c74dSDavid Hildenbrand }
19474b4c74dSDavid Hildenbrand 
19574b4c74dSDavid Hildenbrand static void sigp_restart(CPUState *cs, run_on_cpu_data arg)
19674b4c74dSDavid Hildenbrand {
19774b4c74dSDavid Hildenbrand     S390CPU *cpu = S390_CPU(cs);
19874b4c74dSDavid Hildenbrand     SigpInfo *si = arg.host_ptr;
19974b4c74dSDavid Hildenbrand 
20074b4c74dSDavid Hildenbrand     switch (s390_cpu_get_state(cpu)) {
20174b4c74dSDavid Hildenbrand     case CPU_STATE_STOPPED:
20274b4c74dSDavid Hildenbrand         /* the restart irq has to be delivered prior to any other pending irq */
20374b4c74dSDavid Hildenbrand         cpu_synchronize_state(cs);
20474b4c74dSDavid Hildenbrand         do_restart_interrupt(&cpu->env);
20574b4c74dSDavid Hildenbrand         s390_cpu_set_state(CPU_STATE_OPERATING, cpu);
20674b4c74dSDavid Hildenbrand         break;
20774b4c74dSDavid Hildenbrand     case CPU_STATE_OPERATING:
20874b4c74dSDavid Hildenbrand         cpu_inject_restart(cpu);
20974b4c74dSDavid Hildenbrand         break;
21074b4c74dSDavid Hildenbrand     }
21174b4c74dSDavid Hildenbrand     si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
21274b4c74dSDavid Hildenbrand }
21374b4c74dSDavid Hildenbrand 
21474b4c74dSDavid Hildenbrand static void sigp_initial_cpu_reset(CPUState *cs, run_on_cpu_data arg)
21574b4c74dSDavid Hildenbrand {
21674b4c74dSDavid Hildenbrand     S390CPU *cpu = S390_CPU(cs);
21774b4c74dSDavid Hildenbrand     S390CPUClass *scc = S390_CPU_GET_CLASS(cpu);
21874b4c74dSDavid Hildenbrand     SigpInfo *si = arg.host_ptr;
21974b4c74dSDavid Hildenbrand 
22074b4c74dSDavid Hildenbrand     cpu_synchronize_state(cs);
22174b4c74dSDavid Hildenbrand     scc->initial_cpu_reset(cs);
22274b4c74dSDavid Hildenbrand     cpu_synchronize_post_reset(cs);
22374b4c74dSDavid Hildenbrand     si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
22474b4c74dSDavid Hildenbrand }
22574b4c74dSDavid Hildenbrand 
22674b4c74dSDavid Hildenbrand static void sigp_cpu_reset(CPUState *cs, run_on_cpu_data arg)
22774b4c74dSDavid Hildenbrand {
22874b4c74dSDavid Hildenbrand     S390CPU *cpu = S390_CPU(cs);
22974b4c74dSDavid Hildenbrand     S390CPUClass *scc = S390_CPU_GET_CLASS(cpu);
23074b4c74dSDavid Hildenbrand     SigpInfo *si = arg.host_ptr;
23174b4c74dSDavid Hildenbrand 
23274b4c74dSDavid Hildenbrand     cpu_synchronize_state(cs);
23374b4c74dSDavid Hildenbrand     scc->cpu_reset(cs);
23474b4c74dSDavid Hildenbrand     cpu_synchronize_post_reset(cs);
23574b4c74dSDavid Hildenbrand     si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
23674b4c74dSDavid Hildenbrand }
23774b4c74dSDavid Hildenbrand 
23874b4c74dSDavid Hildenbrand static void sigp_set_prefix(CPUState *cs, run_on_cpu_data arg)
23974b4c74dSDavid Hildenbrand {
24074b4c74dSDavid Hildenbrand     S390CPU *cpu = S390_CPU(cs);
24174b4c74dSDavid Hildenbrand     SigpInfo *si = arg.host_ptr;
24274b4c74dSDavid Hildenbrand     uint32_t addr = si->param & 0x7fffe000u;
24374b4c74dSDavid Hildenbrand 
24474b4c74dSDavid Hildenbrand     cpu_synchronize_state(cs);
24574b4c74dSDavid Hildenbrand 
24674b4c74dSDavid Hildenbrand     if (!address_space_access_valid(&address_space_memory, addr,
24774b4c74dSDavid Hildenbrand                                     sizeof(struct LowCore), false)) {
24874b4c74dSDavid Hildenbrand         set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER);
24974b4c74dSDavid Hildenbrand         return;
25074b4c74dSDavid Hildenbrand     }
25174b4c74dSDavid Hildenbrand 
25274b4c74dSDavid Hildenbrand     /* cpu has to be stopped */
25374b4c74dSDavid Hildenbrand     if (s390_cpu_get_state(cpu) != CPU_STATE_STOPPED) {
25474b4c74dSDavid Hildenbrand         set_sigp_status(si, SIGP_STAT_INCORRECT_STATE);
25574b4c74dSDavid Hildenbrand         return;
25674b4c74dSDavid Hildenbrand     }
25774b4c74dSDavid Hildenbrand 
25874b4c74dSDavid Hildenbrand     cpu->env.psa = addr;
25974b4c74dSDavid Hildenbrand     cpu_synchronize_post_init(cs);
26074b4c74dSDavid Hildenbrand     si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
26174b4c74dSDavid Hildenbrand }
26274b4c74dSDavid Hildenbrand 
263d1b468bcSDavid Hildenbrand static void sigp_sense_running(S390CPU *dst_cpu, SigpInfo *si)
264d1b468bcSDavid Hildenbrand {
265d1b468bcSDavid Hildenbrand     if (!tcg_enabled()) {
266d1b468bcSDavid Hildenbrand         /* handled in KVM */
267d1b468bcSDavid Hildenbrand         set_sigp_status(si, SIGP_STAT_INVALID_ORDER);
268d1b468bcSDavid Hildenbrand         return;
269d1b468bcSDavid Hildenbrand     }
270d1b468bcSDavid Hildenbrand 
271d1b468bcSDavid Hildenbrand     /* sensing without locks is racy, but it's the same for real hw */
272d1b468bcSDavid Hildenbrand     if (!s390_has_feat(S390_FEAT_SENSE_RUNNING_STATUS)) {
273d1b468bcSDavid Hildenbrand         set_sigp_status(si, SIGP_STAT_INVALID_ORDER);
274d1b468bcSDavid Hildenbrand         return;
275d1b468bcSDavid Hildenbrand     }
276d1b468bcSDavid Hildenbrand 
277d1b468bcSDavid Hildenbrand     /* If halted (which includes also STOPPED), it is not running */
278d1b468bcSDavid Hildenbrand     if (CPU(dst_cpu)->halted) {
279d1b468bcSDavid Hildenbrand         si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
280d1b468bcSDavid Hildenbrand     } else {
281d1b468bcSDavid Hildenbrand         set_sigp_status(si, SIGP_STAT_NOT_RUNNING);
282d1b468bcSDavid Hildenbrand     }
283d1b468bcSDavid Hildenbrand }
284d1b468bcSDavid Hildenbrand 
28574b4c74dSDavid Hildenbrand static int handle_sigp_single_dst(S390CPU *dst_cpu, uint8_t order,
28674b4c74dSDavid Hildenbrand                                   uint64_t param, uint64_t *status_reg)
28774b4c74dSDavid Hildenbrand {
28874b4c74dSDavid Hildenbrand     SigpInfo si = {
28974b4c74dSDavid Hildenbrand         .param = param,
29074b4c74dSDavid Hildenbrand         .status_reg = status_reg,
29174b4c74dSDavid Hildenbrand     };
29274b4c74dSDavid Hildenbrand 
29374b4c74dSDavid Hildenbrand     /* cpu available? */
29474b4c74dSDavid Hildenbrand     if (dst_cpu == NULL) {
29574b4c74dSDavid Hildenbrand         return SIGP_CC_NOT_OPERATIONAL;
29674b4c74dSDavid Hildenbrand     }
29774b4c74dSDavid Hildenbrand 
29874b4c74dSDavid Hildenbrand     /* only resets can break pending orders */
29974b4c74dSDavid Hildenbrand     if (dst_cpu->env.sigp_order != 0 &&
30074b4c74dSDavid Hildenbrand         order != SIGP_CPU_RESET &&
30174b4c74dSDavid Hildenbrand         order != SIGP_INITIAL_CPU_RESET) {
30274b4c74dSDavid Hildenbrand         return SIGP_CC_BUSY;
30374b4c74dSDavid Hildenbrand     }
30474b4c74dSDavid Hildenbrand 
30574b4c74dSDavid Hildenbrand     switch (order) {
306*302230fcSDavid Hildenbrand     case SIGP_SENSE:
307*302230fcSDavid Hildenbrand         sigp_sense(dst_cpu, &si);
308*302230fcSDavid Hildenbrand         break;
30974b4c74dSDavid Hildenbrand     case SIGP_START:
31074b4c74dSDavid Hildenbrand         run_on_cpu(CPU(dst_cpu), sigp_start, RUN_ON_CPU_HOST_PTR(&si));
31174b4c74dSDavid Hildenbrand         break;
31274b4c74dSDavid Hildenbrand     case SIGP_STOP:
31374b4c74dSDavid Hildenbrand         run_on_cpu(CPU(dst_cpu), sigp_stop, RUN_ON_CPU_HOST_PTR(&si));
31474b4c74dSDavid Hildenbrand         break;
31574b4c74dSDavid Hildenbrand     case SIGP_RESTART:
31674b4c74dSDavid Hildenbrand         run_on_cpu(CPU(dst_cpu), sigp_restart, RUN_ON_CPU_HOST_PTR(&si));
31774b4c74dSDavid Hildenbrand         break;
31874b4c74dSDavid Hildenbrand     case SIGP_STOP_STORE_STATUS:
31974b4c74dSDavid Hildenbrand         run_on_cpu(CPU(dst_cpu), sigp_stop_and_store_status, RUN_ON_CPU_HOST_PTR(&si));
32074b4c74dSDavid Hildenbrand         break;
32174b4c74dSDavid Hildenbrand     case SIGP_STORE_STATUS_ADDR:
32274b4c74dSDavid Hildenbrand         run_on_cpu(CPU(dst_cpu), sigp_store_status_at_address, RUN_ON_CPU_HOST_PTR(&si));
32374b4c74dSDavid Hildenbrand         break;
32474b4c74dSDavid Hildenbrand     case SIGP_STORE_ADTL_STATUS:
32574b4c74dSDavid Hildenbrand         run_on_cpu(CPU(dst_cpu), sigp_store_adtl_status, RUN_ON_CPU_HOST_PTR(&si));
32674b4c74dSDavid Hildenbrand         break;
32774b4c74dSDavid Hildenbrand     case SIGP_SET_PREFIX:
32874b4c74dSDavid Hildenbrand         run_on_cpu(CPU(dst_cpu), sigp_set_prefix, RUN_ON_CPU_HOST_PTR(&si));
32974b4c74dSDavid Hildenbrand         break;
33074b4c74dSDavid Hildenbrand     case SIGP_INITIAL_CPU_RESET:
33174b4c74dSDavid Hildenbrand         run_on_cpu(CPU(dst_cpu), sigp_initial_cpu_reset, RUN_ON_CPU_HOST_PTR(&si));
33274b4c74dSDavid Hildenbrand         break;
33374b4c74dSDavid Hildenbrand     case SIGP_CPU_RESET:
33474b4c74dSDavid Hildenbrand         run_on_cpu(CPU(dst_cpu), sigp_cpu_reset, RUN_ON_CPU_HOST_PTR(&si));
33574b4c74dSDavid Hildenbrand         break;
336d1b468bcSDavid Hildenbrand     case SIGP_SENSE_RUNNING:
337d1b468bcSDavid Hildenbrand         sigp_sense_running(dst_cpu, &si);
338d1b468bcSDavid Hildenbrand         break;
33974b4c74dSDavid Hildenbrand     default:
34074b4c74dSDavid Hildenbrand         set_sigp_status(&si, SIGP_STAT_INVALID_ORDER);
34174b4c74dSDavid Hildenbrand     }
34274b4c74dSDavid Hildenbrand 
34374b4c74dSDavid Hildenbrand     return si.cc;
34474b4c74dSDavid Hildenbrand }
34574b4c74dSDavid Hildenbrand 
34674b4c74dSDavid Hildenbrand static int sigp_set_architecture(S390CPU *cpu, uint32_t param,
34774b4c74dSDavid Hildenbrand                                  uint64_t *status_reg)
34874b4c74dSDavid Hildenbrand {
34974b4c74dSDavid Hildenbrand     CPUState *cur_cs;
35074b4c74dSDavid Hildenbrand     S390CPU *cur_cpu;
35174b4c74dSDavid Hildenbrand     bool all_stopped = true;
35274b4c74dSDavid Hildenbrand 
35374b4c74dSDavid Hildenbrand     CPU_FOREACH(cur_cs) {
35474b4c74dSDavid Hildenbrand         cur_cpu = S390_CPU(cur_cs);
35574b4c74dSDavid Hildenbrand 
35674b4c74dSDavid Hildenbrand         if (cur_cpu == cpu) {
35774b4c74dSDavid Hildenbrand             continue;
35874b4c74dSDavid Hildenbrand         }
35974b4c74dSDavid Hildenbrand         if (s390_cpu_get_state(cur_cpu) != CPU_STATE_STOPPED) {
36074b4c74dSDavid Hildenbrand             all_stopped = false;
36174b4c74dSDavid Hildenbrand         }
36274b4c74dSDavid Hildenbrand     }
36374b4c74dSDavid Hildenbrand 
36474b4c74dSDavid Hildenbrand     *status_reg &= 0xffffffff00000000ULL;
36574b4c74dSDavid Hildenbrand 
36674b4c74dSDavid Hildenbrand     /* Reject set arch order, with czam we're always in z/Arch mode. */
36774b4c74dSDavid Hildenbrand     *status_reg |= (all_stopped ? SIGP_STAT_INVALID_PARAMETER :
36874b4c74dSDavid Hildenbrand                     SIGP_STAT_INCORRECT_STATE);
36974b4c74dSDavid Hildenbrand     return SIGP_CC_STATUS_STORED;
37074b4c74dSDavid Hildenbrand }
37174b4c74dSDavid Hildenbrand 
37274b4c74dSDavid Hildenbrand int handle_sigp(CPUS390XState *env, uint8_t order, uint64_t r1, uint64_t r3)
37374b4c74dSDavid Hildenbrand {
37474b4c74dSDavid Hildenbrand     uint64_t *status_reg = &env->regs[r1];
37574b4c74dSDavid Hildenbrand     uint64_t param = (r1 % 2) ? env->regs[r1] : env->regs[r1 + 1];
37674b4c74dSDavid Hildenbrand     S390CPU *cpu = s390_env_get_cpu(env);
37774b4c74dSDavid Hildenbrand     S390CPU *dst_cpu = NULL;
37874b4c74dSDavid Hildenbrand     int ret;
37974b4c74dSDavid Hildenbrand 
38074b4c74dSDavid Hildenbrand     if (qemu_mutex_trylock(&qemu_sigp_mutex)) {
38174b4c74dSDavid Hildenbrand         ret = SIGP_CC_BUSY;
38274b4c74dSDavid Hildenbrand         goto out;
38374b4c74dSDavid Hildenbrand     }
38474b4c74dSDavid Hildenbrand 
38574b4c74dSDavid Hildenbrand     switch (order) {
38674b4c74dSDavid Hildenbrand     case SIGP_SET_ARCH:
38774b4c74dSDavid Hildenbrand         ret = sigp_set_architecture(cpu, param, status_reg);
38874b4c74dSDavid Hildenbrand         break;
38974b4c74dSDavid Hildenbrand     default:
39074b4c74dSDavid Hildenbrand         /* all other sigp orders target a single vcpu */
39174b4c74dSDavid Hildenbrand         dst_cpu = s390_cpu_addr2state(env->regs[r3]);
39274b4c74dSDavid Hildenbrand         ret = handle_sigp_single_dst(dst_cpu, order, param, status_reg);
39374b4c74dSDavid Hildenbrand     }
39474b4c74dSDavid Hildenbrand     qemu_mutex_unlock(&qemu_sigp_mutex);
39574b4c74dSDavid Hildenbrand 
39674b4c74dSDavid Hildenbrand out:
39774b4c74dSDavid Hildenbrand     trace_sigp_finished(order, CPU(cpu)->cpu_index,
39874b4c74dSDavid Hildenbrand                         dst_cpu ? CPU(dst_cpu)->cpu_index : -1, ret);
39974b4c74dSDavid Hildenbrand     g_assert(ret >= 0);
40074b4c74dSDavid Hildenbrand 
40174b4c74dSDavid Hildenbrand     return ret;
40274b4c74dSDavid Hildenbrand }
40374b4c74dSDavid Hildenbrand 
40474b4c74dSDavid Hildenbrand int s390_cpu_restart(S390CPU *cpu)
40574b4c74dSDavid Hildenbrand {
40674b4c74dSDavid Hildenbrand     SigpInfo si = {};
40774b4c74dSDavid Hildenbrand 
40874b4c74dSDavid Hildenbrand     if (tcg_enabled()) {
40974b4c74dSDavid Hildenbrand         /* FIXME TCG */
41074b4c74dSDavid Hildenbrand         return -ENOSYS;
41174b4c74dSDavid Hildenbrand     }
41274b4c74dSDavid Hildenbrand 
41374b4c74dSDavid Hildenbrand     run_on_cpu(CPU(cpu), sigp_restart, RUN_ON_CPU_HOST_PTR(&si));
41474b4c74dSDavid Hildenbrand     return 0;
41574b4c74dSDavid Hildenbrand }
41674b4c74dSDavid Hildenbrand 
4173047f8b5SDavid Hildenbrand void do_stop_interrupt(CPUS390XState *env)
4183047f8b5SDavid Hildenbrand {
4193047f8b5SDavid Hildenbrand     S390CPU *cpu = s390_env_get_cpu(env);
4203047f8b5SDavid Hildenbrand 
4213047f8b5SDavid Hildenbrand     if (s390_cpu_set_state(CPU_STATE_STOPPED, cpu) == 0) {
4223047f8b5SDavid Hildenbrand         qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
4233047f8b5SDavid Hildenbrand     }
4243047f8b5SDavid Hildenbrand     if (cpu->env.sigp_order == SIGP_STOP_STORE_STATUS) {
4253047f8b5SDavid Hildenbrand         s390_store_status(cpu, S390_STORE_STATUS_DEF_ADDR, true);
4263047f8b5SDavid Hildenbrand     }
4273047f8b5SDavid Hildenbrand     env->sigp_order = 0;
4283047f8b5SDavid Hildenbrand }
4293047f8b5SDavid Hildenbrand 
43074b4c74dSDavid Hildenbrand void s390_init_sigp(void)
43174b4c74dSDavid Hildenbrand {
43274b4c74dSDavid Hildenbrand     qemu_mutex_init(&qemu_sigp_mutex);
43374b4c74dSDavid Hildenbrand }
434