]> git.lizzy.rs Git - plan9front.git/blobdiff - sys/src/9/pc/trap.c
ether82563: fix multicast for i210
[plan9front.git] / sys / src / 9 / pc / trap.c
index 08033a9eedb8182693d4dc4f770eba4f9aaae53a..97931807faf37b02f75074b5021b976c610c007b 100644 (file)
@@ -13,6 +13,7 @@ static int trapinited;
 
 void   noted(Ureg*, ulong);
 
+static void debugexc(Ureg*, void*);
 static void debugbpt(Ureg*, void*);
 static void fault386(Ureg*, void*);
 static void doublefault(Ureg*, void*);
@@ -39,8 +40,22 @@ intrenable(int irq, void (*f)(Ureg*, void*), void* a, int tbdf, char *name)
                        irq, tbdf, name);
                return;
        }
+       if(tbdf != BUSUNKNOWN && (irq == 0xff || irq == 0)){
+               print("intrenable: got unassigned irq %d, tbdf 0x%uX for %s\n",
+                       irq, tbdf, name);
+               irq = -1;
+       }
+
+       /*
+        * IRQ2 doesn't really exist, it's used to gang the interrupt
+        * controllers together. A device set to IRQ2 will appear on
+        * the second interrupt controller as IRQ9.
+        */
+       if(irq == 2)
+               irq = 9;
 
-       v = xalloc(sizeof(Vctl));
+       if((v = xalloc(sizeof(Vctl))) == nil)
+               panic("intrenable: out of memory");
        v->isintr = 1;
        v->irq = irq;
        v->tbdf = tbdf;
@@ -69,75 +84,71 @@ intrenable(int irq, void (*f)(Ureg*, void*), void* a, int tbdf, char *name)
        iunlock(&vctllock);
 }
 
-int
+void
 intrdisable(int irq, void (*f)(Ureg *, void *), void *a, int tbdf, char *name)
 {
        Vctl **pv, *v;
        int vno;
 
-       /*
-        * For now, none of this will work with the APIC code,
-        * there is no mapping between irq and vector as the IRQ
-        * is pretty meaningless.
-        */
-       if(arch->intrvecno == nil)
-               return -1;
-       vno = arch->intrvecno(irq);
+       if(irq == 2)
+               irq = 9;
+       if(arch->intrvecno == nil || (tbdf != BUSUNKNOWN && (irq == 0xff || irq == 0))){
+               /*
+                * on APIC machine, irq is pretty meaningless
+                * and disabling a the vector is not implemented.
+                * however, we still want to remove the matching
+                * Vctl entry to prevent calling Vctl.f() with a
+                * stale Vctl.a pointer.
+                */
+               irq = -1;
+               vno = VectorPIC;
+       } else {
+               vno = arch->intrvecno(irq);
+       }
        ilock(&vctllock);
-       pv = &vctl[vno];
-       while (*pv &&
-                 ((*pv)->irq != irq || (*pv)->tbdf != tbdf || (*pv)->f != f || (*pv)->a != a ||
-                  strcmp((*pv)->name, name)))
-               pv = &((*pv)->next);
-       assert(*pv);
-
-       v = *pv;
-       *pv = (*pv)->next;      /* Link out the entry */
-
-       if(vctl[vno] == nil && arch->intrdisable != nil)
-               arch->intrdisable(irq);
+       do {
+               for(pv = &vctl[vno]; (v = *pv) != nil; pv = &v->next){
+                       if(v->isintr && (v->irq == irq || irq == -1)
+                       && v->tbdf == tbdf && v->f == f && v->a == a
+                       && strcmp(v->name, name) == 0)
+                               break;
+               }
+               if(v != nil){
+                       *pv = v->next;
+                       xfree(v);
+
+                       if(irq != -1 && vctl[vno] == nil && arch->intrdisable != nil)
+                               arch->intrdisable(irq);
+                       break;
+               }
+       } while(irq == -1 && ++vno <= MaxVectorAPIC);
        iunlock(&vctllock);
-       xfree(v);
-       return 0;
 }
 
 static long
-irqallocread(Chan*, void *vbuf, long n, vlong offset)
+irqallocread(Chan*, void *a, long n, vlong offset)
 {
-       char *buf, *p, str[2*(11+1)+KNAMELEN+1+1];
-       int m, vno;
-       long oldn;
+       char buf[2*(11+1)+KNAMELEN+1+1];
+       int vno, m;
        Vctl *v;
 
        if(n < 0 || offset < 0)
                error(Ebadarg);
 
-       oldn = n;
-       buf = vbuf;
        for(vno=0; vno<nelem(vctl); vno++){
                for(v=vctl[vno]; v; v=v->next){
-                       m = snprint(str, sizeof str, "%11d %11d %.*s\n", vno, v->irq, KNAMELEN, v->name);
-                       if(m <= offset) /* if do not want this, skip entry */
-                               offset -= m;
-                       else{
-                               /* skip offset bytes */
-                               m -= offset;
-                               p = str+offset;
-                               offset = 0;
-
-                               /* write at most max(n,m) bytes */
-                               if(m > n)
-                                       m = n;
-                               memmove(buf, p, m);
-                               n -= m;
-                               buf += m;
-
-                               if(n == 0)
-                                       return oldn;
-                       }
+                       m = snprint(buf, sizeof(buf), "%11d %11d %.*s\n", vno, v->irq, KNAMELEN, v->name);
+                       offset -= m;
+                       if(offset >= 0)
+                               continue;
+                       if(n > -offset)
+                               n = -offset;
+                       offset += m;
+                       memmove(a, buf+offset, n);
+                       return n;
                }
        }
-       return oldn - n;
+       return 0;
 }
 
 void
@@ -147,11 +158,12 @@ trapenable(int vno, void (*f)(Ureg*, void*), void* a, char *name)
 
        if(vno < 0 || vno >= VectorPIC)
                panic("trapenable: vno %d", vno);
-       v = xalloc(sizeof(Vctl));
+       if((v = xalloc(sizeof(Vctl))) == nil)
+               panic("trapenable: out of memory");
        v->tbdf = BUSUNKNOWN;
        v->f = f;
        v->a = a;
-       strncpy(v->name, name, KNAMELEN);
+       strncpy(v->name, name, KNAMELEN-1);
        v->name[KNAMELEN-1] = 0;
 
        ilock(&vctllock);
@@ -173,7 +185,7 @@ nmienable(void)
        outb(0x70, 0);
 
        x = inb(0x61) & 0x07;           /* Enable NMI */
-       outb(0x61, 0x08|x);
+       outb(0x61, 0x0C|x);
        outb(0x61, x);
 }
 
@@ -220,6 +232,7 @@ trapinit(void)
         * Special traps.
         * Syscall() is called directly without going through trap().
         */
+       trapenable(VectorDE, debugexc, 0, "debugexc");
        trapenable(VectorBPT, debugbpt, 0, "debugpt");
        trapenable(VectorPF, fault386, 0, "fault386");
        trapenable(Vector2F, doublefault, 0, "doublefault");
@@ -250,7 +263,7 @@ static char* excname[32] = {
        "coprocessor error",
        "alignment check",
        "machine check",
-       "19 (reserved)",
+       "simd error",
        "20 (reserved)",
        "21 (reserved)",
        "22 (reserved)",
@@ -299,7 +312,7 @@ kexit(Ureg*)
        tos = (Tos*)(USTKTOP-sizeof(Tos));
        cycles(&t);
        tos->kcycles += t - up->kentry;
-       tos->pcycles = up->pcycles;
+       tos->pcycles = t + up->pcycles;
        tos->pid = up->pid;
 }
 
@@ -398,8 +411,8 @@ trap(Ureg* ureg)
                if(0)print("cpu%d: spurious interrupt %d, last %d\n",
                        m->machno, vno, m->lastintr);
                if(0)if(conf.nmach > 1){
-                       for(i = 0; i < 32; i++){
-                               if(!(active.machs & (1<<i)))
+                       for(i = 0; i < MAXMACH; i++){
+                               if(active.machs[i] == 0)
                                        continue;
                                mach = MACHP(i);
                                if(m->machno == mach->machno)
@@ -420,49 +433,57 @@ trap(Ureg* ureg)
                         * Don't re-enable, it confuses the crash dumps.
                        nmienable();
                         */
-                       iprint("cpu%d: PC %#8.8lux\n", m->machno, ureg->pc);
+                       iprint("cpu%d: nmi PC %#8.8lux, status %ux\n",
+                               m->machno, ureg->pc, inb(0x61));
                        while(m->machno != 0)
                                ;
                }
 
-               if(vno == VectorGPF || vno == VectorSNP){
-                       ulong *sp;
-                       uchar *pc;
+               if(!user){
+                       void (*pc)(void);
+                       ulong *sp; 
+
+                       extern void _forkretpopgs(void);
+                       extern void _forkretpopfs(void);
+                       extern void _forkretpopes(void);
+                       extern void _forkretpopds(void);
+                       extern void _forkretiret(void);
+                       extern void _rdmsrinst(void);
+                       extern void _wrmsrinst(void);
+                       extern void _peekinst(void);
 
-                       /* l.s */
                        extern void load_fs(ulong);
                        extern void load_gs(ulong);
 
-                       /*
-                        * CS, SS, DS and ES are initialized by strayintr
-                        * in l.s. initialize the others too so we dont trap
-                        * again when restoring the old context.
-                        */
                        load_fs(NULLSEL);
                        load_gs(NULLSEL);
 
-                       pc = (uchar*)ureg->pc;
-                       sp = (ulong*)&ureg->sp;
-
-                       /*
-                        * we test for the instructions used by forkret()
-                        * to load the segments. this needs to be changed
-                        * if forkret changes!
-                        */
-
-                       /* POP */
-                       if((pc[0] == 0x0f && (pc[1] == 0xa9 /*GS*/ || 
-                               pc[1] == 0xa1 /*FS*/)) || (pc[0] == 0x07) /*ES*/ ||     
-                               (pc[0] == 0x1f) /*DS*/){
-                               sp[0] = NULLSEL;
-                               return;
-                       }
-
-                       /* IRET */
-                       if(pc[0] == 0xcf){
-                               sp[1] = UESEL;  /*CS*/
-                               sp[4] = UDSEL;  /*SS*/
-                               return;
+                       sp = (ulong*)&ureg->sp; /* kernel stack */
+                       pc = (void*)ureg->pc;
+
+                       if(pc == _forkretpopgs || pc == _forkretpopfs || 
+                          pc == _forkretpopes || pc == _forkretpopds){
+                               if(vno == VectorGPF || vno == VectorSNP){
+                                       sp[0] = NULLSEL;
+                                       return;
+                               }
+                       } else if(pc == _forkretiret){
+                               if(vno == VectorGPF || vno == VectorSNP){
+                                       sp[1] = UESEL;  /* CS */
+                                       sp[4] = UDSEL;  /* SS */
+                                       return;
+                               }
+                       } else if(pc == _rdmsrinst || pc == _wrmsrinst){
+                               if(vno == VectorGPF){
+                                       ureg->bp = -1;
+                                       ureg->pc += 2;
+                                       return;
+                               }
+                       } else if(pc == _peekinst){
+                               if(vno == VectorGPF){
+                                       ureg->pc += 2;
+                                       return;
+                               }
                        }
                }
 
@@ -519,8 +540,6 @@ dumpregs2(Ureg* ureg)
 void
 dumpregs(Ureg* ureg)
 {
-       vlong mca, mct;
-
        dumpregs2(ureg);
 
        /*
@@ -532,13 +551,10 @@ dumpregs(Ureg* ureg)
         */
        iprint("  CR0 %8.8lux CR2 %8.8lux CR3 %8.8lux",
                getcr0(), getcr2(), getcr3());
-       if(m->cpuiddx & 0x9A){
-               iprint(" CR4 %8.8lux", getcr4());
-               if((m->cpuiddx & 0xA0) == 0xA0){
-                       rdmsr(0x00, &mca);
-                       rdmsr(0x01, &mct);
-                       iprint("\n  MCA %8.8llux MCT %8.8llux", mca, mct);
-               }
+       if(m->cpuiddx & (Mce|Tsc|Pse|Vmex)){
+               iprint(" CR4 %8.8lux\n", getcr4());
+               if(ureg->trap == 18)
+                       dumpmcregs();
        }
        iprint("\n  ur %#p up %#p\n", ureg, up);
 }
@@ -627,6 +643,40 @@ dumpstack(void)
        callwithureg(_dumpstack);
 }
 
+static void
+debugexc(Ureg *ureg, void *)
+{
+       u32int dr6, m;
+       char buf[ERRMAX];
+       char *p, *e;
+       int i;
+
+       dr6 = getdr6();
+       if(up == nil)
+               panic("kernel debug exception dr6=%#.8ux", dr6);
+       putdr6(up->dr[6]);
+       if(userureg(ureg))
+               qlock(&up->debug);
+       else if(!canqlock(&up->debug))
+               return;
+       m = up->dr[7];
+       m = (m >> 4 | m >> 3) & 8 | (m >> 3 | m >> 2) & 4 | (m >> 2 | m >> 1) & 2 | (m >> 1 | m) & 1;
+       m &= dr6;
+       if(m == 0){
+               sprint(buf, "sys: debug exception dr6=%#.8ux", dr6);
+               postnote(up, 0, buf, NDebug);
+       }else{
+               p = buf;
+               e = buf + sizeof(buf);
+               p = seprint(p, e, "sys: watchpoint ");
+               for(i = 0; i < 4; i++)
+                       if((m & 1<<i) != 0)
+                               p = seprint(p, e, "%d%s", i, (m >> i + 1 != 0) ? "," : "");
+               postnote(up, 0, buf, NDebug);
+       }
+       qunlock(&up->debug);
+}
+
 static void
 debugbpt(Ureg* ureg, void*)
 {
@@ -668,6 +718,13 @@ fault386(Ureg* ureg, void*)
        if(!user){
                if(vmapsync(addr))
                        return;
+               {
+                       extern void _peekinst(void);
+                       if((void(*)(void))ureg->pc == _peekinst){
+                               ureg->pc += 2;
+                               return;
+                       }
+               }
                if(addr >= USTKTOP)
                        panic("kernel fault: bad address pc=0x%.8lux addr=0x%.8lux", ureg->pc, addr);
                if(up == nil)
@@ -678,7 +735,7 @@ fault386(Ureg* ureg, void*)
 
        insyscall = up->insyscall;
        up->insyscall = 1;
-       n = fault(addr, read);
+       n = fault(addr, ureg->pc, read);
        if(n < 0){
                if(!user){
                        dumpregs(ureg);
@@ -725,49 +782,33 @@ syscall(Ureg* ureg)
        scallnr = ureg->ax;
        up->scallnr = scallnr;
 
-       if(up->procctl == Proc_tracesyscall){
-               /*
-                * Redundant validaddr.  Do we care?
-                * Tracing syscalls is not exactly a fast path...
-                * Beware, validaddr currently does a pexit rather
-                * than an error if there's a problem; that might
-                * change in the future.
-                */
-               if(sp < (USTKTOP-BY2PG) || sp > (USTKTOP-sizeof(Sargs)-BY2WD))
-                       validaddr(sp, sizeof(Sargs)+BY2WD, 0);
-
-               syscallfmt(scallnr, ureg->pc, (va_list)(sp+BY2WD));
-               up->procctl = Proc_stopme;
-               procctl(up);
-               if(up->syscalltrace)
-                       free(up->syscalltrace);
-               up->syscalltrace = nil;
-               startns = todget(nil);
-       }
-
-       if(scallnr == RFORK && up->fpstate == FPactive){
-               fpsave(&up->fpsave);
-               up->fpstate = FPinactive;
-       }
        spllo();
 
        up->nerrlab = 0;
        ret = -1;
        if(!waserror()){
+               if(sp<(USTKTOP-BY2PG) || sp>(USTKTOP-sizeof(Sargs)-BY2WD))
+                       validaddr(sp, sizeof(Sargs)+BY2WD, 0);
+
+               up->s = *((Sargs*)(sp+BY2WD));
+
+               if(up->procctl == Proc_tracesyscall){
+                       syscallfmt(scallnr, ureg->pc, (va_list)up->s.args);
+                       s = splhi();
+                       up->procctl = Proc_stopme;
+                       procctl();
+                       splx(s);
+                       startns = todget(nil);
+               }
+
                if(scallnr >= nsyscall || systab[scallnr] == 0){
                        pprint("bad sys call number %lud pc %lux\n",
                                scallnr, ureg->pc);
                        postnote(up, 1, "sys: bad sys call", NDebug);
                        error(Ebadarg);
                }
-
-               if(sp<(USTKTOP-BY2PG) || sp>(USTKTOP-sizeof(Sargs)-BY2WD))
-                       validaddr(sp, sizeof(Sargs)+BY2WD, 0);
-
-               up->s = *((Sargs*)(sp+BY2WD));
                up->psstate = sysctab[scallnr];
-
-               ret = systab[scallnr](up->s.args);
+               ret = systab[scallnr]((va_list)up->s.args);
                poperror();
        }else{
                /* failure: save the error buffer for errstr */
@@ -795,21 +836,18 @@ syscall(Ureg* ureg)
 
        if(up->procctl == Proc_tracesyscall){
                stopns = todget(nil);
-               up->procctl = Proc_stopme;
-               sysretfmt(scallnr, (va_list)(sp+BY2WD), ret, startns, stopns);
+               sysretfmt(scallnr, (va_list)up->s.args, ret, startns, stopns);
                s = splhi();
-               procctl(up);
+               up->procctl = Proc_stopme;
+               procctl();
                splx(s);
-               if(up->syscalltrace)
-                       free(up->syscalltrace);
-               up->syscalltrace = nil;
        }
 
        up->insyscall = 0;
        up->psstate = 0;
 
        if(scallnr == NOTED)
-               noted(ureg, *(ulong*)(sp+BY2WD));
+               noted(ureg, *((ulong*)up->s.args));
 
        if(scallnr!=RFORK && (up->procctl || up->nnote)){
                splhi();
@@ -833,12 +871,12 @@ notify(Ureg* ureg)
        Note *n;
 
        if(up->procctl)
-               procctl(up);
+               procctl();
        if(up->nnote == 0)
                return 0;
 
        if(up->fpstate == FPactive){
-               fpsave(&up->fpsave);
+               fpsave(up->fpsave);
                up->fpstate = FPinactive;
        }
        up->fpstate |= FPillegal;
@@ -855,9 +893,9 @@ notify(Ureg* ureg)
        }
 
        if(n->flag!=NUser && (up->notified || up->notify==0)){
+               qunlock(&up->debug);
                if(n->flag == NDebug)
                        pprint("suicide: %s\n", n->msg);
-               qunlock(&up->debug);
                pexit(n->msg, n->flag!=NDebug);
        }
 
@@ -877,7 +915,7 @@ notify(Ureg* ureg)
 if(0) print("%s %lud: notify %.8lux %.8lux %.8lux %s\n",
        up->text, up->pid, ureg->pc, ureg->usp, sp, n->msg);
 
-       if(!okaddr((ulong)up->notify, 1, 0)
+       if(!okaddr((uintptr)up->notify, 1, 0)
        || !okaddr(sp-ERRMAX-4*BY2WD, sizeof(Ureg)+ERRMAX+4*BY2WD, 1)){
                qunlock(&up->debug);
                pprint("suicide: bad address in notify\n");
@@ -930,7 +968,7 @@ noted(Ureg* ureg, ulong arg0)
 
        /* sanity clause */
        oureg = (ulong)nureg;
-       if(!okaddr((ulong)oureg-BY2WD, BY2WD+sizeof(Ureg), 0)){
+       if(!okaddr(oureg-BY2WD, BY2WD+sizeof(Ureg), 0)){
                qunlock(&up->debug);
                pprint("bad ureg in noted or call to noted when not notified\n");
                pexit("Suicide", 0);
@@ -973,29 +1011,23 @@ if(0) print("%s %lud: noted %.8lux %.8lux\n",
                break;
 
        default:
-               pprint("unknown noted arg 0x%lux\n", arg0);
                up->lastnote.flag = NDebug;
                /* fall through */
 
        case NDFLT:
-               if(up->lastnote.flag == NDebug){
-                       qunlock(&up->debug);
+               qunlock(&up->debug);
+               if(up->lastnote.flag == NDebug)
                        pprint("suicide: %s\n", up->lastnote.msg);
-               } else
-                       qunlock(&up->debug);
                pexit(up->lastnote.msg, up->lastnote.flag!=NDebug);
        }
 }
 
-long
-execregs(ulong entry, ulong ssize, ulong nargs)
+uintptr
+execregs(uintptr entry, ulong ssize, ulong nargs)
 {
        ulong *sp;
        Ureg *ureg;
 
-       up->fpstate = FPinit;
-       fpoff();
-
        sp = (ulong*)(USTKTOP - ssize);
        *--sp = nargs;
 
@@ -1011,7 +1043,7 @@ execregs(ulong entry, ulong ssize, ulong nargs)
 /*
  *  return the userpc the last exception happened at
  */
-ulong
+uintptr
 userpc(void)
 {
        Ureg *ureg;