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