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