blob: 9cf159c07c04b8e1396e4c0d14fe762f59dcd5b9 [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);
165 /* Switch to the new mode, and clear the thumb bit. */
166 /* ??? Thumb interrupt handlers not implemented. */
167 env->uncached_cpsr = (env->uncached_cpsr & ~(CPSR_M | CPSR_T)) | new_mode;
168 env->uncached_cpsr |= mask;
169 env->regs[14] = env->regs[15] + offset;
170 env->regs[15] = addr;
171 env->interrupt_request |= CPU_INTERRUPT_EXITTB;
172}
173
174/* Check section/page access permissions.
175 Returns the page protection flags, or zero if the access is not
176 permitted. */
177static inline int check_ap(CPUState *env, int ap, int domain, int access_type,
178 int is_user)
179{
180 if (domain == 3)
181 return PAGE_READ | PAGE_WRITE;
182
183 switch (ap) {
184 case 0:
185 if (access_type != 1)
186 return 0;
187 switch ((env->cp15.c1_sys >> 8) & 3) {
188 case 1:
189 return is_user ? 0 : PAGE_READ;
190 case 2:
191 return PAGE_READ;
192 default:
193 return 0;
194 }
195 case 1:
196 return is_user ? 0 : PAGE_READ | PAGE_WRITE;
197 case 2:
198 if (is_user)
199 return (access_type == 1) ? 0 : PAGE_READ;
200 else
201 return PAGE_READ | PAGE_WRITE;
202 case 3:
203 return PAGE_READ | PAGE_WRITE;
204 default:
205 abort();
206 }
207}
208
209static int get_phys_addr(CPUState *env, uint32_t address, int access_type,
210 int is_user, uint32_t *phys_ptr, int *prot)
211{
212 int code;
213 uint32_t table;
214 uint32_t desc;
215 int type;
216 int ap;
217 int domain;
218 uint32_t phys_addr;
219
220 /* Fast Context Switch Extension. */
221 if (address < 0x02000000)
222 address += env->cp15.c13_fcse;
223
224 if ((env->cp15.c1_sys & 1) == 0) {
225 /* MMU diusabled. */
226 *phys_ptr = address;
227 *prot = PAGE_READ | PAGE_WRITE;
228 } else {
229 /* Pagetable walk. */
230 /* Lookup l1 descriptor. */
231 table = (env->cp15.c2 & 0xffffc000) | ((address >> 18) & 0x3ffc);
232 desc = ldl_phys(table);
233 type = (desc & 3);
234 domain = (env->cp15.c3 >> ((desc >> 4) & 0x1e)) & 3;
235 if (type == 0) {
236 /* Secton translation fault. */
237 code = 5;
238 goto do_fault;
239 }
240 if (domain == 0 || domain == 2) {
241 if (type == 2)
242 code = 9; /* Section domain fault. */
243 else
244 code = 11; /* Page domain fault. */
245 goto do_fault;
246 }
247 if (type == 2) {
248 /* 1Mb section. */
249 phys_addr = (desc & 0xfff00000) | (address & 0x000fffff);
250 ap = (desc >> 10) & 3;
251 code = 13;
252 } else {
253 /* Lookup l2 entry. */
254 table = (desc & 0xfffffc00) | ((address >> 10) & 0x3fc);
255 desc = ldl_phys(table);
256 switch (desc & 3) {
257 case 0: /* Page translation fault. */
258 code = 7;
259 goto do_fault;
260 case 1: /* 64k page. */
261 phys_addr = (desc & 0xffff0000) | (address & 0xffff);
262 ap = (desc >> (4 + ((address >> 13) & 6))) & 3;
263 break;
264 case 2: /* 4k page. */
265 phys_addr = (desc & 0xfffff000) | (address & 0xfff);
266 ap = (desc >> (4 + ((address >> 13) & 6))) & 3;
267 break;
268 case 3: /* 1k page. */
269 if (type == 1) {
270 /* Page translation fault. */
271 code = 7;
272 goto do_fault;
273 }
274 phys_addr = (desc & 0xfffffc00) | (address & 0x3ff);
275 ap = (desc >> 4) & 3;
276 break;
277 default:
278 /* Never happens, but compiler isn't smart enough to tell. */
279 abort();
280 }
281 code = 15;
282 }
283 *prot = check_ap(env, ap, domain, access_type, is_user);
284 if (!*prot) {
285 /* Access permission fault. */
286 goto do_fault;
287 }
288 *phys_ptr = phys_addr;
289 }
290 return 0;
291do_fault:
292 return code | (domain << 4);
293}
294
295int cpu_arm_handle_mmu_fault (CPUState *env, target_ulong address,
296 int access_type, int is_user, int is_softmmu)
297{
298 uint32_t phys_addr;
299 int prot;
300 int ret;
301
302 ret = get_phys_addr(env, address, access_type, is_user, &phys_addr, &prot);
303 if (ret == 0) {
304 /* Map a single [sub]page. */
305 phys_addr &= ~(uint32_t)0x3ff;
306 address &= ~(uint32_t)0x3ff;
307 return tlb_set_page (env, address, phys_addr, prot, is_user,
308 is_softmmu);
309 }
310
311 if (access_type == 2) {
312 env->cp15.c5_insn = ret;
313 env->cp15.c6_insn = address;
314 env->exception_index = EXCP_PREFETCH_ABORT;
315 } else {
316 env->cp15.c5_data = ret;
317 env->cp15.c6_data = address;
318 env->exception_index = EXCP_DATA_ABORT;
319 }
320 return 1;
321}
322
323target_ulong cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
324{
325 uint32_t phys_addr;
326 int prot;
327 int ret;
328
329 ret = get_phys_addr(env, addr, 0, 0, &phys_addr, &prot);
330
331 if (ret != 0)
332 return -1;
333
334 return phys_addr;
335}
336
337void helper_set_cp15(CPUState *env, uint32_t insn, uint32_t val)
338{
339 uint32_t op2;
340
341 op2 = (insn >> 5) & 7;
342 switch ((insn >> 16) & 0xf) {
343 case 0: /* ID codes. */
344 goto bad_reg;
345 case 1: /* System configuration. */
346 switch (op2) {
347 case 0:
348 env->cp15.c1_sys = val;
349 /* ??? Lots of these bits are not implemented. */
350 /* This may enable/disable the MMU, so do a TLB flush. */
351 tlb_flush(env, 1);
352 break;
353 case 2:
354 env->cp15.c1_coproc = val;
355 /* ??? Is this safe when called from within a TB? */
356 tb_flush(env);
357 default:
358 goto bad_reg;
359 }
360 break;
361 case 2: /* MMU Page table control. */
362 env->cp15.c2 = val;
363 break;
364 case 3: /* MMU Domain access control. */
365 env->cp15.c3 = val;
366 break;
367 case 4: /* Reserved. */
368 goto bad_reg;
369 case 5: /* MMU Fault status. */
370 switch (op2) {
371 case 0:
372 env->cp15.c5_data = val;
373 break;
374 case 1:
375 env->cp15.c5_insn = val;
376 break;
377 default:
378 goto bad_reg;
379 }
380 break;
381 case 6: /* MMU Fault address. */
382 switch (op2) {
383 case 0:
384 env->cp15.c6_data = val;
385 break;
386 case 1:
387 env->cp15.c6_insn = val;
388 break;
389 default:
390 goto bad_reg;
391 }
392 break;
393 case 7: /* Cache control. */
394 /* No cache, so nothing to do. */
395 break;
396 case 8: /* MMU TLB control. */
397 switch (op2) {
398 case 0: /* Invalidate all. */
399 tlb_flush(env, 0);
400 break;
401 case 1: /* Invalidate single TLB entry. */
402#if 0
403 /* ??? This is wrong for large pages and sections. */
404 /* As an ugly hack to make linux work we always flush a 4K
405 pages. */
406 val &= 0xfffff000;
407 tlb_flush_page(env, val);
408 tlb_flush_page(env, val + 0x400);
409 tlb_flush_page(env, val + 0x800);
410 tlb_flush_page(env, val + 0xc00);
411#else
412 tlb_flush(env, 1);
413#endif
414 break;
415 default:
416 goto bad_reg;
417 }
418 break;
419 case 9: /* Cache lockdown. */
420 switch (op2) {
421 case 0:
422 env->cp15.c9_data = val;
423 break;
424 case 1:
425 env->cp15.c9_insn = val;
426 break;
427 default:
428 goto bad_reg;
429 }
430 break;
431 case 10: /* MMU TLB lockdown. */
432 /* ??? TLB lockdown not implemented. */
433 break;
434 case 11: /* TCM DMA control. */
435 case 12: /* Reserved. */
436 goto bad_reg;
437 case 13: /* Process ID. */
438 switch (op2) {
439 case 0:
440 env->cp15.c9_data = val;
441 break;
442 case 1:
443 env->cp15.c9_insn = val;
444 break;
445 default:
446 goto bad_reg;
447 }
448 break;
449 case 14: /* Reserved. */
450 goto bad_reg;
451 case 15: /* Implementation specific. */
452 /* ??? Internal registers not implemented. */
453 break;
454 }
455 return;
456bad_reg:
457 /* ??? For debugging only. Should raise illegal instruction exception. */
458 cpu_abort(env, "Unimplemented cp15 register read\n");
459}
460
461uint32_t helper_get_cp15(CPUState *env, uint32_t insn)
462{
463 uint32_t op2;
464
465 op2 = (insn >> 5) & 7;
466 switch ((insn >> 16) & 0xf) {
467 case 0: /* ID codes. */
468 switch (op2) {
469 default: /* Device ID. */
470 return 0x4106a262;
471 case 1: /* Cache Type. */
472 return 0x1dd20d2;
473 case 2: /* TCM status. */
474 return 0;
475 }
476 case 1: /* System configuration. */
477 switch (op2) {
478 case 0: /* Control register. */
479 return env->cp15.c1_sys;
480 case 1: /* Auxiliary control register. */
481 return 1;
482 case 2: /* Coprocessor access register. */
483 return env->cp15.c1_coproc;
484 default:
485 goto bad_reg;
486 }
487 case 2: /* MMU Page table control. */
488 return env->cp15.c2;
489 case 3: /* MMU Domain access control. */
490 return env->cp15.c3;
491 case 4: /* Reserved. */
492 goto bad_reg;
493 case 5: /* MMU Fault status. */
494 switch (op2) {
495 case 0:
496 return env->cp15.c5_data;
497 case 1:
498 return env->cp15.c5_insn;
499 default:
500 goto bad_reg;
501 }
502 case 6: /* MMU Fault address. */
503 switch (op2) {
504 case 0:
505 return env->cp15.c6_data;
506 case 1:
507 return env->cp15.c6_insn;
508 default:
509 goto bad_reg;
510 }
511 case 7: /* Cache control. */
512 /* ??? This is for test, clean and invaidate operations that set the
513 Z flag. We can't represent N = Z = 1, so it also clears clears
514 the N flag. Oh well. */
515 env->NZF = 0;
516 return 0;
517 case 8: /* MMU TLB control. */
518 goto bad_reg;
519 case 9: /* Cache lockdown. */
520 switch (op2) {
521 case 0:
522 return env->cp15.c9_data;
523 case 1:
524 return env->cp15.c9_insn;
525 default:
526 goto bad_reg;
527 }
528 case 10: /* MMU TLB lockdown. */
529 /* ??? TLB lockdown not implemented. */
530 return 0;
531 case 11: /* TCM DMA control. */
532 case 12: /* Reserved. */
533 goto bad_reg;
534 case 13: /* Process ID. */
535 switch (op2) {
536 case 0:
537 return env->cp15.c13_fcse;
538 case 1:
539 return env->cp15.c13_context;
540 default:
541 goto bad_reg;
542 }
543 case 14: /* Reserved. */
544 goto bad_reg;
545 case 15: /* Implementation specific. */
546 /* ??? Internal registers not implemented. */
547 return 0;
548 }
549bad_reg:
550 /* ??? For debugging only. Should raise illegal instruction exception. */
551 cpu_abort(env, "Unimplemented cp15 register read\n");
552 return 0;
553}
554
555#endif