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"
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;
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
);
49 env
->fpcr_exc_enable
= ~t
& FPCR_STATUS_MASK
;
51 switch (fpcr
& FPCR_DYN_MASK
) {
54 t
= float_round_nearest_even
;
56 case FPCR_DYN_CHOPPED
:
57 t
= float_round_to_zero
;
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
86 if (reg
>= 8 && reg
<= 14) {
87 return &env
->shadow
[reg
- 8];
88 } else if (reg
== 25) {
89 return &env
->shadow
[7];
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
,
110 AlphaCPU
*cpu
= ALPHA_CPU(cs
);
112 cs
->exception_index
= EXCP_MMFAULT
;
113 cpu
->env
.trap_arg0
= address
;
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
;
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) {
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
) {
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
;
155 /* Interpret the page table exactly like PALcode does. */
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)) {
167 if (unlikely((L1pte
& PTE_KRE
) == 0)) {
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)) {
180 if (unlikely((L2pte
& PTE_KRE
) == 0)) {
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)) {
195 #if PAGE_READ != 1 || PAGE_WRITE != 2 || PAGE_EXEC != 4
196 # error page bits out of date
199 /* Check access violations. */
200 if (L3pte
& (PTE_KRE
<< mmu_idx
)) {
201 prot
|= PAGE_READ
| PAGE_EXEC
;
203 if (L3pte
& (PTE_KWE
<< mmu_idx
)) {
206 if (unlikely((prot
& prot_need
) == 0 && prot_need
)) {
210 /* Check fault-on-operation violations. */
211 prot
&= ~(L3pte
>> 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);
225 hwaddr
alpha_cpu_get_phys_page_debug(CPUState
*cs
, vaddr addr
)
227 AlphaCPU
*cpu
= ALPHA_CPU(cs
);
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
,
238 AlphaCPU
*cpu
= ALPHA_CPU(cs
);
239 CPUAlphaState
*env
= &cpu
->env
;
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
);
252 tlb_set_page(cs
, addr
& TARGET_PAGE_MASK
, phys
& TARGET_PAGE_MASK
,
253 prot
, mmu_idx
, TARGET_PAGE_SIZE
);
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
)) {
266 const char *name
= "<unknown>";
275 case EXCP_SMP_INTERRUPT
:
276 name
= "smp_interrupt";
278 case EXCP_CLK_INTERRUPT
:
279 name
= "clk_interrupt";
281 case EXCP_DEV_INTERRUPT
:
282 name
= "dev_interrupt";
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)
323 case EXCP_SMP_INTERRUPT
:
326 case EXCP_CLK_INTERRUPT
:
329 case EXCP_DEV_INTERRUPT
:
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. */
353 i
= 0x2000 + (i
- 0x80) * 64;
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. */
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
;
380 /* We never take interrupts while in PALmode. */
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
) {
390 if (interrupt_request
& CPU_INTERRUPT_HARD
) {
391 idx
= EXCP_DEV_INTERRUPT
;
395 if (interrupt_request
& CPU_INTERRUPT_TIMER
) {
396 idx
= EXCP_CLK_INTERRUPT
;
400 if (interrupt_request
& CPU_INTERRUPT_SMP
) {
401 idx
= EXCP_SMP_INTERRUPT
;
405 if (interrupt_request
& CPU_INTERRUPT_MCHK
) {
410 cs
->exception_index
= idx
;
412 alpha_cpu_do_interrupt(cs
);
418 void alpha_cpu_dump_state(CPUState
*cs
, FILE *f
, fprintf_function cpu_fprintf
,
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
;
431 cpu_fprintf(f
, " PC " TARGET_FMT_lx
" PS %02x\n",
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
));
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
])));
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
;
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
,
468 AlphaCPU
*cpu
= alpha_env_get_cpu(env
);
469 CPUState
*cs
= CPU(cpu
);
471 cs
->exception_index
= excp
;
472 env
->error_code
= error
;
474 cpu_restore_state(cs
, retaddr
);
475 /* Floating-point exceptions (our only users) point to the next PC. */
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);