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