]> Git Repo - qemu.git/blob - target-alpha/helper.c
Merge remote-tracking branch 'afaerber/qom-cpu' 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     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 hwaddr 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 alpha_cpu_do_interrupt(CPUState *cs)
349 {
350     AlphaCPU *cpu = ALPHA_CPU(cs);
351     CPUAlphaState *env = &cpu->env;
352     int i = env->exception_index;
353
354     if (qemu_loglevel_mask(CPU_LOG_INT)) {
355         static int count;
356         const char *name = "<unknown>";
357
358         switch (i) {
359         case EXCP_RESET:
360             name = "reset";
361             break;
362         case EXCP_MCHK:
363             name = "mchk";
364             break;
365         case EXCP_SMP_INTERRUPT:
366             name = "smp_interrupt";
367             break;
368         case EXCP_CLK_INTERRUPT:
369             name = "clk_interrupt";
370             break;
371         case EXCP_DEV_INTERRUPT:
372             name = "dev_interrupt";
373             break;
374         case EXCP_MMFAULT:
375             name = "mmfault";
376             break;
377         case EXCP_UNALIGN:
378             name = "unalign";
379             break;
380         case EXCP_OPCDEC:
381             name = "opcdec";
382             break;
383         case EXCP_ARITH:
384             name = "arith";
385             break;
386         case EXCP_FEN:
387             name = "fen";
388             break;
389         case EXCP_CALL_PAL:
390             name = "call_pal";
391             break;
392         case EXCP_STL_C:
393             name = "stl_c";
394             break;
395         case EXCP_STQ_C:
396             name = "stq_c";
397             break;
398         }
399         qemu_log("INT %6d: %s(%#x) pc=%016" PRIx64 " sp=%016" PRIx64 "\n",
400                  ++count, name, env->error_code, env->pc, env->ir[IR_SP]);
401     }
402
403     env->exception_index = -1;
404
405 #if !defined(CONFIG_USER_ONLY)
406     switch (i) {
407     case EXCP_RESET:
408         i = 0x0000;
409         break;
410     case EXCP_MCHK:
411         i = 0x0080;
412         break;
413     case EXCP_SMP_INTERRUPT:
414         i = 0x0100;
415         break;
416     case EXCP_CLK_INTERRUPT:
417         i = 0x0180;
418         break;
419     case EXCP_DEV_INTERRUPT:
420         i = 0x0200;
421         break;
422     case EXCP_MMFAULT:
423         i = 0x0280;
424         break;
425     case EXCP_UNALIGN:
426         i = 0x0300;
427         break;
428     case EXCP_OPCDEC:
429         i = 0x0380;
430         break;
431     case EXCP_ARITH:
432         i = 0x0400;
433         break;
434     case EXCP_FEN:
435         i = 0x0480;
436         break;
437     case EXCP_CALL_PAL:
438         i = env->error_code;
439         /* There are 64 entry points for both privileged and unprivileged,
440            with bit 0x80 indicating unprivileged.  Each entry point gets
441            64 bytes to do its job.  */
442         if (i & 0x80) {
443             i = 0x2000 + (i - 0x80) * 64;
444         } else {
445             i = 0x1000 + i * 64;
446         }
447         break;
448     default:
449         cpu_abort(env, "Unhandled CPU exception");
450     }
451
452     /* Remember where the exception happened.  Emulate real hardware in
453        that the low bit of the PC indicates PALmode.  */
454     env->exc_addr = env->pc | env->pal_mode;
455
456     /* Continue execution at the PALcode entry point.  */
457     env->pc = env->palbr + i;
458
459     /* Switch to PALmode.  */
460     if (!env->pal_mode) {
461         env->pal_mode = 1;
462         swap_shadow_regs(env);
463     }
464 #endif /* !USER_ONLY */
465 }
466
467 void alpha_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf,
468                           int flags)
469 {
470     static const char *linux_reg_names[] = {
471         "v0 ", "t0 ", "t1 ", "t2 ", "t3 ", "t4 ", "t5 ", "t6 ",
472         "t7 ", "s0 ", "s1 ", "s2 ", "s3 ", "s4 ", "s5 ", "fp ",
473         "a0 ", "a1 ", "a2 ", "a3 ", "a4 ", "a5 ", "t8 ", "t9 ",
474         "t10", "t11", "ra ", "t12", "at ", "gp ", "sp ", "zero",
475     };
476     AlphaCPU *cpu = ALPHA_CPU(cs);
477     CPUAlphaState *env = &cpu->env;
478     int i;
479
480     cpu_fprintf(f, "     PC  " TARGET_FMT_lx "      PS  %02x\n",
481                 env->pc, env->ps);
482     for (i = 0; i < 31; i++) {
483         cpu_fprintf(f, "IR%02d %s " TARGET_FMT_lx " ", i,
484                     linux_reg_names[i], env->ir[i]);
485         if ((i % 3) == 2)
486             cpu_fprintf(f, "\n");
487     }
488
489     cpu_fprintf(f, "lock_a   " TARGET_FMT_lx " lock_v   " TARGET_FMT_lx "\n",
490                 env->lock_addr, env->lock_value);
491
492     for (i = 0; i < 31; i++) {
493         cpu_fprintf(f, "FIR%02d    " TARGET_FMT_lx " ", i,
494                     *((uint64_t *)(&env->fir[i])));
495         if ((i % 3) == 2)
496             cpu_fprintf(f, "\n");
497     }
498     cpu_fprintf(f, "\n");
499 }
500
501 /* This should only be called from translate, via gen_excp.
502    We expect that ENV->PC has already been updated.  */
503 void QEMU_NORETURN helper_excp(CPUAlphaState *env, int excp, int error)
504 {
505     env->exception_index = excp;
506     env->error_code = error;
507     cpu_loop_exit(env);
508 }
509
510 /* This may be called from any of the helpers to set up EXCEPTION_INDEX.  */
511 void QEMU_NORETURN dynamic_excp(CPUAlphaState *env, uintptr_t retaddr,
512                                 int excp, int error)
513 {
514     env->exception_index = excp;
515     env->error_code = error;
516     if (retaddr) {
517         cpu_restore_state(env, retaddr);
518     }
519     cpu_loop_exit(env);
520 }
521
522 void QEMU_NORETURN arith_excp(CPUAlphaState *env, uintptr_t retaddr,
523                               int exc, uint64_t mask)
524 {
525     env->trap_arg0 = exc;
526     env->trap_arg1 = mask;
527     dynamic_excp(env, retaddr, EXCP_ARITH, 0);
528 }
This page took 0.054602 seconds and 4 git commands to generate.