2 * arm mpcore generic interrupt controller (gic) v1
3 * traps, exceptions, interrupts, system calls.
5 * there are two pieces: the interrupt distributor and the cpu interface.
7 * memset or memmove on any of the distributor registers generates an
8 * exception like this one:
9 * panic: external abort 0x28 pc 0xc048bf68 addr 0x50041800
11 * we use l1 and l2 cache ops to force vectors to be visible everywhere.
13 * apparently irqs 0—15 (SGIs) are always enabled.
16 #include "../port/lib.h"
20 #include "../port/error.h"
25 #define ISSGI(irq) ((uint)(irq) < Nsgi)
30 Nvec = 8, /* # of vectors at start of lexception.s */
31 Bi2long = BI2BY * sizeof(long),
33 Nsgi = 16, /* software-generated (inter-processor) intrs */
34 Nppi = 32, /* sgis + other private peripheral intrs */
37 typedef struct Intrcpuregs Intrcpuregs;
38 typedef struct Intrdistregs Intrdistregs;
41 * almost this entire register set is buggered.
42 * the distributor is supposed to be per-system, not per-cpu,
43 * yet some registers are banked per-cpu, as marked.
45 struct Intrdistregs { /* distributor */
49 uchar _pad0[0x80 - 0xc];
51 /* botch: *[0] are banked per-cpu from here */
53 ulong grp[32]; /* in group 1 (non-secure) */
54 ulong setena[32]; /* forward to cpu interfaces */
58 ulong setact[32]; /* active? */
60 /* botch: *[0] are banked per-cpu until here */
62 uchar pri[1020]; /* botch: pri[0] — pri[7] are banked per-cpu */
64 /* botch: targ[0] through targ[7] are banked per-cpu and RO */
65 uchar targ[1020]; /* byte bit maps: cpu targets indexed by intr */
67 /* botch: cfg[1] is banked per-cpu */
68 ulong cfg[64]; /* bit pairs: edge? 1-N? */
70 ulong nsac[64]; /* bit pairs (v2 only) */
72 /* software-generated intrs (a.k.a. sgi) */
73 ulong swgen; /* intr targets */
74 uchar _pad2[0xf10 - 0xf04];
75 uchar clrsgipend[16]; /* bit map (v2 only) */
76 uchar setsgipend[16]; /* bit map (v2 only) */
90 Edge = 1<<1, /* edge-, not level-sensitive */
92 To1 = 1<<0, /* vs. to all */
100 /* each cpu sees its own registers at the same base address (soc.intr) */
105 ulong binpt; /* group pri vs subpri split */
111 /* aliased regs (secure, for group 1) */
113 ulong aliack; /* (v2 only) */
114 ulong aliend; /* (v2 only) */
115 ulong alihipripend; /* (v2 only) */
117 uchar _pad0[0xd0 - 0x2c];
118 ulong actpri[4]; /* (v2 only) */
119 ulong nsactpri[4]; /* (v2 only) */
121 uchar _pad0[0xfc - 0xf0];
124 uchar _pad0[0x1000 - 0x100];
125 ulong deact; /* wo (v2 only) */
131 Eoinodeact = 1<<9, /* (v2 only) */
133 /* (ali) ack/end/hipriend/deact bits */
140 Archversmask = MASK(4),
143 typedef struct Vctl Vctl;
144 typedef struct Vctl {
145 Vctl* next; /* handlers on this vector */
146 char *name; /* of driver, xallocated */
147 void (*f)(Ureg*, void*); /* handler to call */
148 void* a; /* argument to call it with */
151 static Lock vctllock;
152 static Vctl* vctl[Nirqs];
155 * Layout at virtual address 0.
157 typedef struct Vpage0 {
158 void (*vectors[Nvec])(void);
164 Ntimevec = 20 /* number of time buckets for each intr */
166 ulong intrtimes[Nirqs][Ntimevec];
169 uvlong ninterruptticks;
172 static ulong shadena[32]; /* copy of enable bits, saved by intcmaskall */
173 static Lock distlock, nintrlock;
175 extern int notify(Ureg*);
177 static void dumpstackwithureg(Ureg *ureg);
180 printrs(int base, ulong word)
184 for (bit = 0; word; bit++, word >>= 1)
186 iprint(" %d", base + bit);
190 dumpintrs(char *what, ulong *bits)
194 Intrdistregs *idp = (Intrdistregs *)soc.intrdist;
199 for (i = 0; i < nelem(idp->setpend); i++) {
207 printrs(i * Bi2long, word);
211 iprint("%s none", what);
218 Intrdistregs *idp = (Intrdistregs *)soc.intrdist;
220 iprint("\ncpu%d gic regs:\n", m->machno);
221 dumpintrs("group 1", idp->grp);
222 dumpintrs("enabled", idp->setena);
223 dumpintrs("pending", idp->setpend);
224 dumpintrs("active ", idp->setact);
228 * keep histogram of interrupt service times
231 intrtime(Mach*, int vno)
237 diff = x - m->perf.intrts;
240 m->perf.inintr += diff;
241 if(up == nil && m->perf.inidle > diff)
242 m->perf.inidle -= diff;
245 return; /* don't divide by zero */
246 diff /= m->cpumhz*100; /* quantum = 100µsec */
249 if ((uint)vno >= Nirqs)
251 intrtimes[vno][diff]++;
255 intack(Intrcpuregs *icp)
257 return icp->ack & Intrmask;
261 intdismiss(Intrcpuregs *icp, ulong ack)
270 Intrdistregs *idp = (Intrdistregs *)soc.intrdist;
272 return idp->setena[irq / Bi2long] & (1 << (irq % Bi2long));
278 Intrdistregs *idp = (Intrdistregs *)soc.intrdist;
281 idp->setena[irq / Bi2long] = 1 << (irq % Bi2long);
288 Intrdistregs *idp = (Intrdistregs *)soc.intrdist;
291 idp->clrena[irq / Bi2long] = 1 << (irq % Bi2long);
296 intcmaskall(Intrdistregs *idp) /* mask all intrs for all cpus */
300 for (i = 0; i < nelem(idp->setena); i++)
301 shadena[i] = idp->setena[i];
302 for (i = 0; i < nelem(idp->clrena); i++)
308 intcunmaskall(Intrdistregs *idp) /* unused */
312 for (i = 0; i < nelem(idp->setena); i++)
313 idp->setena[i] = shadena[i];
318 permintrs(Intrdistregs *idp, int base, int r)
322 idp->clrena[r] = ~0; /* disable all */
324 perms = idp->clrena[r];
326 iprint("perm intrs:");
327 printrs(base, perms);
334 intrcfg(Intrdistregs *idp)
339 /* set up all interrupts as level-sensitive, to one cpu (0) */
341 for (i = 0; i < Bi2long; i += 2)
342 pat |= (Level | To1) << i;
344 if (m->machno == 0) { /* system-wide & cpu0 cfg */
345 for (i = 0; i < nelem(idp->grp); i++)
346 idp->grp[i] = 0; /* secure */
347 for (i = 0; i < nelem(idp->pri); i++)
348 idp->pri[i] = 0; /* highest priority */
349 /* set up all interrupts as level-sensitive, to one cpu (0) */
350 for (i = 0; i < nelem(idp->cfg); i++)
352 /* first Nppi are read-only for SGIs and PPIs */
353 cpumask = 1<<0; /* just cpu 0 */
354 navailcpus = getncpus();
355 for (i = Nppi; i < sizeof idp->targ; i++)
356 idp->targ[i] = cpumask;
360 for (i = 0; i < nelem(idp->clrena); i++) {
361 // permintrs(idp, i * Bi2long, i);
362 idp->clrpend[i] = idp->clract[i] = idp->clrena[i] = ~0;
364 } else { /* per-cpu config */
365 idp->grp[0] = 0; /* secure */
366 for (i = 0; i < 8; i++)
367 idp->pri[i] = 0; /* highest priority */
368 /* idp->targ[0 through Nppi-1] are supposed to be read-only */
369 for (i = 0; i < Nppi; i++)
370 idp->targ[i] = 1<<m->machno;
374 // permintrs(idp, i * Bi2long, i);
375 idp->clrpend[0] = idp->clract[0] = idp->clrena[0] = ~0;
376 /* on cpu1, irq Extpmuirq (118) is always pending here */
382 intrto(int cpu, int irq)
384 Intrdistregs *idp = (Intrdistregs *)soc.intrdist;
386 /* first Nppi are read-only for SGIs and the like */
388 idp->targ[irq] = 1 << cpu;
393 intrsto(int cpu) /* unused */
396 Intrdistregs *idp = (Intrdistregs *)soc.intrdist;
398 /* first Nppi are read-only for SGIs and the like */
399 for (i = Nppi; i < sizeof idp->targ; i++)
407 Intrdistregs *idp = (Intrdistregs *)soc.intrdist;
410 idp->swgen = Totargets | 1 << (cpu + 16) | m->machno;
415 * set up for exceptions
421 Intrdistregs *idp = (Intrdistregs *)soc.intrdist;
422 Intrcpuregs *icp = (Intrcpuregs *)soc.intr;
424 enum { Vecsize = sizeof vpage0->vectors + sizeof vpage0->vtable, };
427 * set up the exception vectors, high and low.
429 * we can't use cache ops on HVECTORS address, since they
430 * work on virtual addresses, and only those that have a
431 * physical address == PADDR(virtual).
433 if (m->machno == 0) {
434 vpage0 = (Vpage0*)HVECTORS;
435 memmove(vpage0->vectors, vectors, sizeof(vpage0->vectors));
436 memmove(vpage0->vtable, vtable, sizeof(vpage0->vtable));
438 vpage0 = (Vpage0*)KADDR(0);
439 memmove(vpage0->vectors, vectors, sizeof(vpage0->vectors));
440 memmove(vpage0->vtable, vtable, sizeof(vpage0->vtable));
442 allcache->wbse(vpage0, Vecsize);
447 * set up the stack pointers for the exception modes for this cpu.
448 * they point to small `save areas' in Mach, not actual stacks.
450 s = splhi(); /* make these modes ignore intrs too */
451 setr13(PsrMfiq, m->sfiq);
452 setr13(PsrMirq, m->sirq);
453 setr13(PsrMmon, m->smon);
454 setr13(PsrMabt, m->sabt);
455 setr13(PsrMund, m->sund);
456 setr13(PsrMsys, m->ssys);
459 assert((idp->distid & MASK(12)) == 0x43b); /* made by arm */
460 assert((icp->ifid & MASK(12)) == 0x43b); /* made by arm */
467 intrcfg(idp); /* some per-cpu cfg here */
470 icp->primask = (uchar)~0; /* let all priorities through */
473 idp->ctl = Forw2cpuif;
481 intcmaskall((Intrdistregs *)soc.intrdist);
486 intrcpushutdown(void)
488 Intrcpuregs *icp = (Intrcpuregs *)soc.intr;
491 icp->primask = 0; /* let no priorities through */
495 /* called from cpu0 after other cpus are shutdown */
499 Intrdistregs *idp = (Intrdistregs *)soc.intrdist;
507 * enable an irq interrupt
508 * note that the same private interrupt may be enabled on multiple cpus
511 irqenable(uint irq, void (*f)(Ureg*, void*), void* a, char *name)
515 if(irq >= nelem(vctl))
516 panic("irqenable irq %d", irq);
519 iprint("irqenable for %d %s called too early\n", irq, name);
523 * if in use, could be a private interrupt on a secondary cpu,
524 * so don't add anything to the vector chain. irqs should
525 * otherwise be one-to-one with devices.
527 if(!ISSGI(irq) && irqinuse(irq)) {
529 if (vctl[irq] == nil) {
531 panic("non-sgi irq %d in use yet no Vctl allocated", irq);
535 /* could be 1st use of this irq or could be an sgi (always in use) */
536 else if (vctl[irq] == nil) {
537 v = malloc(sizeof(Vctl));
539 panic("irqenable: malloc Vctl");
542 v->name = malloc(strlen(name)+1);
544 panic("irqenable: malloc name");
545 strcpy(v->name, name);
548 if (vctl[irq] != nil) {
549 /* allocation race: someone else did it first */
563 * disable an irq interrupt
566 irqdisable(uint irq, void (*f)(Ureg*, void*), void* a, char *name)
570 if(irq >= nelem(vctl))
571 panic("irqdisable irq %d", irq);
574 for(vp = &vctl[irq]; v = *vp; vp = &v->next)
575 if (v->f == f && v->a == a && strcmp(v->name, name) == 0){
576 print("irqdisable: remove %s\n", name);
584 print("irqdisable: irq %d, name %s not enabled\n", irq, name);
585 if(vctl[irq] == nil){
586 print("irqdisable: clear icmr bit %d\n", irq);
595 * called by trap to handle access faults
598 faultarm(Ureg *ureg, uintptr va, int user, int read)
603 dumpstackwithureg(ureg);
604 panic("faultarm: cpu%d: nil up, %sing %#p at %#p",
605 m->machno, (read? "read": "writ"), va, ureg->pc);
607 insyscall = up->insyscall;
610 n = fault(va, read); /* goes spllo */
616 dumpstackwithureg(ureg);
617 panic("fault: cpu%d: kernel %sing %#p at %#p",
618 m->machno, read? "read": "writ", va, ureg->pc);
620 /* don't dump registers; programs suicide all the time */
621 snprint(buf, sizeof buf, "sys: trap: fault %s va=%#p",
622 read? "read": "write", va);
623 postnote(up, 1, buf, NDebug);
625 up->insyscall = insyscall;
629 * called by trap to handle interrupts.
630 * returns true iff a clock interrupt, thus maybe reschedule.
636 uint irqno, handled, t, ticks;
637 Intrcpuregs *icp = (Intrcpuregs *)soc.intr;
643 irqno = ack & Intrmask;
645 if (irqno >= nelem(vctl)) {
646 iprint("trap: irq %d >= # vectors (%d)\n", irqno, nelem(vctl));
647 intdismiss(icp, ack);
651 if (irqno == Loctmrirq) /* this is a clock intr? */
652 m->inclockintr++; /* yes, count nesting */
653 if(m->machno && m->inclockintr > 1) {
654 iprint("cpu%d: nested clock intrs\n", m->machno);
656 intdismiss(icp, ack);
660 for(v = vctl[irqno]; v != nil; v = v->next)
663 panic("trap: pl0 before trap handler for %s",
667 panic("trap: %s lowered pl", v->name);
668 // splhi(); /* in case v->f lowered pl */
673 iprint("cpu%d: ignoring spurious interrupt\n", m->machno);
676 iprint("cpu%d: unexpected interrupt %d, now masked\n",
680 if (0) { /* left over from another port? */
684 ninterruptticks += ticks-t;
686 ninterruptticks += t-ticks;
690 clockintr = m->inclockintr == 1;
691 if (irqno == Loctmrirq)
694 intdismiss(icp, ack);
700 * returns 1 if the instruction writes memory, 0 otherwise
703 writetomem(ulong inst)
705 /* swap always write memory */
706 if((inst & 0x0FC00000) == 0x01000000)
709 /* loads and stores are distinguished by bit 20 */
717 datafault(Ureg *ureg, int user)
725 if (m->probing && !user) {
726 if (m->trapped++ > 0) {
727 dumpstackwithureg(ureg);
728 panic("trap: recursive probe %#lux", va);
730 ureg->pc += 4; /* continue after faulting instr'n */
734 inst = *(ulong*)(ureg->pc);
735 /* bits 12 and 10 have to be concatenated with status */
737 fsr = (x>>7) & 0x20 | (x>>6) & 0x10 | x & 0xf;
740 case 0xa: /* ? was under external abort */
741 panic("unknown data fault, 6b fsr %#lux", fsr);
744 panic("vector exception at %#lux", ureg->pc);
746 case 0x1: /* alignment fault */
747 case 0x3: /* access flag fault (section) */
751 snprint(buf, sizeof buf,
752 "sys: alignment: pc %#lux va %#p\n",
754 postnote(up, 1, buf, NDebug);
756 dumpstackwithureg(ureg);
757 panic("kernel alignment: pc %#lux va %#p", ureg->pc, va);
761 panic("terminal exception at %#lux", ureg->pc);
763 case 0x4: /* icache maint fault */
764 case 0x6: /* access flag fault (page) */
765 case 0x8: /* precise external abort, non-xlat'n */
767 case 0x16: /* imprecise ext. abort, non-xlt'n */
769 panic("external non-translation abort %#lux pc %#lux addr %#p",
772 case 0xc: /* l1 translation, precise ext. abort */
774 case 0xe: /* l2 translation, precise ext. abort */
776 panic("external translation abort %#lux pc %#lux addr %#p",
779 case 0x1c: /* l1 translation, precise parity err */
780 case 0x1e: /* l2 translation, precise parity err */
781 case 0x18: /* imprecise parity or ecc err */
782 panic("translation parity error %#lux pc %#lux addr %#p",
785 case 0x5: /* translation fault, no section entry */
786 case 0x7: /* translation fault, no page entry */
787 faultarm(ureg, va, user, !writetomem(inst));
791 /* domain fault, accessing something we shouldn't */
795 snprint(buf, sizeof buf,
796 "sys: access violation: pc %#lux va %#p\n",
798 postnote(up, 1, buf, NDebug);
800 panic("kernel access violation: pc %#lux va %#p",
805 /* permission error, copy on write or real permission error */
806 faultarm(ureg, va, user, !writetomem(inst));
812 * here on all exceptions other than syscall (SWI) and reset
817 int clockintr, user, rem;
818 uintptr va, ifar, ifsr;
820 splhi(); /* paranoia */
822 rem = ((char*)ureg)-up->kstack;
824 rem = ((char*)ureg)-((char*)m+sizeof(Mach));
826 iprint("trap: %d stack bytes left, up %#p ureg %#p m %#p cpu%d at pc %#lux\n",
827 rem, up, ureg, m, m->machno, ureg->pc);
828 dumpstackwithureg(ureg);
829 panic("trap: %d stack bytes left, up %#p ureg %#p at pc %#lux",
830 rem, up, ureg, ureg->pc);
833 m->perf.intrts = perfticks();
834 user = (ureg->psr & PsrMask) == PsrMusr;
841 * All interrupts/exceptions should be resumed at ureg->pc-4,
842 * except for Data Abort which resumes at ureg->pc-8.
844 if(ureg->type == (PsrMabt+1))
849 clockintr = 0; /* if set, may call sched() before return */
852 panic("unknown trap; type %#lux, psr mode %#lux", ureg->type,
853 ureg->psr & PsrMask);
857 clockintr = irq(ureg);
858 if(0 && up && !clockintr)
859 preempted(); /* this causes spurious suicides */
861 case PsrMabt: /* prefetch (instruction) fault */
863 ifsr = cprdsc(0, CpFSR, 0, CpIFSR);
864 ifsr = (ifsr>>7) & 0x8 | ifsr & 0x7;
866 case 0x02: /* instruction debug event (BKPT) */
868 postnote(up, 1, "sys: breakpoint", NDebug);
870 iprint("kernel bkpt: pc %#lux inst %#ux\n",
872 panic("kernel bkpt");
876 ifar = cprdsc(0, CpFAR, 0, CpIFAR);
878 iprint("trap: cpu%d: i-fault va %#p != ifar %#p\n",
879 m->machno, va, ifar);
880 faultarm(ureg, va, user, 1);
884 case PsrMabt+1: /* data fault */
885 datafault(ureg, user);
887 case PsrMund: /* undefined instruction */
890 iprint("rounding fault pc %#lux down to word\n",
895 iprint("mathemu: cpu%d fpon %d instr %#8.8lux at %#p\n",
896 m->machno, m->fpon, *(ulong *)ureg->pc,
898 dumpstackwithureg(ureg);
899 panic("cpu%d: undefined instruction: pc %#lux inst %#ux",
900 m->machno, ureg->pc, ((u32int*)ureg->pc)[0]);
901 } else if(seg(up, ureg->pc, 0) != nil &&
902 *(u32int*)ureg->pc == 0xD1200070)
903 postnote(up, 1, "sys: breakpoint", NDebug);
904 else if(fpuemu(ureg) == 0){ /* didn't find any FP instrs? */
907 snprint(buf, sizeof buf,
908 "undefined instruction: pc %#lux instr %#8.8lux\n",
909 ureg->pc, *(ulong *)ureg->pc);
910 postnote(up, 1, buf, NDebug);
916 /* delaysched set because we held a lock or because our quantum ended */
917 if(up && up->delaysched && clockintr){
918 sched(); /* can cause more traps */
923 if(up->procctl || up->nnote)
930 * Fill in enough of Ureg to get a stack trace, and call a function.
931 * Used by debugging interface rdb.
934 callwithureg(void (*fn)(Ureg*))
938 memset(&ureg, 0, sizeof ureg);
939 ureg.pc = getcallerpc(&fn);
940 ureg.sp = PTR2UINT(&fn);
945 dumpstackwithureg(Ureg *ureg)
948 uintptr l, v, i, estack;
952 if((s = getconf("*nodumpstack")) != nil && strcmp(s, "0") != 0){
953 iprint("dumpstack disabled\n");
957 iprint("dumpstack\n");
960 x += iprint("ktrace /kernel/path %#.8lux %#.8lux %#.8lux # pc, sp, link\n",
961 ureg->pc, ureg->sp, ureg->r14);
965 && (uintptr)&l >= (uintptr)up->kstack
966 && (uintptr)&l <= (uintptr)up->kstack+KSTACK)
967 estack = (uintptr)up->kstack+KSTACK;
968 else if((uintptr)&l >= (uintptr)m->stack
969 && (uintptr)&l <= (uintptr)m+MACHSIZE)
970 estack = (uintptr)m+MACHSIZE;
973 x += iprint("estackx %p\n", estack);
975 for(l = (uintptr)&l; l < estack; l += sizeof(uintptr)){
977 if((KTZERO < v && v < (uintptr)etext) || estack-l < 32){
978 x += iprint("%.8p ", v);
996 callwithureg(dumpstackwithureg);
1000 * dump system control coprocessor registers
1005 iprint("0:\t%#8.8ux id\n", cpidget());
1006 iprint("\t%8.8#ux ct\n", cpctget());
1007 iprint("1:\t%#8.8ux control\n", controlget());
1008 iprint("2:\t%#8.8ux ttb\n", ttbget());
1009 iprint("3:\t%#8.8ux dac\n", dacget());
1010 iprint("4:\t(reserved)\n");
1011 iprint("5:\t%#8.8ux fsr\n", fsrget());
1012 iprint("6:\t%#8.8ux far\n", farget());
1013 iprint("7:\twrite-only cache\n");
1014 iprint("8:\twrite-only tlb\n");
1015 iprint("13:\t%#8.8ux pid\n", pidget());
1020 * dump general registers
1026 iprint("cpu%d: registers for %s %lud\n",
1027 m->machno, up->text, up->pid);
1029 iprint("cpu%d: registers for kernel\n", m->machno);
1032 iprint("%#8.8lux\tr0\n", ureg->r0);
1033 iprint("%#8.8lux\tr1\n", ureg->r1);
1034 iprint("%#8.8lux\tr2\n", ureg->r2);
1036 iprint("%#8.8lux\tr3\n", ureg->r3);
1037 iprint("%#8.8lux\tr4\n", ureg->r4);
1038 iprint("%#8.8lux\tr5\n", ureg->r5);
1040 iprint("%#8.8lux\tr6\n", ureg->r6);
1041 iprint("%#8.8lux\tr7\n", ureg->r7);
1042 iprint("%#8.8lux\tr8\n", ureg->r8);
1044 iprint("%#8.8lux\tr9 (up)\n", ureg->r9);
1045 iprint("%#8.8lux\tr10 (m)\n", ureg->r10);
1046 iprint("%#8.8lux\tr11 (loader temporary)\n", ureg->r11);
1047 iprint("%#8.8lux\tr12 (SB)\n", ureg->r12);
1049 iprint("%#8.8lux\tr13 (sp)\n", ureg->r13);
1050 iprint("%#8.8lux\tr14 (link)\n", ureg->r14);
1051 iprint("%#8.8lux\tr15 (pc)\n", ureg->pc);
1053 iprint("%10.10lud\ttype\n", ureg->type);
1054 iprint("%#8.8lux\tpsr\n", ureg->psr);
1059 dumpregs(Ureg* ureg)
1066 probeaddr(uintptr addr)
1070 ilock(&m->probelock);
1075 v = *(ulong *)addr; /* this may cause a fault */
1081 iunlock(&m->probelock);