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/>.
25 #include "fpu/softfloat.h"
26 #include "exec/helper-proto.h"
28 uint64_t cpu_alpha_load_fpcr (CPUAlphaState
*env
)
33 t
= env
->fpcr_exc_status
;
36 if (t
& float_flag_invalid
) {
39 if (t
& float_flag_divbyzero
) {
42 if (t
& float_flag_overflow
) {
45 if (t
& float_flag_underflow
) {
48 if (t
& float_flag_inexact
) {
53 t
= env
->fpcr_exc_mask
;
54 if (t
& float_flag_invalid
) {
57 if (t
& float_flag_divbyzero
) {
60 if (t
& float_flag_overflow
) {
63 if (t
& float_flag_underflow
) {
66 if (t
& float_flag_inexact
) {
70 switch (env
->fpcr_dyn_round
) {
71 case float_round_nearest_even
:
74 case float_round_down
:
80 case float_round_to_zero
:
81 r
|= FPCR_DYN_CHOPPED
;
85 if (env
->fp_status
.flush_inputs_to_zero
) {
98 void cpu_alpha_store_fpcr (CPUAlphaState
*env
, uint64_t val
)
103 if (val
& FPCR_INV
) {
104 t
|= float_flag_invalid
;
106 if (val
& FPCR_DZE
) {
107 t
|= float_flag_divbyzero
;
109 if (val
& FPCR_OVF
) {
110 t
|= float_flag_overflow
;
112 if (val
& FPCR_UNF
) {
113 t
|= float_flag_underflow
;
115 if (val
& FPCR_INE
) {
116 t
|= float_flag_inexact
;
118 env
->fpcr_exc_status
= t
;
121 if (val
& FPCR_INVD
) {
122 t
|= float_flag_invalid
;
124 if (val
& FPCR_DZED
) {
125 t
|= float_flag_divbyzero
;
127 if (val
& FPCR_OVFD
) {
128 t
|= float_flag_overflow
;
130 if (val
& FPCR_UNFD
) {
131 t
|= float_flag_underflow
;
133 if (val
& FPCR_INED
) {
134 t
|= float_flag_inexact
;
136 env
->fpcr_exc_mask
= t
;
138 switch (val
& FPCR_DYN_MASK
) {
139 case FPCR_DYN_CHOPPED
:
140 t
= float_round_to_zero
;
143 t
= float_round_down
;
145 case FPCR_DYN_NORMAL
:
146 t
= float_round_nearest_even
;
152 env
->fpcr_dyn_round
= t
;
154 env
->fpcr_dnod
= (val
& FPCR_DNOD
) != 0;
155 env
->fpcr_undz
= (val
& FPCR_UNDZ
) != 0;
156 env
->fpcr_flush_to_zero
= env
->fpcr_dnod
& env
->fpcr_undz
;
157 env
->fp_status
.flush_inputs_to_zero
= (val
& FPCR_DNZ
) != 0;
160 uint64_t helper_load_fpcr(CPUAlphaState
*env
)
162 return cpu_alpha_load_fpcr(env
);
165 void helper_store_fpcr(CPUAlphaState
*env
, uint64_t val
)
167 cpu_alpha_store_fpcr(env
, val
);
170 #if defined(CONFIG_USER_ONLY)
171 int alpha_cpu_handle_mmu_fault(CPUState
*cs
, vaddr address
,
174 AlphaCPU
*cpu
= ALPHA_CPU(cs
);
176 cs
->exception_index
= EXCP_MMFAULT
;
177 cpu
->env
.trap_arg0
= address
;
181 void swap_shadow_regs(CPUAlphaState
*env
)
183 uint64_t i0
, i1
, i2
, i3
, i4
, i5
, i6
, i7
;
194 env
->ir
[8] = env
->shadow
[0];
195 env
->ir
[9] = env
->shadow
[1];
196 env
->ir
[10] = env
->shadow
[2];
197 env
->ir
[11] = env
->shadow
[3];
198 env
->ir
[12] = env
->shadow
[4];
199 env
->ir
[13] = env
->shadow
[5];
200 env
->ir
[14] = env
->shadow
[6];
201 env
->ir
[25] = env
->shadow
[7];
213 /* Returns the OSF/1 entMM failure indication, or -1 on success. */
214 static int get_physical_address(CPUAlphaState
*env
, target_ulong addr
,
215 int prot_need
, int mmu_idx
,
216 target_ulong
*pphys
, int *pprot
)
218 CPUState
*cs
= CPU(alpha_env_get_cpu(env
));
219 target_long saddr
= addr
;
220 target_ulong phys
= 0;
221 target_ulong L1pte
, L2pte
, L3pte
;
222 target_ulong pt
, index
;
226 /* Ensure that the virtual address is properly sign-extended from
227 the last implemented virtual address bit. */
228 if (saddr
>> TARGET_VIRT_ADDR_SPACE_BITS
!= saddr
>> 63) {
232 /* Translate the superpage. */
233 /* ??? When we do more than emulate Unix PALcode, we'll need to
234 determine which KSEG is actually active. */
235 if (saddr
< 0 && ((saddr
>> 41) & 3) == 2) {
236 /* User-space cannot access KSEG addresses. */
237 if (mmu_idx
!= MMU_KERNEL_IDX
) {
241 /* For the benefit of the Typhoon chipset, move bit 40 to bit 43.
242 We would not do this if the 48-bit KSEG is enabled. */
243 phys
= saddr
& ((1ull << 40) - 1);
244 phys
|= (saddr
& (1ull << 40)) << 3;
246 prot
= PAGE_READ
| PAGE_WRITE
| PAGE_EXEC
;
251 /* Interpret the page table exactly like PALcode does. */
255 /* L1 page table read. */
256 index
= (addr
>> (TARGET_PAGE_BITS
+ 20)) & 0x3ff;
257 L1pte
= ldq_phys(cs
->as
, pt
+ index
*8);
259 if (unlikely((L1pte
& PTE_VALID
) == 0)) {
263 if (unlikely((L1pte
& PTE_KRE
) == 0)) {
266 pt
= L1pte
>> 32 << TARGET_PAGE_BITS
;
268 /* L2 page table read. */
269 index
= (addr
>> (TARGET_PAGE_BITS
+ 10)) & 0x3ff;
270 L2pte
= ldq_phys(cs
->as
, pt
+ index
*8);
272 if (unlikely((L2pte
& PTE_VALID
) == 0)) {
276 if (unlikely((L2pte
& PTE_KRE
) == 0)) {
279 pt
= L2pte
>> 32 << TARGET_PAGE_BITS
;
281 /* L3 page table read. */
282 index
= (addr
>> TARGET_PAGE_BITS
) & 0x3ff;
283 L3pte
= ldq_phys(cs
->as
, pt
+ index
*8);
285 phys
= L3pte
>> 32 << TARGET_PAGE_BITS
;
286 if (unlikely((L3pte
& PTE_VALID
) == 0)) {
291 #if PAGE_READ != 1 || PAGE_WRITE != 2 || PAGE_EXEC != 4
292 # error page bits out of date
295 /* Check access violations. */
296 if (L3pte
& (PTE_KRE
<< mmu_idx
)) {
297 prot
|= PAGE_READ
| PAGE_EXEC
;
299 if (L3pte
& (PTE_KWE
<< mmu_idx
)) {
302 if (unlikely((prot
& prot_need
) == 0 && prot_need
)) {
306 /* Check fault-on-operation violations. */
307 prot
&= ~(L3pte
>> 1);
309 if (unlikely((prot
& prot_need
) == 0)) {
310 ret
= (prot_need
& PAGE_EXEC
? MM_K_FOE
:
311 prot_need
& PAGE_WRITE
? MM_K_FOW
:
312 prot_need
& PAGE_READ
? MM_K_FOR
: -1);
321 hwaddr
alpha_cpu_get_phys_page_debug(CPUState
*cs
, vaddr addr
)
323 AlphaCPU
*cpu
= ALPHA_CPU(cs
);
327 fail
= get_physical_address(&cpu
->env
, addr
, 0, 0, &phys
, &prot
);
328 return (fail
>= 0 ? -1 : phys
);
331 int alpha_cpu_handle_mmu_fault(CPUState
*cs
, vaddr addr
, int rw
,
334 AlphaCPU
*cpu
= ALPHA_CPU(cs
);
335 CPUAlphaState
*env
= &cpu
->env
;
339 fail
= get_physical_address(env
, addr
, 1 << rw
, mmu_idx
, &phys
, &prot
);
340 if (unlikely(fail
>= 0)) {
341 cs
->exception_index
= EXCP_MMFAULT
;
342 env
->trap_arg0
= addr
;
343 env
->trap_arg1
= fail
;
344 env
->trap_arg2
= (rw
== 2 ? -1 : rw
);
348 tlb_set_page(cs
, addr
& TARGET_PAGE_MASK
, phys
& TARGET_PAGE_MASK
,
349 prot
, mmu_idx
, TARGET_PAGE_SIZE
);
352 #endif /* USER_ONLY */
354 void alpha_cpu_do_interrupt(CPUState
*cs
)
356 AlphaCPU
*cpu
= ALPHA_CPU(cs
);
357 CPUAlphaState
*env
= &cpu
->env
;
358 int i
= cs
->exception_index
;
360 if (qemu_loglevel_mask(CPU_LOG_INT
)) {
362 const char *name
= "<unknown>";
371 case EXCP_SMP_INTERRUPT
:
372 name
= "smp_interrupt";
374 case EXCP_CLK_INTERRUPT
:
375 name
= "clk_interrupt";
377 case EXCP_DEV_INTERRUPT
:
378 name
= "dev_interrupt";
405 qemu_log("INT %6d: %s(%#x) pc=%016" PRIx64
" sp=%016" PRIx64
"\n",
406 ++count
, name
, env
->error_code
, env
->pc
, env
->ir
[IR_SP
]);
409 cs
->exception_index
= -1;
411 #if !defined(CONFIG_USER_ONLY)
419 case EXCP_SMP_INTERRUPT
:
422 case EXCP_CLK_INTERRUPT
:
425 case EXCP_DEV_INTERRUPT
:
445 /* There are 64 entry points for both privileged and unprivileged,
446 with bit 0x80 indicating unprivileged. Each entry point gets
447 64 bytes to do its job. */
449 i
= 0x2000 + (i
- 0x80) * 64;
455 cpu_abort(cs
, "Unhandled CPU exception");
458 /* Remember where the exception happened. Emulate real hardware in
459 that the low bit of the PC indicates PALmode. */
460 env
->exc_addr
= env
->pc
| env
->pal_mode
;
462 /* Continue execution at the PALcode entry point. */
463 env
->pc
= env
->palbr
+ i
;
465 /* Switch to PALmode. */
466 if (!env
->pal_mode
) {
468 swap_shadow_regs(env
);
470 #endif /* !USER_ONLY */
473 void alpha_cpu_dump_state(CPUState
*cs
, FILE *f
, fprintf_function cpu_fprintf
,
476 static const char *linux_reg_names
[] = {
477 "v0 ", "t0 ", "t1 ", "t2 ", "t3 ", "t4 ", "t5 ", "t6 ",
478 "t7 ", "s0 ", "s1 ", "s2 ", "s3 ", "s4 ", "s5 ", "fp ",
479 "a0 ", "a1 ", "a2 ", "a3 ", "a4 ", "a5 ", "t8 ", "t9 ",
480 "t10", "t11", "ra ", "t12", "at ", "gp ", "sp ", "zero",
482 AlphaCPU
*cpu
= ALPHA_CPU(cs
);
483 CPUAlphaState
*env
= &cpu
->env
;
486 cpu_fprintf(f
, " PC " TARGET_FMT_lx
" PS %02x\n",
488 for (i
= 0; i
< 31; i
++) {
489 cpu_fprintf(f
, "IR%02d %s " TARGET_FMT_lx
" ", i
,
490 linux_reg_names
[i
], env
->ir
[i
]);
492 cpu_fprintf(f
, "\n");
495 cpu_fprintf(f
, "lock_a " TARGET_FMT_lx
" lock_v " TARGET_FMT_lx
"\n",
496 env
->lock_addr
, env
->lock_value
);
498 for (i
= 0; i
< 31; i
++) {
499 cpu_fprintf(f
, "FIR%02d " TARGET_FMT_lx
" ", i
,
500 *((uint64_t *)(&env
->fir
[i
])));
502 cpu_fprintf(f
, "\n");
504 cpu_fprintf(f
, "\n");
507 /* This should only be called from translate, via gen_excp.
508 We expect that ENV->PC has already been updated. */
509 void QEMU_NORETURN
helper_excp(CPUAlphaState
*env
, int excp
, int error
)
511 AlphaCPU
*cpu
= alpha_env_get_cpu(env
);
512 CPUState
*cs
= CPU(cpu
);
514 cs
->exception_index
= excp
;
515 env
->error_code
= error
;
519 /* This may be called from any of the helpers to set up EXCEPTION_INDEX. */
520 void QEMU_NORETURN
dynamic_excp(CPUAlphaState
*env
, uintptr_t retaddr
,
523 AlphaCPU
*cpu
= alpha_env_get_cpu(env
);
524 CPUState
*cs
= CPU(cpu
);
526 cs
->exception_index
= excp
;
527 env
->error_code
= error
;
529 cpu_restore_state(cs
, retaddr
);
534 void QEMU_NORETURN
arith_excp(CPUAlphaState
*env
, uintptr_t retaddr
,
535 int exc
, uint64_t mask
)
537 env
->trap_arg0
= exc
;
538 env
->trap_arg1
= mask
;
539 dynamic_excp(env
, retaddr
, EXCP_ARITH
, 0);