hw/arm/virt: error_report cleanups
[qemu/ar7.git] / target-alpha / helper.c
blob5a85335838d02d66a8077a701e0ac174375498fa
1 /*
2 * Alpha emulation cpu helpers for qemu.
4 * Copyright (c) 2007 Jocelyn Mayer
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
20 #include <stdint.h>
21 #include <stdlib.h>
22 #include <stdio.h>
24 #include "cpu.h"
25 #include "fpu/softfloat.h"
26 #include "exec/helper-proto.h"
29 #define CONVERT_BIT(X, SRC, DST) \
30 (SRC > DST ? (X) / (SRC / DST) & (DST) : ((X) & SRC) * (DST / SRC))
32 uint64_t cpu_alpha_load_fpcr (CPUAlphaState *env)
34 return (uint64_t)env->fpcr << 32;
37 void cpu_alpha_store_fpcr (CPUAlphaState *env, uint64_t val)
39 uint32_t fpcr = val >> 32;
40 uint32_t t = 0;
42 t |= CONVERT_BIT(fpcr, FPCR_INED, FPCR_INE);
43 t |= CONVERT_BIT(fpcr, FPCR_UNFD, FPCR_UNF);
44 t |= CONVERT_BIT(fpcr, FPCR_OVFD, FPCR_OVF);
45 t |= CONVERT_BIT(fpcr, FPCR_DZED, FPCR_DZE);
46 t |= CONVERT_BIT(fpcr, FPCR_INVD, FPCR_INV);
48 env->fpcr = fpcr;
49 env->fpcr_exc_enable = ~t & FPCR_STATUS_MASK;
51 switch (fpcr & FPCR_DYN_MASK) {
52 case FPCR_DYN_NORMAL:
53 default:
54 t = float_round_nearest_even;
55 break;
56 case FPCR_DYN_CHOPPED:
57 t = float_round_to_zero;
58 break;
59 case FPCR_DYN_MINUS:
60 t = float_round_down;
61 break;
62 case FPCR_DYN_PLUS:
63 t = float_round_up;
64 break;
66 env->fpcr_dyn_round = t;
68 env->fpcr_flush_to_zero = (fpcr & FPCR_UNFD) && (fpcr & FPCR_UNDZ);
69 env->fp_status.flush_inputs_to_zero = (fpcr & FPCR_DNZ) != 0;
72 uint64_t helper_load_fpcr(CPUAlphaState *env)
74 return cpu_alpha_load_fpcr(env);
77 void helper_store_fpcr(CPUAlphaState *env, uint64_t val)
79 cpu_alpha_store_fpcr(env, val);
82 static uint64_t *cpu_alpha_addr_gr(CPUAlphaState *env, unsigned reg)
84 #ifndef CONFIG_USER_ONLY
85 if (env->pal_mode) {
86 if (reg >= 8 && reg <= 14) {
87 return &env->shadow[reg - 8];
88 } else if (reg == 25) {
89 return &env->shadow[7];
92 #endif
93 return &env->ir[reg];
96 uint64_t cpu_alpha_load_gr(CPUAlphaState *env, unsigned reg)
98 return *cpu_alpha_addr_gr(env, reg);
101 void cpu_alpha_store_gr(CPUAlphaState *env, unsigned reg, uint64_t val)
103 *cpu_alpha_addr_gr(env, reg) = val;
106 #if defined(CONFIG_USER_ONLY)
107 int alpha_cpu_handle_mmu_fault(CPUState *cs, vaddr address,
108 int rw, int mmu_idx)
110 AlphaCPU *cpu = ALPHA_CPU(cs);
112 cs->exception_index = EXCP_MMFAULT;
113 cpu->env.trap_arg0 = address;
114 return 1;
116 #else
117 /* Returns the OSF/1 entMM failure indication, or -1 on success. */
118 static int get_physical_address(CPUAlphaState *env, target_ulong addr,
119 int prot_need, int mmu_idx,
120 target_ulong *pphys, int *pprot)
122 CPUState *cs = CPU(alpha_env_get_cpu(env));
123 target_long saddr = addr;
124 target_ulong phys = 0;
125 target_ulong L1pte, L2pte, L3pte;
126 target_ulong pt, index;
127 int prot = 0;
128 int ret = MM_K_ACV;
130 /* Ensure that the virtual address is properly sign-extended from
131 the last implemented virtual address bit. */
132 if (saddr >> TARGET_VIRT_ADDR_SPACE_BITS != saddr >> 63) {
133 goto exit;
136 /* Translate the superpage. */
137 /* ??? When we do more than emulate Unix PALcode, we'll need to
138 determine which KSEG is actually active. */
139 if (saddr < 0 && ((saddr >> 41) & 3) == 2) {
140 /* User-space cannot access KSEG addresses. */
141 if (mmu_idx != MMU_KERNEL_IDX) {
142 goto exit;
145 /* For the benefit of the Typhoon chipset, move bit 40 to bit 43.
146 We would not do this if the 48-bit KSEG is enabled. */
147 phys = saddr & ((1ull << 40) - 1);
148 phys |= (saddr & (1ull << 40)) << 3;
150 prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
151 ret = -1;
152 goto exit;
155 /* Interpret the page table exactly like PALcode does. */
157 pt = env->ptbr;
159 /* L1 page table read. */
160 index = (addr >> (TARGET_PAGE_BITS + 20)) & 0x3ff;
161 L1pte = ldq_phys(cs->as, pt + index*8);
163 if (unlikely((L1pte & PTE_VALID) == 0)) {
164 ret = MM_K_TNV;
165 goto exit;
167 if (unlikely((L1pte & PTE_KRE) == 0)) {
168 goto exit;
170 pt = L1pte >> 32 << TARGET_PAGE_BITS;
172 /* L2 page table read. */
173 index = (addr >> (TARGET_PAGE_BITS + 10)) & 0x3ff;
174 L2pte = ldq_phys(cs->as, pt + index*8);
176 if (unlikely((L2pte & PTE_VALID) == 0)) {
177 ret = MM_K_TNV;
178 goto exit;
180 if (unlikely((L2pte & PTE_KRE) == 0)) {
181 goto exit;
183 pt = L2pte >> 32 << TARGET_PAGE_BITS;
185 /* L3 page table read. */
186 index = (addr >> TARGET_PAGE_BITS) & 0x3ff;
187 L3pte = ldq_phys(cs->as, pt + index*8);
189 phys = L3pte >> 32 << TARGET_PAGE_BITS;
190 if (unlikely((L3pte & PTE_VALID) == 0)) {
191 ret = MM_K_TNV;
192 goto exit;
195 #if PAGE_READ != 1 || PAGE_WRITE != 2 || PAGE_EXEC != 4
196 # error page bits out of date
197 #endif
199 /* Check access violations. */
200 if (L3pte & (PTE_KRE << mmu_idx)) {
201 prot |= PAGE_READ | PAGE_EXEC;
203 if (L3pte & (PTE_KWE << mmu_idx)) {
204 prot |= PAGE_WRITE;
206 if (unlikely((prot & prot_need) == 0 && prot_need)) {
207 goto exit;
210 /* Check fault-on-operation violations. */
211 prot &= ~(L3pte >> 1);
212 ret = -1;
213 if (unlikely((prot & prot_need) == 0)) {
214 ret = (prot_need & PAGE_EXEC ? MM_K_FOE :
215 prot_need & PAGE_WRITE ? MM_K_FOW :
216 prot_need & PAGE_READ ? MM_K_FOR : -1);
219 exit:
220 *pphys = phys;
221 *pprot = prot;
222 return ret;
225 hwaddr alpha_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
227 AlphaCPU *cpu = ALPHA_CPU(cs);
228 target_ulong phys;
229 int prot, fail;
231 fail = get_physical_address(&cpu->env, addr, 0, 0, &phys, &prot);
232 return (fail >= 0 ? -1 : phys);
235 int alpha_cpu_handle_mmu_fault(CPUState *cs, vaddr addr, int rw,
236 int mmu_idx)
238 AlphaCPU *cpu = ALPHA_CPU(cs);
239 CPUAlphaState *env = &cpu->env;
240 target_ulong phys;
241 int prot, fail;
243 fail = get_physical_address(env, addr, 1 << rw, mmu_idx, &phys, &prot);
244 if (unlikely(fail >= 0)) {
245 cs->exception_index = EXCP_MMFAULT;
246 env->trap_arg0 = addr;
247 env->trap_arg1 = fail;
248 env->trap_arg2 = (rw == 2 ? -1 : rw);
249 return 1;
252 tlb_set_page(cs, addr & TARGET_PAGE_MASK, phys & TARGET_PAGE_MASK,
253 prot, mmu_idx, TARGET_PAGE_SIZE);
254 return 0;
256 #endif /* USER_ONLY */
258 void alpha_cpu_do_interrupt(CPUState *cs)
260 AlphaCPU *cpu = ALPHA_CPU(cs);
261 CPUAlphaState *env = &cpu->env;
262 int i = cs->exception_index;
264 if (qemu_loglevel_mask(CPU_LOG_INT)) {
265 static int count;
266 const char *name = "<unknown>";
268 switch (i) {
269 case EXCP_RESET:
270 name = "reset";
271 break;
272 case EXCP_MCHK:
273 name = "mchk";
274 break;
275 case EXCP_SMP_INTERRUPT:
276 name = "smp_interrupt";
277 break;
278 case EXCP_CLK_INTERRUPT:
279 name = "clk_interrupt";
280 break;
281 case EXCP_DEV_INTERRUPT:
282 name = "dev_interrupt";
283 break;
284 case EXCP_MMFAULT:
285 name = "mmfault";
286 break;
287 case EXCP_UNALIGN:
288 name = "unalign";
289 break;
290 case EXCP_OPCDEC:
291 name = "opcdec";
292 break;
293 case EXCP_ARITH:
294 name = "arith";
295 break;
296 case EXCP_FEN:
297 name = "fen";
298 break;
299 case EXCP_CALL_PAL:
300 name = "call_pal";
301 break;
302 case EXCP_STL_C:
303 name = "stl_c";
304 break;
305 case EXCP_STQ_C:
306 name = "stq_c";
307 break;
309 qemu_log("INT %6d: %s(%#x) pc=%016" PRIx64 " sp=%016" PRIx64 "\n",
310 ++count, name, env->error_code, env->pc, env->ir[IR_SP]);
313 cs->exception_index = -1;
315 #if !defined(CONFIG_USER_ONLY)
316 switch (i) {
317 case EXCP_RESET:
318 i = 0x0000;
319 break;
320 case EXCP_MCHK:
321 i = 0x0080;
322 break;
323 case EXCP_SMP_INTERRUPT:
324 i = 0x0100;
325 break;
326 case EXCP_CLK_INTERRUPT:
327 i = 0x0180;
328 break;
329 case EXCP_DEV_INTERRUPT:
330 i = 0x0200;
331 break;
332 case EXCP_MMFAULT:
333 i = 0x0280;
334 break;
335 case EXCP_UNALIGN:
336 i = 0x0300;
337 break;
338 case EXCP_OPCDEC:
339 i = 0x0380;
340 break;
341 case EXCP_ARITH:
342 i = 0x0400;
343 break;
344 case EXCP_FEN:
345 i = 0x0480;
346 break;
347 case EXCP_CALL_PAL:
348 i = env->error_code;
349 /* There are 64 entry points for both privileged and unprivileged,
350 with bit 0x80 indicating unprivileged. Each entry point gets
351 64 bytes to do its job. */
352 if (i & 0x80) {
353 i = 0x2000 + (i - 0x80) * 64;
354 } else {
355 i = 0x1000 + i * 64;
357 break;
358 default:
359 cpu_abort(cs, "Unhandled CPU exception");
362 /* Remember where the exception happened. Emulate real hardware in
363 that the low bit of the PC indicates PALmode. */
364 env->exc_addr = env->pc | env->pal_mode;
366 /* Continue execution at the PALcode entry point. */
367 env->pc = env->palbr + i;
369 /* Switch to PALmode. */
370 env->pal_mode = 1;
371 #endif /* !USER_ONLY */
374 bool alpha_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
376 AlphaCPU *cpu = ALPHA_CPU(cs);
377 CPUAlphaState *env = &cpu->env;
378 int idx = -1;
380 /* We never take interrupts while in PALmode. */
381 if (env->pal_mode) {
382 return false;
385 /* Fall through the switch, collecting the highest priority
386 interrupt that isn't masked by the processor status IPL. */
387 /* ??? This hard-codes the OSF/1 interrupt levels. */
388 switch (env->ps & PS_INT_MASK) {
389 case 0 ... 3:
390 if (interrupt_request & CPU_INTERRUPT_HARD) {
391 idx = EXCP_DEV_INTERRUPT;
393 /* FALLTHRU */
394 case 4:
395 if (interrupt_request & CPU_INTERRUPT_TIMER) {
396 idx = EXCP_CLK_INTERRUPT;
398 /* FALLTHRU */
399 case 5:
400 if (interrupt_request & CPU_INTERRUPT_SMP) {
401 idx = EXCP_SMP_INTERRUPT;
403 /* FALLTHRU */
404 case 6:
405 if (interrupt_request & CPU_INTERRUPT_MCHK) {
406 idx = EXCP_MCHK;
409 if (idx >= 0) {
410 cs->exception_index = idx;
411 env->error_code = 0;
412 alpha_cpu_do_interrupt(cs);
413 return true;
415 return false;
418 void alpha_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf,
419 int flags)
421 static const char *linux_reg_names[] = {
422 "v0 ", "t0 ", "t1 ", "t2 ", "t3 ", "t4 ", "t5 ", "t6 ",
423 "t7 ", "s0 ", "s1 ", "s2 ", "s3 ", "s4 ", "s5 ", "fp ",
424 "a0 ", "a1 ", "a2 ", "a3 ", "a4 ", "a5 ", "t8 ", "t9 ",
425 "t10", "t11", "ra ", "t12", "at ", "gp ", "sp ", "zero",
427 AlphaCPU *cpu = ALPHA_CPU(cs);
428 CPUAlphaState *env = &cpu->env;
429 int i;
431 cpu_fprintf(f, " PC " TARGET_FMT_lx " PS %02x\n",
432 env->pc, env->ps);
433 for (i = 0; i < 31; i++) {
434 cpu_fprintf(f, "IR%02d %s " TARGET_FMT_lx " ", i,
435 linux_reg_names[i], cpu_alpha_load_gr(env, i));
436 if ((i % 3) == 2)
437 cpu_fprintf(f, "\n");
440 cpu_fprintf(f, "lock_a " TARGET_FMT_lx " lock_v " TARGET_FMT_lx "\n",
441 env->lock_addr, env->lock_value);
443 for (i = 0; i < 31; i++) {
444 cpu_fprintf(f, "FIR%02d " TARGET_FMT_lx " ", i,
445 *((uint64_t *)(&env->fir[i])));
446 if ((i % 3) == 2)
447 cpu_fprintf(f, "\n");
449 cpu_fprintf(f, "\n");
452 /* This should only be called from translate, via gen_excp.
453 We expect that ENV->PC has already been updated. */
454 void QEMU_NORETURN helper_excp(CPUAlphaState *env, int excp, int error)
456 AlphaCPU *cpu = alpha_env_get_cpu(env);
457 CPUState *cs = CPU(cpu);
459 cs->exception_index = excp;
460 env->error_code = error;
461 cpu_loop_exit(cs);
464 /* This may be called from any of the helpers to set up EXCEPTION_INDEX. */
465 void QEMU_NORETURN dynamic_excp(CPUAlphaState *env, uintptr_t retaddr,
466 int excp, int error)
468 AlphaCPU *cpu = alpha_env_get_cpu(env);
469 CPUState *cs = CPU(cpu);
471 cs->exception_index = excp;
472 env->error_code = error;
473 if (retaddr) {
474 cpu_restore_state(cs, retaddr);
475 /* Floating-point exceptions (our only users) point to the next PC. */
476 env->pc += 4;
478 cpu_loop_exit(cs);
481 void QEMU_NORETURN arith_excp(CPUAlphaState *env, uintptr_t retaddr,
482 int exc, uint64_t mask)
484 env->trap_arg0 = exc;
485 env->trap_arg1 = mask;
486 dynamic_excp(env, retaddr, EXCP_ARITH, 0);