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