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 35302230fcSDavid Hildenbrand static void sigp_sense(S390CPU *dst_cpu, SigpInfo *si) 36302230fcSDavid Hildenbrand { 37302230fcSDavid Hildenbrand uint8_t state = s390_cpu_get_state(dst_cpu); 38302230fcSDavid Hildenbrand bool ext_call = dst_cpu->env.pending_int & INTERRUPT_EXTERNAL_CALL; 39302230fcSDavid Hildenbrand uint64_t status = 0; 40302230fcSDavid Hildenbrand 41302230fcSDavid Hildenbrand if (!tcg_enabled()) { 42302230fcSDavid Hildenbrand /* handled in KVM */ 43302230fcSDavid Hildenbrand set_sigp_status(si, SIGP_STAT_INVALID_ORDER); 44302230fcSDavid Hildenbrand return; 45302230fcSDavid Hildenbrand } 46302230fcSDavid Hildenbrand 47302230fcSDavid Hildenbrand /* sensing without locks is racy, but it's the same for real hw */ 48302230fcSDavid Hildenbrand if (state != CPU_STATE_STOPPED && !ext_call) { 49302230fcSDavid Hildenbrand si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; 50302230fcSDavid Hildenbrand } else { 51302230fcSDavid Hildenbrand if (ext_call) { 52302230fcSDavid Hildenbrand status |= SIGP_STAT_EXT_CALL_PENDING; 53302230fcSDavid Hildenbrand } 54302230fcSDavid Hildenbrand if (state == CPU_STATE_STOPPED) { 55302230fcSDavid Hildenbrand status |= SIGP_STAT_STOPPED; 56302230fcSDavid Hildenbrand } 57302230fcSDavid Hildenbrand set_sigp_status(si, status); 58302230fcSDavid Hildenbrand } 59302230fcSDavid Hildenbrand } 60302230fcSDavid Hildenbrand 61070aa1a4SDavid Hildenbrand static void sigp_external_call(S390CPU *src_cpu, S390CPU *dst_cpu, SigpInfo *si) 62070aa1a4SDavid Hildenbrand { 63070aa1a4SDavid Hildenbrand int ret; 64070aa1a4SDavid Hildenbrand 65070aa1a4SDavid Hildenbrand if (!tcg_enabled()) { 66070aa1a4SDavid Hildenbrand /* handled in KVM */ 67070aa1a4SDavid Hildenbrand set_sigp_status(si, SIGP_STAT_INVALID_ORDER); 68070aa1a4SDavid Hildenbrand return; 69070aa1a4SDavid Hildenbrand } 70070aa1a4SDavid Hildenbrand 71070aa1a4SDavid Hildenbrand ret = cpu_inject_external_call(dst_cpu, src_cpu->env.core_id); 72070aa1a4SDavid Hildenbrand if (!ret) { 73070aa1a4SDavid Hildenbrand si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; 74070aa1a4SDavid Hildenbrand } else { 75070aa1a4SDavid Hildenbrand set_sigp_status(si, SIGP_STAT_EXT_CALL_PENDING); 76070aa1a4SDavid Hildenbrand } 77070aa1a4SDavid Hildenbrand } 78070aa1a4SDavid Hildenbrand 79c50105d4SDavid Hildenbrand static void sigp_emergency(S390CPU *src_cpu, S390CPU *dst_cpu, SigpInfo *si) 80c50105d4SDavid Hildenbrand { 81c50105d4SDavid Hildenbrand if (!tcg_enabled()) { 82c50105d4SDavid Hildenbrand /* handled in KVM */ 83c50105d4SDavid Hildenbrand set_sigp_status(si, SIGP_STAT_INVALID_ORDER); 84c50105d4SDavid Hildenbrand return; 85c50105d4SDavid Hildenbrand } 86c50105d4SDavid Hildenbrand 87c50105d4SDavid Hildenbrand cpu_inject_emergency_signal(dst_cpu, src_cpu->env.core_id); 88c50105d4SDavid Hildenbrand si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; 89c50105d4SDavid Hildenbrand } 90c50105d4SDavid Hildenbrand 9174b4c74dSDavid Hildenbrand static void sigp_start(CPUState *cs, run_on_cpu_data arg) 9274b4c74dSDavid Hildenbrand { 9374b4c74dSDavid Hildenbrand S390CPU *cpu = S390_CPU(cs); 9474b4c74dSDavid Hildenbrand SigpInfo *si = arg.host_ptr; 9574b4c74dSDavid Hildenbrand 9674b4c74dSDavid Hildenbrand if (s390_cpu_get_state(cpu) != CPU_STATE_STOPPED) { 9774b4c74dSDavid Hildenbrand si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; 9874b4c74dSDavid Hildenbrand return; 9974b4c74dSDavid Hildenbrand } 10074b4c74dSDavid Hildenbrand 10174b4c74dSDavid Hildenbrand s390_cpu_set_state(CPU_STATE_OPERATING, cpu); 10274b4c74dSDavid Hildenbrand si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; 10374b4c74dSDavid Hildenbrand } 10474b4c74dSDavid Hildenbrand 10574b4c74dSDavid Hildenbrand static void sigp_stop(CPUState *cs, run_on_cpu_data arg) 10674b4c74dSDavid Hildenbrand { 10774b4c74dSDavid Hildenbrand S390CPU *cpu = S390_CPU(cs); 10874b4c74dSDavid Hildenbrand SigpInfo *si = arg.host_ptr; 10974b4c74dSDavid Hildenbrand 11074b4c74dSDavid Hildenbrand if (s390_cpu_get_state(cpu) != CPU_STATE_OPERATING) { 11174b4c74dSDavid Hildenbrand si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; 11274b4c74dSDavid Hildenbrand return; 11374b4c74dSDavid Hildenbrand } 11474b4c74dSDavid Hildenbrand 11574b4c74dSDavid Hildenbrand /* disabled wait - sleeping in user space */ 11674b4c74dSDavid Hildenbrand if (cs->halted) { 11774b4c74dSDavid Hildenbrand s390_cpu_set_state(CPU_STATE_STOPPED, cpu); 11874b4c74dSDavid Hildenbrand } else { 11974b4c74dSDavid Hildenbrand /* execute the stop function */ 12074b4c74dSDavid Hildenbrand cpu->env.sigp_order = SIGP_STOP; 12174b4c74dSDavid Hildenbrand cpu_inject_stop(cpu); 12274b4c74dSDavid Hildenbrand } 12374b4c74dSDavid Hildenbrand si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; 12474b4c74dSDavid Hildenbrand } 12574b4c74dSDavid Hildenbrand 12674b4c74dSDavid Hildenbrand static void sigp_stop_and_store_status(CPUState *cs, run_on_cpu_data arg) 12774b4c74dSDavid Hildenbrand { 12874b4c74dSDavid Hildenbrand S390CPU *cpu = S390_CPU(cs); 12974b4c74dSDavid Hildenbrand SigpInfo *si = arg.host_ptr; 13074b4c74dSDavid Hildenbrand 13174b4c74dSDavid Hildenbrand /* disabled wait - sleeping in user space */ 13274b4c74dSDavid Hildenbrand if (s390_cpu_get_state(cpu) == CPU_STATE_OPERATING && cs->halted) { 13374b4c74dSDavid Hildenbrand s390_cpu_set_state(CPU_STATE_STOPPED, cpu); 13474b4c74dSDavid Hildenbrand } 13574b4c74dSDavid Hildenbrand 13674b4c74dSDavid Hildenbrand switch (s390_cpu_get_state(cpu)) { 13774b4c74dSDavid Hildenbrand case CPU_STATE_OPERATING: 13874b4c74dSDavid Hildenbrand cpu->env.sigp_order = SIGP_STOP_STORE_STATUS; 13974b4c74dSDavid Hildenbrand cpu_inject_stop(cpu); 1403047f8b5SDavid Hildenbrand /* store will be performed in do_stop_interrup() */ 14174b4c74dSDavid Hildenbrand break; 14274b4c74dSDavid Hildenbrand case CPU_STATE_STOPPED: 14374b4c74dSDavid Hildenbrand /* already stopped, just store the status */ 14474b4c74dSDavid Hildenbrand cpu_synchronize_state(cs); 14574b4c74dSDavid Hildenbrand s390_store_status(cpu, S390_STORE_STATUS_DEF_ADDR, true); 14674b4c74dSDavid Hildenbrand break; 14774b4c74dSDavid Hildenbrand } 14874b4c74dSDavid Hildenbrand si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; 14974b4c74dSDavid Hildenbrand } 15074b4c74dSDavid Hildenbrand 15174b4c74dSDavid Hildenbrand static void sigp_store_status_at_address(CPUState *cs, run_on_cpu_data arg) 15274b4c74dSDavid Hildenbrand { 15374b4c74dSDavid Hildenbrand S390CPU *cpu = S390_CPU(cs); 15474b4c74dSDavid Hildenbrand SigpInfo *si = arg.host_ptr; 15574b4c74dSDavid Hildenbrand uint32_t address = si->param & 0x7ffffe00u; 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 cpu_synchronize_state(cs); 16474b4c74dSDavid Hildenbrand 16574b4c74dSDavid Hildenbrand if (s390_store_status(cpu, address, false)) { 16674b4c74dSDavid Hildenbrand set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER); 16774b4c74dSDavid Hildenbrand return; 16874b4c74dSDavid Hildenbrand } 16974b4c74dSDavid Hildenbrand si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; 17074b4c74dSDavid Hildenbrand } 17174b4c74dSDavid Hildenbrand 17274b4c74dSDavid Hildenbrand #define ADTL_SAVE_LC_MASK 0xfUL 17374b4c74dSDavid Hildenbrand static void sigp_store_adtl_status(CPUState *cs, run_on_cpu_data arg) 17474b4c74dSDavid Hildenbrand { 17574b4c74dSDavid Hildenbrand S390CPU *cpu = S390_CPU(cs); 17674b4c74dSDavid Hildenbrand SigpInfo *si = arg.host_ptr; 17774b4c74dSDavid Hildenbrand uint8_t lc = si->param & ADTL_SAVE_LC_MASK; 17874b4c74dSDavid Hildenbrand hwaddr addr = si->param & ~ADTL_SAVE_LC_MASK; 17974b4c74dSDavid Hildenbrand hwaddr len = 1UL << (lc ? lc : 10); 18074b4c74dSDavid Hildenbrand 18174b4c74dSDavid Hildenbrand if (!s390_has_feat(S390_FEAT_VECTOR) && 18274b4c74dSDavid Hildenbrand !s390_has_feat(S390_FEAT_GUARDED_STORAGE)) { 18374b4c74dSDavid Hildenbrand set_sigp_status(si, SIGP_STAT_INVALID_ORDER); 18474b4c74dSDavid Hildenbrand return; 18574b4c74dSDavid Hildenbrand } 18674b4c74dSDavid Hildenbrand 18774b4c74dSDavid Hildenbrand /* cpu has to be stopped */ 18874b4c74dSDavid Hildenbrand if (s390_cpu_get_state(cpu) != CPU_STATE_STOPPED) { 18974b4c74dSDavid Hildenbrand set_sigp_status(si, SIGP_STAT_INCORRECT_STATE); 19074b4c74dSDavid Hildenbrand return; 19174b4c74dSDavid Hildenbrand } 19274b4c74dSDavid Hildenbrand 19374b4c74dSDavid Hildenbrand /* address must be aligned to length */ 19474b4c74dSDavid Hildenbrand if (addr & (len - 1)) { 19574b4c74dSDavid Hildenbrand set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER); 19674b4c74dSDavid Hildenbrand return; 19774b4c74dSDavid Hildenbrand } 19874b4c74dSDavid Hildenbrand 19974b4c74dSDavid Hildenbrand /* no GS: only lc == 0 is valid */ 20074b4c74dSDavid Hildenbrand if (!s390_has_feat(S390_FEAT_GUARDED_STORAGE) && 20174b4c74dSDavid Hildenbrand lc != 0) { 20274b4c74dSDavid Hildenbrand set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER); 20374b4c74dSDavid Hildenbrand return; 20474b4c74dSDavid Hildenbrand } 20574b4c74dSDavid Hildenbrand 20674b4c74dSDavid Hildenbrand /* GS: 0, 10, 11, 12 are valid */ 20774b4c74dSDavid Hildenbrand if (s390_has_feat(S390_FEAT_GUARDED_STORAGE) && 20874b4c74dSDavid Hildenbrand lc != 0 && 20974b4c74dSDavid Hildenbrand lc != 10 && 21074b4c74dSDavid Hildenbrand lc != 11 && 21174b4c74dSDavid Hildenbrand lc != 12) { 21274b4c74dSDavid Hildenbrand set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER); 21374b4c74dSDavid Hildenbrand return; 21474b4c74dSDavid Hildenbrand } 21574b4c74dSDavid Hildenbrand 21674b4c74dSDavid Hildenbrand cpu_synchronize_state(cs); 21774b4c74dSDavid Hildenbrand 21874b4c74dSDavid Hildenbrand if (s390_store_adtl_status(cpu, addr, len)) { 21974b4c74dSDavid Hildenbrand set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER); 22074b4c74dSDavid Hildenbrand return; 22174b4c74dSDavid Hildenbrand } 22274b4c74dSDavid Hildenbrand si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; 22374b4c74dSDavid Hildenbrand } 22474b4c74dSDavid Hildenbrand 22574b4c74dSDavid Hildenbrand static void sigp_restart(CPUState *cs, run_on_cpu_data arg) 22674b4c74dSDavid Hildenbrand { 22774b4c74dSDavid Hildenbrand S390CPU *cpu = S390_CPU(cs); 22874b4c74dSDavid Hildenbrand SigpInfo *si = arg.host_ptr; 22974b4c74dSDavid Hildenbrand 23074b4c74dSDavid Hildenbrand switch (s390_cpu_get_state(cpu)) { 23174b4c74dSDavid Hildenbrand case CPU_STATE_STOPPED: 23274b4c74dSDavid Hildenbrand /* the restart irq has to be delivered prior to any other pending irq */ 23374b4c74dSDavid Hildenbrand cpu_synchronize_state(cs); 23474b4c74dSDavid Hildenbrand do_restart_interrupt(&cpu->env); 23574b4c74dSDavid Hildenbrand s390_cpu_set_state(CPU_STATE_OPERATING, cpu); 23674b4c74dSDavid Hildenbrand break; 23774b4c74dSDavid Hildenbrand case CPU_STATE_OPERATING: 23874b4c74dSDavid Hildenbrand cpu_inject_restart(cpu); 23974b4c74dSDavid Hildenbrand break; 24074b4c74dSDavid Hildenbrand } 24174b4c74dSDavid Hildenbrand si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; 24274b4c74dSDavid Hildenbrand } 24374b4c74dSDavid Hildenbrand 24474b4c74dSDavid Hildenbrand static void sigp_initial_cpu_reset(CPUState *cs, run_on_cpu_data arg) 24574b4c74dSDavid Hildenbrand { 24674b4c74dSDavid Hildenbrand S390CPU *cpu = S390_CPU(cs); 24774b4c74dSDavid Hildenbrand S390CPUClass *scc = S390_CPU_GET_CLASS(cpu); 24874b4c74dSDavid Hildenbrand SigpInfo *si = arg.host_ptr; 24974b4c74dSDavid Hildenbrand 25074b4c74dSDavid Hildenbrand cpu_synchronize_state(cs); 25174b4c74dSDavid Hildenbrand scc->initial_cpu_reset(cs); 25274b4c74dSDavid Hildenbrand cpu_synchronize_post_reset(cs); 25374b4c74dSDavid Hildenbrand si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; 25474b4c74dSDavid Hildenbrand } 25574b4c74dSDavid Hildenbrand 25674b4c74dSDavid Hildenbrand static void sigp_cpu_reset(CPUState *cs, run_on_cpu_data arg) 25774b4c74dSDavid Hildenbrand { 25874b4c74dSDavid Hildenbrand S390CPU *cpu = S390_CPU(cs); 25974b4c74dSDavid Hildenbrand S390CPUClass *scc = S390_CPU_GET_CLASS(cpu); 26074b4c74dSDavid Hildenbrand SigpInfo *si = arg.host_ptr; 26174b4c74dSDavid Hildenbrand 26274b4c74dSDavid Hildenbrand cpu_synchronize_state(cs); 26374b4c74dSDavid Hildenbrand scc->cpu_reset(cs); 26474b4c74dSDavid Hildenbrand cpu_synchronize_post_reset(cs); 26574b4c74dSDavid Hildenbrand si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; 26674b4c74dSDavid Hildenbrand } 26774b4c74dSDavid Hildenbrand 26874b4c74dSDavid Hildenbrand static void sigp_set_prefix(CPUState *cs, run_on_cpu_data arg) 26974b4c74dSDavid Hildenbrand { 27074b4c74dSDavid Hildenbrand S390CPU *cpu = S390_CPU(cs); 27174b4c74dSDavid Hildenbrand SigpInfo *si = arg.host_ptr; 27274b4c74dSDavid Hildenbrand uint32_t addr = si->param & 0x7fffe000u; 27374b4c74dSDavid Hildenbrand 27474b4c74dSDavid Hildenbrand cpu_synchronize_state(cs); 27574b4c74dSDavid Hildenbrand 27674b4c74dSDavid Hildenbrand if (!address_space_access_valid(&address_space_memory, addr, 27774b4c74dSDavid Hildenbrand sizeof(struct LowCore), false)) { 27874b4c74dSDavid Hildenbrand set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER); 27974b4c74dSDavid Hildenbrand return; 28074b4c74dSDavid Hildenbrand } 28174b4c74dSDavid Hildenbrand 28274b4c74dSDavid Hildenbrand /* cpu has to be stopped */ 28374b4c74dSDavid Hildenbrand if (s390_cpu_get_state(cpu) != CPU_STATE_STOPPED) { 28474b4c74dSDavid Hildenbrand set_sigp_status(si, SIGP_STAT_INCORRECT_STATE); 28574b4c74dSDavid Hildenbrand return; 28674b4c74dSDavid Hildenbrand } 28774b4c74dSDavid Hildenbrand 28874b4c74dSDavid Hildenbrand cpu->env.psa = addr; 28974b4c74dSDavid Hildenbrand cpu_synchronize_post_init(cs); 29074b4c74dSDavid Hildenbrand si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; 29174b4c74dSDavid Hildenbrand } 29274b4c74dSDavid Hildenbrand 293*a6880d21SDavid Hildenbrand static void sigp_cond_emergency(S390CPU *src_cpu, S390CPU *dst_cpu, 294*a6880d21SDavid Hildenbrand SigpInfo *si) 295*a6880d21SDavid Hildenbrand { 296*a6880d21SDavid Hildenbrand const uint64_t psw_int_mask = PSW_MASK_IO | PSW_MASK_EXT; 297*a6880d21SDavid Hildenbrand uint16_t p_asn, s_asn, asn; 298*a6880d21SDavid Hildenbrand uint64_t psw_addr, psw_mask; 299*a6880d21SDavid Hildenbrand bool idle; 300*a6880d21SDavid Hildenbrand 301*a6880d21SDavid Hildenbrand if (!tcg_enabled()) { 302*a6880d21SDavid Hildenbrand /* handled in KVM */ 303*a6880d21SDavid Hildenbrand set_sigp_status(si, SIGP_STAT_INVALID_ORDER); 304*a6880d21SDavid Hildenbrand return; 305*a6880d21SDavid Hildenbrand } 306*a6880d21SDavid Hildenbrand 307*a6880d21SDavid Hildenbrand /* this looks racy, but these values are only used when STOPPED */ 308*a6880d21SDavid Hildenbrand idle = CPU(dst_cpu)->halted; 309*a6880d21SDavid Hildenbrand psw_addr = dst_cpu->env.psw.addr; 310*a6880d21SDavid Hildenbrand psw_mask = dst_cpu->env.psw.mask; 311*a6880d21SDavid Hildenbrand asn = si->param; 312*a6880d21SDavid Hildenbrand p_asn = dst_cpu->env.cregs[4] & 0xffff; /* Primary ASN */ 313*a6880d21SDavid Hildenbrand s_asn = dst_cpu->env.cregs[3] & 0xffff; /* Secondary ASN */ 314*a6880d21SDavid Hildenbrand 315*a6880d21SDavid Hildenbrand if (s390_cpu_get_state(dst_cpu) != CPU_STATE_STOPPED || 316*a6880d21SDavid Hildenbrand (psw_mask & psw_int_mask) != psw_int_mask || 317*a6880d21SDavid Hildenbrand (idle && psw_addr != 0) || 318*a6880d21SDavid Hildenbrand (!idle && (asn == p_asn || asn == s_asn))) { 319*a6880d21SDavid Hildenbrand cpu_inject_emergency_signal(dst_cpu, src_cpu->env.core_id); 320*a6880d21SDavid Hildenbrand } else { 321*a6880d21SDavid Hildenbrand set_sigp_status(si, SIGP_STAT_INCORRECT_STATE); 322*a6880d21SDavid Hildenbrand } 323*a6880d21SDavid Hildenbrand 324*a6880d21SDavid Hildenbrand si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; 325*a6880d21SDavid Hildenbrand } 326*a6880d21SDavid Hildenbrand 327d1b468bcSDavid Hildenbrand static void sigp_sense_running(S390CPU *dst_cpu, SigpInfo *si) 328d1b468bcSDavid Hildenbrand { 329d1b468bcSDavid Hildenbrand if (!tcg_enabled()) { 330d1b468bcSDavid Hildenbrand /* handled in KVM */ 331d1b468bcSDavid Hildenbrand set_sigp_status(si, SIGP_STAT_INVALID_ORDER); 332d1b468bcSDavid Hildenbrand return; 333d1b468bcSDavid Hildenbrand } 334d1b468bcSDavid Hildenbrand 335d1b468bcSDavid Hildenbrand /* sensing without locks is racy, but it's the same for real hw */ 336d1b468bcSDavid Hildenbrand if (!s390_has_feat(S390_FEAT_SENSE_RUNNING_STATUS)) { 337d1b468bcSDavid Hildenbrand set_sigp_status(si, SIGP_STAT_INVALID_ORDER); 338d1b468bcSDavid Hildenbrand return; 339d1b468bcSDavid Hildenbrand } 340d1b468bcSDavid Hildenbrand 341d1b468bcSDavid Hildenbrand /* If halted (which includes also STOPPED), it is not running */ 342d1b468bcSDavid Hildenbrand if (CPU(dst_cpu)->halted) { 343d1b468bcSDavid Hildenbrand si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; 344d1b468bcSDavid Hildenbrand } else { 345d1b468bcSDavid Hildenbrand set_sigp_status(si, SIGP_STAT_NOT_RUNNING); 346d1b468bcSDavid Hildenbrand } 347d1b468bcSDavid Hildenbrand } 348d1b468bcSDavid Hildenbrand 349070aa1a4SDavid Hildenbrand static int handle_sigp_single_dst(S390CPU *cpu, S390CPU *dst_cpu, uint8_t order, 35074b4c74dSDavid Hildenbrand uint64_t param, uint64_t *status_reg) 35174b4c74dSDavid Hildenbrand { 35274b4c74dSDavid Hildenbrand SigpInfo si = { 35374b4c74dSDavid Hildenbrand .param = param, 35474b4c74dSDavid Hildenbrand .status_reg = status_reg, 35574b4c74dSDavid Hildenbrand }; 35674b4c74dSDavid Hildenbrand 35774b4c74dSDavid Hildenbrand /* cpu available? */ 35874b4c74dSDavid Hildenbrand if (dst_cpu == NULL) { 35974b4c74dSDavid Hildenbrand return SIGP_CC_NOT_OPERATIONAL; 36074b4c74dSDavid Hildenbrand } 36174b4c74dSDavid Hildenbrand 36274b4c74dSDavid Hildenbrand /* only resets can break pending orders */ 36374b4c74dSDavid Hildenbrand if (dst_cpu->env.sigp_order != 0 && 36474b4c74dSDavid Hildenbrand order != SIGP_CPU_RESET && 36574b4c74dSDavid Hildenbrand order != SIGP_INITIAL_CPU_RESET) { 36674b4c74dSDavid Hildenbrand return SIGP_CC_BUSY; 36774b4c74dSDavid Hildenbrand } 36874b4c74dSDavid Hildenbrand 36974b4c74dSDavid Hildenbrand switch (order) { 370302230fcSDavid Hildenbrand case SIGP_SENSE: 371302230fcSDavid Hildenbrand sigp_sense(dst_cpu, &si); 372302230fcSDavid Hildenbrand break; 373070aa1a4SDavid Hildenbrand case SIGP_EXTERNAL_CALL: 374070aa1a4SDavid Hildenbrand sigp_external_call(cpu, dst_cpu, &si); 375070aa1a4SDavid Hildenbrand break; 376c50105d4SDavid Hildenbrand case SIGP_EMERGENCY: 377c50105d4SDavid Hildenbrand sigp_emergency(cpu, dst_cpu, &si); 378c50105d4SDavid Hildenbrand break; 37974b4c74dSDavid Hildenbrand case SIGP_START: 38074b4c74dSDavid Hildenbrand run_on_cpu(CPU(dst_cpu), sigp_start, RUN_ON_CPU_HOST_PTR(&si)); 38174b4c74dSDavid Hildenbrand break; 38274b4c74dSDavid Hildenbrand case SIGP_STOP: 38374b4c74dSDavid Hildenbrand run_on_cpu(CPU(dst_cpu), sigp_stop, RUN_ON_CPU_HOST_PTR(&si)); 38474b4c74dSDavid Hildenbrand break; 38574b4c74dSDavid Hildenbrand case SIGP_RESTART: 38674b4c74dSDavid Hildenbrand run_on_cpu(CPU(dst_cpu), sigp_restart, RUN_ON_CPU_HOST_PTR(&si)); 38774b4c74dSDavid Hildenbrand break; 38874b4c74dSDavid Hildenbrand case SIGP_STOP_STORE_STATUS: 38974b4c74dSDavid Hildenbrand run_on_cpu(CPU(dst_cpu), sigp_stop_and_store_status, RUN_ON_CPU_HOST_PTR(&si)); 39074b4c74dSDavid Hildenbrand break; 39174b4c74dSDavid Hildenbrand case SIGP_STORE_STATUS_ADDR: 39274b4c74dSDavid Hildenbrand run_on_cpu(CPU(dst_cpu), sigp_store_status_at_address, RUN_ON_CPU_HOST_PTR(&si)); 39374b4c74dSDavid Hildenbrand break; 39474b4c74dSDavid Hildenbrand case SIGP_STORE_ADTL_STATUS: 39574b4c74dSDavid Hildenbrand run_on_cpu(CPU(dst_cpu), sigp_store_adtl_status, RUN_ON_CPU_HOST_PTR(&si)); 39674b4c74dSDavid Hildenbrand break; 39774b4c74dSDavid Hildenbrand case SIGP_SET_PREFIX: 39874b4c74dSDavid Hildenbrand run_on_cpu(CPU(dst_cpu), sigp_set_prefix, RUN_ON_CPU_HOST_PTR(&si)); 39974b4c74dSDavid Hildenbrand break; 40074b4c74dSDavid Hildenbrand case SIGP_INITIAL_CPU_RESET: 40174b4c74dSDavid Hildenbrand run_on_cpu(CPU(dst_cpu), sigp_initial_cpu_reset, RUN_ON_CPU_HOST_PTR(&si)); 40274b4c74dSDavid Hildenbrand break; 40374b4c74dSDavid Hildenbrand case SIGP_CPU_RESET: 40474b4c74dSDavid Hildenbrand run_on_cpu(CPU(dst_cpu), sigp_cpu_reset, RUN_ON_CPU_HOST_PTR(&si)); 40574b4c74dSDavid Hildenbrand break; 406*a6880d21SDavid Hildenbrand case SIGP_COND_EMERGENCY: 407*a6880d21SDavid Hildenbrand sigp_cond_emergency(cpu, dst_cpu, &si); 408*a6880d21SDavid Hildenbrand break; 409d1b468bcSDavid Hildenbrand case SIGP_SENSE_RUNNING: 410d1b468bcSDavid Hildenbrand sigp_sense_running(dst_cpu, &si); 411d1b468bcSDavid Hildenbrand break; 41274b4c74dSDavid Hildenbrand default: 41374b4c74dSDavid Hildenbrand set_sigp_status(&si, SIGP_STAT_INVALID_ORDER); 41474b4c74dSDavid Hildenbrand } 41574b4c74dSDavid Hildenbrand 41674b4c74dSDavid Hildenbrand return si.cc; 41774b4c74dSDavid Hildenbrand } 41874b4c74dSDavid Hildenbrand 41974b4c74dSDavid Hildenbrand static int sigp_set_architecture(S390CPU *cpu, uint32_t param, 42074b4c74dSDavid Hildenbrand uint64_t *status_reg) 42174b4c74dSDavid Hildenbrand { 42274b4c74dSDavid Hildenbrand CPUState *cur_cs; 42374b4c74dSDavid Hildenbrand S390CPU *cur_cpu; 42474b4c74dSDavid Hildenbrand bool all_stopped = true; 42574b4c74dSDavid Hildenbrand 42674b4c74dSDavid Hildenbrand CPU_FOREACH(cur_cs) { 42774b4c74dSDavid Hildenbrand cur_cpu = S390_CPU(cur_cs); 42874b4c74dSDavid Hildenbrand 42974b4c74dSDavid Hildenbrand if (cur_cpu == cpu) { 43074b4c74dSDavid Hildenbrand continue; 43174b4c74dSDavid Hildenbrand } 43274b4c74dSDavid Hildenbrand if (s390_cpu_get_state(cur_cpu) != CPU_STATE_STOPPED) { 43374b4c74dSDavid Hildenbrand all_stopped = false; 43474b4c74dSDavid Hildenbrand } 43574b4c74dSDavid Hildenbrand } 43674b4c74dSDavid Hildenbrand 43774b4c74dSDavid Hildenbrand *status_reg &= 0xffffffff00000000ULL; 43874b4c74dSDavid Hildenbrand 43974b4c74dSDavid Hildenbrand /* Reject set arch order, with czam we're always in z/Arch mode. */ 44074b4c74dSDavid Hildenbrand *status_reg |= (all_stopped ? SIGP_STAT_INVALID_PARAMETER : 44174b4c74dSDavid Hildenbrand SIGP_STAT_INCORRECT_STATE); 44274b4c74dSDavid Hildenbrand return SIGP_CC_STATUS_STORED; 44374b4c74dSDavid Hildenbrand } 44474b4c74dSDavid Hildenbrand 44574b4c74dSDavid Hildenbrand int handle_sigp(CPUS390XState *env, uint8_t order, uint64_t r1, uint64_t r3) 44674b4c74dSDavid Hildenbrand { 44774b4c74dSDavid Hildenbrand uint64_t *status_reg = &env->regs[r1]; 44874b4c74dSDavid Hildenbrand uint64_t param = (r1 % 2) ? env->regs[r1] : env->regs[r1 + 1]; 44974b4c74dSDavid Hildenbrand S390CPU *cpu = s390_env_get_cpu(env); 45074b4c74dSDavid Hildenbrand S390CPU *dst_cpu = NULL; 45174b4c74dSDavid Hildenbrand int ret; 45274b4c74dSDavid Hildenbrand 45374b4c74dSDavid Hildenbrand if (qemu_mutex_trylock(&qemu_sigp_mutex)) { 45474b4c74dSDavid Hildenbrand ret = SIGP_CC_BUSY; 45574b4c74dSDavid Hildenbrand goto out; 45674b4c74dSDavid Hildenbrand } 45774b4c74dSDavid Hildenbrand 45874b4c74dSDavid Hildenbrand switch (order) { 45974b4c74dSDavid Hildenbrand case SIGP_SET_ARCH: 46074b4c74dSDavid Hildenbrand ret = sigp_set_architecture(cpu, param, status_reg); 46174b4c74dSDavid Hildenbrand break; 46274b4c74dSDavid Hildenbrand default: 46374b4c74dSDavid Hildenbrand /* all other sigp orders target a single vcpu */ 46474b4c74dSDavid Hildenbrand dst_cpu = s390_cpu_addr2state(env->regs[r3]); 465070aa1a4SDavid Hildenbrand ret = handle_sigp_single_dst(cpu, dst_cpu, order, param, status_reg); 46674b4c74dSDavid Hildenbrand } 46774b4c74dSDavid Hildenbrand qemu_mutex_unlock(&qemu_sigp_mutex); 46874b4c74dSDavid Hildenbrand 46974b4c74dSDavid Hildenbrand out: 47074b4c74dSDavid Hildenbrand trace_sigp_finished(order, CPU(cpu)->cpu_index, 47174b4c74dSDavid Hildenbrand dst_cpu ? CPU(dst_cpu)->cpu_index : -1, ret); 47274b4c74dSDavid Hildenbrand g_assert(ret >= 0); 47374b4c74dSDavid Hildenbrand 47474b4c74dSDavid Hildenbrand return ret; 47574b4c74dSDavid Hildenbrand } 47674b4c74dSDavid Hildenbrand 47774b4c74dSDavid Hildenbrand int s390_cpu_restart(S390CPU *cpu) 47874b4c74dSDavid Hildenbrand { 47974b4c74dSDavid Hildenbrand SigpInfo si = {}; 48074b4c74dSDavid Hildenbrand 48174b4c74dSDavid Hildenbrand if (tcg_enabled()) { 48274b4c74dSDavid Hildenbrand /* FIXME TCG */ 48374b4c74dSDavid Hildenbrand return -ENOSYS; 48474b4c74dSDavid Hildenbrand } 48574b4c74dSDavid Hildenbrand 48674b4c74dSDavid Hildenbrand run_on_cpu(CPU(cpu), sigp_restart, RUN_ON_CPU_HOST_PTR(&si)); 48774b4c74dSDavid Hildenbrand return 0; 48874b4c74dSDavid Hildenbrand } 48974b4c74dSDavid Hildenbrand 4903047f8b5SDavid Hildenbrand void do_stop_interrupt(CPUS390XState *env) 4913047f8b5SDavid Hildenbrand { 4923047f8b5SDavid Hildenbrand S390CPU *cpu = s390_env_get_cpu(env); 4933047f8b5SDavid Hildenbrand 4943047f8b5SDavid Hildenbrand if (s390_cpu_set_state(CPU_STATE_STOPPED, cpu) == 0) { 4953047f8b5SDavid Hildenbrand qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); 4963047f8b5SDavid Hildenbrand } 4973047f8b5SDavid Hildenbrand if (cpu->env.sigp_order == SIGP_STOP_STORE_STATUS) { 4983047f8b5SDavid Hildenbrand s390_store_status(cpu, S390_STORE_STATUS_DEF_ADDR, true); 4993047f8b5SDavid Hildenbrand } 5003047f8b5SDavid Hildenbrand env->sigp_order = 0; 5013047f8b5SDavid Hildenbrand } 5023047f8b5SDavid Hildenbrand 50374b4c74dSDavid Hildenbrand void s390_init_sigp(void) 50474b4c74dSDavid Hildenbrand { 50574b4c74dSDavid Hildenbrand qemu_mutex_init(&qemu_sigp_mutex); 50674b4c74dSDavid Hildenbrand } 507