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 "qemu/osdep.h"
23 #include "exec/exec-all.h"
24 #include "fpu/softfloat.h"
25 #include "exec/helper-proto.h"
28 #define CONVERT_BIT(X, SRC, DST) \
29 (SRC > DST ? (X) / (SRC / DST) & (DST) : ((X) & SRC) * (DST / SRC))
31 uint64_t cpu_alpha_load_fpcr (CPUAlphaState
*env
)
33 return (uint64_t)env
->fpcr
<< 32;
36 void cpu_alpha_store_fpcr (CPUAlphaState
*env
, uint64_t val
)
38 uint32_t fpcr
= val
>> 32;
41 t
|= CONVERT_BIT(fpcr
, FPCR_INED
, FPCR_INE
);
42 t
|= CONVERT_BIT(fpcr
, FPCR_UNFD
, FPCR_UNF
);
43 t
|= CONVERT_BIT(fpcr
, FPCR_OVFD
, FPCR_OVF
);
44 t
|= CONVERT_BIT(fpcr
, FPCR_DZED
, FPCR_DZE
);
45 t
|= CONVERT_BIT(fpcr
, FPCR_INVD
, FPCR_INV
);
48 env
->fpcr_exc_enable
= ~t
& FPCR_STATUS_MASK
;
50 switch (fpcr
& FPCR_DYN_MASK
) {
53 t
= float_round_nearest_even
;
55 case FPCR_DYN_CHOPPED
:
56 t
= float_round_to_zero
;
65 env
->fpcr_dyn_round
= t
;
67 env
->fpcr_flush_to_zero
= (fpcr
& FPCR_UNFD
) && (fpcr
& FPCR_UNDZ
);
68 env
->fp_status
.flush_inputs_to_zero
= (fpcr
& FPCR_DNZ
) != 0;
71 uint64_t helper_load_fpcr(CPUAlphaState
*env
)
73 return cpu_alpha_load_fpcr(env
);
76 void helper_store_fpcr(CPUAlphaState
*env
, uint64_t val
)
78 cpu_alpha_store_fpcr(env
, val
);
81 static uint64_t *cpu_alpha_addr_gr(CPUAlphaState
*env
, unsigned reg
)
83 #ifndef CONFIG_USER_ONLY
84 if (env
->flags
& ENV_FLAG_PAL_MODE
) {
85 if (reg
>= 8 && reg
<= 14) {
86 return &env
->shadow
[reg
- 8];
87 } else if (reg
== 25) {
88 return &env
->shadow
[7];
95 uint64_t cpu_alpha_load_gr(CPUAlphaState
*env
, unsigned reg
)
97 return *cpu_alpha_addr_gr(env
, reg
);
100 void cpu_alpha_store_gr(CPUAlphaState
*env
, unsigned reg
, uint64_t val
)
102 *cpu_alpha_addr_gr(env
, reg
) = val
;
105 #if defined(CONFIG_USER_ONLY)
106 int alpha_cpu_handle_mmu_fault(CPUState
*cs
, vaddr address
, int size
,
109 AlphaCPU
*cpu
= ALPHA_CPU(cs
);
111 cs
->exception_index
= EXCP_MMFAULT
;
112 cpu
->env
.trap_arg0
= address
;
116 /* Returns the OSF/1 entMM failure indication, or -1 on success. */
117 static int get_physical_address(CPUAlphaState
*env
, target_ulong addr
,
118 int prot_need
, int mmu_idx
,
119 target_ulong
*pphys
, int *pprot
)
121 CPUState
*cs
= CPU(alpha_env_get_cpu(env
));
122 target_long saddr
= addr
;
123 target_ulong phys
= 0;
124 target_ulong L1pte
, L2pte
, L3pte
;
125 target_ulong pt
, index
;
129 /* Handle physical accesses. */
130 if (mmu_idx
== MMU_PHYS_IDX
) {
132 prot
= PAGE_READ
| PAGE_WRITE
| PAGE_EXEC
;
137 /* Ensure that the virtual address is properly sign-extended from
138 the last implemented virtual address bit. */
139 if (saddr
>> TARGET_VIRT_ADDR_SPACE_BITS
!= saddr
>> 63) {
143 /* Translate the superpage. */
144 /* ??? When we do more than emulate Unix PALcode, we'll need to
145 determine which KSEG is actually active. */
146 if (saddr
< 0 && ((saddr
>> 41) & 3) == 2) {
147 /* User-space cannot access KSEG addresses. */
148 if (mmu_idx
!= MMU_KERNEL_IDX
) {
152 /* For the benefit of the Typhoon chipset, move bit 40 to bit 43.
153 We would not do this if the 48-bit KSEG is enabled. */
154 phys
= saddr
& ((1ull << 40) - 1);
155 phys
|= (saddr
& (1ull << 40)) << 3;
157 prot
= PAGE_READ
| PAGE_WRITE
| PAGE_EXEC
;
162 /* Interpret the page table exactly like PALcode does. */
166 /* TODO: rather than using ldq_phys() to read the page table we should
167 * use address_space_ldq() so that we can handle the case when
168 * the page table read gives a bus fault, rather than ignoring it.
169 * For the existing code the zero data that ldq_phys will return for
170 * an access to invalid memory will result in our treating the page
171 * table as invalid, which may even be the right behaviour.
174 /* L1 page table read. */
175 index
= (addr
>> (TARGET_PAGE_BITS
+ 20)) & 0x3ff;
176 L1pte
= ldq_phys(cs
->as
, pt
+ index
*8);
178 if (unlikely((L1pte
& PTE_VALID
) == 0)) {
182 if (unlikely((L1pte
& PTE_KRE
) == 0)) {
185 pt
= L1pte
>> 32 << TARGET_PAGE_BITS
;
187 /* L2 page table read. */
188 index
= (addr
>> (TARGET_PAGE_BITS
+ 10)) & 0x3ff;
189 L2pte
= ldq_phys(cs
->as
, pt
+ index
*8);
191 if (unlikely((L2pte
& PTE_VALID
) == 0)) {
195 if (unlikely((L2pte
& PTE_KRE
) == 0)) {
198 pt
= L2pte
>> 32 << TARGET_PAGE_BITS
;
200 /* L3 page table read. */
201 index
= (addr
>> TARGET_PAGE_BITS
) & 0x3ff;
202 L3pte
= ldq_phys(cs
->as
, pt
+ index
*8);
204 phys
= L3pte
>> 32 << TARGET_PAGE_BITS
;
205 if (unlikely((L3pte
& PTE_VALID
) == 0)) {
210 #if PAGE_READ != 1 || PAGE_WRITE != 2 || PAGE_EXEC != 4
211 # error page bits out of date
214 /* Check access violations. */
215 if (L3pte
& (PTE_KRE
<< mmu_idx
)) {
216 prot
|= PAGE_READ
| PAGE_EXEC
;
218 if (L3pte
& (PTE_KWE
<< mmu_idx
)) {
221 if (unlikely((prot
& prot_need
) == 0 && prot_need
)) {
225 /* Check fault-on-operation violations. */
226 prot
&= ~(L3pte
>> 1);
228 if (unlikely((prot
& prot_need
) == 0)) {
229 ret
= (prot_need
& PAGE_EXEC
? MM_K_FOE
:
230 prot_need
& PAGE_WRITE
? MM_K_FOW
:
231 prot_need
& PAGE_READ
? MM_K_FOR
: -1);
240 hwaddr
alpha_cpu_get_phys_page_debug(CPUState
*cs
, vaddr addr
)
242 AlphaCPU
*cpu
= ALPHA_CPU(cs
);
246 fail
= get_physical_address(&cpu
->env
, addr
, 0, 0, &phys
, &prot
);
247 return (fail
>= 0 ? -1 : phys
);
250 int alpha_cpu_handle_mmu_fault(CPUState
*cs
, vaddr addr
, int size
, int rw
,
253 AlphaCPU
*cpu
= ALPHA_CPU(cs
);
254 CPUAlphaState
*env
= &cpu
->env
;
258 fail
= get_physical_address(env
, addr
, 1 << rw
, mmu_idx
, &phys
, &prot
);
259 if (unlikely(fail
>= 0)) {
260 cs
->exception_index
= EXCP_MMFAULT
;
261 env
->trap_arg0
= addr
;
262 env
->trap_arg1
= fail
;
263 env
->trap_arg2
= (rw
== 2 ? -1 : rw
);
267 tlb_set_page(cs
, addr
& TARGET_PAGE_MASK
, phys
& TARGET_PAGE_MASK
,
268 prot
, mmu_idx
, TARGET_PAGE_SIZE
);
271 #endif /* USER_ONLY */
273 void alpha_cpu_do_interrupt(CPUState
*cs
)
275 AlphaCPU
*cpu
= ALPHA_CPU(cs
);
276 CPUAlphaState
*env
= &cpu
->env
;
277 int i
= cs
->exception_index
;
279 if (qemu_loglevel_mask(CPU_LOG_INT
)) {
281 const char *name
= "<unknown>";
290 case EXCP_SMP_INTERRUPT
:
291 name
= "smp_interrupt";
293 case EXCP_CLK_INTERRUPT
:
294 name
= "clk_interrupt";
296 case EXCP_DEV_INTERRUPT
:
297 name
= "dev_interrupt";
318 qemu_log("INT %6d: %s(%#x) cpu=%d pc=%016"
319 PRIx64
" sp=%016" PRIx64
"\n",
320 ++count
, name
, env
->error_code
, cs
->cpu_index
,
321 env
->pc
, env
->ir
[IR_SP
]);
324 cs
->exception_index
= -1;
326 #if !defined(CONFIG_USER_ONLY)
334 case EXCP_SMP_INTERRUPT
:
337 case EXCP_CLK_INTERRUPT
:
340 case EXCP_DEV_INTERRUPT
:
360 /* There are 64 entry points for both privileged and unprivileged,
361 with bit 0x80 indicating unprivileged. Each entry point gets
362 64 bytes to do its job. */
364 i
= 0x2000 + (i
- 0x80) * 64;
370 cpu_abort(cs
, "Unhandled CPU exception");
373 /* Remember where the exception happened. Emulate real hardware in
374 that the low bit of the PC indicates PALmode. */
375 env
->exc_addr
= env
->pc
| (env
->flags
& ENV_FLAG_PAL_MODE
);
377 /* Continue execution at the PALcode entry point. */
378 env
->pc
= env
->palbr
+ i
;
380 /* Switch to PALmode. */
381 env
->flags
|= ENV_FLAG_PAL_MODE
;
382 #endif /* !USER_ONLY */
385 bool alpha_cpu_exec_interrupt(CPUState
*cs
, int interrupt_request
)
387 AlphaCPU
*cpu
= ALPHA_CPU(cs
);
388 CPUAlphaState
*env
= &cpu
->env
;
391 /* We never take interrupts while in PALmode. */
392 if (env
->flags
& ENV_FLAG_PAL_MODE
) {
396 /* Fall through the switch, collecting the highest priority
397 interrupt that isn't masked by the processor status IPL. */
398 /* ??? This hard-codes the OSF/1 interrupt levels. */
399 switch ((env
->flags
>> ENV_FLAG_PS_SHIFT
) & PS_INT_MASK
) {
401 if (interrupt_request
& CPU_INTERRUPT_HARD
) {
402 idx
= EXCP_DEV_INTERRUPT
;
406 if (interrupt_request
& CPU_INTERRUPT_TIMER
) {
407 idx
= EXCP_CLK_INTERRUPT
;
411 if (interrupt_request
& CPU_INTERRUPT_SMP
) {
412 idx
= EXCP_SMP_INTERRUPT
;
416 if (interrupt_request
& CPU_INTERRUPT_MCHK
) {
421 cs
->exception_index
= idx
;
423 alpha_cpu_do_interrupt(cs
);
429 void alpha_cpu_dump_state(CPUState
*cs
, FILE *f
, fprintf_function cpu_fprintf
,
432 static const char *linux_reg_names
[] = {
433 "v0 ", "t0 ", "t1 ", "t2 ", "t3 ", "t4 ", "t5 ", "t6 ",
434 "t7 ", "s0 ", "s1 ", "s2 ", "s3 ", "s4 ", "s5 ", "fp ",
435 "a0 ", "a1 ", "a2 ", "a3 ", "a4 ", "a5 ", "t8 ", "t9 ",
436 "t10", "t11", "ra ", "t12", "at ", "gp ", "sp ", "zero",
438 AlphaCPU
*cpu
= ALPHA_CPU(cs
);
439 CPUAlphaState
*env
= &cpu
->env
;
442 cpu_fprintf(f
, " PC " TARGET_FMT_lx
" PS %02x\n",
443 env
->pc
, extract32(env
->flags
, ENV_FLAG_PS_SHIFT
, 8));
444 for (i
= 0; i
< 31; i
++) {
445 cpu_fprintf(f
, "IR%02d %s " TARGET_FMT_lx
"%c", i
,
446 linux_reg_names
[i
], cpu_alpha_load_gr(env
, i
),
447 (i
% 3) == 2 ? '\n' : ' ');
450 cpu_fprintf(f
, "lock_a " TARGET_FMT_lx
" lock_v " TARGET_FMT_lx
"\n",
451 env
->lock_addr
, env
->lock_value
);
453 if (flags
& CPU_DUMP_FPU
) {
454 for (i
= 0; i
< 31; i
++) {
455 cpu_fprintf(f
, "FIR%02d %016" PRIx64
"%c", i
, env
->fir
[i
],
456 (i
% 3) == 2 ? '\n' : ' ');
459 cpu_fprintf(f
, "\n");
462 /* This should only be called from translate, via gen_excp.
463 We expect that ENV->PC has already been updated. */
464 void QEMU_NORETURN
helper_excp(CPUAlphaState
*env
, int excp
, int error
)
466 AlphaCPU
*cpu
= alpha_env_get_cpu(env
);
467 CPUState
*cs
= CPU(cpu
);
469 cs
->exception_index
= excp
;
470 env
->error_code
= error
;
474 /* This may be called from any of the helpers to set up EXCEPTION_INDEX. */
475 void QEMU_NORETURN
dynamic_excp(CPUAlphaState
*env
, uintptr_t retaddr
,
478 AlphaCPU
*cpu
= alpha_env_get_cpu(env
);
479 CPUState
*cs
= CPU(cpu
);
481 cs
->exception_index
= excp
;
482 env
->error_code
= error
;
484 cpu_restore_state(cs
, retaddr
, true);
485 /* Floating-point exceptions (our only users) point to the next PC. */
491 void QEMU_NORETURN
arith_excp(CPUAlphaState
*env
, uintptr_t retaddr
,
492 int exc
, uint64_t mask
)
494 env
->trap_arg0
= exc
;
495 env
->trap_arg1
= mask
;
496 dynamic_excp(env
, retaddr
, EXCP_ARITH
, 0);