1 #include "u.h" 2 #include "../port/lib.h" 3 #include "mem.h" 4 #include "dat.h" 5 #include "fns.h" 6 #include "ureg.h" 7 #include "io.h" 8 #include "tos.h" 9 #include "../port/error.h" 10 11 static Lock vctllock; 12 static Vctl *vctl[256]; 13 14 void 15 hwintrinit(void) 16 { 17 i8259init(); 18 mpicenable(0, nil); /* 8259 interrupts are routed through MPIC intr 0 */ 19 } 20 21 static int 22 hwintrenable(Vctl *v) 23 { 24 int vec, irq; 25 26 irq = v->irq; 27 if(BUSTYPE(v->tbdf) == BusPCI) { /* MPIC? */ 28 if(irq > 15) { 29 print("intrenable: pci irq %d out of range\n", v->irq); 30 return -1; 31 } 32 vec = irq; 33 mpicenable(vec, v); 34 } 35 else { 36 if(irq > MaxIrqPIC) { 37 print("intrenable: irq %d out of range\n", v->irq); 38 return -1; 39 } 40 vec = irq+VectorPIC; 41 if(i8259enable(v) == -1) 42 return -1; 43 } 44 return vec; 45 } 46 47 static int 48 hwintrdisable(Vctl *v) 49 { 50 int vec, irq; 51 52 irq = v->irq; 53 if(BUSTYPE(v->tbdf) == BusPCI) { /* MPIC? */ 54 if(irq > 15) { 55 print("intrdisable: pci irq %d out of range\n", v->irq); 56 return -1; 57 } 58 vec = irq; 59 mpicdisable(vec); 60 } 61 else { 62 if(irq > MaxIrqPIC) { 63 print("intrdisable: irq %d out of range\n", v->irq); 64 return -1; 65 } 66 vec = irq+VectorPIC; 67 if(i8259disable(irq) == -1) 68 return -1; 69 } 70 return vec; 71 } 72 73 static int 74 hwvecno(int irq, int tbdf) 75 { 76 if(BUSTYPE(tbdf) == BusPCI) /* MPIC? */ 77 return irq; 78 else 79 return irq+VectorPIC; 80 } 81 82 void 83 intrenable(int irq, void (*f)(Ureg*, void*), void* a, int tbdf, char *name) 84 { 85 int vno; 86 Vctl *v; 87 88 if(f == nil){ 89 print("intrenable: nil handler for %d, tbdf 0x%uX for %s\n", 90 irq, tbdf, name); 91 return; 92 } 93 94 v = xalloc(sizeof(Vctl)); 95 v->isintr = 1; 96 v->irq = irq; 97 v->tbdf = tbdf; 98 v->f = f; 99 v->a = a; 100 strncpy(v->name, name, KNAMELEN-1); 101 v->name[KNAMELEN-1] = 0; 102 103 ilock(&vctllock); 104 vno = hwintrenable(v); 105 if(vno == -1){ 106 iunlock(&vctllock); 107 print("intrenable: couldn't enable irq %d, tbdf 0x%uX for %s\n", 108 irq, tbdf, v->name); 109 xfree(v); 110 return; 111 } 112 if(vctl[vno]){ 113 if(vctl[vno]->isr != v->isr || vctl[vno]->eoi != v->eoi) 114 panic("intrenable: handler: %s %s %#p %#p %#p %#p", 115 vctl[vno]->name, v->name, 116 vctl[vno]->isr, v->isr, vctl[vno]->eoi, v->eoi); 117 v->next = vctl[vno]; 118 } 119 vctl[vno] = v; 120 iunlock(&vctllock); 121 } 122 123 void 124 intrdisable(int irq, void (*f)(Ureg *, void *), void *a, int tbdf, char *name) 125 { 126 Vctl **pv, *v; 127 int vno; 128 129 vno = hwvecno(irq, tbdf); 130 ilock(&vctllock); 131 pv = &vctl[vno]; 132 while (*pv && 133 ((*pv)->irq != irq || (*pv)->tbdf != tbdf || (*pv)->f != f || (*pv)->a != a || 134 strcmp((*pv)->name, name))) 135 pv = &((*pv)->next); 136 assert(*pv); 137 138 v = *pv; 139 *pv = (*pv)->next; /* Link out the entry */ 140 141 if(vctl[vno] == nil) 142 hwintrdisable(v); 143 iunlock(&vctllock); 144 xfree(v); 145 } 146 147 void syscall(Ureg*); 148 void noted(Ureg*, ulong); 149 static void _dumpstack(Ureg*); 150 151 char *excname[] = 152 { 153 "reserved 0", 154 "system reset", 155 "machine check", 156 "data access", 157 "instruction access", 158 "external interrupt", 159 "alignment", 160 "program exception", 161 "floating-point unavailable", 162 "decrementer", 163 "reserved A", 164 "reserved B", 165 "system call", 166 "trace trap", 167 "floating point assist", 168 "reserved F", 169 "reserved 10", 170 "reserved 11", 171 "reserved 12", 172 "instruction address breakpoint", 173 "system management interrupt", 174 }; 175 176 char *fpcause[] = 177 { 178 "inexact operation", 179 "division by zero", 180 "underflow", 181 "overflow", 182 "invalid operation", 183 }; 184 char *fpexcname(Ureg*, ulong, char*); 185 #define FPEXPMASK 0xfff80300 /* Floating exception bits in fpscr */ 186 187 188 char *regname[]={ 189 "CAUSE", "SRR1", 190 "PC", "GOK", 191 "LR", "CR", 192 "XER", "CTR", 193 "R0", "R1", 194 "R2", "R3", 195 "R4", "R5", 196 "R6", "R7", 197 "R8", "R9", 198 "R10", "R11", 199 "R12", "R13", 200 "R14", "R15", 201 "R16", "R17", 202 "R18", "R19", 203 "R20", "R21", 204 "R22", "R23", 205 "R24", "R25", 206 "R26", "R27", 207 "R28", "R29", 208 "R30", "R31", 209 }; 210 211 void 212 trap(Ureg *ureg) 213 { 214 ulong dsisr; 215 int ecode, user; 216 char buf[ERRMAX], *s; 217 218 ecode = (ureg->cause >> 8) & 0xff; 219 user = (ureg->srr1 & MSR_PR) != 0; 220 if(user) 221 up->dbgreg = ureg; 222 223 if(ureg->status & MSR_RI == 0) 224 print("double fault?: ecode = %d\n", ecode); 225 226 switch(ecode) { 227 case CEI: 228 intr(ureg); 229 break; 230 case CDEC: 231 clockintr(ureg); 232 break; 233 case CSYSCALL: 234 if(!user) 235 panic("syscall in kernel: srr1 0x%4.4luX", ureg->srr1); 236 syscall(ureg); 237 return; /* syscall() calls notify itself, don't do it again */ 238 case CFPU: 239 if(!user || up == nil) { 240 dumpregs(ureg); 241 panic("floating point in kernel"); 242 } 243 switch(up->fpstate){ 244 case FPinit: 245 fprestore(&initfp); 246 up->fpstate = FPactive; 247 break; 248 case FPinactive: 249 fprestore(&up->fpsave); 250 up->fpstate = FPactive; 251 break; 252 default: 253 panic("fpstate"); 254 } 255 ureg->srr1 |= MSR_FP; 256 break; 257 case CISI: 258 faultpower(ureg, ureg->pc, 1); 259 break; 260 case CDSI: 261 dsisr = getdsisr(); 262 if(dsisr & BIT(6)) 263 faultpower(ureg, getdar(), 0); 264 else 265 faultpower(ureg, getdar(), 1); 266 break; 267 case CPROG: 268 if(ureg->status & (1<<19)) 269 s = "floating point exception"; 270 else if(ureg->status & (1<<18)) 271 s = "illegal instruction"; 272 else if(ureg->status & (1<<17)) 273 s = "privileged instruction"; 274 else 275 s = "undefined program exception"; 276 if(user){ 277 spllo(); 278 sprint(buf, "sys: trap: %s", s); 279 postnote(up, 1, buf, NDebug); 280 break; 281 } 282 dumpregs(ureg); 283 panic(s); 284 break; 285 default: 286 if(ecode < nelem(excname) && user){ 287 spllo(); 288 sprint(buf, "sys: trap: %s", excname[ecode]); 289 postnote(up, 1, buf, NDebug); 290 break; 291 } 292 dumpregs(ureg); 293 if(ecode < nelem(excname)) 294 panic("%s", excname[ecode]); 295 panic("unknown trap/intr: %d", ecode); 296 } 297 298 /* restoreureg must execute at high IPL */ 299 splhi(); 300 301 /* delaysched set because we held a lock or because our quantum ended */ 302 if(up && up->delaysched && ecode == CDEC){ 303 sched(); 304 splhi(); 305 } 306 307 if(user) { 308 notify(ureg); 309 if(up->fpstate == FPinactive) 310 ureg->srr1 &= ~MSR_FP; 311 kexit(ureg); 312 } 313 } 314 315 void 316 faultpower(Ureg *ureg, ulong addr, int read) 317 { 318 int user, insyscall, n; 319 char buf[ERRMAX]; 320 321 user = (ureg->srr1 & MSR_PR) != 0; 322 insyscall = up->insyscall; 323 up->insyscall = 1; 324 n = fault(addr, read); 325 if(n < 0){ 326 if(!user){ 327 dumpregs(ureg); 328 panic("fault: 0x%lux", addr); 329 } 330 sprint(buf, "sys: trap: fault %s addr=0x%lux", read? "read" : "write", addr); 331 postnote(up, 1, buf, NDebug); 332 } 333 up->insyscall = insyscall; 334 } 335 336 void 337 sethvec(int v, void (*r)(void)) 338 { 339 ulong *vp, pa, o; 340 341 vp = KADDR(v); 342 vp[0] = 0x7c1043a6; /* MOVW R0, SPR(SPRG0) */ 343 vp[1] = 0x7c0802a6; /* MOVW LR, R0 */ 344 vp[2] = 0x7c1243a6; /* MOVW R0, SPR(SPRG2) */ 345 pa = PADDR(r); 346 o = pa >> 25; 347 if(o != 0 && o != 0x7F){ 348 /* a branch too far */ 349 vp[3] = (15<<26)|(pa>>16); /* MOVW $r&~0xFFFF, R0 */ 350 vp[4] = (24<<26)|(pa&0xFFFF); /* OR $r&0xFFFF, R0 */ 351 vp[5] = 0x7c0803a6; /* MOVW R0, LR */ 352 vp[6] = 0x4e800021; /* BL (LR) */ 353 }else 354 vp[3] = (18<<26)|(pa&0x3FFFFFC)|3; /* bla */ 355 dcflush(vp, 8*sizeof(ulong)); 356 } 357 358 void 359 trapinit(void) 360 { 361 int i; 362 363 /* 364 * set all exceptions to trap 365 */ 366 for(i = 0; i < 0x2000; i += 0x100) 367 sethvec(i, trapvec); 368 369 putmsr(getmsr() & ~MSR_IP); 370 } 371 372 void 373 intr(Ureg *ureg) 374 { 375 int vno; 376 Vctl *ctl, *v; 377 378 vno = mpicintack(); 379 if(vno == 0) { /* 8259, wired through MPIC vec 0 */ 380 vno = i8259intack(); 381 mpiceoi(0); 382 } 383 384 if(vno > nelem(vctl) || (ctl = vctl[vno]) == 0) { 385 panic("spurious intr %d", vno); 386 return; 387 } 388 389 if(ctl->isr) 390 ctl->isr(vno); 391 for(v = ctl; v != nil; v = v->next){ 392 if(v->f) 393 v->f(ureg, v->a); 394 } 395 if(ctl->eoi) 396 ctl->eoi(vno); 397 398 if(up) 399 preempted(); 400 } 401 402 char* 403 fpexcname(Ureg *ur, ulong fpscr, char *buf) 404 { 405 int i; 406 char *s; 407 ulong fppc; 408 409 fppc = ur->pc; 410 s = 0; 411 fpscr >>= 3; /* trap enable bits */ 412 fpscr &= (fpscr>>22); /* anded with exceptions */ 413 for(i=0; i<5; i++) 414 if(fpscr & (1<<i)) 415 s = fpcause[i]; 416 if(s == 0) 417 return "no floating point exception"; 418 sprint(buf, "%s fppc=0x%lux", s, fppc); 419 return buf; 420 } 421 422 /* 423 * Fill in enough of Ureg to get a stack trace, and call a function. 424 * Used by debugging interface rdb. 425 */ 426 427 static void 428 getpcsp(ulong *pc, ulong *sp) 429 { 430 *pc = getcallerpc(&pc); 431 *sp = (ulong)&pc-4; 432 } 433 434 void 435 callwithureg(void (*fn)(Ureg*)) 436 { 437 Ureg ureg; 438 439 getpcsp((ulong*)&ureg.pc, (ulong*)&ureg.sp); 440 ureg.lr = getcallerpc(&fn); 441 fn(&ureg); 442 } 443 444 static void 445 _dumpstack(Ureg *ureg) 446 { 447 ulong l, sl, el, v; 448 int i; 449 450 l = (ulong)&l; 451 if(up == 0){ 452 el = (ulong)m+BY2PG; 453 sl = el-KSTACK; 454 } 455 else{ 456 sl = (ulong)up->kstack; 457 el = sl + KSTACK; 458 } 459 if(l > el || l < sl){ 460 el = (ulong)m+BY2PG; 461 sl = el-KSTACK; 462 } 463 if(l > el || l < sl) 464 return; 465 print("ktrace /kernel/path %.8lux %.8lux %.8lux\n", ureg->pc, ureg->sp, ureg->lr); 466 i = 0; 467 for(; l < el; l += 4){ 468 v = *(ulong*)l; 469 if(KTZERO < v && v < (ulong)etext){ 470 print("%.8lux=%.8lux ", l, v); 471 if(i++ == 4){ 472 print("\n"); 473 i = 0; 474 } 475 } 476 } 477 } 478 479 void 480 dumpstack(void) 481 { 482 callwithureg(_dumpstack); 483 } 484 485 void 486 dumpregs(Ureg *ur) 487 { 488 int i; 489 ulong *l; 490 491 if(up) { 492 print("registers for %s %ld\n", up->text, up->pid); 493 if(ur->srr1 & MSR_PR == 0) 494 if(ur->usp < (ulong)up->kstack || ur->usp > (ulong)up->kstack+KSTACK) 495 print("invalid stack ptr\n"); 496 } 497 else 498 print("registers for kernel\n"); 499 500 print("dsisr\t%.8lux\tdar\t%.8lux\n", getdsisr(), getdar()); 501 l = &ur->cause; 502 for(i=0; i<sizeof regname/sizeof(char*); i+=2, l+=2) 503 print("%s\t%.8lux\t%s\t%.8lux\n", regname[i], l[0], regname[i+1], l[1]); 504 } 505 506 static void 507 linkproc(void) 508 { 509 spllo(); 510 (*up->kpfun)(up->kparg); 511 pexit("", 0); 512 } 513 514 void 515 kprocchild(Proc *p, void (*func)(void*), void *arg) 516 { 517 p->sched.pc = (ulong)linkproc; 518 p->sched.sp = (ulong)p->kstack+KSTACK; 519 520 p->kpfun = func; 521 p->kparg = arg; 522 } 523 524 /* 525 * called in sysfile.c 526 */ 527 void 528 evenaddr(ulong addr) 529 { 530 if(addr & 3){ 531 postnote(up, 1, "sys: odd address", NDebug); 532 error(Ebadarg); 533 } 534 } 535 536 long 537 execregs(ulong entry, ulong ssize, ulong nargs) 538 { 539 ulong *sp; 540 Ureg *ureg; 541 542 sp = (ulong*)(USTKTOP - ssize); 543 *--sp = nargs; 544 545 ureg = up->dbgreg; 546 ureg->usp = (ulong)sp; 547 ureg->pc = entry; 548 ureg->srr1 &= ~MSR_FP; 549 return USTKTOP-sizeof(Tos); /* address of kernel/user shared data */ 550 } 551 552 void 553 forkchild(Proc *p, Ureg *ur) 554 { 555 Ureg *cur; 556 557 p->sched.sp = (ulong)p->kstack+KSTACK-UREGSIZE; 558 p->sched.pc = (ulong)forkret; 559 560 cur = (Ureg*)(p->sched.sp+2*BY2WD); 561 memmove(cur, ur, sizeof(Ureg)); 562 cur->r3 = 0; 563 564 /* Things from bottom of syscall we never got to execute */ 565 p->psstate = 0; 566 p->insyscall = 0; 567 } 568 569 ulong 570 userpc(void) 571 { 572 Ureg *ureg; 573 574 ureg = (Ureg*)up->dbgreg; 575 return ureg->pc; 576 } 577 578 579 /* This routine must save the values of registers the user is not 580 * permitted to write from devproc and then restore the saved values 581 * before returning 582 */ 583 void 584 setregisters(Ureg *xp, char *pureg, char *uva, int n) 585 { 586 ulong status; 587 588 status = xp->status; 589 memmove(pureg, uva, n); 590 xp->status = status; 591 } 592 593 /* Give enough context in the ureg to produce a kernel stack for 594 * a sleeping process 595 */ 596 void 597 setkernur(Ureg* ureg, Proc* p) 598 { 599 ureg->pc = p->sched.pc; 600 ureg->sp = p->sched.sp+4; 601 } 602 603 ulong 604 dbgpc(Proc *p) 605 { 606 Ureg *ureg; 607 608 ureg = p->dbgreg; 609 if(ureg == 0) 610 return 0; 611 612 return ureg->pc; 613 } 614 615 /* 616 * system calls 617 */ 618 #include "../port/systab.h" 619 620 /* TODO: make this trap a separate asm entry point, like on other RISC architectures */ 621 void 622 syscall(Ureg* ureg) 623 { 624 int i; 625 char *e; 626 long ret; 627 ulong sp, scallnr; 628 629 m->syscall++; 630 up->insyscall = 1; 631 up->pc = ureg->pc; 632 up->dbgreg = ureg; 633 634 scallnr = ureg->r3; 635 up->scallnr = ureg->r3; 636 spllo(); 637 638 sp = ureg->usp; 639 up->nerrlab = 0; 640 ret = -1; 641 if(!waserror()){ 642 if(scallnr >= nsyscall || systab[scallnr] == nil){ 643 pprint("bad sys call number %d pc %lux\n", scallnr, ureg->pc); 644 postnote(up, 1, "sys: bad sys call", NDebug); 645 error(Ebadarg); 646 } 647 648 if(sp<(USTKTOP-BY2PG) || sp>(USTKTOP-sizeof(Sargs)-BY2WD)) 649 validaddr(sp, sizeof(Sargs)+BY2WD, 0); 650 651 up->s = *((Sargs*)(sp+BY2WD)); 652 up->psstate = sysctab[scallnr]; 653 654 ret = systab[scallnr](up->s.args); 655 poperror(); 656 }else{ 657 /* failure: save the error buffer for errstr */ 658 e = up->syserrstr; 659 up->syserrstr = up->errstr; 660 up->errstr = e; 661 } 662 if(up->nerrlab){ 663 print("bad errstack [%uld]: %d extra\n", scallnr, up->nerrlab); 664 print("scall %s lr =%lux\n", sysctab[scallnr], ureg->lr); 665 for(i = 0; i < NERR; i++) 666 print("sp=%lux pc=%lux\n", up->errlab[i].sp, up->errlab[i].pc); 667 panic("error stack"); 668 } 669 670 up->insyscall = 0; 671 up->psstate = 0; 672 673 /* 674 * Put return value in frame. On the x86 the syscall is 675 * just another trap and the return value from syscall is 676 * ignored. On other machines the return value is put into 677 * the results register by caller of syscall. 678 */ 679 ureg->r3 = ret; 680 681 if(scallnr == NOTED) 682 noted(ureg, *(ulong*)(sp+BY2WD)); 683 684 /* restoreureg must execute at high IPL */ 685 splhi(); 686 if(scallnr!=RFORK) 687 notify(ureg); 688 if(up->fpstate == FPinactive) 689 ureg->srr1 &= ~MSR_FP; 690 } 691 692 /* 693 * Call user, if necessary, with note. 694 * Pass user the Ureg struct and the note on his stack. 695 */ 696 int 697 notify(Ureg* ur) 698 { 699 int l; 700 ulong s, sp; 701 Note *n; 702 703 if(up->procctl) 704 procctl(up); 705 if(up->nnote == 0) 706 return 0; 707 708 s = spllo(); 709 qlock(&up->debug); 710 up->notepending = 0; 711 n = &up->note[0]; 712 if(strncmp(n->msg, "sys:", 4) == 0){ 713 l = strlen(n->msg); 714 if(l > ERRMAX-15) /* " pc=0x12345678\0" */ 715 l = ERRMAX-15; 716 sprint(n->msg+l, " pc=0x%.8lux", ur->pc); 717 } 718 719 if(n->flag!=NUser && (up->notified || up->notify==0)){ 720 if(n->flag == NDebug) 721 pprint("suicide: %s\n", n->msg); 722 qunlock(&up->debug); 723 pexit(n->msg, n->flag!=NDebug); 724 } 725 726 if(up->notified) { 727 qunlock(&up->debug); 728 splhi(); 729 return 0; 730 } 731 732 if(!up->notify) { 733 qunlock(&up->debug); 734 pexit(n->msg, n->flag!=NDebug); 735 } 736 sp = ur->usp & ~(BY2V-1); 737 sp -= sizeof(Ureg); 738 739 if(!okaddr((ulong)up->notify, BY2WD, 0) || 740 !okaddr(sp-ERRMAX-4*BY2WD, sizeof(Ureg)+ERRMAX+4*BY2WD, 1)) { 741 pprint("suicide: bad address or sp in notify\n"); 742 qunlock(&up->debug); 743 pexit("Suicide", 0); 744 } 745 746 memmove((Ureg*)sp, ur, sizeof(Ureg)); 747 *(Ureg**)(sp-BY2WD) = up->ureg; /* word under Ureg is old up->ureg */ 748 up->ureg = (void*)sp; 749 sp -= BY2WD+ERRMAX; 750 memmove((char*)sp, up->note[0].msg, ERRMAX); 751 sp -= 3*BY2WD; 752 *(ulong*)(sp+2*BY2WD) = sp+3*BY2WD; /* arg 2 is string */ 753 ur->r1 = (long)up->ureg; /* arg 1 is ureg* */ 754 ((ulong*)sp)[1] = (ulong)up->ureg; /* arg 1 0(FP) is ureg* */ 755 ((ulong*)sp)[0] = 0; /* arg 0 is pc */ 756 ur->usp = sp; 757 ur->pc = (ulong)up->notify; 758 up->notified = 1; 759 up->nnote--; 760 memmove(&up->lastnote, &up->note[0], sizeof(Note)); 761 memmove(&up->note[0], &up->note[1], up->nnote*sizeof(Note)); 762 763 qunlock(&up->debug); 764 splx(s); 765 return 1; 766 } 767 768 769 /* 770 * Return user to state before notify() 771 */ 772 void 773 noted(Ureg* ureg, ulong arg0) 774 { 775 Ureg *nureg; 776 ulong oureg, sp; 777 778 qlock(&up->debug); 779 if(arg0!=NRSTR && !up->notified) { 780 qunlock(&up->debug); 781 pprint("call to noted() when not notified\n"); 782 pexit("Suicide", 0); 783 } 784 up->notified = 0; 785 786 nureg = up->ureg; /* pointer to user returned Ureg struct */ 787 788 /* sanity clause */ 789 oureg = (ulong)nureg; 790 if(!okaddr((ulong)oureg-BY2WD, BY2WD+sizeof(Ureg), 0)){ 791 pprint("bad ureg in noted or call to noted when not notified\n"); 792 qunlock(&up->debug); 793 pexit("Suicide", 0); 794 } 795 796 memmove(ureg, nureg, sizeof(Ureg)); 797 798 switch(arg0){ 799 case NCONT: 800 case NRSTR: 801 if(!okaddr(nureg->pc, 1, 0) || !okaddr(nureg->usp, BY2WD, 0)){ 802 pprint("suicide: trap in noted\n"); 803 qunlock(&up->debug); 804 pexit("Suicide", 0); 805 } 806 up->ureg = (Ureg*)(*(ulong*)(oureg-BY2WD)); 807 qunlock(&up->debug); 808 break; 809 810 case NSAVE: 811 if(!okaddr(nureg->pc, BY2WD, 0) 812 || !okaddr(nureg->usp, BY2WD, 0)){ 813 pprint("suicide: trap in noted\n"); 814 qunlock(&up->debug); 815 pexit("Suicide", 0); 816 } 817 qunlock(&up->debug); 818 sp = oureg-4*BY2WD-ERRMAX; 819 splhi(); 820 ureg->sp = sp; 821 ((ulong*)sp)[1] = oureg; /* arg 1 0(FP) is ureg* */ 822 ((ulong*)sp)[0] = 0; /* arg 0 is pc */ 823 break; 824 825 default: 826 pprint("unknown noted arg 0x%lux\n", arg0); 827 up->lastnote.flag = NDebug; 828 /* fall through */ 829 830 case NDFLT: 831 if(up->lastnote.flag == NDebug) 832 pprint("suicide: %s\n", up->lastnote.msg); 833 qunlock(&up->debug); 834 pexit(up->lastnote.msg, up->lastnote.flag!=NDebug); 835 } 836 } 837