xref: /qemu/target/s390x/sigp.c (revision e2b2a8649bcd4769f453497b2abffbe44c7f86ad)
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 "cpu.h"
1374b4c74dSDavid Hildenbrand #include "internal.h"
1474b4c74dSDavid Hildenbrand #include "sysemu/hw_accel.h"
1554d31236SMarkus Armbruster #include "sysemu/runstate.h"
1674b4c74dSDavid Hildenbrand #include "exec/address-spaces.h"
17b1ab5f60SDavid Hildenbrand #include "exec/exec-all.h"
1814a48c1dSMarkus Armbruster #include "sysemu/tcg.h"
1974b4c74dSDavid Hildenbrand #include "trace.h"
208ac25c84SMarkus Armbruster #include "qapi/qapi-types-machine.h"
2174b4c74dSDavid Hildenbrand 
2274b4c74dSDavid Hildenbrand QemuMutex qemu_sigp_mutex;
2374b4c74dSDavid Hildenbrand 
2474b4c74dSDavid Hildenbrand typedef struct SigpInfo {
2574b4c74dSDavid Hildenbrand     uint64_t param;
2674b4c74dSDavid Hildenbrand     int cc;
2774b4c74dSDavid Hildenbrand     uint64_t *status_reg;
2874b4c74dSDavid Hildenbrand } SigpInfo;
2974b4c74dSDavid Hildenbrand 
3074b4c74dSDavid Hildenbrand static void set_sigp_status(SigpInfo *si, uint64_t status)
3174b4c74dSDavid Hildenbrand {
3274b4c74dSDavid Hildenbrand     *si->status_reg &= 0xffffffff00000000ULL;
3374b4c74dSDavid Hildenbrand     *si->status_reg |= status;
3474b4c74dSDavid Hildenbrand     si->cc = SIGP_CC_STATUS_STORED;
3574b4c74dSDavid Hildenbrand }
3674b4c74dSDavid Hildenbrand 
37302230fcSDavid Hildenbrand static void sigp_sense(S390CPU *dst_cpu, SigpInfo *si)
38302230fcSDavid Hildenbrand {
39302230fcSDavid Hildenbrand     uint8_t state = s390_cpu_get_state(dst_cpu);
40302230fcSDavid Hildenbrand     bool ext_call = dst_cpu->env.pending_int & INTERRUPT_EXTERNAL_CALL;
41302230fcSDavid Hildenbrand     uint64_t status = 0;
42302230fcSDavid Hildenbrand 
43302230fcSDavid Hildenbrand     if (!tcg_enabled()) {
44302230fcSDavid Hildenbrand         /* handled in KVM */
45302230fcSDavid Hildenbrand         set_sigp_status(si, SIGP_STAT_INVALID_ORDER);
46302230fcSDavid Hildenbrand         return;
47302230fcSDavid Hildenbrand     }
48302230fcSDavid Hildenbrand 
49302230fcSDavid Hildenbrand     /* sensing without locks is racy, but it's the same for real hw */
509d0306dfSViktor Mihajlovski     if (state != S390_CPU_STATE_STOPPED && !ext_call) {
51302230fcSDavid Hildenbrand         si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
52302230fcSDavid Hildenbrand     } else {
53302230fcSDavid Hildenbrand         if (ext_call) {
54302230fcSDavid Hildenbrand             status |= SIGP_STAT_EXT_CALL_PENDING;
55302230fcSDavid Hildenbrand         }
569d0306dfSViktor Mihajlovski         if (state == S390_CPU_STATE_STOPPED) {
57302230fcSDavid Hildenbrand             status |= SIGP_STAT_STOPPED;
58302230fcSDavid Hildenbrand         }
59302230fcSDavid Hildenbrand         set_sigp_status(si, status);
60302230fcSDavid Hildenbrand     }
61302230fcSDavid Hildenbrand }
62302230fcSDavid Hildenbrand 
63070aa1a4SDavid Hildenbrand static void sigp_external_call(S390CPU *src_cpu, S390CPU *dst_cpu, SigpInfo *si)
64070aa1a4SDavid Hildenbrand {
65070aa1a4SDavid Hildenbrand     int ret;
66070aa1a4SDavid Hildenbrand 
67070aa1a4SDavid Hildenbrand     if (!tcg_enabled()) {
68070aa1a4SDavid Hildenbrand         /* handled in KVM */
69070aa1a4SDavid Hildenbrand         set_sigp_status(si, SIGP_STAT_INVALID_ORDER);
70070aa1a4SDavid Hildenbrand         return;
71070aa1a4SDavid Hildenbrand     }
72070aa1a4SDavid Hildenbrand 
73070aa1a4SDavid Hildenbrand     ret = cpu_inject_external_call(dst_cpu, src_cpu->env.core_id);
74070aa1a4SDavid Hildenbrand     if (!ret) {
75070aa1a4SDavid Hildenbrand         si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
76070aa1a4SDavid Hildenbrand     } else {
77070aa1a4SDavid Hildenbrand         set_sigp_status(si, SIGP_STAT_EXT_CALL_PENDING);
78070aa1a4SDavid Hildenbrand     }
79070aa1a4SDavid Hildenbrand }
80070aa1a4SDavid Hildenbrand 
81c50105d4SDavid Hildenbrand static void sigp_emergency(S390CPU *src_cpu, S390CPU *dst_cpu, SigpInfo *si)
82c50105d4SDavid Hildenbrand {
83c50105d4SDavid Hildenbrand     if (!tcg_enabled()) {
84c50105d4SDavid Hildenbrand         /* handled in KVM */
85c50105d4SDavid Hildenbrand         set_sigp_status(si, SIGP_STAT_INVALID_ORDER);
86c50105d4SDavid Hildenbrand         return;
87c50105d4SDavid Hildenbrand     }
88c50105d4SDavid Hildenbrand 
89c50105d4SDavid Hildenbrand     cpu_inject_emergency_signal(dst_cpu, src_cpu->env.core_id);
90c50105d4SDavid Hildenbrand     si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
91c50105d4SDavid Hildenbrand }
92c50105d4SDavid Hildenbrand 
9374b4c74dSDavid Hildenbrand static void sigp_start(CPUState *cs, run_on_cpu_data arg)
9474b4c74dSDavid Hildenbrand {
9574b4c74dSDavid Hildenbrand     S390CPU *cpu = S390_CPU(cs);
9674b4c74dSDavid Hildenbrand     SigpInfo *si = arg.host_ptr;
9774b4c74dSDavid Hildenbrand 
989d0306dfSViktor Mihajlovski     if (s390_cpu_get_state(cpu) != S390_CPU_STATE_STOPPED) {
9974b4c74dSDavid Hildenbrand         si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
10074b4c74dSDavid Hildenbrand         return;
10174b4c74dSDavid Hildenbrand     }
10274b4c74dSDavid Hildenbrand 
1039d0306dfSViktor Mihajlovski     s390_cpu_set_state(S390_CPU_STATE_OPERATING, cpu);
10474b4c74dSDavid Hildenbrand     si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
10574b4c74dSDavid Hildenbrand }
10674b4c74dSDavid Hildenbrand 
10774b4c74dSDavid Hildenbrand static void sigp_stop(CPUState *cs, run_on_cpu_data arg)
10874b4c74dSDavid Hildenbrand {
10974b4c74dSDavid Hildenbrand     S390CPU *cpu = S390_CPU(cs);
11074b4c74dSDavid Hildenbrand     SigpInfo *si = arg.host_ptr;
11174b4c74dSDavid Hildenbrand 
1129d0306dfSViktor Mihajlovski     if (s390_cpu_get_state(cpu) != S390_CPU_STATE_OPERATING) {
11374b4c74dSDavid Hildenbrand         si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
11474b4c74dSDavid Hildenbrand         return;
11574b4c74dSDavid Hildenbrand     }
11674b4c74dSDavid Hildenbrand 
11774b4c74dSDavid Hildenbrand     /* disabled wait - sleeping in user space */
11874b4c74dSDavid Hildenbrand     if (cs->halted) {
1199d0306dfSViktor Mihajlovski         s390_cpu_set_state(S390_CPU_STATE_STOPPED, cpu);
12074b4c74dSDavid Hildenbrand     } else {
12174b4c74dSDavid Hildenbrand         /* execute the stop function */
12274b4c74dSDavid Hildenbrand         cpu->env.sigp_order = SIGP_STOP;
12374b4c74dSDavid Hildenbrand         cpu_inject_stop(cpu);
12474b4c74dSDavid Hildenbrand     }
12574b4c74dSDavid Hildenbrand     si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
12674b4c74dSDavid Hildenbrand }
12774b4c74dSDavid Hildenbrand 
12874b4c74dSDavid Hildenbrand static void sigp_stop_and_store_status(CPUState *cs, run_on_cpu_data arg)
12974b4c74dSDavid Hildenbrand {
13074b4c74dSDavid Hildenbrand     S390CPU *cpu = S390_CPU(cs);
13174b4c74dSDavid Hildenbrand     SigpInfo *si = arg.host_ptr;
13274b4c74dSDavid Hildenbrand 
13374b4c74dSDavid Hildenbrand     /* disabled wait - sleeping in user space */
1349d0306dfSViktor Mihajlovski     if (s390_cpu_get_state(cpu) == S390_CPU_STATE_OPERATING && cs->halted) {
1359d0306dfSViktor Mihajlovski         s390_cpu_set_state(S390_CPU_STATE_STOPPED, cpu);
13674b4c74dSDavid Hildenbrand     }
13774b4c74dSDavid Hildenbrand 
13874b4c74dSDavid Hildenbrand     switch (s390_cpu_get_state(cpu)) {
1399d0306dfSViktor Mihajlovski     case S390_CPU_STATE_OPERATING:
14074b4c74dSDavid Hildenbrand         cpu->env.sigp_order = SIGP_STOP_STORE_STATUS;
14174b4c74dSDavid Hildenbrand         cpu_inject_stop(cpu);
1423047f8b5SDavid Hildenbrand         /* store will be performed in do_stop_interrup() */
14374b4c74dSDavid Hildenbrand         break;
1449d0306dfSViktor Mihajlovski     case S390_CPU_STATE_STOPPED:
14574b4c74dSDavid Hildenbrand         /* already stopped, just store the status */
14674b4c74dSDavid Hildenbrand         cpu_synchronize_state(cs);
14774b4c74dSDavid Hildenbrand         s390_store_status(cpu, S390_STORE_STATUS_DEF_ADDR, true);
14874b4c74dSDavid Hildenbrand         break;
14974b4c74dSDavid Hildenbrand     }
15074b4c74dSDavid Hildenbrand     si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
15174b4c74dSDavid Hildenbrand }
15274b4c74dSDavid Hildenbrand 
15374b4c74dSDavid Hildenbrand static void sigp_store_status_at_address(CPUState *cs, run_on_cpu_data arg)
15474b4c74dSDavid Hildenbrand {
15574b4c74dSDavid Hildenbrand     S390CPU *cpu = S390_CPU(cs);
15674b4c74dSDavid Hildenbrand     SigpInfo *si = arg.host_ptr;
15774b4c74dSDavid Hildenbrand     uint32_t address = si->param & 0x7ffffe00u;
15874b4c74dSDavid Hildenbrand 
15974b4c74dSDavid Hildenbrand     /* cpu has to be stopped */
1609d0306dfSViktor Mihajlovski     if (s390_cpu_get_state(cpu) != S390_CPU_STATE_STOPPED) {
16174b4c74dSDavid Hildenbrand         set_sigp_status(si, SIGP_STAT_INCORRECT_STATE);
16274b4c74dSDavid Hildenbrand         return;
16374b4c74dSDavid Hildenbrand     }
16474b4c74dSDavid Hildenbrand 
16574b4c74dSDavid Hildenbrand     cpu_synchronize_state(cs);
16674b4c74dSDavid Hildenbrand 
16774b4c74dSDavid Hildenbrand     if (s390_store_status(cpu, address, false)) {
16874b4c74dSDavid Hildenbrand         set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER);
16974b4c74dSDavid Hildenbrand         return;
17074b4c74dSDavid Hildenbrand     }
17174b4c74dSDavid Hildenbrand     si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
17274b4c74dSDavid Hildenbrand }
17374b4c74dSDavid Hildenbrand 
17474b4c74dSDavid Hildenbrand #define ADTL_SAVE_LC_MASK  0xfUL
17574b4c74dSDavid Hildenbrand static void sigp_store_adtl_status(CPUState *cs, run_on_cpu_data arg)
17674b4c74dSDavid Hildenbrand {
17774b4c74dSDavid Hildenbrand     S390CPU *cpu = S390_CPU(cs);
17874b4c74dSDavid Hildenbrand     SigpInfo *si = arg.host_ptr;
17974b4c74dSDavid Hildenbrand     uint8_t lc = si->param & ADTL_SAVE_LC_MASK;
18074b4c74dSDavid Hildenbrand     hwaddr addr = si->param & ~ADTL_SAVE_LC_MASK;
18174b4c74dSDavid Hildenbrand     hwaddr len = 1UL << (lc ? lc : 10);
18274b4c74dSDavid Hildenbrand 
18374b4c74dSDavid Hildenbrand     if (!s390_has_feat(S390_FEAT_VECTOR) &&
18474b4c74dSDavid Hildenbrand         !s390_has_feat(S390_FEAT_GUARDED_STORAGE)) {
18574b4c74dSDavid Hildenbrand         set_sigp_status(si, SIGP_STAT_INVALID_ORDER);
18674b4c74dSDavid Hildenbrand         return;
18774b4c74dSDavid Hildenbrand     }
18874b4c74dSDavid Hildenbrand 
18974b4c74dSDavid Hildenbrand     /* cpu has to be stopped */
1909d0306dfSViktor Mihajlovski     if (s390_cpu_get_state(cpu) != S390_CPU_STATE_STOPPED) {
19174b4c74dSDavid Hildenbrand         set_sigp_status(si, SIGP_STAT_INCORRECT_STATE);
19274b4c74dSDavid Hildenbrand         return;
19374b4c74dSDavid Hildenbrand     }
19474b4c74dSDavid Hildenbrand 
19574b4c74dSDavid Hildenbrand     /* address must be aligned to length */
19674b4c74dSDavid Hildenbrand     if (addr & (len - 1)) {
19774b4c74dSDavid Hildenbrand         set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER);
19874b4c74dSDavid Hildenbrand         return;
19974b4c74dSDavid Hildenbrand     }
20074b4c74dSDavid Hildenbrand 
20174b4c74dSDavid Hildenbrand     /* no GS: only lc == 0 is valid */
20274b4c74dSDavid Hildenbrand     if (!s390_has_feat(S390_FEAT_GUARDED_STORAGE) &&
20374b4c74dSDavid Hildenbrand         lc != 0) {
20474b4c74dSDavid Hildenbrand         set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER);
20574b4c74dSDavid Hildenbrand         return;
20674b4c74dSDavid Hildenbrand     }
20774b4c74dSDavid Hildenbrand 
20874b4c74dSDavid Hildenbrand     /* GS: 0, 10, 11, 12 are valid */
20974b4c74dSDavid Hildenbrand     if (s390_has_feat(S390_FEAT_GUARDED_STORAGE) &&
21074b4c74dSDavid Hildenbrand         lc != 0 &&
21174b4c74dSDavid Hildenbrand         lc != 10 &&
21274b4c74dSDavid Hildenbrand         lc != 11 &&
21374b4c74dSDavid Hildenbrand         lc != 12) {
21474b4c74dSDavid Hildenbrand         set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER);
21574b4c74dSDavid Hildenbrand         return;
21674b4c74dSDavid Hildenbrand     }
21774b4c74dSDavid Hildenbrand 
21874b4c74dSDavid Hildenbrand     cpu_synchronize_state(cs);
21974b4c74dSDavid Hildenbrand 
22074b4c74dSDavid Hildenbrand     if (s390_store_adtl_status(cpu, addr, len)) {
22174b4c74dSDavid Hildenbrand         set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER);
22274b4c74dSDavid Hildenbrand         return;
22374b4c74dSDavid Hildenbrand     }
22474b4c74dSDavid Hildenbrand     si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
22574b4c74dSDavid Hildenbrand }
22674b4c74dSDavid Hildenbrand 
22774b4c74dSDavid Hildenbrand static void sigp_restart(CPUState *cs, run_on_cpu_data arg)
22874b4c74dSDavid Hildenbrand {
22974b4c74dSDavid Hildenbrand     S390CPU *cpu = S390_CPU(cs);
23074b4c74dSDavid Hildenbrand     SigpInfo *si = arg.host_ptr;
23174b4c74dSDavid Hildenbrand 
23274b4c74dSDavid Hildenbrand     switch (s390_cpu_get_state(cpu)) {
2339d0306dfSViktor Mihajlovski     case S390_CPU_STATE_STOPPED:
23474b4c74dSDavid Hildenbrand         /* the restart irq has to be delivered prior to any other pending irq */
23574b4c74dSDavid Hildenbrand         cpu_synchronize_state(cs);
236741a4ec1SDavid Hildenbrand         /*
237741a4ec1SDavid Hildenbrand          * Set OPERATING (and unhalting) before loading the restart PSW.
238*e2b2a864SRichard Henderson          * s390_cpu_set_psw() will then properly halt the CPU again if
239*e2b2a864SRichard Henderson          * necessary (TCG).
240741a4ec1SDavid Hildenbrand          */
2419d0306dfSViktor Mihajlovski         s390_cpu_set_state(S390_CPU_STATE_OPERATING, cpu);
242741a4ec1SDavid Hildenbrand         do_restart_interrupt(&cpu->env);
24374b4c74dSDavid Hildenbrand         break;
2449d0306dfSViktor Mihajlovski     case S390_CPU_STATE_OPERATING:
24574b4c74dSDavid Hildenbrand         cpu_inject_restart(cpu);
24674b4c74dSDavid Hildenbrand         break;
24774b4c74dSDavid Hildenbrand     }
24874b4c74dSDavid Hildenbrand     si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
24974b4c74dSDavid Hildenbrand }
25074b4c74dSDavid Hildenbrand 
25174b4c74dSDavid Hildenbrand static void sigp_initial_cpu_reset(CPUState *cs, run_on_cpu_data arg)
25274b4c74dSDavid Hildenbrand {
25374b4c74dSDavid Hildenbrand     S390CPU *cpu = S390_CPU(cs);
25474b4c74dSDavid Hildenbrand     S390CPUClass *scc = S390_CPU_GET_CLASS(cpu);
25574b4c74dSDavid Hildenbrand     SigpInfo *si = arg.host_ptr;
25674b4c74dSDavid Hildenbrand 
25774b4c74dSDavid Hildenbrand     cpu_synchronize_state(cs);
25881b92223SJanosch Frank     scc->reset(cs, S390_CPU_RESET_INITIAL);
25974b4c74dSDavid Hildenbrand     cpu_synchronize_post_reset(cs);
26074b4c74dSDavid Hildenbrand     si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
26174b4c74dSDavid Hildenbrand }
26274b4c74dSDavid Hildenbrand 
26374b4c74dSDavid Hildenbrand static void sigp_cpu_reset(CPUState *cs, run_on_cpu_data arg)
26474b4c74dSDavid Hildenbrand {
26574b4c74dSDavid Hildenbrand     S390CPU *cpu = S390_CPU(cs);
26674b4c74dSDavid Hildenbrand     S390CPUClass *scc = S390_CPU_GET_CLASS(cpu);
26774b4c74dSDavid Hildenbrand     SigpInfo *si = arg.host_ptr;
26874b4c74dSDavid Hildenbrand 
26974b4c74dSDavid Hildenbrand     cpu_synchronize_state(cs);
270eac4f827SJanosch Frank     scc->reset(cs, S390_CPU_RESET_NORMAL);
27174b4c74dSDavid Hildenbrand     cpu_synchronize_post_reset(cs);
27274b4c74dSDavid Hildenbrand     si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
27374b4c74dSDavid Hildenbrand }
27474b4c74dSDavid Hildenbrand 
27574b4c74dSDavid Hildenbrand static void sigp_set_prefix(CPUState *cs, run_on_cpu_data arg)
27674b4c74dSDavid Hildenbrand {
27774b4c74dSDavid Hildenbrand     S390CPU *cpu = S390_CPU(cs);
27874b4c74dSDavid Hildenbrand     SigpInfo *si = arg.host_ptr;
27974b4c74dSDavid Hildenbrand     uint32_t addr = si->param & 0x7fffe000u;
28074b4c74dSDavid Hildenbrand 
28174b4c74dSDavid Hildenbrand     cpu_synchronize_state(cs);
28274b4c74dSDavid Hildenbrand 
28374b4c74dSDavid Hildenbrand     if (!address_space_access_valid(&address_space_memory, addr,
284fddffa42SPeter Maydell                                     sizeof(struct LowCore), false,
285fddffa42SPeter Maydell                                     MEMTXATTRS_UNSPECIFIED)) {
28674b4c74dSDavid Hildenbrand         set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER);
28774b4c74dSDavid Hildenbrand         return;
28874b4c74dSDavid Hildenbrand     }
28974b4c74dSDavid Hildenbrand 
29074b4c74dSDavid Hildenbrand     /* cpu has to be stopped */
2919d0306dfSViktor Mihajlovski     if (s390_cpu_get_state(cpu) != S390_CPU_STATE_STOPPED) {
29274b4c74dSDavid Hildenbrand         set_sigp_status(si, SIGP_STAT_INCORRECT_STATE);
29374b4c74dSDavid Hildenbrand         return;
29474b4c74dSDavid Hildenbrand     }
29574b4c74dSDavid Hildenbrand 
29674b4c74dSDavid Hildenbrand     cpu->env.psa = addr;
297b376a554SDavid Hildenbrand     tlb_flush(cs);
29874b4c74dSDavid Hildenbrand     cpu_synchronize_post_init(cs);
29974b4c74dSDavid Hildenbrand     si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
30074b4c74dSDavid Hildenbrand }
30174b4c74dSDavid Hildenbrand 
302a6880d21SDavid Hildenbrand static void sigp_cond_emergency(S390CPU *src_cpu, S390CPU *dst_cpu,
303a6880d21SDavid Hildenbrand                                 SigpInfo *si)
304a6880d21SDavid Hildenbrand {
305a6880d21SDavid Hildenbrand     const uint64_t psw_int_mask = PSW_MASK_IO | PSW_MASK_EXT;
306a6880d21SDavid Hildenbrand     uint16_t p_asn, s_asn, asn;
307a6880d21SDavid Hildenbrand     uint64_t psw_addr, psw_mask;
308a6880d21SDavid Hildenbrand     bool idle;
309a6880d21SDavid Hildenbrand 
310a6880d21SDavid Hildenbrand     if (!tcg_enabled()) {
311a6880d21SDavid Hildenbrand         /* handled in KVM */
312a6880d21SDavid Hildenbrand         set_sigp_status(si, SIGP_STAT_INVALID_ORDER);
313a6880d21SDavid Hildenbrand         return;
314a6880d21SDavid Hildenbrand     }
315a6880d21SDavid Hildenbrand 
316a6880d21SDavid Hildenbrand     /* this looks racy, but these values are only used when STOPPED */
317a6880d21SDavid Hildenbrand     idle = CPU(dst_cpu)->halted;
318a6880d21SDavid Hildenbrand     psw_addr = dst_cpu->env.psw.addr;
319a6880d21SDavid Hildenbrand     psw_mask = dst_cpu->env.psw.mask;
320a6880d21SDavid Hildenbrand     asn = si->param;
321a6880d21SDavid Hildenbrand     p_asn = dst_cpu->env.cregs[4] & 0xffff;  /* Primary ASN */
322a6880d21SDavid Hildenbrand     s_asn = dst_cpu->env.cregs[3] & 0xffff;  /* Secondary ASN */
323a6880d21SDavid Hildenbrand 
3249d0306dfSViktor Mihajlovski     if (s390_cpu_get_state(dst_cpu) != S390_CPU_STATE_STOPPED ||
325a6880d21SDavid Hildenbrand         (psw_mask & psw_int_mask) != psw_int_mask ||
326a6880d21SDavid Hildenbrand         (idle && psw_addr != 0) ||
327a6880d21SDavid Hildenbrand         (!idle && (asn == p_asn || asn == s_asn))) {
328a6880d21SDavid Hildenbrand         cpu_inject_emergency_signal(dst_cpu, src_cpu->env.core_id);
329a6880d21SDavid Hildenbrand     } else {
330a6880d21SDavid Hildenbrand         set_sigp_status(si, SIGP_STAT_INCORRECT_STATE);
331a6880d21SDavid Hildenbrand     }
332a6880d21SDavid Hildenbrand 
333a6880d21SDavid Hildenbrand     si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
334a6880d21SDavid Hildenbrand }
335a6880d21SDavid Hildenbrand 
336d1b468bcSDavid Hildenbrand static void sigp_sense_running(S390CPU *dst_cpu, SigpInfo *si)
337d1b468bcSDavid Hildenbrand {
338d1b468bcSDavid Hildenbrand     if (!tcg_enabled()) {
339d1b468bcSDavid Hildenbrand         /* handled in KVM */
340d1b468bcSDavid Hildenbrand         set_sigp_status(si, SIGP_STAT_INVALID_ORDER);
341d1b468bcSDavid Hildenbrand         return;
342d1b468bcSDavid Hildenbrand     }
343d1b468bcSDavid Hildenbrand 
344d1b468bcSDavid Hildenbrand     /* sensing without locks is racy, but it's the same for real hw */
345d1b468bcSDavid Hildenbrand     if (!s390_has_feat(S390_FEAT_SENSE_RUNNING_STATUS)) {
346d1b468bcSDavid Hildenbrand         set_sigp_status(si, SIGP_STAT_INVALID_ORDER);
347d1b468bcSDavid Hildenbrand         return;
348d1b468bcSDavid Hildenbrand     }
349d1b468bcSDavid Hildenbrand 
350d1b468bcSDavid Hildenbrand     /* If halted (which includes also STOPPED), it is not running */
351d1b468bcSDavid Hildenbrand     if (CPU(dst_cpu)->halted) {
352d1b468bcSDavid Hildenbrand         set_sigp_status(si, SIGP_STAT_NOT_RUNNING);
3534103500eSJanosch Frank     } else {
3544103500eSJanosch Frank         si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
355d1b468bcSDavid Hildenbrand     }
356d1b468bcSDavid Hildenbrand }
357d1b468bcSDavid Hildenbrand 
358070aa1a4SDavid Hildenbrand static int handle_sigp_single_dst(S390CPU *cpu, S390CPU *dst_cpu, uint8_t order,
35974b4c74dSDavid Hildenbrand                                   uint64_t param, uint64_t *status_reg)
36074b4c74dSDavid Hildenbrand {
36174b4c74dSDavid Hildenbrand     SigpInfo si = {
36274b4c74dSDavid Hildenbrand         .param = param,
36374b4c74dSDavid Hildenbrand         .status_reg = status_reg,
36474b4c74dSDavid Hildenbrand     };
36574b4c74dSDavid Hildenbrand 
36674b4c74dSDavid Hildenbrand     /* cpu available? */
36774b4c74dSDavid Hildenbrand     if (dst_cpu == NULL) {
36874b4c74dSDavid Hildenbrand         return SIGP_CC_NOT_OPERATIONAL;
36974b4c74dSDavid Hildenbrand     }
37074b4c74dSDavid Hildenbrand 
37174b4c74dSDavid Hildenbrand     /* only resets can break pending orders */
37274b4c74dSDavid Hildenbrand     if (dst_cpu->env.sigp_order != 0 &&
37374b4c74dSDavid Hildenbrand         order != SIGP_CPU_RESET &&
37474b4c74dSDavid Hildenbrand         order != SIGP_INITIAL_CPU_RESET) {
37574b4c74dSDavid Hildenbrand         return SIGP_CC_BUSY;
37674b4c74dSDavid Hildenbrand     }
37774b4c74dSDavid Hildenbrand 
37874b4c74dSDavid Hildenbrand     switch (order) {
379302230fcSDavid Hildenbrand     case SIGP_SENSE:
380302230fcSDavid Hildenbrand         sigp_sense(dst_cpu, &si);
381302230fcSDavid Hildenbrand         break;
382070aa1a4SDavid Hildenbrand     case SIGP_EXTERNAL_CALL:
383070aa1a4SDavid Hildenbrand         sigp_external_call(cpu, dst_cpu, &si);
384070aa1a4SDavid Hildenbrand         break;
385c50105d4SDavid Hildenbrand     case SIGP_EMERGENCY:
386c50105d4SDavid Hildenbrand         sigp_emergency(cpu, dst_cpu, &si);
387c50105d4SDavid Hildenbrand         break;
38874b4c74dSDavid Hildenbrand     case SIGP_START:
38974b4c74dSDavid Hildenbrand         run_on_cpu(CPU(dst_cpu), sigp_start, RUN_ON_CPU_HOST_PTR(&si));
39074b4c74dSDavid Hildenbrand         break;
39174b4c74dSDavid Hildenbrand     case SIGP_STOP:
39274b4c74dSDavid Hildenbrand         run_on_cpu(CPU(dst_cpu), sigp_stop, RUN_ON_CPU_HOST_PTR(&si));
39374b4c74dSDavid Hildenbrand         break;
39474b4c74dSDavid Hildenbrand     case SIGP_RESTART:
39574b4c74dSDavid Hildenbrand         run_on_cpu(CPU(dst_cpu), sigp_restart, RUN_ON_CPU_HOST_PTR(&si));
39674b4c74dSDavid Hildenbrand         break;
39774b4c74dSDavid Hildenbrand     case SIGP_STOP_STORE_STATUS:
39874b4c74dSDavid Hildenbrand         run_on_cpu(CPU(dst_cpu), sigp_stop_and_store_status, RUN_ON_CPU_HOST_PTR(&si));
39974b4c74dSDavid Hildenbrand         break;
40074b4c74dSDavid Hildenbrand     case SIGP_STORE_STATUS_ADDR:
40174b4c74dSDavid Hildenbrand         run_on_cpu(CPU(dst_cpu), sigp_store_status_at_address, RUN_ON_CPU_HOST_PTR(&si));
40274b4c74dSDavid Hildenbrand         break;
40374b4c74dSDavid Hildenbrand     case SIGP_STORE_ADTL_STATUS:
40474b4c74dSDavid Hildenbrand         run_on_cpu(CPU(dst_cpu), sigp_store_adtl_status, RUN_ON_CPU_HOST_PTR(&si));
40574b4c74dSDavid Hildenbrand         break;
40674b4c74dSDavid Hildenbrand     case SIGP_SET_PREFIX:
40774b4c74dSDavid Hildenbrand         run_on_cpu(CPU(dst_cpu), sigp_set_prefix, RUN_ON_CPU_HOST_PTR(&si));
40874b4c74dSDavid Hildenbrand         break;
40974b4c74dSDavid Hildenbrand     case SIGP_INITIAL_CPU_RESET:
41074b4c74dSDavid Hildenbrand         run_on_cpu(CPU(dst_cpu), sigp_initial_cpu_reset, RUN_ON_CPU_HOST_PTR(&si));
41174b4c74dSDavid Hildenbrand         break;
41274b4c74dSDavid Hildenbrand     case SIGP_CPU_RESET:
41374b4c74dSDavid Hildenbrand         run_on_cpu(CPU(dst_cpu), sigp_cpu_reset, RUN_ON_CPU_HOST_PTR(&si));
41474b4c74dSDavid Hildenbrand         break;
415a6880d21SDavid Hildenbrand     case SIGP_COND_EMERGENCY:
416a6880d21SDavid Hildenbrand         sigp_cond_emergency(cpu, dst_cpu, &si);
417a6880d21SDavid Hildenbrand         break;
418d1b468bcSDavid Hildenbrand     case SIGP_SENSE_RUNNING:
419d1b468bcSDavid Hildenbrand         sigp_sense_running(dst_cpu, &si);
420d1b468bcSDavid Hildenbrand         break;
42174b4c74dSDavid Hildenbrand     default:
42274b4c74dSDavid Hildenbrand         set_sigp_status(&si, SIGP_STAT_INVALID_ORDER);
42374b4c74dSDavid Hildenbrand     }
42474b4c74dSDavid Hildenbrand 
42574b4c74dSDavid Hildenbrand     return si.cc;
42674b4c74dSDavid Hildenbrand }
42774b4c74dSDavid Hildenbrand 
42874b4c74dSDavid Hildenbrand static int sigp_set_architecture(S390CPU *cpu, uint32_t param,
42974b4c74dSDavid Hildenbrand                                  uint64_t *status_reg)
43074b4c74dSDavid Hildenbrand {
43174b4c74dSDavid Hildenbrand     CPUState *cur_cs;
43274b4c74dSDavid Hildenbrand     S390CPU *cur_cpu;
43374b4c74dSDavid Hildenbrand     bool all_stopped = true;
43474b4c74dSDavid Hildenbrand 
43574b4c74dSDavid Hildenbrand     CPU_FOREACH(cur_cs) {
43674b4c74dSDavid Hildenbrand         cur_cpu = S390_CPU(cur_cs);
43774b4c74dSDavid Hildenbrand 
43874b4c74dSDavid Hildenbrand         if (cur_cpu == cpu) {
43974b4c74dSDavid Hildenbrand             continue;
44074b4c74dSDavid Hildenbrand         }
4419d0306dfSViktor Mihajlovski         if (s390_cpu_get_state(cur_cpu) != S390_CPU_STATE_STOPPED) {
44274b4c74dSDavid Hildenbrand             all_stopped = false;
44374b4c74dSDavid Hildenbrand         }
44474b4c74dSDavid Hildenbrand     }
44574b4c74dSDavid Hildenbrand 
44674b4c74dSDavid Hildenbrand     *status_reg &= 0xffffffff00000000ULL;
44774b4c74dSDavid Hildenbrand 
44874b4c74dSDavid Hildenbrand     /* Reject set arch order, with czam we're always in z/Arch mode. */
44974b4c74dSDavid Hildenbrand     *status_reg |= (all_stopped ? SIGP_STAT_INVALID_PARAMETER :
45074b4c74dSDavid Hildenbrand                     SIGP_STAT_INCORRECT_STATE);
45174b4c74dSDavid Hildenbrand     return SIGP_CC_STATUS_STORED;
45274b4c74dSDavid Hildenbrand }
45374b4c74dSDavid Hildenbrand 
45474b4c74dSDavid Hildenbrand int handle_sigp(CPUS390XState *env, uint8_t order, uint64_t r1, uint64_t r3)
45574b4c74dSDavid Hildenbrand {
45674b4c74dSDavid Hildenbrand     uint64_t *status_reg = &env->regs[r1];
45774b4c74dSDavid Hildenbrand     uint64_t param = (r1 % 2) ? env->regs[r1] : env->regs[r1 + 1];
458dc79e928SRichard Henderson     S390CPU *cpu = env_archcpu(env);
45974b4c74dSDavid Hildenbrand     S390CPU *dst_cpu = NULL;
46074b4c74dSDavid Hildenbrand     int ret;
46174b4c74dSDavid Hildenbrand 
46274b4c74dSDavid Hildenbrand     if (qemu_mutex_trylock(&qemu_sigp_mutex)) {
46374b4c74dSDavid Hildenbrand         ret = SIGP_CC_BUSY;
46474b4c74dSDavid Hildenbrand         goto out;
46574b4c74dSDavid Hildenbrand     }
46674b4c74dSDavid Hildenbrand 
46774b4c74dSDavid Hildenbrand     switch (order) {
46874b4c74dSDavid Hildenbrand     case SIGP_SET_ARCH:
46974b4c74dSDavid Hildenbrand         ret = sigp_set_architecture(cpu, param, status_reg);
47074b4c74dSDavid Hildenbrand         break;
47174b4c74dSDavid Hildenbrand     default:
47274b4c74dSDavid Hildenbrand         /* all other sigp orders target a single vcpu */
47374b4c74dSDavid Hildenbrand         dst_cpu = s390_cpu_addr2state(env->regs[r3]);
474070aa1a4SDavid Hildenbrand         ret = handle_sigp_single_dst(cpu, dst_cpu, order, param, status_reg);
47574b4c74dSDavid Hildenbrand     }
47674b4c74dSDavid Hildenbrand     qemu_mutex_unlock(&qemu_sigp_mutex);
47774b4c74dSDavid Hildenbrand 
47874b4c74dSDavid Hildenbrand out:
47974b4c74dSDavid Hildenbrand     trace_sigp_finished(order, CPU(cpu)->cpu_index,
48074b4c74dSDavid Hildenbrand                         dst_cpu ? CPU(dst_cpu)->cpu_index : -1, ret);
48174b4c74dSDavid Hildenbrand     g_assert(ret >= 0);
48274b4c74dSDavid Hildenbrand 
48374b4c74dSDavid Hildenbrand     return ret;
48474b4c74dSDavid Hildenbrand }
48574b4c74dSDavid Hildenbrand 
48674b4c74dSDavid Hildenbrand int s390_cpu_restart(S390CPU *cpu)
48774b4c74dSDavid Hildenbrand {
48874b4c74dSDavid Hildenbrand     SigpInfo si = {};
48974b4c74dSDavid Hildenbrand 
49074b4c74dSDavid Hildenbrand     run_on_cpu(CPU(cpu), sigp_restart, RUN_ON_CPU_HOST_PTR(&si));
49174b4c74dSDavid Hildenbrand     return 0;
49274b4c74dSDavid Hildenbrand }
49374b4c74dSDavid Hildenbrand 
4943047f8b5SDavid Hildenbrand void do_stop_interrupt(CPUS390XState *env)
4953047f8b5SDavid Hildenbrand {
496dc79e928SRichard Henderson     S390CPU *cpu = env_archcpu(env);
4973047f8b5SDavid Hildenbrand 
4989d0306dfSViktor Mihajlovski     if (s390_cpu_set_state(S390_CPU_STATE_STOPPED, cpu) == 0) {
4993047f8b5SDavid Hildenbrand         qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
5003047f8b5SDavid Hildenbrand     }
5013047f8b5SDavid Hildenbrand     if (cpu->env.sigp_order == SIGP_STOP_STORE_STATUS) {
5023047f8b5SDavid Hildenbrand         s390_store_status(cpu, S390_STORE_STATUS_DEF_ADDR, true);
5033047f8b5SDavid Hildenbrand     }
5043047f8b5SDavid Hildenbrand     env->sigp_order = 0;
505b1ab5f60SDavid Hildenbrand     env->pending_int &= ~INTERRUPT_STOP;
5063047f8b5SDavid Hildenbrand }
5073047f8b5SDavid Hildenbrand 
50874b4c74dSDavid Hildenbrand void s390_init_sigp(void)
50974b4c74dSDavid Hildenbrand {
51074b4c74dSDavid Hildenbrand     qemu_mutex_init(&qemu_sigp_mutex);
51174b4c74dSDavid Hildenbrand }
512