]> Git Repo - qemu.git/blob - target-alpha/helper.c
Merge remote-tracking branch 'remotes/kvm/uq/master' into staging
[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 "fpu/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     CPUState *cs = ENV_GET_CPU(env);
217     target_long saddr = addr;
218     target_ulong phys = 0;
219     target_ulong L1pte, L2pte, L3pte;
220     target_ulong pt, index;
221     int prot = 0;
222     int ret = MM_K_ACV;
223
224     /* Ensure that the virtual address is properly sign-extended from
225        the last implemented virtual address bit.  */
226     if (saddr >> TARGET_VIRT_ADDR_SPACE_BITS != saddr >> 63) {
227         goto exit;
228     }
229
230     /* Translate the superpage.  */
231     /* ??? When we do more than emulate Unix PALcode, we'll need to
232        determine which KSEG is actually active.  */
233     if (saddr < 0 && ((saddr >> 41) & 3) == 2) {
234         /* User-space cannot access KSEG addresses.  */
235         if (mmu_idx != MMU_KERNEL_IDX) {
236             goto exit;
237         }
238
239         /* For the benefit of the Typhoon chipset, move bit 40 to bit 43.
240            We would not do this if the 48-bit KSEG is enabled.  */
241         phys = saddr & ((1ull << 40) - 1);
242         phys |= (saddr & (1ull << 40)) << 3;
243
244         prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
245         ret = -1;
246         goto exit;
247     }
248
249     /* Interpret the page table exactly like PALcode does.  */
250
251     pt = env->ptbr;
252
253     /* L1 page table read.  */
254     index = (addr >> (TARGET_PAGE_BITS + 20)) & 0x3ff;
255     L1pte = ldq_phys(cs->as, pt + index*8);
256
257     if (unlikely((L1pte & PTE_VALID) == 0)) {
258         ret = MM_K_TNV;
259         goto exit;
260     }
261     if (unlikely((L1pte & PTE_KRE) == 0)) {
262         goto exit;
263     }
264     pt = L1pte >> 32 << TARGET_PAGE_BITS;
265
266     /* L2 page table read.  */
267     index = (addr >> (TARGET_PAGE_BITS + 10)) & 0x3ff;
268     L2pte = ldq_phys(cs->as, pt + index*8);
269
270     if (unlikely((L2pte & PTE_VALID) == 0)) {
271         ret = MM_K_TNV;
272         goto exit;
273     }
274     if (unlikely((L2pte & PTE_KRE) == 0)) {
275         goto exit;
276     }
277     pt = L2pte >> 32 << TARGET_PAGE_BITS;
278
279     /* L3 page table read.  */
280     index = (addr >> TARGET_PAGE_BITS) & 0x3ff;
281     L3pte = ldq_phys(cs->as, pt + index*8);
282
283     phys = L3pte >> 32 << TARGET_PAGE_BITS;
284     if (unlikely((L3pte & PTE_VALID) == 0)) {
285         ret = MM_K_TNV;
286         goto exit;
287     }
288
289 #if PAGE_READ != 1 || PAGE_WRITE != 2 || PAGE_EXEC != 4
290 # error page bits out of date
291 #endif
292
293     /* Check access violations.  */
294     if (L3pte & (PTE_KRE << mmu_idx)) {
295         prot |= PAGE_READ | PAGE_EXEC;
296     }
297     if (L3pte & (PTE_KWE << mmu_idx)) {
298         prot |= PAGE_WRITE;
299     }
300     if (unlikely((prot & prot_need) == 0 && prot_need)) {
301         goto exit;
302     }
303
304     /* Check fault-on-operation violations.  */
305     prot &= ~(L3pte >> 1);
306     ret = -1;
307     if (unlikely((prot & prot_need) == 0)) {
308         ret = (prot_need & PAGE_EXEC ? MM_K_FOE :
309                prot_need & PAGE_WRITE ? MM_K_FOW :
310                prot_need & PAGE_READ ? MM_K_FOR : -1);
311     }
312
313  exit:
314     *pphys = phys;
315     *pprot = prot;
316     return ret;
317 }
318
319 hwaddr alpha_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
320 {
321     AlphaCPU *cpu = ALPHA_CPU(cs);
322     target_ulong phys;
323     int prot, fail;
324
325     fail = get_physical_address(&cpu->env, addr, 0, 0, &phys, &prot);
326     return (fail >= 0 ? -1 : phys);
327 }
328
329 int cpu_alpha_handle_mmu_fault(CPUAlphaState *env, target_ulong addr, int rw,
330                                int mmu_idx)
331 {
332     target_ulong phys;
333     int prot, fail;
334
335     fail = get_physical_address(env, addr, 1 << rw, mmu_idx, &phys, &prot);
336     if (unlikely(fail >= 0)) {
337         env->exception_index = EXCP_MMFAULT;
338         env->trap_arg0 = addr;
339         env->trap_arg1 = fail;
340         env->trap_arg2 = (rw == 2 ? -1 : rw);
341         return 1;
342     }
343
344     tlb_set_page(env, addr & TARGET_PAGE_MASK, phys & TARGET_PAGE_MASK,
345                  prot, mmu_idx, TARGET_PAGE_SIZE);
346     return 0;
347 }
348 #endif /* USER_ONLY */
349
350 void alpha_cpu_do_interrupt(CPUState *cs)
351 {
352     AlphaCPU *cpu = ALPHA_CPU(cs);
353     CPUAlphaState *env = &cpu->env;
354     int i = env->exception_index;
355
356     if (qemu_loglevel_mask(CPU_LOG_INT)) {
357         static int count;
358         const char *name = "<unknown>";
359
360         switch (i) {
361         case EXCP_RESET:
362             name = "reset";
363             break;
364         case EXCP_MCHK:
365             name = "mchk";
366             break;
367         case EXCP_SMP_INTERRUPT:
368             name = "smp_interrupt";
369             break;
370         case EXCP_CLK_INTERRUPT:
371             name = "clk_interrupt";
372             break;
373         case EXCP_DEV_INTERRUPT:
374             name = "dev_interrupt";
375             break;
376         case EXCP_MMFAULT:
377             name = "mmfault";
378             break;
379         case EXCP_UNALIGN:
380             name = "unalign";
381             break;
382         case EXCP_OPCDEC:
383             name = "opcdec";
384             break;
385         case EXCP_ARITH:
386             name = "arith";
387             break;
388         case EXCP_FEN:
389             name = "fen";
390             break;
391         case EXCP_CALL_PAL:
392             name = "call_pal";
393             break;
394         case EXCP_STL_C:
395             name = "stl_c";
396             break;
397         case EXCP_STQ_C:
398             name = "stq_c";
399             break;
400         }
401         qemu_log("INT %6d: %s(%#x) pc=%016" PRIx64 " sp=%016" PRIx64 "\n",
402                  ++count, name, env->error_code, env->pc, env->ir[IR_SP]);
403     }
404
405     env->exception_index = -1;
406
407 #if !defined(CONFIG_USER_ONLY)
408     switch (i) {
409     case EXCP_RESET:
410         i = 0x0000;
411         break;
412     case EXCP_MCHK:
413         i = 0x0080;
414         break;
415     case EXCP_SMP_INTERRUPT:
416         i = 0x0100;
417         break;
418     case EXCP_CLK_INTERRUPT:
419         i = 0x0180;
420         break;
421     case EXCP_DEV_INTERRUPT:
422         i = 0x0200;
423         break;
424     case EXCP_MMFAULT:
425         i = 0x0280;
426         break;
427     case EXCP_UNALIGN:
428         i = 0x0300;
429         break;
430     case EXCP_OPCDEC:
431         i = 0x0380;
432         break;
433     case EXCP_ARITH:
434         i = 0x0400;
435         break;
436     case EXCP_FEN:
437         i = 0x0480;
438         break;
439     case EXCP_CALL_PAL:
440         i = env->error_code;
441         /* There are 64 entry points for both privileged and unprivileged,
442            with bit 0x80 indicating unprivileged.  Each entry point gets
443            64 bytes to do its job.  */
444         if (i & 0x80) {
445             i = 0x2000 + (i - 0x80) * 64;
446         } else {
447             i = 0x1000 + i * 64;
448         }
449         break;
450     default:
451         cpu_abort(env, "Unhandled CPU exception");
452     }
453
454     /* Remember where the exception happened.  Emulate real hardware in
455        that the low bit of the PC indicates PALmode.  */
456     env->exc_addr = env->pc | env->pal_mode;
457
458     /* Continue execution at the PALcode entry point.  */
459     env->pc = env->palbr + i;
460
461     /* Switch to PALmode.  */
462     if (!env->pal_mode) {
463         env->pal_mode = 1;
464         swap_shadow_regs(env);
465     }
466 #endif /* !USER_ONLY */
467 }
468
469 void alpha_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf,
470                           int flags)
471 {
472     static const char *linux_reg_names[] = {
473         "v0 ", "t0 ", "t1 ", "t2 ", "t3 ", "t4 ", "t5 ", "t6 ",
474         "t7 ", "s0 ", "s1 ", "s2 ", "s3 ", "s4 ", "s5 ", "fp ",
475         "a0 ", "a1 ", "a2 ", "a3 ", "a4 ", "a5 ", "t8 ", "t9 ",
476         "t10", "t11", "ra ", "t12", "at ", "gp ", "sp ", "zero",
477     };
478     AlphaCPU *cpu = ALPHA_CPU(cs);
479     CPUAlphaState *env = &cpu->env;
480     int i;
481
482     cpu_fprintf(f, "     PC  " TARGET_FMT_lx "      PS  %02x\n",
483                 env->pc, env->ps);
484     for (i = 0; i < 31; i++) {
485         cpu_fprintf(f, "IR%02d %s " TARGET_FMT_lx " ", i,
486                     linux_reg_names[i], env->ir[i]);
487         if ((i % 3) == 2)
488             cpu_fprintf(f, "\n");
489     }
490
491     cpu_fprintf(f, "lock_a   " TARGET_FMT_lx " lock_v   " TARGET_FMT_lx "\n",
492                 env->lock_addr, env->lock_value);
493
494     for (i = 0; i < 31; i++) {
495         cpu_fprintf(f, "FIR%02d    " TARGET_FMT_lx " ", i,
496                     *((uint64_t *)(&env->fir[i])));
497         if ((i % 3) == 2)
498             cpu_fprintf(f, "\n");
499     }
500     cpu_fprintf(f, "\n");
501 }
502
503 /* This should only be called from translate, via gen_excp.
504    We expect that ENV->PC has already been updated.  */
505 void QEMU_NORETURN helper_excp(CPUAlphaState *env, int excp, int error)
506 {
507     env->exception_index = excp;
508     env->error_code = error;
509     cpu_loop_exit(env);
510 }
511
512 /* This may be called from any of the helpers to set up EXCEPTION_INDEX.  */
513 void QEMU_NORETURN dynamic_excp(CPUAlphaState *env, uintptr_t retaddr,
514                                 int excp, int error)
515 {
516     env->exception_index = excp;
517     env->error_code = error;
518     if (retaddr) {
519         cpu_restore_state(env, retaddr);
520     }
521     cpu_loop_exit(env);
522 }
523
524 void QEMU_NORETURN arith_excp(CPUAlphaState *env, uintptr_t retaddr,
525                               int exc, uint64_t mask)
526 {
527     env->trap_arg0 = exc;
528     env->trap_arg1 = mask;
529     dynamic_excp(env, retaddr, EXCP_ARITH, 0);
530 }
This page took 0.053464 seconds and 4 git commands to generate.