]>
Commit | Line | Data |
---|---|---|
cded4014 TH |
1 | /* |
2 | * s390x exception / interrupt helpers | |
3 | * | |
4 | * Copyright (c) 2009 Ulrich Hecht | |
5 | * Copyright (c) 2011 Alexander Graf | |
6 | * | |
7 | * This library is free software; you can redistribute it and/or | |
8 | * modify it under the terms of the GNU Lesser General Public | |
9 | * License as published by the Free Software Foundation; either | |
10 | * version 2 of the License, or (at your option) any later version. | |
11 | * | |
12 | * This library is distributed in the hope that it will be useful, | |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 | * Lesser General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU Lesser General Public | |
18 | * License along with this library; if not, see <http://www.gnu.org/licenses/>. | |
19 | */ | |
20 | ||
21 | #include "qemu/osdep.h" | |
cded4014 | 22 | #include "cpu.h" |
4e58b838 | 23 | #include "internal.h" |
cded4014 TH |
24 | #include "qemu/timer.h" |
25 | #include "exec/exec-all.h" | |
26 | #include "exec/cpu_ldst.h" | |
27 | #include "hw/s390x/ioinst.h" | |
0bd695a9 | 28 | #include "exec/address-spaces.h" |
cded4014 TH |
29 | #ifndef CONFIG_USER_ONLY |
30 | #include "sysemu/sysemu.h" | |
b194e447 | 31 | #include "hw/s390x/s390_flic.h" |
cded4014 TH |
32 | #endif |
33 | ||
34 | /* #define DEBUG_S390 */ | |
35 | /* #define DEBUG_S390_STDOUT */ | |
36 | ||
37 | #ifdef DEBUG_S390 | |
38 | #ifdef DEBUG_S390_STDOUT | |
39 | #define DPRINTF(fmt, ...) \ | |
40 | do { fprintf(stderr, fmt, ## __VA_ARGS__); \ | |
41 | if (qemu_log_separate()) { qemu_log(fmt, ##__VA_ARGS__); } } while (0) | |
42 | #else | |
43 | #define DPRINTF(fmt, ...) \ | |
44 | do { qemu_log(fmt, ## __VA_ARGS__); } while (0) | |
45 | #endif | |
46 | #else | |
47 | #define DPRINTF(fmt, ...) \ | |
48 | do { } while (0) | |
49 | #endif | |
50 | ||
51 | #if defined(CONFIG_USER_ONLY) | |
52 | ||
53 | void s390_cpu_do_interrupt(CPUState *cs) | |
54 | { | |
55 | cs->exception_index = -1; | |
56 | } | |
57 | ||
98670d47 | 58 | int s390_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int size, |
cded4014 TH |
59 | int rw, int mmu_idx) |
60 | { | |
61 | S390CPU *cpu = S390_CPU(cs); | |
62 | ||
525f4b65 | 63 | trigger_pgm_exception(&cpu->env, PGM_ADDRESSING, ILEN_AUTO); |
cded4014 TH |
64 | /* On real machines this value is dropped into LowMem. Since this |
65 | is userland, simply put this someplace that cpu_loop can find it. */ | |
66 | cpu->env.__excp_addr = address; | |
67 | return 1; | |
68 | } | |
69 | ||
70 | #else /* !CONFIG_USER_ONLY */ | |
71 | ||
12e1e8f1 DH |
72 | static inline uint64_t cpu_mmu_idx_to_asc(int mmu_idx) |
73 | { | |
74 | switch (mmu_idx) { | |
75 | case MMU_PRIMARY_IDX: | |
76 | return PSW_ASC_PRIMARY; | |
77 | case MMU_SECONDARY_IDX: | |
78 | return PSW_ASC_SECONDARY; | |
79 | case MMU_HOME_IDX: | |
80 | return PSW_ASC_HOME; | |
81 | default: | |
82 | abort(); | |
83 | } | |
84 | } | |
85 | ||
98670d47 | 86 | int s390_cpu_handle_mmu_fault(CPUState *cs, vaddr orig_vaddr, int size, |
cded4014 TH |
87 | int rw, int mmu_idx) |
88 | { | |
89 | S390CPU *cpu = S390_CPU(cs); | |
90 | CPUS390XState *env = &cpu->env; | |
cded4014 | 91 | target_ulong vaddr, raddr; |
fb66944d | 92 | uint64_t asc; |
cded4014 TH |
93 | int prot; |
94 | ||
95 | DPRINTF("%s: address 0x%" VADDR_PRIx " rw %d mmu_idx %d\n", | |
96 | __func__, orig_vaddr, rw, mmu_idx); | |
97 | ||
cded4014 TH |
98 | vaddr = orig_vaddr; |
99 | ||
fb66944d DH |
100 | if (mmu_idx < MMU_REAL_IDX) { |
101 | asc = cpu_mmu_idx_to_asc(mmu_idx); | |
102 | /* 31-Bit mode */ | |
103 | if (!(env->psw.mask & PSW_MASK_64)) { | |
104 | vaddr &= 0x7fffffff; | |
105 | } | |
106 | if (mmu_translate(env, vaddr, rw, asc, &raddr, &prot, true)) { | |
107 | return 1; | |
108 | } | |
109 | } else if (mmu_idx == MMU_REAL_IDX) { | |
110 | if (mmu_translate_real(env, vaddr, rw, &raddr, &prot)) { | |
111 | return 1; | |
112 | } | |
113 | } else { | |
114 | abort(); | |
cded4014 TH |
115 | } |
116 | ||
117 | /* check out of RAM access */ | |
0bd695a9 DH |
118 | if (!address_space_access_valid(&address_space_memory, raddr, |
119 | TARGET_PAGE_SIZE, rw)) { | |
cded4014 TH |
120 | DPRINTF("%s: raddr %" PRIx64 " > ram_size %" PRIx64 "\n", __func__, |
121 | (uint64_t)raddr, (uint64_t)ram_size); | |
122 | trigger_pgm_exception(env, PGM_ADDRESSING, ILEN_AUTO); | |
123 | return 1; | |
124 | } | |
125 | ||
126 | qemu_log_mask(CPU_LOG_MMU, "%s: set tlb %" PRIx64 " -> %" PRIx64 " (%x)\n", | |
127 | __func__, (uint64_t)vaddr, (uint64_t)raddr, prot); | |
128 | ||
2bcf0183 | 129 | tlb_set_page(cs, orig_vaddr & TARGET_PAGE_MASK, raddr, prot, |
cded4014 TH |
130 | mmu_idx, TARGET_PAGE_SIZE); |
131 | ||
132 | return 0; | |
133 | } | |
134 | ||
135 | static void do_program_interrupt(CPUS390XState *env) | |
136 | { | |
137 | uint64_t mask, addr; | |
138 | LowCore *lowcore; | |
139 | int ilen = env->int_pgm_ilen; | |
140 | ||
141 | if (ilen == ILEN_AUTO) { | |
142 | ilen = get_ilen(cpu_ldub_code(env, env->psw.addr)); | |
143 | } | |
144 | assert(ilen == 2 || ilen == 4 || ilen == 6); | |
145 | ||
146 | switch (env->int_pgm_code) { | |
147 | case PGM_PER: | |
148 | if (env->per_perc_atmid & PER_CODE_EVENT_NULLIFICATION) { | |
149 | break; | |
150 | } | |
151 | /* FALL THROUGH */ | |
152 | case PGM_OPERATION: | |
153 | case PGM_PRIVILEGED: | |
154 | case PGM_EXECUTE: | |
155 | case PGM_PROTECTION: | |
156 | case PGM_ADDRESSING: | |
157 | case PGM_SPECIFICATION: | |
158 | case PGM_DATA: | |
159 | case PGM_FIXPT_OVERFLOW: | |
160 | case PGM_FIXPT_DIVIDE: | |
161 | case PGM_DEC_OVERFLOW: | |
162 | case PGM_DEC_DIVIDE: | |
163 | case PGM_HFP_EXP_OVERFLOW: | |
164 | case PGM_HFP_EXP_UNDERFLOW: | |
165 | case PGM_HFP_SIGNIFICANCE: | |
166 | case PGM_HFP_DIVIDE: | |
167 | case PGM_TRANS_SPEC: | |
168 | case PGM_SPECIAL_OP: | |
169 | case PGM_OPERAND: | |
170 | case PGM_HFP_SQRT: | |
171 | case PGM_PC_TRANS_SPEC: | |
172 | case PGM_ALET_SPEC: | |
173 | case PGM_MONITOR: | |
174 | /* advance the PSW if our exception is not nullifying */ | |
175 | env->psw.addr += ilen; | |
176 | break; | |
177 | } | |
178 | ||
179 | qemu_log_mask(CPU_LOG_INT, "%s: code=0x%x ilen=%d\n", | |
180 | __func__, env->int_pgm_code, ilen); | |
181 | ||
182 | lowcore = cpu_map_lowcore(env); | |
183 | ||
184 | /* Signal PER events with the exception. */ | |
185 | if (env->per_perc_atmid) { | |
186 | env->int_pgm_code |= PGM_PER; | |
187 | lowcore->per_address = cpu_to_be64(env->per_address); | |
188 | lowcore->per_perc_atmid = cpu_to_be16(env->per_perc_atmid); | |
189 | env->per_perc_atmid = 0; | |
190 | } | |
191 | ||
192 | lowcore->pgm_ilen = cpu_to_be16(ilen); | |
193 | lowcore->pgm_code = cpu_to_be16(env->int_pgm_code); | |
194 | lowcore->program_old_psw.mask = cpu_to_be64(get_psw_mask(env)); | |
195 | lowcore->program_old_psw.addr = cpu_to_be64(env->psw.addr); | |
196 | mask = be64_to_cpu(lowcore->program_new_psw.mask); | |
197 | addr = be64_to_cpu(lowcore->program_new_psw.addr); | |
198 | lowcore->per_breaking_event_addr = cpu_to_be64(env->gbea); | |
199 | ||
200 | cpu_unmap_lowcore(lowcore); | |
201 | ||
202 | DPRINTF("%s: %x %x %" PRIx64 " %" PRIx64 "\n", __func__, | |
203 | env->int_pgm_code, ilen, env->psw.mask, | |
204 | env->psw.addr); | |
205 | ||
206 | load_psw(env, mask, addr); | |
207 | } | |
208 | ||
209 | static void do_svc_interrupt(CPUS390XState *env) | |
210 | { | |
211 | uint64_t mask, addr; | |
212 | LowCore *lowcore; | |
213 | ||
214 | lowcore = cpu_map_lowcore(env); | |
215 | ||
216 | lowcore->svc_code = cpu_to_be16(env->int_svc_code); | |
217 | lowcore->svc_ilen = cpu_to_be16(env->int_svc_ilen); | |
218 | lowcore->svc_old_psw.mask = cpu_to_be64(get_psw_mask(env)); | |
219 | lowcore->svc_old_psw.addr = cpu_to_be64(env->psw.addr + env->int_svc_ilen); | |
220 | mask = be64_to_cpu(lowcore->svc_new_psw.mask); | |
221 | addr = be64_to_cpu(lowcore->svc_new_psw.addr); | |
222 | ||
223 | cpu_unmap_lowcore(lowcore); | |
224 | ||
225 | load_psw(env, mask, addr); | |
226 | ||
227 | /* When a PER event is pending, the PER exception has to happen | |
228 | immediately after the SERVICE CALL one. */ | |
229 | if (env->per_perc_atmid) { | |
230 | env->int_pgm_code = PGM_PER; | |
231 | env->int_pgm_ilen = env->int_svc_ilen; | |
232 | do_program_interrupt(env); | |
233 | } | |
234 | } | |
235 | ||
236 | #define VIRTIO_SUBCODE_64 0x0D00 | |
237 | ||
238 | static void do_ext_interrupt(CPUS390XState *env) | |
239 | { | |
b194e447 | 240 | QEMUS390FLICState *flic = QEMU_S390_FLIC(s390_get_flic()); |
cded4014 TH |
241 | S390CPU *cpu = s390_env_get_cpu(env); |
242 | uint64_t mask, addr; | |
14ca122e | 243 | uint16_t cpu_addr; |
cded4014 | 244 | LowCore *lowcore; |
cded4014 TH |
245 | |
246 | if (!(env->psw.mask & PSW_MASK_EXT)) { | |
247 | cpu_abort(CPU(cpu), "Ext int w/o ext mask\n"); | |
248 | } | |
249 | ||
cded4014 TH |
250 | lowcore = cpu_map_lowcore(env); |
251 | ||
9dec2388 DH |
252 | if ((env->pending_int & INTERRUPT_EMERGENCY_SIGNAL) && |
253 | (env->cregs[0] & CR0_EMERGENCY_SIGNAL_SC)) { | |
14ca122e DH |
254 | lowcore->ext_int_code = cpu_to_be16(EXT_EMERGENCY); |
255 | cpu_addr = find_first_bit(env->emergency_signals, S390_MAX_CPUS); | |
256 | g_assert(cpu_addr < S390_MAX_CPUS); | |
257 | lowcore->cpu_addr = cpu_to_be16(cpu_addr); | |
258 | clear_bit(cpu_addr, env->emergency_signals); | |
259 | if (bitmap_empty(env->emergency_signals, max_cpus)) { | |
260 | env->pending_int &= ~INTERRUPT_EMERGENCY_SIGNAL; | |
261 | } | |
9dec2388 DH |
262 | } else if ((env->pending_int & INTERRUPT_EXTERNAL_CALL) && |
263 | (env->cregs[0] & CR0_EXTERNAL_CALL_SC)) { | |
14ca122e DH |
264 | lowcore->ext_int_code = cpu_to_be16(EXT_EXTERNAL_CALL); |
265 | lowcore->cpu_addr = cpu_to_be16(env->external_call_addr); | |
266 | env->pending_int &= ~INTERRUPT_EXTERNAL_CALL; | |
9dec2388 DH |
267 | } else if ((env->pending_int & INTERRUPT_EXT_CLOCK_COMPARATOR) && |
268 | (env->cregs[0] & CR0_CKC_SC)) { | |
6482b0ff DH |
269 | lowcore->ext_int_code = cpu_to_be16(EXT_CLOCK_COMP); |
270 | lowcore->cpu_addr = 0; | |
271 | env->pending_int &= ~INTERRUPT_EXT_CLOCK_COMPARATOR; | |
9dec2388 DH |
272 | } else if ((env->pending_int & INTERRUPT_EXT_CPU_TIMER) && |
273 | (env->cregs[0] & CR0_CPU_TIMER_SC)) { | |
6482b0ff DH |
274 | lowcore->ext_int_code = cpu_to_be16(EXT_CPU_TIMER); |
275 | lowcore->cpu_addr = 0; | |
276 | env->pending_int &= ~INTERRUPT_EXT_CPU_TIMER; | |
b194e447 | 277 | } else if (qemu_s390_flic_has_service(flic) && |
9dec2388 | 278 | (env->cregs[0] & CR0_SERVICE_SC)) { |
b194e447 DH |
279 | uint32_t param; |
280 | ||
281 | param = qemu_s390_flic_dequeue_service(flic); | |
d516f74c | 282 | lowcore->ext_int_code = cpu_to_be16(EXT_SERVICE); |
b194e447 | 283 | lowcore->ext_params = cpu_to_be32(param); |
d516f74c | 284 | lowcore->cpu_addr = 0; |
6482b0ff DH |
285 | } else { |
286 | g_assert_not_reached(); | |
287 | } | |
288 | ||
cded4014 TH |
289 | mask = be64_to_cpu(lowcore->external_new_psw.mask); |
290 | addr = be64_to_cpu(lowcore->external_new_psw.addr); | |
6482b0ff DH |
291 | lowcore->external_old_psw.mask = cpu_to_be64(get_psw_mask(env)); |
292 | lowcore->external_old_psw.addr = cpu_to_be64(env->psw.addr); | |
cded4014 TH |
293 | |
294 | cpu_unmap_lowcore(lowcore); | |
295 | ||
cded4014 TH |
296 | DPRINTF("%s: %" PRIx64 " %" PRIx64 "\n", __func__, |
297 | env->psw.mask, env->psw.addr); | |
298 | ||
299 | load_psw(env, mask, addr); | |
300 | } | |
301 | ||
302 | static void do_io_interrupt(CPUS390XState *env) | |
303 | { | |
b194e447 DH |
304 | QEMUS390FLICState *flic = QEMU_S390_FLIC(s390_get_flic()); |
305 | uint64_t mask, addr; | |
306 | QEMUS390FlicIO *io; | |
cded4014 | 307 | LowCore *lowcore; |
cded4014 | 308 | |
b194e447 DH |
309 | g_assert(env->psw.mask & PSW_MASK_IO); |
310 | io = qemu_s390_flic_dequeue_io(flic, env->cregs[6]); | |
311 | g_assert(io); | |
cded4014 | 312 | |
b194e447 | 313 | lowcore = cpu_map_lowcore(env); |
cded4014 | 314 | |
b194e447 DH |
315 | lowcore->subchannel_id = cpu_to_be16(io->id); |
316 | lowcore->subchannel_nr = cpu_to_be16(io->nr); | |
317 | lowcore->io_int_parm = cpu_to_be32(io->parm); | |
318 | lowcore->io_int_word = cpu_to_be32(io->word); | |
319 | lowcore->io_old_psw.mask = cpu_to_be64(get_psw_mask(env)); | |
320 | lowcore->io_old_psw.addr = cpu_to_be64(env->psw.addr); | |
321 | mask = be64_to_cpu(lowcore->io_new_psw.mask); | |
322 | addr = be64_to_cpu(lowcore->io_new_psw.addr); | |
cded4014 | 323 | |
b194e447 DH |
324 | cpu_unmap_lowcore(lowcore); |
325 | g_free(io); | |
cded4014 | 326 | |
b194e447 DH |
327 | DPRINTF("%s: %" PRIx64 " %" PRIx64 "\n", __func__, env->psw.mask, |
328 | env->psw.addr); | |
329 | load_psw(env, mask, addr); | |
cded4014 TH |
330 | } |
331 | ||
332 | static void do_mchk_interrupt(CPUS390XState *env) | |
333 | { | |
b194e447 | 334 | QEMUS390FLICState *flic = QEMU_S390_FLIC(s390_get_flic()); |
cded4014 TH |
335 | uint64_t mask, addr; |
336 | LowCore *lowcore; | |
cded4014 TH |
337 | int i; |
338 | ||
520db63f DH |
339 | /* for now we only support channel report machine checks (floating) */ |
340 | g_assert(env->psw.mask & PSW_MASK_MCHECK); | |
341 | g_assert(env->cregs[14] & CR14_CHANNEL_REPORT_SC); | |
cded4014 | 342 | |
b194e447 | 343 | qemu_s390_flic_dequeue_crw_mchk(flic); |
cded4014 TH |
344 | |
345 | lowcore = cpu_map_lowcore(env); | |
346 | ||
b8d55db0 DH |
347 | /* we are always in z/Architecture mode */ |
348 | lowcore->ar_access_id = 1; | |
349 | ||
cded4014 TH |
350 | for (i = 0; i < 16; i++) { |
351 | lowcore->floating_pt_save_area[i] = cpu_to_be64(get_freg(env, i)->ll); | |
352 | lowcore->gpregs_save_area[i] = cpu_to_be64(env->regs[i]); | |
353 | lowcore->access_regs_save_area[i] = cpu_to_be32(env->aregs[i]); | |
354 | lowcore->cregs_save_area[i] = cpu_to_be64(env->cregs[i]); | |
355 | } | |
356 | lowcore->prefixreg_save_area = cpu_to_be32(env->psa); | |
357 | lowcore->fpt_creg_save_area = cpu_to_be32(env->fpc); | |
358 | lowcore->tod_progreg_save_area = cpu_to_be32(env->todpr); | |
b8d55db0 DH |
359 | lowcore->cpu_timer_save_area = cpu_to_be64(env->cputm); |
360 | lowcore->clock_comp_save_area = cpu_to_be64(env->ckc >> 8); | |
cded4014 | 361 | |
b8d55db0 | 362 | lowcore->mcic = cpu_to_be64(s390_build_validity_mcic() | MCIC_SC_CP); |
cded4014 TH |
363 | lowcore->mcck_old_psw.mask = cpu_to_be64(get_psw_mask(env)); |
364 | lowcore->mcck_old_psw.addr = cpu_to_be64(env->psw.addr); | |
365 | mask = be64_to_cpu(lowcore->mcck_new_psw.mask); | |
366 | addr = be64_to_cpu(lowcore->mcck_new_psw.addr); | |
367 | ||
368 | cpu_unmap_lowcore(lowcore); | |
369 | ||
cded4014 TH |
370 | DPRINTF("%s: %" PRIx64 " %" PRIx64 "\n", __func__, |
371 | env->psw.mask, env->psw.addr); | |
372 | ||
373 | load_psw(env, mask, addr); | |
374 | } | |
375 | ||
376 | void s390_cpu_do_interrupt(CPUState *cs) | |
377 | { | |
b194e447 | 378 | QEMUS390FLICState *flic = QEMU_S390_FLIC(s390_get_flic()); |
cded4014 TH |
379 | S390CPU *cpu = S390_CPU(cs); |
380 | CPUS390XState *env = &cpu->env; | |
ce204cba | 381 | bool stopped = false; |
cded4014 TH |
382 | |
383 | qemu_log_mask(CPU_LOG_INT, "%s: %d at pc=%" PRIx64 "\n", | |
384 | __func__, cs->exception_index, env->psw.addr); | |
385 | ||
ce204cba | 386 | try_deliver: |
cded4014 | 387 | /* handle machine checks */ |
8417f904 DH |
388 | if (cs->exception_index == -1 && s390_cpu_has_mcck_int(cpu)) { |
389 | cs->exception_index = EXCP_MCHK; | |
cded4014 TH |
390 | } |
391 | /* handle external interrupts */ | |
8417f904 | 392 | if (cs->exception_index == -1 && s390_cpu_has_ext_int(cpu)) { |
6482b0ff | 393 | cs->exception_index = EXCP_EXT; |
cded4014 TH |
394 | } |
395 | /* handle I/O interrupts */ | |
8417f904 DH |
396 | if (cs->exception_index == -1 && s390_cpu_has_io_int(cpu)) { |
397 | cs->exception_index = EXCP_IO; | |
cded4014 | 398 | } |
b1ab5f60 DH |
399 | /* RESTART interrupt */ |
400 | if (cs->exception_index == -1 && s390_cpu_has_restart_int(cpu)) { | |
401 | cs->exception_index = EXCP_RESTART; | |
402 | } | |
403 | /* STOP interrupt has least priority */ | |
404 | if (cs->exception_index == -1 && s390_cpu_has_stop_int(cpu)) { | |
405 | cs->exception_index = EXCP_STOP; | |
406 | } | |
cded4014 TH |
407 | |
408 | switch (cs->exception_index) { | |
409 | case EXCP_PGM: | |
410 | do_program_interrupt(env); | |
411 | break; | |
412 | case EXCP_SVC: | |
413 | do_svc_interrupt(env); | |
414 | break; | |
415 | case EXCP_EXT: | |
416 | do_ext_interrupt(env); | |
417 | break; | |
418 | case EXCP_IO: | |
419 | do_io_interrupt(env); | |
420 | break; | |
421 | case EXCP_MCHK: | |
422 | do_mchk_interrupt(env); | |
423 | break; | |
b1ab5f60 DH |
424 | case EXCP_RESTART: |
425 | do_restart_interrupt(env); | |
426 | break; | |
427 | case EXCP_STOP: | |
428 | do_stop_interrupt(env); | |
ce204cba | 429 | stopped = true; |
b1ab5f60 | 430 | break; |
cded4014 | 431 | } |
f1cbfe6a | 432 | |
ce204cba DH |
433 | if (cs->exception_index != -1 && !stopped) { |
434 | /* check if there are more pending interrupts to deliver */ | |
435 | cs->exception_index = -1; | |
436 | goto try_deliver; | |
f1cbfe6a | 437 | } |
cded4014 TH |
438 | cs->exception_index = -1; |
439 | ||
8417f904 | 440 | /* we might still have pending interrupts, but not deliverable */ |
b194e447 | 441 | if (!env->pending_int && !qemu_s390_flic_has_any(flic)) { |
cded4014 TH |
442 | cs->interrupt_request &= ~CPU_INTERRUPT_HARD; |
443 | } | |
ce204cba DH |
444 | |
445 | /* WAIT PSW during interrupt injection or STOP interrupt */ | |
446 | if ((env->psw.mask & PSW_MASK_WAIT) || stopped) { | |
447 | /* don't trigger a cpu_loop_exit(), use an interrupt instead */ | |
448 | cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HALT); | |
449 | } else if (cs->halted) { | |
450 | /* unhalt if we had a WAIT PSW somehwere in our injection chain */ | |
451 | s390_cpu_unhalt(cpu); | |
452 | } | |
cded4014 TH |
453 | } |
454 | ||
455 | bool s390_cpu_exec_interrupt(CPUState *cs, int interrupt_request) | |
456 | { | |
457 | if (interrupt_request & CPU_INTERRUPT_HARD) { | |
458 | S390CPU *cpu = S390_CPU(cs); | |
459 | CPUS390XState *env = &cpu->env; | |
460 | ||
461 | if (env->ex_value) { | |
462 | /* Execution of the target insn is indivisible from | |
463 | the parent EXECUTE insn. */ | |
464 | return false; | |
465 | } | |
8417f904 | 466 | if (s390_cpu_has_int(cpu)) { |
cded4014 TH |
467 | s390_cpu_do_interrupt(cs); |
468 | return true; | |
469 | } | |
6ca62eb5 DH |
470 | if (env->psw.mask & PSW_MASK_WAIT) { |
471 | /* Woken up because of a floating interrupt but it has already | |
472 | * been delivered. Go back to sleep. */ | |
473 | cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HALT); | |
474 | } | |
cded4014 TH |
475 | } |
476 | return false; | |
477 | } | |
478 | ||
479 | void s390x_cpu_debug_excp_handler(CPUState *cs) | |
480 | { | |
481 | S390CPU *cpu = S390_CPU(cs); | |
482 | CPUS390XState *env = &cpu->env; | |
483 | CPUWatchpoint *wp_hit = cs->watchpoint_hit; | |
484 | ||
485 | if (wp_hit && wp_hit->flags & BP_CPU) { | |
486 | /* FIXME: When the storage-alteration-space control bit is set, | |
487 | the exception should only be triggered if the memory access | |
488 | is done using an address space with the storage-alteration-event | |
489 | bit set. We have no way to detect that with the current | |
490 | watchpoint code. */ | |
491 | cs->watchpoint_hit = NULL; | |
492 | ||
493 | env->per_address = env->psw.addr; | |
494 | env->per_perc_atmid |= PER_CODE_EVENT_STORE | get_per_atmid(env); | |
495 | /* FIXME: We currently no way to detect the address space used | |
496 | to trigger the watchpoint. For now just consider it is the | |
497 | current default ASC. This turn to be true except when MVCP | |
498 | and MVCS instrutions are not used. */ | |
499 | env->per_perc_atmid |= env->psw.mask & (PSW_MASK_ASC) >> 46; | |
500 | ||
501 | /* Remove all watchpoints to re-execute the code. A PER exception | |
502 | will be triggered, it will call load_psw which will recompute | |
503 | the watchpoints. */ | |
504 | cpu_watchpoint_remove_all(cs, BP_CPU); | |
505 | cpu_loop_exit_noexc(cs); | |
506 | } | |
507 | } | |
508 | ||
509 | /* Unaligned accesses are only diagnosed with MO_ALIGN. At the moment, | |
510 | this is only for the atomic operations, for which we want to raise a | |
511 | specification exception. */ | |
512 | void s390x_cpu_do_unaligned_access(CPUState *cs, vaddr addr, | |
513 | MMUAccessType access_type, | |
514 | int mmu_idx, uintptr_t retaddr) | |
515 | { | |
516 | S390CPU *cpu = S390_CPU(cs); | |
517 | CPUS390XState *env = &cpu->env; | |
518 | ||
8d2f850a | 519 | s390_program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO, retaddr); |
cded4014 TH |
520 | } |
521 | ||
522 | #endif /* CONFIG_USER_ONLY */ |