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