root / target-arm / helper.c @ 36bb244b
History | View | Annotate | Download (18.3 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 | f3d6b95e | pbrook | break;
|
21 | f3d6b95e | pbrook | case ARM_CPUID_ARM1026:
|
22 | f3d6b95e | pbrook | set_feature(env, ARM_FEATURE_VFP); |
23 | f3d6b95e | pbrook | set_feature(env, ARM_FEATURE_AUXCR); |
24 | f3d6b95e | pbrook | env->vfp.xregs[ARM_VFP_FPSID] = 0x410110a0;
|
25 | f3d6b95e | pbrook | break;
|
26 | f3d6b95e | pbrook | default:
|
27 | f3d6b95e | pbrook | cpu_abort(env, "Bad CPU ID: %x\n", id);
|
28 | f3d6b95e | pbrook | break;
|
29 | f3d6b95e | pbrook | } |
30 | f3d6b95e | pbrook | } |
31 | f3d6b95e | pbrook | |
32 | 40f137e1 | pbrook | void cpu_reset(CPUARMState *env)
|
33 | 40f137e1 | pbrook | { |
34 | f3d6b95e | pbrook | uint32_t id; |
35 | f3d6b95e | pbrook | id = env->cp15.c0_cpuid; |
36 | f3d6b95e | pbrook | memset(env, 0, offsetof(CPUARMState, breakpoints));
|
37 | f3d6b95e | pbrook | if (id)
|
38 | f3d6b95e | pbrook | cpu_reset_model_id(env, id); |
39 | 40f137e1 | pbrook | #if defined (CONFIG_USER_ONLY)
|
40 | 40f137e1 | pbrook | env->uncached_cpsr = ARM_CPU_MODE_USR; |
41 | 40f137e1 | pbrook | env->vfp.xregs[ARM_VFP_FPEXC] = 1 << 30; |
42 | 40f137e1 | pbrook | #else
|
43 | 40f137e1 | pbrook | /* SVC mode with interrupts disabled. */
|
44 | 40f137e1 | pbrook | env->uncached_cpsr = ARM_CPU_MODE_SVC | CPSR_A | CPSR_F | CPSR_I; |
45 | 40f137e1 | pbrook | env->vfp.xregs[ARM_VFP_FPEXC] = 0;
|
46 | 40f137e1 | pbrook | #endif
|
47 | 40f137e1 | pbrook | env->regs[15] = 0; |
48 | f3d6b95e | pbrook | tlb_flush(env, 1);
|
49 | 40f137e1 | pbrook | } |
50 | 40f137e1 | pbrook | |
51 | 40f137e1 | pbrook | CPUARMState *cpu_arm_init(void)
|
52 | 40f137e1 | pbrook | { |
53 | 40f137e1 | pbrook | CPUARMState *env; |
54 | 40f137e1 | pbrook | |
55 | 40f137e1 | pbrook | env = qemu_mallocz(sizeof(CPUARMState));
|
56 | 40f137e1 | pbrook | if (!env)
|
57 | 40f137e1 | pbrook | return NULL; |
58 | 40f137e1 | pbrook | cpu_exec_init(env); |
59 | 40f137e1 | pbrook | cpu_reset(env); |
60 | 40f137e1 | pbrook | return env;
|
61 | 40f137e1 | pbrook | } |
62 | 40f137e1 | pbrook | |
63 | 3371d272 | pbrook | struct arm_cpu_t {
|
64 | 3371d272 | pbrook | uint32_t id; |
65 | 3371d272 | pbrook | const char *name; |
66 | 3371d272 | pbrook | }; |
67 | 3371d272 | pbrook | |
68 | 3371d272 | pbrook | static const struct arm_cpu_t arm_cpu_names[] = { |
69 | 3371d272 | pbrook | { ARM_CPUID_ARM926, "arm926"},
|
70 | 3371d272 | pbrook | { ARM_CPUID_ARM1026, "arm1026"},
|
71 | 3371d272 | pbrook | { 0, NULL} |
72 | 3371d272 | pbrook | }; |
73 | 3371d272 | pbrook | |
74 | 5adb4839 | pbrook | void arm_cpu_list(void) |
75 | 5adb4839 | pbrook | { |
76 | 5adb4839 | pbrook | int i;
|
77 | 5adb4839 | pbrook | |
78 | 5adb4839 | pbrook | printf ("Available CPUs:\n");
|
79 | 5adb4839 | pbrook | for (i = 0; arm_cpu_names[i].name; i++) { |
80 | 5adb4839 | pbrook | printf(" %s\n", arm_cpu_names[i].name);
|
81 | 5adb4839 | pbrook | } |
82 | 5adb4839 | pbrook | } |
83 | 5adb4839 | pbrook | |
84 | 3371d272 | pbrook | void cpu_arm_set_model(CPUARMState *env, const char *name) |
85 | 40f137e1 | pbrook | { |
86 | 3371d272 | pbrook | int i;
|
87 | 3371d272 | pbrook | uint32_t id; |
88 | 3371d272 | pbrook | |
89 | 3371d272 | pbrook | id = 0;
|
90 | 3371d272 | pbrook | i = 0;
|
91 | 3371d272 | pbrook | for (i = 0; arm_cpu_names[i].name; i++) { |
92 | 3371d272 | pbrook | if (strcmp(name, arm_cpu_names[i].name) == 0) { |
93 | 3371d272 | pbrook | id = arm_cpu_names[i].id; |
94 | 3371d272 | pbrook | break;
|
95 | 3371d272 | pbrook | } |
96 | 3371d272 | pbrook | } |
97 | 3371d272 | pbrook | if (!id) {
|
98 | 3371d272 | pbrook | cpu_abort(env, "Unknown CPU '%s'", name);
|
99 | 3371d272 | pbrook | return;
|
100 | 3371d272 | pbrook | } |
101 | f3d6b95e | pbrook | cpu_reset_model_id(env, id); |
102 | 40f137e1 | pbrook | } |
103 | 40f137e1 | pbrook | |
104 | 40f137e1 | pbrook | void cpu_arm_close(CPUARMState *env)
|
105 | 40f137e1 | pbrook | { |
106 | 40f137e1 | pbrook | free(env); |
107 | 40f137e1 | pbrook | } |
108 | 40f137e1 | pbrook | |
109 | b5ff1b31 | bellard | #if defined(CONFIG_USER_ONLY)
|
110 | b5ff1b31 | bellard | |
111 | b5ff1b31 | bellard | void do_interrupt (CPUState *env)
|
112 | b5ff1b31 | bellard | { |
113 | b5ff1b31 | bellard | env->exception_index = -1;
|
114 | b5ff1b31 | bellard | } |
115 | b5ff1b31 | bellard | |
116 | b5ff1b31 | bellard | int cpu_arm_handle_mmu_fault (CPUState *env, target_ulong address, int rw, |
117 | b5ff1b31 | bellard | int is_user, int is_softmmu) |
118 | b5ff1b31 | bellard | { |
119 | b5ff1b31 | bellard | if (rw == 2) { |
120 | b5ff1b31 | bellard | env->exception_index = EXCP_PREFETCH_ABORT; |
121 | b5ff1b31 | bellard | env->cp15.c6_insn = address; |
122 | b5ff1b31 | bellard | } else {
|
123 | b5ff1b31 | bellard | env->exception_index = EXCP_DATA_ABORT; |
124 | b5ff1b31 | bellard | env->cp15.c6_data = address; |
125 | b5ff1b31 | bellard | } |
126 | b5ff1b31 | bellard | return 1; |
127 | b5ff1b31 | bellard | } |
128 | b5ff1b31 | bellard | |
129 | b5ff1b31 | bellard | target_ulong cpu_get_phys_page_debug(CPUState *env, target_ulong addr) |
130 | b5ff1b31 | bellard | { |
131 | b5ff1b31 | bellard | return addr;
|
132 | b5ff1b31 | bellard | } |
133 | b5ff1b31 | bellard | |
134 | b5ff1b31 | bellard | /* These should probably raise undefined insn exceptions. */
|
135 | b5ff1b31 | bellard | void helper_set_cp15(CPUState *env, uint32_t insn, uint32_t val)
|
136 | b5ff1b31 | bellard | { |
137 | b5ff1b31 | bellard | cpu_abort(env, "cp15 insn %08x\n", insn);
|
138 | b5ff1b31 | bellard | } |
139 | b5ff1b31 | bellard | |
140 | b5ff1b31 | bellard | uint32_t helper_get_cp15(CPUState *env, uint32_t insn) |
141 | b5ff1b31 | bellard | { |
142 | b5ff1b31 | bellard | cpu_abort(env, "cp15 insn %08x\n", insn);
|
143 | b5ff1b31 | bellard | return 0; |
144 | b5ff1b31 | bellard | } |
145 | b5ff1b31 | bellard | |
146 | b5ff1b31 | bellard | void switch_mode(CPUState *env, int mode) |
147 | b5ff1b31 | bellard | { |
148 | b5ff1b31 | bellard | if (mode != ARM_CPU_MODE_USR)
|
149 | b5ff1b31 | bellard | cpu_abort(env, "Tried to switch out of user mode\n");
|
150 | b5ff1b31 | bellard | } |
151 | b5ff1b31 | bellard | |
152 | b5ff1b31 | bellard | #else
|
153 | b5ff1b31 | bellard | |
154 | 8e71621f | pbrook | extern int semihosting_enabled; |
155 | 8e71621f | pbrook | |
156 | b5ff1b31 | bellard | /* Map CPU modes onto saved register banks. */
|
157 | b5ff1b31 | bellard | static inline int bank_number (int mode) |
158 | b5ff1b31 | bellard | { |
159 | b5ff1b31 | bellard | switch (mode) {
|
160 | b5ff1b31 | bellard | case ARM_CPU_MODE_USR:
|
161 | b5ff1b31 | bellard | case ARM_CPU_MODE_SYS:
|
162 | b5ff1b31 | bellard | return 0; |
163 | b5ff1b31 | bellard | case ARM_CPU_MODE_SVC:
|
164 | b5ff1b31 | bellard | return 1; |
165 | b5ff1b31 | bellard | case ARM_CPU_MODE_ABT:
|
166 | b5ff1b31 | bellard | return 2; |
167 | b5ff1b31 | bellard | case ARM_CPU_MODE_UND:
|
168 | b5ff1b31 | bellard | return 3; |
169 | b5ff1b31 | bellard | case ARM_CPU_MODE_IRQ:
|
170 | b5ff1b31 | bellard | return 4; |
171 | b5ff1b31 | bellard | case ARM_CPU_MODE_FIQ:
|
172 | b5ff1b31 | bellard | return 5; |
173 | b5ff1b31 | bellard | } |
174 | b5ff1b31 | bellard | cpu_abort(cpu_single_env, "Bad mode %x\n", mode);
|
175 | b5ff1b31 | bellard | return -1; |
176 | b5ff1b31 | bellard | } |
177 | b5ff1b31 | bellard | |
178 | b5ff1b31 | bellard | void switch_mode(CPUState *env, int mode) |
179 | b5ff1b31 | bellard | { |
180 | b5ff1b31 | bellard | int old_mode;
|
181 | b5ff1b31 | bellard | int i;
|
182 | b5ff1b31 | bellard | |
183 | b5ff1b31 | bellard | old_mode = env->uncached_cpsr & CPSR_M; |
184 | b5ff1b31 | bellard | if (mode == old_mode)
|
185 | b5ff1b31 | bellard | return;
|
186 | b5ff1b31 | bellard | |
187 | b5ff1b31 | bellard | if (old_mode == ARM_CPU_MODE_FIQ) {
|
188 | b5ff1b31 | bellard | memcpy (env->fiq_regs, env->regs + 8, 5 * sizeof(uint32_t)); |
189 | 8637c67f | pbrook | memcpy (env->regs + 8, env->usr_regs, 5 * sizeof(uint32_t)); |
190 | b5ff1b31 | bellard | } else if (mode == ARM_CPU_MODE_FIQ) { |
191 | b5ff1b31 | bellard | memcpy (env->usr_regs, env->regs + 8, 5 * sizeof(uint32_t)); |
192 | 8637c67f | pbrook | memcpy (env->regs + 8, env->fiq_regs, 5 * sizeof(uint32_t)); |
193 | b5ff1b31 | bellard | } |
194 | b5ff1b31 | bellard | |
195 | b5ff1b31 | bellard | i = bank_number(old_mode); |
196 | b5ff1b31 | bellard | env->banked_r13[i] = env->regs[13];
|
197 | b5ff1b31 | bellard | env->banked_r14[i] = env->regs[14];
|
198 | b5ff1b31 | bellard | env->banked_spsr[i] = env->spsr; |
199 | b5ff1b31 | bellard | |
200 | b5ff1b31 | bellard | i = bank_number(mode); |
201 | b5ff1b31 | bellard | env->regs[13] = env->banked_r13[i];
|
202 | b5ff1b31 | bellard | env->regs[14] = env->banked_r14[i];
|
203 | b5ff1b31 | bellard | env->spsr = env->banked_spsr[i]; |
204 | b5ff1b31 | bellard | } |
205 | b5ff1b31 | bellard | |
206 | b5ff1b31 | bellard | /* Handle a CPU exception. */
|
207 | b5ff1b31 | bellard | void do_interrupt(CPUARMState *env)
|
208 | b5ff1b31 | bellard | { |
209 | b5ff1b31 | bellard | uint32_t addr; |
210 | b5ff1b31 | bellard | uint32_t mask; |
211 | b5ff1b31 | bellard | int new_mode;
|
212 | b5ff1b31 | bellard | uint32_t offset; |
213 | b5ff1b31 | bellard | |
214 | b5ff1b31 | bellard | /* TODO: Vectored interrupt controller. */
|
215 | b5ff1b31 | bellard | switch (env->exception_index) {
|
216 | b5ff1b31 | bellard | case EXCP_UDEF:
|
217 | b5ff1b31 | bellard | new_mode = ARM_CPU_MODE_UND; |
218 | b5ff1b31 | bellard | addr = 0x04;
|
219 | b5ff1b31 | bellard | mask = CPSR_I; |
220 | b5ff1b31 | bellard | if (env->thumb)
|
221 | b5ff1b31 | bellard | offset = 2;
|
222 | b5ff1b31 | bellard | else
|
223 | b5ff1b31 | bellard | offset = 4;
|
224 | b5ff1b31 | bellard | break;
|
225 | b5ff1b31 | bellard | case EXCP_SWI:
|
226 | 8e71621f | pbrook | if (semihosting_enabled) {
|
227 | 8e71621f | pbrook | /* Check for semihosting interrupt. */
|
228 | 8e71621f | pbrook | if (env->thumb) {
|
229 | 8e71621f | pbrook | mask = lduw_code(env->regs[15] - 2) & 0xff; |
230 | 8e71621f | pbrook | } else {
|
231 | 8e71621f | pbrook | mask = ldl_code(env->regs[15] - 4) & 0xffffff; |
232 | 8e71621f | pbrook | } |
233 | 8e71621f | pbrook | /* Only intercept calls from privileged modes, to provide some
|
234 | 8e71621f | pbrook | semblance of security. */
|
235 | 8e71621f | pbrook | if (((mask == 0x123456 && !env->thumb) |
236 | 8e71621f | pbrook | || (mask == 0xab && env->thumb))
|
237 | 8e71621f | pbrook | && (env->uncached_cpsr & CPSR_M) != ARM_CPU_MODE_USR) { |
238 | 8e71621f | pbrook | env->regs[0] = do_arm_semihosting(env);
|
239 | 8e71621f | pbrook | return;
|
240 | 8e71621f | pbrook | } |
241 | 8e71621f | pbrook | } |
242 | b5ff1b31 | bellard | new_mode = ARM_CPU_MODE_SVC; |
243 | b5ff1b31 | bellard | addr = 0x08;
|
244 | b5ff1b31 | bellard | mask = CPSR_I; |
245 | b5ff1b31 | bellard | /* The PC already points to the next instructon. */
|
246 | b5ff1b31 | bellard | offset = 0;
|
247 | b5ff1b31 | bellard | break;
|
248 | b5ff1b31 | bellard | case EXCP_PREFETCH_ABORT:
|
249 | 06c949e6 | pbrook | case EXCP_BKPT:
|
250 | b5ff1b31 | bellard | new_mode = ARM_CPU_MODE_ABT; |
251 | b5ff1b31 | bellard | addr = 0x0c;
|
252 | b5ff1b31 | bellard | mask = CPSR_A | CPSR_I; |
253 | b5ff1b31 | bellard | offset = 4;
|
254 | b5ff1b31 | bellard | break;
|
255 | b5ff1b31 | bellard | case EXCP_DATA_ABORT:
|
256 | b5ff1b31 | bellard | new_mode = ARM_CPU_MODE_ABT; |
257 | b5ff1b31 | bellard | addr = 0x10;
|
258 | b5ff1b31 | bellard | mask = CPSR_A | CPSR_I; |
259 | b5ff1b31 | bellard | offset = 8;
|
260 | b5ff1b31 | bellard | break;
|
261 | b5ff1b31 | bellard | case EXCP_IRQ:
|
262 | b5ff1b31 | bellard | new_mode = ARM_CPU_MODE_IRQ; |
263 | b5ff1b31 | bellard | addr = 0x18;
|
264 | b5ff1b31 | bellard | /* Disable IRQ and imprecise data aborts. */
|
265 | b5ff1b31 | bellard | mask = CPSR_A | CPSR_I; |
266 | b5ff1b31 | bellard | offset = 4;
|
267 | b5ff1b31 | bellard | break;
|
268 | b5ff1b31 | bellard | case EXCP_FIQ:
|
269 | b5ff1b31 | bellard | new_mode = ARM_CPU_MODE_FIQ; |
270 | b5ff1b31 | bellard | addr = 0x1c;
|
271 | b5ff1b31 | bellard | /* Disable FIQ, IRQ and imprecise data aborts. */
|
272 | b5ff1b31 | bellard | mask = CPSR_A | CPSR_I | CPSR_F; |
273 | b5ff1b31 | bellard | offset = 4;
|
274 | b5ff1b31 | bellard | break;
|
275 | b5ff1b31 | bellard | default:
|
276 | b5ff1b31 | bellard | cpu_abort(env, "Unhandled exception 0x%x\n", env->exception_index);
|
277 | b5ff1b31 | bellard | return; /* Never happens. Keep compiler happy. */ |
278 | b5ff1b31 | bellard | } |
279 | b5ff1b31 | bellard | /* High vectors. */
|
280 | b5ff1b31 | bellard | if (env->cp15.c1_sys & (1 << 13)) { |
281 | b5ff1b31 | bellard | addr += 0xffff0000;
|
282 | b5ff1b31 | bellard | } |
283 | b5ff1b31 | bellard | switch_mode (env, new_mode); |
284 | b5ff1b31 | bellard | env->spsr = cpsr_read(env); |
285 | 6d7e6326 | bellard | /* Switch to the new mode, and switch to Arm mode. */
|
286 | b5ff1b31 | bellard | /* ??? Thumb interrupt handlers not implemented. */
|
287 | 6d7e6326 | bellard | env->uncached_cpsr = (env->uncached_cpsr & ~CPSR_M) | new_mode; |
288 | b5ff1b31 | bellard | env->uncached_cpsr |= mask; |
289 | 6d7e6326 | bellard | env->thumb = 0;
|
290 | b5ff1b31 | bellard | env->regs[14] = env->regs[15] + offset; |
291 | b5ff1b31 | bellard | env->regs[15] = addr;
|
292 | b5ff1b31 | bellard | env->interrupt_request |= CPU_INTERRUPT_EXITTB; |
293 | b5ff1b31 | bellard | } |
294 | b5ff1b31 | bellard | |
295 | b5ff1b31 | bellard | /* Check section/page access permissions.
|
296 | b5ff1b31 | bellard | Returns the page protection flags, or zero if the access is not
|
297 | b5ff1b31 | bellard | permitted. */
|
298 | b5ff1b31 | bellard | static inline int check_ap(CPUState *env, int ap, int domain, int access_type, |
299 | b5ff1b31 | bellard | int is_user)
|
300 | b5ff1b31 | bellard | { |
301 | b5ff1b31 | bellard | if (domain == 3) |
302 | b5ff1b31 | bellard | return PAGE_READ | PAGE_WRITE;
|
303 | b5ff1b31 | bellard | |
304 | b5ff1b31 | bellard | switch (ap) {
|
305 | b5ff1b31 | bellard | case 0: |
306 | 78600320 | pbrook | if (access_type == 1) |
307 | b5ff1b31 | bellard | return 0; |
308 | b5ff1b31 | bellard | switch ((env->cp15.c1_sys >> 8) & 3) { |
309 | b5ff1b31 | bellard | case 1: |
310 | b5ff1b31 | bellard | return is_user ? 0 : PAGE_READ; |
311 | b5ff1b31 | bellard | case 2: |
312 | b5ff1b31 | bellard | return PAGE_READ;
|
313 | b5ff1b31 | bellard | default:
|
314 | b5ff1b31 | bellard | return 0; |
315 | b5ff1b31 | bellard | } |
316 | b5ff1b31 | bellard | case 1: |
317 | b5ff1b31 | bellard | return is_user ? 0 : PAGE_READ | PAGE_WRITE; |
318 | b5ff1b31 | bellard | case 2: |
319 | b5ff1b31 | bellard | if (is_user)
|
320 | b5ff1b31 | bellard | return (access_type == 1) ? 0 : PAGE_READ; |
321 | b5ff1b31 | bellard | else
|
322 | b5ff1b31 | bellard | return PAGE_READ | PAGE_WRITE;
|
323 | b5ff1b31 | bellard | case 3: |
324 | b5ff1b31 | bellard | return PAGE_READ | PAGE_WRITE;
|
325 | b5ff1b31 | bellard | default:
|
326 | b5ff1b31 | bellard | abort(); |
327 | b5ff1b31 | bellard | } |
328 | b5ff1b31 | bellard | } |
329 | b5ff1b31 | bellard | |
330 | b5ff1b31 | bellard | static int get_phys_addr(CPUState *env, uint32_t address, int access_type, |
331 | b5ff1b31 | bellard | int is_user, uint32_t *phys_ptr, int *prot) |
332 | b5ff1b31 | bellard | { |
333 | b5ff1b31 | bellard | int code;
|
334 | b5ff1b31 | bellard | uint32_t table; |
335 | b5ff1b31 | bellard | uint32_t desc; |
336 | b5ff1b31 | bellard | int type;
|
337 | b5ff1b31 | bellard | int ap;
|
338 | b5ff1b31 | bellard | int domain;
|
339 | b5ff1b31 | bellard | uint32_t phys_addr; |
340 | b5ff1b31 | bellard | |
341 | b5ff1b31 | bellard | /* Fast Context Switch Extension. */
|
342 | b5ff1b31 | bellard | if (address < 0x02000000) |
343 | b5ff1b31 | bellard | address += env->cp15.c13_fcse; |
344 | b5ff1b31 | bellard | |
345 | b5ff1b31 | bellard | if ((env->cp15.c1_sys & 1) == 0) { |
346 | b5ff1b31 | bellard | /* MMU diusabled. */
|
347 | b5ff1b31 | bellard | *phys_ptr = address; |
348 | b5ff1b31 | bellard | *prot = PAGE_READ | PAGE_WRITE; |
349 | b5ff1b31 | bellard | } else {
|
350 | b5ff1b31 | bellard | /* Pagetable walk. */
|
351 | b5ff1b31 | bellard | /* Lookup l1 descriptor. */
|
352 | b5ff1b31 | bellard | table = (env->cp15.c2 & 0xffffc000) | ((address >> 18) & 0x3ffc); |
353 | b5ff1b31 | bellard | desc = ldl_phys(table); |
354 | b5ff1b31 | bellard | type = (desc & 3);
|
355 | b5ff1b31 | bellard | domain = (env->cp15.c3 >> ((desc >> 4) & 0x1e)) & 3; |
356 | b5ff1b31 | bellard | if (type == 0) { |
357 | b5ff1b31 | bellard | /* Secton translation fault. */
|
358 | b5ff1b31 | bellard | code = 5;
|
359 | b5ff1b31 | bellard | goto do_fault;
|
360 | b5ff1b31 | bellard | } |
361 | b5ff1b31 | bellard | if (domain == 0 || domain == 2) { |
362 | b5ff1b31 | bellard | if (type == 2) |
363 | b5ff1b31 | bellard | code = 9; /* Section domain fault. */ |
364 | b5ff1b31 | bellard | else
|
365 | b5ff1b31 | bellard | code = 11; /* Page domain fault. */ |
366 | b5ff1b31 | bellard | goto do_fault;
|
367 | b5ff1b31 | bellard | } |
368 | b5ff1b31 | bellard | if (type == 2) { |
369 | b5ff1b31 | bellard | /* 1Mb section. */
|
370 | b5ff1b31 | bellard | phys_addr = (desc & 0xfff00000) | (address & 0x000fffff); |
371 | b5ff1b31 | bellard | ap = (desc >> 10) & 3; |
372 | b5ff1b31 | bellard | code = 13;
|
373 | b5ff1b31 | bellard | } else {
|
374 | b5ff1b31 | bellard | /* Lookup l2 entry. */
|
375 | b5ff1b31 | bellard | table = (desc & 0xfffffc00) | ((address >> 10) & 0x3fc); |
376 | b5ff1b31 | bellard | desc = ldl_phys(table); |
377 | b5ff1b31 | bellard | switch (desc & 3) { |
378 | b5ff1b31 | bellard | case 0: /* Page translation fault. */ |
379 | b5ff1b31 | bellard | code = 7;
|
380 | b5ff1b31 | bellard | goto do_fault;
|
381 | b5ff1b31 | bellard | case 1: /* 64k page. */ |
382 | b5ff1b31 | bellard | phys_addr = (desc & 0xffff0000) | (address & 0xffff); |
383 | b5ff1b31 | bellard | ap = (desc >> (4 + ((address >> 13) & 6))) & 3; |
384 | b5ff1b31 | bellard | break;
|
385 | b5ff1b31 | bellard | case 2: /* 4k page. */ |
386 | b5ff1b31 | bellard | phys_addr = (desc & 0xfffff000) | (address & 0xfff); |
387 | b5ff1b31 | bellard | ap = (desc >> (4 + ((address >> 13) & 6))) & 3; |
388 | b5ff1b31 | bellard | break;
|
389 | b5ff1b31 | bellard | case 3: /* 1k page. */ |
390 | b5ff1b31 | bellard | if (type == 1) { |
391 | b5ff1b31 | bellard | /* Page translation fault. */
|
392 | b5ff1b31 | bellard | code = 7;
|
393 | b5ff1b31 | bellard | goto do_fault;
|
394 | b5ff1b31 | bellard | } |
395 | b5ff1b31 | bellard | phys_addr = (desc & 0xfffffc00) | (address & 0x3ff); |
396 | b5ff1b31 | bellard | ap = (desc >> 4) & 3; |
397 | b5ff1b31 | bellard | break;
|
398 | b5ff1b31 | bellard | default:
|
399 | b5ff1b31 | bellard | /* Never happens, but compiler isn't smart enough to tell. */
|
400 | b5ff1b31 | bellard | abort(); |
401 | b5ff1b31 | bellard | } |
402 | b5ff1b31 | bellard | code = 15;
|
403 | b5ff1b31 | bellard | } |
404 | b5ff1b31 | bellard | *prot = check_ap(env, ap, domain, access_type, is_user); |
405 | b5ff1b31 | bellard | if (!*prot) {
|
406 | b5ff1b31 | bellard | /* Access permission fault. */
|
407 | b5ff1b31 | bellard | goto do_fault;
|
408 | b5ff1b31 | bellard | } |
409 | b5ff1b31 | bellard | *phys_ptr = phys_addr; |
410 | b5ff1b31 | bellard | } |
411 | b5ff1b31 | bellard | return 0; |
412 | b5ff1b31 | bellard | do_fault:
|
413 | b5ff1b31 | bellard | return code | (domain << 4); |
414 | b5ff1b31 | bellard | } |
415 | b5ff1b31 | bellard | |
416 | b5ff1b31 | bellard | int cpu_arm_handle_mmu_fault (CPUState *env, target_ulong address,
|
417 | b5ff1b31 | bellard | int access_type, int is_user, int is_softmmu) |
418 | b5ff1b31 | bellard | { |
419 | b5ff1b31 | bellard | uint32_t phys_addr; |
420 | b5ff1b31 | bellard | int prot;
|
421 | b5ff1b31 | bellard | int ret;
|
422 | b5ff1b31 | bellard | |
423 | b5ff1b31 | bellard | ret = get_phys_addr(env, address, access_type, is_user, &phys_addr, &prot); |
424 | b5ff1b31 | bellard | if (ret == 0) { |
425 | b5ff1b31 | bellard | /* Map a single [sub]page. */
|
426 | b5ff1b31 | bellard | phys_addr &= ~(uint32_t)0x3ff;
|
427 | b5ff1b31 | bellard | address &= ~(uint32_t)0x3ff;
|
428 | b5ff1b31 | bellard | return tlb_set_page (env, address, phys_addr, prot, is_user,
|
429 | b5ff1b31 | bellard | is_softmmu); |
430 | b5ff1b31 | bellard | } |
431 | b5ff1b31 | bellard | |
432 | b5ff1b31 | bellard | if (access_type == 2) { |
433 | b5ff1b31 | bellard | env->cp15.c5_insn = ret; |
434 | b5ff1b31 | bellard | env->cp15.c6_insn = address; |
435 | b5ff1b31 | bellard | env->exception_index = EXCP_PREFETCH_ABORT; |
436 | b5ff1b31 | bellard | } else {
|
437 | b5ff1b31 | bellard | env->cp15.c5_data = ret; |
438 | b5ff1b31 | bellard | env->cp15.c6_data = address; |
439 | b5ff1b31 | bellard | env->exception_index = EXCP_DATA_ABORT; |
440 | b5ff1b31 | bellard | } |
441 | b5ff1b31 | bellard | return 1; |
442 | b5ff1b31 | bellard | } |
443 | b5ff1b31 | bellard | |
444 | b5ff1b31 | bellard | target_ulong cpu_get_phys_page_debug(CPUState *env, target_ulong addr) |
445 | b5ff1b31 | bellard | { |
446 | b5ff1b31 | bellard | uint32_t phys_addr; |
447 | b5ff1b31 | bellard | int prot;
|
448 | b5ff1b31 | bellard | int ret;
|
449 | b5ff1b31 | bellard | |
450 | b5ff1b31 | bellard | ret = get_phys_addr(env, addr, 0, 0, &phys_addr, &prot); |
451 | b5ff1b31 | bellard | |
452 | b5ff1b31 | bellard | if (ret != 0) |
453 | b5ff1b31 | bellard | return -1; |
454 | b5ff1b31 | bellard | |
455 | b5ff1b31 | bellard | return phys_addr;
|
456 | b5ff1b31 | bellard | } |
457 | b5ff1b31 | bellard | |
458 | b5ff1b31 | bellard | void helper_set_cp15(CPUState *env, uint32_t insn, uint32_t val)
|
459 | b5ff1b31 | bellard | { |
460 | b5ff1b31 | bellard | uint32_t op2; |
461 | b5ff1b31 | bellard | |
462 | b5ff1b31 | bellard | op2 = (insn >> 5) & 7; |
463 | b5ff1b31 | bellard | switch ((insn >> 16) & 0xf) { |
464 | b5ff1b31 | bellard | case 0: /* ID codes. */ |
465 | b5ff1b31 | bellard | goto bad_reg;
|
466 | b5ff1b31 | bellard | case 1: /* System configuration. */ |
467 | b5ff1b31 | bellard | switch (op2) {
|
468 | b5ff1b31 | bellard | case 0: |
469 | b5ff1b31 | bellard | env->cp15.c1_sys = val; |
470 | b5ff1b31 | bellard | /* ??? Lots of these bits are not implemented. */
|
471 | b5ff1b31 | bellard | /* This may enable/disable the MMU, so do a TLB flush. */
|
472 | b5ff1b31 | bellard | tlb_flush(env, 1);
|
473 | b5ff1b31 | bellard | break;
|
474 | b5ff1b31 | bellard | case 2: |
475 | b5ff1b31 | bellard | env->cp15.c1_coproc = val; |
476 | b5ff1b31 | bellard | /* ??? Is this safe when called from within a TB? */
|
477 | b5ff1b31 | bellard | tb_flush(env); |
478 | b5ff1b31 | bellard | default:
|
479 | b5ff1b31 | bellard | goto bad_reg;
|
480 | b5ff1b31 | bellard | } |
481 | b5ff1b31 | bellard | break;
|
482 | b5ff1b31 | bellard | case 2: /* MMU Page table control. */ |
483 | b5ff1b31 | bellard | env->cp15.c2 = val; |
484 | b5ff1b31 | bellard | break;
|
485 | b5ff1b31 | bellard | case 3: /* MMU Domain access control. */ |
486 | b5ff1b31 | bellard | env->cp15.c3 = val; |
487 | b5ff1b31 | bellard | break;
|
488 | b5ff1b31 | bellard | case 4: /* Reserved. */ |
489 | b5ff1b31 | bellard | goto bad_reg;
|
490 | b5ff1b31 | bellard | case 5: /* MMU Fault status. */ |
491 | b5ff1b31 | bellard | switch (op2) {
|
492 | b5ff1b31 | bellard | case 0: |
493 | b5ff1b31 | bellard | env->cp15.c5_data = val; |
494 | b5ff1b31 | bellard | break;
|
495 | b5ff1b31 | bellard | case 1: |
496 | b5ff1b31 | bellard | env->cp15.c5_insn = val; |
497 | b5ff1b31 | bellard | break;
|
498 | b5ff1b31 | bellard | default:
|
499 | b5ff1b31 | bellard | goto bad_reg;
|
500 | b5ff1b31 | bellard | } |
501 | b5ff1b31 | bellard | break;
|
502 | b5ff1b31 | bellard | case 6: /* MMU Fault address. */ |
503 | b5ff1b31 | bellard | switch (op2) {
|
504 | b5ff1b31 | bellard | case 0: |
505 | b5ff1b31 | bellard | env->cp15.c6_data = val; |
506 | b5ff1b31 | bellard | break;
|
507 | b5ff1b31 | bellard | case 1: |
508 | b5ff1b31 | bellard | env->cp15.c6_insn = val; |
509 | b5ff1b31 | bellard | break;
|
510 | b5ff1b31 | bellard | default:
|
511 | b5ff1b31 | bellard | goto bad_reg;
|
512 | b5ff1b31 | bellard | } |
513 | b5ff1b31 | bellard | break;
|
514 | b5ff1b31 | bellard | case 7: /* Cache control. */ |
515 | b5ff1b31 | bellard | /* No cache, so nothing to do. */
|
516 | b5ff1b31 | bellard | break;
|
517 | b5ff1b31 | bellard | case 8: /* MMU TLB control. */ |
518 | b5ff1b31 | bellard | switch (op2) {
|
519 | b5ff1b31 | bellard | case 0: /* Invalidate all. */ |
520 | b5ff1b31 | bellard | tlb_flush(env, 0);
|
521 | b5ff1b31 | bellard | break;
|
522 | b5ff1b31 | bellard | case 1: /* Invalidate single TLB entry. */ |
523 | b5ff1b31 | bellard | #if 0
|
524 | b5ff1b31 | bellard | /* ??? This is wrong for large pages and sections. */
|
525 | b5ff1b31 | bellard | /* As an ugly hack to make linux work we always flush a 4K
|
526 | b5ff1b31 | bellard | pages. */
|
527 | b5ff1b31 | bellard | val &= 0xfffff000;
|
528 | b5ff1b31 | bellard | tlb_flush_page(env, val);
|
529 | b5ff1b31 | bellard | tlb_flush_page(env, val + 0x400);
|
530 | b5ff1b31 | bellard | tlb_flush_page(env, val + 0x800);
|
531 | b5ff1b31 | bellard | tlb_flush_page(env, val + 0xc00);
|
532 | b5ff1b31 | bellard | #else
|
533 | b5ff1b31 | bellard | tlb_flush(env, 1);
|
534 | b5ff1b31 | bellard | #endif
|
535 | b5ff1b31 | bellard | break;
|
536 | b5ff1b31 | bellard | default:
|
537 | b5ff1b31 | bellard | goto bad_reg;
|
538 | b5ff1b31 | bellard | } |
539 | b5ff1b31 | bellard | break;
|
540 | b5ff1b31 | bellard | case 9: /* Cache lockdown. */ |
541 | b5ff1b31 | bellard | switch (op2) {
|
542 | b5ff1b31 | bellard | case 0: |
543 | b5ff1b31 | bellard | env->cp15.c9_data = val; |
544 | b5ff1b31 | bellard | break;
|
545 | b5ff1b31 | bellard | case 1: |
546 | b5ff1b31 | bellard | env->cp15.c9_insn = val; |
547 | b5ff1b31 | bellard | break;
|
548 | b5ff1b31 | bellard | default:
|
549 | b5ff1b31 | bellard | goto bad_reg;
|
550 | b5ff1b31 | bellard | } |
551 | b5ff1b31 | bellard | break;
|
552 | b5ff1b31 | bellard | case 10: /* MMU TLB lockdown. */ |
553 | b5ff1b31 | bellard | /* ??? TLB lockdown not implemented. */
|
554 | b5ff1b31 | bellard | break;
|
555 | b5ff1b31 | bellard | case 11: /* TCM DMA control. */ |
556 | b5ff1b31 | bellard | case 12: /* Reserved. */ |
557 | b5ff1b31 | bellard | goto bad_reg;
|
558 | b5ff1b31 | bellard | case 13: /* Process ID. */ |
559 | b5ff1b31 | bellard | switch (op2) {
|
560 | b5ff1b31 | bellard | case 0: |
561 | d07edbfa | pbrook | /* Unlike real hardware the qemu TLB uses virtual addresses,
|
562 | d07edbfa | pbrook | not modified virtual addresses, so this causes a TLB flush.
|
563 | d07edbfa | pbrook | */
|
564 | d07edbfa | pbrook | if (env->cp15.c13_fcse != val)
|
565 | d07edbfa | pbrook | tlb_flush(env, 1);
|
566 | d07edbfa | pbrook | env->cp15.c13_fcse = val; |
567 | b5ff1b31 | bellard | break;
|
568 | b5ff1b31 | bellard | case 1: |
569 | d07edbfa | pbrook | /* This changes the ASID, so do a TLB flush. */
|
570 | d07edbfa | pbrook | if (env->cp15.c13_context != val)
|
571 | d07edbfa | pbrook | tlb_flush(env, 0);
|
572 | d07edbfa | pbrook | env->cp15.c13_context = val; |
573 | b5ff1b31 | bellard | break;
|
574 | b5ff1b31 | bellard | default:
|
575 | b5ff1b31 | bellard | goto bad_reg;
|
576 | b5ff1b31 | bellard | } |
577 | b5ff1b31 | bellard | break;
|
578 | b5ff1b31 | bellard | case 14: /* Reserved. */ |
579 | b5ff1b31 | bellard | goto bad_reg;
|
580 | b5ff1b31 | bellard | case 15: /* Implementation specific. */ |
581 | b5ff1b31 | bellard | /* ??? Internal registers not implemented. */
|
582 | b5ff1b31 | bellard | break;
|
583 | b5ff1b31 | bellard | } |
584 | b5ff1b31 | bellard | return;
|
585 | b5ff1b31 | bellard | bad_reg:
|
586 | b5ff1b31 | bellard | /* ??? For debugging only. Should raise illegal instruction exception. */
|
587 | b5ff1b31 | bellard | cpu_abort(env, "Unimplemented cp15 register read\n");
|
588 | b5ff1b31 | bellard | } |
589 | b5ff1b31 | bellard | |
590 | b5ff1b31 | bellard | uint32_t helper_get_cp15(CPUState *env, uint32_t insn) |
591 | b5ff1b31 | bellard | { |
592 | b5ff1b31 | bellard | uint32_t op2; |
593 | b5ff1b31 | bellard | |
594 | b5ff1b31 | bellard | op2 = (insn >> 5) & 7; |
595 | b5ff1b31 | bellard | switch ((insn >> 16) & 0xf) { |
596 | b5ff1b31 | bellard | case 0: /* ID codes. */ |
597 | b5ff1b31 | bellard | switch (op2) {
|
598 | b5ff1b31 | bellard | default: /* Device ID. */ |
599 | 40f137e1 | pbrook | return env->cp15.c0_cpuid;
|
600 | b5ff1b31 | bellard | case 1: /* Cache Type. */ |
601 | b5ff1b31 | bellard | return 0x1dd20d2; |
602 | b5ff1b31 | bellard | case 2: /* TCM status. */ |
603 | b5ff1b31 | bellard | return 0; |
604 | b5ff1b31 | bellard | } |
605 | b5ff1b31 | bellard | case 1: /* System configuration. */ |
606 | b5ff1b31 | bellard | switch (op2) {
|
607 | b5ff1b31 | bellard | case 0: /* Control register. */ |
608 | b5ff1b31 | bellard | return env->cp15.c1_sys;
|
609 | b5ff1b31 | bellard | case 1: /* Auxiliary control register. */ |
610 | 40f137e1 | pbrook | if (arm_feature(env, ARM_FEATURE_AUXCR))
|
611 | 40f137e1 | pbrook | return 1; |
612 | 40f137e1 | pbrook | goto bad_reg;
|
613 | b5ff1b31 | bellard | case 2: /* Coprocessor access register. */ |
614 | b5ff1b31 | bellard | return env->cp15.c1_coproc;
|
615 | b5ff1b31 | bellard | default:
|
616 | b5ff1b31 | bellard | goto bad_reg;
|
617 | b5ff1b31 | bellard | } |
618 | b5ff1b31 | bellard | case 2: /* MMU Page table control. */ |
619 | b5ff1b31 | bellard | return env->cp15.c2;
|
620 | b5ff1b31 | bellard | case 3: /* MMU Domain access control. */ |
621 | b5ff1b31 | bellard | return env->cp15.c3;
|
622 | b5ff1b31 | bellard | case 4: /* Reserved. */ |
623 | b5ff1b31 | bellard | goto bad_reg;
|
624 | b5ff1b31 | bellard | case 5: /* MMU Fault status. */ |
625 | b5ff1b31 | bellard | switch (op2) {
|
626 | b5ff1b31 | bellard | case 0: |
627 | b5ff1b31 | bellard | return env->cp15.c5_data;
|
628 | b5ff1b31 | bellard | case 1: |
629 | b5ff1b31 | bellard | return env->cp15.c5_insn;
|
630 | b5ff1b31 | bellard | default:
|
631 | b5ff1b31 | bellard | goto bad_reg;
|
632 | b5ff1b31 | bellard | } |
633 | b5ff1b31 | bellard | case 6: /* MMU Fault address. */ |
634 | b5ff1b31 | bellard | switch (op2) {
|
635 | b5ff1b31 | bellard | case 0: |
636 | b5ff1b31 | bellard | return env->cp15.c6_data;
|
637 | b5ff1b31 | bellard | case 1: |
638 | 40f137e1 | pbrook | /* Arm9 doesn't have an IFAR, but implementing it anyway shouldn't
|
639 | 40f137e1 | pbrook | do any harm. */
|
640 | b5ff1b31 | bellard | return env->cp15.c6_insn;
|
641 | b5ff1b31 | bellard | default:
|
642 | b5ff1b31 | bellard | goto bad_reg;
|
643 | b5ff1b31 | bellard | } |
644 | b5ff1b31 | bellard | case 7: /* Cache control. */ |
645 | b5ff1b31 | bellard | /* ??? This is for test, clean and invaidate operations that set the
|
646 | b5ff1b31 | bellard | Z flag. We can't represent N = Z = 1, so it also clears clears
|
647 | b5ff1b31 | bellard | the N flag. Oh well. */
|
648 | b5ff1b31 | bellard | env->NZF = 0;
|
649 | b5ff1b31 | bellard | return 0; |
650 | b5ff1b31 | bellard | case 8: /* MMU TLB control. */ |
651 | b5ff1b31 | bellard | goto bad_reg;
|
652 | b5ff1b31 | bellard | case 9: /* Cache lockdown. */ |
653 | b5ff1b31 | bellard | switch (op2) {
|
654 | b5ff1b31 | bellard | case 0: |
655 | b5ff1b31 | bellard | return env->cp15.c9_data;
|
656 | b5ff1b31 | bellard | case 1: |
657 | b5ff1b31 | bellard | return env->cp15.c9_insn;
|
658 | b5ff1b31 | bellard | default:
|
659 | b5ff1b31 | bellard | goto bad_reg;
|
660 | b5ff1b31 | bellard | } |
661 | b5ff1b31 | bellard | case 10: /* MMU TLB lockdown. */ |
662 | b5ff1b31 | bellard | /* ??? TLB lockdown not implemented. */
|
663 | b5ff1b31 | bellard | return 0; |
664 | b5ff1b31 | bellard | case 11: /* TCM DMA control. */ |
665 | b5ff1b31 | bellard | case 12: /* Reserved. */ |
666 | b5ff1b31 | bellard | goto bad_reg;
|
667 | b5ff1b31 | bellard | case 13: /* Process ID. */ |
668 | b5ff1b31 | bellard | switch (op2) {
|
669 | b5ff1b31 | bellard | case 0: |
670 | b5ff1b31 | bellard | return env->cp15.c13_fcse;
|
671 | b5ff1b31 | bellard | case 1: |
672 | b5ff1b31 | bellard | return env->cp15.c13_context;
|
673 | b5ff1b31 | bellard | default:
|
674 | b5ff1b31 | bellard | goto bad_reg;
|
675 | b5ff1b31 | bellard | } |
676 | b5ff1b31 | bellard | case 14: /* Reserved. */ |
677 | b5ff1b31 | bellard | goto bad_reg;
|
678 | b5ff1b31 | bellard | case 15: /* Implementation specific. */ |
679 | b5ff1b31 | bellard | /* ??? Internal registers not implemented. */
|
680 | b5ff1b31 | bellard | return 0; |
681 | b5ff1b31 | bellard | } |
682 | b5ff1b31 | bellard | bad_reg:
|
683 | b5ff1b31 | bellard | /* ??? For debugging only. Should raise illegal instruction exception. */
|
684 | b5ff1b31 | bellard | cpu_abort(env, "Unimplemented cp15 register read\n");
|
685 | b5ff1b31 | bellard | return 0; |
686 | b5ff1b31 | bellard | } |
687 | b5ff1b31 | bellard | |
688 | b5ff1b31 | bellard | #endif |