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