xref: /qemu/target/s390x/interrupt.c (revision c87ff4d108efce2546150be057721cb41ca1f74d)
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