blob: 093acc9af42543a66f4878996adacc43fea4fb16 [file] [log] [blame]
bellardb5ff1b32005-11-26 10:38:39 +00001#include <stdio.h>
2#include <stdlib.h>
3#include <string.h>
4
5#include "cpu.h"
6#include "exec-all.h"
7
pbrook40f137e2006-02-20 00:33:36 +00008void cpu_reset(CPUARMState *env)
9{
10#if defined (CONFIG_USER_ONLY)
11 env->uncached_cpsr = ARM_CPU_MODE_USR;
12 env->vfp.xregs[ARM_VFP_FPEXC] = 1 << 30;
13#else
14 /* SVC mode with interrupts disabled. */
15 env->uncached_cpsr = ARM_CPU_MODE_SVC | CPSR_A | CPSR_F | CPSR_I;
16 env->vfp.xregs[ARM_VFP_FPEXC] = 0;
17#endif
18 env->regs[15] = 0;
19}
20
21CPUARMState *cpu_arm_init(void)
22{
23 CPUARMState *env;
24
25 env = qemu_mallocz(sizeof(CPUARMState));
26 if (!env)
27 return NULL;
28 cpu_exec_init(env);
29 cpu_reset(env);
30 tlb_flush(env, 1);
31 return env;
32}
33
34static inline void set_feature(CPUARMState *env, int feature)
35{
36 env->features |= 1u << feature;
37}
38
pbrook3371d272007-03-08 03:04:12 +000039struct arm_cpu_t {
40 uint32_t id;
41 const char *name;
42};
43
44static const struct arm_cpu_t arm_cpu_names[] = {
45 { ARM_CPUID_ARM926, "arm926"},
46 { ARM_CPUID_ARM1026, "arm1026"},
47 { 0, NULL}
48};
49
50void cpu_arm_set_model(CPUARMState *env, const char *name)
pbrook40f137e2006-02-20 00:33:36 +000051{
pbrook3371d272007-03-08 03:04:12 +000052 int i;
53 uint32_t id;
54
55 id = 0;
56 i = 0;
57 for (i = 0; arm_cpu_names[i].name; i++) {
58 if (strcmp(name, arm_cpu_names[i].name) == 0) {
59 id = arm_cpu_names[i].id;
60 break;
61 }
62 }
63 if (!id) {
64 cpu_abort(env, "Unknown CPU '%s'", name);
65 return;
66 }
67
pbrook40f137e2006-02-20 00:33:36 +000068 env->cp15.c0_cpuid = id;
69 switch (id) {
70 case ARM_CPUID_ARM926:
71 set_feature(env, ARM_FEATURE_VFP);
72 env->vfp.xregs[ARM_VFP_FPSID] = 0x41011090;
73 break;
74 case ARM_CPUID_ARM1026:
75 set_feature(env, ARM_FEATURE_VFP);
76 set_feature(env, ARM_FEATURE_AUXCR);
77 env->vfp.xregs[ARM_VFP_FPSID] = 0x410110a0;
78 break;
79 default:
80 cpu_abort(env, "Bad CPU ID: %x\n", id);
81 break;
82 }
83}
84
85void cpu_arm_close(CPUARMState *env)
86{
87 free(env);
88}
89
bellardb5ff1b32005-11-26 10:38:39 +000090#if defined(CONFIG_USER_ONLY)
91
92void do_interrupt (CPUState *env)
93{
94 env->exception_index = -1;
95}
96
97int cpu_arm_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
98 int is_user, int is_softmmu)
99{
100 if (rw == 2) {
101 env->exception_index = EXCP_PREFETCH_ABORT;
102 env->cp15.c6_insn = address;
103 } else {
104 env->exception_index = EXCP_DATA_ABORT;
105 env->cp15.c6_data = address;
106 }
107 return 1;
108}
109
110target_ulong cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
111{
112 return addr;
113}
114
115/* These should probably raise undefined insn exceptions. */
116void helper_set_cp15(CPUState *env, uint32_t insn, uint32_t val)
117{
118 cpu_abort(env, "cp15 insn %08x\n", insn);
119}
120
121uint32_t helper_get_cp15(CPUState *env, uint32_t insn)
122{
123 cpu_abort(env, "cp15 insn %08x\n", insn);
124 return 0;
125}
126
127void switch_mode(CPUState *env, int mode)
128{
129 if (mode != ARM_CPU_MODE_USR)
130 cpu_abort(env, "Tried to switch out of user mode\n");
131}
132
133#else
134
pbrook8e716212007-01-20 17:12:09 +0000135extern int semihosting_enabled;
136
bellardb5ff1b32005-11-26 10:38:39 +0000137/* Map CPU modes onto saved register banks. */
138static inline int bank_number (int mode)
139{
140 switch (mode) {
141 case ARM_CPU_MODE_USR:
142 case ARM_CPU_MODE_SYS:
143 return 0;
144 case ARM_CPU_MODE_SVC:
145 return 1;
146 case ARM_CPU_MODE_ABT:
147 return 2;
148 case ARM_CPU_MODE_UND:
149 return 3;
150 case ARM_CPU_MODE_IRQ:
151 return 4;
152 case ARM_CPU_MODE_FIQ:
153 return 5;
154 }
155 cpu_abort(cpu_single_env, "Bad mode %x\n", mode);
156 return -1;
157}
158
159void switch_mode(CPUState *env, int mode)
160{
161 int old_mode;
162 int i;
163
164 old_mode = env->uncached_cpsr & CPSR_M;
165 if (mode == old_mode)
166 return;
167
168 if (old_mode == ARM_CPU_MODE_FIQ) {
169 memcpy (env->fiq_regs, env->regs + 8, 5 * sizeof(uint32_t));
pbrook8637c672006-03-14 14:20:32 +0000170 memcpy (env->regs + 8, env->usr_regs, 5 * sizeof(uint32_t));
bellardb5ff1b32005-11-26 10:38:39 +0000171 } else if (mode == ARM_CPU_MODE_FIQ) {
172 memcpy (env->usr_regs, env->regs + 8, 5 * sizeof(uint32_t));
pbrook8637c672006-03-14 14:20:32 +0000173 memcpy (env->regs + 8, env->fiq_regs, 5 * sizeof(uint32_t));
bellardb5ff1b32005-11-26 10:38:39 +0000174 }
175
176 i = bank_number(old_mode);
177 env->banked_r13[i] = env->regs[13];
178 env->banked_r14[i] = env->regs[14];
179 env->banked_spsr[i] = env->spsr;
180
181 i = bank_number(mode);
182 env->regs[13] = env->banked_r13[i];
183 env->regs[14] = env->banked_r14[i];
184 env->spsr = env->banked_spsr[i];
185}
186
187/* Handle a CPU exception. */
188void do_interrupt(CPUARMState *env)
189{
190 uint32_t addr;
191 uint32_t mask;
192 int new_mode;
193 uint32_t offset;
194
195 /* TODO: Vectored interrupt controller. */
196 switch (env->exception_index) {
197 case EXCP_UDEF:
198 new_mode = ARM_CPU_MODE_UND;
199 addr = 0x04;
200 mask = CPSR_I;
201 if (env->thumb)
202 offset = 2;
203 else
204 offset = 4;
205 break;
206 case EXCP_SWI:
pbrook8e716212007-01-20 17:12:09 +0000207 if (semihosting_enabled) {
208 /* Check for semihosting interrupt. */
209 if (env->thumb) {
210 mask = lduw_code(env->regs[15] - 2) & 0xff;
211 } else {
212 mask = ldl_code(env->regs[15] - 4) & 0xffffff;
213 }
214 /* Only intercept calls from privileged modes, to provide some
215 semblance of security. */
216 if (((mask == 0x123456 && !env->thumb)
217 || (mask == 0xab && env->thumb))
218 && (env->uncached_cpsr & CPSR_M) != ARM_CPU_MODE_USR) {
219 env->regs[0] = do_arm_semihosting(env);
220 return;
221 }
222 }
bellardb5ff1b32005-11-26 10:38:39 +0000223 new_mode = ARM_CPU_MODE_SVC;
224 addr = 0x08;
225 mask = CPSR_I;
226 /* The PC already points to the next instructon. */
227 offset = 0;
228 break;
229 case EXCP_PREFETCH_ABORT:
pbrook06c949e2006-02-04 19:35:26 +0000230 case EXCP_BKPT:
bellardb5ff1b32005-11-26 10:38:39 +0000231 new_mode = ARM_CPU_MODE_ABT;
232 addr = 0x0c;
233 mask = CPSR_A | CPSR_I;
234 offset = 4;
235 break;
236 case EXCP_DATA_ABORT:
237 new_mode = ARM_CPU_MODE_ABT;
238 addr = 0x10;
239 mask = CPSR_A | CPSR_I;
240 offset = 8;
241 break;
242 case EXCP_IRQ:
243 new_mode = ARM_CPU_MODE_IRQ;
244 addr = 0x18;
245 /* Disable IRQ and imprecise data aborts. */
246 mask = CPSR_A | CPSR_I;
247 offset = 4;
248 break;
249 case EXCP_FIQ:
250 new_mode = ARM_CPU_MODE_FIQ;
251 addr = 0x1c;
252 /* Disable FIQ, IRQ and imprecise data aborts. */
253 mask = CPSR_A | CPSR_I | CPSR_F;
254 offset = 4;
255 break;
256 default:
257 cpu_abort(env, "Unhandled exception 0x%x\n", env->exception_index);
258 return; /* Never happens. Keep compiler happy. */
259 }
260 /* High vectors. */
261 if (env->cp15.c1_sys & (1 << 13)) {
262 addr += 0xffff0000;
263 }
264 switch_mode (env, new_mode);
265 env->spsr = cpsr_read(env);
bellard6d7e6322005-12-18 16:54:08 +0000266 /* Switch to the new mode, and switch to Arm mode. */
bellardb5ff1b32005-11-26 10:38:39 +0000267 /* ??? Thumb interrupt handlers not implemented. */
bellard6d7e6322005-12-18 16:54:08 +0000268 env->uncached_cpsr = (env->uncached_cpsr & ~CPSR_M) | new_mode;
bellardb5ff1b32005-11-26 10:38:39 +0000269 env->uncached_cpsr |= mask;
bellard6d7e6322005-12-18 16:54:08 +0000270 env->thumb = 0;
bellardb5ff1b32005-11-26 10:38:39 +0000271 env->regs[14] = env->regs[15] + offset;
272 env->regs[15] = addr;
273 env->interrupt_request |= CPU_INTERRUPT_EXITTB;
274}
275
276/* Check section/page access permissions.
277 Returns the page protection flags, or zero if the access is not
278 permitted. */
279static inline int check_ap(CPUState *env, int ap, int domain, int access_type,
280 int is_user)
281{
282 if (domain == 3)
283 return PAGE_READ | PAGE_WRITE;
284
285 switch (ap) {
286 case 0:
pbrook78600322006-09-09 14:36:26 +0000287 if (access_type == 1)
bellardb5ff1b32005-11-26 10:38:39 +0000288 return 0;
289 switch ((env->cp15.c1_sys >> 8) & 3) {
290 case 1:
291 return is_user ? 0 : PAGE_READ;
292 case 2:
293 return PAGE_READ;
294 default:
295 return 0;
296 }
297 case 1:
298 return is_user ? 0 : PAGE_READ | PAGE_WRITE;
299 case 2:
300 if (is_user)
301 return (access_type == 1) ? 0 : PAGE_READ;
302 else
303 return PAGE_READ | PAGE_WRITE;
304 case 3:
305 return PAGE_READ | PAGE_WRITE;
306 default:
307 abort();
308 }
309}
310
311static int get_phys_addr(CPUState *env, uint32_t address, int access_type,
312 int is_user, uint32_t *phys_ptr, int *prot)
313{
314 int code;
315 uint32_t table;
316 uint32_t desc;
317 int type;
318 int ap;
319 int domain;
320 uint32_t phys_addr;
321
322 /* Fast Context Switch Extension. */
323 if (address < 0x02000000)
324 address += env->cp15.c13_fcse;
325
326 if ((env->cp15.c1_sys & 1) == 0) {
327 /* MMU diusabled. */
328 *phys_ptr = address;
329 *prot = PAGE_READ | PAGE_WRITE;
330 } else {
331 /* Pagetable walk. */
332 /* Lookup l1 descriptor. */
333 table = (env->cp15.c2 & 0xffffc000) | ((address >> 18) & 0x3ffc);
334 desc = ldl_phys(table);
335 type = (desc & 3);
336 domain = (env->cp15.c3 >> ((desc >> 4) & 0x1e)) & 3;
337 if (type == 0) {
338 /* Secton translation fault. */
339 code = 5;
340 goto do_fault;
341 }
342 if (domain == 0 || domain == 2) {
343 if (type == 2)
344 code = 9; /* Section domain fault. */
345 else
346 code = 11; /* Page domain fault. */
347 goto do_fault;
348 }
349 if (type == 2) {
350 /* 1Mb section. */
351 phys_addr = (desc & 0xfff00000) | (address & 0x000fffff);
352 ap = (desc >> 10) & 3;
353 code = 13;
354 } else {
355 /* Lookup l2 entry. */
356 table = (desc & 0xfffffc00) | ((address >> 10) & 0x3fc);
357 desc = ldl_phys(table);
358 switch (desc & 3) {
359 case 0: /* Page translation fault. */
360 code = 7;
361 goto do_fault;
362 case 1: /* 64k page. */
363 phys_addr = (desc & 0xffff0000) | (address & 0xffff);
364 ap = (desc >> (4 + ((address >> 13) & 6))) & 3;
365 break;
366 case 2: /* 4k page. */
367 phys_addr = (desc & 0xfffff000) | (address & 0xfff);
368 ap = (desc >> (4 + ((address >> 13) & 6))) & 3;
369 break;
370 case 3: /* 1k page. */
371 if (type == 1) {
372 /* Page translation fault. */
373 code = 7;
374 goto do_fault;
375 }
376 phys_addr = (desc & 0xfffffc00) | (address & 0x3ff);
377 ap = (desc >> 4) & 3;
378 break;
379 default:
380 /* Never happens, but compiler isn't smart enough to tell. */
381 abort();
382 }
383 code = 15;
384 }
385 *prot = check_ap(env, ap, domain, access_type, is_user);
386 if (!*prot) {
387 /* Access permission fault. */
388 goto do_fault;
389 }
390 *phys_ptr = phys_addr;
391 }
392 return 0;
393do_fault:
394 return code | (domain << 4);
395}
396
397int cpu_arm_handle_mmu_fault (CPUState *env, target_ulong address,
398 int access_type, int is_user, int is_softmmu)
399{
400 uint32_t phys_addr;
401 int prot;
402 int ret;
403
404 ret = get_phys_addr(env, address, access_type, is_user, &phys_addr, &prot);
405 if (ret == 0) {
406 /* Map a single [sub]page. */
407 phys_addr &= ~(uint32_t)0x3ff;
408 address &= ~(uint32_t)0x3ff;
409 return tlb_set_page (env, address, phys_addr, prot, is_user,
410 is_softmmu);
411 }
412
413 if (access_type == 2) {
414 env->cp15.c5_insn = ret;
415 env->cp15.c6_insn = address;
416 env->exception_index = EXCP_PREFETCH_ABORT;
417 } else {
418 env->cp15.c5_data = ret;
419 env->cp15.c6_data = address;
420 env->exception_index = EXCP_DATA_ABORT;
421 }
422 return 1;
423}
424
425target_ulong cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
426{
427 uint32_t phys_addr;
428 int prot;
429 int ret;
430
431 ret = get_phys_addr(env, addr, 0, 0, &phys_addr, &prot);
432
433 if (ret != 0)
434 return -1;
435
436 return phys_addr;
437}
438
439void helper_set_cp15(CPUState *env, uint32_t insn, uint32_t val)
440{
441 uint32_t op2;
442
443 op2 = (insn >> 5) & 7;
444 switch ((insn >> 16) & 0xf) {
445 case 0: /* ID codes. */
446 goto bad_reg;
447 case 1: /* System configuration. */
448 switch (op2) {
449 case 0:
450 env->cp15.c1_sys = val;
451 /* ??? Lots of these bits are not implemented. */
452 /* This may enable/disable the MMU, so do a TLB flush. */
453 tlb_flush(env, 1);
454 break;
455 case 2:
456 env->cp15.c1_coproc = val;
457 /* ??? Is this safe when called from within a TB? */
458 tb_flush(env);
459 default:
460 goto bad_reg;
461 }
462 break;
463 case 2: /* MMU Page table control. */
464 env->cp15.c2 = val;
465 break;
466 case 3: /* MMU Domain access control. */
467 env->cp15.c3 = val;
468 break;
469 case 4: /* Reserved. */
470 goto bad_reg;
471 case 5: /* MMU Fault status. */
472 switch (op2) {
473 case 0:
474 env->cp15.c5_data = val;
475 break;
476 case 1:
477 env->cp15.c5_insn = val;
478 break;
479 default:
480 goto bad_reg;
481 }
482 break;
483 case 6: /* MMU Fault address. */
484 switch (op2) {
485 case 0:
486 env->cp15.c6_data = val;
487 break;
488 case 1:
489 env->cp15.c6_insn = val;
490 break;
491 default:
492 goto bad_reg;
493 }
494 break;
495 case 7: /* Cache control. */
496 /* No cache, so nothing to do. */
497 break;
498 case 8: /* MMU TLB control. */
499 switch (op2) {
500 case 0: /* Invalidate all. */
501 tlb_flush(env, 0);
502 break;
503 case 1: /* Invalidate single TLB entry. */
504#if 0
505 /* ??? This is wrong for large pages and sections. */
506 /* As an ugly hack to make linux work we always flush a 4K
507 pages. */
508 val &= 0xfffff000;
509 tlb_flush_page(env, val);
510 tlb_flush_page(env, val + 0x400);
511 tlb_flush_page(env, val + 0x800);
512 tlb_flush_page(env, val + 0xc00);
513#else
514 tlb_flush(env, 1);
515#endif
516 break;
517 default:
518 goto bad_reg;
519 }
520 break;
521 case 9: /* Cache lockdown. */
522 switch (op2) {
523 case 0:
524 env->cp15.c9_data = val;
525 break;
526 case 1:
527 env->cp15.c9_insn = val;
528 break;
529 default:
530 goto bad_reg;
531 }
532 break;
533 case 10: /* MMU TLB lockdown. */
534 /* ??? TLB lockdown not implemented. */
535 break;
536 case 11: /* TCM DMA control. */
537 case 12: /* Reserved. */
538 goto bad_reg;
539 case 13: /* Process ID. */
540 switch (op2) {
541 case 0:
pbrookd07edbf2006-07-21 22:39:57 +0000542 /* Unlike real hardware the qemu TLB uses virtual addresses,
543 not modified virtual addresses, so this causes a TLB flush.
544 */
545 if (env->cp15.c13_fcse != val)
546 tlb_flush(env, 1);
547 env->cp15.c13_fcse = val;
bellardb5ff1b32005-11-26 10:38:39 +0000548 break;
549 case 1:
pbrookd07edbf2006-07-21 22:39:57 +0000550 /* This changes the ASID, so do a TLB flush. */
551 if (env->cp15.c13_context != val)
552 tlb_flush(env, 0);
553 env->cp15.c13_context = val;
bellardb5ff1b32005-11-26 10:38:39 +0000554 break;
555 default:
556 goto bad_reg;
557 }
558 break;
559 case 14: /* Reserved. */
560 goto bad_reg;
561 case 15: /* Implementation specific. */
562 /* ??? Internal registers not implemented. */
563 break;
564 }
565 return;
566bad_reg:
567 /* ??? For debugging only. Should raise illegal instruction exception. */
568 cpu_abort(env, "Unimplemented cp15 register read\n");
569}
570
571uint32_t helper_get_cp15(CPUState *env, uint32_t insn)
572{
573 uint32_t op2;
574
575 op2 = (insn >> 5) & 7;
576 switch ((insn >> 16) & 0xf) {
577 case 0: /* ID codes. */
578 switch (op2) {
579 default: /* Device ID. */
pbrook40f137e2006-02-20 00:33:36 +0000580 return env->cp15.c0_cpuid;
bellardb5ff1b32005-11-26 10:38:39 +0000581 case 1: /* Cache Type. */
582 return 0x1dd20d2;
583 case 2: /* TCM status. */
584 return 0;
585 }
586 case 1: /* System configuration. */
587 switch (op2) {
588 case 0: /* Control register. */
589 return env->cp15.c1_sys;
590 case 1: /* Auxiliary control register. */
pbrook40f137e2006-02-20 00:33:36 +0000591 if (arm_feature(env, ARM_FEATURE_AUXCR))
592 return 1;
593 goto bad_reg;
bellardb5ff1b32005-11-26 10:38:39 +0000594 case 2: /* Coprocessor access register. */
595 return env->cp15.c1_coproc;
596 default:
597 goto bad_reg;
598 }
599 case 2: /* MMU Page table control. */
600 return env->cp15.c2;
601 case 3: /* MMU Domain access control. */
602 return env->cp15.c3;
603 case 4: /* Reserved. */
604 goto bad_reg;
605 case 5: /* MMU Fault status. */
606 switch (op2) {
607 case 0:
608 return env->cp15.c5_data;
609 case 1:
610 return env->cp15.c5_insn;
611 default:
612 goto bad_reg;
613 }
614 case 6: /* MMU Fault address. */
615 switch (op2) {
616 case 0:
617 return env->cp15.c6_data;
618 case 1:
pbrook40f137e2006-02-20 00:33:36 +0000619 /* Arm9 doesn't have an IFAR, but implementing it anyway shouldn't
620 do any harm. */
bellardb5ff1b32005-11-26 10:38:39 +0000621 return env->cp15.c6_insn;
622 default:
623 goto bad_reg;
624 }
625 case 7: /* Cache control. */
626 /* ??? This is for test, clean and invaidate operations that set the
627 Z flag. We can't represent N = Z = 1, so it also clears clears
628 the N flag. Oh well. */
629 env->NZF = 0;
630 return 0;
631 case 8: /* MMU TLB control. */
632 goto bad_reg;
633 case 9: /* Cache lockdown. */
634 switch (op2) {
635 case 0:
636 return env->cp15.c9_data;
637 case 1:
638 return env->cp15.c9_insn;
639 default:
640 goto bad_reg;
641 }
642 case 10: /* MMU TLB lockdown. */
643 /* ??? TLB lockdown not implemented. */
644 return 0;
645 case 11: /* TCM DMA control. */
646 case 12: /* Reserved. */
647 goto bad_reg;
648 case 13: /* Process ID. */
649 switch (op2) {
650 case 0:
651 return env->cp15.c13_fcse;
652 case 1:
653 return env->cp15.c13_context;
654 default:
655 goto bad_reg;
656 }
657 case 14: /* Reserved. */
658 goto bad_reg;
659 case 15: /* Implementation specific. */
660 /* ??? Internal registers not implemented. */
661 return 0;
662 }
663bad_reg:
664 /* ??? For debugging only. Should raise illegal instruction exception. */
665 cpu_abort(env, "Unimplemented cp15 register read\n");
666 return 0;
667}
668
669#endif