1 /*
2 * sheevaplug 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 enum {
16 Debug = 0,
17
18 Ntimevec = 20, /* # of time buckets for each intr */
19 Nvecs = 256,
20 };
21
22 extern int notify(Ureg*);
23
24 extern int ldrexvalid;
25
26 typedef struct Vctl Vctl;
27 typedef struct Vctl {
28 Vctl* next; /* handlers on this vector */
29 char *name; /* of driver, xallocated */
30 void (*f)(Ureg*, void*); /* handler to call */
31 void* a; /* argument to call it with */
32 } Vctl;
33
34 static Lock vctllock;
35 static Vctl* vctl[32];
36
37 uvlong ninterrupt;
38 uvlong ninterruptticks;
39 ulong intrtimes[Nvecs][Ntimevec];
40
41 typedef struct Handler Handler;
42 struct Handler {
43 void (*r)(Ureg*, void*);
44 void *a;
45 char name[KNAMELEN];
46 };
47
48 static Handler irqlo[32];
49 static Handler irqhi[32];
50 static Handler irqbridge[32];
51 static Lock irqlock;
52 static int probing, trapped;
53
54 typedef struct Irq Irq;
55 struct Irq {
56 ulong *irq;
57 ulong *irqmask;
58 Handler *irqvec;
59 int nirqvec;
60 char *name;
61 };
62 /* irq and irqmask are filled in by trapinit */
63 static Irq irqs[] = {
64 [Irqlo] {nil, nil, irqlo, nelem(irqlo), "lo"},
65 [Irqhi] {nil, nil, irqhi, nelem(irqhi), "hi"},
66 [Irqbridge] {nil, nil, irqbridge, nelem(irqbridge), "bridge"},
67 };
68
69 /*
70 * keep histogram of interrupt service times
71 */
72 void
intrtime(Mach *,int vno)73 intrtime(Mach*, int vno)
74 {
75 ulong diff, x;
76
77 if (m == nil)
78 return;
79 x = perfticks();
80 diff = x - m->perf.intrts;
81 m->perf.intrts = x;
82
83 m->perf.inintr += diff;
84 if(up == nil && m->perf.inidle > diff)
85 m->perf.inidle -= diff;
86
87 if (m->cpuhz == 0) /* not set yet? */
88 return;
89 diff /= (m->cpuhz/1000000)*100; /* quantum = 100µsec */
90 if(diff >= Ntimevec)
91 diff = Ntimevec-1;
92 assert(vno >= 0 && vno < Nvecs);
93 intrtimes[vno][diff]++;
94 }
95
96 void
intrfmtcounts(char * s,char * se)97 intrfmtcounts(char *s, char *se)
98 {
99 USED(s, se);
100 }
101
102 static void
dumpcounts(void)103 dumpcounts(void)
104 {
105 }
106
107 void
intrclear(int sort,int v)108 intrclear(int sort, int v)
109 {
110 *irqs[sort].irq = ~(1 << v);
111 }
112
113 void
intrmask(int sort,int v)114 intrmask(int sort, int v)
115 {
116 *irqs[sort].irqmask &= ~(1 << v);
117 }
118
119 void
intrunmask(int sort,int v)120 intrunmask(int sort, int v)
121 {
122 *irqs[sort].irqmask |= 1 << v;
123 }
124
125 static void
maskallints(void)126 maskallints(void)
127 {
128 CpucsReg *cpu = (CpucsReg *)soc.cpu;
129 IntrReg *intr;
130
131 /* no fiq or ep in use */
132 intr = (IntrReg *)soc.intr;
133 intr->lo.irqmask = 0;
134 intr->hi.irqmask = 0;
135 cpu->irqmask = 0;
136 coherence();
137 }
138
139 void
intrset(Handler * h,void (* f)(Ureg *,void *),void * a,char * name)140 intrset(Handler *h, void (*f)(Ureg*, void*), void *a, char *name)
141 {
142 if(h->r != nil) {
143 // iprint("duplicate irq: %s (%#p)\n", h->name, h->r);
144 return;
145 }
146 h->r = f;
147 h->a = a;
148 strncpy(h->name, name, KNAMELEN-1);
149 h->name[KNAMELEN-1] = 0;
150 }
151
152 void
intrunset(Handler * h)153 intrunset(Handler *h)
154 {
155 h->r = nil;
156 h->a = nil;
157 h->name[0] = 0;
158 }
159
160 void
intrdel(Handler * h,void (* f)(Ureg *,void *),void * a,char * name)161 intrdel(Handler *h, void (*f)(Ureg*, void*), void *a, char *name)
162 {
163 if(h->r != f || h->a != a || strcmp(h->name, name) != 0)
164 return;
165 intrunset(h);
166 }
167
168 void
intrenable(int sort,int v,void (* f)(Ureg *,void *),void * a,char * name)169 intrenable(int sort, int v, void (*f)(Ureg*, void*), void *a, char *name)
170 {
171 //iprint("enabling intr %d vec %d for %s\n", sort, v, name);
172 ilock(&irqlock);
173 intrset(&irqs[sort].irqvec[v], f, a, name);
174 intrunmask(sort, v);
175 iunlock(&irqlock);
176 }
177
178 void
intrdisable(int sort,int v,void (* f)(Ureg *,void *),void * a,char * name)179 intrdisable(int sort, int v, void (*f)(Ureg*, void*), void* a, char *name)
180 {
181 ilock(&irqlock);
182 intrdel(&irqs[sort].irqvec[v], f, a, name);
183 intrmask(sort, v);
184 iunlock(&irqlock);
185 }
186
187 /*
188 * called by trap to handle interrupts
189 */
190 static void
intrs(Ureg * ur,int sort)191 intrs(Ureg *ur, int sort)
192 {
193 int i, s;
194 ulong ibits;
195 Handler *h;
196 Irq irq;
197
198 assert(sort >= 0 && sort < nelem(irqs));
199 irq = irqs[sort];
200 ibits = *irq.irq;
201 ibits &= *irq.irqmask;
202
203 for(i = 0; i < irq.nirqvec && ibits; i++)
204 if(ibits & (1<<i)){
205 h = &irq.irqvec[i];
206 if(h->r != nil){
207 h->r(ur, h->a);
208 splhi();
209 intrtime(m, sort*32 + i);
210 if (sort == Irqbridge && i == IRQcputimer0)
211 m->inclockintr = 1;
212 ibits &= ~(1<<i);
213 }
214 }
215 if(ibits != 0) {
216 iprint("spurious irq%s interrupt: %8.8lux\n", irq.name, ibits);
217 s = splfhi();
218 *irq.irq &= ibits;
219 *irq.irqmask &= ~ibits;
220 splx(s);
221 }
222 }
223
224 void
intrhi(Ureg * ureg,void *)225 intrhi(Ureg *ureg, void*)
226 {
227 intrs(ureg, Irqhi);
228 }
229
230 void
intrbridge(Ureg * ureg,void *)231 intrbridge(Ureg *ureg, void*)
232 {
233 intrs(ureg, Irqbridge);
234 intrclear(Irqlo, IRQ0bridge);
235 }
236
237 void
trapinit(void)238 trapinit(void)
239 {
240 int i;
241 CpucsReg *cpu;
242 IntrReg *intr;
243 Vectorpage *page0 = (Vectorpage*)HVECTORS;
244
245 intr = (IntrReg *)soc.intr;
246 cpu = (CpucsReg *)soc.cpu;
247 irqs[Irqlo].irq = &intr->lo.irq;
248 irqs[Irqlo].irqmask = &intr->lo.irqmask;
249 irqs[Irqhi].irq = &intr->hi.irq;
250 irqs[Irqhi].irqmask = &intr->hi.irqmask;
251 irqs[Irqbridge].irq = &cpu->irq;
252 irqs[Irqbridge].irqmask = &cpu->irqmask;
253 coherence();
254
255 setr13(PsrMfiq, m->fiqstack + nelem(m->fiqstack));
256 setr13(PsrMirq, m->irqstack + nelem(m->irqstack));
257 setr13(PsrMabt, m->abtstack + nelem(m->abtstack));
258 setr13(PsrMund, m->undstack + nelem(m->undstack));
259
260 memmove(page0->vectors, vectors, sizeof page0->vectors);
261 memmove(page0->vtable, vtable, sizeof page0->vtable);
262 cacheuwbinv();
263 l2cacheuwbinv();
264
265 cpu->cpucfg &= ~Cfgvecinithi;
266
267 for(i = 0; i < nelem(irqlo); i++)
268 intrunset(&irqlo[i]);
269 for(i = 0; i < nelem(irqhi); i++)
270 intrunset(&irqhi[i]);
271 for(i = 0; i < nelem(irqbridge); i++)
272 intrunset(&irqbridge[i]);
273
274 /* disable all interrupts */
275 intr->lo.fiqmask = intr->hi.fiqmask = 0;
276 intr->lo.irqmask = intr->hi.irqmask = 0;
277 intr->lo.epmask = intr->hi.epmask = 0;
278 cpu->irqmask = 0;
279 coherence();
280
281 /* clear interrupts */
282 intr->lo.irq = intr->hi.irq = ~0;
283 cpu->irq = ~0;
284 coherence();
285
286 intrenable(Irqlo, IRQ0hisum, intrhi, nil, "hi");
287 intrenable(Irqlo, IRQ0bridge, intrbridge, nil, "bridge");
288
289 /* enable watchdog & access-error interrupts */
290 cpu->irqmask |= 1 << IRQcputimerwd | 1 << IRQaccesserr;
291 coherence();
292 }
293
294 static char *trapnames[PsrMask+1] = {
295 [ PsrMusr ] "user mode",
296 [ PsrMfiq ] "fiq interrupt",
297 [ PsrMirq ] "irq interrupt",
298 [ PsrMsvc ] "svc/swi exception",
299 [ PsrMabt ] "prefetch abort/data abort",
300 [ PsrMabt+1 ] "data abort",
301 [ PsrMund ] "undefined instruction",
302 [ PsrMsys ] "sys trap",
303 };
304
305 static char *
trapname(int psr)306 trapname(int psr)
307 {
308 char *s;
309
310 s = trapnames[psr & PsrMask];
311 if(s == nil)
312 s = "unknown trap number in psr";
313 return s;
314 }
315
316 /* this is quite helpful during mmu and cache debugging */
317 static void
ckfaultstuck(uintptr va)318 ckfaultstuck(uintptr va)
319 {
320 static int cnt, lastpid;
321 static uintptr lastva;
322
323 if (va == lastva && up->pid == lastpid) {
324 ++cnt;
325 if (cnt >= 2)
326 /* fault() isn't fixing the underlying cause */
327 panic("fault: %d consecutive faults for va %#p",
328 cnt+1, va);
329 } else {
330 cnt = 0;
331 lastva = va;
332 lastpid = up->pid;
333 }
334 }
335
336 /*
337 * called by trap to handle access faults
338 */
339 static void
faultarm(Ureg * ureg,uintptr va,int user,int read)340 faultarm(Ureg *ureg, uintptr va, int user, int read)
341 {
342 int n, insyscall;
343 char buf[ERRMAX];
344 static int cnt, lastpid;
345 static ulong lastva;
346
347 if(up == nil) {
348 dumpregs(ureg);
349 panic("fault: nil up in faultarm, accessing %#p", va);
350 }
351 insyscall = up->insyscall;
352 up->insyscall = 1;
353 if (Debug)
354 ckfaultstuck(va);
355
356 n = fault(va, read);
357 if(n < 0){
358 if(!user){
359 dumpregs(ureg);
360 panic("fault: kernel accessing %#p", va);
361 }
362 /* don't dump registers; programs suicide all the time */
363 snprint(buf, sizeof buf, "sys: trap: fault %s va=%#p",
364 read? "read": "write", va);
365 postnote(up, 1, buf, NDebug);
366 }
367 up->insyscall = insyscall;
368 }
369
370 /*
371 * returns 1 if the instruction writes memory, 0 otherwise
372 */
373 int
writetomem(ulong inst)374 writetomem(ulong inst)
375 {
376 /* swap always write memory */
377 if((inst & 0x0FC00000) == 0x01000000)
378 return 1;
379
380 /* loads and stores are distinguished by bit 20 */
381 if(inst & (1<<20))
382 return 0;
383
384 return 1;
385 }
386
387 void
trap(Ureg * ureg)388 trap(Ureg *ureg)
389 {
390 int user, x, rv, rem;
391 ulong inst;
392 u32int fsr;
393 uintptr va;
394 char buf[ERRMAX];
395
396 if(up != nil)
397 rem = (char*)ureg - up->kstack;
398 else
399 rem = (char*)ureg - ((char*)m + sizeof(Mach));
400 if(rem < 256) {
401 dumpstack();
402 panic("trap %d bytes remaining, up %#p ureg %#p at pc %#lux",
403 rem, up, ureg, ureg->pc);
404 }
405
406 user = (ureg->psr & PsrMask) == PsrMusr;
407 if(user){
408 up->dbgreg = ureg;
409 cycles(&up->kentry);
410 }
411
412 if(ureg->type == PsrMabt+1)
413 ureg->pc -= 8;
414 else
415 ureg->pc -= 4;
416
417 m->inclockintr = 0;
418 switch(ureg->type) {
419 default:
420 panic("unknown trap %ld", ureg->type);
421 break;
422 case PsrMirq:
423 ldrexvalid = 0;
424 // splflo(); /* allow fast interrupts */
425 intrs(ureg, Irqlo);
426 m->intr++;
427 break;
428 case PsrMabt: /* prefetch fault */
429 ldrexvalid = 0;
430 faultarm(ureg, ureg->pc, user, 1);
431 if(up->nnote == 0 &&
432 (*(u32int*)ureg->pc & ~(0xF<<28)) == 0x01200070)
433 postnote(up, 1, "sys: breakpoint", NDebug);
434 break;
435 case PsrMabt+1: /* data fault */
436 ldrexvalid = 0;
437 va = farget();
438 inst = *(ulong*)(ureg->pc);
439 fsr = fsrget() & 0xf;
440 if (probing && !user) {
441 if (trapped++ > 0)
442 panic("trap: recursive probe %#lux", va);
443 ureg->pc += 4; /* continue at next instruction */
444 break;
445 }
446 switch(fsr){
447 case 0x0:
448 panic("vector exception at %#lux", ureg->pc);
449 break;
450 case 0x1:
451 case 0x3:
452 if(user){
453 snprint(buf, sizeof buf,
454 "sys: alignment: pc %#lux va %#p\n",
455 ureg->pc, va);
456 postnote(up, 1, buf, NDebug);
457 } else
458 panic("kernel alignment: pc %#lux va %#p", ureg->pc, va);
459 break;
460 case 0x2:
461 panic("terminal exception at %#lux", ureg->pc);
462 break;
463 case 0x4:
464 case 0x6:
465 case 0x8:
466 case 0xa:
467 case 0xc:
468 case 0xe:
469 panic("external abort %#ux pc %#lux addr %#px",
470 fsr, ureg->pc, va);
471 break;
472 case 0x5: /* translation fault, no section entry */
473 case 0x7: /* translation fault, no page entry */
474 faultarm(ureg, va, user, !writetomem(inst));
475 break;
476 case 0x9:
477 case 0xb:
478 /* domain fault, accessing something we shouldn't */
479 if(user){
480 snprint(buf, sizeof buf,
481 "sys: access violation: pc %#lux va %#p\n",
482 ureg->pc, va);
483 postnote(up, 1, buf, NDebug);
484 } else
485 panic("kernel access violation: pc %#lux va %#p",
486 ureg->pc, va);
487 break;
488 case 0xd:
489 case 0xf:
490 /* permission error, copy on write or real permission error */
491 faultarm(ureg, va, user, !writetomem(inst));
492 break;
493 }
494 break;
495 case PsrMund: /* undefined instruction */
496 if(user){
497 if(seg(up, ureg->pc, 0) != nil &&
498 (*(u32int*)ureg->pc & ~(0xF<<28)) == 0x01200070)
499 postnote(up, 1, "sys: breakpoint", NDebug);
500 else{
501 /* look for floating point instructions to interpret */
502 x = spllo();
503 rv = fpiarm(ureg);
504 splx(x);
505 if(rv == 0){
506 ldrexvalid = 0;
507 snprint(buf, sizeof buf,
508 "undefined instruction: pc %#lux",
509 ureg->pc);
510 postnote(up, 1, buf, NDebug);
511 }
512 }
513 }else{
514 iprint("undefined instruction: pc %#lux inst %#ux\n",
515 ureg->pc, ((u32int*)ureg->pc)[-2]);
516 panic("undefined instruction");
517 }
518 break;
519 }
520 splhi();
521
522 /* delaysched set because we held a lock or because our quantum ended */
523 if(up && up->delaysched && m->inclockintr){
524 ldrexvalid = 0;
525 sched();
526 splhi();
527 }
528
529 if(user){
530 if(up->procctl || up->nnote)
531 notify(ureg);
532 kexit(ureg);
533 }
534 }
535
536 int
isvalidaddr(void * v)537 isvalidaddr(void *v)
538 {
539 return (uintptr)v >= KZERO;
540 }
541
542 void
dumplongs(char * msg,ulong * v,int n)543 dumplongs(char *msg, ulong *v, int n)
544 {
545 int i, l;
546
547 l = 0;
548 iprint("%s at %.8p: ", msg, v);
549 for(i=0; i<n; i++){
550 if(l >= 4){
551 iprint("\n %.8p: ", v);
552 l = 0;
553 }
554 if(isvalidaddr(v)){
555 iprint(" %.8lux", *v++);
556 l++;
557 }else{
558 iprint(" invalid");
559 break;
560 }
561 }
562 iprint("\n");
563 }
564
565 static void
dumpstackwithureg(Ureg * ureg)566 dumpstackwithureg(Ureg *ureg)
567 {
568 uintptr l, i, v, estack;
569 u32int *p;
570
571 iprint("ktrace /kernel/path %#.8lux %#.8lux %#.8lux # pc, sp, link\n",
572 ureg->pc, ureg->sp, ureg->r14);
573 delay(2000);
574 i = 0;
575 if(up != nil && (uintptr)&l <= (uintptr)up->kstack+KSTACK)
576 estack = (uintptr)up->kstack+KSTACK;
577 else if((uintptr)&l >= (uintptr)m->stack
578 && (uintptr)&l <= (uintptr)m+MACHSIZE)
579 estack = (uintptr)m+MACHSIZE;
580 else{
581 if(up != nil)
582 iprint("&up->kstack %#p &l %#p\n", up->kstack, &l);
583 else
584 iprint("&m %#p &l %#p\n", m, &l);
585 return;
586 }
587 for(l = (uintptr)&l; l < estack; l += sizeof(uintptr)){
588 v = *(uintptr*)l;
589 if(KTZERO < v && v < (uintptr)etext && !(v & 3)){
590 v -= sizeof(u32int); /* back up an instr */
591 p = (u32int*)v;
592 if((*p & 0x0f000000) == 0x0b000000){ /* BL instr? */
593 iprint("%#8.8lux=%#8.8lux ", l, v);
594 i++;
595 }
596 }
597 if(i == 4){
598 i = 0;
599 iprint("\n");
600 }
601 }
602 if(i)
603 iprint("\n");
604 }
605
606 /*
607 * Fill in enough of Ureg to get a stack trace, and call a function.
608 * Used by debugging interface rdb.
609 */
610 void
callwithureg(void (* fn)(Ureg *))611 callwithureg(void (*fn)(Ureg*))
612 {
613 Ureg ureg;
614
615 ureg.pc = getcallerpc(&fn);
616 ureg.sp = PTR2UINT(&fn);
617 fn(&ureg);
618 }
619
620 void
dumpstack(void)621 dumpstack(void)
622 {
623 callwithureg(dumpstackwithureg);
624 }
625
626 void
dumpregs(Ureg * ureg)627 dumpregs(Ureg* ureg)
628 {
629 int s;
630
631 if (ureg == nil) {
632 iprint("trap: no user process\n");
633 return;
634 }
635 s = splhi();
636 iprint("trap: %s", trapname(ureg->type));
637 if(ureg != nil && (ureg->psr & PsrMask) != PsrMsvc)
638 iprint(" in %s", trapname(ureg->psr));
639 iprint("\n");
640 iprint("psr %8.8lux type %2.2lux pc %8.8lux link %8.8lux\n",
641 ureg->psr, ureg->type, ureg->pc, ureg->link);
642 iprint("R14 %8.8lux R13 %8.8lux R12 %8.8lux R11 %8.8lux R10 %8.8lux\n",
643 ureg->r14, ureg->r13, ureg->r12, ureg->r11, ureg->r10);
644 iprint("R9 %8.8lux R8 %8.8lux R7 %8.8lux R6 %8.8lux R5 %8.8lux\n",
645 ureg->r9, ureg->r8, ureg->r7, ureg->r6, ureg->r5);
646 iprint("R4 %8.8lux R3 %8.8lux R2 %8.8lux R1 %8.8lux R0 %8.8lux\n",
647 ureg->r4, ureg->r3, ureg->r2, ureg->r1, ureg->r0);
648 iprint("stack is at %#p\n", ureg);
649 iprint("pc %#lux link %#lux\n", ureg->pc, ureg->link);
650
651 if(up)
652 iprint("user stack: %#p-%#p\n", up->kstack, up->kstack+KSTACK-4);
653 else
654 iprint("kernel stack: %8.8lux-%8.8lux\n",
655 (ulong)(m+1), (ulong)m+BY2PG-4);
656 dumplongs("stack", (ulong *)(ureg + 1), 16);
657 delay(2000);
658 dumpstack();
659 splx(s);
660 }
661
662 void
idlehands(void)663 idlehands(void)
664 {
665 extern void _idlehands(void);
666
667 _idlehands();
668 }
669
670 /* assumes that addr is already mapped suitable (e.g., by mmuidmap) */
671 vlong
probeaddr(uintptr addr)672 probeaddr(uintptr addr)
673 {
674 vlong v;
675 static Lock fltlck;
676
677 ilock(&fltlck);
678 trapped = 0;
679 probing = 1;
680 coherence();
681
682 v = *(ulong *)addr; /* this may cause a fault (okay under ilock) */
683 USED(probing);
684 coherence();
685
686 probing = 0;
687 coherence();
688 if (trapped)
689 v = -1;
690 iunlock(&fltlck);
691 return v;
692 }
693