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