Subversion Repositories planix.SVN

Rev

Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
 * traps, exceptions, interrupts, system calls.
 */
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
#include "ureg.h"
#include "../port/error.h"

#include "arm.h"

#define INTREGS         (VIRTIO+0xB200)

typedef struct Intregs Intregs;
typedef struct Vctl Vctl;

enum {
        Debug = 0,

        Nvec = 8,               /* # of vectors at start of lexception.s */
        Fiqenable = 1<<7,
};

/*
 *   Layout at virtual address KZERO (double mapped at HVECTORS).
 */
typedef struct Vpage0 {
        void    (*vectors[Nvec])(void);
        u32int  vtable[Nvec];
} Vpage0;

/*
 * interrupt control registers
 */
struct Intregs {
        u32int  ARMpending;
        u32int  GPUpending[2];
        u32int  FIQctl;
        u32int  GPUenable[2];
        u32int  ARMenable;
        u32int  GPUdisable[2];
        u32int  ARMdisable;
};

struct Vctl {
        Vctl    *next;
        int     irq;
        u32int  *reg;
        u32int  mask;
        void    (*f)(Ureg*, void*);
        void    *a;
};

static Vctl *vctl, *vfiq;

static char *trapnames[PsrMask+1] = {
        [ PsrMusr ] "user mode",
        [ PsrMfiq ] "fiq interrupt",
        [ PsrMirq ] "irq interrupt",
        [ PsrMsvc ] "svc/swi exception",
        [ PsrMabt ] "prefetch abort/data abort",
        [ PsrMabt+1 ] "data abort",
        [ PsrMund ] "undefined instruction",
        [ PsrMsys ] "sys trap",
};

extern int notify(Ureg*);

/*
 *  set up for exceptions
 */
void
trapinit(void)
{
        Vpage0 *vpage0;

        /* disable everything */
        intrsoff();

        /* set up the exception vectors */
        vpage0 = (Vpage0*)HVECTORS;
        memmove(vpage0->vectors, vectors, sizeof(vpage0->vectors));
        memmove(vpage0->vtable, vtable, sizeof(vpage0->vtable));
        cacheuwbinv();

        /* set up the stacks for the interrupt modes */
        setr13(PsrMfiq, (u32int*)(FIQSTKTOP));
        setr13(PsrMirq, m->sirq);
        setr13(PsrMabt, m->sabt);
        setr13(PsrMund, m->sund);
        setr13(PsrMsys, m->ssys);

        coherence();
}

void
intrsoff(void)
{
        Intregs *ip;
        int disable;

        ip = (Intregs*)INTREGS;
        disable = ~0;
        ip->GPUdisable[0] = disable;
        ip->GPUdisable[1] = disable;
        ip->ARMdisable = disable;
        ip->FIQctl = 0;
}

/*
 *  called by trap to handle irq interrupts.
 *  returns true iff a clock interrupt, thus maybe reschedule.
 */
static int
irq(Ureg* ureg)
{
        Vctl *v;
        int clockintr;

        clockintr = 0;
        for(v = vctl; v; v = v->next)
                if(*v->reg & v->mask){
                        coherence();
                        v->f(ureg, v->a);
                        coherence();
                        if(v->irq == IRQclock)
                                clockintr = 1;
                }
        return clockintr;
}

/*
 * called direct from lexception.s to handle fiq interrupt.
 */
void
fiq(Ureg *ureg)
{
        Vctl *v;

        v = vfiq;
        if(v == nil)
                panic("unexpected item in bagging area");
        m->intr++;
        ureg->pc -= 4;
        coherence();
        v->f(ureg, v->a);
        coherence();
}

void
irqenable(int irq, void (*f)(Ureg*, void*), void* a)
{
        Vctl *v;
        Intregs *ip;
        u32int *enable;

        ip = (Intregs*)INTREGS;
        v = (Vctl*)malloc(sizeof(Vctl));
        if(v == nil)
                panic("irqenable: no mem");
        v->irq = irq;
        if(irq >= IRQbasic){
                enable = &ip->ARMenable;
                v->reg = &ip->ARMpending;
                v->mask = 1 << (irq - IRQbasic);
        }else{
                enable = &ip->GPUenable[irq/32];
                v->reg = &ip->GPUpending[irq/32];
                v->mask = 1 << (irq % 32);
        }
        v->f = f;
        v->a = a;
        if(irq == IRQfiq){
                assert((ip->FIQctl & Fiqenable) == 0);
                assert((*enable & v->mask) == 0);
                vfiq = v;
                ip->FIQctl = Fiqenable | irq;
        }else{
                v->next = vctl;
                vctl = v;
                *enable = v->mask;
        }
}

static char *
trapname(int psr)
{
        char *s;

        s = trapnames[psr & PsrMask];
        if(s == nil)
                s = "unknown trap number in psr";
        return s;
}

/* this is quite helpful during mmu and cache debugging */
static void
ckfaultstuck(uintptr va)
{
        static int cnt, lastpid;
        static uintptr lastva;

        if (va == lastva && up->pid == lastpid) {
                ++cnt;
                if (cnt >= 2)
                        /* fault() isn't fixing the underlying cause */
                        panic("fault: %d consecutive faults for va %#p",
                                cnt+1, va);
        } else {
                cnt = 0;
                lastva = va;
                lastpid = up->pid;
        }
}

/*
 *  called by trap to handle access faults
 */
static void
faultarm(Ureg *ureg, uintptr va, int user, int read)
{
        int n, insyscall;
        char buf[ERRMAX];

        if(up == nil) {
                dumpregs(ureg);
                panic("fault: nil up in faultarm, accessing %#p", va);
        }
        insyscall = up->insyscall;
        up->insyscall = 1;
        if (Debug)
                ckfaultstuck(va);

        n = fault(va, read);
        if(n < 0){
                if(!user){
                        dumpregs(ureg);
                        panic("fault: kernel accessing %#p", va);
                }
                /* don't dump registers; programs suicide all the time */
                snprint(buf, sizeof buf, "sys: trap: fault %s va=%#p",
                        read? "read": "write", va);
                postnote(up, 1, buf, NDebug);
        }
        up->insyscall = insyscall;
}

/*
 *  returns 1 if the instruction writes memory, 0 otherwise
 */
int
writetomem(ulong inst)
{
        /* swap always write memory */
        if((inst & 0x0FC00000) == 0x01000000)
                return 1;

        /* loads and stores are distinguished by bit 20 */
        if(inst & (1<<20))
                return 0;

        return 1;
}

/*
 *  here on all exceptions other than syscall (SWI) and fiq
 */
void
trap(Ureg *ureg)
{
        int clockintr, user, x, rv, rem;
        ulong inst, fsr;
        uintptr va;
        char buf[ERRMAX];

        assert(!islo());
        if(up != nil)
                rem = ((char*)ureg)-up->kstack;
        else
                rem = ((char*)ureg)-((char*)m+sizeof(Mach));
        if(rem < 256) {
                iprint("trap: %d stack bytes left, up %#p ureg %#p at pc %#lux\n",
                        rem, up, ureg, ureg->pc);
                delay(1000);
                dumpstack();
                panic("trap: %d stack bytes left, up %#p ureg %#p at pc %#lux",
                        rem, up, ureg, ureg->pc);
        }

        user = (ureg->psr & PsrMask) == PsrMusr;
        if(user){
                up->dbgreg = ureg;
                cycles(&up->kentry);
        }

        /*
         * All interrupts/exceptions should be resumed at ureg->pc-4,
         * except for Data Abort which resumes at ureg->pc-8.
         */
        if(ureg->type == (PsrMabt+1))
                ureg->pc -= 8;
        else
                ureg->pc -= 4;

        clockintr = 0;          /* if set, may call sched() before return */
        switch(ureg->type){
        default:
                panic("unknown trap; type %#lux, psr mode %#lux", ureg->type,
                        ureg->psr & PsrMask);
                break;
        case PsrMirq:
                clockintr = irq(ureg);
                m->intr++;
                break;
        case PsrMabt:                   /* prefetch fault */
                x = ifsrget();
                fsr = (x>>7) & 0x8 | x & 0x7;
                switch(fsr){
                case 0x02:              /* instruction debug event (BKPT) */
                        if(user){
                                snprint(buf, sizeof buf, "sys: breakpoint");
                                postnote(up, 1, buf, NDebug);
                        }else{
                                iprint("kernel bkpt: pc %#lux inst %#ux\n",
                                        ureg->pc, *(u32int*)ureg->pc);
                                panic("kernel bkpt");
                        }
                        break;
                default:
                        faultarm(ureg, ureg->pc, user, 1);
                        break;
                }
                break;
        case PsrMabt+1:                 /* data fault */
                va = farget();
                inst = *(ulong*)(ureg->pc);
                /* bits 12 and 10 have to be concatenated with status */
                x = fsrget();
                fsr = (x>>7) & 0x20 | (x>>6) & 0x10 | x & 0xf;
                switch(fsr){
                default:
                case 0xa:               /* ? was under external abort */
                        panic("unknown data fault, 6b fsr %#lux", fsr);
                        break;
                case 0x0:
                        panic("vector exception at %#lux", ureg->pc);
                        break;
                case 0x1:               /* alignment fault */
                case 0x3:               /* access flag fault (section) */
                        if(user){
                                snprint(buf, sizeof buf,
                                        "sys: alignment: pc %#lux va %#p\n",
                                        ureg->pc, va);
                                postnote(up, 1, buf, NDebug);
                        } else
                                panic("kernel alignment: pc %#lux va %#p", ureg->pc, va);
                        break;
                case 0x2:
                        panic("terminal exception at %#lux", ureg->pc);
                        break;
                case 0x4:               /* icache maint fault */
                case 0x6:               /* access flag fault (page) */
                case 0x8:               /* precise external abort, non-xlat'n */
                case 0x28:
                case 0xc:               /* l1 translation, precise ext. abort */
                case 0x2c:
                case 0xe:               /* l2 translation, precise ext. abort */
                case 0x2e:
                case 0x16:              /* imprecise ext. abort, non-xlt'n */
                case 0x36:
                        panic("external abort %#lux pc %#lux addr %#p",
                                fsr, ureg->pc, va);
                        break;
                case 0x1c:              /* l1 translation, precise parity err */
                case 0x1e:              /* l2 translation, precise parity err */
                case 0x18:              /* imprecise parity or ecc err */
                        panic("translation parity error %#lux pc %#lux addr %#p",
                                fsr, ureg->pc, va);
                        break;
                case 0x5:               /* translation fault, no section entry */
                case 0x7:               /* translation fault, no page entry */
                        faultarm(ureg, va, user, !writetomem(inst));
                        break;
                case 0x9:
                case 0xb:
                        /* domain fault, accessing something we shouldn't */
                        if(user){
                                snprint(buf, sizeof buf,
                                        "sys: access violation: pc %#lux va %#p\n",
                                        ureg->pc, va);
                                postnote(up, 1, buf, NDebug);
                        } else
                                panic("kernel access violation: pc %#lux va %#p",
                                        ureg->pc, va);
                        break;
                case 0xd:
                case 0xf:
                        /* permission error, copy on write or real permission error */
                        faultarm(ureg, va, user, !writetomem(inst));
                        break;
                }
                break;
        case PsrMund:                   /* undefined instruction */
                if(user){
                        if(seg(up, ureg->pc, 0) != nil &&
                           *(u32int*)ureg->pc == 0xD1200070)
                                postnote(up, 1, "sys: breakpoint", NDebug);
                        else{
                                /* look for floating point instructions to interpret */
                                rv = fpuemu(ureg);
                                if(rv == 0){
                                        snprint(buf, sizeof buf,
                                                "undefined instruction: pc %#lux\n",
                                                ureg->pc);
                                        postnote(up, 1, buf, NDebug);
                                }
                        }
                }else{
                        if (ureg->pc & 3) {
                                iprint("rounding fault pc %#lux down to word\n",
                                        ureg->pc);
                                ureg->pc &= ~3;
                        }
                        iprint("undefined instruction: pc %#lux inst %#ux\n",
                                ureg->pc, *(u32int*)ureg->pc);
                        panic("undefined instruction");
                }
                break;
        }
        splhi();

        /* delaysched set because we held a lock or because our quantum ended */
        if(up && up->delaysched && clockintr){
                sched();                /* can cause more traps */
                splhi();
        }

        if(user){
                if(up->procctl || up->nnote)
                        notify(ureg);
                kexit(ureg);
        }
}

int
isvalidaddr(void *v)
{
        return (uintptr)v >= KZERO;
}

static void
dumplongs(char *msg, ulong *v, int n)
{
        int i, l;

        l = 0;
        iprint("%s at %.8p: ", msg, v);
        for(i=0; i<n; i++){
                if(l >= 4){
                        iprint("\n    %.8p: ", v);
                        l = 0;
                }
                if(isvalidaddr(v)){
                        iprint(" %.8lux", *v++);
                        l++;
                }else{
                        iprint(" invalid");
                        break;
                }
        }
        iprint("\n");
}

static void
dumpstackwithureg(Ureg *ureg)
{
        uintptr l, i, v, estack;
        u32int *p;
        char *s;

        if((s = getconf("*nodumpstack")) != nil && strcmp(s, "0") != 0){
                iprint("dumpstack disabled\n");
                return;
        }
        iprint("ktrace /kernel/path %#.8lux %#.8lux %#.8lux # pc, sp, link\n",
                ureg->pc, ureg->sp, ureg->r14);
        delay(2000);
        i = 0;
        if(up != nil && (uintptr)&l <= (uintptr)up->kstack+KSTACK)
                estack = (uintptr)up->kstack+KSTACK;
        else if((uintptr)&l >= (uintptr)m->stack
             && (uintptr)&l <= (uintptr)m+MACHSIZE)
                estack = (uintptr)m+MACHSIZE;
        else{
                if(up != nil)
                        iprint("&up->kstack %#p &l %#p\n", up->kstack, &l);
                else
                        iprint("&m %#p &l %#p\n", m, &l);
                return;
        }
        for(l = (uintptr)&l; l < estack; l += sizeof(uintptr)){
                v = *(uintptr*)l;
                if(KTZERO < v && v < (uintptr)etext && !(v & 3)){
                        v -= sizeof(u32int);            /* back up an instr */
                        p = (u32int*)v;
                        if((*p & 0x0f000000) == 0x0b000000){    /* BL instr? */
                                iprint("%#8.8lux=%#8.8lux ", l, v);
                                i++;
                        }
                }
                if(i == 4){
                        i = 0;
                        iprint("\n");
                }
        }
        if(i)
                iprint("\n");
}

/*
 * Fill in enough of Ureg to get a stack trace, and call a function.
 * Used by debugging interface rdb.
 */
void
callwithureg(void (*fn)(Ureg*))
{
        Ureg ureg;

        ureg.pc = getcallerpc(&fn);
        ureg.sp = PTR2UINT(&fn);
        fn(&ureg);
}

void
dumpstack(void)
{
        callwithureg(dumpstackwithureg);
}

void
dumpregs(Ureg* ureg)
{
        int s;

        if (ureg == nil) {
                iprint("trap: no user process\n");
                return;
        }
        s = splhi();
        iprint("trap: %s", trapname(ureg->type));
        if(ureg != nil && (ureg->psr & PsrMask) != PsrMsvc)
                iprint(" in %s", trapname(ureg->psr));
        iprint("\n");
        iprint("psr %8.8lux type %2.2lux pc %8.8lux link %8.8lux\n",
                ureg->psr, ureg->type, ureg->pc, ureg->link);
        iprint("R14 %8.8lux R13 %8.8lux R12 %8.8lux R11 %8.8lux R10 %8.8lux\n",
                ureg->r14, ureg->r13, ureg->r12, ureg->r11, ureg->r10);
        iprint("R9  %8.8lux R8  %8.8lux R7  %8.8lux R6  %8.8lux R5  %8.8lux\n",
                ureg->r9, ureg->r8, ureg->r7, ureg->r6, ureg->r5);
        iprint("R4  %8.8lux R3  %8.8lux R2  %8.8lux R1  %8.8lux R0  %8.8lux\n",
                ureg->r4, ureg->r3, ureg->r2, ureg->r1, ureg->r0);
        iprint("stack is at %#p\n", ureg);
        iprint("pc %#lux link %#lux\n", ureg->pc, ureg->link);

        if(up)
                iprint("user stack: %#p-%#p\n", up->kstack, up->kstack+KSTACK-4);
        else
                iprint("kernel stack: %8.8lux-%8.8lux\n",
                        (ulong)(m+1), (ulong)m+BY2PG-4);
        dumplongs("stack", (ulong *)(ureg + 1), 16);
        delay(2000);
        dumpstack();
        splx(s);
}