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