#include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "ureg.h" #include "../port/error.h" #include "io.h" enum { Debug = 1, }; typedef struct Fault Fault; struct Fault { uintptr va; ulong pid; uintptr pc; int cnt; char *prog; int code; }; extern char *excname[]; static Fault lflt, maxflt; ulong* reg(Ureg *ur, int regno) { ulong *l; switch(regno) { case 31: return &ur->r31; case 30: return &ur->r30; case 29: return &ur->sp; default: l = &ur->r1; return &l[regno-1]; } } /* * Ask if the instruction at EPC could have cause this badvaddr */ int tstbadvaddr(Ureg *ur) { int rn; ulong iw, off, ea; iw = ur->pc; if(ur->cause & BD) iw += 4; if(seg(up, iw, 0) == 0) return 0; iw = *(ulong*)iw; /* print("iw: %#lux\n", iw); /**/ switch((iw>>26) & 0x3f) { default: return 1; case 0x20: /* LB */ case 0x24: /* LBU */ /* LD */ case 0x35: case 0x36: case 0x37: /* LDCz */ case 0x1A: /* LDL */ case 0x1B: /* LDR */ case 0x21: /* LH */ case 0x25: /* LHU */ case 0x30: /* LL */ case 0x34: /* LLD */ case 0x23: /* LW */ case 0x31: case 0x32: /* LWCz possible 0x33 */ case 0x27: /* LWU */ case 0x22: /* LWL */ case 0x26: /* LWR */ break; case 0x28: /* SB */ case 0x38: /* SC */ case 0x3C: /* SCD */ case 0x3D: case 0x3E: case 0x3F: /* SDCz */ case 0x2C: /* SDL */ case 0x2D: /* SDR */ case 0x29: /* SH */ case 0x2B: /* SW */ case 0x39: case 0x3A: /* SWCz */ case 0x2A: /* SWL */ case 0x2E: /* SWR */ break; } off = iw & 0xffff; if(off & 0x8000) off |= ~0xffff; rn = (iw>>21) & 0x1f; ea = *reg(ur, rn); if(rn == 0) ea = 0; ea += off; /* print("ea %#lux %#lux(R%d) bv %#lux pc %#lux\n", ea, off, rn, ur->badvaddr, ur->pc); /**/ if(ur->badvaddr == ea) return 0; return 1; } /* * we think we get consecutive page faults from unlucky combinations of * scheduling and stlb hashes, and they only happen with 16K pages. * however, we also get page faults while servicing the exact same fault. * more than 5 consecutive faults is unusual, now that we have a better * hash function. * * this can be helpful during mmu and cache debugging. */ static int ckfaultstuck(Ureg *ur, int read, int code) { uintptr pc, va; va = ur->badvaddr; pc = ur->pc; if (va != lflt.va || up->pid != lflt.pid || pc != lflt.pc || code != lflt.code) { /* at least one address or cause is different from last time */ lflt.cnt = 1; lflt.va = va; lflt.pid = up->pid; lflt.pc = pc; lflt.code = code; return 0; } ++lflt.cnt; if (lflt.cnt >= 1000) /* fixfault() isn't fixing underlying cause? */ panic("fault: %d consecutive faults for va %#p", lflt.cnt, va); if (lflt.cnt > maxflt.cnt) { maxflt.cnt = lflt.cnt; maxflt.va = va; maxflt.pid = up->pid; maxflt.pc = pc; kstrdup(&maxflt.prog, up->text); } /* we're servicing that fault now! */ /* adjust the threshold and program name to suit */ if (lflt.cnt < 5 || strncmp(up->text, "8l", 2) != 0) return 0; iprint("%d consecutive faults for va %#p at pc %#p in %s " "pid %ld\n", lflt.cnt, lflt.va, pc, up->text, lflt.pid); iprint("\t%s: %s%s r31 %#lux tlbvirt %#lux\n", excname[code], va == pc? "[instruction] ": "", (read? "read": "write"), ur->r31, tlbvirt()); return 0; } char * faultsprint(char *p, char *ep) { if (Debug) p = seprint(p, ep, "max consecutive faults %d for va %#p in %s\n", maxflt.cnt, maxflt.va, maxflt.prog); return p; } /* * find out fault address and type of access. * Call common fault handler. */ void faultmips(Ureg *ur, int user, int code) { int read; ulong addr; char *p, buf[ERRMAX]; iprint("faultmips\n"); addr = ur->badvaddr; addr &= ~(BY2PG-1); read = !(code==CTLBM || code==CTLBS); iprint("fault: %s code %d va %#p pc %#p r31 %#lux tlbvirt %#lux\n", up->text, code, ur->badvaddr, ur->pc, ur->r31, tlbvirt());/**/ if (Debug && ckfaultstuck(ur, read, code) || fault(addr, read) == 0) return; if(user) { p = "store"; if(read) p = "load"; snprint(buf, sizeof buf, "sys: trap: fault %s addr=%#lux r31=%#lux", p, ur->badvaddr, ur->r31); postnote(up, 1, buf, NDebug); return; } splhi(); serialoq = nil; print("kernel %s vaddr=%#lux\n", excname[code], ur->badvaddr); print("st=%#lux pc=%#lux r31=%#lux sp=%#lux\n", ur->status, ur->pc, ur->r31, ur->sp); dumpregs(ur); kmapdump(); panic("fault"); } /* * called in syscallfmt.c, sysfile.c, sysproc.c */ void validalign(uintptr addr, unsigned align) { /* * Plan 9 is a 32-bit O/S, and the hardware it runs on * does not usually have instructions which move 64-bit * quantities directly, synthesizing the operations * with 32-bit move instructions. Therefore, the compiler * (and hardware) usually only enforce 32-bit alignment, * if at all. * * Take this out if the architecture warrants it. */ if(align == sizeof(vlong)) align = sizeof(long); /* * Check align is a power of 2, then addr alignment. */ if((align != 0 && !(align & (align-1))) && !(addr & (align-1))) return; postnote(up, 1, "sys: odd address", NDebug); error(Ebadarg); /*NOTREACHED*/ }