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 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 97 intrfmtcounts(char *s, char *se) 98 { 99 USED(s, se); 100 } 101 102 static void 103 dumpcounts(void) 104 { 105 } 106 107 void 108 intrclear(int sort, int v) 109 { 110 *irqs[sort].irq = ~(1 << v); 111 } 112 113 void 114 intrmask(int sort, int v) 115 { 116 *irqs[sort].irqmask &= ~(1 << v); 117 } 118 119 void 120 intrunmask(int sort, int v) 121 { 122 *irqs[sort].irqmask |= 1 << v; 123 } 124 125 static 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 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 153 intrunset(Handler *h) 154 { 155 h->r = nil; 156 h->a = nil; 157 h->name[0] = 0; 158 } 159 160 void 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 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 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 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 225 intrhi(Ureg *ureg, void*) 226 { 227 intrs(ureg, Irqhi); 228 } 229 230 void 231 intrbridge(Ureg *ureg, void*) 232 { 233 intrs(ureg, Irqbridge); 234 intrclear(Irqlo, IRQ0bridge); 235 } 236 237 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 * 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 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 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 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 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 537 isvalidaddr(void *v) 538 { 539 return (uintptr)v >= KZERO; 540 } 541 542 void 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 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 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 621 dumpstack(void) 622 { 623 callwithureg(dumpstackwithureg); 624 } 625 626 void 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 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 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