|
|
1.1 root 1: /*
2: * Alpha emulation cpu helpers for qemu.
3: *
4: * Copyright (c) 2007 Jocelyn Mayer
5: *
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.
10: *
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.
15: *
16: * You should have received a copy of the GNU Lesser General Public
1.1.1.3 root 17: * License along with this library; if not, see <http://www.gnu.org/licenses/>.
1.1 root 18: */
19:
20: #include <stdint.h>
21: #include <stdlib.h>
22: #include <stdio.h>
23:
24: #include "cpu.h"
1.1.1.4 root 25: #include "softfloat.h"
1.1.1.8 ! root 26: #include "helper.h"
1.1.1.4 root 27:
1.1.1.8 ! root 28: uint64_t cpu_alpha_load_fpcr (CPUAlphaState *env)
1.1.1.4 root 29: {
30: uint64_t r = 0;
31: uint8_t t;
32:
33: t = env->fpcr_exc_status;
34: if (t) {
35: r = FPCR_SUM;
36: if (t & float_flag_invalid) {
37: r |= FPCR_INV;
38: }
39: if (t & float_flag_divbyzero) {
40: r |= FPCR_DZE;
41: }
42: if (t & float_flag_overflow) {
43: r |= FPCR_OVF;
44: }
45: if (t & float_flag_underflow) {
46: r |= FPCR_UNF;
47: }
48: if (t & float_flag_inexact) {
49: r |= FPCR_INE;
50: }
51: }
52:
53: t = env->fpcr_exc_mask;
54: if (t & float_flag_invalid) {
55: r |= FPCR_INVD;
56: }
57: if (t & float_flag_divbyzero) {
58: r |= FPCR_DZED;
59: }
60: if (t & float_flag_overflow) {
61: r |= FPCR_OVFD;
62: }
63: if (t & float_flag_underflow) {
64: r |= FPCR_UNFD;
65: }
66: if (t & float_flag_inexact) {
67: r |= FPCR_INED;
68: }
69:
70: switch (env->fpcr_dyn_round) {
71: case float_round_nearest_even:
72: r |= FPCR_DYN_NORMAL;
73: break;
74: case float_round_down:
75: r |= FPCR_DYN_MINUS;
76: break;
77: case float_round_up:
78: r |= FPCR_DYN_PLUS;
79: break;
80: case float_round_to_zero:
81: r |= FPCR_DYN_CHOPPED;
82: break;
83: }
84:
1.1.1.8 ! root 85: if (env->fp_status.flush_inputs_to_zero) {
1.1.1.4 root 86: r |= FPCR_DNZ;
87: }
88: if (env->fpcr_dnod) {
89: r |= FPCR_DNOD;
90: }
91: if (env->fpcr_undz) {
92: r |= FPCR_UNDZ;
93: }
94:
95: return r;
96: }
97:
1.1.1.8 ! root 98: void cpu_alpha_store_fpcr (CPUAlphaState *env, uint64_t val)
1.1.1.4 root 99: {
100: uint8_t t;
101:
102: t = 0;
103: if (val & FPCR_INV) {
104: t |= float_flag_invalid;
105: }
106: if (val & FPCR_DZE) {
107: t |= float_flag_divbyzero;
108: }
109: if (val & FPCR_OVF) {
110: t |= float_flag_overflow;
111: }
112: if (val & FPCR_UNF) {
113: t |= float_flag_underflow;
114: }
115: if (val & FPCR_INE) {
116: t |= float_flag_inexact;
117: }
118: env->fpcr_exc_status = t;
119:
120: t = 0;
121: if (val & FPCR_INVD) {
122: t |= float_flag_invalid;
123: }
124: if (val & FPCR_DZED) {
125: t |= float_flag_divbyzero;
126: }
127: if (val & FPCR_OVFD) {
128: t |= float_flag_overflow;
129: }
130: if (val & FPCR_UNFD) {
131: t |= float_flag_underflow;
132: }
133: if (val & FPCR_INED) {
134: t |= float_flag_inexact;
135: }
136: env->fpcr_exc_mask = t;
137:
138: switch (val & FPCR_DYN_MASK) {
139: case FPCR_DYN_CHOPPED:
140: t = float_round_to_zero;
141: break;
142: case FPCR_DYN_MINUS:
143: t = float_round_down;
144: break;
145: case FPCR_DYN_NORMAL:
146: t = float_round_nearest_even;
147: break;
148: case FPCR_DYN_PLUS:
149: t = float_round_up;
150: break;
151: }
152: env->fpcr_dyn_round = t;
153:
154: env->fpcr_dnod = (val & FPCR_DNOD) != 0;
155: env->fpcr_undz = (val & FPCR_UNDZ) != 0;
1.1.1.8 ! root 156: env->fpcr_flush_to_zero = env->fpcr_dnod & env->fpcr_undz;
! 157: env->fp_status.flush_inputs_to_zero = (val & FPCR_DNZ) != 0;
! 158: }
! 159:
! 160: uint64_t helper_load_fpcr(CPUAlphaState *env)
! 161: {
! 162: return cpu_alpha_load_fpcr(env);
! 163: }
! 164:
! 165: void helper_store_fpcr(CPUAlphaState *env, uint64_t val)
! 166: {
! 167: cpu_alpha_store_fpcr(env, val);
1.1.1.4 root 168: }
1.1 root 169:
170: #if defined(CONFIG_USER_ONLY)
1.1.1.8 ! root 171: int cpu_alpha_handle_mmu_fault(CPUAlphaState *env, target_ulong address,
! 172: int rw, int mmu_idx)
1.1 root 173: {
1.1.1.6 root 174: env->exception_index = EXCP_MMFAULT;
175: env->trap_arg0 = address;
1.1 root 176: return 1;
177: }
1.1.1.6 root 178: #else
1.1.1.8 ! root 179: void swap_shadow_regs(CPUAlphaState *env)
1.1 root 180: {
1.1.1.6 root 181: uint64_t i0, i1, i2, i3, i4, i5, i6, i7;
182:
183: i0 = env->ir[8];
184: i1 = env->ir[9];
185: i2 = env->ir[10];
186: i3 = env->ir[11];
187: i4 = env->ir[12];
188: i5 = env->ir[13];
189: i6 = env->ir[14];
190: i7 = env->ir[25];
191:
192: env->ir[8] = env->shadow[0];
193: env->ir[9] = env->shadow[1];
194: env->ir[10] = env->shadow[2];
195: env->ir[11] = env->shadow[3];
196: env->ir[12] = env->shadow[4];
197: env->ir[13] = env->shadow[5];
198: env->ir[14] = env->shadow[6];
199: env->ir[25] = env->shadow[7];
200:
201: env->shadow[0] = i0;
202: env->shadow[1] = i1;
203: env->shadow[2] = i2;
204: env->shadow[3] = i3;
205: env->shadow[4] = i4;
206: env->shadow[5] = i5;
207: env->shadow[6] = i6;
208: env->shadow[7] = i7;
1.1 root 209: }
210:
1.1.1.6 root 211: /* Returns the OSF/1 entMM failure indication, or -1 on success. */
1.1.1.8 ! root 212: static int get_physical_address(CPUAlphaState *env, target_ulong addr,
1.1.1.6 root 213: int prot_need, int mmu_idx,
214: target_ulong *pphys, int *pprot)
215: {
216: target_long saddr = addr;
217: target_ulong phys = 0;
218: target_ulong L1pte, L2pte, L3pte;
219: target_ulong pt, index;
220: int prot = 0;
221: int ret = MM_K_ACV;
222:
223: /* Ensure that the virtual address is properly sign-extended from
224: the last implemented virtual address bit. */
225: if (saddr >> TARGET_VIRT_ADDR_SPACE_BITS != saddr >> 63) {
226: goto exit;
227: }
228:
229: /* Translate the superpage. */
230: /* ??? When we do more than emulate Unix PALcode, we'll need to
231: determine which KSEG is actually active. */
232: if (saddr < 0 && ((saddr >> 41) & 3) == 2) {
233: /* User-space cannot access KSEG addresses. */
234: if (mmu_idx != MMU_KERNEL_IDX) {
235: goto exit;
236: }
237:
238: /* For the benefit of the Typhoon chipset, move bit 40 to bit 43.
239: We would not do this if the 48-bit KSEG is enabled. */
240: phys = saddr & ((1ull << 40) - 1);
241: phys |= (saddr & (1ull << 40)) << 3;
242:
243: prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
244: ret = -1;
245: goto exit;
246: }
247:
248: /* Interpret the page table exactly like PALcode does. */
249:
250: pt = env->ptbr;
251:
252: /* L1 page table read. */
253: index = (addr >> (TARGET_PAGE_BITS + 20)) & 0x3ff;
254: L1pte = ldq_phys(pt + index*8);
255:
256: if (unlikely((L1pte & PTE_VALID) == 0)) {
257: ret = MM_K_TNV;
258: goto exit;
259: }
260: if (unlikely((L1pte & PTE_KRE) == 0)) {
261: goto exit;
262: }
263: pt = L1pte >> 32 << TARGET_PAGE_BITS;
264:
265: /* L2 page table read. */
266: index = (addr >> (TARGET_PAGE_BITS + 10)) & 0x3ff;
267: L2pte = ldq_phys(pt + index*8);
268:
269: if (unlikely((L2pte & PTE_VALID) == 0)) {
270: ret = MM_K_TNV;
271: goto exit;
272: }
273: if (unlikely((L2pte & PTE_KRE) == 0)) {
274: goto exit;
275: }
276: pt = L2pte >> 32 << TARGET_PAGE_BITS;
277:
278: /* L3 page table read. */
279: index = (addr >> TARGET_PAGE_BITS) & 0x3ff;
280: L3pte = ldq_phys(pt + index*8);
281:
282: phys = L3pte >> 32 << TARGET_PAGE_BITS;
283: if (unlikely((L3pte & PTE_VALID) == 0)) {
284: ret = MM_K_TNV;
285: goto exit;
286: }
287:
288: #if PAGE_READ != 1 || PAGE_WRITE != 2 || PAGE_EXEC != 4
289: # error page bits out of date
290: #endif
1.1 root 291:
1.1.1.6 root 292: /* Check access violations. */
293: if (L3pte & (PTE_KRE << mmu_idx)) {
294: prot |= PAGE_READ | PAGE_EXEC;
295: }
296: if (L3pte & (PTE_KWE << mmu_idx)) {
297: prot |= PAGE_WRITE;
298: }
299: if (unlikely((prot & prot_need) == 0 && prot_need)) {
300: goto exit;
301: }
302:
303: /* Check fault-on-operation violations. */
304: prot &= ~(L3pte >> 1);
305: ret = -1;
306: if (unlikely((prot & prot_need) == 0)) {
307: ret = (prot_need & PAGE_EXEC ? MM_K_FOE :
308: prot_need & PAGE_WRITE ? MM_K_FOW :
309: prot_need & PAGE_READ ? MM_K_FOR : -1);
310: }
311:
312: exit:
313: *pphys = phys;
314: *pprot = prot;
315: return ret;
316: }
317:
1.1.1.8 ! root 318: target_phys_addr_t cpu_get_phys_page_debug(CPUAlphaState *env, target_ulong addr)
1.1 root 319: {
1.1.1.6 root 320: target_ulong phys;
321: int prot, fail;
322:
323: fail = get_physical_address(env, addr, 0, 0, &phys, &prot);
324: return (fail >= 0 ? -1 : phys);
1.1 root 325: }
326:
1.1.1.8 ! root 327: int cpu_alpha_handle_mmu_fault(CPUAlphaState *env, target_ulong addr, int rw,
1.1.1.7 root 328: int mmu_idx)
1.1 root 329: {
1.1.1.6 root 330: target_ulong phys;
331: int prot, fail;
1.1 root 332:
1.1.1.6 root 333: fail = get_physical_address(env, addr, 1 << rw, mmu_idx, &phys, &prot);
334: if (unlikely(fail >= 0)) {
335: env->exception_index = EXCP_MMFAULT;
336: env->trap_arg0 = addr;
337: env->trap_arg1 = fail;
338: env->trap_arg2 = (rw == 2 ? -1 : rw);
339: return 1;
1.1 root 340: }
341:
1.1.1.6 root 342: tlb_set_page(env, addr & TARGET_PAGE_MASK, phys & TARGET_PAGE_MASK,
343: prot, mmu_idx, TARGET_PAGE_SIZE);
344: return 0;
1.1 root 345: }
1.1.1.6 root 346: #endif /* USER_ONLY */
1.1 root 347:
1.1.1.8 ! root 348: void do_interrupt (CPUAlphaState *env)
1.1 root 349: {
1.1.1.6 root 350: int i = env->exception_index;
1.1 root 351:
1.1.1.6 root 352: if (qemu_loglevel_mask(CPU_LOG_INT)) {
353: static int count;
354: const char *name = "<unknown>";
355:
356: switch (i) {
357: case EXCP_RESET:
358: name = "reset";
359: break;
360: case EXCP_MCHK:
361: name = "mchk";
362: break;
363: case EXCP_SMP_INTERRUPT:
364: name = "smp_interrupt";
365: break;
366: case EXCP_CLK_INTERRUPT:
367: name = "clk_interrupt";
368: break;
369: case EXCP_DEV_INTERRUPT:
370: name = "dev_interrupt";
371: break;
372: case EXCP_MMFAULT:
373: name = "mmfault";
374: break;
375: case EXCP_UNALIGN:
376: name = "unalign";
377: break;
378: case EXCP_OPCDEC:
379: name = "opcdec";
380: break;
381: case EXCP_ARITH:
382: name = "arith";
383: break;
384: case EXCP_FEN:
385: name = "fen";
386: break;
387: case EXCP_CALL_PAL:
388: name = "call_pal";
389: break;
390: case EXCP_STL_C:
391: name = "stl_c";
392: break;
393: case EXCP_STQ_C:
394: name = "stq_c";
395: break;
1.1 root 396: }
1.1.1.6 root 397: qemu_log("INT %6d: %s(%#x) pc=%016" PRIx64 " sp=%016" PRIx64 "\n",
398: ++count, name, env->error_code, env->pc, env->ir[IR_SP]);
1.1 root 399: }
400:
1.1.1.6 root 401: env->exception_index = -1;
1.1 root 402:
1.1.1.6 root 403: #if !defined(CONFIG_USER_ONLY)
404: switch (i) {
405: case EXCP_RESET:
406: i = 0x0000;
1.1 root 407: break;
1.1.1.6 root 408: case EXCP_MCHK:
409: i = 0x0080;
1.1 root 410: break;
1.1.1.6 root 411: case EXCP_SMP_INTERRUPT:
412: i = 0x0100;
1.1 root 413: break;
1.1.1.6 root 414: case EXCP_CLK_INTERRUPT:
415: i = 0x0180;
1.1.1.4 root 416: break;
1.1.1.6 root 417: case EXCP_DEV_INTERRUPT:
418: i = 0x0200;
1.1 root 419: break;
1.1.1.6 root 420: case EXCP_MMFAULT:
421: i = 0x0280;
1.1 root 422: break;
1.1.1.6 root 423: case EXCP_UNALIGN:
424: i = 0x0300;
1.1 root 425: break;
1.1.1.6 root 426: case EXCP_OPCDEC:
427: i = 0x0380;
1.1 root 428: break;
1.1.1.6 root 429: case EXCP_ARITH:
430: i = 0x0400;
1.1 root 431: break;
1.1.1.6 root 432: case EXCP_FEN:
433: i = 0x0480;
1.1 root 434: break;
1.1.1.6 root 435: case EXCP_CALL_PAL:
436: i = env->error_code;
437: /* There are 64 entry points for both privileged and unprivileged,
438: with bit 0x80 indicating unprivileged. Each entry point gets
439: 64 bytes to do its job. */
440: if (i & 0x80) {
441: i = 0x2000 + (i - 0x80) * 64;
442: } else {
443: i = 0x1000 + i * 64;
444: }
1.1 root 445: break;
446: default:
1.1.1.6 root 447: cpu_abort(env, "Unhandled CPU exception");
1.1 root 448: }
449:
1.1.1.6 root 450: /* Remember where the exception happened. Emulate real hardware in
451: that the low bit of the PC indicates PALmode. */
452: env->exc_addr = env->pc | env->pal_mode;
1.1 root 453:
1.1.1.6 root 454: /* Continue execution at the PALcode entry point. */
455: env->pc = env->palbr + i;
1.1 root 456:
1.1.1.6 root 457: /* Switch to PALmode. */
458: if (!env->pal_mode) {
459: env->pal_mode = 1;
460: swap_shadow_regs(env);
1.1 root 461: }
1.1.1.6 root 462: #endif /* !USER_ONLY */
1.1 root 463: }
464:
1.1.1.8 ! root 465: void cpu_dump_state (CPUAlphaState *env, FILE *f, fprintf_function cpu_fprintf,
1.1 root 466: int flags)
467: {
1.1.1.2 root 468: static const char *linux_reg_names[] = {
1.1 root 469: "v0 ", "t0 ", "t1 ", "t2 ", "t3 ", "t4 ", "t5 ", "t6 ",
470: "t7 ", "s0 ", "s1 ", "s2 ", "s3 ", "s4 ", "s5 ", "fp ",
471: "a0 ", "a1 ", "a2 ", "a3 ", "a4 ", "a5 ", "t8 ", "t9 ",
472: "t10", "t11", "ra ", "t12", "at ", "gp ", "sp ", "zero",
473: };
474: int i;
475:
1.1.1.6 root 476: cpu_fprintf(f, " PC " TARGET_FMT_lx " PS %02x\n",
1.1 root 477: env->pc, env->ps);
478: for (i = 0; i < 31; i++) {
479: cpu_fprintf(f, "IR%02d %s " TARGET_FMT_lx " ", i,
480: linux_reg_names[i], env->ir[i]);
481: if ((i % 3) == 2)
482: cpu_fprintf(f, "\n");
483: }
1.1.1.4 root 484:
485: cpu_fprintf(f, "lock_a " TARGET_FMT_lx " lock_v " TARGET_FMT_lx "\n",
486: env->lock_addr, env->lock_value);
487:
1.1 root 488: for (i = 0; i < 31; i++) {
489: cpu_fprintf(f, "FIR%02d " TARGET_FMT_lx " ", i,
490: *((uint64_t *)(&env->fir[i])));
491: if ((i % 3) == 2)
492: cpu_fprintf(f, "\n");
493: }
1.1.1.4 root 494: cpu_fprintf(f, "\n");
1.1 root 495: }
1.1.1.8 ! root 496:
! 497: void do_restore_state(CPUAlphaState *env, uintptr_t retaddr)
! 498: {
! 499: if (retaddr) {
! 500: TranslationBlock *tb = tb_find_pc(retaddr);
! 501: if (tb) {
! 502: cpu_restore_state(tb, env, retaddr);
! 503: }
! 504: }
! 505: }
! 506:
! 507: /* This should only be called from translate, via gen_excp.
! 508: We expect that ENV->PC has already been updated. */
! 509: void QEMU_NORETURN helper_excp(CPUAlphaState *env, int excp, int error)
! 510: {
! 511: env->exception_index = excp;
! 512: env->error_code = error;
! 513: cpu_loop_exit(env);
! 514: }
! 515:
! 516: /* This may be called from any of the helpers to set up EXCEPTION_INDEX. */
! 517: void QEMU_NORETURN dynamic_excp(CPUAlphaState *env, uintptr_t retaddr,
! 518: int excp, int error)
! 519: {
! 520: env->exception_index = excp;
! 521: env->error_code = error;
! 522: do_restore_state(env, retaddr);
! 523: cpu_loop_exit(env);
! 524: }
! 525:
! 526: void QEMU_NORETURN arith_excp(CPUAlphaState *env, uintptr_t retaddr,
! 527: int exc, uint64_t mask)
! 528: {
! 529: env->trap_arg0 = exc;
! 530: env->trap_arg1 = mask;
! 531: dynamic_excp(env, retaddr, EXCP_ARITH, 0);
! 532: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.