]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/bcm/trap.c
pc kernel: fix wrong simd exception mask (fixes go bootstrap)
[plan9front.git] / sys / src / 9 / bcm / trap.c
1 /*
2  * traps, exceptions, interrupts, system calls.
3  */
4 #include "u.h"
5 #include "../port/lib.h"
6 #include "mem.h"
7 #include "dat.h"
8 #include "fns.h"
9 #include "io.h"
10 #include "ureg.h"
11 #include "../port/error.h"
12
13 #include "arm.h"
14
15 #define INTREGS         (VIRTIO+0xB200)
16
17 typedef struct Intregs Intregs;
18 typedef struct Vctl Vctl;
19
20 enum {
21         Nvec = 8,               /* # of vectors at start of lexception.s */
22         Fiqenable = 1<<7,
23 };
24
25 /*
26  *   Layout at virtual address KZERO (double mapped at HVECTORS).
27  */
28 typedef struct Vpage0 {
29         void    (*vectors[Nvec])(void);
30         u32int  vtable[Nvec];
31 } Vpage0;
32
33 /*
34  * interrupt control registers
35  */
36 struct Intregs {
37         u32int  ARMpending;
38         u32int  GPUpending[2];
39         u32int  FIQctl;
40         u32int  GPUenable[2];
41         u32int  ARMenable;
42         u32int  GPUdisable[2];
43         u32int  ARMdisable;
44 };
45
46 struct Vctl {
47         Vctl    *next;
48         int     irq;
49         u32int  *reg;
50         u32int  mask;
51         void    (*f)(Ureg*, void*);
52         void    *a;
53 };
54
55 static Vctl *vctl, *vfiq;
56
57 static char *trapnames[PsrMask+1] = {
58         [ PsrMusr ] "user mode",
59         [ PsrMfiq ] "fiq interrupt",
60         [ PsrMirq ] "irq interrupt",
61         [ PsrMsvc ] "svc/swi exception",
62         [ PsrMabt ] "prefetch abort/data abort",
63         [ PsrMabt+1 ] "data abort",
64         [ PsrMund ] "undefined instruction",
65         [ PsrMsys ] "sys trap",
66 };
67
68 extern int notify(Ureg*);
69
70 /*
71  *  set up for exceptions
72  */
73 void
74 trapinit(void)
75 {
76         Vpage0 *vpage0;
77
78         /* disable everything */
79         intrsoff();
80
81         /* set up the exception vectors */
82         vpage0 = (Vpage0*)HVECTORS;
83         memmove(vpage0->vectors, vectors, sizeof(vpage0->vectors));
84         memmove(vpage0->vtable, vtable, sizeof(vpage0->vtable));
85         cacheuwbinv();
86
87         /* set up the stacks for the interrupt modes */
88         setr13(PsrMfiq, (u32int*)(FIQSTKTOP));
89         setr13(PsrMirq, m->sirq);
90         setr13(PsrMabt, m->sabt);
91         setr13(PsrMund, m->sund);
92         setr13(PsrMsys, m->ssys);
93
94         coherence();
95 }
96
97 void
98 intrsoff(void)
99 {
100         Intregs *ip;
101         int disable;
102
103         ip = (Intregs*)INTREGS;
104         disable = ~0;
105         ip->GPUdisable[0] = disable;
106         ip->GPUdisable[1] = disable;
107         ip->ARMdisable = disable;
108         ip->FIQctl = 0;
109 }
110
111 /*
112  *  called by trap to handle irq interrupts.
113  *  returns true iff a clock interrupt, thus maybe reschedule.
114  */
115 static int
116 irq(Ureg* ureg)
117 {
118         Vctl *v;
119         int clockintr;
120
121         clockintr = 0;
122         for(v = vctl; v; v = v->next)
123                 if(*v->reg & v->mask){
124                         coherence();
125                         v->f(ureg, v->a);
126                         coherence();
127                         if(v->irq == IRQclock)
128                                 clockintr = 1;
129                 }
130         return clockintr;
131 }
132
133 /*
134  * called direct from lexception.s to handle fiq interrupt.
135  */
136 void
137 fiq(Ureg *ureg)
138 {
139         Vctl *v;
140
141         v = vfiq;
142         if(v == nil)
143                 panic("unexpected item in bagging area");
144         m->intr++;
145         ureg->pc -= 4;
146         coherence();
147         v->f(ureg, v->a);
148         coherence();
149 }
150
151 void
152 irqenable(int irq, void (*f)(Ureg*, void*), void* a)
153 {
154         Vctl *v;
155         Intregs *ip;
156         u32int *enable;
157
158         ip = (Intregs*)INTREGS;
159         v = (Vctl*)malloc(sizeof(Vctl));
160         if(v == nil)
161                 panic("irqenable: no mem");
162         v->irq = irq;
163         if(irq >= IRQbasic){
164                 enable = &ip->ARMenable;
165                 v->reg = &ip->ARMpending;
166                 v->mask = 1 << (irq - IRQbasic);
167         }else{
168                 enable = &ip->GPUenable[irq/32];
169                 v->reg = &ip->GPUpending[irq/32];
170                 v->mask = 1 << (irq % 32);
171         }
172         v->f = f;
173         v->a = a;
174         if(irq == IRQfiq){
175                 assert((ip->FIQctl & Fiqenable) == 0);
176                 assert((*enable & v->mask) == 0);
177                 vfiq = v;
178                 ip->FIQctl = Fiqenable | irq;
179         }else{
180                 v->next = vctl;
181                 vctl = v;
182                 *enable = v->mask;
183         }
184 }
185
186 static char *
187 trapname(int psr)
188 {
189         char *s;
190
191         s = trapnames[psr & PsrMask];
192         if(s == nil)
193                 s = "unknown trap number in psr";
194         return s;
195 }
196
197 /*
198  *  called by trap to handle access faults
199  */
200 static void
201 faultarm(Ureg *ureg, uintptr va, int user, int read)
202 {
203         int n, insyscall;
204         char buf[ERRMAX];
205         static int cnt, lastpid;
206         static ulong lastva;
207
208         if(up == nil) {
209                 dumpregs(ureg);
210                 panic("fault: nil up in faultarm, accessing %#p", va);
211         }
212         insyscall = up->insyscall;
213         up->insyscall = 1;
214         /* this is quite helpful during mmu and cache debugging */
215         if(va == lastva && up->pid == lastpid) {
216                 ++cnt;
217                 if (cnt >= 2)
218                         /* fault() isn't fixing the underlying cause */
219                         panic("fault: %d consecutive faults for va %#lux",
220                                 cnt+1, va);
221         } else {
222                 cnt = 0;
223                 lastva = va;
224                 lastpid = up->pid;
225         }
226
227         n = fault(va, read);
228         if(n < 0){
229                 if(!user){
230                         dumpregs(ureg);
231                         panic("fault: kernel accessing %#p", va);
232                 }
233                 /* don't dump registers; programs suicide all the time */
234                 snprint(buf, sizeof buf, "sys: trap: fault %s va=%#p",
235                         read? "read": "write", va);
236                 postnote(up, 1, buf, NDebug);
237         }
238         up->insyscall = insyscall;
239 }
240
241 /*
242  *  returns 1 if the instruction writes memory, 0 otherwise
243  */
244 int
245 writetomem(ulong inst)
246 {
247         /* swap always write memory */
248         if((inst & 0x0FC00000) == 0x01000000)
249                 return 1;
250
251         /* loads and stores are distinguished by bit 20 */
252         if(inst & (1<<20))
253                 return 0;
254
255         return 1;
256 }
257
258 /*
259  *  here on all exceptions other than syscall (SWI) and fiq
260  */
261 void
262 trap(Ureg *ureg)
263 {
264         int clockintr, user, x, rv, rem;
265         ulong inst, fsr;
266         uintptr va;
267         char buf[ERRMAX];
268
269         assert(!islo());
270         if(up != nil)
271                 rem = ((char*)ureg)-up->kstack;
272         else
273                 rem = ((char*)ureg)-((char*)m+sizeof(Mach));
274         if(rem < 256) {
275                 iprint("trap: %d stack bytes left, up %#p ureg %#p at pc %#lux\n",
276                         rem, up, ureg, ureg->pc);
277                 delay(1000);
278                 dumpstack();
279                 panic("trap: %d stack bytes left, up %#p ureg %#p at pc %#lux",
280                         rem, up, ureg, ureg->pc);
281         }
282
283         user = (ureg->psr & PsrMask) == PsrMusr;
284         if(user){
285                 up->dbgreg = ureg;
286                 cycles(&up->kentry);
287         }
288
289         /*
290          * All interrupts/exceptions should be resumed at ureg->pc-4,
291          * except for Data Abort which resumes at ureg->pc-8.
292          */
293         if(ureg->type == (PsrMabt+1))
294                 ureg->pc -= 8;
295         else
296                 ureg->pc -= 4;
297
298         clockintr = 0;          /* if set, may call sched() before return */
299         switch(ureg->type){
300         default:
301                 panic("unknown trap; type %#lux, psr mode %#lux", ureg->type,
302                         ureg->psr & PsrMask);
303                 break;
304         case PsrMirq:
305                 clockintr = irq(ureg);
306                 m->intr++;
307                 break;
308         case PsrMabt:                   /* prefetch fault */
309                 x = ifsrget();
310                 fsr = (x>>7) & 0x8 | x & 0x7;
311                 switch(fsr){
312                 case 0x02:              /* instruction debug event (BKPT) */
313                         if(user){
314                                 snprint(buf, sizeof buf, "sys: breakpoint");
315                                 postnote(up, 1, buf, NDebug);
316                         }else{
317                                 iprint("kernel bkpt: pc %#lux inst %#ux\n",
318                                         ureg->pc, *(u32int*)ureg->pc);
319                                 panic("kernel bkpt");
320                         }
321                         break;
322                 default:
323                         faultarm(ureg, ureg->pc, user, 1);
324                         break;
325                 }
326                 break;
327         case PsrMabt+1:                 /* data fault */
328                 va = farget();
329                 inst = *(ulong*)(ureg->pc);
330                 /* bits 12 and 10 have to be concatenated with status */
331                 x = fsrget();
332                 fsr = (x>>7) & 0x20 | (x>>6) & 0x10 | x & 0xf;
333                 switch(fsr){
334                 default:
335                 case 0xa:               /* ? was under external abort */
336                         panic("unknown data fault, 6b fsr %#lux", fsr);
337                         break;
338                 case 0x0:
339                         panic("vector exception at %#lux", ureg->pc);
340                         break;
341                 case 0x1:               /* alignment fault */
342                 case 0x3:               /* access flag fault (section) */
343                         if(user){
344                                 snprint(buf, sizeof buf,
345                                         "sys: alignment: pc %#lux va %#p\n",
346                                         ureg->pc, va);
347                                 postnote(up, 1, buf, NDebug);
348                         } else
349                                 panic("kernel alignment: pc %#lux va %#p", ureg->pc, va);
350                         break;
351                 case 0x2:
352                         panic("terminal exception at %#lux", ureg->pc);
353                         break;
354                 case 0x4:               /* icache maint fault */
355                 case 0x6:               /* access flag fault (page) */
356                 case 0x8:               /* precise external abort, non-xlat'n */
357                 case 0x28:
358                 case 0xc:               /* l1 translation, precise ext. abort */
359                 case 0x2c:
360                 case 0xe:               /* l2 translation, precise ext. abort */
361                 case 0x2e:
362                 case 0x16:              /* imprecise ext. abort, non-xlt'n */
363                 case 0x36:
364                         panic("external abort %#lux pc %#lux addr %#p",
365                                 fsr, ureg->pc, va);
366                         break;
367                 case 0x1c:              /* l1 translation, precise parity err */
368                 case 0x1e:              /* l2 translation, precise parity err */
369                 case 0x18:              /* imprecise parity or ecc err */
370                         panic("translation parity error %#lux pc %#lux addr %#p",
371                                 fsr, ureg->pc, va);
372                         break;
373                 case 0x5:               /* translation fault, no section entry */
374                 case 0x7:               /* translation fault, no page entry */
375                         faultarm(ureg, va, user, !writetomem(inst));
376                         break;
377                 case 0x9:
378                 case 0xb:
379                         /* domain fault, accessing something we shouldn't */
380                         if(user){
381                                 snprint(buf, sizeof buf,
382                                         "sys: access violation: pc %#lux va %#p\n",
383                                         ureg->pc, va);
384                                 postnote(up, 1, buf, NDebug);
385                         } else
386                                 panic("kernel access violation: pc %#lux va %#p",
387                                         ureg->pc, va);
388                         break;
389                 case 0xd:
390                 case 0xf:
391                         /* permission error, copy on write or real permission error */
392                         faultarm(ureg, va, user, !writetomem(inst));
393                         break;
394                 }
395                 break;
396         case PsrMund:                   /* undefined instruction */
397                 if(user){
398                         if(seg(up, ureg->pc, 0) != nil &&
399                            *(u32int*)ureg->pc == 0xD1200070)
400                                 postnote(up, 1, "sys: breakpoint", NDebug);
401                         else{
402                                 /* look for floating point instructions to interpret */
403                                 rv = fpuemu(ureg);
404                                 if(rv == 0){
405                                         snprint(buf, sizeof buf,
406                                                 "undefined instruction: pc %#lux\n",
407                                                 ureg->pc);
408                                         postnote(up, 1, buf, NDebug);
409                                 }
410                         }
411                 }else{
412                         if (ureg->pc & 3) {
413                                 iprint("rounding fault pc %#lux down to word\n",
414                                         ureg->pc);
415                                 ureg->pc &= ~3;
416                         }
417                         iprint("undefined instruction: pc %#lux inst %#ux\n",
418                                 ureg->pc, *(u32int*)ureg->pc);
419                         panic("undefined instruction");
420                 }
421                 break;
422         }
423         splhi();
424
425         /* delaysched set because we held a lock or because our quantum ended */
426         if(up && up->delaysched && clockintr){
427                 sched();                /* can cause more traps */
428                 splhi();
429         }
430
431         if(user){
432                 if(up->procctl || up->nnote)
433                         notify(ureg);
434                 kexit(ureg);
435         }
436 }
437
438 int
439 isvalidaddr(void *v)
440 {
441         return (uintptr)v >= KZERO;
442 }
443
444 static void
445 dumplongs(char *msg, ulong *v, int n)
446 {
447         int i, l;
448
449         l = 0;
450         iprint("%s at %.8p: ", msg, v);
451         for(i=0; i<n; i++){
452                 if(l >= 4){
453                         iprint("\n    %.8p: ", v);
454                         l = 0;
455                 }
456                 if(isvalidaddr(v)){
457                         iprint(" %.8lux", *v++);
458                         l++;
459                 }else{
460                         iprint(" invalid");
461                         break;
462                 }
463         }
464         iprint("\n");
465 }
466
467 static void
468 dumpstackwithureg(Ureg *ureg)
469 {
470         uintptr l, i, v, estack;
471         u32int *p;
472         char *s;
473
474         if((s = getconf("*nodumpstack")) != nil && strcmp(s, "0") != 0){
475                 iprint("dumpstack disabled\n");
476                 return;
477         }
478         iprint("ktrace /kernel/path %#.8lux %#.8lux %#.8lux # pc, sp, link\n",
479                 ureg->pc, ureg->sp, ureg->r14);
480         delay(2000);
481         i = 0;
482         if(up != nil && (uintptr)&l <= (uintptr)up->kstack+KSTACK)
483                 estack = (uintptr)up->kstack+KSTACK;
484         else if((uintptr)&l >= (uintptr)m->stack
485              && (uintptr)&l <= (uintptr)m+MACHSIZE)
486                 estack = (uintptr)m+MACHSIZE;
487         else{
488                 if(up != nil)
489                         iprint("&up->kstack %#p &l %#p\n", up->kstack, &l);
490                 else
491                         iprint("&m %#p &l %#p\n", m, &l);
492                 return;
493         }
494         for(l = (uintptr)&l; l < estack; l += sizeof(uintptr)){
495                 v = *(uintptr*)l;
496                 if(KTZERO < v && v < (uintptr)etext && !(v & 3)){
497                         v -= sizeof(u32int);            /* back up an instr */
498                         p = (u32int*)v;
499                         if((*p & 0x0f000000) == 0x0b000000){    /* BL instr? */
500                                 iprint("%#8.8lux=%#8.8lux ", l, v);
501                                 i++;
502                         }
503                 }
504                 if(i == 4){
505                         i = 0;
506                         iprint("\n");
507                 }
508         }
509         if(i)
510                 iprint("\n");
511 }
512
513 /*
514  * Fill in enough of Ureg to get a stack trace, and call a function.
515  * Used by debugging interface rdb.
516  */
517 void
518 callwithureg(void (*fn)(Ureg*))
519 {
520         Ureg ureg;
521
522         ureg.pc = getcallerpc(&fn);
523         ureg.sp = PTR2UINT(&fn);
524         fn(&ureg);
525 }
526
527 void
528 dumpstack(void)
529 {
530         callwithureg(dumpstackwithureg);
531 }
532
533 void
534 dumpregs(Ureg* ureg)
535 {
536         int s;
537
538         if (ureg == nil) {
539                 iprint("trap: no user process\n");
540                 return;
541         }
542         s = splhi();
543         iprint("trap: %s", trapname(ureg->type));
544         if(ureg != nil && (ureg->psr & PsrMask) != PsrMsvc)
545                 iprint(" in %s", trapname(ureg->psr));
546         iprint("\n");
547         iprint("psr %8.8lux type %2.2lux pc %8.8lux link %8.8lux\n",
548                 ureg->psr, ureg->type, ureg->pc, ureg->link);
549         iprint("R14 %8.8lux R13 %8.8lux R12 %8.8lux R11 %8.8lux R10 %8.8lux\n",
550                 ureg->r14, ureg->r13, ureg->r12, ureg->r11, ureg->r10);
551         iprint("R9  %8.8lux R8  %8.8lux R7  %8.8lux R6  %8.8lux R5  %8.8lux\n",
552                 ureg->r9, ureg->r8, ureg->r7, ureg->r6, ureg->r5);
553         iprint("R4  %8.8lux R3  %8.8lux R2  %8.8lux R1  %8.8lux R0  %8.8lux\n",
554                 ureg->r4, ureg->r3, ureg->r2, ureg->r1, ureg->r0);
555         iprint("stack is at %#p\n", ureg);
556         iprint("pc %#lux link %#lux\n", ureg->pc, ureg->link);
557
558         if(up)
559                 iprint("user stack: %#p-%#p\n", up->kstack, up->kstack+KSTACK-4);
560         else
561                 iprint("kernel stack: %8.8lux-%8.8lux\n",
562                         (ulong)(m+1), (ulong)m+BY2PG-4);
563         dumplongs("stack", (ulong *)(ureg + 1), 16);
564         delay(2000);
565         dumpstack();
566         splx(s);
567 }