Merge remote-tracking branch 'remotes/lalrae/tags/mips-20150804' into staging
[qemu.git] / target-alpha / helper.c
blob46b8ef9141c26cefb83637cfcfeeeb0f4814bb44
1 /*
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 <stdint.h>
21 #include <stdlib.h>
22 #include <stdio.h>
24 #include "cpu.h"
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;
40 uint32_t t = 0;
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);
48 env->fpcr = fpcr;
49 env->fpcr_exc_enable = ~t & FPCR_STATUS_MASK;
51 switch (fpcr & FPCR_DYN_MASK) {
52 case FPCR_DYN_NORMAL:
53 default:
54 t = float_round_nearest_even;
55 break;
56 case FPCR_DYN_CHOPPED:
57 t = float_round_to_zero;
58 break;
59 case FPCR_DYN_MINUS:
60 t = float_round_down;
61 break;
62 case FPCR_DYN_PLUS:
63 t = float_round_up;
64 break;
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,
84 int rw, int mmu_idx)
86 AlphaCPU *cpu = ALPHA_CPU(cs);
88 cs->exception_index = EXCP_MMFAULT;
89 cpu->env.trap_arg0 = address;
90 return 1;
92 #else
93 void swap_shadow_regs(CPUAlphaState *env)
95 uint64_t i0, i1, i2, i3, i4, i5, i6, i7;
97 i0 = env->ir[8];
98 i1 = env->ir[9];
99 i2 = env->ir[10];
100 i3 = env->ir[11];
101 i4 = env->ir[12];
102 i5 = env->ir[13];
103 i6 = env->ir[14];
104 i7 = env->ir[25];
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];
115 env->shadow[0] = i0;
116 env->shadow[1] = i1;
117 env->shadow[2] = i2;
118 env->shadow[3] = i3;
119 env->shadow[4] = i4;
120 env->shadow[5] = i5;
121 env->shadow[6] = i6;
122 env->shadow[7] = i7;
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;
135 int prot = 0;
136 int ret = MM_K_ACV;
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) {
141 goto exit;
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) {
150 goto exit;
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;
159 ret = -1;
160 goto exit;
163 /* Interpret the page table exactly like PALcode does. */
165 pt = env->ptbr;
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)) {
172 ret = MM_K_TNV;
173 goto exit;
175 if (unlikely((L1pte & PTE_KRE) == 0)) {
176 goto exit;
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)) {
185 ret = MM_K_TNV;
186 goto exit;
188 if (unlikely((L2pte & PTE_KRE) == 0)) {
189 goto exit;
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)) {
199 ret = MM_K_TNV;
200 goto exit;
203 #if PAGE_READ != 1 || PAGE_WRITE != 2 || PAGE_EXEC != 4
204 # error page bits out of date
205 #endif
207 /* Check access violations. */
208 if (L3pte & (PTE_KRE << mmu_idx)) {
209 prot |= PAGE_READ | PAGE_EXEC;
211 if (L3pte & (PTE_KWE << mmu_idx)) {
212 prot |= PAGE_WRITE;
214 if (unlikely((prot & prot_need) == 0 && prot_need)) {
215 goto exit;
218 /* Check fault-on-operation violations. */
219 prot &= ~(L3pte >> 1);
220 ret = -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);
227 exit:
228 *pphys = phys;
229 *pprot = prot;
230 return ret;
233 hwaddr alpha_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
235 AlphaCPU *cpu = ALPHA_CPU(cs);
236 target_ulong phys;
237 int prot, fail;
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,
244 int mmu_idx)
246 AlphaCPU *cpu = ALPHA_CPU(cs);
247 CPUAlphaState *env = &cpu->env;
248 target_ulong phys;
249 int prot, fail;
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);
257 return 1;
260 tlb_set_page(cs, addr & TARGET_PAGE_MASK, phys & TARGET_PAGE_MASK,
261 prot, mmu_idx, TARGET_PAGE_SIZE);
262 return 0;
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)) {
273 static int count;
274 const char *name = "<unknown>";
276 switch (i) {
277 case EXCP_RESET:
278 name = "reset";
279 break;
280 case EXCP_MCHK:
281 name = "mchk";
282 break;
283 case EXCP_SMP_INTERRUPT:
284 name = "smp_interrupt";
285 break;
286 case EXCP_CLK_INTERRUPT:
287 name = "clk_interrupt";
288 break;
289 case EXCP_DEV_INTERRUPT:
290 name = "dev_interrupt";
291 break;
292 case EXCP_MMFAULT:
293 name = "mmfault";
294 break;
295 case EXCP_UNALIGN:
296 name = "unalign";
297 break;
298 case EXCP_OPCDEC:
299 name = "opcdec";
300 break;
301 case EXCP_ARITH:
302 name = "arith";
303 break;
304 case EXCP_FEN:
305 name = "fen";
306 break;
307 case EXCP_CALL_PAL:
308 name = "call_pal";
309 break;
310 case EXCP_STL_C:
311 name = "stl_c";
312 break;
313 case EXCP_STQ_C:
314 name = "stq_c";
315 break;
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)
324 switch (i) {
325 case EXCP_RESET:
326 i = 0x0000;
327 break;
328 case EXCP_MCHK:
329 i = 0x0080;
330 break;
331 case EXCP_SMP_INTERRUPT:
332 i = 0x0100;
333 break;
334 case EXCP_CLK_INTERRUPT:
335 i = 0x0180;
336 break;
337 case EXCP_DEV_INTERRUPT:
338 i = 0x0200;
339 break;
340 case EXCP_MMFAULT:
341 i = 0x0280;
342 break;
343 case EXCP_UNALIGN:
344 i = 0x0300;
345 break;
346 case EXCP_OPCDEC:
347 i = 0x0380;
348 break;
349 case EXCP_ARITH:
350 i = 0x0400;
351 break;
352 case EXCP_FEN:
353 i = 0x0480;
354 break;
355 case EXCP_CALL_PAL:
356 i = env->error_code;
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. */
360 if (i & 0x80) {
361 i = 0x2000 + (i - 0x80) * 64;
362 } else {
363 i = 0x1000 + i * 64;
365 break;
366 default:
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) {
379 env->pal_mode = 1;
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;
389 int idx = -1;
391 /* We never take interrupts while in PALmode. */
392 if (env->pal_mode) {
393 return false;
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) {
400 case 0 ... 3:
401 if (interrupt_request & CPU_INTERRUPT_HARD) {
402 idx = EXCP_DEV_INTERRUPT;
404 /* FALLTHRU */
405 case 4:
406 if (interrupt_request & CPU_INTERRUPT_TIMER) {
407 idx = EXCP_CLK_INTERRUPT;
409 /* FALLTHRU */
410 case 5:
411 if (interrupt_request & CPU_INTERRUPT_SMP) {
412 idx = EXCP_SMP_INTERRUPT;
414 /* FALLTHRU */
415 case 6:
416 if (interrupt_request & CPU_INTERRUPT_MCHK) {
417 idx = EXCP_MCHK;
420 if (idx >= 0) {
421 cs->exception_index = idx;
422 env->error_code = 0;
423 alpha_cpu_do_interrupt(cs);
424 return true;
426 return false;
429 void alpha_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf,
430 int flags)
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;
440 int i;
442 cpu_fprintf(f, " PC " TARGET_FMT_lx " PS %02x\n",
443 env->pc, env->ps);
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]);
447 if ((i % 3) == 2)
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])));
457 if ((i % 3) == 2)
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;
472 cpu_loop_exit(cs);
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,
477 int excp, int error)
479 AlphaCPU *cpu = alpha_env_get_cpu(env);
480 CPUState *cs = CPU(cpu);
482 cs->exception_index = excp;
483 env->error_code = error;
484 if (retaddr) {
485 cpu_restore_state(cs, retaddr);
486 /* Floating-point exceptions (our only users) point to the next PC. */
487 env->pc += 4;
489 cpu_loop_exit(cs);
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);