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 35*302230fcSDavid Hildenbrand static void sigp_sense(S390CPU *dst_cpu, SigpInfo *si) 36*302230fcSDavid Hildenbrand { 37*302230fcSDavid Hildenbrand uint8_t state = s390_cpu_get_state(dst_cpu); 38*302230fcSDavid Hildenbrand bool ext_call = dst_cpu->env.pending_int & INTERRUPT_EXTERNAL_CALL; 39*302230fcSDavid Hildenbrand uint64_t status = 0; 40*302230fcSDavid Hildenbrand 41*302230fcSDavid Hildenbrand if (!tcg_enabled()) { 42*302230fcSDavid Hildenbrand /* handled in KVM */ 43*302230fcSDavid Hildenbrand set_sigp_status(si, SIGP_STAT_INVALID_ORDER); 44*302230fcSDavid Hildenbrand return; 45*302230fcSDavid Hildenbrand } 46*302230fcSDavid Hildenbrand 47*302230fcSDavid Hildenbrand /* sensing without locks is racy, but it's the same for real hw */ 48*302230fcSDavid Hildenbrand if (state != CPU_STATE_STOPPED && !ext_call) { 49*302230fcSDavid Hildenbrand si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; 50*302230fcSDavid Hildenbrand } else { 51*302230fcSDavid Hildenbrand if (ext_call) { 52*302230fcSDavid Hildenbrand status |= SIGP_STAT_EXT_CALL_PENDING; 53*302230fcSDavid Hildenbrand } 54*302230fcSDavid Hildenbrand if (state == CPU_STATE_STOPPED) { 55*302230fcSDavid Hildenbrand status |= SIGP_STAT_STOPPED; 56*302230fcSDavid Hildenbrand } 57*302230fcSDavid Hildenbrand set_sigp_status(si, status); 58*302230fcSDavid Hildenbrand } 59*302230fcSDavid Hildenbrand } 60*302230fcSDavid Hildenbrand 6174b4c74dSDavid Hildenbrand static void sigp_start(CPUState *cs, run_on_cpu_data arg) 6274b4c74dSDavid Hildenbrand { 6374b4c74dSDavid Hildenbrand S390CPU *cpu = S390_CPU(cs); 6474b4c74dSDavid Hildenbrand SigpInfo *si = arg.host_ptr; 6574b4c74dSDavid Hildenbrand 6674b4c74dSDavid Hildenbrand if (s390_cpu_get_state(cpu) != CPU_STATE_STOPPED) { 6774b4c74dSDavid Hildenbrand si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; 6874b4c74dSDavid Hildenbrand return; 6974b4c74dSDavid Hildenbrand } 7074b4c74dSDavid Hildenbrand 7174b4c74dSDavid Hildenbrand s390_cpu_set_state(CPU_STATE_OPERATING, cpu); 7274b4c74dSDavid Hildenbrand si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; 7374b4c74dSDavid Hildenbrand } 7474b4c74dSDavid Hildenbrand 7574b4c74dSDavid Hildenbrand static void sigp_stop(CPUState *cs, run_on_cpu_data arg) 7674b4c74dSDavid Hildenbrand { 7774b4c74dSDavid Hildenbrand S390CPU *cpu = S390_CPU(cs); 7874b4c74dSDavid Hildenbrand SigpInfo *si = arg.host_ptr; 7974b4c74dSDavid Hildenbrand 8074b4c74dSDavid Hildenbrand if (s390_cpu_get_state(cpu) != CPU_STATE_OPERATING) { 8174b4c74dSDavid Hildenbrand si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; 8274b4c74dSDavid Hildenbrand return; 8374b4c74dSDavid Hildenbrand } 8474b4c74dSDavid Hildenbrand 8574b4c74dSDavid Hildenbrand /* disabled wait - sleeping in user space */ 8674b4c74dSDavid Hildenbrand if (cs->halted) { 8774b4c74dSDavid Hildenbrand s390_cpu_set_state(CPU_STATE_STOPPED, cpu); 8874b4c74dSDavid Hildenbrand } else { 8974b4c74dSDavid Hildenbrand /* execute the stop function */ 9074b4c74dSDavid Hildenbrand cpu->env.sigp_order = SIGP_STOP; 9174b4c74dSDavid Hildenbrand cpu_inject_stop(cpu); 9274b4c74dSDavid Hildenbrand } 9374b4c74dSDavid Hildenbrand si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; 9474b4c74dSDavid Hildenbrand } 9574b4c74dSDavid Hildenbrand 9674b4c74dSDavid Hildenbrand static void sigp_stop_and_store_status(CPUState *cs, run_on_cpu_data arg) 9774b4c74dSDavid Hildenbrand { 9874b4c74dSDavid Hildenbrand S390CPU *cpu = S390_CPU(cs); 9974b4c74dSDavid Hildenbrand SigpInfo *si = arg.host_ptr; 10074b4c74dSDavid Hildenbrand 10174b4c74dSDavid Hildenbrand /* disabled wait - sleeping in user space */ 10274b4c74dSDavid Hildenbrand if (s390_cpu_get_state(cpu) == CPU_STATE_OPERATING && cs->halted) { 10374b4c74dSDavid Hildenbrand s390_cpu_set_state(CPU_STATE_STOPPED, cpu); 10474b4c74dSDavid Hildenbrand } 10574b4c74dSDavid Hildenbrand 10674b4c74dSDavid Hildenbrand switch (s390_cpu_get_state(cpu)) { 10774b4c74dSDavid Hildenbrand case CPU_STATE_OPERATING: 10874b4c74dSDavid Hildenbrand cpu->env.sigp_order = SIGP_STOP_STORE_STATUS; 10974b4c74dSDavid Hildenbrand cpu_inject_stop(cpu); 1103047f8b5SDavid Hildenbrand /* store will be performed in do_stop_interrup() */ 11174b4c74dSDavid Hildenbrand break; 11274b4c74dSDavid Hildenbrand case CPU_STATE_STOPPED: 11374b4c74dSDavid Hildenbrand /* already stopped, just store the status */ 11474b4c74dSDavid Hildenbrand cpu_synchronize_state(cs); 11574b4c74dSDavid Hildenbrand s390_store_status(cpu, S390_STORE_STATUS_DEF_ADDR, true); 11674b4c74dSDavid Hildenbrand break; 11774b4c74dSDavid Hildenbrand } 11874b4c74dSDavid Hildenbrand si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; 11974b4c74dSDavid Hildenbrand } 12074b4c74dSDavid Hildenbrand 12174b4c74dSDavid Hildenbrand static void sigp_store_status_at_address(CPUState *cs, run_on_cpu_data arg) 12274b4c74dSDavid Hildenbrand { 12374b4c74dSDavid Hildenbrand S390CPU *cpu = S390_CPU(cs); 12474b4c74dSDavid Hildenbrand SigpInfo *si = arg.host_ptr; 12574b4c74dSDavid Hildenbrand uint32_t address = si->param & 0x7ffffe00u; 12674b4c74dSDavid Hildenbrand 12774b4c74dSDavid Hildenbrand /* cpu has to be stopped */ 12874b4c74dSDavid Hildenbrand if (s390_cpu_get_state(cpu) != CPU_STATE_STOPPED) { 12974b4c74dSDavid Hildenbrand set_sigp_status(si, SIGP_STAT_INCORRECT_STATE); 13074b4c74dSDavid Hildenbrand return; 13174b4c74dSDavid Hildenbrand } 13274b4c74dSDavid Hildenbrand 13374b4c74dSDavid Hildenbrand cpu_synchronize_state(cs); 13474b4c74dSDavid Hildenbrand 13574b4c74dSDavid Hildenbrand if (s390_store_status(cpu, address, false)) { 13674b4c74dSDavid Hildenbrand set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER); 13774b4c74dSDavid Hildenbrand return; 13874b4c74dSDavid Hildenbrand } 13974b4c74dSDavid Hildenbrand si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; 14074b4c74dSDavid Hildenbrand } 14174b4c74dSDavid Hildenbrand 14274b4c74dSDavid Hildenbrand #define ADTL_SAVE_LC_MASK 0xfUL 14374b4c74dSDavid Hildenbrand static void sigp_store_adtl_status(CPUState *cs, run_on_cpu_data arg) 14474b4c74dSDavid Hildenbrand { 14574b4c74dSDavid Hildenbrand S390CPU *cpu = S390_CPU(cs); 14674b4c74dSDavid Hildenbrand SigpInfo *si = arg.host_ptr; 14774b4c74dSDavid Hildenbrand uint8_t lc = si->param & ADTL_SAVE_LC_MASK; 14874b4c74dSDavid Hildenbrand hwaddr addr = si->param & ~ADTL_SAVE_LC_MASK; 14974b4c74dSDavid Hildenbrand hwaddr len = 1UL << (lc ? lc : 10); 15074b4c74dSDavid Hildenbrand 15174b4c74dSDavid Hildenbrand if (!s390_has_feat(S390_FEAT_VECTOR) && 15274b4c74dSDavid Hildenbrand !s390_has_feat(S390_FEAT_GUARDED_STORAGE)) { 15374b4c74dSDavid Hildenbrand set_sigp_status(si, SIGP_STAT_INVALID_ORDER); 15474b4c74dSDavid Hildenbrand return; 15574b4c74dSDavid Hildenbrand } 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 /* address must be aligned to length */ 16474b4c74dSDavid Hildenbrand if (addr & (len - 1)) { 16574b4c74dSDavid Hildenbrand set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER); 16674b4c74dSDavid Hildenbrand return; 16774b4c74dSDavid Hildenbrand } 16874b4c74dSDavid Hildenbrand 16974b4c74dSDavid Hildenbrand /* no GS: only lc == 0 is valid */ 17074b4c74dSDavid Hildenbrand if (!s390_has_feat(S390_FEAT_GUARDED_STORAGE) && 17174b4c74dSDavid Hildenbrand lc != 0) { 17274b4c74dSDavid Hildenbrand set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER); 17374b4c74dSDavid Hildenbrand return; 17474b4c74dSDavid Hildenbrand } 17574b4c74dSDavid Hildenbrand 17674b4c74dSDavid Hildenbrand /* GS: 0, 10, 11, 12 are valid */ 17774b4c74dSDavid Hildenbrand if (s390_has_feat(S390_FEAT_GUARDED_STORAGE) && 17874b4c74dSDavid Hildenbrand lc != 0 && 17974b4c74dSDavid Hildenbrand lc != 10 && 18074b4c74dSDavid Hildenbrand lc != 11 && 18174b4c74dSDavid Hildenbrand lc != 12) { 18274b4c74dSDavid Hildenbrand set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER); 18374b4c74dSDavid Hildenbrand return; 18474b4c74dSDavid Hildenbrand } 18574b4c74dSDavid Hildenbrand 18674b4c74dSDavid Hildenbrand cpu_synchronize_state(cs); 18774b4c74dSDavid Hildenbrand 18874b4c74dSDavid Hildenbrand if (s390_store_adtl_status(cpu, addr, len)) { 18974b4c74dSDavid Hildenbrand set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER); 19074b4c74dSDavid Hildenbrand return; 19174b4c74dSDavid Hildenbrand } 19274b4c74dSDavid Hildenbrand si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; 19374b4c74dSDavid Hildenbrand } 19474b4c74dSDavid Hildenbrand 19574b4c74dSDavid Hildenbrand static void sigp_restart(CPUState *cs, run_on_cpu_data arg) 19674b4c74dSDavid Hildenbrand { 19774b4c74dSDavid Hildenbrand S390CPU *cpu = S390_CPU(cs); 19874b4c74dSDavid Hildenbrand SigpInfo *si = arg.host_ptr; 19974b4c74dSDavid Hildenbrand 20074b4c74dSDavid Hildenbrand switch (s390_cpu_get_state(cpu)) { 20174b4c74dSDavid Hildenbrand case CPU_STATE_STOPPED: 20274b4c74dSDavid Hildenbrand /* the restart irq has to be delivered prior to any other pending irq */ 20374b4c74dSDavid Hildenbrand cpu_synchronize_state(cs); 20474b4c74dSDavid Hildenbrand do_restart_interrupt(&cpu->env); 20574b4c74dSDavid Hildenbrand s390_cpu_set_state(CPU_STATE_OPERATING, cpu); 20674b4c74dSDavid Hildenbrand break; 20774b4c74dSDavid Hildenbrand case CPU_STATE_OPERATING: 20874b4c74dSDavid Hildenbrand cpu_inject_restart(cpu); 20974b4c74dSDavid Hildenbrand break; 21074b4c74dSDavid Hildenbrand } 21174b4c74dSDavid Hildenbrand si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; 21274b4c74dSDavid Hildenbrand } 21374b4c74dSDavid Hildenbrand 21474b4c74dSDavid Hildenbrand static void sigp_initial_cpu_reset(CPUState *cs, run_on_cpu_data arg) 21574b4c74dSDavid Hildenbrand { 21674b4c74dSDavid Hildenbrand S390CPU *cpu = S390_CPU(cs); 21774b4c74dSDavid Hildenbrand S390CPUClass *scc = S390_CPU_GET_CLASS(cpu); 21874b4c74dSDavid Hildenbrand SigpInfo *si = arg.host_ptr; 21974b4c74dSDavid Hildenbrand 22074b4c74dSDavid Hildenbrand cpu_synchronize_state(cs); 22174b4c74dSDavid Hildenbrand scc->initial_cpu_reset(cs); 22274b4c74dSDavid Hildenbrand cpu_synchronize_post_reset(cs); 22374b4c74dSDavid Hildenbrand si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; 22474b4c74dSDavid Hildenbrand } 22574b4c74dSDavid Hildenbrand 22674b4c74dSDavid Hildenbrand static void sigp_cpu_reset(CPUState *cs, run_on_cpu_data arg) 22774b4c74dSDavid Hildenbrand { 22874b4c74dSDavid Hildenbrand S390CPU *cpu = S390_CPU(cs); 22974b4c74dSDavid Hildenbrand S390CPUClass *scc = S390_CPU_GET_CLASS(cpu); 23074b4c74dSDavid Hildenbrand SigpInfo *si = arg.host_ptr; 23174b4c74dSDavid Hildenbrand 23274b4c74dSDavid Hildenbrand cpu_synchronize_state(cs); 23374b4c74dSDavid Hildenbrand scc->cpu_reset(cs); 23474b4c74dSDavid Hildenbrand cpu_synchronize_post_reset(cs); 23574b4c74dSDavid Hildenbrand si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; 23674b4c74dSDavid Hildenbrand } 23774b4c74dSDavid Hildenbrand 23874b4c74dSDavid Hildenbrand static void sigp_set_prefix(CPUState *cs, run_on_cpu_data arg) 23974b4c74dSDavid Hildenbrand { 24074b4c74dSDavid Hildenbrand S390CPU *cpu = S390_CPU(cs); 24174b4c74dSDavid Hildenbrand SigpInfo *si = arg.host_ptr; 24274b4c74dSDavid Hildenbrand uint32_t addr = si->param & 0x7fffe000u; 24374b4c74dSDavid Hildenbrand 24474b4c74dSDavid Hildenbrand cpu_synchronize_state(cs); 24574b4c74dSDavid Hildenbrand 24674b4c74dSDavid Hildenbrand if (!address_space_access_valid(&address_space_memory, addr, 24774b4c74dSDavid Hildenbrand sizeof(struct LowCore), false)) { 24874b4c74dSDavid Hildenbrand set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER); 24974b4c74dSDavid Hildenbrand return; 25074b4c74dSDavid Hildenbrand } 25174b4c74dSDavid Hildenbrand 25274b4c74dSDavid Hildenbrand /* cpu has to be stopped */ 25374b4c74dSDavid Hildenbrand if (s390_cpu_get_state(cpu) != CPU_STATE_STOPPED) { 25474b4c74dSDavid Hildenbrand set_sigp_status(si, SIGP_STAT_INCORRECT_STATE); 25574b4c74dSDavid Hildenbrand return; 25674b4c74dSDavid Hildenbrand } 25774b4c74dSDavid Hildenbrand 25874b4c74dSDavid Hildenbrand cpu->env.psa = addr; 25974b4c74dSDavid Hildenbrand cpu_synchronize_post_init(cs); 26074b4c74dSDavid Hildenbrand si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; 26174b4c74dSDavid Hildenbrand } 26274b4c74dSDavid Hildenbrand 263d1b468bcSDavid Hildenbrand static void sigp_sense_running(S390CPU *dst_cpu, SigpInfo *si) 264d1b468bcSDavid Hildenbrand { 265d1b468bcSDavid Hildenbrand if (!tcg_enabled()) { 266d1b468bcSDavid Hildenbrand /* handled in KVM */ 267d1b468bcSDavid Hildenbrand set_sigp_status(si, SIGP_STAT_INVALID_ORDER); 268d1b468bcSDavid Hildenbrand return; 269d1b468bcSDavid Hildenbrand } 270d1b468bcSDavid Hildenbrand 271d1b468bcSDavid Hildenbrand /* sensing without locks is racy, but it's the same for real hw */ 272d1b468bcSDavid Hildenbrand if (!s390_has_feat(S390_FEAT_SENSE_RUNNING_STATUS)) { 273d1b468bcSDavid Hildenbrand set_sigp_status(si, SIGP_STAT_INVALID_ORDER); 274d1b468bcSDavid Hildenbrand return; 275d1b468bcSDavid Hildenbrand } 276d1b468bcSDavid Hildenbrand 277d1b468bcSDavid Hildenbrand /* If halted (which includes also STOPPED), it is not running */ 278d1b468bcSDavid Hildenbrand if (CPU(dst_cpu)->halted) { 279d1b468bcSDavid Hildenbrand si->cc = SIGP_CC_ORDER_CODE_ACCEPTED; 280d1b468bcSDavid Hildenbrand } else { 281d1b468bcSDavid Hildenbrand set_sigp_status(si, SIGP_STAT_NOT_RUNNING); 282d1b468bcSDavid Hildenbrand } 283d1b468bcSDavid Hildenbrand } 284d1b468bcSDavid Hildenbrand 28574b4c74dSDavid Hildenbrand static int handle_sigp_single_dst(S390CPU *dst_cpu, uint8_t order, 28674b4c74dSDavid Hildenbrand uint64_t param, uint64_t *status_reg) 28774b4c74dSDavid Hildenbrand { 28874b4c74dSDavid Hildenbrand SigpInfo si = { 28974b4c74dSDavid Hildenbrand .param = param, 29074b4c74dSDavid Hildenbrand .status_reg = status_reg, 29174b4c74dSDavid Hildenbrand }; 29274b4c74dSDavid Hildenbrand 29374b4c74dSDavid Hildenbrand /* cpu available? */ 29474b4c74dSDavid Hildenbrand if (dst_cpu == NULL) { 29574b4c74dSDavid Hildenbrand return SIGP_CC_NOT_OPERATIONAL; 29674b4c74dSDavid Hildenbrand } 29774b4c74dSDavid Hildenbrand 29874b4c74dSDavid Hildenbrand /* only resets can break pending orders */ 29974b4c74dSDavid Hildenbrand if (dst_cpu->env.sigp_order != 0 && 30074b4c74dSDavid Hildenbrand order != SIGP_CPU_RESET && 30174b4c74dSDavid Hildenbrand order != SIGP_INITIAL_CPU_RESET) { 30274b4c74dSDavid Hildenbrand return SIGP_CC_BUSY; 30374b4c74dSDavid Hildenbrand } 30474b4c74dSDavid Hildenbrand 30574b4c74dSDavid Hildenbrand switch (order) { 306*302230fcSDavid Hildenbrand case SIGP_SENSE: 307*302230fcSDavid Hildenbrand sigp_sense(dst_cpu, &si); 308*302230fcSDavid Hildenbrand break; 30974b4c74dSDavid Hildenbrand case SIGP_START: 31074b4c74dSDavid Hildenbrand run_on_cpu(CPU(dst_cpu), sigp_start, RUN_ON_CPU_HOST_PTR(&si)); 31174b4c74dSDavid Hildenbrand break; 31274b4c74dSDavid Hildenbrand case SIGP_STOP: 31374b4c74dSDavid Hildenbrand run_on_cpu(CPU(dst_cpu), sigp_stop, RUN_ON_CPU_HOST_PTR(&si)); 31474b4c74dSDavid Hildenbrand break; 31574b4c74dSDavid Hildenbrand case SIGP_RESTART: 31674b4c74dSDavid Hildenbrand run_on_cpu(CPU(dst_cpu), sigp_restart, RUN_ON_CPU_HOST_PTR(&si)); 31774b4c74dSDavid Hildenbrand break; 31874b4c74dSDavid Hildenbrand case SIGP_STOP_STORE_STATUS: 31974b4c74dSDavid Hildenbrand run_on_cpu(CPU(dst_cpu), sigp_stop_and_store_status, RUN_ON_CPU_HOST_PTR(&si)); 32074b4c74dSDavid Hildenbrand break; 32174b4c74dSDavid Hildenbrand case SIGP_STORE_STATUS_ADDR: 32274b4c74dSDavid Hildenbrand run_on_cpu(CPU(dst_cpu), sigp_store_status_at_address, RUN_ON_CPU_HOST_PTR(&si)); 32374b4c74dSDavid Hildenbrand break; 32474b4c74dSDavid Hildenbrand case SIGP_STORE_ADTL_STATUS: 32574b4c74dSDavid Hildenbrand run_on_cpu(CPU(dst_cpu), sigp_store_adtl_status, RUN_ON_CPU_HOST_PTR(&si)); 32674b4c74dSDavid Hildenbrand break; 32774b4c74dSDavid Hildenbrand case SIGP_SET_PREFIX: 32874b4c74dSDavid Hildenbrand run_on_cpu(CPU(dst_cpu), sigp_set_prefix, RUN_ON_CPU_HOST_PTR(&si)); 32974b4c74dSDavid Hildenbrand break; 33074b4c74dSDavid Hildenbrand case SIGP_INITIAL_CPU_RESET: 33174b4c74dSDavid Hildenbrand run_on_cpu(CPU(dst_cpu), sigp_initial_cpu_reset, RUN_ON_CPU_HOST_PTR(&si)); 33274b4c74dSDavid Hildenbrand break; 33374b4c74dSDavid Hildenbrand case SIGP_CPU_RESET: 33474b4c74dSDavid Hildenbrand run_on_cpu(CPU(dst_cpu), sigp_cpu_reset, RUN_ON_CPU_HOST_PTR(&si)); 33574b4c74dSDavid Hildenbrand break; 336d1b468bcSDavid Hildenbrand case SIGP_SENSE_RUNNING: 337d1b468bcSDavid Hildenbrand sigp_sense_running(dst_cpu, &si); 338d1b468bcSDavid Hildenbrand break; 33974b4c74dSDavid Hildenbrand default: 34074b4c74dSDavid Hildenbrand set_sigp_status(&si, SIGP_STAT_INVALID_ORDER); 34174b4c74dSDavid Hildenbrand } 34274b4c74dSDavid Hildenbrand 34374b4c74dSDavid Hildenbrand return si.cc; 34474b4c74dSDavid Hildenbrand } 34574b4c74dSDavid Hildenbrand 34674b4c74dSDavid Hildenbrand static int sigp_set_architecture(S390CPU *cpu, uint32_t param, 34774b4c74dSDavid Hildenbrand uint64_t *status_reg) 34874b4c74dSDavid Hildenbrand { 34974b4c74dSDavid Hildenbrand CPUState *cur_cs; 35074b4c74dSDavid Hildenbrand S390CPU *cur_cpu; 35174b4c74dSDavid Hildenbrand bool all_stopped = true; 35274b4c74dSDavid Hildenbrand 35374b4c74dSDavid Hildenbrand CPU_FOREACH(cur_cs) { 35474b4c74dSDavid Hildenbrand cur_cpu = S390_CPU(cur_cs); 35574b4c74dSDavid Hildenbrand 35674b4c74dSDavid Hildenbrand if (cur_cpu == cpu) { 35774b4c74dSDavid Hildenbrand continue; 35874b4c74dSDavid Hildenbrand } 35974b4c74dSDavid Hildenbrand if (s390_cpu_get_state(cur_cpu) != CPU_STATE_STOPPED) { 36074b4c74dSDavid Hildenbrand all_stopped = false; 36174b4c74dSDavid Hildenbrand } 36274b4c74dSDavid Hildenbrand } 36374b4c74dSDavid Hildenbrand 36474b4c74dSDavid Hildenbrand *status_reg &= 0xffffffff00000000ULL; 36574b4c74dSDavid Hildenbrand 36674b4c74dSDavid Hildenbrand /* Reject set arch order, with czam we're always in z/Arch mode. */ 36774b4c74dSDavid Hildenbrand *status_reg |= (all_stopped ? SIGP_STAT_INVALID_PARAMETER : 36874b4c74dSDavid Hildenbrand SIGP_STAT_INCORRECT_STATE); 36974b4c74dSDavid Hildenbrand return SIGP_CC_STATUS_STORED; 37074b4c74dSDavid Hildenbrand } 37174b4c74dSDavid Hildenbrand 37274b4c74dSDavid Hildenbrand int handle_sigp(CPUS390XState *env, uint8_t order, uint64_t r1, uint64_t r3) 37374b4c74dSDavid Hildenbrand { 37474b4c74dSDavid Hildenbrand uint64_t *status_reg = &env->regs[r1]; 37574b4c74dSDavid Hildenbrand uint64_t param = (r1 % 2) ? env->regs[r1] : env->regs[r1 + 1]; 37674b4c74dSDavid Hildenbrand S390CPU *cpu = s390_env_get_cpu(env); 37774b4c74dSDavid Hildenbrand S390CPU *dst_cpu = NULL; 37874b4c74dSDavid Hildenbrand int ret; 37974b4c74dSDavid Hildenbrand 38074b4c74dSDavid Hildenbrand if (qemu_mutex_trylock(&qemu_sigp_mutex)) { 38174b4c74dSDavid Hildenbrand ret = SIGP_CC_BUSY; 38274b4c74dSDavid Hildenbrand goto out; 38374b4c74dSDavid Hildenbrand } 38474b4c74dSDavid Hildenbrand 38574b4c74dSDavid Hildenbrand switch (order) { 38674b4c74dSDavid Hildenbrand case SIGP_SET_ARCH: 38774b4c74dSDavid Hildenbrand ret = sigp_set_architecture(cpu, param, status_reg); 38874b4c74dSDavid Hildenbrand break; 38974b4c74dSDavid Hildenbrand default: 39074b4c74dSDavid Hildenbrand /* all other sigp orders target a single vcpu */ 39174b4c74dSDavid Hildenbrand dst_cpu = s390_cpu_addr2state(env->regs[r3]); 39274b4c74dSDavid Hildenbrand ret = handle_sigp_single_dst(dst_cpu, order, param, status_reg); 39374b4c74dSDavid Hildenbrand } 39474b4c74dSDavid Hildenbrand qemu_mutex_unlock(&qemu_sigp_mutex); 39574b4c74dSDavid Hildenbrand 39674b4c74dSDavid Hildenbrand out: 39774b4c74dSDavid Hildenbrand trace_sigp_finished(order, CPU(cpu)->cpu_index, 39874b4c74dSDavid Hildenbrand dst_cpu ? CPU(dst_cpu)->cpu_index : -1, ret); 39974b4c74dSDavid Hildenbrand g_assert(ret >= 0); 40074b4c74dSDavid Hildenbrand 40174b4c74dSDavid Hildenbrand return ret; 40274b4c74dSDavid Hildenbrand } 40374b4c74dSDavid Hildenbrand 40474b4c74dSDavid Hildenbrand int s390_cpu_restart(S390CPU *cpu) 40574b4c74dSDavid Hildenbrand { 40674b4c74dSDavid Hildenbrand SigpInfo si = {}; 40774b4c74dSDavid Hildenbrand 40874b4c74dSDavid Hildenbrand if (tcg_enabled()) { 40974b4c74dSDavid Hildenbrand /* FIXME TCG */ 41074b4c74dSDavid Hildenbrand return -ENOSYS; 41174b4c74dSDavid Hildenbrand } 41274b4c74dSDavid Hildenbrand 41374b4c74dSDavid Hildenbrand run_on_cpu(CPU(cpu), sigp_restart, RUN_ON_CPU_HOST_PTR(&si)); 41474b4c74dSDavid Hildenbrand return 0; 41574b4c74dSDavid Hildenbrand } 41674b4c74dSDavid Hildenbrand 4173047f8b5SDavid Hildenbrand void do_stop_interrupt(CPUS390XState *env) 4183047f8b5SDavid Hildenbrand { 4193047f8b5SDavid Hildenbrand S390CPU *cpu = s390_env_get_cpu(env); 4203047f8b5SDavid Hildenbrand 4213047f8b5SDavid Hildenbrand if (s390_cpu_set_state(CPU_STATE_STOPPED, cpu) == 0) { 4223047f8b5SDavid Hildenbrand qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); 4233047f8b5SDavid Hildenbrand } 4243047f8b5SDavid Hildenbrand if (cpu->env.sigp_order == SIGP_STOP_STORE_STATUS) { 4253047f8b5SDavid Hildenbrand s390_store_status(cpu, S390_STORE_STATUS_DEF_ADDR, true); 4263047f8b5SDavid Hildenbrand } 4273047f8b5SDavid Hildenbrand env->sigp_order = 0; 4283047f8b5SDavid Hildenbrand } 4293047f8b5SDavid Hildenbrand 43074b4c74dSDavid Hildenbrand void s390_init_sigp(void) 43174b4c74dSDavid Hildenbrand { 43274b4c74dSDavid Hildenbrand qemu_mutex_init(&qemu_sigp_mutex); 43374b4c74dSDavid Hildenbrand } 434