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