blob: 538e17a35c80d4fbf46bec52e5619fdc24817818 [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
8#if defined(CONFIG_USER_ONLY)
9
10void do_interrupt (CPUState *env)
11{
12 env->exception_index = -1;
13}
14
15int cpu_arm_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
16 int is_user, int is_softmmu)
17{
18 if (rw == 2) {
19 env->exception_index = EXCP_PREFETCH_ABORT;
20 env->cp15.c6_insn = address;
21 } else {
22 env->exception_index = EXCP_DATA_ABORT;
23 env->cp15.c6_data = address;
24 }
25 return 1;
26}
27
28target_ulong cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
29{
30 return addr;
31}
32
33/* These should probably raise undefined insn exceptions. */
34void helper_set_cp15(CPUState *env, uint32_t insn, uint32_t val)
35{
36 cpu_abort(env, "cp15 insn %08x\n", insn);
37}
38
39uint32_t helper_get_cp15(CPUState *env, uint32_t insn)
40{
41 cpu_abort(env, "cp15 insn %08x\n", insn);
42 return 0;
43}
44
45void switch_mode(CPUState *env, int mode)
46{
47 if (mode != ARM_CPU_MODE_USR)
48 cpu_abort(env, "Tried to switch out of user mode\n");
49}
50
51#else
52
53/* Map CPU modes onto saved register banks. */
54static inline int bank_number (int mode)
55{
56 switch (mode) {
57 case ARM_CPU_MODE_USR:
58 case ARM_CPU_MODE_SYS:
59 return 0;
60 case ARM_CPU_MODE_SVC:
61 return 1;
62 case ARM_CPU_MODE_ABT:
63 return 2;
64 case ARM_CPU_MODE_UND:
65 return 3;
66 case ARM_CPU_MODE_IRQ:
67 return 4;
68 case ARM_CPU_MODE_FIQ:
69 return 5;
70 }
71 cpu_abort(cpu_single_env, "Bad mode %x\n", mode);
72 return -1;
73}
74
75void switch_mode(CPUState *env, int mode)
76{
77 int old_mode;
78 int i;
79
80 old_mode = env->uncached_cpsr & CPSR_M;
81 if (mode == old_mode)
82 return;
83
84 if (old_mode == ARM_CPU_MODE_FIQ) {
85 memcpy (env->fiq_regs, env->regs + 8, 5 * sizeof(uint32_t));
86 memcpy (env->regs, env->usr_regs + 8, 5 * sizeof(uint32_t));
87 } else if (mode == ARM_CPU_MODE_FIQ) {
88 memcpy (env->usr_regs, env->regs + 8, 5 * sizeof(uint32_t));
89 memcpy (env->regs, env->fiq_regs + 8, 5 * sizeof(uint32_t));
90 }
91
92 i = bank_number(old_mode);
93 env->banked_r13[i] = env->regs[13];
94 env->banked_r14[i] = env->regs[14];
95 env->banked_spsr[i] = env->spsr;
96
97 i = bank_number(mode);
98 env->regs[13] = env->banked_r13[i];
99 env->regs[14] = env->banked_r14[i];
100 env->spsr = env->banked_spsr[i];
101}
102
103/* Handle a CPU exception. */
104void do_interrupt(CPUARMState *env)
105{
106 uint32_t addr;
107 uint32_t mask;
108 int new_mode;
109 uint32_t offset;
110
111 /* TODO: Vectored interrupt controller. */
112 switch (env->exception_index) {
113 case EXCP_UDEF:
114 new_mode = ARM_CPU_MODE_UND;
115 addr = 0x04;
116 mask = CPSR_I;
117 if (env->thumb)
118 offset = 2;
119 else
120 offset = 4;
121 break;
122 case EXCP_SWI:
123 new_mode = ARM_CPU_MODE_SVC;
124 addr = 0x08;
125 mask = CPSR_I;
126 /* The PC already points to the next instructon. */
127 offset = 0;
128 break;
129 case EXCP_PREFETCH_ABORT:
130 new_mode = ARM_CPU_MODE_ABT;
131 addr = 0x0c;
132 mask = CPSR_A | CPSR_I;
133 offset = 4;
134 break;
135 case EXCP_DATA_ABORT:
136 new_mode = ARM_CPU_MODE_ABT;
137 addr = 0x10;
138 mask = CPSR_A | CPSR_I;
139 offset = 8;
140 break;
141 case EXCP_IRQ:
142 new_mode = ARM_CPU_MODE_IRQ;
143 addr = 0x18;
144 /* Disable IRQ and imprecise data aborts. */
145 mask = CPSR_A | CPSR_I;
146 offset = 4;
147 break;
148 case EXCP_FIQ:
149 new_mode = ARM_CPU_MODE_FIQ;
150 addr = 0x1c;
151 /* Disable FIQ, IRQ and imprecise data aborts. */
152 mask = CPSR_A | CPSR_I | CPSR_F;
153 offset = 4;
154 break;
155 default:
156 cpu_abort(env, "Unhandled exception 0x%x\n", env->exception_index);
157 return; /* Never happens. Keep compiler happy. */
158 }
159 /* High vectors. */
160 if (env->cp15.c1_sys & (1 << 13)) {
161 addr += 0xffff0000;
162 }
163 switch_mode (env, new_mode);
164 env->spsr = cpsr_read(env);
bellard6d7e6322005-12-18 16:54:08 +0000165 /* Switch to the new mode, and switch to Arm mode. */
bellardb5ff1b32005-11-26 10:38:39 +0000166 /* ??? Thumb interrupt handlers not implemented. */
bellard6d7e6322005-12-18 16:54:08 +0000167 env->uncached_cpsr = (env->uncached_cpsr & ~CPSR_M) | new_mode;
bellardb5ff1b32005-11-26 10:38:39 +0000168 env->uncached_cpsr |= mask;
bellard6d7e6322005-12-18 16:54:08 +0000169 env->thumb = 0;
bellardb5ff1b32005-11-26 10:38:39 +0000170 env->regs[14] = env->regs[15] + offset;
171 env->regs[15] = addr;
172 env->interrupt_request |= CPU_INTERRUPT_EXITTB;
173}
174
175/* Check section/page access permissions.
176 Returns the page protection flags, or zero if the access is not
177 permitted. */
178static inline int check_ap(CPUState *env, int ap, int domain, int access_type,
179 int is_user)
180{
181 if (domain == 3)
182 return PAGE_READ | PAGE_WRITE;
183
184 switch (ap) {
185 case 0:
186 if (access_type != 1)
187 return 0;
188 switch ((env->cp15.c1_sys >> 8) & 3) {
189 case 1:
190 return is_user ? 0 : PAGE_READ;
191 case 2:
192 return PAGE_READ;
193 default:
194 return 0;
195 }
196 case 1:
197 return is_user ? 0 : PAGE_READ | PAGE_WRITE;
198 case 2:
199 if (is_user)
200 return (access_type == 1) ? 0 : PAGE_READ;
201 else
202 return PAGE_READ | PAGE_WRITE;
203 case 3:
204 return PAGE_READ | PAGE_WRITE;
205 default:
206 abort();
207 }
208}
209
210static int get_phys_addr(CPUState *env, uint32_t address, int access_type,
211 int is_user, uint32_t *phys_ptr, int *prot)
212{
213 int code;
214 uint32_t table;
215 uint32_t desc;
216 int type;
217 int ap;
218 int domain;
219 uint32_t phys_addr;
220
221 /* Fast Context Switch Extension. */
222 if (address < 0x02000000)
223 address += env->cp15.c13_fcse;
224
225 if ((env->cp15.c1_sys & 1) == 0) {
226 /* MMU diusabled. */
227 *phys_ptr = address;
228 *prot = PAGE_READ | PAGE_WRITE;
229 } else {
230 /* Pagetable walk. */
231 /* Lookup l1 descriptor. */
232 table = (env->cp15.c2 & 0xffffc000) | ((address >> 18) & 0x3ffc);
233 desc = ldl_phys(table);
234 type = (desc & 3);
235 domain = (env->cp15.c3 >> ((desc >> 4) & 0x1e)) & 3;
236 if (type == 0) {
237 /* Secton translation fault. */
238 code = 5;
239 goto do_fault;
240 }
241 if (domain == 0 || domain == 2) {
242 if (type == 2)
243 code = 9; /* Section domain fault. */
244 else
245 code = 11; /* Page domain fault. */
246 goto do_fault;
247 }
248 if (type == 2) {
249 /* 1Mb section. */
250 phys_addr = (desc & 0xfff00000) | (address & 0x000fffff);
251 ap = (desc >> 10) & 3;
252 code = 13;
253 } else {
254 /* Lookup l2 entry. */
255 table = (desc & 0xfffffc00) | ((address >> 10) & 0x3fc);
256 desc = ldl_phys(table);
257 switch (desc & 3) {
258 case 0: /* Page translation fault. */
259 code = 7;
260 goto do_fault;
261 case 1: /* 64k page. */
262 phys_addr = (desc & 0xffff0000) | (address & 0xffff);
263 ap = (desc >> (4 + ((address >> 13) & 6))) & 3;
264 break;
265 case 2: /* 4k page. */
266 phys_addr = (desc & 0xfffff000) | (address & 0xfff);
267 ap = (desc >> (4 + ((address >> 13) & 6))) & 3;
268 break;
269 case 3: /* 1k page. */
270 if (type == 1) {
271 /* Page translation fault. */
272 code = 7;
273 goto do_fault;
274 }
275 phys_addr = (desc & 0xfffffc00) | (address & 0x3ff);
276 ap = (desc >> 4) & 3;
277 break;
278 default:
279 /* Never happens, but compiler isn't smart enough to tell. */
280 abort();
281 }
282 code = 15;
283 }
284 *prot = check_ap(env, ap, domain, access_type, is_user);
285 if (!*prot) {
286 /* Access permission fault. */
287 goto do_fault;
288 }
289 *phys_ptr = phys_addr;
290 }
291 return 0;
292do_fault:
293 return code | (domain << 4);
294}
295
296int cpu_arm_handle_mmu_fault (CPUState *env, target_ulong address,
297 int access_type, int is_user, int is_softmmu)
298{
299 uint32_t phys_addr;
300 int prot;
301 int ret;
302
303 ret = get_phys_addr(env, address, access_type, is_user, &phys_addr, &prot);
304 if (ret == 0) {
305 /* Map a single [sub]page. */
306 phys_addr &= ~(uint32_t)0x3ff;
307 address &= ~(uint32_t)0x3ff;
308 return tlb_set_page (env, address, phys_addr, prot, is_user,
309 is_softmmu);
310 }
311
312 if (access_type == 2) {
313 env->cp15.c5_insn = ret;
314 env->cp15.c6_insn = address;
315 env->exception_index = EXCP_PREFETCH_ABORT;
316 } else {
317 env->cp15.c5_data = ret;
318 env->cp15.c6_data = address;
319 env->exception_index = EXCP_DATA_ABORT;
320 }
321 return 1;
322}
323
324target_ulong cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
325{
326 uint32_t phys_addr;
327 int prot;
328 int ret;
329
330 ret = get_phys_addr(env, addr, 0, 0, &phys_addr, &prot);
331
332 if (ret != 0)
333 return -1;
334
335 return phys_addr;
336}
337
338void helper_set_cp15(CPUState *env, uint32_t insn, uint32_t val)
339{
340 uint32_t op2;
341
342 op2 = (insn >> 5) & 7;
343 switch ((insn >> 16) & 0xf) {
344 case 0: /* ID codes. */
345 goto bad_reg;
346 case 1: /* System configuration. */
347 switch (op2) {
348 case 0:
349 env->cp15.c1_sys = val;
350 /* ??? Lots of these bits are not implemented. */
351 /* This may enable/disable the MMU, so do a TLB flush. */
352 tlb_flush(env, 1);
353 break;
354 case 2:
355 env->cp15.c1_coproc = val;
356 /* ??? Is this safe when called from within a TB? */
357 tb_flush(env);
358 default:
359 goto bad_reg;
360 }
361 break;
362 case 2: /* MMU Page table control. */
363 env->cp15.c2 = val;
364 break;
365 case 3: /* MMU Domain access control. */
366 env->cp15.c3 = val;
367 break;
368 case 4: /* Reserved. */
369 goto bad_reg;
370 case 5: /* MMU Fault status. */
371 switch (op2) {
372 case 0:
373 env->cp15.c5_data = val;
374 break;
375 case 1:
376 env->cp15.c5_insn = val;
377 break;
378 default:
379 goto bad_reg;
380 }
381 break;
382 case 6: /* MMU Fault address. */
383 switch (op2) {
384 case 0:
385 env->cp15.c6_data = val;
386 break;
387 case 1:
388 env->cp15.c6_insn = val;
389 break;
390 default:
391 goto bad_reg;
392 }
393 break;
394 case 7: /* Cache control. */
395 /* No cache, so nothing to do. */
396 break;
397 case 8: /* MMU TLB control. */
398 switch (op2) {
399 case 0: /* Invalidate all. */
400 tlb_flush(env, 0);
401 break;
402 case 1: /* Invalidate single TLB entry. */
403#if 0
404 /* ??? This is wrong for large pages and sections. */
405 /* As an ugly hack to make linux work we always flush a 4K
406 pages. */
407 val &= 0xfffff000;
408 tlb_flush_page(env, val);
409 tlb_flush_page(env, val + 0x400);
410 tlb_flush_page(env, val + 0x800);
411 tlb_flush_page(env, val + 0xc00);
412#else
413 tlb_flush(env, 1);
414#endif
415 break;
416 default:
417 goto bad_reg;
418 }
419 break;
420 case 9: /* Cache lockdown. */
421 switch (op2) {
422 case 0:
423 env->cp15.c9_data = val;
424 break;
425 case 1:
426 env->cp15.c9_insn = val;
427 break;
428 default:
429 goto bad_reg;
430 }
431 break;
432 case 10: /* MMU TLB lockdown. */
433 /* ??? TLB lockdown not implemented. */
434 break;
435 case 11: /* TCM DMA control. */
436 case 12: /* Reserved. */
437 goto bad_reg;
438 case 13: /* Process ID. */
439 switch (op2) {
440 case 0:
441 env->cp15.c9_data = val;
442 break;
443 case 1:
444 env->cp15.c9_insn = val;
445 break;
446 default:
447 goto bad_reg;
448 }
449 break;
450 case 14: /* Reserved. */
451 goto bad_reg;
452 case 15: /* Implementation specific. */
453 /* ??? Internal registers not implemented. */
454 break;
455 }
456 return;
457bad_reg:
458 /* ??? For debugging only. Should raise illegal instruction exception. */
459 cpu_abort(env, "Unimplemented cp15 register read\n");
460}
461
462uint32_t helper_get_cp15(CPUState *env, uint32_t insn)
463{
464 uint32_t op2;
465
466 op2 = (insn >> 5) & 7;
467 switch ((insn >> 16) & 0xf) {
468 case 0: /* ID codes. */
469 switch (op2) {
470 default: /* Device ID. */
471 return 0x4106a262;
472 case 1: /* Cache Type. */
473 return 0x1dd20d2;
474 case 2: /* TCM status. */
475 return 0;
476 }
477 case 1: /* System configuration. */
478 switch (op2) {
479 case 0: /* Control register. */
480 return env->cp15.c1_sys;
481 case 1: /* Auxiliary control register. */
482 return 1;
483 case 2: /* Coprocessor access register. */
484 return env->cp15.c1_coproc;
485 default:
486 goto bad_reg;
487 }
488 case 2: /* MMU Page table control. */
489 return env->cp15.c2;
490 case 3: /* MMU Domain access control. */
491 return env->cp15.c3;
492 case 4: /* Reserved. */
493 goto bad_reg;
494 case 5: /* MMU Fault status. */
495 switch (op2) {
496 case 0:
497 return env->cp15.c5_data;
498 case 1:
499 return env->cp15.c5_insn;
500 default:
501 goto bad_reg;
502 }
503 case 6: /* MMU Fault address. */
504 switch (op2) {
505 case 0:
506 return env->cp15.c6_data;
507 case 1:
508 return env->cp15.c6_insn;
509 default:
510 goto bad_reg;
511 }
512 case 7: /* Cache control. */
513 /* ??? This is for test, clean and invaidate operations that set the
514 Z flag. We can't represent N = Z = 1, so it also clears clears
515 the N flag. Oh well. */
516 env->NZF = 0;
517 return 0;
518 case 8: /* MMU TLB control. */
519 goto bad_reg;
520 case 9: /* Cache lockdown. */
521 switch (op2) {
522 case 0:
523 return env->cp15.c9_data;
524 case 1:
525 return env->cp15.c9_insn;
526 default:
527 goto bad_reg;
528 }
529 case 10: /* MMU TLB lockdown. */
530 /* ??? TLB lockdown not implemented. */
531 return 0;
532 case 11: /* TCM DMA control. */
533 case 12: /* Reserved. */
534 goto bad_reg;
535 case 13: /* Process ID. */
536 switch (op2) {
537 case 0:
538 return env->cp15.c13_fcse;
539 case 1:
540 return env->cp15.c13_context;
541 default:
542 goto bad_reg;
543 }
544 case 14: /* Reserved. */
545 goto bad_reg;
546 case 15: /* Implementation specific. */
547 /* ??? Internal registers not implemented. */
548 return 0;
549 }
550bad_reg:
551 /* ??? For debugging only. Should raise illegal instruction exception. */
552 cpu_abort(env, "Unimplemented cp15 register read\n");
553 return 0;
554}
555
556#endif