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