]> Git Repo - qemu.git/blame - target/s390x/excp_helper.c
Merge remote-tracking branch 'remotes/vivier2/tags/linux-user-for-2.12-pull-request...
[qemu.git] / target / s390x / excp_helper.c
CommitLineData
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
53void s390_cpu_do_interrupt(CPUState *cs)
54{
55 cs->exception_index = -1;
56}
57
98670d47 58int 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
72static 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 86int 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
135static 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
209static 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
238static 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
302static 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
332static 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
376void 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 386try_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
455bool 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
479void 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. */
512void 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 */
This page took 0.141981 seconds and 4 git commands to generate.