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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 50974b4c74dSDavid Hildenbrand void s390_init_sigp(void) 51074b4c74dSDavid Hildenbrand { 51174b4c74dSDavid Hildenbrand qemu_mutex_init(&qemu_sigp_mutex); 51274b4c74dSDavid Hildenbrand } 513