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" 11*e3cfd926SThomas Huth #include "qemu/log.h" 12000a1a38SChristian Borntraeger #include "cpu.h" 13*e3cfd926SThomas Huth #include "exec/exec-all.h" 149c17d615SPaolo Bonzini #include "sysemu/kvm.h" 15bd3f16acSPaolo Bonzini #include "hw/s390x/ioinst.h" 16bd3f16acSPaolo Bonzini 17*e3cfd926SThomas Huth /* Ensure to exit the TB after this call! */ 18*e3cfd926SThomas Huth void trigger_pgm_exception(CPUS390XState *env, uint32_t code, uint32_t ilen) 19*e3cfd926SThomas Huth { 20*e3cfd926SThomas Huth CPUState *cs = CPU(s390_env_get_cpu(env)); 21*e3cfd926SThomas Huth 22*e3cfd926SThomas Huth cs->exception_index = EXCP_PGM; 23*e3cfd926SThomas Huth env->int_pgm_code = code; 24*e3cfd926SThomas Huth env->int_pgm_ilen = ilen; 25*e3cfd926SThomas Huth } 26*e3cfd926SThomas Huth 27*e3cfd926SThomas Huth static void tcg_s390_program_interrupt(CPUS390XState *env, uint32_t code, 28*e3cfd926SThomas Huth int ilen) 29*e3cfd926SThomas Huth { 30*e3cfd926SThomas Huth #ifdef CONFIG_TCG 31*e3cfd926SThomas Huth trigger_pgm_exception(env, code, ilen); 32*e3cfd926SThomas Huth cpu_loop_exit(CPU(s390_env_get_cpu(env))); 33*e3cfd926SThomas Huth #else 34*e3cfd926SThomas Huth g_assert_not_reached(); 35*e3cfd926SThomas Huth #endif 36*e3cfd926SThomas Huth } 37*e3cfd926SThomas Huth 38*e3cfd926SThomas Huth void program_interrupt(CPUS390XState *env, uint32_t code, int ilen) 39*e3cfd926SThomas Huth { 40*e3cfd926SThomas Huth S390CPU *cpu = s390_env_get_cpu(env); 41*e3cfd926SThomas Huth 42*e3cfd926SThomas Huth qemu_log_mask(CPU_LOG_INT, "program interrupt at %#" PRIx64 "\n", 43*e3cfd926SThomas Huth env->psw.addr); 44*e3cfd926SThomas Huth 45*e3cfd926SThomas Huth if (kvm_enabled()) { 46*e3cfd926SThomas Huth kvm_s390_program_interrupt(cpu, code); 47*e3cfd926SThomas Huth } else if (tcg_enabled()) { 48*e3cfd926SThomas Huth tcg_s390_program_interrupt(env, code, ilen); 49*e3cfd926SThomas Huth } else { 50*e3cfd926SThomas Huth g_assert_not_reached(); 51*e3cfd926SThomas Huth } 52*e3cfd926SThomas Huth } 53*e3cfd926SThomas Huth 54bd3f16acSPaolo Bonzini #if !defined(CONFIG_USER_ONLY) 55bd3f16acSPaolo Bonzini void cpu_inject_ext(S390CPU *cpu, uint32_t code, uint32_t param, 56bd3f16acSPaolo Bonzini uint64_t param64) 57bd3f16acSPaolo Bonzini { 58bd3f16acSPaolo Bonzini CPUS390XState *env = &cpu->env; 59bd3f16acSPaolo Bonzini 60bd3f16acSPaolo Bonzini if (env->ext_index == MAX_EXT_QUEUE - 1) { 61bd3f16acSPaolo Bonzini /* ugh - can't queue anymore. Let's drop. */ 62bd3f16acSPaolo Bonzini return; 63bd3f16acSPaolo Bonzini } 64bd3f16acSPaolo Bonzini 65bd3f16acSPaolo Bonzini env->ext_index++; 66bd3f16acSPaolo Bonzini assert(env->ext_index < MAX_EXT_QUEUE); 67bd3f16acSPaolo Bonzini 68bd3f16acSPaolo Bonzini env->ext_queue[env->ext_index].code = code; 69bd3f16acSPaolo Bonzini env->ext_queue[env->ext_index].param = param; 70bd3f16acSPaolo Bonzini env->ext_queue[env->ext_index].param64 = param64; 71bd3f16acSPaolo Bonzini 72bd3f16acSPaolo Bonzini env->pending_int |= INTERRUPT_EXT; 73bd3f16acSPaolo Bonzini cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); 74bd3f16acSPaolo Bonzini } 75bd3f16acSPaolo Bonzini 76bd3f16acSPaolo Bonzini static void cpu_inject_io(S390CPU *cpu, uint16_t subchannel_id, 77bd3f16acSPaolo Bonzini uint16_t subchannel_number, 78bd3f16acSPaolo Bonzini uint32_t io_int_parm, uint32_t io_int_word) 79bd3f16acSPaolo Bonzini { 80bd3f16acSPaolo Bonzini CPUS390XState *env = &cpu->env; 81bd3f16acSPaolo Bonzini int isc = IO_INT_WORD_ISC(io_int_word); 82bd3f16acSPaolo Bonzini 83bd3f16acSPaolo Bonzini if (env->io_index[isc] == MAX_IO_QUEUE - 1) { 84bd3f16acSPaolo Bonzini /* ugh - can't queue anymore. Let's drop. */ 85bd3f16acSPaolo Bonzini return; 86bd3f16acSPaolo Bonzini } 87bd3f16acSPaolo Bonzini 88bd3f16acSPaolo Bonzini env->io_index[isc]++; 89bd3f16acSPaolo Bonzini assert(env->io_index[isc] < MAX_IO_QUEUE); 90bd3f16acSPaolo Bonzini 91bd3f16acSPaolo Bonzini env->io_queue[env->io_index[isc]][isc].id = subchannel_id; 92bd3f16acSPaolo Bonzini env->io_queue[env->io_index[isc]][isc].nr = subchannel_number; 93bd3f16acSPaolo Bonzini env->io_queue[env->io_index[isc]][isc].parm = io_int_parm; 94bd3f16acSPaolo Bonzini env->io_queue[env->io_index[isc]][isc].word = io_int_word; 95bd3f16acSPaolo Bonzini 96bd3f16acSPaolo Bonzini env->pending_int |= INTERRUPT_IO; 97bd3f16acSPaolo Bonzini cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); 98bd3f16acSPaolo Bonzini } 99bd3f16acSPaolo Bonzini 100bd3f16acSPaolo Bonzini static void cpu_inject_crw_mchk(S390CPU *cpu) 101bd3f16acSPaolo Bonzini { 102bd3f16acSPaolo Bonzini CPUS390XState *env = &cpu->env; 103bd3f16acSPaolo Bonzini 104bd3f16acSPaolo Bonzini if (env->mchk_index == MAX_MCHK_QUEUE - 1) { 105bd3f16acSPaolo Bonzini /* ugh - can't queue anymore. Let's drop. */ 106bd3f16acSPaolo Bonzini return; 107bd3f16acSPaolo Bonzini } 108bd3f16acSPaolo Bonzini 109bd3f16acSPaolo Bonzini env->mchk_index++; 110bd3f16acSPaolo Bonzini assert(env->mchk_index < MAX_MCHK_QUEUE); 111bd3f16acSPaolo Bonzini 112bd3f16acSPaolo Bonzini env->mchk_queue[env->mchk_index].type = 1; 113bd3f16acSPaolo Bonzini 114bd3f16acSPaolo Bonzini env->pending_int |= INTERRUPT_MCHK; 115bd3f16acSPaolo Bonzini cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); 116bd3f16acSPaolo Bonzini } 117000a1a38SChristian Borntraeger 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 { 125de13d216SCornelia Huck if (kvm_enabled()) { 126de13d216SCornelia Huck kvm_s390_service_interrupt(parm); 127de13d216SCornelia Huck } else { 128000a1a38SChristian Borntraeger S390CPU *dummy_cpu = s390_cpu_addr2state(0); 129000a1a38SChristian Borntraeger 130f9466733SAndreas Färber cpu_inject_ext(dummy_cpu, EXT_SERVICE, parm, 0); 131000a1a38SChristian Borntraeger } 132000a1a38SChristian Borntraeger } 13379afc36dSCornelia Huck 134de13d216SCornelia Huck void s390_io_interrupt(uint16_t subchannel_id, uint16_t subchannel_nr, 135de13d216SCornelia Huck uint32_t io_int_parm, uint32_t io_int_word) 13679afc36dSCornelia Huck { 13779afc36dSCornelia Huck if (kvm_enabled()) { 138de13d216SCornelia Huck kvm_s390_io_interrupt(subchannel_id, subchannel_nr, io_int_parm, 13979afc36dSCornelia Huck io_int_word); 14079afc36dSCornelia Huck } else { 141de13d216SCornelia Huck S390CPU *dummy_cpu = s390_cpu_addr2state(0); 142de13d216SCornelia Huck 143de13d216SCornelia Huck cpu_inject_io(dummy_cpu, subchannel_id, subchannel_nr, io_int_parm, 14479afc36dSCornelia Huck io_int_word); 14579afc36dSCornelia Huck } 14679afc36dSCornelia Huck } 14779afc36dSCornelia Huck 148de13d216SCornelia Huck void s390_crw_mchk(void) 14979afc36dSCornelia Huck { 15079afc36dSCornelia Huck if (kvm_enabled()) { 151de13d216SCornelia Huck kvm_s390_crw_mchk(); 15279afc36dSCornelia Huck } else { 153de13d216SCornelia Huck S390CPU *dummy_cpu = s390_cpu_addr2state(0); 154de13d216SCornelia Huck 155de13d216SCornelia Huck cpu_inject_crw_mchk(dummy_cpu); 15679afc36dSCornelia Huck } 15779afc36dSCornelia Huck } 15879afc36dSCornelia Huck 159000a1a38SChristian Borntraeger #endif 160