]> git.proxmox.com Git - mirror_qemu.git/blob - target-alpha/helper.c
Use uintptr_t for various op related functions
[mirror_qemu.git] / target-alpha / helper.c
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
17 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include <stdint.h>
21 #include <stdlib.h>
22 #include <stdio.h>
23
24 #include "cpu.h"
25 #include "softfloat.h"
26 #include "helper.h"
27
28 uint64_t cpu_alpha_load_fpcr (CPUAlphaState *env)
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
85 if (env->fp_status.flush_inputs_to_zero) {
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
98 void cpu_alpha_store_fpcr (CPUAlphaState *env, uint64_t val)
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;
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);
168 }
169
170 #if defined(CONFIG_USER_ONLY)
171 int cpu_alpha_handle_mmu_fault(CPUAlphaState *env, target_ulong address,
172 int rw, int mmu_idx)
173 {
174 env->exception_index = EXCP_MMFAULT;
175 env->trap_arg0 = address;
176 return 1;
177 }
178 #else
179 void swap_shadow_regs(CPUAlphaState *env)
180 {
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;
209 }
210
211 /* Returns the OSF/1 entMM failure indication, or -1 on success. */
212 static int get_physical_address(CPUAlphaState *env, target_ulong addr,
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
291
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
318 target_phys_addr_t cpu_get_phys_page_debug(CPUAlphaState *env, target_ulong addr)
319 {
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);
325 }
326
327 int cpu_alpha_handle_mmu_fault(CPUAlphaState *env, target_ulong addr, int rw,
328 int mmu_idx)
329 {
330 target_ulong phys;
331 int prot, fail;
332
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;
340 }
341
342 tlb_set_page(env, addr & TARGET_PAGE_MASK, phys & TARGET_PAGE_MASK,
343 prot, mmu_idx, TARGET_PAGE_SIZE);
344 return 0;
345 }
346 #endif /* USER_ONLY */
347
348 void do_interrupt (CPUAlphaState *env)
349 {
350 int i = env->exception_index;
351
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;
396 }
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]);
399 }
400
401 env->exception_index = -1;
402
403 #if !defined(CONFIG_USER_ONLY)
404 switch (i) {
405 case EXCP_RESET:
406 i = 0x0000;
407 break;
408 case EXCP_MCHK:
409 i = 0x0080;
410 break;
411 case EXCP_SMP_INTERRUPT:
412 i = 0x0100;
413 break;
414 case EXCP_CLK_INTERRUPT:
415 i = 0x0180;
416 break;
417 case EXCP_DEV_INTERRUPT:
418 i = 0x0200;
419 break;
420 case EXCP_MMFAULT:
421 i = 0x0280;
422 break;
423 case EXCP_UNALIGN:
424 i = 0x0300;
425 break;
426 case EXCP_OPCDEC:
427 i = 0x0380;
428 break;
429 case EXCP_ARITH:
430 i = 0x0400;
431 break;
432 case EXCP_FEN:
433 i = 0x0480;
434 break;
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 }
445 break;
446 default:
447 cpu_abort(env, "Unhandled CPU exception");
448 }
449
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;
453
454 /* Continue execution at the PALcode entry point. */
455 env->pc = env->palbr + i;
456
457 /* Switch to PALmode. */
458 if (!env->pal_mode) {
459 env->pal_mode = 1;
460 swap_shadow_regs(env);
461 }
462 #endif /* !USER_ONLY */
463 }
464
465 void cpu_dump_state (CPUAlphaState *env, FILE *f, fprintf_function cpu_fprintf,
466 int flags)
467 {
468 static const char *linux_reg_names[] = {
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
476 cpu_fprintf(f, " PC " TARGET_FMT_lx " PS %02x\n",
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 }
484
485 cpu_fprintf(f, "lock_a " TARGET_FMT_lx " lock_v " TARGET_FMT_lx "\n",
486 env->lock_addr, env->lock_value);
487
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 }
494 cpu_fprintf(f, "\n");
495 }
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 }