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