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 "fpu/softfloat.h"
24#include "exec/helper-proto.h"
25
26
27#define CONVERT_BIT(X, SRC, DST) \
28 (SRC > DST ? (X) / (SRC / DST) & (DST) : ((X) & SRC) * (DST / SRC))
29
30uint64_t cpu_alpha_load_fpcr (CPUAlphaState *env)
31{
32 return (uint64_t)env->fpcr << 32;
33}
34
35void cpu_alpha_store_fpcr (CPUAlphaState *env, uint64_t val)
36{
37 uint32_t fpcr = val >> 32;
38 uint32_t t = 0;
39
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);
45
46 env->fpcr = fpcr;
47 env->fpcr_exc_enable = ~t & FPCR_STATUS_MASK;
48
49 switch (fpcr & FPCR_DYN_MASK) {
50 case FPCR_DYN_NORMAL:
51 default:
52 t = float_round_nearest_even;
53 break;
54 case FPCR_DYN_CHOPPED:
55 t = float_round_to_zero;
56 break;
57 case FPCR_DYN_MINUS:
58 t = float_round_down;
59 break;
60 case FPCR_DYN_PLUS:
61 t = float_round_up;
62 break;
63 }
64 env->fpcr_dyn_round = t;
65
66 env->fpcr_flush_to_zero = (fpcr & FPCR_UNFD) && (fpcr & FPCR_UNDZ);
67 env->fp_status.flush_inputs_to_zero = (fpcr & FPCR_DNZ) != 0;
68}
69
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
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
104#if defined(CONFIG_USER_ONLY)
105int alpha_cpu_handle_mmu_fault(CPUState *cs, vaddr address,
106 int rw, int mmu_idx)
107{
108 AlphaCPU *cpu = ALPHA_CPU(cs);
109
110 cs->exception_index = EXCP_MMFAULT;
111 cpu->env.trap_arg0 = address;
112 return 1;
113}
114#else
115
116static int get_physical_address(CPUAlphaState *env, target_ulong addr,
117 int prot_need, int mmu_idx,
118 target_ulong *pphys, int *pprot)
119{
120 CPUState *cs = CPU(alpha_env_get_cpu(env));
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
129
130 if (saddr >> TARGET_VIRT_ADDR_SPACE_BITS != saddr >> 63) {
131 goto exit;
132 }
133
134
135
136
137 if (saddr < 0 && ((saddr >> 41) & 3) == 2) {
138
139 if (mmu_idx != MMU_KERNEL_IDX) {
140 goto exit;
141 }
142
143
144
145 phys = saddr & ((1ull << 40) - 1);
146 phys |= (saddr & (1ull << 40)) << 3;
147
148 prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
149 ret = -1;
150 goto exit;
151 }
152
153
154
155 pt = env->ptbr;
156
157
158 index = (addr >> (TARGET_PAGE_BITS + 20)) & 0x3ff;
159 L1pte = ldq_phys(cs->as, pt + index*8);
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
171 index = (addr >> (TARGET_PAGE_BITS + 10)) & 0x3ff;
172 L2pte = ldq_phys(cs->as, pt + index*8);
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
184 index = (addr >> TARGET_PAGE_BITS) & 0x3ff;
185 L3pte = ldq_phys(cs->as, pt + index*8);
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
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
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;
221}
222
223hwaddr alpha_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
224{
225 AlphaCPU *cpu = ALPHA_CPU(cs);
226 target_ulong phys;
227 int prot, fail;
228
229 fail = get_physical_address(&cpu->env, addr, 0, 0, &phys, &prot);
230 return (fail >= 0 ? -1 : phys);
231}
232
233int alpha_cpu_handle_mmu_fault(CPUState *cs, vaddr addr, int rw,
234 int mmu_idx)
235{
236 AlphaCPU *cpu = ALPHA_CPU(cs);
237 CPUAlphaState *env = &cpu->env;
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)) {
243 cs->exception_index = EXCP_MMFAULT;
244 env->trap_arg0 = addr;
245 env->trap_arg1 = fail;
246 env->trap_arg2 = (rw == 2 ? -1 : rw);
247 return 1;
248 }
249
250 tlb_set_page(cs, addr & TARGET_PAGE_MASK, phys & TARGET_PAGE_MASK,
251 prot, mmu_idx, TARGET_PAGE_SIZE);
252 return 0;
253}
254#endif
255
256void alpha_cpu_do_interrupt(CPUState *cs)
257{
258 AlphaCPU *cpu = ALPHA_CPU(cs);
259 CPUAlphaState *env = &cpu->env;
260 int i = cs->exception_index;
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
311 cs->exception_index = -1;
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
348
349
350 if (i & 0x80) {
351 i = 0x2000 + (i - 0x80) * 64;
352 } else {
353 i = 0x1000 + i * 64;
354 }
355 break;
356 default:
357 cpu_abort(cs, "Unhandled CPU exception");
358 }
359
360
361
362 env->exc_addr = env->pc | env->pal_mode;
363
364
365 env->pc = env->palbr + i;
366
367
368 env->pal_mode = 1;
369#endif
370}
371
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
379 if (env->pal_mode) {
380 return false;
381 }
382
383
384
385
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
392 case 4:
393 if (interrupt_request & CPU_INTERRUPT_TIMER) {
394 idx = EXCP_CLK_INTERRUPT;
395 }
396
397 case 5:
398 if (interrupt_request & CPU_INTERRUPT_SMP) {
399 idx = EXCP_SMP_INTERRUPT;
400 }
401
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
416void alpha_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf,
417 int flags)
418{
419 static const char *linux_reg_names[] = {
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 };
425 AlphaCPU *cpu = ALPHA_CPU(cs);
426 CPUAlphaState *env = &cpu->env;
427 int i;
428
429 cpu_fprintf(f, " PC " TARGET_FMT_lx " PS %02x\n",
430 env->pc, env->ps);
431 for (i = 0; i < 31; i++) {
432 cpu_fprintf(f, "IR%02d %s " TARGET_FMT_lx " ", i,
433 linux_reg_names[i], cpu_alpha_load_gr(env, i));
434 if ((i % 3) == 2)
435 cpu_fprintf(f, "\n");
436 }
437
438 cpu_fprintf(f, "lock_a " TARGET_FMT_lx " lock_v " TARGET_FMT_lx "\n",
439 env->lock_addr, env->lock_value);
440
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 }
447 cpu_fprintf(f, "\n");
448}
449
450
451
452void QEMU_NORETURN helper_excp(CPUAlphaState *env, int excp, int error)
453{
454 AlphaCPU *cpu = alpha_env_get_cpu(env);
455 CPUState *cs = CPU(cpu);
456
457 cs->exception_index = excp;
458 env->error_code = error;
459 cpu_loop_exit(cs);
460}
461
462
463void QEMU_NORETURN dynamic_excp(CPUAlphaState *env, uintptr_t retaddr,
464 int excp, int error)
465{
466 AlphaCPU *cpu = alpha_env_get_cpu(env);
467 CPUState *cs = CPU(cpu);
468
469 cs->exception_index = excp;
470 env->error_code = error;
471 if (retaddr) {
472 cpu_restore_state(cs, retaddr);
473
474 env->pc += 4;
475 }
476 cpu_loop_exit(cs);
477}
478
479void QEMU_NORETURN arith_excp(CPUAlphaState *env, uintptr_t retaddr,
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}
486