xref: /qemu/target/s390x/interrupt.c (revision f68ecdd4f3584130cf0cb8f545b7359fae150fe0)
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"
18e6505d53SDavid Hildenbrand #if !defined(CONFIG_USER_ONLY)
19e6505d53SDavid Hildenbrand #include "hw/s390x/s390_flic.h"
20e6505d53SDavid Hildenbrand #endif
21bd3f16acSPaolo Bonzini 
22e3cfd926SThomas Huth /* Ensure to exit the TB after this call! */
23e3cfd926SThomas Huth void trigger_pgm_exception(CPUS390XState *env, uint32_t code, uint32_t ilen)
24e3cfd926SThomas Huth {
25e3cfd926SThomas Huth     CPUState *cs = CPU(s390_env_get_cpu(env));
26e3cfd926SThomas Huth 
27e3cfd926SThomas Huth     cs->exception_index = EXCP_PGM;
28e3cfd926SThomas Huth     env->int_pgm_code = code;
29e3cfd926SThomas Huth     env->int_pgm_ilen = ilen;
30e3cfd926SThomas Huth }
31e3cfd926SThomas Huth 
32e3cfd926SThomas Huth static void tcg_s390_program_interrupt(CPUS390XState *env, uint32_t code,
3351dcdbd3SDavid Hildenbrand                                        int ilen, uintptr_t ra)
34e3cfd926SThomas Huth {
35e3cfd926SThomas Huth #ifdef CONFIG_TCG
36e3cfd926SThomas Huth     trigger_pgm_exception(env, code, ilen);
3751dcdbd3SDavid Hildenbrand     cpu_loop_exit_restore(CPU(s390_env_get_cpu(env)), ra);
38e3cfd926SThomas Huth #else
39e3cfd926SThomas Huth     g_assert_not_reached();
40e3cfd926SThomas Huth #endif
41e3cfd926SThomas Huth }
42e3cfd926SThomas Huth 
4351dcdbd3SDavid Hildenbrand void s390_program_interrupt(CPUS390XState *env, uint32_t code, int ilen,
4451dcdbd3SDavid Hildenbrand                             uintptr_t ra)
45e3cfd926SThomas Huth {
46e3cfd926SThomas Huth     S390CPU *cpu = s390_env_get_cpu(env);
47e3cfd926SThomas Huth 
48e3cfd926SThomas Huth     qemu_log_mask(CPU_LOG_INT, "program interrupt at %#" PRIx64 "\n",
49e3cfd926SThomas Huth                   env->psw.addr);
50e3cfd926SThomas Huth 
51e3cfd926SThomas Huth     if (kvm_enabled()) {
52e3cfd926SThomas Huth         kvm_s390_program_interrupt(cpu, code);
53e3cfd926SThomas Huth     } else if (tcg_enabled()) {
5451dcdbd3SDavid Hildenbrand         tcg_s390_program_interrupt(env, code, ilen, ra);
55e3cfd926SThomas Huth     } else {
56e3cfd926SThomas Huth         g_assert_not_reached();
57e3cfd926SThomas Huth     }
58e3cfd926SThomas Huth }
59e3cfd926SThomas Huth 
60bd3f16acSPaolo Bonzini #if !defined(CONFIG_USER_ONLY)
616482b0ffSDavid Hildenbrand void cpu_inject_clock_comparator(S390CPU *cpu)
626482b0ffSDavid Hildenbrand {
636482b0ffSDavid Hildenbrand     CPUS390XState *env = &cpu->env;
646482b0ffSDavid Hildenbrand 
656482b0ffSDavid Hildenbrand     env->pending_int |= INTERRUPT_EXT_CLOCK_COMPARATOR;
666482b0ffSDavid Hildenbrand     cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD);
676482b0ffSDavid Hildenbrand }
686482b0ffSDavid Hildenbrand 
696482b0ffSDavid Hildenbrand void cpu_inject_cpu_timer(S390CPU *cpu)
706482b0ffSDavid Hildenbrand {
716482b0ffSDavid Hildenbrand     CPUS390XState *env = &cpu->env;
726482b0ffSDavid Hildenbrand 
736482b0ffSDavid Hildenbrand     env->pending_int |= INTERRUPT_EXT_CPU_TIMER;
74bd3f16acSPaolo Bonzini     cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD);
75bd3f16acSPaolo Bonzini }
76bd3f16acSPaolo Bonzini 
7714ca122eSDavid Hildenbrand void cpu_inject_emergency_signal(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     set_bit(src_cpu_addr, env->emergency_signals);
8314ca122eSDavid Hildenbrand 
8414ca122eSDavid Hildenbrand     env->pending_int |= INTERRUPT_EMERGENCY_SIGNAL;
8514ca122eSDavid Hildenbrand     cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD);
8614ca122eSDavid Hildenbrand }
8714ca122eSDavid Hildenbrand 
8814ca122eSDavid Hildenbrand int cpu_inject_external_call(S390CPU *cpu, uint16_t src_cpu_addr)
8914ca122eSDavid Hildenbrand {
9014ca122eSDavid Hildenbrand     CPUS390XState *env = &cpu->env;
9114ca122eSDavid Hildenbrand 
9214ca122eSDavid Hildenbrand     g_assert(src_cpu_addr < S390_MAX_CPUS);
9314ca122eSDavid Hildenbrand     if (env->pending_int & INTERRUPT_EXTERNAL_CALL) {
9414ca122eSDavid Hildenbrand         return -EBUSY;
9514ca122eSDavid Hildenbrand     }
9614ca122eSDavid Hildenbrand     env->external_call_addr = src_cpu_addr;
9714ca122eSDavid Hildenbrand 
9814ca122eSDavid Hildenbrand     env->pending_int |= INTERRUPT_EXTERNAL_CALL;
9914ca122eSDavid Hildenbrand     cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD);
10014ca122eSDavid Hildenbrand     return 0;
10114ca122eSDavid Hildenbrand }
10214ca122eSDavid Hildenbrand 
103eabcea18SDavid Hildenbrand void cpu_inject_restart(S390CPU *cpu)
104eabcea18SDavid Hildenbrand {
105b1ab5f60SDavid Hildenbrand     CPUS390XState *env = &cpu->env;
106b1ab5f60SDavid Hildenbrand 
107eabcea18SDavid Hildenbrand     if (kvm_enabled()) {
108eabcea18SDavid Hildenbrand         kvm_s390_restart_interrupt(cpu);
109eabcea18SDavid Hildenbrand         return;
110eabcea18SDavid Hildenbrand     }
111b1ab5f60SDavid Hildenbrand 
112b1ab5f60SDavid Hildenbrand     env->pending_int |= INTERRUPT_RESTART;
113b1ab5f60SDavid Hildenbrand     cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD);
114eabcea18SDavid Hildenbrand }
115eabcea18SDavid Hildenbrand 
116eabcea18SDavid Hildenbrand void cpu_inject_stop(S390CPU *cpu)
117eabcea18SDavid Hildenbrand {
118b1ab5f60SDavid Hildenbrand     CPUS390XState *env = &cpu->env;
119b1ab5f60SDavid Hildenbrand 
120eabcea18SDavid Hildenbrand     if (kvm_enabled()) {
121eabcea18SDavid Hildenbrand         kvm_s390_stop_interrupt(cpu);
122eabcea18SDavid Hildenbrand         return;
123eabcea18SDavid Hildenbrand     }
124b1ab5f60SDavid Hildenbrand 
125b1ab5f60SDavid Hildenbrand     env->pending_int |= INTERRUPT_STOP;
126b1ab5f60SDavid Hildenbrand     cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD);
127eabcea18SDavid Hildenbrand }
128eabcea18SDavid Hildenbrand 
12979afc36dSCornelia Huck /*
13079afc36dSCornelia Huck  * All of the following interrupts are floating, i.e. not per-vcpu.
131de13d216SCornelia Huck  * We just need a dummy cpustate in order to be able to inject in the
132de13d216SCornelia Huck  * non-kvm case.
13379afc36dSCornelia Huck  */
134000a1a38SChristian Borntraeger void s390_sclp_extint(uint32_t parm)
135000a1a38SChristian Borntraeger {
136e6505d53SDavid Hildenbrand     S390FLICState *fs = s390_get_flic();
137e6505d53SDavid Hildenbrand     S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs);
138000a1a38SChristian Borntraeger 
139e6505d53SDavid Hildenbrand     fsc->inject_service(fs, parm);
140000a1a38SChristian Borntraeger }
14179afc36dSCornelia Huck 
142de13d216SCornelia Huck void s390_io_interrupt(uint16_t subchannel_id, uint16_t subchannel_nr,
143de13d216SCornelia Huck                        uint32_t io_int_parm, uint32_t io_int_word)
14479afc36dSCornelia Huck {
145e6505d53SDavid Hildenbrand     S390FLICState *fs = s390_get_flic();
146e6505d53SDavid Hildenbrand     S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs);
147de13d216SCornelia Huck 
148e6505d53SDavid Hildenbrand     fsc->inject_io(fs, subchannel_id, subchannel_nr, io_int_parm, io_int_word);
14979afc36dSCornelia Huck }
15079afc36dSCornelia Huck 
151de13d216SCornelia Huck void s390_crw_mchk(void)
15279afc36dSCornelia Huck {
153e6505d53SDavid Hildenbrand     S390FLICState *fs = s390_get_flic();
154e6505d53SDavid Hildenbrand     S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs);
155de13d216SCornelia Huck 
156e6505d53SDavid Hildenbrand     fsc->inject_crw_mchk(fs);
15779afc36dSCornelia Huck }
15879afc36dSCornelia Huck 
1598417f904SDavid Hildenbrand bool s390_cpu_has_mcck_int(S390CPU *cpu)
1608417f904SDavid Hildenbrand {
161*f68ecdd4SDavid Hildenbrand     QEMUS390FLICState *flic = s390_get_qemu_flic(s390_get_flic());
1628417f904SDavid Hildenbrand     CPUS390XState *env = &cpu->env;
1638417f904SDavid Hildenbrand 
1648417f904SDavid Hildenbrand     if (!(env->psw.mask & PSW_MASK_MCHECK)) {
1658417f904SDavid Hildenbrand         return false;
1668417f904SDavid Hildenbrand     }
1678417f904SDavid Hildenbrand 
168520db63fSDavid Hildenbrand     /* for now we only support channel report machine checks (floating) */
169b194e447SDavid Hildenbrand     if (qemu_s390_flic_has_crw_mchk(flic) &&
170520db63fSDavid Hildenbrand         (env->cregs[14] & CR14_CHANNEL_REPORT_SC)) {
171520db63fSDavid Hildenbrand         return true;
172520db63fSDavid Hildenbrand     }
173520db63fSDavid Hildenbrand 
174520db63fSDavid Hildenbrand     return false;
1758417f904SDavid Hildenbrand }
1768417f904SDavid Hildenbrand 
1778417f904SDavid Hildenbrand bool s390_cpu_has_ext_int(S390CPU *cpu)
1788417f904SDavid Hildenbrand {
179*f68ecdd4SDavid Hildenbrand     QEMUS390FLICState *flic = s390_get_qemu_flic(s390_get_flic());
1808417f904SDavid Hildenbrand     CPUS390XState *env = &cpu->env;
1818417f904SDavid Hildenbrand 
1828417f904SDavid Hildenbrand     if (!(env->psw.mask & PSW_MASK_EXT)) {
1838417f904SDavid Hildenbrand         return false;
1848417f904SDavid Hildenbrand     }
1858417f904SDavid Hildenbrand 
1869dec2388SDavid Hildenbrand     if ((env->pending_int & INTERRUPT_EMERGENCY_SIGNAL) &&
1879dec2388SDavid Hildenbrand         (env->cregs[0] & CR0_EMERGENCY_SIGNAL_SC)) {
1889dec2388SDavid Hildenbrand         return true;
1899dec2388SDavid Hildenbrand     }
1909dec2388SDavid Hildenbrand 
1919dec2388SDavid Hildenbrand     if ((env->pending_int & INTERRUPT_EXTERNAL_CALL) &&
1929dec2388SDavid Hildenbrand         (env->cregs[0] & CR0_EXTERNAL_CALL_SC)) {
1939dec2388SDavid Hildenbrand         return true;
1949dec2388SDavid Hildenbrand     }
1959dec2388SDavid Hildenbrand 
1969dec2388SDavid Hildenbrand     if ((env->pending_int & INTERRUPT_EXTERNAL_CALL) &&
1979dec2388SDavid Hildenbrand         (env->cregs[0] & CR0_EXTERNAL_CALL_SC)) {
1989dec2388SDavid Hildenbrand         return true;
1999dec2388SDavid Hildenbrand     }
2009dec2388SDavid Hildenbrand 
2019dec2388SDavid Hildenbrand     if ((env->pending_int & INTERRUPT_EXT_CLOCK_COMPARATOR) &&
2029dec2388SDavid Hildenbrand         (env->cregs[0] & CR0_CKC_SC)) {
2039dec2388SDavid Hildenbrand         return true;
2049dec2388SDavid Hildenbrand     }
2059dec2388SDavid Hildenbrand 
2069dec2388SDavid Hildenbrand     if ((env->pending_int & INTERRUPT_EXT_CPU_TIMER) &&
2079dec2388SDavid Hildenbrand         (env->cregs[0] & CR0_CPU_TIMER_SC)) {
2089dec2388SDavid Hildenbrand         return true;
2099dec2388SDavid Hildenbrand     }
2109dec2388SDavid Hildenbrand 
211b194e447SDavid Hildenbrand     if (qemu_s390_flic_has_service(flic) &&
2129dec2388SDavid Hildenbrand         (env->cregs[0] & CR0_SERVICE_SC)) {
2139dec2388SDavid Hildenbrand         return true;
2149dec2388SDavid Hildenbrand     }
2159dec2388SDavid Hildenbrand 
2169dec2388SDavid Hildenbrand     return false;
2178417f904SDavid Hildenbrand }
2188417f904SDavid Hildenbrand 
2198417f904SDavid Hildenbrand bool s390_cpu_has_io_int(S390CPU *cpu)
2208417f904SDavid Hildenbrand {
221*f68ecdd4SDavid Hildenbrand     QEMUS390FLICState *flic = s390_get_qemu_flic(s390_get_flic());
2228417f904SDavid Hildenbrand     CPUS390XState *env = &cpu->env;
2238417f904SDavid Hildenbrand 
2248417f904SDavid Hildenbrand     if (!(env->psw.mask & PSW_MASK_IO)) {
2258417f904SDavid Hildenbrand         return false;
2268417f904SDavid Hildenbrand     }
2278417f904SDavid Hildenbrand 
228b194e447SDavid Hildenbrand     return qemu_s390_flic_has_io(flic, env->cregs[6]);
2298417f904SDavid Hildenbrand }
230b1ab5f60SDavid Hildenbrand 
231b1ab5f60SDavid Hildenbrand bool s390_cpu_has_restart_int(S390CPU *cpu)
232b1ab5f60SDavid Hildenbrand {
233b1ab5f60SDavid Hildenbrand     CPUS390XState *env = &cpu->env;
234b1ab5f60SDavid Hildenbrand 
235b1ab5f60SDavid Hildenbrand     return env->pending_int & INTERRUPT_RESTART;
236b1ab5f60SDavid Hildenbrand }
237b1ab5f60SDavid Hildenbrand 
238b1ab5f60SDavid Hildenbrand bool s390_cpu_has_stop_int(S390CPU *cpu)
239b1ab5f60SDavid Hildenbrand {
240b1ab5f60SDavid Hildenbrand     CPUS390XState *env = &cpu->env;
241b1ab5f60SDavid Hildenbrand 
242b1ab5f60SDavid Hildenbrand     return env->pending_int & INTERRUPT_STOP;
243b1ab5f60SDavid Hildenbrand }
244000a1a38SChristian Borntraeger #endif
2458417f904SDavid Hildenbrand 
2468417f904SDavid Hildenbrand bool s390_cpu_has_int(S390CPU *cpu)
2478417f904SDavid Hildenbrand {
2488417f904SDavid Hildenbrand #ifndef CONFIG_USER_ONLY
2498417f904SDavid Hildenbrand     if (!tcg_enabled()) {
2508417f904SDavid Hildenbrand         return false;
2518417f904SDavid Hildenbrand     }
2528417f904SDavid Hildenbrand     return s390_cpu_has_mcck_int(cpu) ||
2538417f904SDavid Hildenbrand            s390_cpu_has_ext_int(cpu) ||
254b1ab5f60SDavid Hildenbrand            s390_cpu_has_io_int(cpu) ||
255b1ab5f60SDavid Hildenbrand            s390_cpu_has_restart_int(cpu) ||
256b1ab5f60SDavid Hildenbrand            s390_cpu_has_stop_int(cpu);
2578417f904SDavid Hildenbrand #else
2588417f904SDavid Hildenbrand     return false;
2598417f904SDavid Hildenbrand #endif
2608417f904SDavid Hildenbrand }
261