From c523b5d0a320372e5ce54500032826508571205e Mon Sep 17 00:00:00 2001 From: David du Colombier <0intro@gmail.com> Date: Tue, 8 Dec 1992 00:00:00 +0000 Subject: [PATCH] Plan 9 from Bell Labs 1992-12-08 --- pc/devrtc.c | 9 +- port/proc.c | 16 +- power/io.h | 9 + power/scsi.c | 763 ++++++++++++++++++++++++++++++++++++++++++++++++ power/scsibuf.c | 79 +++++ power/trap.c | 6 +- 6 files changed, 868 insertions(+), 14 deletions(-) create mode 100644 power/scsi.c create mode 100644 power/scsibuf.c diff --git a/pc/devrtc.c b/pc/devrtc.c index d675532c11a5e5d1f697eaf74256d232898c89fd..f13ffcb85bebb2763dec2df36af586a30e899df7 100644 --- a/pc/devrtc.c +++ b/pc/devrtc.c @@ -122,10 +122,13 @@ rtctime(void) { uchar bcdclock[Nbcd]; Rtc rtc; + int i; - outb(Paddr, Status); - while(inb(Pdata) & 1) - ; + for(i = 0; i < 10000; i++){ + outb(Paddr, Status); + if((inb(Pdata) & 1) == 0) + break; + } outb(Paddr, Seconds); bcdclock[0] = inb(Pdata); outb(Paddr, Minutes); bcdclock[1] = inb(Pdata); outb(Paddr, Hours); bcdclock[2] = inb(Pdata); diff --git a/port/proc.c b/port/proc.c index 9515db899f76db4c64ab510934edcd391ed8ba25..0b8bd730bb8ef573c286a7b2a38c9bf2b4e20f27 100644 --- a/port/proc.c +++ b/port/proc.c @@ -495,14 +495,14 @@ unbreak(Proc *p) int b; qlock(&broken); - for(b=0; b < NBROKEN; b++) { - if(broken.p[b] != p) - continue; - - broken.n--; - memmove(&broken.p[b], &broken.p[b+1], sizeof(Proc*)*(NBROKEN-(b+1))); - ready(p); - } + for(b=0; b < broken.n; b++) + if(broken.p[b] != p) { + broken.n--; + memmove(&broken.p[b], &broken.p[b+1], + sizeof(Proc*)*(NBROKEN-(b+1))); + ready(p); + break; + } qunlock(&broken); } diff --git a/power/io.h b/power/io.h index ea351baae92d92e17da64127e8532c965586ffc3..c49c4b93d1b912915fce542a6993ddc310e98f40 100644 --- a/power/io.h +++ b/power/io.h @@ -67,6 +67,15 @@ struct SBCC #define WRITEMAP IO2(ulong, 0xFA0000) #define LANCEINDEX 0x1E00 /* index of lancemap */ +#define SCSI0ADDR IO2(uchar, 0xF08007) +#define SCSI0DATA IO2(uchar, 0xF08107) +#define SCSI1ADDR IO2(uchar, 0xF0C007) +#define SCSI1DATA IO2(uchar, 0xF0C107) +#define SCSI0CNT IO2(ulong, 0xF54000) +#define SCSI1CNT IO2(ulong, 0xF58000) +#define SCSI0FLS IO2(uchar, 0xF30001) +#define SCSI1FLS IO2(uchar, 0xF34001) + #define IOID IO2(uchar, 0xFFFFF0) #define IO2R1 1 /* IO2 revision level 1 */ #define IO2R2 2 /* IO2 revision level 2 */ diff --git a/power/scsi.c b/power/scsi.c new file mode 100644 index 0000000000000000000000000000000000000000..d6eaa2bc633b54bf7f8f20c3d27bfcc6f56151fb --- /dev/null +++ b/power/scsi.c @@ -0,0 +1,763 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +enum +{ + Rownid = 0x00, /* own ID/CDB size register */ + Eaf = 0x08, /* enable advanced features */ + Fs16MHz = 0x80, /* frequency select */ + Rcontrol = 0x01, /* control register */ + Hsp = 0x01, /* halt on scsi parity error */ + Idi = 0x04, /* intermediate disconnect interrupt */ + Edi = 0x08, /* ending disconnect interrupt */ + BurstDma = 0x20, /* burst mode dma */ + Rtimeout = 0x02, /* timeout period register */ + Rcdb = 0x03, /* CDB first byte */ + Rlun = 0x0F, /* target LUN register */ + Rcmdphase = 0x10, /* command phase register */ + Rsync = 0x11, /* synchronous transfer register */ + Rtchi = 0x12, /* transfer count hi register */ + Rtcmi = 0x13, /* transfer count mi register */ + Rtclo = 0x14, /* transfer count lo register */ + Rdestid = 0x15, /* destination ID */ + Dpd = 0x40, /* data-phase direction (read) */ + Rsrcid = 0x16, /* source ID */ + Siv = 0x08, /* source ID is valid */ + Er = 0x80, /* enable reselection */ + Rstatus = 0x17, /* scsi status register */ + Rcmd = 0x18, /* command register */ + Rdata = 0x19, /* data register */ + Raux = 0x1F, /* auxiliary status register */ + Dbr = 0x01, /* data buffer ready */ + Intrpend = 0x80, /* interrupt pending */ + + Creset = 0x00, /* reset */ + Catn = 0x02, /* assert ATN */ + Cnack = 0x03, /* negate ACK */ + CselectATN = 0x06, /* select with ATN */ + Cselect = 0x07, /* select without ATN */ + CtransferATN = 0x08, /* select with ATN and transfer */ + Ctransfer = 0x09, /* select without ATN and transfer */ + Ctinfo = 0x20, /* transfer info */ + Csbt = 0x80, /* single-byte transfer */ + + Mcomplete = 0x00, /* complete */ + Msaveptr = 0x02, /* save pointers */ + Mdisconnect = 0x04, /* disconnect */ + Mabort = 0x06, /* abort */ + Mreject = 0x07, /* reject */ + Mnop = 0x08, /* nop */ + Mbdr = 0x0C, /* bus device reset */ + Midentify = (0x80|0x40), /* identify|disconnectok */ +}; + +typedef struct Target Target; +struct Target +{ + QLock; + Rendez; + ulong flags; + uchar sync; + uchar transfer; + Target *active; /* link on active list */ + + int target; /* SCSI Unit and logical unit */ + int lun; + int status; + + struct { /* DMA things */ + ulong dmaaddr; + ulong dmalen; + int rflag; + + ulong *dmacnt; + uchar *dmafls; + ulong dmamap; + }; + + uchar cdb[16]; + int clen; + + uchar *addr; + uchar *data; + Target *base; + + Target *ctlr; +}; + +enum +{ + NCtlr = 2, + NTarget = 8, + CtlrID = 0, + + Fbusy = 0x0001, + Fidentified = 0x0002, + Fdisconnected = 0x0004, + Freselect = 0x0008, + Fcomplete = 0x0010, + Fsyncxfer = 0x0100, + Freadxfer = 0x0200, + Fdmaxfer = 0x0400, + Fretry = 0x1000, + Fidentifying = 0x2000, +}; + +static Target target[NCtlr][NTarget]; + +static void dmamap(Target*); + +/* + * Set up and start a data transfer. + * We don't load the command phase here as + * we may be continuing a stopped operation. + */ +static void +start(Target *tp) +{ + ulong count; + Target *dev = tp->ctlr; + + /* + * Set up the chip for the transfer. + * First the LUN and transfer mode. + */ + *dev->addr = Rlun; + *dev->data = tp->lun; + *dev->addr = Rsync; + *dev->data = tp->sync; + + /* + * Set up transfer count and targetID+direction. + * No need to set the address register here, we're + * already pointing to the right one. + */ + count = tp->dmalen; + *dev->data = (count>>16) & 0xFF; + *dev->data = (count>>8) & 0xFF; + *dev->data = count & 0xFF; + + if(tp->rflag == 0) + *dev->data = Dpd|tp->target; + else + *dev->data = tp->target; + + /* + * Load the command into the chip. + */ + *dev->addr = Rownid; + *dev->data = tp->clen; + *dev->addr = Rcdb; + +print("start %d cdb: ", tp->target); + for(count = 0; count < tp->clen; count++) { + *dev->data = tp->cdb[count]; + print("%2.2lux ", tp->cdb[count]); + } +print("\n"); + dmamap(tp); + + /* + * Select and transfer. + */ + *dev->addr = Rcmd; + *dev->data = tp->transfer; +} + +/* + * Arbitrate for the bus and start a data transfer. + * If this is the first time through for the target, + * i.e. we haven't identified yet, just set up for + * selection so we can try a synchronous negotiation. + */ +static void +arbitrate(Target *dev) +{ + Target *tp; + + tp = dev->active; + + *dev->addr = Rcmdphase; + *dev->data = 0x00; + + if(tp->flags & Fidentified) { + start(tp); + return; + } + + /* + * Fretry means that we will try this request again, + * but without initialising the command phase, so we will + * continue from where we left off. + */ + tp->flags |= Fidentifying|Fretry; + *dev->addr = Rlun; + *dev->data = tp->lun; + *dev->addr = Rdestid; + *dev->data = tp->target; + *dev->addr = Rcmd; + *dev->data = CselectATN; +} + +static int +done(void *arg) +{ + return (((Target*)arg)->flags & Fbusy) == 0; +} + +static int +scsiio(int nbus, int unit, int rw, uchar *cmd, int cbytes, void *data, int dbytes) +{ + ulong s; + int status; + Target *ctlr, **l, *f, *tp; + + tp = &target[nbus][unit]; + ctlr = tp->ctlr; + + qlock(tp); + tp->flags |= Fbusy; + tp->rflag = rw; + tp->dmaaddr = (ulong)data; + tp->dmalen = dbytes; + tp->status = 0x6002; + memmove(tp->cdb, cmd, cbytes); + tp->clen = cbytes; + + s = splhi(); + qlock(ctlr); + + /* + * Link the target onto the end of the + * ctlr active list and start the request if + * the controller is idle. + */ + tp->active = 0; + l = &ctlr->active; + for(f = *l; f; f = f->active) + l = &f->active; + *l = tp; + + if((ctlr->flags & Fbusy) == 0){ + ctlr->flags |= Fbusy; + arbitrate(ctlr); + } + + qunlock(ctlr); + splx(s); + + /* + * Wait for the request to complete + * and return the status. + */ + sleep(tp, done, tp); + + status = tp->status; + qunlock(tp); +print("status: #%4.4lux\n", status); + return status; +} + +int +scsiexec(Scsi *p, int read) +{ + int rw, s; + ulong clen, dlen; + + clen = p->cmd.lim - p->cmd.base; + dlen = p->data.lim - p->data.base; + + rw = 1; + if(read == ScsiIn) + rw = 0; + + if(p->target == CtlrID) + return 0x6002; + + s = scsiio(1, p->target, rw, p->cmd.base, clen, p->data.base, dlen); + p->status = s; + return s; +} + +/* + * Target request complete. + * Only called from interrupt level, + * i.e. ctlr is 'locked'. + */ +static void +complete(Target *ctlr) +{ + Target *tp; + + tp = ctlr->active; + tp->flags &= (Fretry|Fidentified|Fbusy); + + /* + * Retry (continue) the operation if necessary + * (used for initial identify), otherwise unlink from + * the ctlr active list. + */ + if(tp->flags & Fretry){ + tp->flags &= ~Fretry; + start(tp); + return; + } + + ctlr->active = tp->active; + + /* + * All done with this target, wakeup scsiio. + * If there are other targets queued, start + * one up. + */ + tp->flags &= Fidentified; + *tp->dmafls = 1; + + wakeup(tp); + + if(ctlr->active){ + arbitrate(ctlr); + return; + } + ctlr->flags &= ~Fbusy; +} + +/* + * A target has disconnected. + * Unlink it from the active list and + * start another if possible. + * Only called from interrupt level, + * i.e. ctlr is 'locked'. + */ +static void +disconnect(Target *ctlr) +{ + Target *tp; + + tp = ctlr->active; + tp->flags |= Fdisconnected; + ctlr->active = tp->active; + + if(ctlr->active){ + arbitrate(ctlr); + return; + } + ctlr->flags &= ~Fbusy; +} + +/* + * A target reselected, put it back + * on the ctlr active list. We mark the ctlr + * busy, it's the responsibility of the caller + * to restart the transfer. + * Only called from interrupt level, + * i.e. ctlr is 'locked'. + */ +static void +reselect(Target *ctlr, uchar id) +{ + Target *tp; + + tp = &ctlr->base[id]; + tp->active = ctlr->active; + ctlr->active = tp; + ctlr->flags |= Fbusy; + tp->flags &= ~Fdisconnected; +} + +static int +identify(Target *tp, uchar status) +{ + int i, n; + Target *dev; + static uchar msgout[6] = { + 0x00, 0x01, 0x03, 0x01, 0x3F, 0x0C + }; + uchar msgin[16]; + + dev = tp->ctlr; + + switch(status){ + + case 0x11: /* select complete */ + delay(7); + *dev->addr = Rcmd; + *dev->data = Catn; + break; + + case 0x8E: /* service required - MSGOUT */ + /* + * Send a synchronous data transfer request. + * The target may act strangely here and not respond + * correctly. + */ + tp->sync = 0x00; /* asynchronous transfer mode */ + tp->flags |= Fidentified; + *dev->addr = Rtchi; + *dev->data = 0x00; + *dev->data = 0x00; + *dev->data = sizeof(msgout); + *dev->addr = Rcmd; + *dev->data = Ctinfo; + msgout[0] = Midentify|tp->lun; + *dev->addr = Rdata; + n = 0; + while(n < sizeof(msgout)) { + /* + * Wait until the chip says ready. + * If an interrupt is indicated, something + * went wrong, so get out of here and let + * the interrupt happen. + */ + if(*dev->addr & Intrpend) + return 0; + if(*dev->addr & Dbr){ + *dev->data = msgout[n]; + n++; + } + } + break; + + case 0x1F: /* transfer complete - MSGIN */ + case 0x4F: /* unexpected MSGIN */ + /* + * Collect the response. The first 3 bytes + * of an extended message are: + * 0. extended message code (0x01) + * 1. extended message length (0x03) + * 2. sync. tranfer req. code (0x01) + * Only other defined code with length >1 is + * 'modify data pointer', which has length 7; + * we better not receive anything much longer... + * Adjust the number of bytes to collect once we see + * how long the response is (byte 2). + * If we collect anything other than an extended message + * or sync. transfer req. response, toss it + * and assume async. + */ + *dev->addr = Rtchi; + *dev->data = 0x00; + *dev->data = 0x00; + *dev->data = 5; + *dev->addr = Rcmd; + *dev->data = Ctinfo; + *dev->addr = Rdata; + for(i = sizeof(msgin)-1, n = 0; n < i; n++){ + while((*dev->addr & Dbr) == 0){ + if(*dev->addr & Intrpend) + return 0; + } + msgin[n] = *dev->data; + if(n == 1) + i = msgin[1] + 2; + } + + if(msgin[2] == 0x01 && (n = msgin[3]) && n != 0xFF){ + if(n < 0x40) + n = 0x02; + else if(n < 0x80) + n = 0x04; + else + n = 0; + n |= msgin[4]<<4; + tp->sync = n; + } + break; + + case 0x4E: + *dev->addr = Rcmd; + *dev->data = Csbt|Ctinfo; + *dev->addr = Rdata; + while((*dev->addr & Dbr) == 0){ + if(*dev->addr & Intrpend) + return 0; + } + *dev->data = Mnop; + break; + + case 0x20: /* transfer info paused */ + delay(7); + *dev->addr = Rcmd; + *dev->data = Cnack; + break; + + case 0x1A: + case 0x8A: + /* + * Fretry should be set, so we will + * pick up from here. + */ + *dev->addr = Rcmdphase; + *dev->data = 0x30; /* command phase has begun */ + complete(dev); + break; + + default: + return 1; + } + return 0; +} + +static void +saveptr(Target *tp) +{ + ulong count; + Target *dev; + + *tp->dmafls = 1; + + dev = tp->ctlr; + /* + * The transfer count register contains the + * number of bytes NOT transferred. + */ + *dev->addr = Rtchi; + count = (*dev->data & 0xFF)<<16; + count += (*dev->data & 0xFF)<<8; + count += (*dev->data & 0xFF); + + tp->dmaaddr += tp->dmalen - count; + tp->dmalen = count; + + *dev->addr = Rcmd; + *dev->data = tp->transfer; +} + +int +scsistatus(Target *ctlr) +{ + ulong status; + + *ctlr->addr = Rlun; + status = *ctlr->data & 0xFF; /* target status */ + status |= *ctlr->data<<8; /* command phase */ + + return status; +} + +void +scsiintr(int ctlrno) +{ + uchar status, x; + Target *ctlr, *tp; + + ctlr = &target[ctlrno][CtlrID]; + tp = ctlr->active; + + *ctlr->addr = Rstatus; + status = *ctlr->data; +print("scsiintr: #%2.2lux\n", status); + if(tp == 0) + return; + + switch(status){ + case 0x42: /* timeout */ + tp->flags &= ~Fretry; + scsistatus(ctlr); /* Must read lun register */ + complete(ctlr); + return; + + case 0x16: /* select-and-transfer complete */ + /* + * The target status comes back in the Rlun + * register. Returned status is combined + * with the final command phase, which + * should be 0x60. + */ + tp->status = scsistatus(ctlr); /* target status */ + complete(ctlr); + return; + + case 0x21: /* save data pointers */ + saveptr(tp); + return; + + case 0x4B: /* unexpected status phase */ + *ctlr->addr = Rcmdphase; + *ctlr->data = 0x46; /* */ + *ctlr->addr = Rtchi; + *ctlr->data = 0; + *ctlr->data = 0; + *ctlr->data = 0; + *ctlr->addr = Rcmd; + *ctlr->data = tp->transfer; + return; + + case 0x85: /* disconnect */ + if(tp->flags & Fidentifying){ + *ctlr->addr = Rcmdphase; + *ctlr->data = 0x00; /* complete retry */ + complete(ctlr); + return; + } + disconnect(ctlr); + return; + + case 0x81: + *ctlr->addr = Rsrcid; + x = *ctlr->data; + if(x & Siv){ /* source ID valid */ + reselect(ctlr, x & 0x07); + *ctlr->addr = Rcmdphase; + *ctlr->data = 0x44; /* reselected by target */ + start(ctlr->active); + return; + } + break; + + default: + if((tp->flags & Fidentifying) && identify(tp, status) == 0) + return; + break; + } + delay(1000); + print("scsi intr status #%.2ux\n", status); + print("scsi aux status #%.2ux\n", *ctlr->addr & 0xFF); + for(*ctlr->addr = 0x00, x = 0x00; x < 0x19; x++){ + if((x & 0x07) == 0x07) + print("#%.2ux: #%.2ux\n", x, *ctlr->data & 0xFF); + else + print("#%.2ux: #%.2ux ", x, *ctlr->data & 0xFF); + } + panic("scsiintr\n"); +} + +void +scsiicltr(int ctlrnr, uchar *data, uchar *addr, ulong *cnt, uchar *fls, ulong map) +{ + int i; + uchar status; + Target *ctlr, *tp; + + ctlr = &target[ctlrnr][CtlrID]; + + ctlr->data = data; + ctlr->addr = addr; + ctlr->base = target[ctlrnr]; + + if(*ctlr->addr & Intrpend) { + *ctlr->addr = Rstatus; + status = *ctlr->data; + USED(status); + } + + /* + * Give the chip a soft reset. This will + * cause an interrupt. Wait for it and clear it. + */ + *ctlr->addr = Rownid; + *ctlr->data = Fs16MHz|Eaf|CtlrID; + *ctlr->addr = Rcmd; + *ctlr->data = Creset; + + while((*ctlr->addr & Intrpend) == 0) + delay(250); + + *ctlr->addr = Rstatus; + status = *ctlr->data; + USED(status); + + /* + * Freselect means enable the chip to + * respond to reselects. + */ + ctlr->flags = /*Freselect|*/Fidentified; + + *ctlr->addr = Rcontrol; + *ctlr->data = BurstDma|Edi|Idi|Hsp; + *ctlr->addr = Rtimeout; + *ctlr->data = 40; /* 200ms */ + *ctlr->addr = Rsrcid; + + if(ctlr->flags & Freselect) + *ctlr->data = Er; + else + *ctlr->data = 0x00; + + for(i = 0; i < NTarget; i++){ + if(i == CtlrID) + continue; + + tp = &target[ctlrnr][i]; + tp->lun = 0; + tp->target = i; + tp->ctlr = ctlr; + tp->dmacnt = cnt; + tp->dmafls = fls; + tp->dmamap = map; + + if(ctlr->flags & Freselect){ + tp->flags = 0; + tp->transfer = CtransferATN; + } + else { + tp->flags = Fidentified; + tp->transfer = Ctransfer; + } + } +} + +/* + * Called at boot time. + * Initialise the hardware. + * There will be an interrupt in reponse + * to the reset. + */ +void +resetscsi(void) +{ + uchar m3; +print("scsi init\n"); + /* + * Reset both scsi busses + */ + m3 = MODEREG->promenet; + m3 &= ~((1<<6)|(1<<7)); + MODEREG->promenet = m3 | (1<<6) | (1<<7); + delay(500); + MODEREG->promenet = m3; + delay(100); + + scsiicltr(0, SCSI0DATA, SCSI0ADDR, SCSI0CNT, SCSI0FLS, 0x1fc0); + scsiicltr(1, SCSI1DATA, SCSI1ADDR, SCSI1CNT, SCSI1FLS, 0x1f80); +} + +static void +dmamap(Target *tp) +{ + ulong y, len, addr, index, off; + + *tp->dmafls = 1; + + len = tp->dmalen; + if(len == 0) + return; + + addr = tp->dmaaddr; + index = tp->dmamap; + for(y = addr+len+BY2PG; addr < y; addr += BY2PG){ + *WRITEMAP = (index<<16)|(addr>>12)&0xFFFF; + index++; + } + off = tp->dmaaddr & (BY2PG-1); + if(tp->rflag == 0) + off |= 1<<15; + + *tp->dmacnt = off; +} + +/* + * Known to devscsi.c. + */ +int scsidebugs[8]; +int scsiownid = CtlrID; + +void +initscsi(void) +{ +} diff --git a/power/scsibuf.c b/power/scsibuf.c new file mode 100644 index 0000000000000000000000000000000000000000..5037146fafde59c7ea9247c8111b2c4ad3b19abb --- /dev/null +++ b/power/scsibuf.c @@ -0,0 +1,79 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "io.h" + +/* + * The following routines + * scsibufreset, scsibuf, scsifree and scsialloc + * provide a pool of buffers used for scsi i/o. + * This is simplistic at best, and opportunities for + * deadlock abound. + */ +#define Nbuf 2 + +struct { + Lock; + Scsibuf *free; +} scsibufalloc; + +Scsibuf * +scsialloc(ulong n) +{ + void *x; + Scsibuf *b; + + if(n > 512*1024) + panic("scsialloc too big: %d", n); + + b = xalloc(sizeof(Scsibuf)); + x = xalloc(n); + + b->phys = x; + b->virt = x; + + return b; +} + +void +scsibufreset(ulong datasize) +{ + int i; + + for(i = 0; i < Nbuf; i++) + scsifree(scsialloc(datasize)); +} + +/* + * get a scsi io buffer of DATASIZE size + */ +Scsibuf * +scsibuf(void) +{ + Scsibuf *b; + + for(;;) { + lock(&scsibufalloc); + b = scsibufalloc.free; + if(b != 0) { + scsibufalloc.free = b->next; + unlock(&scsibufalloc); + return b; + } + unlock(&scsibufalloc); + resrcwait(0); + } + return 0; /* not reached */ +} + +void +scsifree(Scsibuf *b) +{ + lock(&scsibufalloc); + b->next = scsibufalloc.free; + scsibufalloc.free = b; + unlock(&scsibufalloc); +} diff --git a/power/trap.c b/power/trap.c index e641fb8af88d7fcbc6e7f2f54e517fd8f5fe54ed..acaf65ed608ba3c4a26887ab76fb9820fdc7ee51 100644 --- a/power/trap.c +++ b/power/trap.c @@ -258,9 +258,9 @@ intr(Ureg *ur) if(v & (1<<2)) lanceintr(); if(v & (1<<1)) - print("SCSI 1 interrupt\n"); + scsiintr(1); if(v & (1<<0)) - print("SCSI 0 interrupt\n"); + scsiintr(0); } } /* @@ -415,7 +415,7 @@ notify(Ureg *ur) sp = ur->usp - sizeof(Ureg); if(sp&0x3 || !okaddr((ulong)u->notify, BY2WD, 0) - || !okaddr(sp-ERRLEN-3*BY2WD, sizeof(Ureg)+ERRLEN-3*BY2WD, 1)) { + || !okaddr(sp-ERRLEN-3*BY2WD, sizeof(Ureg)+ERRLEN+3*BY2WD, 1)) { pprint("suicide: bad address or sp in notify\n"); qunlock(&u->p->debug); pexit("Suicide", 0);