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