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