]>
Commit | Line | Data |
---|---|---|
000a1a38 CB |
1 | /* |
2 | * QEMU S/390 Interrupt support | |
3 | * | |
79afc36d | 4 | * Copyright IBM Corp. 2012, 2014 |
000a1a38 CB |
5 | * |
6 | * This work is licensed under the terms of the GNU GPL, version 2 or (at your | |
7 | * option) any later version. See the COPYING file in the top-level directory. | |
8 | */ | |
9 | ||
9615495a | 10 | #include "qemu/osdep.h" |
e3cfd926 | 11 | #include "qemu/log.h" |
000a1a38 | 12 | #include "cpu.h" |
f16bbb9b | 13 | #include "kvm_s390x.h" |
4e58b838 | 14 | #include "internal.h" |
e3cfd926 | 15 | #include "exec/exec-all.h" |
9c17d615 | 16 | #include "sysemu/kvm.h" |
bd3f16ac PB |
17 | #include "hw/s390x/ioinst.h" |
18 | ||
e3cfd926 TH |
19 | /* Ensure to exit the TB after this call! */ |
20 | void trigger_pgm_exception(CPUS390XState *env, uint32_t code, uint32_t ilen) | |
21 | { | |
22 | CPUState *cs = CPU(s390_env_get_cpu(env)); | |
23 | ||
24 | cs->exception_index = EXCP_PGM; | |
25 | env->int_pgm_code = code; | |
26 | env->int_pgm_ilen = ilen; | |
27 | } | |
28 | ||
29 | static void tcg_s390_program_interrupt(CPUS390XState *env, uint32_t code, | |
30 | int ilen) | |
31 | { | |
32 | #ifdef CONFIG_TCG | |
33 | trigger_pgm_exception(env, code, ilen); | |
34 | cpu_loop_exit(CPU(s390_env_get_cpu(env))); | |
35 | #else | |
36 | g_assert_not_reached(); | |
37 | #endif | |
38 | } | |
39 | ||
40 | void program_interrupt(CPUS390XState *env, uint32_t code, int ilen) | |
41 | { | |
42 | S390CPU *cpu = s390_env_get_cpu(env); | |
43 | ||
44 | qemu_log_mask(CPU_LOG_INT, "program interrupt at %#" PRIx64 "\n", | |
45 | env->psw.addr); | |
46 | ||
47 | if (kvm_enabled()) { | |
48 | kvm_s390_program_interrupt(cpu, code); | |
49 | } else if (tcg_enabled()) { | |
50 | tcg_s390_program_interrupt(env, code, ilen); | |
51 | } else { | |
52 | g_assert_not_reached(); | |
53 | } | |
54 | } | |
55 | ||
bd3f16ac | 56 | #if !defined(CONFIG_USER_ONLY) |
d516f74c | 57 | static void cpu_inject_service(S390CPU *cpu, uint32_t param) |
bd3f16ac PB |
58 | { |
59 | CPUS390XState *env = &cpu->env; | |
60 | ||
d516f74c DH |
61 | /* multiplexing is good enough for sclp - kvm does it internally as well*/ |
62 | env->service_param |= param; | |
bd3f16ac | 63 | |
6482b0ff DH |
64 | env->pending_int |= INTERRUPT_EXT_SERVICE; |
65 | cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); | |
66 | } | |
67 | ||
68 | void cpu_inject_clock_comparator(S390CPU *cpu) | |
69 | { | |
70 | CPUS390XState *env = &cpu->env; | |
71 | ||
72 | env->pending_int |= INTERRUPT_EXT_CLOCK_COMPARATOR; | |
73 | cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); | |
74 | } | |
75 | ||
76 | void cpu_inject_cpu_timer(S390CPU *cpu) | |
77 | { | |
78 | CPUS390XState *env = &cpu->env; | |
79 | ||
80 | env->pending_int |= INTERRUPT_EXT_CPU_TIMER; | |
bd3f16ac PB |
81 | cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); |
82 | } | |
83 | ||
14ca122e DH |
84 | void cpu_inject_emergency_signal(S390CPU *cpu, uint16_t src_cpu_addr) |
85 | { | |
86 | CPUS390XState *env = &cpu->env; | |
87 | ||
88 | g_assert(src_cpu_addr < S390_MAX_CPUS); | |
89 | set_bit(src_cpu_addr, env->emergency_signals); | |
90 | ||
91 | env->pending_int |= INTERRUPT_EMERGENCY_SIGNAL; | |
92 | cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); | |
93 | } | |
94 | ||
95 | int cpu_inject_external_call(S390CPU *cpu, uint16_t src_cpu_addr) | |
96 | { | |
97 | CPUS390XState *env = &cpu->env; | |
98 | ||
99 | g_assert(src_cpu_addr < S390_MAX_CPUS); | |
100 | if (env->pending_int & INTERRUPT_EXTERNAL_CALL) { | |
101 | return -EBUSY; | |
102 | } | |
103 | env->external_call_addr = src_cpu_addr; | |
104 | ||
105 | env->pending_int |= INTERRUPT_EXTERNAL_CALL; | |
106 | cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); | |
107 | return 0; | |
108 | } | |
109 | ||
bd3f16ac PB |
110 | static void cpu_inject_io(S390CPU *cpu, uint16_t subchannel_id, |
111 | uint16_t subchannel_number, | |
112 | uint32_t io_int_parm, uint32_t io_int_word) | |
113 | { | |
114 | CPUS390XState *env = &cpu->env; | |
115 | int isc = IO_INT_WORD_ISC(io_int_word); | |
116 | ||
117 | if (env->io_index[isc] == MAX_IO_QUEUE - 1) { | |
118 | /* ugh - can't queue anymore. Let's drop. */ | |
119 | return; | |
120 | } | |
121 | ||
122 | env->io_index[isc]++; | |
123 | assert(env->io_index[isc] < MAX_IO_QUEUE); | |
124 | ||
125 | env->io_queue[env->io_index[isc]][isc].id = subchannel_id; | |
126 | env->io_queue[env->io_index[isc]][isc].nr = subchannel_number; | |
127 | env->io_queue[env->io_index[isc]][isc].parm = io_int_parm; | |
128 | env->io_queue[env->io_index[isc]][isc].word = io_int_word; | |
129 | ||
130 | env->pending_int |= INTERRUPT_IO; | |
131 | cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); | |
132 | } | |
133 | ||
134 | static void cpu_inject_crw_mchk(S390CPU *cpu) | |
135 | { | |
136 | CPUS390XState *env = &cpu->env; | |
137 | ||
138 | if (env->mchk_index == MAX_MCHK_QUEUE - 1) { | |
139 | /* ugh - can't queue anymore. Let's drop. */ | |
140 | return; | |
141 | } | |
142 | ||
143 | env->mchk_index++; | |
144 | assert(env->mchk_index < MAX_MCHK_QUEUE); | |
145 | ||
146 | env->mchk_queue[env->mchk_index].type = 1; | |
147 | ||
148 | env->pending_int |= INTERRUPT_MCHK; | |
149 | cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); | |
150 | } | |
000a1a38 | 151 | |
79afc36d CH |
152 | /* |
153 | * All of the following interrupts are floating, i.e. not per-vcpu. | |
de13d216 CH |
154 | * We just need a dummy cpustate in order to be able to inject in the |
155 | * non-kvm case. | |
79afc36d | 156 | */ |
000a1a38 CB |
157 | void s390_sclp_extint(uint32_t parm) |
158 | { | |
000a1a38 | 159 | if (kvm_enabled()) { |
de13d216 | 160 | kvm_s390_service_interrupt(parm); |
000a1a38 | 161 | } else { |
de13d216 | 162 | S390CPU *dummy_cpu = s390_cpu_addr2state(0); |
de13d216 | 163 | |
d516f74c | 164 | cpu_inject_service(dummy_cpu, parm); |
000a1a38 CB |
165 | } |
166 | } | |
79afc36d | 167 | |
de13d216 CH |
168 | void s390_io_interrupt(uint16_t subchannel_id, uint16_t subchannel_nr, |
169 | uint32_t io_int_parm, uint32_t io_int_word) | |
79afc36d CH |
170 | { |
171 | if (kvm_enabled()) { | |
de13d216 | 172 | kvm_s390_io_interrupt(subchannel_id, subchannel_nr, io_int_parm, |
79afc36d CH |
173 | io_int_word); |
174 | } else { | |
de13d216 CH |
175 | S390CPU *dummy_cpu = s390_cpu_addr2state(0); |
176 | ||
177 | cpu_inject_io(dummy_cpu, subchannel_id, subchannel_nr, io_int_parm, | |
79afc36d CH |
178 | io_int_word); |
179 | } | |
180 | } | |
181 | ||
de13d216 | 182 | void s390_crw_mchk(void) |
79afc36d CH |
183 | { |
184 | if (kvm_enabled()) { | |
de13d216 | 185 | kvm_s390_crw_mchk(); |
79afc36d | 186 | } else { |
de13d216 CH |
187 | S390CPU *dummy_cpu = s390_cpu_addr2state(0); |
188 | ||
189 | cpu_inject_crw_mchk(dummy_cpu); | |
79afc36d CH |
190 | } |
191 | } | |
192 | ||
000a1a38 | 193 | #endif |