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