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
cpu_alpha_load_fpcr(CPUAlphaState * env)33 uint64_t cpu_alpha_load_fpcr(CPUAlphaState *env)
34 {
35 return (uint64_t)env->fpcr << 32;
36 }
37
cpu_alpha_store_fpcr(CPUAlphaState * env,uint64_t val)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
helper_load_fpcr(CPUAlphaState * env)89 uint64_t helper_load_fpcr(CPUAlphaState *env)
90 {
91 return cpu_alpha_load_fpcr(env);
92 }
93
helper_store_fpcr(CPUAlphaState * env,uint64_t val)94 void helper_store_fpcr(CPUAlphaState *env, uint64_t val)
95 {
96 cpu_alpha_store_fpcr(env, val);
97 }
98
cpu_alpha_addr_gr(CPUAlphaState * env,unsigned reg)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
cpu_alpha_load_gr(CPUAlphaState * env,unsigned reg)113 uint64_t cpu_alpha_load_gr(CPUAlphaState *env, unsigned reg)
114 {
115 return *cpu_alpha_addr_gr(env, reg);
116 }
117
cpu_alpha_store_gr(CPUAlphaState * env,unsigned reg,uint64_t val)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)
alpha_cpu_record_sigsegv(CPUState * cs,vaddr address,MMUAccessType access_type,bool maperr,uintptr_t retaddr)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. */
get_physical_address(CPUAlphaState * env,target_ulong addr,int prot_need,int mmu_idx,target_ulong * pphys,int * pprot)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
alpha_cpu_get_phys_page_debug(CPUState * cs,vaddr addr)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
alpha_cpu_tlb_fill(CPUState * cs,vaddr addr,int size,MMUAccessType access_type,int mmu_idx,bool probe,uintptr_t retaddr)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
alpha_cpu_do_interrupt(CPUState * cs)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
alpha_cpu_exec_interrupt(CPUState * cs,int interrupt_request)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
alpha_cpu_dump_state(CPUState * cs,FILE * f,int flags)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. */
helper_excp(CPUAlphaState * env,int excp,int error)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. */
dynamic_excp(CPUAlphaState * env,uintptr_t retaddr,int excp,int error)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
arith_excp(CPUAlphaState * env,uintptr_t retaddr,int exc,uint64_t mask)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