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 #if defined(CONFIG_USER_ONLY)
83 int alpha_cpu_handle_mmu_fault(CPUState
*cs
, vaddr address
,
86 AlphaCPU
*cpu
= ALPHA_CPU(cs
);
88 cs
->exception_index
= EXCP_MMFAULT
;
89 cpu
->env
.trap_arg0
= address
;
93 void swap_shadow_regs(CPUAlphaState
*env
)
95 uint64_t i0
, i1
, i2
, i3
, i4
, i5
, i6
, i7
;
106 env
->ir
[8] = env
->shadow
[0];
107 env
->ir
[9] = env
->shadow
[1];
108 env
->ir
[10] = env
->shadow
[2];
109 env
->ir
[11] = env
->shadow
[3];
110 env
->ir
[12] = env
->shadow
[4];
111 env
->ir
[13] = env
->shadow
[5];
112 env
->ir
[14] = env
->shadow
[6];
113 env
->ir
[25] = env
->shadow
[7];
125 /* Returns the OSF/1 entMM failure indication, or -1 on success. */
126 static int get_physical_address(CPUAlphaState
*env
, target_ulong addr
,
127 int prot_need
, int mmu_idx
,
128 target_ulong
*pphys
, int *pprot
)
130 CPUState
*cs
= CPU(alpha_env_get_cpu(env
));
131 target_long saddr
= addr
;
132 target_ulong phys
= 0;
133 target_ulong L1pte
, L2pte
, L3pte
;
134 target_ulong pt
, index
;
138 /* Ensure that the virtual address is properly sign-extended from
139 the last implemented virtual address bit. */
140 if (saddr
>> TARGET_VIRT_ADDR_SPACE_BITS
!= saddr
>> 63) {
144 /* Translate the superpage. */
145 /* ??? When we do more than emulate Unix PALcode, we'll need to
146 determine which KSEG is actually active. */
147 if (saddr
< 0 && ((saddr
>> 41) & 3) == 2) {
148 /* User-space cannot access KSEG addresses. */
149 if (mmu_idx
!= MMU_KERNEL_IDX
) {
153 /* For the benefit of the Typhoon chipset, move bit 40 to bit 43.
154 We would not do this if the 48-bit KSEG is enabled. */
155 phys
= saddr
& ((1ull << 40) - 1);
156 phys
|= (saddr
& (1ull << 40)) << 3;
158 prot
= PAGE_READ
| PAGE_WRITE
| PAGE_EXEC
;
163 /* Interpret the page table exactly like PALcode does. */
167 /* L1 page table read. */
168 index
= (addr
>> (TARGET_PAGE_BITS
+ 20)) & 0x3ff;
169 L1pte
= ldq_phys(cs
->as
, pt
+ index
*8);
171 if (unlikely((L1pte
& PTE_VALID
) == 0)) {
175 if (unlikely((L1pte
& PTE_KRE
) == 0)) {
178 pt
= L1pte
>> 32 << TARGET_PAGE_BITS
;
180 /* L2 page table read. */
181 index
= (addr
>> (TARGET_PAGE_BITS
+ 10)) & 0x3ff;
182 L2pte
= ldq_phys(cs
->as
, pt
+ index
*8);
184 if (unlikely((L2pte
& PTE_VALID
) == 0)) {
188 if (unlikely((L2pte
& PTE_KRE
) == 0)) {
191 pt
= L2pte
>> 32 << TARGET_PAGE_BITS
;
193 /* L3 page table read. */
194 index
= (addr
>> TARGET_PAGE_BITS
) & 0x3ff;
195 L3pte
= ldq_phys(cs
->as
, pt
+ index
*8);
197 phys
= L3pte
>> 32 << TARGET_PAGE_BITS
;
198 if (unlikely((L3pte
& PTE_VALID
) == 0)) {
203 #if PAGE_READ != 1 || PAGE_WRITE != 2 || PAGE_EXEC != 4
204 # error page bits out of date
207 /* Check access violations. */
208 if (L3pte
& (PTE_KRE
<< mmu_idx
)) {
209 prot
|= PAGE_READ
| PAGE_EXEC
;
211 if (L3pte
& (PTE_KWE
<< mmu_idx
)) {
214 if (unlikely((prot
& prot_need
) == 0 && prot_need
)) {
218 /* Check fault-on-operation violations. */
219 prot
&= ~(L3pte
>> 1);
221 if (unlikely((prot
& prot_need
) == 0)) {
222 ret
= (prot_need
& PAGE_EXEC
? MM_K_FOE
:
223 prot_need
& PAGE_WRITE
? MM_K_FOW
:
224 prot_need
& PAGE_READ
? MM_K_FOR
: -1);
233 hwaddr
alpha_cpu_get_phys_page_debug(CPUState
*cs
, vaddr addr
)
235 AlphaCPU
*cpu
= ALPHA_CPU(cs
);
239 fail
= get_physical_address(&cpu
->env
, addr
, 0, 0, &phys
, &prot
);
240 return (fail
>= 0 ? -1 : phys
);
243 int alpha_cpu_handle_mmu_fault(CPUState
*cs
, vaddr addr
, int rw
,
246 AlphaCPU
*cpu
= ALPHA_CPU(cs
);
247 CPUAlphaState
*env
= &cpu
->env
;
251 fail
= get_physical_address(env
, addr
, 1 << rw
, mmu_idx
, &phys
, &prot
);
252 if (unlikely(fail
>= 0)) {
253 cs
->exception_index
= EXCP_MMFAULT
;
254 env
->trap_arg0
= addr
;
255 env
->trap_arg1
= fail
;
256 env
->trap_arg2
= (rw
== 2 ? -1 : rw
);
260 tlb_set_page(cs
, addr
& TARGET_PAGE_MASK
, phys
& TARGET_PAGE_MASK
,
261 prot
, mmu_idx
, TARGET_PAGE_SIZE
);
264 #endif /* USER_ONLY */
266 void alpha_cpu_do_interrupt(CPUState
*cs
)
268 AlphaCPU
*cpu
= ALPHA_CPU(cs
);
269 CPUAlphaState
*env
= &cpu
->env
;
270 int i
= cs
->exception_index
;
272 if (qemu_loglevel_mask(CPU_LOG_INT
)) {
274 const char *name
= "<unknown>";
283 case EXCP_SMP_INTERRUPT
:
284 name
= "smp_interrupt";
286 case EXCP_CLK_INTERRUPT
:
287 name
= "clk_interrupt";
289 case EXCP_DEV_INTERRUPT
:
290 name
= "dev_interrupt";
317 qemu_log("INT %6d: %s(%#x) pc=%016" PRIx64
" sp=%016" PRIx64
"\n",
318 ++count
, name
, env
->error_code
, env
->pc
, env
->ir
[IR_SP
]);
321 cs
->exception_index
= -1;
323 #if !defined(CONFIG_USER_ONLY)
331 case EXCP_SMP_INTERRUPT
:
334 case EXCP_CLK_INTERRUPT
:
337 case EXCP_DEV_INTERRUPT
:
357 /* There are 64 entry points for both privileged and unprivileged,
358 with bit 0x80 indicating unprivileged. Each entry point gets
359 64 bytes to do its job. */
361 i
= 0x2000 + (i
- 0x80) * 64;
367 cpu_abort(cs
, "Unhandled CPU exception");
370 /* Remember where the exception happened. Emulate real hardware in
371 that the low bit of the PC indicates PALmode. */
372 env
->exc_addr
= env
->pc
| env
->pal_mode
;
374 /* Continue execution at the PALcode entry point. */
375 env
->pc
= env
->palbr
+ i
;
377 /* Switch to PALmode. */
378 if (!env
->pal_mode
) {
380 swap_shadow_regs(env
);
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. */
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
->ps
& 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",
444 for (i
= 0; i
< 31; i
++) {
445 cpu_fprintf(f
, "IR%02d %s " TARGET_FMT_lx
" ", i
,
446 linux_reg_names
[i
], env
->ir
[i
]);
448 cpu_fprintf(f
, "\n");
451 cpu_fprintf(f
, "lock_a " TARGET_FMT_lx
" lock_v " TARGET_FMT_lx
"\n",
452 env
->lock_addr
, env
->lock_value
);
454 for (i
= 0; i
< 31; i
++) {
455 cpu_fprintf(f
, "FIR%02d " TARGET_FMT_lx
" ", i
,
456 *((uint64_t *)(&env
->fir
[i
])));
458 cpu_fprintf(f
, "\n");
460 cpu_fprintf(f
, "\n");
463 /* This should only be called from translate, via gen_excp.
464 We expect that ENV->PC has already been updated. */
465 void QEMU_NORETURN
helper_excp(CPUAlphaState
*env
, int excp
, int error
)
467 AlphaCPU
*cpu
= alpha_env_get_cpu(env
);
468 CPUState
*cs
= CPU(cpu
);
470 cs
->exception_index
= excp
;
471 env
->error_code
= error
;
475 /* This may be called from any of the helpers to set up EXCEPTION_INDEX. */
476 void QEMU_NORETURN
dynamic_excp(CPUAlphaState
*env
, uintptr_t retaddr
,
479 AlphaCPU
*cpu
= alpha_env_get_cpu(env
);
480 CPUState
*cs
= CPU(cpu
);
482 cs
->exception_index
= excp
;
483 env
->error_code
= error
;
485 cpu_restore_state(cs
, retaddr
);
486 /* Floating-point exceptions (our only users) point to the next PC. */
492 void QEMU_NORETURN
arith_excp(CPUAlphaState
*env
, uintptr_t retaddr
,
493 int exc
, uint64_t mask
)
495 env
->trap_arg0
= exc
;
496 env
->trap_arg1
= mask
;
497 dynamic_excp(env
, retaddr
, EXCP_ARITH
, 0);