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