|
|
1.1 root 1: #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:
10: void do_interrupt (CPUState *env)
11: {
12: env->exception_index = -1;
13: }
14:
15: int 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:
28: target_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. */
34: void helper_set_cp15(CPUState *env, uint32_t insn, uint32_t val)
35: {
36: cpu_abort(env, "cp15 insn %08x\n", insn);
37: }
38:
39: uint32_t helper_get_cp15(CPUState *env, uint32_t insn)
40: {
41: cpu_abort(env, "cp15 insn %08x\n", insn);
42: return 0;
43: }
44:
45: void 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. */
54: static 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:
75: void 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. */
104: void 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 switch to Arm mode. */
166: /* ??? Thumb interrupt handlers not implemented. */
167: env->uncached_cpsr = (env->uncached_cpsr & ~CPSR_M) | new_mode;
168: env->uncached_cpsr |= mask;
169: env->thumb = 0;
170: 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. */
178: static 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:
210: static 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;
292: do_fault:
293: return code | (domain << 4);
294: }
295:
296: int 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:
324: target_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:
338: void 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;
457: bad_reg:
458: /* ??? For debugging only. Should raise illegal instruction exception. */
459: cpu_abort(env, "Unimplemented cp15 register read\n");
460: }
461:
462: uint32_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: }
550: bad_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
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.