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