]>
Commit | Line | Data |
---|---|---|
b5ff1b31 FB |
1 | #include <stdio.h> |
2 | #include <stdlib.h> | |
3 | #include <string.h> | |
4 | ||
5 | #include "cpu.h" | |
6 | #include "exec-all.h" | |
7 | ||
f3d6b95e PB |
8 | static inline void set_feature(CPUARMState *env, int feature) |
9 | { | |
10 | env->features |= 1u << feature; | |
11 | } | |
12 | ||
13 | static void cpu_reset_model_id(CPUARMState *env, uint32_t id) | |
14 | { | |
15 | env->cp15.c0_cpuid = id; | |
16 | switch (id) { | |
17 | case ARM_CPUID_ARM926: | |
18 | set_feature(env, ARM_FEATURE_VFP); | |
19 | env->vfp.xregs[ARM_VFP_FPSID] = 0x41011090; | |
c1713132 | 20 | env->cp15.c0_cachetype = 0x1dd20d2; |
610c3c8a | 21 | env->cp15.c1_sys = 0x00090078; |
f3d6b95e | 22 | break; |
ce819861 PB |
23 | case ARM_CPUID_ARM946: |
24 | set_feature(env, ARM_FEATURE_MPU); | |
25 | env->cp15.c0_cachetype = 0x0f004006; | |
610c3c8a | 26 | env->cp15.c1_sys = 0x00000078; |
ce819861 | 27 | break; |
f3d6b95e PB |
28 | case ARM_CPUID_ARM1026: |
29 | set_feature(env, ARM_FEATURE_VFP); | |
30 | set_feature(env, ARM_FEATURE_AUXCR); | |
31 | env->vfp.xregs[ARM_VFP_FPSID] = 0x410110a0; | |
c1713132 | 32 | env->cp15.c0_cachetype = 0x1dd20d2; |
610c3c8a | 33 | env->cp15.c1_sys = 0x00090078; |
c1713132 | 34 | break; |
c3d2689d AZ |
35 | case ARM_CPUID_TI915T: |
36 | case ARM_CPUID_TI925T: | |
37 | set_feature(env, ARM_FEATURE_OMAPCP); | |
38 | env->cp15.c0_cpuid = ARM_CPUID_TI925T; /* Depends on wiring. */ | |
39 | env->cp15.c0_cachetype = 0x5109149; | |
40 | env->cp15.c1_sys = 0x00000070; | |
41 | env->cp15.c15_i_max = 0x000; | |
42 | env->cp15.c15_i_min = 0xff0; | |
43 | break; | |
c1713132 AZ |
44 | case ARM_CPUID_PXA250: |
45 | case ARM_CPUID_PXA255: | |
46 | case ARM_CPUID_PXA260: | |
47 | case ARM_CPUID_PXA261: | |
48 | case ARM_CPUID_PXA262: | |
49 | set_feature(env, ARM_FEATURE_XSCALE); | |
50 | /* JTAG_ID is ((id << 28) | 0x09265013) */ | |
51 | env->cp15.c0_cachetype = 0xd172172; | |
610c3c8a | 52 | env->cp15.c1_sys = 0x00000078; |
c1713132 AZ |
53 | break; |
54 | case ARM_CPUID_PXA270_A0: | |
55 | case ARM_CPUID_PXA270_A1: | |
56 | case ARM_CPUID_PXA270_B0: | |
57 | case ARM_CPUID_PXA270_B1: | |
58 | case ARM_CPUID_PXA270_C0: | |
59 | case ARM_CPUID_PXA270_C5: | |
60 | set_feature(env, ARM_FEATURE_XSCALE); | |
61 | /* JTAG_ID is ((id << 28) | 0x09265013) */ | |
18c9b560 AZ |
62 | set_feature(env, ARM_FEATURE_IWMMXT); |
63 | env->iwmmxt.cregs[ARM_IWMMXT_wCID] = 0x69051000 | 'Q'; | |
c1713132 | 64 | env->cp15.c0_cachetype = 0xd172172; |
610c3c8a | 65 | env->cp15.c1_sys = 0x00000078; |
f3d6b95e PB |
66 | break; |
67 | default: | |
68 | cpu_abort(env, "Bad CPU ID: %x\n", id); | |
69 | break; | |
70 | } | |
71 | } | |
72 | ||
40f137e1 PB |
73 | void cpu_reset(CPUARMState *env) |
74 | { | |
f3d6b95e PB |
75 | uint32_t id; |
76 | id = env->cp15.c0_cpuid; | |
77 | memset(env, 0, offsetof(CPUARMState, breakpoints)); | |
78 | if (id) | |
79 | cpu_reset_model_id(env, id); | |
40f137e1 PB |
80 | #if defined (CONFIG_USER_ONLY) |
81 | env->uncached_cpsr = ARM_CPU_MODE_USR; | |
82 | env->vfp.xregs[ARM_VFP_FPEXC] = 1 << 30; | |
83 | #else | |
84 | /* SVC mode with interrupts disabled. */ | |
85 | env->uncached_cpsr = ARM_CPU_MODE_SVC | CPSR_A | CPSR_F | CPSR_I; | |
86 | env->vfp.xregs[ARM_VFP_FPEXC] = 0; | |
87 | #endif | |
88 | env->regs[15] = 0; | |
f3d6b95e | 89 | tlb_flush(env, 1); |
40f137e1 PB |
90 | } |
91 | ||
92 | CPUARMState *cpu_arm_init(void) | |
93 | { | |
94 | CPUARMState *env; | |
95 | ||
96 | env = qemu_mallocz(sizeof(CPUARMState)); | |
97 | if (!env) | |
98 | return NULL; | |
99 | cpu_exec_init(env); | |
100 | cpu_reset(env); | |
40f137e1 PB |
101 | return env; |
102 | } | |
103 | ||
3371d272 PB |
104 | struct arm_cpu_t { |
105 | uint32_t id; | |
106 | const char *name; | |
107 | }; | |
108 | ||
109 | static const struct arm_cpu_t arm_cpu_names[] = { | |
110 | { ARM_CPUID_ARM926, "arm926"}, | |
ce819861 | 111 | { ARM_CPUID_ARM946, "arm946"}, |
3371d272 | 112 | { ARM_CPUID_ARM1026, "arm1026"}, |
c3d2689d | 113 | { ARM_CPUID_TI925T, "ti925t" }, |
c1713132 AZ |
114 | { ARM_CPUID_PXA250, "pxa250" }, |
115 | { ARM_CPUID_PXA255, "pxa255" }, | |
116 | { ARM_CPUID_PXA260, "pxa260" }, | |
117 | { ARM_CPUID_PXA261, "pxa261" }, | |
118 | { ARM_CPUID_PXA262, "pxa262" }, | |
119 | { ARM_CPUID_PXA270, "pxa270" }, | |
120 | { ARM_CPUID_PXA270_A0, "pxa270-a0" }, | |
121 | { ARM_CPUID_PXA270_A1, "pxa270-a1" }, | |
122 | { ARM_CPUID_PXA270_B0, "pxa270-b0" }, | |
123 | { ARM_CPUID_PXA270_B1, "pxa270-b1" }, | |
124 | { ARM_CPUID_PXA270_C0, "pxa270-c0" }, | |
125 | { ARM_CPUID_PXA270_C5, "pxa270-c5" }, | |
3371d272 PB |
126 | { 0, NULL} |
127 | }; | |
128 | ||
c732abe2 | 129 | void arm_cpu_list(FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...)) |
5adb4839 PB |
130 | { |
131 | int i; | |
132 | ||
c732abe2 | 133 | (*cpu_fprintf)(f, "Available CPUs:\n"); |
5adb4839 | 134 | for (i = 0; arm_cpu_names[i].name; i++) { |
c732abe2 | 135 | (*cpu_fprintf)(f, " %s\n", arm_cpu_names[i].name); |
5adb4839 PB |
136 | } |
137 | } | |
138 | ||
3371d272 | 139 | void cpu_arm_set_model(CPUARMState *env, const char *name) |
40f137e1 | 140 | { |
3371d272 PB |
141 | int i; |
142 | uint32_t id; | |
143 | ||
144 | id = 0; | |
145 | i = 0; | |
146 | for (i = 0; arm_cpu_names[i].name; i++) { | |
147 | if (strcmp(name, arm_cpu_names[i].name) == 0) { | |
148 | id = arm_cpu_names[i].id; | |
149 | break; | |
150 | } | |
151 | } | |
152 | if (!id) { | |
153 | cpu_abort(env, "Unknown CPU '%s'", name); | |
154 | return; | |
155 | } | |
f3d6b95e | 156 | cpu_reset_model_id(env, id); |
40f137e1 PB |
157 | } |
158 | ||
159 | void cpu_arm_close(CPUARMState *env) | |
160 | { | |
161 | free(env); | |
162 | } | |
163 | ||
5fafdf24 | 164 | #if defined(CONFIG_USER_ONLY) |
b5ff1b31 FB |
165 | |
166 | void do_interrupt (CPUState *env) | |
167 | { | |
168 | env->exception_index = -1; | |
169 | } | |
170 | ||
171 | int cpu_arm_handle_mmu_fault (CPUState *env, target_ulong address, int rw, | |
172 | int is_user, int is_softmmu) | |
173 | { | |
174 | if (rw == 2) { | |
175 | env->exception_index = EXCP_PREFETCH_ABORT; | |
176 | env->cp15.c6_insn = address; | |
177 | } else { | |
178 | env->exception_index = EXCP_DATA_ABORT; | |
179 | env->cp15.c6_data = address; | |
180 | } | |
181 | return 1; | |
182 | } | |
183 | ||
9b3c35e0 | 184 | target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr) |
b5ff1b31 FB |
185 | { |
186 | return addr; | |
187 | } | |
188 | ||
189 | /* These should probably raise undefined insn exceptions. */ | |
c1713132 AZ |
190 | void helper_set_cp(CPUState *env, uint32_t insn, uint32_t val) |
191 | { | |
192 | int op1 = (insn >> 8) & 0xf; | |
193 | cpu_abort(env, "cp%i insn %08x\n", op1, insn); | |
194 | return; | |
195 | } | |
196 | ||
197 | uint32_t helper_get_cp(CPUState *env, uint32_t insn) | |
198 | { | |
199 | int op1 = (insn >> 8) & 0xf; | |
200 | cpu_abort(env, "cp%i insn %08x\n", op1, insn); | |
201 | return 0; | |
202 | } | |
203 | ||
b5ff1b31 FB |
204 | void helper_set_cp15(CPUState *env, uint32_t insn, uint32_t val) |
205 | { | |
206 | cpu_abort(env, "cp15 insn %08x\n", insn); | |
207 | } | |
208 | ||
209 | uint32_t helper_get_cp15(CPUState *env, uint32_t insn) | |
210 | { | |
211 | cpu_abort(env, "cp15 insn %08x\n", insn); | |
212 | return 0; | |
213 | } | |
214 | ||
215 | void switch_mode(CPUState *env, int mode) | |
216 | { | |
217 | if (mode != ARM_CPU_MODE_USR) | |
218 | cpu_abort(env, "Tried to switch out of user mode\n"); | |
219 | } | |
220 | ||
221 | #else | |
222 | ||
8e71621f PB |
223 | extern int semihosting_enabled; |
224 | ||
b5ff1b31 FB |
225 | /* Map CPU modes onto saved register banks. */ |
226 | static inline int bank_number (int mode) | |
227 | { | |
228 | switch (mode) { | |
229 | case ARM_CPU_MODE_USR: | |
230 | case ARM_CPU_MODE_SYS: | |
231 | return 0; | |
232 | case ARM_CPU_MODE_SVC: | |
233 | return 1; | |
234 | case ARM_CPU_MODE_ABT: | |
235 | return 2; | |
236 | case ARM_CPU_MODE_UND: | |
237 | return 3; | |
238 | case ARM_CPU_MODE_IRQ: | |
239 | return 4; | |
240 | case ARM_CPU_MODE_FIQ: | |
241 | return 5; | |
242 | } | |
243 | cpu_abort(cpu_single_env, "Bad mode %x\n", mode); | |
244 | return -1; | |
245 | } | |
246 | ||
247 | void switch_mode(CPUState *env, int mode) | |
248 | { | |
249 | int old_mode; | |
250 | int i; | |
251 | ||
252 | old_mode = env->uncached_cpsr & CPSR_M; | |
253 | if (mode == old_mode) | |
254 | return; | |
255 | ||
256 | if (old_mode == ARM_CPU_MODE_FIQ) { | |
257 | memcpy (env->fiq_regs, env->regs + 8, 5 * sizeof(uint32_t)); | |
8637c67f | 258 | memcpy (env->regs + 8, env->usr_regs, 5 * sizeof(uint32_t)); |
b5ff1b31 FB |
259 | } else if (mode == ARM_CPU_MODE_FIQ) { |
260 | memcpy (env->usr_regs, env->regs + 8, 5 * sizeof(uint32_t)); | |
8637c67f | 261 | memcpy (env->regs + 8, env->fiq_regs, 5 * sizeof(uint32_t)); |
b5ff1b31 FB |
262 | } |
263 | ||
264 | i = bank_number(old_mode); | |
265 | env->banked_r13[i] = env->regs[13]; | |
266 | env->banked_r14[i] = env->regs[14]; | |
267 | env->banked_spsr[i] = env->spsr; | |
268 | ||
269 | i = bank_number(mode); | |
270 | env->regs[13] = env->banked_r13[i]; | |
271 | env->regs[14] = env->banked_r14[i]; | |
272 | env->spsr = env->banked_spsr[i]; | |
273 | } | |
274 | ||
275 | /* Handle a CPU exception. */ | |
276 | void do_interrupt(CPUARMState *env) | |
277 | { | |
278 | uint32_t addr; | |
279 | uint32_t mask; | |
280 | int new_mode; | |
281 | uint32_t offset; | |
282 | ||
283 | /* TODO: Vectored interrupt controller. */ | |
284 | switch (env->exception_index) { | |
285 | case EXCP_UDEF: | |
286 | new_mode = ARM_CPU_MODE_UND; | |
287 | addr = 0x04; | |
288 | mask = CPSR_I; | |
289 | if (env->thumb) | |
290 | offset = 2; | |
291 | else | |
292 | offset = 4; | |
293 | break; | |
294 | case EXCP_SWI: | |
8e71621f PB |
295 | if (semihosting_enabled) { |
296 | /* Check for semihosting interrupt. */ | |
297 | if (env->thumb) { | |
298 | mask = lduw_code(env->regs[15] - 2) & 0xff; | |
299 | } else { | |
300 | mask = ldl_code(env->regs[15] - 4) & 0xffffff; | |
301 | } | |
302 | /* Only intercept calls from privileged modes, to provide some | |
303 | semblance of security. */ | |
304 | if (((mask == 0x123456 && !env->thumb) | |
305 | || (mask == 0xab && env->thumb)) | |
306 | && (env->uncached_cpsr & CPSR_M) != ARM_CPU_MODE_USR) { | |
307 | env->regs[0] = do_arm_semihosting(env); | |
308 | return; | |
309 | } | |
310 | } | |
b5ff1b31 FB |
311 | new_mode = ARM_CPU_MODE_SVC; |
312 | addr = 0x08; | |
313 | mask = CPSR_I; | |
314 | /* The PC already points to the next instructon. */ | |
315 | offset = 0; | |
316 | break; | |
317 | case EXCP_PREFETCH_ABORT: | |
06c949e6 | 318 | case EXCP_BKPT: |
b5ff1b31 FB |
319 | new_mode = ARM_CPU_MODE_ABT; |
320 | addr = 0x0c; | |
321 | mask = CPSR_A | CPSR_I; | |
322 | offset = 4; | |
323 | break; | |
324 | case EXCP_DATA_ABORT: | |
325 | new_mode = ARM_CPU_MODE_ABT; | |
326 | addr = 0x10; | |
327 | mask = CPSR_A | CPSR_I; | |
328 | offset = 8; | |
329 | break; | |
330 | case EXCP_IRQ: | |
331 | new_mode = ARM_CPU_MODE_IRQ; | |
332 | addr = 0x18; | |
333 | /* Disable IRQ and imprecise data aborts. */ | |
334 | mask = CPSR_A | CPSR_I; | |
335 | offset = 4; | |
336 | break; | |
337 | case EXCP_FIQ: | |
338 | new_mode = ARM_CPU_MODE_FIQ; | |
339 | addr = 0x1c; | |
340 | /* Disable FIQ, IRQ and imprecise data aborts. */ | |
341 | mask = CPSR_A | CPSR_I | CPSR_F; | |
342 | offset = 4; | |
343 | break; | |
344 | default: | |
345 | cpu_abort(env, "Unhandled exception 0x%x\n", env->exception_index); | |
346 | return; /* Never happens. Keep compiler happy. */ | |
347 | } | |
348 | /* High vectors. */ | |
349 | if (env->cp15.c1_sys & (1 << 13)) { | |
350 | addr += 0xffff0000; | |
351 | } | |
352 | switch_mode (env, new_mode); | |
353 | env->spsr = cpsr_read(env); | |
6d7e6326 | 354 | /* Switch to the new mode, and switch to Arm mode. */ |
b5ff1b31 | 355 | /* ??? Thumb interrupt handlers not implemented. */ |
6d7e6326 | 356 | env->uncached_cpsr = (env->uncached_cpsr & ~CPSR_M) | new_mode; |
b5ff1b31 | 357 | env->uncached_cpsr |= mask; |
6d7e6326 | 358 | env->thumb = 0; |
b5ff1b31 FB |
359 | env->regs[14] = env->regs[15] + offset; |
360 | env->regs[15] = addr; | |
361 | env->interrupt_request |= CPU_INTERRUPT_EXITTB; | |
362 | } | |
363 | ||
364 | /* Check section/page access permissions. | |
365 | Returns the page protection flags, or zero if the access is not | |
366 | permitted. */ | |
367 | static inline int check_ap(CPUState *env, int ap, int domain, int access_type, | |
368 | int is_user) | |
369 | { | |
370 | if (domain == 3) | |
371 | return PAGE_READ | PAGE_WRITE; | |
372 | ||
373 | switch (ap) { | |
374 | case 0: | |
78600320 | 375 | if (access_type == 1) |
b5ff1b31 FB |
376 | return 0; |
377 | switch ((env->cp15.c1_sys >> 8) & 3) { | |
378 | case 1: | |
379 | return is_user ? 0 : PAGE_READ; | |
380 | case 2: | |
381 | return PAGE_READ; | |
382 | default: | |
383 | return 0; | |
384 | } | |
385 | case 1: | |
386 | return is_user ? 0 : PAGE_READ | PAGE_WRITE; | |
387 | case 2: | |
388 | if (is_user) | |
389 | return (access_type == 1) ? 0 : PAGE_READ; | |
390 | else | |
391 | return PAGE_READ | PAGE_WRITE; | |
392 | case 3: | |
393 | return PAGE_READ | PAGE_WRITE; | |
394 | default: | |
395 | abort(); | |
396 | } | |
397 | } | |
398 | ||
399 | static int get_phys_addr(CPUState *env, uint32_t address, int access_type, | |
400 | int is_user, uint32_t *phys_ptr, int *prot) | |
401 | { | |
402 | int code; | |
403 | uint32_t table; | |
404 | uint32_t desc; | |
405 | int type; | |
406 | int ap; | |
407 | int domain; | |
408 | uint32_t phys_addr; | |
409 | ||
410 | /* Fast Context Switch Extension. */ | |
411 | if (address < 0x02000000) | |
412 | address += env->cp15.c13_fcse; | |
413 | ||
414 | if ((env->cp15.c1_sys & 1) == 0) { | |
ce819861 | 415 | /* MMU/MPU disabled. */ |
b5ff1b31 FB |
416 | *phys_ptr = address; |
417 | *prot = PAGE_READ | PAGE_WRITE; | |
ce819861 PB |
418 | } else if (arm_feature(env, ARM_FEATURE_MPU)) { |
419 | int n; | |
420 | uint32_t mask; | |
421 | uint32_t base; | |
422 | ||
423 | *phys_ptr = address; | |
424 | for (n = 7; n >= 0; n--) { | |
425 | base = env->cp15.c6_region[n]; | |
426 | if ((base & 1) == 0) | |
427 | continue; | |
428 | mask = 1 << ((base >> 1) & 0x1f); | |
429 | /* Keep this shift separate from the above to avoid an | |
430 | (undefined) << 32. */ | |
431 | mask = (mask << 1) - 1; | |
432 | if (((base ^ address) & ~mask) == 0) | |
433 | break; | |
434 | } | |
435 | if (n < 0) | |
436 | return 2; | |
437 | ||
438 | if (access_type == 2) { | |
439 | mask = env->cp15.c5_insn; | |
440 | } else { | |
441 | mask = env->cp15.c5_data; | |
442 | } | |
443 | mask = (mask >> (n * 4)) & 0xf; | |
444 | switch (mask) { | |
445 | case 0: | |
446 | return 1; | |
447 | case 1: | |
448 | if (is_user) | |
449 | return 1; | |
450 | *prot = PAGE_READ | PAGE_WRITE; | |
451 | break; | |
452 | case 2: | |
453 | *prot = PAGE_READ; | |
454 | if (!is_user) | |
455 | *prot |= PAGE_WRITE; | |
456 | break; | |
457 | case 3: | |
458 | *prot = PAGE_READ | PAGE_WRITE; | |
459 | break; | |
460 | case 5: | |
461 | if (is_user) | |
462 | return 1; | |
463 | *prot = PAGE_READ; | |
464 | break; | |
465 | case 6: | |
466 | *prot = PAGE_READ; | |
467 | break; | |
468 | default: | |
469 | /* Bad permission. */ | |
470 | return 1; | |
471 | } | |
b5ff1b31 FB |
472 | } else { |
473 | /* Pagetable walk. */ | |
474 | /* Lookup l1 descriptor. */ | |
ce819861 | 475 | table = (env->cp15.c2_base & 0xffffc000) | ((address >> 18) & 0x3ffc); |
b5ff1b31 FB |
476 | desc = ldl_phys(table); |
477 | type = (desc & 3); | |
478 | domain = (env->cp15.c3 >> ((desc >> 4) & 0x1e)) & 3; | |
479 | if (type == 0) { | |
480 | /* Secton translation fault. */ | |
481 | code = 5; | |
482 | goto do_fault; | |
483 | } | |
484 | if (domain == 0 || domain == 2) { | |
485 | if (type == 2) | |
486 | code = 9; /* Section domain fault. */ | |
487 | else | |
488 | code = 11; /* Page domain fault. */ | |
489 | goto do_fault; | |
490 | } | |
491 | if (type == 2) { | |
492 | /* 1Mb section. */ | |
493 | phys_addr = (desc & 0xfff00000) | (address & 0x000fffff); | |
494 | ap = (desc >> 10) & 3; | |
495 | code = 13; | |
496 | } else { | |
497 | /* Lookup l2 entry. */ | |
c73c3aa0 PB |
498 | if (type == 1) { |
499 | /* Coarse pagetable. */ | |
500 | table = (desc & 0xfffffc00) | ((address >> 10) & 0x3fc); | |
501 | } else { | |
502 | /* Fine pagetable. */ | |
503 | table = (desc & 0xfffff000) | ((address >> 8) & 0xffc); | |
504 | } | |
b5ff1b31 FB |
505 | desc = ldl_phys(table); |
506 | switch (desc & 3) { | |
507 | case 0: /* Page translation fault. */ | |
508 | code = 7; | |
509 | goto do_fault; | |
510 | case 1: /* 64k page. */ | |
511 | phys_addr = (desc & 0xffff0000) | (address & 0xffff); | |
512 | ap = (desc >> (4 + ((address >> 13) & 6))) & 3; | |
513 | break; | |
514 | case 2: /* 4k page. */ | |
515 | phys_addr = (desc & 0xfffff000) | (address & 0xfff); | |
516 | ap = (desc >> (4 + ((address >> 13) & 6))) & 3; | |
517 | break; | |
518 | case 3: /* 1k page. */ | |
330c4d61 AZ |
519 | if (type == 1) { |
520 | if (arm_feature(env, ARM_FEATURE_XSCALE)) | |
521 | phys_addr = (desc & 0xfffff000) | (address & 0xfff); | |
522 | else { | |
c1713132 AZ |
523 | /* Page translation fault. */ |
524 | code = 7; | |
525 | goto do_fault; | |
526 | } | |
330c4d61 | 527 | } else |
c1713132 | 528 | phys_addr = (desc & 0xfffffc00) | (address & 0x3ff); |
b5ff1b31 FB |
529 | ap = (desc >> 4) & 3; |
530 | break; | |
531 | default: | |
532 | /* Never happens, but compiler isn't smart enough to tell. */ | |
533 | abort(); | |
534 | } | |
535 | code = 15; | |
536 | } | |
537 | *prot = check_ap(env, ap, domain, access_type, is_user); | |
538 | if (!*prot) { | |
539 | /* Access permission fault. */ | |
540 | goto do_fault; | |
541 | } | |
542 | *phys_ptr = phys_addr; | |
543 | } | |
544 | return 0; | |
545 | do_fault: | |
546 | return code | (domain << 4); | |
547 | } | |
548 | ||
549 | int cpu_arm_handle_mmu_fault (CPUState *env, target_ulong address, | |
550 | int access_type, int is_user, int is_softmmu) | |
551 | { | |
552 | uint32_t phys_addr; | |
553 | int prot; | |
554 | int ret; | |
555 | ||
556 | ret = get_phys_addr(env, address, access_type, is_user, &phys_addr, &prot); | |
557 | if (ret == 0) { | |
558 | /* Map a single [sub]page. */ | |
559 | phys_addr &= ~(uint32_t)0x3ff; | |
560 | address &= ~(uint32_t)0x3ff; | |
561 | return tlb_set_page (env, address, phys_addr, prot, is_user, | |
562 | is_softmmu); | |
563 | } | |
564 | ||
565 | if (access_type == 2) { | |
566 | env->cp15.c5_insn = ret; | |
567 | env->cp15.c6_insn = address; | |
568 | env->exception_index = EXCP_PREFETCH_ABORT; | |
569 | } else { | |
570 | env->cp15.c5_data = ret; | |
571 | env->cp15.c6_data = address; | |
572 | env->exception_index = EXCP_DATA_ABORT; | |
573 | } | |
574 | return 1; | |
575 | } | |
576 | ||
9b3c35e0 | 577 | target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr) |
b5ff1b31 FB |
578 | { |
579 | uint32_t phys_addr; | |
580 | int prot; | |
581 | int ret; | |
582 | ||
583 | ret = get_phys_addr(env, addr, 0, 0, &phys_addr, &prot); | |
584 | ||
585 | if (ret != 0) | |
586 | return -1; | |
587 | ||
588 | return phys_addr; | |
589 | } | |
590 | ||
c1713132 AZ |
591 | void helper_set_cp(CPUState *env, uint32_t insn, uint32_t val) |
592 | { | |
593 | int cp_num = (insn >> 8) & 0xf; | |
594 | int cp_info = (insn >> 5) & 7; | |
595 | int src = (insn >> 16) & 0xf; | |
596 | int operand = insn & 0xf; | |
597 | ||
598 | if (env->cp[cp_num].cp_write) | |
599 | env->cp[cp_num].cp_write(env->cp[cp_num].opaque, | |
600 | cp_info, src, operand, val); | |
601 | } | |
602 | ||
603 | uint32_t helper_get_cp(CPUState *env, uint32_t insn) | |
604 | { | |
605 | int cp_num = (insn >> 8) & 0xf; | |
606 | int cp_info = (insn >> 5) & 7; | |
607 | int dest = (insn >> 16) & 0xf; | |
608 | int operand = insn & 0xf; | |
609 | ||
610 | if (env->cp[cp_num].cp_read) | |
611 | return env->cp[cp_num].cp_read(env->cp[cp_num].opaque, | |
612 | cp_info, dest, operand); | |
613 | return 0; | |
614 | } | |
615 | ||
ce819861 PB |
616 | /* Return basic MPU access permission bits. */ |
617 | static uint32_t simple_mpu_ap_bits(uint32_t val) | |
618 | { | |
619 | uint32_t ret; | |
620 | uint32_t mask; | |
621 | int i; | |
622 | ret = 0; | |
623 | mask = 3; | |
624 | for (i = 0; i < 16; i += 2) { | |
625 | ret |= (val >> i) & mask; | |
626 | mask <<= 2; | |
627 | } | |
628 | return ret; | |
629 | } | |
630 | ||
631 | /* Pad basic MPU access permission bits to extended format. */ | |
632 | static uint32_t extended_mpu_ap_bits(uint32_t val) | |
633 | { | |
634 | uint32_t ret; | |
635 | uint32_t mask; | |
636 | int i; | |
637 | ret = 0; | |
638 | mask = 3; | |
639 | for (i = 0; i < 16; i += 2) { | |
640 | ret |= (val & mask) << i; | |
641 | mask <<= 2; | |
642 | } | |
643 | return ret; | |
644 | } | |
645 | ||
b5ff1b31 FB |
646 | void helper_set_cp15(CPUState *env, uint32_t insn, uint32_t val) |
647 | { | |
648 | uint32_t op2; | |
ce819861 | 649 | uint32_t crm; |
b5ff1b31 FB |
650 | |
651 | op2 = (insn >> 5) & 7; | |
ce819861 | 652 | crm = insn & 0xf; |
b5ff1b31 FB |
653 | switch ((insn >> 16) & 0xf) { |
654 | case 0: /* ID codes. */ | |
610c3c8a AZ |
655 | if (arm_feature(env, ARM_FEATURE_XSCALE)) |
656 | break; | |
c3d2689d AZ |
657 | if (arm_feature(env, ARM_FEATURE_OMAPCP)) |
658 | break; | |
b5ff1b31 FB |
659 | goto bad_reg; |
660 | case 1: /* System configuration. */ | |
c3d2689d AZ |
661 | if (arm_feature(env, ARM_FEATURE_OMAPCP)) |
662 | op2 = 0; | |
b5ff1b31 FB |
663 | switch (op2) { |
664 | case 0: | |
ce819861 | 665 | if (!arm_feature(env, ARM_FEATURE_XSCALE) || crm == 0) |
c1713132 | 666 | env->cp15.c1_sys = val; |
b5ff1b31 FB |
667 | /* ??? Lots of these bits are not implemented. */ |
668 | /* This may enable/disable the MMU, so do a TLB flush. */ | |
669 | tlb_flush(env, 1); | |
670 | break; | |
c1713132 | 671 | case 1: |
610c3c8a AZ |
672 | if (arm_feature(env, ARM_FEATURE_XSCALE)) { |
673 | env->cp15.c1_xscaleauxcr = val; | |
c1713132 | 674 | break; |
610c3c8a | 675 | } |
c1713132 | 676 | goto bad_reg; |
b5ff1b31 | 677 | case 2: |
610c3c8a AZ |
678 | if (arm_feature(env, ARM_FEATURE_XSCALE)) |
679 | goto bad_reg; | |
b5ff1b31 FB |
680 | env->cp15.c1_coproc = val; |
681 | /* ??? Is this safe when called from within a TB? */ | |
682 | tb_flush(env); | |
c1713132 | 683 | break; |
b5ff1b31 FB |
684 | default: |
685 | goto bad_reg; | |
686 | } | |
687 | break; | |
ce819861 PB |
688 | case 2: /* MMU Page table control / MPU cache control. */ |
689 | if (arm_feature(env, ARM_FEATURE_MPU)) { | |
690 | switch (op2) { | |
691 | case 0: | |
692 | env->cp15.c2_data = val; | |
693 | break; | |
694 | case 1: | |
695 | env->cp15.c2_insn = val; | |
696 | break; | |
697 | default: | |
698 | goto bad_reg; | |
699 | } | |
700 | } else { | |
701 | env->cp15.c2_base = val; | |
702 | } | |
b5ff1b31 | 703 | break; |
ce819861 | 704 | case 3: /* MMU Domain access control / MPU write buffer control. */ |
b5ff1b31 FB |
705 | env->cp15.c3 = val; |
706 | break; | |
707 | case 4: /* Reserved. */ | |
708 | goto bad_reg; | |
ce819861 | 709 | case 5: /* MMU Fault status / MPU access permission. */ |
c3d2689d AZ |
710 | if (arm_feature(env, ARM_FEATURE_OMAPCP)) |
711 | op2 = 0; | |
b5ff1b31 FB |
712 | switch (op2) { |
713 | case 0: | |
ce819861 PB |
714 | if (arm_feature(env, ARM_FEATURE_MPU)) |
715 | val = extended_mpu_ap_bits(val); | |
b5ff1b31 FB |
716 | env->cp15.c5_data = val; |
717 | break; | |
718 | case 1: | |
ce819861 PB |
719 | if (arm_feature(env, ARM_FEATURE_MPU)) |
720 | val = extended_mpu_ap_bits(val); | |
b5ff1b31 FB |
721 | env->cp15.c5_insn = val; |
722 | break; | |
ce819861 PB |
723 | case 2: |
724 | if (!arm_feature(env, ARM_FEATURE_MPU)) | |
725 | goto bad_reg; | |
726 | env->cp15.c5_data = val; | |
b5ff1b31 | 727 | break; |
ce819861 PB |
728 | case 3: |
729 | if (!arm_feature(env, ARM_FEATURE_MPU)) | |
730 | goto bad_reg; | |
731 | env->cp15.c5_insn = val; | |
b5ff1b31 FB |
732 | break; |
733 | default: | |
734 | goto bad_reg; | |
735 | } | |
736 | break; | |
ce819861 PB |
737 | case 6: /* MMU Fault address / MPU base/size. */ |
738 | if (arm_feature(env, ARM_FEATURE_MPU)) { | |
739 | if (crm >= 8) | |
740 | goto bad_reg; | |
741 | env->cp15.c6_region[crm] = val; | |
742 | } else { | |
c3d2689d AZ |
743 | if (arm_feature(env, ARM_FEATURE_OMAPCP)) |
744 | op2 = 0; | |
ce819861 PB |
745 | switch (op2) { |
746 | case 0: | |
747 | env->cp15.c6_data = val; | |
748 | break; | |
749 | case 1: | |
750 | env->cp15.c6_insn = val; | |
751 | break; | |
752 | default: | |
753 | goto bad_reg; | |
754 | } | |
755 | } | |
756 | break; | |
b5ff1b31 | 757 | case 7: /* Cache control. */ |
c3d2689d AZ |
758 | env->cp15.c15_i_max = 0x000; |
759 | env->cp15.c15_i_min = 0xff0; | |
b5ff1b31 FB |
760 | /* No cache, so nothing to do. */ |
761 | break; | |
762 | case 8: /* MMU TLB control. */ | |
763 | switch (op2) { | |
764 | case 0: /* Invalidate all. */ | |
765 | tlb_flush(env, 0); | |
766 | break; | |
767 | case 1: /* Invalidate single TLB entry. */ | |
768 | #if 0 | |
769 | /* ??? This is wrong for large pages and sections. */ | |
770 | /* As an ugly hack to make linux work we always flush a 4K | |
771 | pages. */ | |
772 | val &= 0xfffff000; | |
773 | tlb_flush_page(env, val); | |
774 | tlb_flush_page(env, val + 0x400); | |
775 | tlb_flush_page(env, val + 0x800); | |
776 | tlb_flush_page(env, val + 0xc00); | |
777 | #else | |
778 | tlb_flush(env, 1); | |
779 | #endif | |
780 | break; | |
781 | default: | |
782 | goto bad_reg; | |
783 | } | |
784 | break; | |
ce819861 | 785 | case 9: |
c3d2689d AZ |
786 | if (arm_feature(env, ARM_FEATURE_OMAPCP)) |
787 | break; | |
ce819861 PB |
788 | switch (crm) { |
789 | case 0: /* Cache lockdown. */ | |
790 | switch (op2) { | |
791 | case 0: | |
792 | env->cp15.c9_data = val; | |
793 | break; | |
794 | case 1: | |
795 | env->cp15.c9_insn = val; | |
796 | break; | |
797 | default: | |
798 | goto bad_reg; | |
799 | } | |
b5ff1b31 | 800 | break; |
ce819861 PB |
801 | case 1: /* TCM memory region registers. */ |
802 | /* Not implemented. */ | |
803 | goto bad_reg; | |
b5ff1b31 FB |
804 | default: |
805 | goto bad_reg; | |
806 | } | |
807 | break; | |
808 | case 10: /* MMU TLB lockdown. */ | |
809 | /* ??? TLB lockdown not implemented. */ | |
810 | break; | |
b5ff1b31 FB |
811 | case 12: /* Reserved. */ |
812 | goto bad_reg; | |
813 | case 13: /* Process ID. */ | |
814 | switch (op2) { | |
815 | case 0: | |
ce819861 PB |
816 | if (!arm_feature(env, ARM_FEATURE_MPU)) |
817 | goto bad_reg; | |
d07edbfa PB |
818 | /* Unlike real hardware the qemu TLB uses virtual addresses, |
819 | not modified virtual addresses, so this causes a TLB flush. | |
820 | */ | |
821 | if (env->cp15.c13_fcse != val) | |
822 | tlb_flush(env, 1); | |
823 | env->cp15.c13_fcse = val; | |
b5ff1b31 FB |
824 | break; |
825 | case 1: | |
d07edbfa | 826 | /* This changes the ASID, so do a TLB flush. */ |
ce819861 PB |
827 | if (env->cp15.c13_context != val |
828 | && !arm_feature(env, ARM_FEATURE_MPU)) | |
d07edbfa PB |
829 | tlb_flush(env, 0); |
830 | env->cp15.c13_context = val; | |
b5ff1b31 FB |
831 | break; |
832 | default: | |
833 | goto bad_reg; | |
834 | } | |
835 | break; | |
836 | case 14: /* Reserved. */ | |
837 | goto bad_reg; | |
838 | case 15: /* Implementation specific. */ | |
c1713132 | 839 | if (arm_feature(env, ARM_FEATURE_XSCALE)) { |
ce819861 | 840 | if (op2 == 0 && crm == 1) { |
2e23213f AZ |
841 | if (env->cp15.c15_cpar != (val & 0x3fff)) { |
842 | /* Changes cp0 to cp13 behavior, so needs a TB flush. */ | |
843 | tb_flush(env); | |
844 | env->cp15.c15_cpar = val & 0x3fff; | |
845 | } | |
c1713132 AZ |
846 | break; |
847 | } | |
848 | goto bad_reg; | |
849 | } | |
c3d2689d AZ |
850 | if (arm_feature(env, ARM_FEATURE_OMAPCP)) { |
851 | switch (crm) { | |
852 | case 0: | |
853 | break; | |
854 | case 1: /* Set TI925T configuration. */ | |
855 | env->cp15.c15_ticonfig = val & 0xe7; | |
856 | env->cp15.c0_cpuid = (val & (1 << 5)) ? /* OS_TYPE bit */ | |
857 | ARM_CPUID_TI915T : ARM_CPUID_TI925T; | |
858 | break; | |
859 | case 2: /* Set I_max. */ | |
860 | env->cp15.c15_i_max = val; | |
861 | break; | |
862 | case 3: /* Set I_min. */ | |
863 | env->cp15.c15_i_min = val; | |
864 | break; | |
865 | case 4: /* Set thread-ID. */ | |
866 | env->cp15.c15_threadid = val & 0xffff; | |
867 | break; | |
868 | case 8: /* Wait-for-interrupt (deprecated). */ | |
869 | cpu_interrupt(env, CPU_INTERRUPT_HALT); | |
870 | break; | |
871 | default: | |
872 | goto bad_reg; | |
873 | } | |
874 | } | |
b5ff1b31 FB |
875 | break; |
876 | } | |
877 | return; | |
878 | bad_reg: | |
879 | /* ??? For debugging only. Should raise illegal instruction exception. */ | |
c1713132 | 880 | cpu_abort(env, "Unimplemented cp15 register write\n"); |
b5ff1b31 FB |
881 | } |
882 | ||
883 | uint32_t helper_get_cp15(CPUState *env, uint32_t insn) | |
884 | { | |
885 | uint32_t op2; | |
c3d2689d | 886 | uint32_t crm; |
b5ff1b31 FB |
887 | |
888 | op2 = (insn >> 5) & 7; | |
c3d2689d | 889 | crm = insn & 0xf; |
b5ff1b31 FB |
890 | switch ((insn >> 16) & 0xf) { |
891 | case 0: /* ID codes. */ | |
892 | switch (op2) { | |
893 | default: /* Device ID. */ | |
40f137e1 | 894 | return env->cp15.c0_cpuid; |
b5ff1b31 | 895 | case 1: /* Cache Type. */ |
c1713132 | 896 | return env->cp15.c0_cachetype; |
b5ff1b31 | 897 | case 2: /* TCM status. */ |
610c3c8a AZ |
898 | if (arm_feature(env, ARM_FEATURE_XSCALE)) |
899 | goto bad_reg; | |
b5ff1b31 FB |
900 | return 0; |
901 | } | |
902 | case 1: /* System configuration. */ | |
c3d2689d AZ |
903 | if (arm_feature(env, ARM_FEATURE_OMAPCP)) |
904 | op2 = 0; | |
b5ff1b31 FB |
905 | switch (op2) { |
906 | case 0: /* Control register. */ | |
907 | return env->cp15.c1_sys; | |
908 | case 1: /* Auxiliary control register. */ | |
40f137e1 PB |
909 | if (arm_feature(env, ARM_FEATURE_AUXCR)) |
910 | return 1; | |
c1713132 | 911 | if (arm_feature(env, ARM_FEATURE_XSCALE)) |
610c3c8a | 912 | return env->cp15.c1_xscaleauxcr; |
40f137e1 | 913 | goto bad_reg; |
b5ff1b31 | 914 | case 2: /* Coprocessor access register. */ |
610c3c8a AZ |
915 | if (arm_feature(env, ARM_FEATURE_XSCALE)) |
916 | goto bad_reg; | |
b5ff1b31 FB |
917 | return env->cp15.c1_coproc; |
918 | default: | |
919 | goto bad_reg; | |
920 | } | |
ce819861 PB |
921 | case 2: /* MMU Page table control / MPU cache control. */ |
922 | if (arm_feature(env, ARM_FEATURE_MPU)) { | |
923 | switch (op2) { | |
924 | case 0: | |
925 | return env->cp15.c2_data; | |
926 | break; | |
927 | case 1: | |
928 | return env->cp15.c2_insn; | |
929 | break; | |
930 | default: | |
931 | goto bad_reg; | |
932 | } | |
933 | } else { | |
934 | return env->cp15.c2_base; | |
935 | } | |
936 | case 3: /* MMU Domain access control / MPU write buffer control. */ | |
b5ff1b31 FB |
937 | return env->cp15.c3; |
938 | case 4: /* Reserved. */ | |
939 | goto bad_reg; | |
ce819861 | 940 | case 5: /* MMU Fault status / MPU access permission. */ |
c3d2689d AZ |
941 | if (arm_feature(env, ARM_FEATURE_OMAPCP)) |
942 | op2 = 0; | |
b5ff1b31 FB |
943 | switch (op2) { |
944 | case 0: | |
ce819861 PB |
945 | if (arm_feature(env, ARM_FEATURE_MPU)) |
946 | return simple_mpu_ap_bits(env->cp15.c5_data); | |
b5ff1b31 FB |
947 | return env->cp15.c5_data; |
948 | case 1: | |
ce819861 PB |
949 | if (arm_feature(env, ARM_FEATURE_MPU)) |
950 | return simple_mpu_ap_bits(env->cp15.c5_data); | |
951 | return env->cp15.c5_insn; | |
952 | case 2: | |
953 | if (!arm_feature(env, ARM_FEATURE_MPU)) | |
954 | goto bad_reg; | |
955 | return env->cp15.c5_data; | |
956 | case 3: | |
957 | if (!arm_feature(env, ARM_FEATURE_MPU)) | |
958 | goto bad_reg; | |
b5ff1b31 FB |
959 | return env->cp15.c5_insn; |
960 | default: | |
961 | goto bad_reg; | |
962 | } | |
ce819861 PB |
963 | case 6: /* MMU Fault address / MPU base/size. */ |
964 | if (arm_feature(env, ARM_FEATURE_MPU)) { | |
965 | int n; | |
966 | n = (insn & 0xf); | |
967 | if (n >= 8) | |
968 | goto bad_reg; | |
969 | return env->cp15.c6_region[n]; | |
970 | } else { | |
c3d2689d AZ |
971 | if (arm_feature(env, ARM_FEATURE_OMAPCP)) |
972 | op2 = 0; | |
ce819861 PB |
973 | switch (op2) { |
974 | case 0: | |
975 | return env->cp15.c6_data; | |
976 | case 1: | |
977 | /* Arm9 doesn't have an IFAR, but implementing it anyway | |
978 | shouldn't do any harm. */ | |
979 | return env->cp15.c6_insn; | |
980 | default: | |
981 | goto bad_reg; | |
982 | } | |
b5ff1b31 FB |
983 | } |
984 | case 7: /* Cache control. */ | |
985 | /* ??? This is for test, clean and invaidate operations that set the | |
c1713132 | 986 | Z flag. We can't represent N = Z = 1, so it also clears |
b5ff1b31 FB |
987 | the N flag. Oh well. */ |
988 | env->NZF = 0; | |
989 | return 0; | |
990 | case 8: /* MMU TLB control. */ | |
991 | goto bad_reg; | |
992 | case 9: /* Cache lockdown. */ | |
c3d2689d AZ |
993 | if (arm_feature(env, ARM_FEATURE_OMAPCP)) |
994 | return 0; | |
b5ff1b31 FB |
995 | switch (op2) { |
996 | case 0: | |
997 | return env->cp15.c9_data; | |
998 | case 1: | |
999 | return env->cp15.c9_insn; | |
1000 | default: | |
1001 | goto bad_reg; | |
1002 | } | |
1003 | case 10: /* MMU TLB lockdown. */ | |
1004 | /* ??? TLB lockdown not implemented. */ | |
1005 | return 0; | |
1006 | case 11: /* TCM DMA control. */ | |
1007 | case 12: /* Reserved. */ | |
1008 | goto bad_reg; | |
1009 | case 13: /* Process ID. */ | |
1010 | switch (op2) { | |
1011 | case 0: | |
1012 | return env->cp15.c13_fcse; | |
1013 | case 1: | |
1014 | return env->cp15.c13_context; | |
1015 | default: | |
1016 | goto bad_reg; | |
1017 | } | |
1018 | case 14: /* Reserved. */ | |
1019 | goto bad_reg; | |
1020 | case 15: /* Implementation specific. */ | |
c1713132 | 1021 | if (arm_feature(env, ARM_FEATURE_XSCALE)) { |
c3d2689d | 1022 | if (op2 == 0 && crm == 1) |
c1713132 AZ |
1023 | return env->cp15.c15_cpar; |
1024 | ||
1025 | goto bad_reg; | |
1026 | } | |
c3d2689d AZ |
1027 | if (arm_feature(env, ARM_FEATURE_OMAPCP)) { |
1028 | switch (crm) { | |
1029 | case 0: | |
1030 | return 0; | |
1031 | case 1: /* Read TI925T configuration. */ | |
1032 | return env->cp15.c15_ticonfig; | |
1033 | case 2: /* Read I_max. */ | |
1034 | return env->cp15.c15_i_max; | |
1035 | case 3: /* Read I_min. */ | |
1036 | return env->cp15.c15_i_min; | |
1037 | case 4: /* Read thread-ID. */ | |
1038 | return env->cp15.c15_threadid; | |
1039 | case 8: /* TI925T_status */ | |
1040 | return 0; | |
1041 | } | |
1042 | goto bad_reg; | |
1043 | } | |
b5ff1b31 FB |
1044 | return 0; |
1045 | } | |
1046 | bad_reg: | |
1047 | /* ??? For debugging only. Should raise illegal instruction exception. */ | |
1048 | cpu_abort(env, "Unimplemented cp15 register read\n"); | |
1049 | return 0; | |
1050 | } | |
1051 | ||
c1713132 AZ |
1052 | void cpu_arm_set_cp_io(CPUARMState *env, int cpnum, |
1053 | ARMReadCPFunc *cp_read, ARMWriteCPFunc *cp_write, | |
1054 | void *opaque) | |
1055 | { | |
1056 | if (cpnum < 0 || cpnum > 14) { | |
1057 | cpu_abort(env, "Bad coprocessor number: %i\n", cpnum); | |
1058 | return; | |
1059 | } | |
1060 | ||
1061 | env->cp[cpnum].cp_read = cp_read; | |
1062 | env->cp[cpnum].cp_write = cp_write; | |
1063 | env->cp[cpnum].opaque = opaque; | |
1064 | } | |
1065 | ||
b5ff1b31 | 1066 | #endif |