248 lines
5 KiB
C
248 lines
5 KiB
C
#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 = 0,
|
|
};
|
|
|
|
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];
|
|
|
|
addr = ur->badvaddr;
|
|
addr &= ~(BY2PG-1);
|
|
|
|
read = !(code==CTLBM || code==CTLBS);
|
|
|
|
/* print("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, ur->pc, 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);
|
|
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*/
|
|
}
|