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" 17bd3f16acSPaolo Bonzini #include "hw/s390x/ioinst.h" 18bd3f16acSPaolo Bonzini 19e3cfd926SThomas Huth /* Ensure to exit the TB after this call! */ 20e3cfd926SThomas Huth void trigger_pgm_exception(CPUS390XState *env, uint32_t code, uint32_t ilen) 21e3cfd926SThomas Huth { 22e3cfd926SThomas Huth CPUState *cs = CPU(s390_env_get_cpu(env)); 23e3cfd926SThomas Huth 24e3cfd926SThomas Huth cs->exception_index = EXCP_PGM; 25e3cfd926SThomas Huth env->int_pgm_code = code; 26e3cfd926SThomas Huth env->int_pgm_ilen = ilen; 27e3cfd926SThomas Huth } 28e3cfd926SThomas Huth 29e3cfd926SThomas Huth static void tcg_s390_program_interrupt(CPUS390XState *env, uint32_t code, 3051dcdbd3SDavid Hildenbrand int ilen, uintptr_t ra) 31e3cfd926SThomas Huth { 32e3cfd926SThomas Huth #ifdef CONFIG_TCG 33e3cfd926SThomas Huth trigger_pgm_exception(env, code, ilen); 3451dcdbd3SDavid Hildenbrand cpu_loop_exit_restore(CPU(s390_env_get_cpu(env)), ra); 35e3cfd926SThomas Huth #else 36e3cfd926SThomas Huth g_assert_not_reached(); 37e3cfd926SThomas Huth #endif 38e3cfd926SThomas Huth } 39e3cfd926SThomas Huth 4051dcdbd3SDavid Hildenbrand void s390_program_interrupt(CPUS390XState *env, uint32_t code, int ilen, 4151dcdbd3SDavid Hildenbrand uintptr_t ra) 42e3cfd926SThomas Huth { 43e3cfd926SThomas Huth S390CPU *cpu = s390_env_get_cpu(env); 44e3cfd926SThomas Huth 45e3cfd926SThomas Huth qemu_log_mask(CPU_LOG_INT, "program interrupt at %#" PRIx64 "\n", 46e3cfd926SThomas Huth env->psw.addr); 47e3cfd926SThomas Huth 48e3cfd926SThomas Huth if (kvm_enabled()) { 49e3cfd926SThomas Huth kvm_s390_program_interrupt(cpu, code); 50e3cfd926SThomas Huth } else if (tcg_enabled()) { 5151dcdbd3SDavid Hildenbrand tcg_s390_program_interrupt(env, code, ilen, ra); 52e3cfd926SThomas Huth } else { 53e3cfd926SThomas Huth g_assert_not_reached(); 54e3cfd926SThomas Huth } 55e3cfd926SThomas Huth } 56e3cfd926SThomas Huth 57bd3f16acSPaolo Bonzini #if !defined(CONFIG_USER_ONLY) 58d516f74cSDavid Hildenbrand static void cpu_inject_service(S390CPU *cpu, uint32_t param) 59bd3f16acSPaolo Bonzini { 60bd3f16acSPaolo Bonzini CPUS390XState *env = &cpu->env; 61bd3f16acSPaolo Bonzini 62d516f74cSDavid Hildenbrand /* multiplexing is good enough for sclp - kvm does it internally as well*/ 63d516f74cSDavid Hildenbrand env->service_param |= param; 64bd3f16acSPaolo Bonzini 656482b0ffSDavid Hildenbrand env->pending_int |= INTERRUPT_EXT_SERVICE; 666482b0ffSDavid Hildenbrand cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); 676482b0ffSDavid Hildenbrand } 686482b0ffSDavid Hildenbrand 696482b0ffSDavid Hildenbrand void cpu_inject_clock_comparator(S390CPU *cpu) 706482b0ffSDavid Hildenbrand { 716482b0ffSDavid Hildenbrand CPUS390XState *env = &cpu->env; 726482b0ffSDavid Hildenbrand 736482b0ffSDavid Hildenbrand env->pending_int |= INTERRUPT_EXT_CLOCK_COMPARATOR; 746482b0ffSDavid Hildenbrand cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); 756482b0ffSDavid Hildenbrand } 766482b0ffSDavid Hildenbrand 776482b0ffSDavid Hildenbrand void cpu_inject_cpu_timer(S390CPU *cpu) 786482b0ffSDavid Hildenbrand { 796482b0ffSDavid Hildenbrand CPUS390XState *env = &cpu->env; 806482b0ffSDavid Hildenbrand 816482b0ffSDavid Hildenbrand env->pending_int |= INTERRUPT_EXT_CPU_TIMER; 82bd3f16acSPaolo Bonzini cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); 83bd3f16acSPaolo Bonzini } 84bd3f16acSPaolo Bonzini 8514ca122eSDavid Hildenbrand void cpu_inject_emergency_signal(S390CPU *cpu, uint16_t src_cpu_addr) 8614ca122eSDavid Hildenbrand { 8714ca122eSDavid Hildenbrand CPUS390XState *env = &cpu->env; 8814ca122eSDavid Hildenbrand 8914ca122eSDavid Hildenbrand g_assert(src_cpu_addr < S390_MAX_CPUS); 9014ca122eSDavid Hildenbrand set_bit(src_cpu_addr, env->emergency_signals); 9114ca122eSDavid Hildenbrand 9214ca122eSDavid Hildenbrand env->pending_int |= INTERRUPT_EMERGENCY_SIGNAL; 9314ca122eSDavid Hildenbrand cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); 9414ca122eSDavid Hildenbrand } 9514ca122eSDavid Hildenbrand 9614ca122eSDavid Hildenbrand int cpu_inject_external_call(S390CPU *cpu, uint16_t src_cpu_addr) 9714ca122eSDavid Hildenbrand { 9814ca122eSDavid Hildenbrand CPUS390XState *env = &cpu->env; 9914ca122eSDavid Hildenbrand 10014ca122eSDavid Hildenbrand g_assert(src_cpu_addr < S390_MAX_CPUS); 10114ca122eSDavid Hildenbrand if (env->pending_int & INTERRUPT_EXTERNAL_CALL) { 10214ca122eSDavid Hildenbrand return -EBUSY; 10314ca122eSDavid Hildenbrand } 10414ca122eSDavid Hildenbrand env->external_call_addr = src_cpu_addr; 10514ca122eSDavid Hildenbrand 10614ca122eSDavid Hildenbrand env->pending_int |= INTERRUPT_EXTERNAL_CALL; 10714ca122eSDavid Hildenbrand cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); 10814ca122eSDavid Hildenbrand return 0; 10914ca122eSDavid Hildenbrand } 11014ca122eSDavid Hildenbrand 111eabcea18SDavid Hildenbrand void cpu_inject_restart(S390CPU *cpu) 112eabcea18SDavid Hildenbrand { 113b1ab5f60SDavid Hildenbrand CPUS390XState *env = &cpu->env; 114b1ab5f60SDavid Hildenbrand 115eabcea18SDavid Hildenbrand if (kvm_enabled()) { 116eabcea18SDavid Hildenbrand kvm_s390_restart_interrupt(cpu); 117eabcea18SDavid Hildenbrand return; 118eabcea18SDavid Hildenbrand } 119b1ab5f60SDavid Hildenbrand 120b1ab5f60SDavid Hildenbrand env->pending_int |= INTERRUPT_RESTART; 121b1ab5f60SDavid Hildenbrand cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); 122eabcea18SDavid Hildenbrand } 123eabcea18SDavid Hildenbrand 124eabcea18SDavid Hildenbrand void cpu_inject_stop(S390CPU *cpu) 125eabcea18SDavid Hildenbrand { 126b1ab5f60SDavid Hildenbrand CPUS390XState *env = &cpu->env; 127b1ab5f60SDavid Hildenbrand 128eabcea18SDavid Hildenbrand if (kvm_enabled()) { 129eabcea18SDavid Hildenbrand kvm_s390_stop_interrupt(cpu); 130eabcea18SDavid Hildenbrand return; 131eabcea18SDavid Hildenbrand } 132b1ab5f60SDavid Hildenbrand 133b1ab5f60SDavid Hildenbrand env->pending_int |= INTERRUPT_STOP; 134b1ab5f60SDavid Hildenbrand cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); 135eabcea18SDavid Hildenbrand } 136eabcea18SDavid Hildenbrand 137bd3f16acSPaolo Bonzini static void cpu_inject_io(S390CPU *cpu, uint16_t subchannel_id, 138bd3f16acSPaolo Bonzini uint16_t subchannel_number, 139bd3f16acSPaolo Bonzini uint32_t io_int_parm, uint32_t io_int_word) 140bd3f16acSPaolo Bonzini { 141bd3f16acSPaolo Bonzini CPUS390XState *env = &cpu->env; 142bd3f16acSPaolo Bonzini int isc = IO_INT_WORD_ISC(io_int_word); 143bd3f16acSPaolo Bonzini 144bd3f16acSPaolo Bonzini if (env->io_index[isc] == MAX_IO_QUEUE - 1) { 145bd3f16acSPaolo Bonzini /* ugh - can't queue anymore. Let's drop. */ 146bd3f16acSPaolo Bonzini return; 147bd3f16acSPaolo Bonzini } 148bd3f16acSPaolo Bonzini 149bd3f16acSPaolo Bonzini env->io_index[isc]++; 150bd3f16acSPaolo Bonzini assert(env->io_index[isc] < MAX_IO_QUEUE); 151bd3f16acSPaolo Bonzini 152bd3f16acSPaolo Bonzini env->io_queue[env->io_index[isc]][isc].id = subchannel_id; 153bd3f16acSPaolo Bonzini env->io_queue[env->io_index[isc]][isc].nr = subchannel_number; 154bd3f16acSPaolo Bonzini env->io_queue[env->io_index[isc]][isc].parm = io_int_parm; 155bd3f16acSPaolo Bonzini env->io_queue[env->io_index[isc]][isc].word = io_int_word; 156bd3f16acSPaolo Bonzini 157bd3f16acSPaolo Bonzini env->pending_int |= INTERRUPT_IO; 158bd3f16acSPaolo Bonzini cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); 159bd3f16acSPaolo Bonzini } 160bd3f16acSPaolo Bonzini 161bd3f16acSPaolo Bonzini static void cpu_inject_crw_mchk(S390CPU *cpu) 162bd3f16acSPaolo Bonzini { 163bd3f16acSPaolo Bonzini CPUS390XState *env = &cpu->env; 164bd3f16acSPaolo Bonzini 165bd3f16acSPaolo Bonzini env->pending_int |= INTERRUPT_MCHK; 166bd3f16acSPaolo Bonzini cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); 167bd3f16acSPaolo Bonzini } 168000a1a38SChristian Borntraeger 16979afc36dSCornelia Huck /* 17079afc36dSCornelia Huck * All of the following interrupts are floating, i.e. not per-vcpu. 171de13d216SCornelia Huck * We just need a dummy cpustate in order to be able to inject in the 172de13d216SCornelia Huck * non-kvm case. 17379afc36dSCornelia Huck */ 174000a1a38SChristian Borntraeger void s390_sclp_extint(uint32_t parm) 175000a1a38SChristian Borntraeger { 176de13d216SCornelia Huck if (kvm_enabled()) { 177de13d216SCornelia Huck kvm_s390_service_interrupt(parm); 178de13d216SCornelia Huck } else { 179000a1a38SChristian Borntraeger S390CPU *dummy_cpu = s390_cpu_addr2state(0); 180000a1a38SChristian Borntraeger 181d516f74cSDavid Hildenbrand cpu_inject_service(dummy_cpu, parm); 182000a1a38SChristian Borntraeger } 183000a1a38SChristian Borntraeger } 18479afc36dSCornelia Huck 185de13d216SCornelia Huck void s390_io_interrupt(uint16_t subchannel_id, uint16_t subchannel_nr, 186de13d216SCornelia Huck uint32_t io_int_parm, uint32_t io_int_word) 18779afc36dSCornelia Huck { 18879afc36dSCornelia Huck if (kvm_enabled()) { 189de13d216SCornelia Huck kvm_s390_io_interrupt(subchannel_id, subchannel_nr, io_int_parm, 19079afc36dSCornelia Huck io_int_word); 19179afc36dSCornelia Huck } else { 192de13d216SCornelia Huck S390CPU *dummy_cpu = s390_cpu_addr2state(0); 193de13d216SCornelia Huck 194de13d216SCornelia Huck cpu_inject_io(dummy_cpu, subchannel_id, subchannel_nr, io_int_parm, 19579afc36dSCornelia Huck io_int_word); 19679afc36dSCornelia Huck } 19779afc36dSCornelia Huck } 19879afc36dSCornelia Huck 199de13d216SCornelia Huck void s390_crw_mchk(void) 20079afc36dSCornelia Huck { 20179afc36dSCornelia Huck if (kvm_enabled()) { 202de13d216SCornelia Huck kvm_s390_crw_mchk(); 20379afc36dSCornelia Huck } else { 204de13d216SCornelia Huck S390CPU *dummy_cpu = s390_cpu_addr2state(0); 205de13d216SCornelia Huck 206de13d216SCornelia Huck cpu_inject_crw_mchk(dummy_cpu); 20779afc36dSCornelia Huck } 20879afc36dSCornelia Huck } 20979afc36dSCornelia Huck 2108417f904SDavid Hildenbrand bool s390_cpu_has_mcck_int(S390CPU *cpu) 2118417f904SDavid Hildenbrand { 2128417f904SDavid Hildenbrand CPUS390XState *env = &cpu->env; 2138417f904SDavid Hildenbrand 2148417f904SDavid Hildenbrand if (!(env->psw.mask & PSW_MASK_MCHECK)) { 2158417f904SDavid Hildenbrand return false; 2168417f904SDavid Hildenbrand } 2178417f904SDavid Hildenbrand 218*520db63fSDavid Hildenbrand /* for now we only support channel report machine checks (floating) */ 219*520db63fSDavid Hildenbrand if ((env->pending_int & INTERRUPT_MCHK) && 220*520db63fSDavid Hildenbrand (env->cregs[14] & CR14_CHANNEL_REPORT_SC)) { 221*520db63fSDavid Hildenbrand return true; 222*520db63fSDavid Hildenbrand } 223*520db63fSDavid Hildenbrand 224*520db63fSDavid Hildenbrand return false; 2258417f904SDavid Hildenbrand } 2268417f904SDavid Hildenbrand 2278417f904SDavid Hildenbrand bool s390_cpu_has_ext_int(S390CPU *cpu) 2288417f904SDavid Hildenbrand { 2298417f904SDavid Hildenbrand CPUS390XState *env = &cpu->env; 2308417f904SDavid Hildenbrand 2318417f904SDavid Hildenbrand if (!(env->psw.mask & PSW_MASK_EXT)) { 2328417f904SDavid Hildenbrand return false; 2338417f904SDavid Hildenbrand } 2348417f904SDavid Hildenbrand 2359dec2388SDavid Hildenbrand if ((env->pending_int & INTERRUPT_EMERGENCY_SIGNAL) && 2369dec2388SDavid Hildenbrand (env->cregs[0] & CR0_EMERGENCY_SIGNAL_SC)) { 2379dec2388SDavid Hildenbrand return true; 2389dec2388SDavid Hildenbrand } 2399dec2388SDavid Hildenbrand 2409dec2388SDavid Hildenbrand if ((env->pending_int & INTERRUPT_EXTERNAL_CALL) && 2419dec2388SDavid Hildenbrand (env->cregs[0] & CR0_EXTERNAL_CALL_SC)) { 2429dec2388SDavid Hildenbrand return true; 2439dec2388SDavid Hildenbrand } 2449dec2388SDavid Hildenbrand 2459dec2388SDavid Hildenbrand if ((env->pending_int & INTERRUPT_EXTERNAL_CALL) && 2469dec2388SDavid Hildenbrand (env->cregs[0] & CR0_EXTERNAL_CALL_SC)) { 2479dec2388SDavid Hildenbrand return true; 2489dec2388SDavid Hildenbrand } 2499dec2388SDavid Hildenbrand 2509dec2388SDavid Hildenbrand if ((env->pending_int & INTERRUPT_EXT_CLOCK_COMPARATOR) && 2519dec2388SDavid Hildenbrand (env->cregs[0] & CR0_CKC_SC)) { 2529dec2388SDavid Hildenbrand return true; 2539dec2388SDavid Hildenbrand } 2549dec2388SDavid Hildenbrand 2559dec2388SDavid Hildenbrand if ((env->pending_int & INTERRUPT_EXT_CPU_TIMER) && 2569dec2388SDavid Hildenbrand (env->cregs[0] & CR0_CPU_TIMER_SC)) { 2579dec2388SDavid Hildenbrand return true; 2589dec2388SDavid Hildenbrand } 2599dec2388SDavid Hildenbrand 2609dec2388SDavid Hildenbrand if ((env->pending_int & INTERRUPT_EXT_SERVICE) && 2619dec2388SDavid Hildenbrand (env->cregs[0] & CR0_SERVICE_SC)) { 2629dec2388SDavid Hildenbrand return true; 2639dec2388SDavid Hildenbrand } 2649dec2388SDavid Hildenbrand 2659dec2388SDavid Hildenbrand return false; 2668417f904SDavid Hildenbrand } 2678417f904SDavid Hildenbrand 2688417f904SDavid Hildenbrand bool s390_cpu_has_io_int(S390CPU *cpu) 2698417f904SDavid Hildenbrand { 2708417f904SDavid Hildenbrand CPUS390XState *env = &cpu->env; 2718417f904SDavid Hildenbrand 2728417f904SDavid Hildenbrand if (!(env->psw.mask & PSW_MASK_IO)) { 2738417f904SDavid Hildenbrand return false; 2748417f904SDavid Hildenbrand } 2758417f904SDavid Hildenbrand 2768417f904SDavid Hildenbrand return env->pending_int & INTERRUPT_IO; 2778417f904SDavid Hildenbrand } 278b1ab5f60SDavid Hildenbrand 279b1ab5f60SDavid Hildenbrand bool s390_cpu_has_restart_int(S390CPU *cpu) 280b1ab5f60SDavid Hildenbrand { 281b1ab5f60SDavid Hildenbrand CPUS390XState *env = &cpu->env; 282b1ab5f60SDavid Hildenbrand 283b1ab5f60SDavid Hildenbrand return env->pending_int & INTERRUPT_RESTART; 284b1ab5f60SDavid Hildenbrand } 285b1ab5f60SDavid Hildenbrand 286b1ab5f60SDavid Hildenbrand bool s390_cpu_has_stop_int(S390CPU *cpu) 287b1ab5f60SDavid Hildenbrand { 288b1ab5f60SDavid Hildenbrand CPUS390XState *env = &cpu->env; 289b1ab5f60SDavid Hildenbrand 290b1ab5f60SDavid Hildenbrand return env->pending_int & INTERRUPT_STOP; 291b1ab5f60SDavid Hildenbrand } 292000a1a38SChristian Borntraeger #endif 2938417f904SDavid Hildenbrand 2948417f904SDavid Hildenbrand bool s390_cpu_has_int(S390CPU *cpu) 2958417f904SDavid Hildenbrand { 2968417f904SDavid Hildenbrand #ifndef CONFIG_USER_ONLY 2978417f904SDavid Hildenbrand if (!tcg_enabled()) { 2988417f904SDavid Hildenbrand return false; 2998417f904SDavid Hildenbrand } 3008417f904SDavid Hildenbrand return s390_cpu_has_mcck_int(cpu) || 3018417f904SDavid Hildenbrand s390_cpu_has_ext_int(cpu) || 302b1ab5f60SDavid Hildenbrand s390_cpu_has_io_int(cpu) || 303b1ab5f60SDavid Hildenbrand s390_cpu_has_restart_int(cpu) || 304b1ab5f60SDavid Hildenbrand s390_cpu_has_stop_int(cpu); 3058417f904SDavid Hildenbrand #else 3068417f904SDavid Hildenbrand return false; 3078417f904SDavid Hildenbrand #endif 3088417f904SDavid Hildenbrand } 309