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.1 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-types.h"
25 #include "exec/helper-proto.h"
26 #include "qemu/qemu-print.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 static const uint8_t rm_map
[] = {
40 [FPCR_DYN_NORMAL
>> FPCR_DYN_SHIFT
] = float_round_nearest_even
,
41 [FPCR_DYN_CHOPPED
>> FPCR_DYN_SHIFT
] = float_round_to_zero
,
42 [FPCR_DYN_MINUS
>> FPCR_DYN_SHIFT
] = float_round_down
,
43 [FPCR_DYN_PLUS
>> FPCR_DYN_SHIFT
] = float_round_up
,
46 uint32_t fpcr
= val
>> 32;
49 /* Record the raw value before adjusting for linux-user. */
52 #ifdef CONFIG_USER_ONLY
54 * Override some of these bits with the contents of ENV->SWCR.
55 * In system mode, some of these would trap to the kernel, at
56 * which point the kernel's handler would emulate and apply
57 * the software exception mask.
59 uint32_t soft_fpcr
= alpha_ieee_swcr_to_fpcr(env
->swcr
) >> 32;
60 fpcr
|= soft_fpcr
& (FPCR_STATUS_MASK
| FPCR_DNZ
);
63 * The IOV exception is disabled by the kernel with SWCR_TRAP_ENABLE_INV,
64 * which got mapped by alpha_ieee_swcr_to_fpcr to FPCR_INVD.
65 * Add FPCR_IOV to fpcr_exc_enable so that it is handled identically.
67 t
|= CONVERT_BIT(soft_fpcr
, FPCR_INVD
, FPCR_IOV
);
70 t
|= CONVERT_BIT(fpcr
, FPCR_INED
, FPCR_INE
);
71 t
|= CONVERT_BIT(fpcr
, FPCR_UNFD
, FPCR_UNF
);
72 t
|= CONVERT_BIT(fpcr
, FPCR_OVFD
, FPCR_OVF
);
73 t
|= CONVERT_BIT(fpcr
, FPCR_DZED
, FPCR_DZE
);
74 t
|= CONVERT_BIT(fpcr
, FPCR_INVD
, FPCR_INV
);
76 env
->fpcr_exc_enable
= ~t
& FPCR_STATUS_MASK
;
78 env
->fpcr_dyn_round
= rm_map
[(fpcr
& FPCR_DYN_MASK
) >> FPCR_DYN_SHIFT
];
79 env
->fp_status
.flush_inputs_to_zero
= (fpcr
& FPCR_DNZ
) != 0;
81 t
= (fpcr
& FPCR_UNFD
) && (fpcr
& FPCR_UNDZ
);
82 #ifdef CONFIG_USER_ONLY
83 t
|= (env
->swcr
& SWCR_MAP_UMZ
) != 0;
85 env
->fpcr_flush_to_zero
= t
;
88 uint64_t helper_load_fpcr(CPUAlphaState
*env
)
90 return cpu_alpha_load_fpcr(env
);
93 void helper_store_fpcr(CPUAlphaState
*env
, uint64_t val
)
95 cpu_alpha_store_fpcr(env
, val
);
98 static uint64_t *cpu_alpha_addr_gr(CPUAlphaState
*env
, unsigned reg
)
100 #ifndef CONFIG_USER_ONLY
101 if (env
->flags
& ENV_FLAG_PAL_MODE
) {
102 if (reg
>= 8 && reg
<= 14) {
103 return &env
->shadow
[reg
- 8];
104 } else if (reg
== 25) {
105 return &env
->shadow
[7];
109 return &env
->ir
[reg
];
112 uint64_t cpu_alpha_load_gr(CPUAlphaState
*env
, unsigned reg
)
114 return *cpu_alpha_addr_gr(env
, reg
);
117 void cpu_alpha_store_gr(CPUAlphaState
*env
, unsigned reg
, uint64_t val
)
119 *cpu_alpha_addr_gr(env
, reg
) = val
;
122 #if defined(CONFIG_USER_ONLY)
123 bool alpha_cpu_tlb_fill(CPUState
*cs
, vaddr address
, int size
,
124 MMUAccessType access_type
, int mmu_idx
,
125 bool probe
, uintptr_t retaddr
)
127 AlphaCPU
*cpu
= ALPHA_CPU(cs
);
129 cs
->exception_index
= EXCP_MMFAULT
;
130 cpu
->env
.trap_arg0
= address
;
131 cpu_loop_exit_restore(cs
, retaddr
);
134 /* Returns the OSF/1 entMM failure indication, or -1 on success. */
135 static int get_physical_address(CPUAlphaState
*env
, target_ulong addr
,
136 int prot_need
, int mmu_idx
,
137 target_ulong
*pphys
, int *pprot
)
139 CPUState
*cs
= env_cpu(env
);
140 target_long saddr
= addr
;
141 target_ulong phys
= 0;
142 target_ulong L1pte
, L2pte
, L3pte
;
143 target_ulong pt
, index
;
147 /* Handle physical accesses. */
148 if (mmu_idx
== MMU_PHYS_IDX
) {
150 prot
= PAGE_READ
| PAGE_WRITE
| PAGE_EXEC
;
155 /* Ensure that the virtual address is properly sign-extended from
156 the last implemented virtual address bit. */
157 if (saddr
>> TARGET_VIRT_ADDR_SPACE_BITS
!= saddr
>> 63) {
161 /* Translate the superpage. */
162 /* ??? When we do more than emulate Unix PALcode, we'll need to
163 determine which KSEG is actually active. */
164 if (saddr
< 0 && ((saddr
>> 41) & 3) == 2) {
165 /* User-space cannot access KSEG addresses. */
166 if (mmu_idx
!= MMU_KERNEL_IDX
) {
170 /* For the benefit of the Typhoon chipset, move bit 40 to bit 43.
171 We would not do this if the 48-bit KSEG is enabled. */
172 phys
= saddr
& ((1ull << 40) - 1);
173 phys
|= (saddr
& (1ull << 40)) << 3;
175 prot
= PAGE_READ
| PAGE_WRITE
| PAGE_EXEC
;
180 /* Interpret the page table exactly like PALcode does. */
184 /* TODO: rather than using ldq_phys() to read the page table we should
185 * use address_space_ldq() so that we can handle the case when
186 * the page table read gives a bus fault, rather than ignoring it.
187 * For the existing code the zero data that ldq_phys will return for
188 * an access to invalid memory will result in our treating the page
189 * table as invalid, which may even be the right behaviour.
192 /* L1 page table read. */
193 index
= (addr
>> (TARGET_PAGE_BITS
+ 20)) & 0x3ff;
194 L1pte
= ldq_phys(cs
->as
, pt
+ index
*8);
196 if (unlikely((L1pte
& PTE_VALID
) == 0)) {
200 if (unlikely((L1pte
& PTE_KRE
) == 0)) {
203 pt
= L1pte
>> 32 << TARGET_PAGE_BITS
;
205 /* L2 page table read. */
206 index
= (addr
>> (TARGET_PAGE_BITS
+ 10)) & 0x3ff;
207 L2pte
= ldq_phys(cs
->as
, pt
+ index
*8);
209 if (unlikely((L2pte
& PTE_VALID
) == 0)) {
213 if (unlikely((L2pte
& PTE_KRE
) == 0)) {
216 pt
= L2pte
>> 32 << TARGET_PAGE_BITS
;
218 /* L3 page table read. */
219 index
= (addr
>> TARGET_PAGE_BITS
) & 0x3ff;
220 L3pte
= ldq_phys(cs
->as
, pt
+ index
*8);
222 phys
= L3pte
>> 32 << TARGET_PAGE_BITS
;
223 if (unlikely((L3pte
& PTE_VALID
) == 0)) {
228 #if PAGE_READ != 1 || PAGE_WRITE != 2 || PAGE_EXEC != 4
229 # error page bits out of date
232 /* Check access violations. */
233 if (L3pte
& (PTE_KRE
<< mmu_idx
)) {
234 prot
|= PAGE_READ
| PAGE_EXEC
;
236 if (L3pte
& (PTE_KWE
<< mmu_idx
)) {
239 if (unlikely((prot
& prot_need
) == 0 && prot_need
)) {
243 /* Check fault-on-operation violations. */
244 prot
&= ~(L3pte
>> 1);
246 if (unlikely((prot
& prot_need
) == 0)) {
247 ret
= (prot_need
& PAGE_EXEC
? MM_K_FOE
:
248 prot_need
& PAGE_WRITE
? MM_K_FOW
:
249 prot_need
& PAGE_READ
? MM_K_FOR
: -1);
258 hwaddr
alpha_cpu_get_phys_page_debug(CPUState
*cs
, vaddr addr
)
260 AlphaCPU
*cpu
= ALPHA_CPU(cs
);
264 fail
= get_physical_address(&cpu
->env
, addr
, 0, 0, &phys
, &prot
);
265 return (fail
>= 0 ? -1 : phys
);
268 bool alpha_cpu_tlb_fill(CPUState
*cs
, vaddr addr
, int size
,
269 MMUAccessType access_type
, int mmu_idx
,
270 bool probe
, uintptr_t retaddr
)
272 AlphaCPU
*cpu
= ALPHA_CPU(cs
);
273 CPUAlphaState
*env
= &cpu
->env
;
277 fail
= get_physical_address(env
, addr
, 1 << access_type
,
278 mmu_idx
, &phys
, &prot
);
279 if (unlikely(fail
>= 0)) {
283 cs
->exception_index
= EXCP_MMFAULT
;
284 env
->trap_arg0
= addr
;
285 env
->trap_arg1
= fail
;
286 env
->trap_arg2
= (access_type
== MMU_DATA_LOAD
? 0ull :
287 access_type
== MMU_DATA_STORE
? 1ull :
288 /* access_type == MMU_INST_FETCH */ -1ull);
289 cpu_loop_exit_restore(cs
, retaddr
);
292 tlb_set_page(cs
, addr
& TARGET_PAGE_MASK
, phys
& TARGET_PAGE_MASK
,
293 prot
, mmu_idx
, TARGET_PAGE_SIZE
);
296 #endif /* USER_ONLY */
298 void alpha_cpu_do_interrupt(CPUState
*cs
)
300 AlphaCPU
*cpu
= ALPHA_CPU(cs
);
301 CPUAlphaState
*env
= &cpu
->env
;
302 int i
= cs
->exception_index
;
304 if (qemu_loglevel_mask(CPU_LOG_INT
)) {
306 const char *name
= "<unknown>";
315 case EXCP_SMP_INTERRUPT
:
316 name
= "smp_interrupt";
318 case EXCP_CLK_INTERRUPT
:
319 name
= "clk_interrupt";
321 case EXCP_DEV_INTERRUPT
:
322 name
= "dev_interrupt";
343 qemu_log("INT %6d: %s(%#x) cpu=%d pc=%016"
344 PRIx64
" sp=%016" PRIx64
"\n",
345 ++count
, name
, env
->error_code
, cs
->cpu_index
,
346 env
->pc
, env
->ir
[IR_SP
]);
349 cs
->exception_index
= -1;
351 #if !defined(CONFIG_USER_ONLY)
359 case EXCP_SMP_INTERRUPT
:
362 case EXCP_CLK_INTERRUPT
:
365 case EXCP_DEV_INTERRUPT
:
385 /* There are 64 entry points for both privileged and unprivileged,
386 with bit 0x80 indicating unprivileged. Each entry point gets
387 64 bytes to do its job. */
389 i
= 0x2000 + (i
- 0x80) * 64;
395 cpu_abort(cs
, "Unhandled CPU exception");
398 /* Remember where the exception happened. Emulate real hardware in
399 that the low bit of the PC indicates PALmode. */
400 env
->exc_addr
= env
->pc
| (env
->flags
& ENV_FLAG_PAL_MODE
);
402 /* Continue execution at the PALcode entry point. */
403 env
->pc
= env
->palbr
+ i
;
405 /* Switch to PALmode. */
406 env
->flags
|= ENV_FLAG_PAL_MODE
;
407 #endif /* !USER_ONLY */
410 bool alpha_cpu_exec_interrupt(CPUState
*cs
, int interrupt_request
)
412 AlphaCPU
*cpu
= ALPHA_CPU(cs
);
413 CPUAlphaState
*env
= &cpu
->env
;
416 /* We never take interrupts while in PALmode. */
417 if (env
->flags
& ENV_FLAG_PAL_MODE
) {
421 /* Fall through the switch, collecting the highest priority
422 interrupt that isn't masked by the processor status IPL. */
423 /* ??? This hard-codes the OSF/1 interrupt levels. */
424 switch ((env
->flags
>> ENV_FLAG_PS_SHIFT
) & PS_INT_MASK
) {
426 if (interrupt_request
& CPU_INTERRUPT_HARD
) {
427 idx
= EXCP_DEV_INTERRUPT
;
431 if (interrupt_request
& CPU_INTERRUPT_TIMER
) {
432 idx
= EXCP_CLK_INTERRUPT
;
436 if (interrupt_request
& CPU_INTERRUPT_SMP
) {
437 idx
= EXCP_SMP_INTERRUPT
;
441 if (interrupt_request
& CPU_INTERRUPT_MCHK
) {
446 cs
->exception_index
= idx
;
448 alpha_cpu_do_interrupt(cs
);
454 void alpha_cpu_dump_state(CPUState
*cs
, FILE *f
, int flags
)
456 static const char linux_reg_names
[31][4] = {
457 "v0", "t0", "t1", "t2", "t3", "t4", "t5", "t6",
458 "t7", "s0", "s1", "s2", "s3", "s4", "s5", "fp",
459 "a0", "a1", "a2", "a3", "a4", "a5", "t8", "t9",
460 "t10", "t11", "ra", "t12", "at", "gp", "sp"
462 AlphaCPU
*cpu
= ALPHA_CPU(cs
);
463 CPUAlphaState
*env
= &cpu
->env
;
466 qemu_fprintf(f
, "PC " TARGET_FMT_lx
" PS %02x\n",
467 env
->pc
, extract32(env
->flags
, ENV_FLAG_PS_SHIFT
, 8));
468 for (i
= 0; i
< 31; i
++) {
469 qemu_fprintf(f
, "%-8s" TARGET_FMT_lx
"%c",
470 linux_reg_names
[i
], cpu_alpha_load_gr(env
, i
),
471 (i
% 3) == 2 ? '\n' : ' ');
474 qemu_fprintf(f
, "lock_a " TARGET_FMT_lx
" lock_v " TARGET_FMT_lx
"\n",
475 env
->lock_addr
, env
->lock_value
);
477 if (flags
& CPU_DUMP_FPU
) {
478 for (i
= 0; i
< 31; i
++) {
479 qemu_fprintf(f
, "f%-7d%016" PRIx64
"%c", i
, env
->fir
[i
],
480 (i
% 3) == 2 ? '\n' : ' ');
482 qemu_fprintf(f
, "fpcr %016" PRIx64
"\n", cpu_alpha_load_fpcr(env
));
484 qemu_fprintf(f
, "\n");
487 /* This should only be called from translate, via gen_excp.
488 We expect that ENV->PC has already been updated. */
489 void QEMU_NORETURN
helper_excp(CPUAlphaState
*env
, int excp
, int error
)
491 CPUState
*cs
= env_cpu(env
);
493 cs
->exception_index
= excp
;
494 env
->error_code
= error
;
498 /* This may be called from any of the helpers to set up EXCEPTION_INDEX. */
499 void QEMU_NORETURN
dynamic_excp(CPUAlphaState
*env
, uintptr_t retaddr
,
502 CPUState
*cs
= env_cpu(env
);
504 cs
->exception_index
= excp
;
505 env
->error_code
= error
;
507 cpu_restore_state(cs
, retaddr
, true);
508 /* Floating-point exceptions (our only users) point to the next PC. */
514 void QEMU_NORETURN
arith_excp(CPUAlphaState
*env
, uintptr_t retaddr
,
515 int exc
, uint64_t mask
)
517 env
->trap_arg0
= exc
;
518 env
->trap_arg1
= mask
;
519 dynamic_excp(env
, retaddr
, EXCP_ARITH
, 0);