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"
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 cpu_alpha_handle_mmu_fault(CPUAlphaState
*env
, target_ulong address
,
174 env
->exception_index
= EXCP_MMFAULT
;
175 env
->trap_arg0
= address
;
179 void swap_shadow_regs(CPUAlphaState
*env
)
181 uint64_t i0
, i1
, i2
, i3
, i4
, i5
, i6
, i7
;
192 env
->ir
[8] = env
->shadow
[0];
193 env
->ir
[9] = env
->shadow
[1];
194 env
->ir
[10] = env
->shadow
[2];
195 env
->ir
[11] = env
->shadow
[3];
196 env
->ir
[12] = env
->shadow
[4];
197 env
->ir
[13] = env
->shadow
[5];
198 env
->ir
[14] = env
->shadow
[6];
199 env
->ir
[25] = env
->shadow
[7];
211 /* Returns the OSF/1 entMM failure indication, or -1 on success. */
212 static int get_physical_address(CPUAlphaState
*env
, target_ulong addr
,
213 int prot_need
, int mmu_idx
,
214 target_ulong
*pphys
, int *pprot
)
216 CPUState
*cs
= CPU(alpha_env_get_cpu(env
));
217 target_long saddr
= addr
;
218 target_ulong phys
= 0;
219 target_ulong L1pte
, L2pte
, L3pte
;
220 target_ulong pt
, index
;
224 /* Ensure that the virtual address is properly sign-extended from
225 the last implemented virtual address bit. */
226 if (saddr
>> TARGET_VIRT_ADDR_SPACE_BITS
!= saddr
>> 63) {
230 /* Translate the superpage. */
231 /* ??? When we do more than emulate Unix PALcode, we'll need to
232 determine which KSEG is actually active. */
233 if (saddr
< 0 && ((saddr
>> 41) & 3) == 2) {
234 /* User-space cannot access KSEG addresses. */
235 if (mmu_idx
!= MMU_KERNEL_IDX
) {
239 /* For the benefit of the Typhoon chipset, move bit 40 to bit 43.
240 We would not do this if the 48-bit KSEG is enabled. */
241 phys
= saddr
& ((1ull << 40) - 1);
242 phys
|= (saddr
& (1ull << 40)) << 3;
244 prot
= PAGE_READ
| PAGE_WRITE
| PAGE_EXEC
;
249 /* Interpret the page table exactly like PALcode does. */
253 /* L1 page table read. */
254 index
= (addr
>> (TARGET_PAGE_BITS
+ 20)) & 0x3ff;
255 L1pte
= ldq_phys(cs
->as
, pt
+ index
*8);
257 if (unlikely((L1pte
& PTE_VALID
) == 0)) {
261 if (unlikely((L1pte
& PTE_KRE
) == 0)) {
264 pt
= L1pte
>> 32 << TARGET_PAGE_BITS
;
266 /* L2 page table read. */
267 index
= (addr
>> (TARGET_PAGE_BITS
+ 10)) & 0x3ff;
268 L2pte
= ldq_phys(cs
->as
, pt
+ index
*8);
270 if (unlikely((L2pte
& PTE_VALID
) == 0)) {
274 if (unlikely((L2pte
& PTE_KRE
) == 0)) {
277 pt
= L2pte
>> 32 << TARGET_PAGE_BITS
;
279 /* L3 page table read. */
280 index
= (addr
>> TARGET_PAGE_BITS
) & 0x3ff;
281 L3pte
= ldq_phys(cs
->as
, pt
+ index
*8);
283 phys
= L3pte
>> 32 << TARGET_PAGE_BITS
;
284 if (unlikely((L3pte
& PTE_VALID
) == 0)) {
289 #if PAGE_READ != 1 || PAGE_WRITE != 2 || PAGE_EXEC != 4
290 # error page bits out of date
293 /* Check access violations. */
294 if (L3pte
& (PTE_KRE
<< mmu_idx
)) {
295 prot
|= PAGE_READ
| PAGE_EXEC
;
297 if (L3pte
& (PTE_KWE
<< mmu_idx
)) {
300 if (unlikely((prot
& prot_need
) == 0 && prot_need
)) {
304 /* Check fault-on-operation violations. */
305 prot
&= ~(L3pte
>> 1);
307 if (unlikely((prot
& prot_need
) == 0)) {
308 ret
= (prot_need
& PAGE_EXEC
? MM_K_FOE
:
309 prot_need
& PAGE_WRITE
? MM_K_FOW
:
310 prot_need
& PAGE_READ
? MM_K_FOR
: -1);
319 hwaddr
alpha_cpu_get_phys_page_debug(CPUState
*cs
, vaddr addr
)
321 AlphaCPU
*cpu
= ALPHA_CPU(cs
);
325 fail
= get_physical_address(&cpu
->env
, addr
, 0, 0, &phys
, &prot
);
326 return (fail
>= 0 ? -1 : phys
);
329 int cpu_alpha_handle_mmu_fault(CPUAlphaState
*env
, target_ulong addr
, int rw
,
335 fail
= get_physical_address(env
, addr
, 1 << rw
, mmu_idx
, &phys
, &prot
);
336 if (unlikely(fail
>= 0)) {
337 env
->exception_index
= EXCP_MMFAULT
;
338 env
->trap_arg0
= addr
;
339 env
->trap_arg1
= fail
;
340 env
->trap_arg2
= (rw
== 2 ? -1 : rw
);
344 tlb_set_page(env
, addr
& TARGET_PAGE_MASK
, phys
& TARGET_PAGE_MASK
,
345 prot
, mmu_idx
, TARGET_PAGE_SIZE
);
348 #endif /* USER_ONLY */
350 void alpha_cpu_do_interrupt(CPUState
*cs
)
352 AlphaCPU
*cpu
= ALPHA_CPU(cs
);
353 CPUAlphaState
*env
= &cpu
->env
;
354 int i
= env
->exception_index
;
356 if (qemu_loglevel_mask(CPU_LOG_INT
)) {
358 const char *name
= "<unknown>";
367 case EXCP_SMP_INTERRUPT
:
368 name
= "smp_interrupt";
370 case EXCP_CLK_INTERRUPT
:
371 name
= "clk_interrupt";
373 case EXCP_DEV_INTERRUPT
:
374 name
= "dev_interrupt";
401 qemu_log("INT %6d: %s(%#x) pc=%016" PRIx64
" sp=%016" PRIx64
"\n",
402 ++count
, name
, env
->error_code
, env
->pc
, env
->ir
[IR_SP
]);
405 env
->exception_index
= -1;
407 #if !defined(CONFIG_USER_ONLY)
415 case EXCP_SMP_INTERRUPT
:
418 case EXCP_CLK_INTERRUPT
:
421 case EXCP_DEV_INTERRUPT
:
441 /* There are 64 entry points for both privileged and unprivileged,
442 with bit 0x80 indicating unprivileged. Each entry point gets
443 64 bytes to do its job. */
445 i
= 0x2000 + (i
- 0x80) * 64;
451 cpu_abort(env
, "Unhandled CPU exception");
454 /* Remember where the exception happened. Emulate real hardware in
455 that the low bit of the PC indicates PALmode. */
456 env
->exc_addr
= env
->pc
| env
->pal_mode
;
458 /* Continue execution at the PALcode entry point. */
459 env
->pc
= env
->palbr
+ i
;
461 /* Switch to PALmode. */
462 if (!env
->pal_mode
) {
464 swap_shadow_regs(env
);
466 #endif /* !USER_ONLY */
469 void alpha_cpu_dump_state(CPUState
*cs
, FILE *f
, fprintf_function cpu_fprintf
,
472 static const char *linux_reg_names
[] = {
473 "v0 ", "t0 ", "t1 ", "t2 ", "t3 ", "t4 ", "t5 ", "t6 ",
474 "t7 ", "s0 ", "s1 ", "s2 ", "s3 ", "s4 ", "s5 ", "fp ",
475 "a0 ", "a1 ", "a2 ", "a3 ", "a4 ", "a5 ", "t8 ", "t9 ",
476 "t10", "t11", "ra ", "t12", "at ", "gp ", "sp ", "zero",
478 AlphaCPU
*cpu
= ALPHA_CPU(cs
);
479 CPUAlphaState
*env
= &cpu
->env
;
482 cpu_fprintf(f
, " PC " TARGET_FMT_lx
" PS %02x\n",
484 for (i
= 0; i
< 31; i
++) {
485 cpu_fprintf(f
, "IR%02d %s " TARGET_FMT_lx
" ", i
,
486 linux_reg_names
[i
], env
->ir
[i
]);
488 cpu_fprintf(f
, "\n");
491 cpu_fprintf(f
, "lock_a " TARGET_FMT_lx
" lock_v " TARGET_FMT_lx
"\n",
492 env
->lock_addr
, env
->lock_value
);
494 for (i
= 0; i
< 31; i
++) {
495 cpu_fprintf(f
, "FIR%02d " TARGET_FMT_lx
" ", i
,
496 *((uint64_t *)(&env
->fir
[i
])));
498 cpu_fprintf(f
, "\n");
500 cpu_fprintf(f
, "\n");
503 /* This should only be called from translate, via gen_excp.
504 We expect that ENV->PC has already been updated. */
505 void QEMU_NORETURN
helper_excp(CPUAlphaState
*env
, int excp
, int error
)
507 env
->exception_index
= excp
;
508 env
->error_code
= error
;
512 /* This may be called from any of the helpers to set up EXCEPTION_INDEX. */
513 void QEMU_NORETURN
dynamic_excp(CPUAlphaState
*env
, uintptr_t retaddr
,
516 env
->exception_index
= excp
;
517 env
->error_code
= error
;
519 cpu_restore_state(env
, retaddr
);
524 void QEMU_NORETURN
arith_excp(CPUAlphaState
*env
, uintptr_t retaddr
,
525 int exc
, uint64_t mask
)
527 env
->trap_arg0
= exc
;
528 env
->trap_arg1
= mask
;
529 dynamic_excp(env
, retaddr
, EXCP_ARITH
, 0);