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"
13b6b47223SCho, Yu-Chen #include "s390x-internal.h"
14ef7c4a97SPaolo Bonzini #include "hw/boards.h"
1574b4c74dSDavid Hildenbrand #include "sysemu/hw_accel.h"
1654d31236SMarkus Armbruster #include "sysemu/runstate.h"
1774b4c74dSDavid Hildenbrand #include "exec/address-spaces.h"
18b1ab5f60SDavid Hildenbrand #include "exec/exec-all.h"
1914a48c1dSMarkus Armbruster #include "sysemu/tcg.h"
2074b4c74dSDavid Hildenbrand #include "trace.h"
218ac25c84SMarkus Armbruster #include "qapi/qapi-types-machine.h"
2274b4c74dSDavid Hildenbrand
2374b4c74dSDavid Hildenbrand QemuMutex qemu_sigp_mutex;
2474b4c74dSDavid Hildenbrand
2574b4c74dSDavid Hildenbrand typedef struct SigpInfo {
2674b4c74dSDavid Hildenbrand uint64_t param;
2774b4c74dSDavid Hildenbrand int cc;
2874b4c74dSDavid Hildenbrand uint64_t *status_reg;
2974b4c74dSDavid Hildenbrand } SigpInfo;
3074b4c74dSDavid Hildenbrand
set_sigp_status(SigpInfo * si,uint64_t status)3174b4c74dSDavid Hildenbrand static void set_sigp_status(SigpInfo *si, uint64_t status)
3274b4c74dSDavid Hildenbrand {
3374b4c74dSDavid Hildenbrand *si->status_reg &= 0xffffffff00000000ULL;
3474b4c74dSDavid Hildenbrand *si->status_reg |= status;
3574b4c74dSDavid Hildenbrand si->cc = SIGP_CC_STATUS_STORED;
3674b4c74dSDavid Hildenbrand }
3774b4c74dSDavid Hildenbrand
sigp_sense(S390CPU * dst_cpu,SigpInfo * si)38302230fcSDavid Hildenbrand static void sigp_sense(S390CPU *dst_cpu, SigpInfo *si)
39302230fcSDavid Hildenbrand {
40302230fcSDavid Hildenbrand uint8_t state = s390_cpu_get_state(dst_cpu);
41302230fcSDavid Hildenbrand bool ext_call = dst_cpu->env.pending_int & INTERRUPT_EXTERNAL_CALL;
42302230fcSDavid Hildenbrand uint64_t status = 0;
43302230fcSDavid Hildenbrand
44302230fcSDavid Hildenbrand if (!tcg_enabled()) {
45302230fcSDavid Hildenbrand /* handled in KVM */
46302230fcSDavid Hildenbrand set_sigp_status(si, SIGP_STAT_INVALID_ORDER);
47302230fcSDavid Hildenbrand return;
48302230fcSDavid Hildenbrand }
49302230fcSDavid Hildenbrand
50302230fcSDavid Hildenbrand /* sensing without locks is racy, but it's the same for real hw */
519d0306dfSViktor Mihajlovski if (state != S390_CPU_STATE_STOPPED && !ext_call) {
52302230fcSDavid Hildenbrand si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
53302230fcSDavid Hildenbrand } else {
54302230fcSDavid Hildenbrand if (ext_call) {
55302230fcSDavid Hildenbrand status |= SIGP_STAT_EXT_CALL_PENDING;
56302230fcSDavid Hildenbrand }
579d0306dfSViktor Mihajlovski if (state == S390_CPU_STATE_STOPPED) {
58302230fcSDavid Hildenbrand status |= SIGP_STAT_STOPPED;
59302230fcSDavid Hildenbrand }
60302230fcSDavid Hildenbrand set_sigp_status(si, status);
61302230fcSDavid Hildenbrand }
62302230fcSDavid Hildenbrand }
63302230fcSDavid Hildenbrand
sigp_external_call(S390CPU * src_cpu,S390CPU * dst_cpu,SigpInfo * si)64070aa1a4SDavid Hildenbrand static void sigp_external_call(S390CPU *src_cpu, S390CPU *dst_cpu, SigpInfo *si)
65070aa1a4SDavid Hildenbrand {
66070aa1a4SDavid Hildenbrand int ret;
67070aa1a4SDavid Hildenbrand
68070aa1a4SDavid Hildenbrand if (!tcg_enabled()) {
69070aa1a4SDavid Hildenbrand /* handled in KVM */
70070aa1a4SDavid Hildenbrand set_sigp_status(si, SIGP_STAT_INVALID_ORDER);
71070aa1a4SDavid Hildenbrand return;
72070aa1a4SDavid Hildenbrand }
73070aa1a4SDavid Hildenbrand
74070aa1a4SDavid Hildenbrand ret = cpu_inject_external_call(dst_cpu, src_cpu->env.core_id);
75070aa1a4SDavid Hildenbrand if (!ret) {
76070aa1a4SDavid Hildenbrand si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
77070aa1a4SDavid Hildenbrand } else {
78070aa1a4SDavid Hildenbrand set_sigp_status(si, SIGP_STAT_EXT_CALL_PENDING);
79070aa1a4SDavid Hildenbrand }
80070aa1a4SDavid Hildenbrand }
81070aa1a4SDavid Hildenbrand
sigp_emergency(S390CPU * src_cpu,S390CPU * dst_cpu,SigpInfo * si)82c50105d4SDavid Hildenbrand static void sigp_emergency(S390CPU *src_cpu, S390CPU *dst_cpu, SigpInfo *si)
83c50105d4SDavid Hildenbrand {
84c50105d4SDavid Hildenbrand if (!tcg_enabled()) {
85c50105d4SDavid Hildenbrand /* handled in KVM */
86c50105d4SDavid Hildenbrand set_sigp_status(si, SIGP_STAT_INVALID_ORDER);
87c50105d4SDavid Hildenbrand return;
88c50105d4SDavid Hildenbrand }
89c50105d4SDavid Hildenbrand
90c50105d4SDavid Hildenbrand cpu_inject_emergency_signal(dst_cpu, src_cpu->env.core_id);
91c50105d4SDavid Hildenbrand si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
92c50105d4SDavid Hildenbrand }
93c50105d4SDavid Hildenbrand
sigp_start(CPUState * cs,run_on_cpu_data arg)9474b4c74dSDavid Hildenbrand static void sigp_start(CPUState *cs, run_on_cpu_data arg)
9574b4c74dSDavid Hildenbrand {
9674b4c74dSDavid Hildenbrand S390CPU *cpu = S390_CPU(cs);
9774b4c74dSDavid Hildenbrand SigpInfo *si = arg.host_ptr;
9874b4c74dSDavid Hildenbrand
999d0306dfSViktor Mihajlovski if (s390_cpu_get_state(cpu) != S390_CPU_STATE_STOPPED) {
10074b4c74dSDavid Hildenbrand si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
10174b4c74dSDavid Hildenbrand return;
10274b4c74dSDavid Hildenbrand }
10374b4c74dSDavid Hildenbrand
1049d0306dfSViktor Mihajlovski s390_cpu_set_state(S390_CPU_STATE_OPERATING, cpu);
10574b4c74dSDavid Hildenbrand si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
10674b4c74dSDavid Hildenbrand }
10774b4c74dSDavid Hildenbrand
sigp_stop(CPUState * cs,run_on_cpu_data arg)10874b4c74dSDavid Hildenbrand static void sigp_stop(CPUState *cs, run_on_cpu_data arg)
10974b4c74dSDavid Hildenbrand {
11074b4c74dSDavid Hildenbrand S390CPU *cpu = S390_CPU(cs);
11174b4c74dSDavid Hildenbrand SigpInfo *si = arg.host_ptr;
11274b4c74dSDavid Hildenbrand
1139d0306dfSViktor Mihajlovski if (s390_cpu_get_state(cpu) != S390_CPU_STATE_OPERATING) {
11474b4c74dSDavid Hildenbrand si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
11574b4c74dSDavid Hildenbrand return;
11674b4c74dSDavid Hildenbrand }
11774b4c74dSDavid Hildenbrand
11874b4c74dSDavid Hildenbrand /* disabled wait - sleeping in user space */
11974b4c74dSDavid Hildenbrand if (cs->halted) {
1209d0306dfSViktor Mihajlovski s390_cpu_set_state(S390_CPU_STATE_STOPPED, cpu);
12174b4c74dSDavid Hildenbrand } else {
12274b4c74dSDavid Hildenbrand /* execute the stop function */
12374b4c74dSDavid Hildenbrand cpu->env.sigp_order = SIGP_STOP;
12474b4c74dSDavid Hildenbrand cpu_inject_stop(cpu);
12574b4c74dSDavid Hildenbrand }
12674b4c74dSDavid Hildenbrand si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
12774b4c74dSDavid Hildenbrand }
12874b4c74dSDavid Hildenbrand
sigp_stop_and_store_status(CPUState * cs,run_on_cpu_data arg)12974b4c74dSDavid Hildenbrand static void sigp_stop_and_store_status(CPUState *cs, run_on_cpu_data arg)
13074b4c74dSDavid Hildenbrand {
13174b4c74dSDavid Hildenbrand S390CPU *cpu = S390_CPU(cs);
13274b4c74dSDavid Hildenbrand SigpInfo *si = arg.host_ptr;
13374b4c74dSDavid Hildenbrand
13474b4c74dSDavid Hildenbrand /* disabled wait - sleeping in user space */
1359d0306dfSViktor Mihajlovski if (s390_cpu_get_state(cpu) == S390_CPU_STATE_OPERATING && cs->halted) {
1369d0306dfSViktor Mihajlovski s390_cpu_set_state(S390_CPU_STATE_STOPPED, cpu);
13774b4c74dSDavid Hildenbrand }
13874b4c74dSDavid Hildenbrand
13974b4c74dSDavid Hildenbrand switch (s390_cpu_get_state(cpu)) {
1409d0306dfSViktor Mihajlovski case S390_CPU_STATE_OPERATING:
14174b4c74dSDavid Hildenbrand cpu->env.sigp_order = SIGP_STOP_STORE_STATUS;
14274b4c74dSDavid Hildenbrand cpu_inject_stop(cpu);
14359b9b518SEric Farman /* store will be performed in do_stop_interrupt() */
14474b4c74dSDavid Hildenbrand break;
1459d0306dfSViktor Mihajlovski case S390_CPU_STATE_STOPPED:
14674b4c74dSDavid Hildenbrand /* already stopped, just store the status */
14774b4c74dSDavid Hildenbrand cpu_synchronize_state(cs);
14874b4c74dSDavid Hildenbrand s390_store_status(cpu, S390_STORE_STATUS_DEF_ADDR, true);
14974b4c74dSDavid Hildenbrand break;
15074b4c74dSDavid Hildenbrand }
15174b4c74dSDavid Hildenbrand si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
15274b4c74dSDavid Hildenbrand }
15374b4c74dSDavid Hildenbrand
sigp_store_status_at_address(CPUState * cs,run_on_cpu_data arg)15474b4c74dSDavid Hildenbrand static void sigp_store_status_at_address(CPUState *cs, run_on_cpu_data arg)
15574b4c74dSDavid Hildenbrand {
15674b4c74dSDavid Hildenbrand S390CPU *cpu = S390_CPU(cs);
15774b4c74dSDavid Hildenbrand SigpInfo *si = arg.host_ptr;
15874b4c74dSDavid Hildenbrand uint32_t address = si->param & 0x7ffffe00u;
15974b4c74dSDavid Hildenbrand
16074b4c74dSDavid Hildenbrand /* cpu has to be stopped */
1619d0306dfSViktor Mihajlovski if (s390_cpu_get_state(cpu) != S390_CPU_STATE_STOPPED) {
16274b4c74dSDavid Hildenbrand set_sigp_status(si, SIGP_STAT_INCORRECT_STATE);
16374b4c74dSDavid Hildenbrand return;
16474b4c74dSDavid Hildenbrand }
16574b4c74dSDavid Hildenbrand
16674b4c74dSDavid Hildenbrand cpu_synchronize_state(cs);
16774b4c74dSDavid Hildenbrand
16874b4c74dSDavid Hildenbrand if (s390_store_status(cpu, address, false)) {
16974b4c74dSDavid Hildenbrand set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER);
17074b4c74dSDavid Hildenbrand return;
17174b4c74dSDavid Hildenbrand }
17274b4c74dSDavid Hildenbrand si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
17374b4c74dSDavid Hildenbrand }
17474b4c74dSDavid Hildenbrand
17574b4c74dSDavid Hildenbrand #define ADTL_SAVE_LC_MASK 0xfUL
sigp_store_adtl_status(CPUState * cs,run_on_cpu_data arg)17674b4c74dSDavid Hildenbrand static void sigp_store_adtl_status(CPUState *cs, run_on_cpu_data arg)
17774b4c74dSDavid Hildenbrand {
17874b4c74dSDavid Hildenbrand S390CPU *cpu = S390_CPU(cs);
17974b4c74dSDavid Hildenbrand SigpInfo *si = arg.host_ptr;
18074b4c74dSDavid Hildenbrand uint8_t lc = si->param & ADTL_SAVE_LC_MASK;
18174b4c74dSDavid Hildenbrand hwaddr addr = si->param & ~ADTL_SAVE_LC_MASK;
18274b4c74dSDavid Hildenbrand hwaddr len = 1UL << (lc ? lc : 10);
18374b4c74dSDavid Hildenbrand
18474b4c74dSDavid Hildenbrand if (!s390_has_feat(S390_FEAT_VECTOR) &&
18574b4c74dSDavid Hildenbrand !s390_has_feat(S390_FEAT_GUARDED_STORAGE)) {
18674b4c74dSDavid Hildenbrand set_sigp_status(si, SIGP_STAT_INVALID_ORDER);
18774b4c74dSDavid Hildenbrand return;
18874b4c74dSDavid Hildenbrand }
18974b4c74dSDavid Hildenbrand
19074b4c74dSDavid Hildenbrand /* cpu has to be stopped */
1919d0306dfSViktor Mihajlovski if (s390_cpu_get_state(cpu) != S390_CPU_STATE_STOPPED) {
19274b4c74dSDavid Hildenbrand set_sigp_status(si, SIGP_STAT_INCORRECT_STATE);
19374b4c74dSDavid Hildenbrand return;
19474b4c74dSDavid Hildenbrand }
19574b4c74dSDavid Hildenbrand
19674b4c74dSDavid Hildenbrand /* address must be aligned to length */
19774b4c74dSDavid Hildenbrand if (addr & (len - 1)) {
19874b4c74dSDavid Hildenbrand set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER);
19974b4c74dSDavid Hildenbrand return;
20074b4c74dSDavid Hildenbrand }
20174b4c74dSDavid Hildenbrand
20274b4c74dSDavid Hildenbrand /* no GS: only lc == 0 is valid */
20374b4c74dSDavid Hildenbrand if (!s390_has_feat(S390_FEAT_GUARDED_STORAGE) &&
20474b4c74dSDavid Hildenbrand lc != 0) {
20574b4c74dSDavid Hildenbrand set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER);
20674b4c74dSDavid Hildenbrand return;
20774b4c74dSDavid Hildenbrand }
20874b4c74dSDavid Hildenbrand
20974b4c74dSDavid Hildenbrand /* GS: 0, 10, 11, 12 are valid */
21074b4c74dSDavid Hildenbrand if (s390_has_feat(S390_FEAT_GUARDED_STORAGE) &&
21174b4c74dSDavid Hildenbrand lc != 0 &&
21274b4c74dSDavid Hildenbrand lc != 10 &&
21374b4c74dSDavid Hildenbrand lc != 11 &&
21474b4c74dSDavid Hildenbrand lc != 12) {
21574b4c74dSDavid Hildenbrand set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER);
21674b4c74dSDavid Hildenbrand return;
21774b4c74dSDavid Hildenbrand }
21874b4c74dSDavid Hildenbrand
21974b4c74dSDavid Hildenbrand cpu_synchronize_state(cs);
22074b4c74dSDavid Hildenbrand
22174b4c74dSDavid Hildenbrand if (s390_store_adtl_status(cpu, addr, len)) {
22274b4c74dSDavid Hildenbrand set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER);
22374b4c74dSDavid Hildenbrand return;
22474b4c74dSDavid Hildenbrand }
22574b4c74dSDavid Hildenbrand si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
22674b4c74dSDavid Hildenbrand }
22774b4c74dSDavid Hildenbrand
sigp_restart(CPUState * cs,run_on_cpu_data arg)22874b4c74dSDavid Hildenbrand static void sigp_restart(CPUState *cs, run_on_cpu_data arg)
22974b4c74dSDavid Hildenbrand {
23074b4c74dSDavid Hildenbrand S390CPU *cpu = S390_CPU(cs);
23174b4c74dSDavid Hildenbrand SigpInfo *si = arg.host_ptr;
23274b4c74dSDavid Hildenbrand
23374b4c74dSDavid Hildenbrand switch (s390_cpu_get_state(cpu)) {
2349d0306dfSViktor Mihajlovski case S390_CPU_STATE_STOPPED:
23574b4c74dSDavid Hildenbrand /* the restart irq has to be delivered prior to any other pending irq */
23674b4c74dSDavid Hildenbrand cpu_synchronize_state(cs);
237741a4ec1SDavid Hildenbrand /*
238741a4ec1SDavid Hildenbrand * Set OPERATING (and unhalting) before loading the restart PSW.
239e2b2a864SRichard Henderson * s390_cpu_set_psw() will then properly halt the CPU again if
240e2b2a864SRichard Henderson * necessary (TCG).
241741a4ec1SDavid Hildenbrand */
2429d0306dfSViktor Mihajlovski s390_cpu_set_state(S390_CPU_STATE_OPERATING, cpu);
243741a4ec1SDavid Hildenbrand do_restart_interrupt(&cpu->env);
24474b4c74dSDavid Hildenbrand break;
2459d0306dfSViktor Mihajlovski case S390_CPU_STATE_OPERATING:
24674b4c74dSDavid Hildenbrand cpu_inject_restart(cpu);
24774b4c74dSDavid Hildenbrand break;
24874b4c74dSDavid Hildenbrand }
24974b4c74dSDavid Hildenbrand si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
25074b4c74dSDavid Hildenbrand }
25174b4c74dSDavid Hildenbrand
sigp_initial_cpu_reset(CPUState * cs,run_on_cpu_data arg)25274b4c74dSDavid Hildenbrand static void sigp_initial_cpu_reset(CPUState *cs, run_on_cpu_data arg)
25374b4c74dSDavid Hildenbrand {
25474b4c74dSDavid Hildenbrand SigpInfo *si = arg.host_ptr;
25574b4c74dSDavid Hildenbrand
25674b4c74dSDavid Hildenbrand cpu_synchronize_state(cs);
257*cf7f61d1SPeter Maydell resettable_reset(OBJECT(cs), RESET_TYPE_S390_CPU_INITIAL);
25874b4c74dSDavid Hildenbrand cpu_synchronize_post_reset(cs);
25974b4c74dSDavid Hildenbrand si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
26074b4c74dSDavid Hildenbrand }
26174b4c74dSDavid Hildenbrand
sigp_cpu_reset(CPUState * cs,run_on_cpu_data arg)26274b4c74dSDavid Hildenbrand static void sigp_cpu_reset(CPUState *cs, run_on_cpu_data arg)
26374b4c74dSDavid Hildenbrand {
26474b4c74dSDavid Hildenbrand SigpInfo *si = arg.host_ptr;
26574b4c74dSDavid Hildenbrand
26674b4c74dSDavid Hildenbrand cpu_synchronize_state(cs);
267*cf7f61d1SPeter Maydell resettable_reset(OBJECT(cs), RESET_TYPE_S390_CPU_NORMAL);
26874b4c74dSDavid Hildenbrand cpu_synchronize_post_reset(cs);
26974b4c74dSDavid Hildenbrand si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
27074b4c74dSDavid Hildenbrand }
27174b4c74dSDavid Hildenbrand
sigp_set_prefix(CPUState * cs,run_on_cpu_data arg)27274b4c74dSDavid Hildenbrand static void sigp_set_prefix(CPUState *cs, run_on_cpu_data arg)
27374b4c74dSDavid Hildenbrand {
27474b4c74dSDavid Hildenbrand S390CPU *cpu = S390_CPU(cs);
27574b4c74dSDavid Hildenbrand SigpInfo *si = arg.host_ptr;
27674b4c74dSDavid Hildenbrand uint32_t addr = si->param & 0x7fffe000u;
27774b4c74dSDavid Hildenbrand
27874b4c74dSDavid Hildenbrand cpu_synchronize_state(cs);
27974b4c74dSDavid Hildenbrand
28074b4c74dSDavid Hildenbrand if (!address_space_access_valid(&address_space_memory, addr,
281fddffa42SPeter Maydell sizeof(struct LowCore), false,
282fddffa42SPeter Maydell MEMTXATTRS_UNSPECIFIED)) {
28374b4c74dSDavid Hildenbrand set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER);
28474b4c74dSDavid Hildenbrand return;
28574b4c74dSDavid Hildenbrand }
28674b4c74dSDavid Hildenbrand
28774b4c74dSDavid Hildenbrand /* cpu has to be stopped */
2889d0306dfSViktor Mihajlovski if (s390_cpu_get_state(cpu) != S390_CPU_STATE_STOPPED) {
28974b4c74dSDavid Hildenbrand set_sigp_status(si, SIGP_STAT_INCORRECT_STATE);
29074b4c74dSDavid Hildenbrand return;
29174b4c74dSDavid Hildenbrand }
29274b4c74dSDavid Hildenbrand
29374b4c74dSDavid Hildenbrand cpu->env.psa = addr;
294b376a554SDavid Hildenbrand tlb_flush(cs);
29574b4c74dSDavid Hildenbrand cpu_synchronize_post_init(cs);
29674b4c74dSDavid Hildenbrand si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
29774b4c74dSDavid Hildenbrand }
29874b4c74dSDavid Hildenbrand
sigp_cond_emergency(S390CPU * src_cpu,S390CPU * dst_cpu,SigpInfo * si)299a6880d21SDavid Hildenbrand static void sigp_cond_emergency(S390CPU *src_cpu, S390CPU *dst_cpu,
300a6880d21SDavid Hildenbrand SigpInfo *si)
301a6880d21SDavid Hildenbrand {
302a6880d21SDavid Hildenbrand const uint64_t psw_int_mask = PSW_MASK_IO | PSW_MASK_EXT;
303a6880d21SDavid Hildenbrand uint16_t p_asn, s_asn, asn;
304a6880d21SDavid Hildenbrand uint64_t psw_addr, psw_mask;
305a6880d21SDavid Hildenbrand bool idle;
306a6880d21SDavid Hildenbrand
307a6880d21SDavid Hildenbrand if (!tcg_enabled()) {
308a6880d21SDavid Hildenbrand /* handled in KVM */
309a6880d21SDavid Hildenbrand set_sigp_status(si, SIGP_STAT_INVALID_ORDER);
310a6880d21SDavid Hildenbrand return;
311a6880d21SDavid Hildenbrand }
312a6880d21SDavid Hildenbrand
313a6880d21SDavid Hildenbrand /* this looks racy, but these values are only used when STOPPED */
314a6880d21SDavid Hildenbrand idle = CPU(dst_cpu)->halted;
315a6880d21SDavid Hildenbrand psw_addr = dst_cpu->env.psw.addr;
316a6880d21SDavid Hildenbrand psw_mask = dst_cpu->env.psw.mask;
317a6880d21SDavid Hildenbrand asn = si->param;
318a6880d21SDavid Hildenbrand p_asn = dst_cpu->env.cregs[4] & 0xffff; /* Primary ASN */
319a6880d21SDavid Hildenbrand s_asn = dst_cpu->env.cregs[3] & 0xffff; /* Secondary ASN */
320a6880d21SDavid Hildenbrand
3219d0306dfSViktor Mihajlovski if (s390_cpu_get_state(dst_cpu) != S390_CPU_STATE_STOPPED ||
322a6880d21SDavid Hildenbrand (psw_mask & psw_int_mask) != psw_int_mask ||
323a6880d21SDavid Hildenbrand (idle && psw_addr != 0) ||
324a6880d21SDavid Hildenbrand (!idle && (asn == p_asn || asn == s_asn))) {
325a6880d21SDavid Hildenbrand cpu_inject_emergency_signal(dst_cpu, src_cpu->env.core_id);
326a6880d21SDavid Hildenbrand } else {
327a6880d21SDavid Hildenbrand set_sigp_status(si, SIGP_STAT_INCORRECT_STATE);
328a6880d21SDavid Hildenbrand }
329a6880d21SDavid Hildenbrand
330a6880d21SDavid Hildenbrand si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
331a6880d21SDavid Hildenbrand }
332a6880d21SDavid Hildenbrand
sigp_sense_running(S390CPU * dst_cpu,SigpInfo * si)333d1b468bcSDavid Hildenbrand static void sigp_sense_running(S390CPU *dst_cpu, SigpInfo *si)
334d1b468bcSDavid Hildenbrand {
335d1b468bcSDavid Hildenbrand if (!tcg_enabled()) {
336d1b468bcSDavid Hildenbrand /* handled in KVM */
337d1b468bcSDavid Hildenbrand set_sigp_status(si, SIGP_STAT_INVALID_ORDER);
338d1b468bcSDavid Hildenbrand return;
339d1b468bcSDavid Hildenbrand }
340d1b468bcSDavid Hildenbrand
341d1b468bcSDavid Hildenbrand /* sensing without locks is racy, but it's the same for real hw */
342d1b468bcSDavid Hildenbrand if (!s390_has_feat(S390_FEAT_SENSE_RUNNING_STATUS)) {
343d1b468bcSDavid Hildenbrand set_sigp_status(si, SIGP_STAT_INVALID_ORDER);
344d1b468bcSDavid Hildenbrand return;
345d1b468bcSDavid Hildenbrand }
346d1b468bcSDavid Hildenbrand
347d1b468bcSDavid Hildenbrand /* If halted (which includes also STOPPED), it is not running */
348d1b468bcSDavid Hildenbrand if (CPU(dst_cpu)->halted) {
349d1b468bcSDavid Hildenbrand set_sigp_status(si, SIGP_STAT_NOT_RUNNING);
3504103500eSJanosch Frank } else {
3514103500eSJanosch Frank si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
352d1b468bcSDavid Hildenbrand }
353d1b468bcSDavid Hildenbrand }
354d1b468bcSDavid Hildenbrand
handle_sigp_single_dst(S390CPU * cpu,S390CPU * dst_cpu,uint8_t order,uint64_t param,uint64_t * status_reg)355070aa1a4SDavid Hildenbrand static int handle_sigp_single_dst(S390CPU *cpu, S390CPU *dst_cpu, uint8_t order,
35674b4c74dSDavid Hildenbrand uint64_t param, uint64_t *status_reg)
35774b4c74dSDavid Hildenbrand {
35874b4c74dSDavid Hildenbrand SigpInfo si = {
35974b4c74dSDavid Hildenbrand .param = param,
36074b4c74dSDavid Hildenbrand .status_reg = status_reg,
36174b4c74dSDavid Hildenbrand };
36274b4c74dSDavid Hildenbrand
36374b4c74dSDavid Hildenbrand /* cpu available? */
36474b4c74dSDavid Hildenbrand if (dst_cpu == NULL) {
36574b4c74dSDavid Hildenbrand return SIGP_CC_NOT_OPERATIONAL;
36674b4c74dSDavid Hildenbrand }
36774b4c74dSDavid Hildenbrand
36874b4c74dSDavid Hildenbrand /* only resets can break pending orders */
36974b4c74dSDavid Hildenbrand if (dst_cpu->env.sigp_order != 0 &&
37074b4c74dSDavid Hildenbrand order != SIGP_CPU_RESET &&
37174b4c74dSDavid Hildenbrand order != SIGP_INITIAL_CPU_RESET) {
37274b4c74dSDavid Hildenbrand return SIGP_CC_BUSY;
37374b4c74dSDavid Hildenbrand }
37474b4c74dSDavid Hildenbrand
37574b4c74dSDavid Hildenbrand switch (order) {
376302230fcSDavid Hildenbrand case SIGP_SENSE:
377302230fcSDavid Hildenbrand sigp_sense(dst_cpu, &si);
378302230fcSDavid Hildenbrand break;
379070aa1a4SDavid Hildenbrand case SIGP_EXTERNAL_CALL:
380070aa1a4SDavid Hildenbrand sigp_external_call(cpu, dst_cpu, &si);
381070aa1a4SDavid Hildenbrand break;
382c50105d4SDavid Hildenbrand case SIGP_EMERGENCY:
383c50105d4SDavid Hildenbrand sigp_emergency(cpu, dst_cpu, &si);
384c50105d4SDavid Hildenbrand break;
38574b4c74dSDavid Hildenbrand case SIGP_START:
38674b4c74dSDavid Hildenbrand run_on_cpu(CPU(dst_cpu), sigp_start, RUN_ON_CPU_HOST_PTR(&si));
38774b4c74dSDavid Hildenbrand break;
38874b4c74dSDavid Hildenbrand case SIGP_STOP:
38974b4c74dSDavid Hildenbrand run_on_cpu(CPU(dst_cpu), sigp_stop, RUN_ON_CPU_HOST_PTR(&si));
39074b4c74dSDavid Hildenbrand break;
39174b4c74dSDavid Hildenbrand case SIGP_RESTART:
39274b4c74dSDavid Hildenbrand run_on_cpu(CPU(dst_cpu), sigp_restart, RUN_ON_CPU_HOST_PTR(&si));
39374b4c74dSDavid Hildenbrand break;
39474b4c74dSDavid Hildenbrand case SIGP_STOP_STORE_STATUS:
39574b4c74dSDavid Hildenbrand run_on_cpu(CPU(dst_cpu), sigp_stop_and_store_status, RUN_ON_CPU_HOST_PTR(&si));
39674b4c74dSDavid Hildenbrand break;
39774b4c74dSDavid Hildenbrand case SIGP_STORE_STATUS_ADDR:
39874b4c74dSDavid Hildenbrand run_on_cpu(CPU(dst_cpu), sigp_store_status_at_address, RUN_ON_CPU_HOST_PTR(&si));
39974b4c74dSDavid Hildenbrand break;
40074b4c74dSDavid Hildenbrand case SIGP_STORE_ADTL_STATUS:
40174b4c74dSDavid Hildenbrand run_on_cpu(CPU(dst_cpu), sigp_store_adtl_status, RUN_ON_CPU_HOST_PTR(&si));
40274b4c74dSDavid Hildenbrand break;
40374b4c74dSDavid Hildenbrand case SIGP_SET_PREFIX:
40474b4c74dSDavid Hildenbrand run_on_cpu(CPU(dst_cpu), sigp_set_prefix, RUN_ON_CPU_HOST_PTR(&si));
40574b4c74dSDavid Hildenbrand break;
40674b4c74dSDavid Hildenbrand case SIGP_INITIAL_CPU_RESET:
40774b4c74dSDavid Hildenbrand run_on_cpu(CPU(dst_cpu), sigp_initial_cpu_reset, RUN_ON_CPU_HOST_PTR(&si));
40874b4c74dSDavid Hildenbrand break;
40974b4c74dSDavid Hildenbrand case SIGP_CPU_RESET:
41074b4c74dSDavid Hildenbrand run_on_cpu(CPU(dst_cpu), sigp_cpu_reset, RUN_ON_CPU_HOST_PTR(&si));
41174b4c74dSDavid Hildenbrand break;
412a6880d21SDavid Hildenbrand case SIGP_COND_EMERGENCY:
413a6880d21SDavid Hildenbrand sigp_cond_emergency(cpu, dst_cpu, &si);
414a6880d21SDavid Hildenbrand break;
415d1b468bcSDavid Hildenbrand case SIGP_SENSE_RUNNING:
416d1b468bcSDavid Hildenbrand sigp_sense_running(dst_cpu, &si);
417d1b468bcSDavid Hildenbrand break;
41874b4c74dSDavid Hildenbrand default:
41974b4c74dSDavid Hildenbrand set_sigp_status(&si, SIGP_STAT_INVALID_ORDER);
42074b4c74dSDavid Hildenbrand }
42174b4c74dSDavid Hildenbrand
42274b4c74dSDavid Hildenbrand return si.cc;
42374b4c74dSDavid Hildenbrand }
42474b4c74dSDavid Hildenbrand
sigp_set_architecture(S390CPU * cpu,uint32_t param,uint64_t * status_reg)42574b4c74dSDavid Hildenbrand static int sigp_set_architecture(S390CPU *cpu, uint32_t param,
42674b4c74dSDavid Hildenbrand uint64_t *status_reg)
42774b4c74dSDavid Hildenbrand {
42874b4c74dSDavid Hildenbrand *status_reg &= 0xffffffff00000000ULL;
42974b4c74dSDavid Hildenbrand
43074b4c74dSDavid Hildenbrand /* Reject set arch order, with czam we're always in z/Arch mode. */
431998eb744SEric Farman *status_reg |= SIGP_STAT_INVALID_PARAMETER;
43274b4c74dSDavid Hildenbrand return SIGP_CC_STATUS_STORED;
43374b4c74dSDavid Hildenbrand }
43474b4c74dSDavid Hildenbrand
s390_cpu_addr2state(uint16_t cpu_addr)435ef7c4a97SPaolo Bonzini S390CPU *s390_cpu_addr2state(uint16_t cpu_addr)
436ef7c4a97SPaolo Bonzini {
437ef7c4a97SPaolo Bonzini static MachineState *ms;
438ef7c4a97SPaolo Bonzini
439ef7c4a97SPaolo Bonzini if (!ms) {
440ef7c4a97SPaolo Bonzini ms = MACHINE(qdev_get_machine());
441ef7c4a97SPaolo Bonzini g_assert(ms->possible_cpus);
442ef7c4a97SPaolo Bonzini }
443ef7c4a97SPaolo Bonzini
444ef7c4a97SPaolo Bonzini /* CPU address corresponds to the core_id and the index */
445ef7c4a97SPaolo Bonzini if (cpu_addr >= ms->possible_cpus->len) {
446ef7c4a97SPaolo Bonzini return NULL;
447ef7c4a97SPaolo Bonzini }
448ef7c4a97SPaolo Bonzini return S390_CPU(ms->possible_cpus->cpus[cpu_addr].cpu);
449ef7c4a97SPaolo Bonzini }
450ef7c4a97SPaolo Bonzini
handle_sigp(CPUS390XState * env,uint8_t order,uint64_t r1,uint64_t r3)45174b4c74dSDavid Hildenbrand int handle_sigp(CPUS390XState *env, uint8_t order, uint64_t r1, uint64_t r3)
45274b4c74dSDavid Hildenbrand {
45374b4c74dSDavid Hildenbrand uint64_t *status_reg = &env->regs[r1];
45474b4c74dSDavid Hildenbrand uint64_t param = (r1 % 2) ? env->regs[r1] : env->regs[r1 + 1];
455dc79e928SRichard Henderson S390CPU *cpu = env_archcpu(env);
45674b4c74dSDavid Hildenbrand S390CPU *dst_cpu = NULL;
45774b4c74dSDavid Hildenbrand int ret;
45874b4c74dSDavid Hildenbrand
45974b4c74dSDavid Hildenbrand if (qemu_mutex_trylock(&qemu_sigp_mutex)) {
46074b4c74dSDavid Hildenbrand ret = SIGP_CC_BUSY;
46174b4c74dSDavid Hildenbrand goto out;
46274b4c74dSDavid Hildenbrand }
46374b4c74dSDavid Hildenbrand
46474b4c74dSDavid Hildenbrand switch (order) {
46574b4c74dSDavid Hildenbrand case SIGP_SET_ARCH:
46674b4c74dSDavid Hildenbrand ret = sigp_set_architecture(cpu, param, status_reg);
46774b4c74dSDavid Hildenbrand break;
46874b4c74dSDavid Hildenbrand default:
46974b4c74dSDavid Hildenbrand /* all other sigp orders target a single vcpu */
47074b4c74dSDavid Hildenbrand dst_cpu = s390_cpu_addr2state(env->regs[r3]);
471070aa1a4SDavid Hildenbrand ret = handle_sigp_single_dst(cpu, dst_cpu, order, param, status_reg);
47274b4c74dSDavid Hildenbrand }
47374b4c74dSDavid Hildenbrand qemu_mutex_unlock(&qemu_sigp_mutex);
47474b4c74dSDavid Hildenbrand
47574b4c74dSDavid Hildenbrand out:
47674b4c74dSDavid Hildenbrand trace_sigp_finished(order, CPU(cpu)->cpu_index,
47774b4c74dSDavid Hildenbrand dst_cpu ? CPU(dst_cpu)->cpu_index : -1, ret);
47874b4c74dSDavid Hildenbrand g_assert(ret >= 0);
47974b4c74dSDavid Hildenbrand
48074b4c74dSDavid Hildenbrand return ret;
48174b4c74dSDavid Hildenbrand }
48274b4c74dSDavid Hildenbrand
s390_cpu_restart(S390CPU * cpu)48374b4c74dSDavid Hildenbrand int s390_cpu_restart(S390CPU *cpu)
48474b4c74dSDavid Hildenbrand {
48574b4c74dSDavid Hildenbrand SigpInfo si = {};
48674b4c74dSDavid Hildenbrand
48774b4c74dSDavid Hildenbrand run_on_cpu(CPU(cpu), sigp_restart, RUN_ON_CPU_HOST_PTR(&si));
48874b4c74dSDavid Hildenbrand return 0;
48974b4c74dSDavid Hildenbrand }
49074b4c74dSDavid Hildenbrand
do_stop_interrupt(CPUS390XState * env)4913047f8b5SDavid Hildenbrand void do_stop_interrupt(CPUS390XState *env)
4923047f8b5SDavid Hildenbrand {
493dc79e928SRichard Henderson S390CPU *cpu = env_archcpu(env);
4943047f8b5SDavid Hildenbrand
49559b9b518SEric Farman /*
49659b9b518SEric Farman * Complete the STOP operation before exposing the CPU as
49759b9b518SEric Farman * STOPPED to the system.
49859b9b518SEric Farman */
4993047f8b5SDavid Hildenbrand if (cpu->env.sigp_order == SIGP_STOP_STORE_STATUS) {
5003047f8b5SDavid Hildenbrand s390_store_status(cpu, S390_STORE_STATUS_DEF_ADDR, true);
5013047f8b5SDavid Hildenbrand }
5023047f8b5SDavid Hildenbrand env->sigp_order = 0;
50359b9b518SEric Farman if (s390_cpu_set_state(S390_CPU_STATE_STOPPED, cpu) == 0) {
50459b9b518SEric Farman qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
50559b9b518SEric Farman }
506b1ab5f60SDavid Hildenbrand env->pending_int &= ~INTERRUPT_STOP;
5073047f8b5SDavid Hildenbrand }
5083047f8b5SDavid Hildenbrand
s390_init_sigp(void)50974b4c74dSDavid Hildenbrand void s390_init_sigp(void)
51074b4c74dSDavid Hildenbrand {
51174b4c74dSDavid Hildenbrand qemu_mutex_init(&qemu_sigp_mutex);
51274b4c74dSDavid Hildenbrand }
513