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