1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20#include "qemu/osdep.h"
21
22#include "cpu.h"
23#include "exec/exec-all.h"
24#include "fpu/softfloat.h"
25#include "exec/helper-proto.h"
26
27
28#define CONVERT_BIT(X, SRC, DST) \
29 (SRC > DST ? (X) / (SRC / DST) & (DST) : ((X) & SRC) * (DST / SRC))
30
31uint64_t cpu_alpha_load_fpcr (CPUAlphaState *env)
32{
33 return (uint64_t)env->fpcr << 32;
34}
35
36void cpu_alpha_store_fpcr (CPUAlphaState *env, uint64_t val)
37{
38 uint32_t fpcr = val >> 32;
39 uint32_t t = 0;
40
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);
46
47 env->fpcr = fpcr;
48 env->fpcr_exc_enable = ~t & FPCR_STATUS_MASK;
49
50 switch (fpcr & FPCR_DYN_MASK) {
51 case FPCR_DYN_NORMAL:
52 default:
53 t = float_round_nearest_even;
54 break;
55 case FPCR_DYN_CHOPPED:
56 t = float_round_to_zero;
57 break;
58 case FPCR_DYN_MINUS:
59 t = float_round_down;
60 break;
61 case FPCR_DYN_PLUS:
62 t = float_round_up;
63 break;
64 }
65 env->fpcr_dyn_round = t;
66
67 env->fpcr_flush_to_zero = (fpcr & FPCR_UNFD) && (fpcr & FPCR_UNDZ);
68 env->fp_status.flush_inputs_to_zero = (fpcr & FPCR_DNZ) != 0;
69}
70
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
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
105#if defined(CONFIG_USER_ONLY)
106int alpha_cpu_handle_mmu_fault(CPUState *cs, vaddr address,
107 int rw, int mmu_idx)
108{
109 AlphaCPU *cpu = ALPHA_CPU(cs);
110
111 cs->exception_index = EXCP_MMFAULT;
112 cpu->env.trap_arg0 = address;
113 return 1;
114}
115#else
116
117static int get_physical_address(CPUAlphaState *env, target_ulong addr,
118 int prot_need, int mmu_idx,
119 target_ulong *pphys, int *pprot)
120{
121 CPUState *cs = CPU(alpha_env_get_cpu(env));
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
130 if (mmu_idx == MMU_PHYS_IDX) {
131 phys = addr;
132 prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
133 ret = -1;
134 goto exit;
135 }
136
137
138
139 if (saddr >> TARGET_VIRT_ADDR_SPACE_BITS != saddr >> 63) {
140 goto exit;
141 }
142
143
144
145
146 if (saddr < 0 && ((saddr >> 41) & 3) == 2) {
147
148 if (mmu_idx != MMU_KERNEL_IDX) {
149 goto exit;
150 }
151
152
153
154 phys = saddr & ((1ull << 40) - 1);
155 phys |= (saddr & (1ull << 40)) << 3;
156
157 prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
158 ret = -1;
159 goto exit;
160 }
161
162
163
164 pt = env->ptbr;
165
166
167 index = (addr >> (TARGET_PAGE_BITS + 20)) & 0x3ff;
168 L1pte = ldq_phys(cs->as, pt + index*8);
169
170 if (unlikely((L1pte & PTE_VALID) == 0)) {
171 ret = MM_K_TNV;
172 goto exit;
173 }
174 if (unlikely((L1pte & PTE_KRE) == 0)) {
175 goto exit;
176 }
177 pt = L1pte >> 32 << TARGET_PAGE_BITS;
178
179
180 index = (addr >> (TARGET_PAGE_BITS + 10)) & 0x3ff;
181 L2pte = ldq_phys(cs->as, pt + index*8);
182
183 if (unlikely((L2pte & PTE_VALID) == 0)) {
184 ret = MM_K_TNV;
185 goto exit;
186 }
187 if (unlikely((L2pte & PTE_KRE) == 0)) {
188 goto exit;
189 }
190 pt = L2pte >> 32 << TARGET_PAGE_BITS;
191
192
193 index = (addr >> TARGET_PAGE_BITS) & 0x3ff;
194 L3pte = ldq_phys(cs->as, pt + index*8);
195
196 phys = L3pte >> 32 << TARGET_PAGE_BITS;
197 if (unlikely((L3pte & PTE_VALID) == 0)) {
198 ret = MM_K_TNV;
199 goto exit;
200 }
201
202#if PAGE_READ != 1 || PAGE_WRITE != 2 || PAGE_EXEC != 4
203# error page bits out of date
204#endif
205
206
207 if (L3pte & (PTE_KRE << mmu_idx)) {
208 prot |= PAGE_READ | PAGE_EXEC;
209 }
210 if (L3pte & (PTE_KWE << mmu_idx)) {
211 prot |= PAGE_WRITE;
212 }
213 if (unlikely((prot & prot_need) == 0 && prot_need)) {
214 goto exit;
215 }
216
217
218 prot &= ~(L3pte >> 1);
219 ret = -1;
220 if (unlikely((prot & prot_need) == 0)) {
221 ret = (prot_need & PAGE_EXEC ? MM_K_FOE :
222 prot_need & PAGE_WRITE ? MM_K_FOW :
223 prot_need & PAGE_READ ? MM_K_FOR : -1);
224 }
225
226 exit:
227 *pphys = phys;
228 *pprot = prot;
229 return ret;
230}
231
232hwaddr alpha_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
233{
234 AlphaCPU *cpu = ALPHA_CPU(cs);
235 target_ulong phys;
236 int prot, fail;
237
238 fail = get_physical_address(&cpu->env, addr, 0, 0, &phys, &prot);
239 return (fail >= 0 ? -1 : phys);
240}
241
242int alpha_cpu_handle_mmu_fault(CPUState *cs, vaddr addr, int rw,
243 int mmu_idx)
244{
245 AlphaCPU *cpu = ALPHA_CPU(cs);
246 CPUAlphaState *env = &cpu->env;
247 target_ulong phys;
248 int prot, fail;
249
250 fail = get_physical_address(env, addr, 1 << rw, mmu_idx, &phys, &prot);
251 if (unlikely(fail >= 0)) {
252 cs->exception_index = EXCP_MMFAULT;
253 env->trap_arg0 = addr;
254 env->trap_arg1 = fail;
255 env->trap_arg2 = (rw == 2 ? -1 : rw);
256 return 1;
257 }
258
259 tlb_set_page(cs, addr & TARGET_PAGE_MASK, phys & TARGET_PAGE_MASK,
260 prot, mmu_idx, TARGET_PAGE_SIZE);
261 return 0;
262}
263#endif
264
265void alpha_cpu_do_interrupt(CPUState *cs)
266{
267 AlphaCPU *cpu = ALPHA_CPU(cs);
268 CPUAlphaState *env = &cpu->env;
269 int i = cs->exception_index;
270
271 if (qemu_loglevel_mask(CPU_LOG_INT)) {
272 static int count;
273 const char *name = "<unknown>";
274
275 switch (i) {
276 case EXCP_RESET:
277 name = "reset";
278 break;
279 case EXCP_MCHK:
280 name = "mchk";
281 break;
282 case EXCP_SMP_INTERRUPT:
283 name = "smp_interrupt";
284 break;
285 case EXCP_CLK_INTERRUPT:
286 name = "clk_interrupt";
287 break;
288 case EXCP_DEV_INTERRUPT:
289 name = "dev_interrupt";
290 break;
291 case EXCP_MMFAULT:
292 name = "mmfault";
293 break;
294 case EXCP_UNALIGN:
295 name = "unalign";
296 break;
297 case EXCP_OPCDEC:
298 name = "opcdec";
299 break;
300 case EXCP_ARITH:
301 name = "arith";
302 break;
303 case EXCP_FEN:
304 name = "fen";
305 break;
306 case EXCP_CALL_PAL:
307 name = "call_pal";
308 break;
309 }
310 qemu_log("INT %6d: %s(%#x) cpu=%d pc=%016"
311 PRIx64 " sp=%016" PRIx64 "\n",
312 ++count, name, env->error_code, cs->cpu_index,
313 env->pc, env->ir[IR_SP]);
314 }
315
316 cs->exception_index = -1;
317
318#if !defined(CONFIG_USER_ONLY)
319 switch (i) {
320 case EXCP_RESET:
321 i = 0x0000;
322 break;
323 case EXCP_MCHK:
324 i = 0x0080;
325 break;
326 case EXCP_SMP_INTERRUPT:
327 i = 0x0100;
328 break;
329 case EXCP_CLK_INTERRUPT:
330 i = 0x0180;
331 break;
332 case EXCP_DEV_INTERRUPT:
333 i = 0x0200;
334 break;
335 case EXCP_MMFAULT:
336 i = 0x0280;
337 break;
338 case EXCP_UNALIGN:
339 i = 0x0300;
340 break;
341 case EXCP_OPCDEC:
342 i = 0x0380;
343 break;
344 case EXCP_ARITH:
345 i = 0x0400;
346 break;
347 case EXCP_FEN:
348 i = 0x0480;
349 break;
350 case EXCP_CALL_PAL:
351 i = env->error_code;
352
353
354
355 if (i & 0x80) {
356 i = 0x2000 + (i - 0x80) * 64;
357 } else {
358 i = 0x1000 + i * 64;
359 }
360 break;
361 default:
362 cpu_abort(cs, "Unhandled CPU exception");
363 }
364
365
366
367 env->exc_addr = env->pc | env->pal_mode;
368
369
370 env->pc = env->palbr + i;
371
372
373 env->pal_mode = 1;
374#endif
375}
376
377bool alpha_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
378{
379 AlphaCPU *cpu = ALPHA_CPU(cs);
380 CPUAlphaState *env = &cpu->env;
381 int idx = -1;
382
383
384 if (env->pal_mode) {
385 return false;
386 }
387
388
389
390
391 switch (env->ps & PS_INT_MASK) {
392 case 0 ... 3:
393 if (interrupt_request & CPU_INTERRUPT_HARD) {
394 idx = EXCP_DEV_INTERRUPT;
395 }
396
397 case 4:
398 if (interrupt_request & CPU_INTERRUPT_TIMER) {
399 idx = EXCP_CLK_INTERRUPT;
400 }
401
402 case 5:
403 if (interrupt_request & CPU_INTERRUPT_SMP) {
404 idx = EXCP_SMP_INTERRUPT;
405 }
406
407 case 6:
408 if (interrupt_request & CPU_INTERRUPT_MCHK) {
409 idx = EXCP_MCHK;
410 }
411 }
412 if (idx >= 0) {
413 cs->exception_index = idx;
414 env->error_code = 0;
415 alpha_cpu_do_interrupt(cs);
416 return true;
417 }
418 return false;
419}
420
421void alpha_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf,
422 int flags)
423{
424 static const char *linux_reg_names[] = {
425 "v0 ", "t0 ", "t1 ", "t2 ", "t3 ", "t4 ", "t5 ", "t6 ",
426 "t7 ", "s0 ", "s1 ", "s2 ", "s3 ", "s4 ", "s5 ", "fp ",
427 "a0 ", "a1 ", "a2 ", "a3 ", "a4 ", "a5 ", "t8 ", "t9 ",
428 "t10", "t11", "ra ", "t12", "at ", "gp ", "sp ", "zero",
429 };
430 AlphaCPU *cpu = ALPHA_CPU(cs);
431 CPUAlphaState *env = &cpu->env;
432 int i;
433
434 cpu_fprintf(f, " PC " TARGET_FMT_lx " PS %02x\n",
435 env->pc, env->ps);
436 for (i = 0; i < 31; i++) {
437 cpu_fprintf(f, "IR%02d %s " TARGET_FMT_lx " ", i,
438 linux_reg_names[i], cpu_alpha_load_gr(env, i));
439 if ((i % 3) == 2)
440 cpu_fprintf(f, "\n");
441 }
442
443 cpu_fprintf(f, "lock_a " TARGET_FMT_lx " lock_v " TARGET_FMT_lx "\n",
444 env->lock_addr, env->lock_value);
445
446 for (i = 0; i < 31; i++) {
447 cpu_fprintf(f, "FIR%02d " TARGET_FMT_lx " ", i,
448 *((uint64_t *)(&env->fir[i])));
449 if ((i % 3) == 2)
450 cpu_fprintf(f, "\n");
451 }
452 cpu_fprintf(f, "\n");
453}
454
455
456
457void QEMU_NORETURN helper_excp(CPUAlphaState *env, int excp, int error)
458{
459 AlphaCPU *cpu = alpha_env_get_cpu(env);
460 CPUState *cs = CPU(cpu);
461
462 cs->exception_index = excp;
463 env->error_code = error;
464 cpu_loop_exit(cs);
465}
466
467
468void QEMU_NORETURN dynamic_excp(CPUAlphaState *env, uintptr_t retaddr,
469 int excp, int error)
470{
471 AlphaCPU *cpu = alpha_env_get_cpu(env);
472 CPUState *cs = CPU(cpu);
473
474 cs->exception_index = excp;
475 env->error_code = error;
476 if (retaddr) {
477 cpu_restore_state(cs, retaddr);
478
479 env->pc += 4;
480 }
481 cpu_loop_exit(cs);
482}
483
484void QEMU_NORETURN arith_excp(CPUAlphaState *env, uintptr_t retaddr,
485 int exc, uint64_t mask)
486{
487 env->trap_arg0 = exc;
488 env->trap_arg1 = mask;
489 dynamic_excp(env, retaddr, EXCP_ARITH, 0);
490}
491