1000a1a38SChristian Borntraeger /* 2000a1a38SChristian Borntraeger * QEMU S/390 Interrupt support 3000a1a38SChristian Borntraeger * 479afc36dSCornelia Huck * Copyright IBM Corp. 2012, 2014 5000a1a38SChristian Borntraeger * 6000a1a38SChristian Borntraeger * This work is licensed under the terms of the GNU GPL, version 2 or (at your 7000a1a38SChristian Borntraeger * option) any later version. See the COPYING file in the top-level directory. 8000a1a38SChristian Borntraeger */ 9000a1a38SChristian Borntraeger 109615495aSPeter Maydell #include "qemu/osdep.h" 11e3cfd926SThomas Huth #include "qemu/log.h" 12000a1a38SChristian Borntraeger #include "cpu.h" 13f16bbb9bSDavid Hildenbrand #include "kvm_s390x.h" 144e58b838SDavid Hildenbrand #include "internal.h" 15e3cfd926SThomas Huth #include "exec/exec-all.h" 169c17d615SPaolo Bonzini #include "sysemu/kvm.h" 1714a48c1dSMarkus Armbruster #include "sysemu/tcg.h" 18bd3f16acSPaolo Bonzini #include "hw/s390x/ioinst.h" 1952341ed6SDavid Hildenbrand #include "tcg_s390x.h" 20e6505d53SDavid Hildenbrand #if !defined(CONFIG_USER_ONLY) 21e6505d53SDavid Hildenbrand #include "hw/s390x/s390_flic.h" 22e6505d53SDavid Hildenbrand #endif 23bd3f16acSPaolo Bonzini 24e3cfd926SThomas Huth /* Ensure to exit the TB after this call! */ 25e3cfd926SThomas Huth void trigger_pgm_exception(CPUS390XState *env, uint32_t code, uint32_t ilen) 26e3cfd926SThomas Huth { 27dc79e928SRichard Henderson CPUState *cs = env_cpu(env); 28e3cfd926SThomas Huth 29e3cfd926SThomas Huth cs->exception_index = EXCP_PGM; 30e3cfd926SThomas Huth env->int_pgm_code = code; 31*c87ff4d1SRichard Henderson /* If ILEN_UNWIND, int_pgm_ilen already has the correct value. */ 32*c87ff4d1SRichard Henderson if (ilen != ILEN_UNWIND) { 33e3cfd926SThomas Huth env->int_pgm_ilen = ilen; 34e3cfd926SThomas Huth } 35*c87ff4d1SRichard Henderson } 36e3cfd926SThomas Huth 3751dcdbd3SDavid Hildenbrand void s390_program_interrupt(CPUS390XState *env, uint32_t code, int ilen, 3851dcdbd3SDavid Hildenbrand uintptr_t ra) 39e3cfd926SThomas Huth { 40e3cfd926SThomas Huth if (kvm_enabled()) { 41dc79e928SRichard Henderson kvm_s390_program_interrupt(env_archcpu(env), code); 42e3cfd926SThomas Huth } else if (tcg_enabled()) { 4351dcdbd3SDavid Hildenbrand tcg_s390_program_interrupt(env, code, ilen, ra); 44e3cfd926SThomas Huth } else { 45e3cfd926SThomas Huth g_assert_not_reached(); 46e3cfd926SThomas Huth } 47e3cfd926SThomas Huth } 48e3cfd926SThomas Huth 49bd3f16acSPaolo Bonzini #if !defined(CONFIG_USER_ONLY) 506482b0ffSDavid Hildenbrand void cpu_inject_clock_comparator(S390CPU *cpu) 516482b0ffSDavid Hildenbrand { 526482b0ffSDavid Hildenbrand CPUS390XState *env = &cpu->env; 536482b0ffSDavid Hildenbrand 546482b0ffSDavid Hildenbrand env->pending_int |= INTERRUPT_EXT_CLOCK_COMPARATOR; 556482b0ffSDavid Hildenbrand cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); 566482b0ffSDavid Hildenbrand } 576482b0ffSDavid Hildenbrand 586482b0ffSDavid Hildenbrand void cpu_inject_cpu_timer(S390CPU *cpu) 596482b0ffSDavid Hildenbrand { 606482b0ffSDavid Hildenbrand CPUS390XState *env = &cpu->env; 616482b0ffSDavid Hildenbrand 626482b0ffSDavid Hildenbrand env->pending_int |= INTERRUPT_EXT_CPU_TIMER; 63bd3f16acSPaolo Bonzini cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); 64bd3f16acSPaolo Bonzini } 65bd3f16acSPaolo Bonzini 6614ca122eSDavid Hildenbrand void cpu_inject_emergency_signal(S390CPU *cpu, uint16_t src_cpu_addr) 6714ca122eSDavid Hildenbrand { 6814ca122eSDavid Hildenbrand CPUS390XState *env = &cpu->env; 6914ca122eSDavid Hildenbrand 7014ca122eSDavid Hildenbrand g_assert(src_cpu_addr < S390_MAX_CPUS); 7114ca122eSDavid Hildenbrand set_bit(src_cpu_addr, env->emergency_signals); 7214ca122eSDavid Hildenbrand 7314ca122eSDavid Hildenbrand env->pending_int |= INTERRUPT_EMERGENCY_SIGNAL; 7414ca122eSDavid Hildenbrand cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); 7514ca122eSDavid Hildenbrand } 7614ca122eSDavid Hildenbrand 7714ca122eSDavid Hildenbrand int cpu_inject_external_call(S390CPU *cpu, uint16_t src_cpu_addr) 7814ca122eSDavid Hildenbrand { 7914ca122eSDavid Hildenbrand CPUS390XState *env = &cpu->env; 8014ca122eSDavid Hildenbrand 8114ca122eSDavid Hildenbrand g_assert(src_cpu_addr < S390_MAX_CPUS); 8214ca122eSDavid Hildenbrand if (env->pending_int & INTERRUPT_EXTERNAL_CALL) { 8314ca122eSDavid Hildenbrand return -EBUSY; 8414ca122eSDavid Hildenbrand } 8514ca122eSDavid Hildenbrand env->external_call_addr = src_cpu_addr; 8614ca122eSDavid Hildenbrand 8714ca122eSDavid Hildenbrand env->pending_int |= INTERRUPT_EXTERNAL_CALL; 8814ca122eSDavid Hildenbrand cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); 8914ca122eSDavid Hildenbrand return 0; 9014ca122eSDavid Hildenbrand } 9114ca122eSDavid Hildenbrand 92eabcea18SDavid Hildenbrand void cpu_inject_restart(S390CPU *cpu) 93eabcea18SDavid Hildenbrand { 94b1ab5f60SDavid Hildenbrand CPUS390XState *env = &cpu->env; 95b1ab5f60SDavid Hildenbrand 96eabcea18SDavid Hildenbrand if (kvm_enabled()) { 97eabcea18SDavid Hildenbrand kvm_s390_restart_interrupt(cpu); 98eabcea18SDavid Hildenbrand return; 99eabcea18SDavid Hildenbrand } 100b1ab5f60SDavid Hildenbrand 101b1ab5f60SDavid Hildenbrand env->pending_int |= INTERRUPT_RESTART; 102b1ab5f60SDavid Hildenbrand cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); 103eabcea18SDavid Hildenbrand } 104eabcea18SDavid Hildenbrand 105eabcea18SDavid Hildenbrand void cpu_inject_stop(S390CPU *cpu) 106eabcea18SDavid Hildenbrand { 107b1ab5f60SDavid Hildenbrand CPUS390XState *env = &cpu->env; 108b1ab5f60SDavid Hildenbrand 109eabcea18SDavid Hildenbrand if (kvm_enabled()) { 110eabcea18SDavid Hildenbrand kvm_s390_stop_interrupt(cpu); 111eabcea18SDavid Hildenbrand return; 112eabcea18SDavid Hildenbrand } 113b1ab5f60SDavid Hildenbrand 114b1ab5f60SDavid Hildenbrand env->pending_int |= INTERRUPT_STOP; 115b1ab5f60SDavid Hildenbrand cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); 116eabcea18SDavid Hildenbrand } 117eabcea18SDavid Hildenbrand 11879afc36dSCornelia Huck /* 11979afc36dSCornelia Huck * All of the following interrupts are floating, i.e. not per-vcpu. 120de13d216SCornelia Huck * We just need a dummy cpustate in order to be able to inject in the 121de13d216SCornelia Huck * non-kvm case. 12279afc36dSCornelia Huck */ 123000a1a38SChristian Borntraeger void s390_sclp_extint(uint32_t parm) 124000a1a38SChristian Borntraeger { 125e6505d53SDavid Hildenbrand S390FLICState *fs = s390_get_flic(); 1266762808fSDavid Hildenbrand S390FLICStateClass *fsc = s390_get_flic_class(fs); 127000a1a38SChristian Borntraeger 128e6505d53SDavid Hildenbrand fsc->inject_service(fs, parm); 129000a1a38SChristian Borntraeger } 13079afc36dSCornelia Huck 131de13d216SCornelia Huck void s390_io_interrupt(uint16_t subchannel_id, uint16_t subchannel_nr, 132de13d216SCornelia Huck uint32_t io_int_parm, uint32_t io_int_word) 13379afc36dSCornelia Huck { 134e6505d53SDavid Hildenbrand S390FLICState *fs = s390_get_flic(); 1356762808fSDavid Hildenbrand S390FLICStateClass *fsc = s390_get_flic_class(fs); 136de13d216SCornelia Huck 137e6505d53SDavid Hildenbrand fsc->inject_io(fs, subchannel_id, subchannel_nr, io_int_parm, io_int_word); 13879afc36dSCornelia Huck } 13979afc36dSCornelia Huck 140de13d216SCornelia Huck void s390_crw_mchk(void) 14179afc36dSCornelia Huck { 142e6505d53SDavid Hildenbrand S390FLICState *fs = s390_get_flic(); 1436762808fSDavid Hildenbrand S390FLICStateClass *fsc = s390_get_flic_class(fs); 144de13d216SCornelia Huck 145e6505d53SDavid Hildenbrand fsc->inject_crw_mchk(fs); 14679afc36dSCornelia Huck } 14779afc36dSCornelia Huck 1488417f904SDavid Hildenbrand bool s390_cpu_has_mcck_int(S390CPU *cpu) 1498417f904SDavid Hildenbrand { 150f68ecdd4SDavid Hildenbrand QEMUS390FLICState *flic = s390_get_qemu_flic(s390_get_flic()); 1518417f904SDavid Hildenbrand CPUS390XState *env = &cpu->env; 1528417f904SDavid Hildenbrand 1538417f904SDavid Hildenbrand if (!(env->psw.mask & PSW_MASK_MCHECK)) { 1548417f904SDavid Hildenbrand return false; 1558417f904SDavid Hildenbrand } 1568417f904SDavid Hildenbrand 157520db63fSDavid Hildenbrand /* for now we only support channel report machine checks (floating) */ 158b194e447SDavid Hildenbrand if (qemu_s390_flic_has_crw_mchk(flic) && 159520db63fSDavid Hildenbrand (env->cregs[14] & CR14_CHANNEL_REPORT_SC)) { 160520db63fSDavid Hildenbrand return true; 161520db63fSDavid Hildenbrand } 162520db63fSDavid Hildenbrand 163520db63fSDavid Hildenbrand return false; 1648417f904SDavid Hildenbrand } 1658417f904SDavid Hildenbrand 1668417f904SDavid Hildenbrand bool s390_cpu_has_ext_int(S390CPU *cpu) 1678417f904SDavid Hildenbrand { 168f68ecdd4SDavid Hildenbrand QEMUS390FLICState *flic = s390_get_qemu_flic(s390_get_flic()); 1698417f904SDavid Hildenbrand CPUS390XState *env = &cpu->env; 1708417f904SDavid Hildenbrand 1718417f904SDavid Hildenbrand if (!(env->psw.mask & PSW_MASK_EXT)) { 1728417f904SDavid Hildenbrand return false; 1738417f904SDavid Hildenbrand } 1748417f904SDavid Hildenbrand 1759dec2388SDavid Hildenbrand if ((env->pending_int & INTERRUPT_EMERGENCY_SIGNAL) && 1769dec2388SDavid Hildenbrand (env->cregs[0] & CR0_EMERGENCY_SIGNAL_SC)) { 1779dec2388SDavid Hildenbrand return true; 1789dec2388SDavid Hildenbrand } 1799dec2388SDavid Hildenbrand 1809dec2388SDavid Hildenbrand if ((env->pending_int & INTERRUPT_EXTERNAL_CALL) && 1819dec2388SDavid Hildenbrand (env->cregs[0] & CR0_EXTERNAL_CALL_SC)) { 1829dec2388SDavid Hildenbrand return true; 1839dec2388SDavid Hildenbrand } 1849dec2388SDavid Hildenbrand 1859dec2388SDavid Hildenbrand if ((env->pending_int & INTERRUPT_EXTERNAL_CALL) && 1869dec2388SDavid Hildenbrand (env->cregs[0] & CR0_EXTERNAL_CALL_SC)) { 1879dec2388SDavid Hildenbrand return true; 1889dec2388SDavid Hildenbrand } 1899dec2388SDavid Hildenbrand 1909dec2388SDavid Hildenbrand if ((env->pending_int & INTERRUPT_EXT_CLOCK_COMPARATOR) && 1919dec2388SDavid Hildenbrand (env->cregs[0] & CR0_CKC_SC)) { 1929dec2388SDavid Hildenbrand return true; 1939dec2388SDavid Hildenbrand } 1949dec2388SDavid Hildenbrand 1959dec2388SDavid Hildenbrand if ((env->pending_int & INTERRUPT_EXT_CPU_TIMER) && 1969dec2388SDavid Hildenbrand (env->cregs[0] & CR0_CPU_TIMER_SC)) { 1979dec2388SDavid Hildenbrand return true; 1989dec2388SDavid Hildenbrand } 1999dec2388SDavid Hildenbrand 200b194e447SDavid Hildenbrand if (qemu_s390_flic_has_service(flic) && 2019dec2388SDavid Hildenbrand (env->cregs[0] & CR0_SERVICE_SC)) { 2029dec2388SDavid Hildenbrand return true; 2039dec2388SDavid Hildenbrand } 2049dec2388SDavid Hildenbrand 2059dec2388SDavid Hildenbrand return false; 2068417f904SDavid Hildenbrand } 2078417f904SDavid Hildenbrand 2088417f904SDavid Hildenbrand bool s390_cpu_has_io_int(S390CPU *cpu) 2098417f904SDavid Hildenbrand { 210f68ecdd4SDavid Hildenbrand QEMUS390FLICState *flic = s390_get_qemu_flic(s390_get_flic()); 2118417f904SDavid Hildenbrand CPUS390XState *env = &cpu->env; 2128417f904SDavid Hildenbrand 2138417f904SDavid Hildenbrand if (!(env->psw.mask & PSW_MASK_IO)) { 2148417f904SDavid Hildenbrand return false; 2158417f904SDavid Hildenbrand } 2168417f904SDavid Hildenbrand 217b194e447SDavid Hildenbrand return qemu_s390_flic_has_io(flic, env->cregs[6]); 2188417f904SDavid Hildenbrand } 219b1ab5f60SDavid Hildenbrand 220b1ab5f60SDavid Hildenbrand bool s390_cpu_has_restart_int(S390CPU *cpu) 221b1ab5f60SDavid Hildenbrand { 222b1ab5f60SDavid Hildenbrand CPUS390XState *env = &cpu->env; 223b1ab5f60SDavid Hildenbrand 224b1ab5f60SDavid Hildenbrand return env->pending_int & INTERRUPT_RESTART; 225b1ab5f60SDavid Hildenbrand } 226b1ab5f60SDavid Hildenbrand 227b1ab5f60SDavid Hildenbrand bool s390_cpu_has_stop_int(S390CPU *cpu) 228b1ab5f60SDavid Hildenbrand { 229b1ab5f60SDavid Hildenbrand CPUS390XState *env = &cpu->env; 230b1ab5f60SDavid Hildenbrand 231b1ab5f60SDavid Hildenbrand return env->pending_int & INTERRUPT_STOP; 232b1ab5f60SDavid Hildenbrand } 233000a1a38SChristian Borntraeger #endif 2348417f904SDavid Hildenbrand 2358417f904SDavid Hildenbrand bool s390_cpu_has_int(S390CPU *cpu) 2368417f904SDavid Hildenbrand { 2378417f904SDavid Hildenbrand #ifndef CONFIG_USER_ONLY 2388417f904SDavid Hildenbrand if (!tcg_enabled()) { 2398417f904SDavid Hildenbrand return false; 2408417f904SDavid Hildenbrand } 2418417f904SDavid Hildenbrand return s390_cpu_has_mcck_int(cpu) || 2428417f904SDavid Hildenbrand s390_cpu_has_ext_int(cpu) || 243b1ab5f60SDavid Hildenbrand s390_cpu_has_io_int(cpu) || 244b1ab5f60SDavid Hildenbrand s390_cpu_has_restart_int(cpu) || 245b1ab5f60SDavid Hildenbrand s390_cpu_has_stop_int(cpu); 2468417f904SDavid Hildenbrand #else 2478417f904SDavid Hildenbrand return false; 2488417f904SDavid Hildenbrand #endif 2498417f904SDavid Hildenbrand } 250