Subversion Repositories planix.SVN

Rev

Rev 2 | Blame | Compare with Previous | Last modification | View Log | RSS feed

#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;

/*
 * 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];
        static int infault, printed;

        if (0 && infault && !printed) {
                printed = 1;
                print("fault: recursive fault (%d deep) pc %#p va %#p\n",
                        infault+1, ur->pc, ur->badvaddr);
        }
        infault++;
        if(waserror()){
                infault--;
                nexterror();
        }

        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, read) == 0){
                infault--;
                poperror();
                return;
        }

        infault--;
        poperror();

        if(tstbadvaddr(ur)) {
                print("fault: spurious badvaddr %#lux in %s at pc %#lux\n",
                        ur->badvaddr, up->text, ur->pc);/**/
                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;
        }

        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*/
}