diff --git a/sys/include/fis.h b/sys/include/fis.h new file mode 100644 index 000000000..9ec0ba6aa --- /dev/null +++ b/sys/include/fis.h @@ -0,0 +1,163 @@ +#pragma lib "libfis.a" +#pragma src "/sys/src/libfis" + +/* ata errors */ +enum { + Emed = 1<<0, /* media error */ + Enm = 1<<1, /* no media */ + Eabrt = 1<<2, /* abort */ + Emcr = 1<<3, /* media change request */ + Eidnf = 1<<4, /* no user-accessible address */ + Emc = 1<<5, /* media change */ + Eunc = 1<<6, /* data error */ + Ewp = 1<<6, /* write protect */ + Eicrc = 1<<7, /* interface crc error */ + + Efatal = Eidnf|Eicrc, /* must sw reset */ +}; + +/* ata status */ +enum { + ASerr = 1<<0, /* error */ + ASdrq = 1<<3, /* request */ + ASdf = 1<<5, /* fault */ + ASdrdy = 1<<6, /* ready */ + ASbsy = 1<<7, /* busy */ + + ASobs = 1<<1|1<<2|1<<4, +}; + +enum { + /* fis types */ + H2dev = 0x27, + D2host = 0x34, + + /* fis flags bits */ + Fiscmd = 0x80, + + /* ata bits */ + Ataobs = 0xa0, + Atalba = 0x40, + + /* nominal fis size (fits any fis) */ + Fissize = 0x20, +}; + +/* sata device-to-host (0x27) fis layout */ +enum { + Ftype, + Fflags, + Fcmd, + Ffeat, + Flba0, + Flba8, + Flba16, + Fdev, + Flba24, + Flba32, + Flba40, + Ffeat8, + Fsc, + Fsc8, + Ficc, /* isochronous cmd completion */ + Fcontrol, +}; + +/* sata host-to-device fis (0x34) differences */ +enum{ + Fioport = 1, + Fstatus, + Frerror, +}; + +/* ata protcol type */ +enum{ + Pnd = 0<<0, /* data direction */ + Pin = 1<<0, + Pout = 2<<0, + Pdatam = 3<<0, + + Ppio = 1<<2, /* ata protocol */ + Pdma = 2<<2, + Pdmq = 3<<2, + Preset = 4<<2, + Pdiag = 5<<2, + Ppkt = 6<<2, + Pprotom = 7<<2, + + P48 = 0<<5, /* command “size” */ + P28 = 1<<5, + Pcmdszm = 1<<5, + + Pssn = 0<<6, /* sector size */ + P512 = 1<<6, + Pssm = 1<<6, +}; + +typedef struct Sfis Sfis; +struct Sfis { + ushort feat; + uchar udma; + uchar speeds; + uint sig; + uint lsectsz; + uint physshift; /* log2(log/phys) */ + uint c; /* disgusting, no? */ + uint h; + uint s; +}; + +enum { + Dlba = 1<<0, /* required for sata */ + Dllba = 1<<1, + Dsmart = 1<<2, + Dpower = 1<<3, + Dnop = 1<<4, + Datapi = 1<<5, + Datapi16= 1<<6, + Data8 = 1<<7, + Dsct = 1<<8, + Dnflag = 9, +}; + +enum { + Pspinup = 1<<0, + Pidready = 1<<1, +}; + +void setfissig(Sfis*, uint); +int txmodefis(Sfis*, uchar*, uchar); +int atapirwfis(Sfis*, uchar*, uchar*, int, int); +int featfis(Sfis*, uchar*, uchar); +int flushcachefis(Sfis*, uchar*); +int identifyfis(Sfis*, uchar*); +int nopfis(Sfis*, uchar*, int); +int rwfis(Sfis*, uchar*, int, int, uvlong); +void skelfis(uchar*); +void sigtofis(Sfis*, uchar*); +uvlong fisrw(Sfis*, uchar*, int*); + +void idmove(char*, ushort*, int); +vlong idfeat(Sfis*, ushort*); +uvlong idwwn(Sfis*, ushort*); +int idss(Sfis*, ushort*); +int idpuis(ushort*); +ushort id16(ushort*, int); +uint id32(ushort*, int); +uvlong id64(ushort*, int); +char *pflag(char*, char*, Sfis*); +uint fistosig(uchar*); + +/* scsi */ +typedef struct Cfis Cfis; +struct Cfis { + uchar phyid; + uchar encid[8]; + uchar tsasaddr[8]; + uchar ssasaddr[8]; + uchar ict[2]; +}; + +void smpskelframe(Cfis*, uchar*, int); +uint sashash(uvlong); +uchar *sasbhash(uchar*, uchar*); diff --git a/sys/src/9/pc/ahci.h b/sys/src/9/pc/ahci.h index cfe28352d..e08932991 100644 --- a/sys/src/9/pc/ahci.h +++ b/sys/src/9/pc/ahci.h @@ -1,34 +1,8 @@ /* * advanced host controller interface (sata) - * © 2007 coraid, inc + * © 2007-9 coraid, inc */ -/* ata errors */ -enum { - Emed = 1<<0, /* media error */ - Enm = 1<<1, /* no media */ - Eabrt = 1<<2, /* abort */ - Emcr = 1<<3, /* media change request */ - Eidnf = 1<<4, /* no user-accessible address */ - Emc = 1<<5, /* media change */ - Eunc = 1<<6, /* data error */ - Ewp = 1<<6, /* write protect */ - Eicrc = 1<<7, /* interface crc error */ - - Efatal = Eidnf|Eicrc, /* must sw reset */ -}; - -/* ata status */ -enum { - ASerr = 1<<0, /* error */ - ASdrq = 1<<3, /* request */ - ASdf = 1<<5, /* fault */ - ASdrdy = 1<<6, /* ready */ - ASbsy = 1<<7, /* busy */ - - ASobs = 1<<1|1<<2|1<<4, -}; - /* pci configuration */ enum { Abar = 5, @@ -47,26 +21,25 @@ enum { /* cap bits: supported features */ enum { - Hs64a = 1<<31, /* 64-bit addressing */ - Hsncq = 1<<30, /* ncq */ - Hssntf = 1<<29, /* snotification reg. */ - Hsmps = 1<<28, /* mech pres switch */ - Hsss = 1<<27, /* staggered spinup */ - Hsalp = 1<<26, /* aggressive link pm */ - Hsal = 1<<25, /* activity led */ - Hsclo = 1<<24, /* command-list override */ + H64a = 1<<31, /* 64-bit addressing */ + Hncq = 1<<30, /* ncq */ + Hsntf = 1<<29, /* snotification reg. */ + Hmps = 1<<28, /* mech pres switch */ + Hss = 1<<27, /* staggered spinup */ + Halp = 1<<26, /* aggressive link pm */ + Hal = 1<<25, /* activity led */ + Hclo = 1<<24, /* command-list override */ Hiss = 1<<20, /* for interface speed */ -// Hsnzo = 1<<19, - Hsam = 1<<18, /* ahci-mode only */ - Hspm = 1<<17, /* port multiplier */ -// Hfbss = 1<<16, + Ham = 1<<18, /* ahci-mode only */ + Hpm = 1<<17, /* port multiplier */ + Hfbs = 1<<16, /* fis-based switching */ Hpmb = 1<<15, /* multiple-block pio */ Hssc = 1<<14, /* slumber state */ Hpsc = 1<<13, /* partial-slumber state */ Hncs = 1<<8, /* n command slots */ Hcccs = 1<<7, /* coal */ Hems = 1<<6, /* enclosure mgmt. */ - Hsxs = 1<<5, /* external sata */ + Hxs = 1<<5, /* external sata */ Hnp = 1<<0, /* n ports */ }; @@ -77,6 +50,29 @@ enum { Hhr = 1<<0, /* hba reset */ }; +/* cap2 bits */ +enum { + Apts = 1<<2, /* automatic partial to slumber */ + Nvmp = 1<<1, /* nvmhci present; nvram */ + Boh = 1<<0, /* bios/os handoff supported */ +}; + +/* emctl bits */ +enum { + Pm = 1<<27, /* port multiplier support */ + Alhd = 1<<26, /* activity led hardware driven */ + Xonly = 1<<25, /* rx messages not supported */ + Smb = 1<<24, /* single msg buffer; rx limited */ + Esgpio = 1<<19, /* sgpio messages supported */ + Eses2 = 1<<18, /* ses-2 supported */ + Esafte = 1<<17, /* saf-te supported */ + Elmt = 1<<16, /* led msg types support */ + Emrst = 1<<9, /* reset all em logic */ + Tmsg = 1<<8, /* transmit message */ + Mr = 1<<0, /* message rx'd */ + Emtype = Esgpio | Eses2 | Esafte | Elmt, +}; + typedef struct { ulong cap; ulong ghc; @@ -87,6 +83,8 @@ typedef struct { ulong cccports; ulong emloc; ulong emctl; + ulong cap2; + ulong bios; } Ahba; enum { @@ -147,6 +145,8 @@ enum { Aalpe = 1<<26, /* aggressive link pm enable */ Adlae = 1<<25, /* drive led on atapi */ Aatapi = 1<<24, /* device is atapi */ + Apste = 1<<23, /* automatic slumber to partial cap */ + Afbsc = 1<<22, /* fis-based switching capable */ Aesp = 1<<21, /* external sata port */ Acpd = 1<<20, /* cold presence detect */ Ampsp = 1<<19, /* mechanical pres. */ @@ -164,6 +164,7 @@ enum { Ast = 1<<0, /* start */ Arun = Ast|Acr|Afre|Afr, + Apwr = Apod|Asud, }; /* ctl register bits */ @@ -173,13 +174,41 @@ enum { Adet = 1<<0, /* device detection */ }; +/* sstatus register bits */ +enum{ + /* sstatus det */ + Smissing = 0<<0, + Spresent = 1<<0, + Sphylink = 3<<0, + Sbist = 4<<0, + Smask = 7<<0, + + /* sstatus speed */ + Gmissing = 0<<4, + Gi = 1<<4, + Gii = 2<<4, + Giii = 3<<4, + Gmask = 7<<4, + + /* sstatus ipm */ + Imissing = 0<<8, + Iactive = 1<<8, + Isleepy = 2<<8, + Islumber = 6<<8, + Imask = 7<<8, + + SImask = Smask | Imask, + SSmask = Smask | Isleepy, +}; + #define sstatus scr0 #define sctl scr2 #define serror scr1 #define sactive scr3 +#define ntf scr4 typedef struct { - ulong list; /* PxCLB must be 1kb aligned. */ + ulong list; /* PxCLB must be 1kb aligned */ ulong listhi; ulong fis; /* 256-byte aligned */ ulong fishi; @@ -194,9 +223,10 @@ typedef struct { ulong scr1; ulong scr3; ulong ci; /* command issue */ - ulong ntf; - uchar res2[8]; - ulong vendor; + ulong scr4; + ulong fbs; + ulong res2[11]; + ulong vendor[4]; } Aport; /* in host's memory; not memory mapped */ @@ -244,26 +274,49 @@ typedef struct { Aprdt prdt; } Actab; +/* enclosure message header */ +enum { + Mled = 0, + Msafte = 1, + Mses2 = 2, + Msgpio = 3, +}; + +typedef struct { + uchar dummy; + uchar msize; + uchar dsize; + uchar type; + uchar hba; /* bits 0:4 are the port */ + uchar pm; + uchar led[2]; +} Aledmsg; + +enum { + Aled = 1<<0, + Locled = 1<<3, + Errled = 1<<6, + + Ledoff = 0, + Ledon = 1, +}; + +typedef struct { + uint encsz; + ulong *enctx; + ulong *encrx; +} Aenc; + enum { Ferror = 1, Fdone = 2, }; -enum { - Dllba = 1, - Dsmart = 1<<1, - Dpower = 1<<2, - Dnop = 1<<3, - Datapi = 1<<4, - Datapi16= 1<<5, -}; - typedef struct { QLock; Rendez; uchar flag; - uchar feat; - uchar smart; + Sfis; Afis fis; Alist *list; Actab *ctab; diff --git a/sys/src/9/pc/mkfile b/sys/src/9/pc/mkfile index c5b7dfbab..a55801d54 100644 --- a/sys/src/9/pc/mkfile +++ b/sys/src/9/pc/mkfile @@ -67,6 +67,7 @@ LIB=\ /$objtype/lib/libc.a\ /$objtype/lib/libsec.a\ /$objtype/lib/libmp.a\ + /$objtype/lib/libfis.a\ ETHER=`{echo devether.c ether*.c | sed 's/\.c/.'$O'/g'} VGA=`{echo devvga.c screen.c vga*.c | sed 's/\.c/.'$O'/g'} diff --git a/sys/src/9/pc/pccpuf b/sys/src/9/pc/pccpuf index 42ea02ea4..6b5c4281e 100644 --- a/sys/src/9/pc/pccpuf +++ b/sys/src/9/pc/pccpuf @@ -75,11 +75,13 @@ misc uarti8250 uartpci pci - sdata pci sdscsi + sdaoe + sdide pci sdscsi sd53c8xx pci sdscsi sdmylex pci sdscsi - sdiahci pci sdscsi - sdaoe + sdiahci pci sdscsi led + sdodin pci sdscsi led + sdloop vga3dfx +cur vgaark2000pv +cur diff --git a/sys/src/9/pc/pcf b/sys/src/9/pc/pcf index 7feb36052..1d8ece9cb 100644 --- a/sys/src/9/pc/pcf +++ b/sys/src/9/pc/pcf @@ -81,11 +81,13 @@ misc archmp mp apic mtrr - sdata pci sdscsi + sdaoe + sdide pci sdscsi sd53c8xx pci sdscsi sdmylex pci sdscsi - sdiahci pci sdscsi - sdaoe + sdiahci pci sdscsi led + sdodin pci sdscsi led + sdloop uarti8250 uartpci pci diff --git a/sys/src/9/pc/sdiahci.c b/sys/src/9/pc/sdiahci.c index 73e46fc63..98e8d7fc8 100644 --- a/sys/src/9/pc/sdiahci.c +++ b/sys/src/9/pc/sdiahci.c @@ -1,6 +1,6 @@ /* * intel/amd ahci sata controller - * copyright © 2007-8 coraid, inc. + * copyright © 2007-10 coraid, inc. */ #include "u.h" @@ -11,20 +11,31 @@ #include "io.h" #include "../port/error.h" #include "../port/sd.h" +#include #include "ahci.h" +#include "../port/led.h" +#pragma varargck type "T" int #define dprint(...) if(debug) iprint(__VA_ARGS__); else USED(debug) -#define idprint(...) if(prid) iprint(__VA_ARGS__); else USED(prid) -#define aprint(...) if(datapi) iprint(__VA_ARGS__); else USED(datapi) +#define idprint(...) if(prid) print(__VA_ARGS__); else USED(prid) +#define aprint(...) if(datapi) print(__VA_ARGS__); else USED(datapi) +#define ledprint(...) if(dled) print(__VA_ARGS__); else USED(dled) +#define Pciwaddrh(a) 0 #define Tname(c) tname[(c)->type] +#define Ticks MACHP(0)->ticks +#define MS2TK(t) (((ulong)(t)*HZ)/1000) enum { NCtlr = 4, NCtlrdrv= 32, NDrive = NCtlr*NCtlrdrv, + Fahdrs = 4, + Read = 0, Write, + + Eesb = 1<<0, /* must have (Eesb & Emtype) == 0 */ }; /* pci space configuration */ @@ -38,16 +49,16 @@ enum { Tesb, Tich, Tsb600, - Tunk, + Tjmicron, + Tahci, }; -#define Intel(x) ((x)->pci->vid == 0x8086) - static char *tname[] = { "63xxesb", "ich", "sb600", - "unk", + "jmicron", + "ahci", }; enum { @@ -80,21 +91,21 @@ enum { DMautoneg, DMsatai, DMsataii, + DMsataiii, + DMlast, }; -static char *modename[] = { +static char *modes[DMlast] = { "auto", "satai", "sataii", + "sataiii", }; -static char *flagname[] = { - "llba", - "smart", - "power", - "nop", - "atapi", - "atapi16", +typedef struct Htab Htab; +struct Htab { + ulong bit; + char *name; }; typedef struct { @@ -105,29 +116,35 @@ typedef struct { char name[10]; Aport *port; Aportm portm; - Aportc portc; /* redundant ptr to port and portm */ + Aportc portc; /* redundant ptr to port and portm. */ + Ledport; - uchar mediachange; + uchar drivechange; uchar state; - uchar smartrs; uvlong sectors; - ulong secsize; - ulong intick; /* start tick of current transfer */ + uint secsize; + ulong totick; ulong lastseen; - int wait; - uchar mode; /* DMautoneg, satai or sataii */ + uint wait; + uchar mode; uchar active; char serial[20+1]; char firmware[8+1]; char model[40+1]; + uvlong wwn; ushort info[0x200]; - int driveno; /* ctlr*NCtlrdrv + unit */ - /* controller port # != driveno when not all ports are enabled */ - int portno; + /* + * ahci allows non-sequential ports. + * to avoid this hassle, we let + * driveno ctlr*NCtlrdrv + unit + * portno nth available port + */ + uint driveno; + uint portno; } Drive; struct Ctlr { @@ -141,11 +158,12 @@ struct Ctlr { uchar *mmio; ulong *lmmio; Ahba *hba; + Aenc; + uint enctype; Drive rawdrive[NCtlrdrv]; Drive* drive[NCtlrdrv]; int ndrive; - int mport; }; static Ctlr iactlr[NCtlr]; @@ -155,10 +173,10 @@ static int niactlr; static Drive *iadrive[NDrive]; static int niadrive; -/* these are fiddled in iawtopctl() */ static int debug; static int prid = 1; static int datapi; +static int dled; static char stab[] = { [0] 'i', 'm', @@ -186,13 +204,13 @@ static char ntab[] = "0123456789abcdef"; static void preg(uchar *reg, int n) { - int i; char buf[25*3+1], *e; + int i; e = buf; for(i = 0; i < n; i++){ - *e++ = ntab[reg[i]>>4]; - *e++ = ntab[reg[i]&0xf]; + *e++ = ntab[reg[i] >> 4]; + *e++ = ntab[reg[i] & 0xf]; *e++ = ' '; } *e++ = '\n'; @@ -203,7 +221,7 @@ preg(uchar *reg, int n) static void dreg(char *s, Aport *p) { - dprint("ahci: %stask=%lux; cmd=%lux; ci=%lux; is=%lux\n", + dprint("%stask=%lux; cmd=%lux; ci=%lux; is=%lux\n", s, p->task, p->cmd, p->ci, p->isr); } @@ -219,7 +237,7 @@ esleep(int ms) typedef struct { Aport *p; int i; -}Asleep; +} Asleep; static int ahciclear(void *v) @@ -242,102 +260,83 @@ aesleep(Aportm *m, Asleep *a, int ms) static int ahciwait(Aportc *c, int ms) { - Asleep as; Aport *p; + Asleep as; p = c->p; p->ci = 1; as.p = p; as.i = 1; aesleep(c->m, &as, ms); - if((p->task&1) == 0 && p->ci == 0) + if((p->task & 1) == 0 && p->ci == 0) return 0; - dreg("ahciwait timeout ", c->p); + dreg("ahciwait fail/timeout ", c->p); return -1; } +static void +mkalist(Aportm *m, uint flags, uchar *data, int len) +{ + Actab *t; + Alist *l; + Aprdt *p; + + t = m->ctab; + l = m->list; + l->flags = flags | 0x5; + l->len = 0; + l->ctab = PCIWADDR(t); + l->ctabhi = Pciwaddrh(t); + if(data){ + l->flags |= 1<<16; + p = &t->prdt; + p->dba = PCIWADDR(data); + p->dbahi = Pciwaddrh(data); + p->count = 1<<31 | len - 2 | 1; + } +} + static int nop(Aportc *pc) { uchar *c; - Actab *t; - Alist *l; if((pc->m->feat & Dnop) == 0) return -1; - - t = pc->m->ctab; - c = t->cfis; - - memset(c, 0, 0x20); - c[0] = 0x27; - c[1] = 0x80; - c[2] = 0x00; - c[7] = 0xa0; /* obsolete device bits */ - - l = pc->m->list; - l->flags = Lwrite | 0x5; - l->len = 0; - l->ctab = PCIWADDR(t); - l->ctabhi = 0; - + c = pc->m->ctab->cfis; + nopfis(pc->m, c, 0); + mkalist(pc->m, Lwrite, 0, 0); return ahciwait(pc, 3*1000); } static int -setfeatures(Aportc *pc, uchar f) +setfeatures(Aportc *pc, uchar f, uint w) { uchar *c; - Actab *t; - Alist *l; - t = pc->m->ctab; - c = t->cfis; - - memset(c, 0, 0x20); - c[0] = 0x27; - c[1] = 0x80; - c[2] = 0xef; - c[3] = f; - c[7] = 0xa0; /* obsolete device bits */ - - l = pc->m->list; - l->flags = Lwrite | 0x5; - l->len = 0; - l->ctab = PCIWADDR(t); - l->ctabhi = 0; - - return ahciwait(pc, 3*1000); + c = pc->m->ctab->cfis; + featfis(pc->m, c, f); + mkalist(pc->m, Lwrite, 0, 0); + return ahciwait(pc, w); } +/* + * ata 7, required for sata, requires that all devices "support" + * udma mode 5, however sata:pata bridges allow older devices + * which may not. the innodisk satadom, for example allows + * only udma mode 2. on the assumption that actual udma is + * taking place on these bridges, we set the highest udma mode + * available, or pio if there is no udma mode available. + */ static int -setudmamode(Aportc *pc, uchar f) +settxmode(Aportc *pc, uchar f) { uchar *c; - Actab *t; - Alist *l; - /* hack */ - if((pc->p->sig >> 16) == 0xeb14) + c = pc->m->ctab->cfis; + if(txmodefis(pc->m, c, f) == -1) return 0; - - t = pc->m->ctab; - c = t->cfis; - - memset(c, 0, 0x20); - c[0] = 0x27; - c[1] = 0x80; - c[2] = 0xef; - c[3] = 3; /* set transfer mode */ - c[7] = 0xa0; /* obsolete device bits */ - c[12] = 0x40 | f; /* sector count */ - - l = pc->m->list; - l->flags = Lwrite | 0x5; - l->len = 0; - l->ctab = PCIWADDR(t); - l->ctabhi = 0; - + mkalist(pc->m, Lwrite, 0, 0); return ahciwait(pc, 3*1000); } @@ -351,7 +350,7 @@ asleep(int ms) } static int -ahciportreset(Aportc *c) +ahciportreset(Aportc *c, uint mode) { ulong *cmd, i; Aport *p; @@ -360,228 +359,71 @@ ahciportreset(Aportc *c) cmd = &p->cmd; *cmd &= ~(Afre|Ast); for(i = 0; i < 500; i += 25){ - if((*cmd&Acr) == 0) + if((*cmd & Acr) == 0) break; asleep(25); } - p->sctl = 1|(p->sctl&~7); + p->sctl = 3*Aipm | 0*Aspd | Adet; delay(1); - p->sctl &= ~7; - return 0; -} - -static int -smart(Aportc *pc, int n) -{ - uchar *c; - Actab *t; - Alist *l; - - if((pc->m->feat&Dsmart) == 0) - return -1; - - t = pc->m->ctab; - c = t->cfis; - - memset(c, 0, 0x20); - c[0] = 0x27; - c[1] = 0x80; - c[2] = 0xb0; - c[3] = 0xd8 + n; /* able smart */ - c[5] = 0x4f; - c[6] = 0xc2; - c[7] = 0xa0; - - l = pc->m->list; - l->flags = Lwrite | 0x5; - l->len = 0; - l->ctab = PCIWADDR(t); - l->ctabhi = 0; - - if(ahciwait(pc, 1000) == -1 || pc->p->task & (1|32)){ - dprint("ahci: smart fail %lux\n", pc->p->task); -// preg(pc->m->fis.r, 20); - return -1; - } - if(n) - return 0; - return 1; -} - -static int -smartrs(Aportc *pc) -{ - uchar *c; - Actab *t; - Alist *l; - - t = pc->m->ctab; - c = t->cfis; - - memset(c, 0, 0x20); - c[0] = 0x27; - c[1] = 0x80; - c[2] = 0xb0; - c[3] = 0xda; /* return smart status */ - c[5] = 0x4f; - c[6] = 0xc2; - c[7] = 0xa0; - - l = pc->m->list; - l->flags = Lwrite | 0x5; - l->len = 0; - l->ctab = PCIWADDR(t); - l->ctabhi = 0; - - c = pc->m->fis.r; - if(ahciwait(pc, 1000) == -1 || pc->p->task & (1|32)){ - dprint("ahci: smart fail %lux\n", pc->p->task); - preg(c, 20); - return -1; - } - if(c[5] == 0x4f && c[6] == 0xc2) - return 1; + p->sctl = 3*Aipm | mode*Aspd; return 0; } static int ahciflushcache(Aportc *pc) { - uchar *c, llba; - Actab *t; - Alist *l; - static uchar tab[2] = {0xe7, 0xea}; + uchar *c; - llba = pc->m->feat&Dllba? 1: 0; - t = pc->m->ctab; - c = t->cfis; - - memset(c, 0, 0x20); - c[0] = 0x27; - c[1] = 0x80; - c[2] = tab[llba]; - c[7] = 0xa0; - - l = pc->m->list; - l->flags = Lwrite | 0x5; - l->len = 0; - l->ctab = PCIWADDR(t); - l->ctabhi = 0; + c = pc->m->ctab->cfis; + flushcachefis(pc->m, c); + mkalist(pc->m, Lwrite, 0, 0); if(ahciwait(pc, 60000) == -1 || pc->p->task & (1|32)){ - dprint("ahciflushcache: fail %lux\n", pc->p->task); -// preg( pc->m->fis.r, 20); + dprint("ahciflushcache fail %lux\n", pc->p->task); +// preg(pc->m->fis.r, 20); return -1; } return 0; } -static ushort -gbit16(void *a) -{ - uchar *i; - - i = a; - return i[1]<<8 | i[0]; -} - -static ulong -gbit32(void *a) -{ - ulong j; - uchar *i; - - i = a; - j = i[3] << 24; - j |= i[2] << 16; - j |= i[1] << 8; - j |= i[0]; - return j; -} - -static uvlong -gbit64(void *a) -{ - uchar *i; - - i = a; - return (uvlong)gbit32(i+4) << 32 | gbit32(a); -} - static int -ahciidentify0(Aportc *pc, void *id, int atapi) +ahciidentify0(Aportc *pc, void *id) { uchar *c; Actab *t; - Alist *l; - Aprdt *p; - static uchar tab[] = { 0xec, 0xa1, }; t = pc->m->ctab; c = t->cfis; - - memset(c, 0, 0x20); - c[0] = 0x27; - c[1] = 0x80; - c[2] = tab[atapi]; - c[7] = 0xa0; /* obsolete device bits */ - - l = pc->m->list; - l->flags = 1<<16 | 0x5; - l->len = 0; - l->ctab = PCIWADDR(t); - l->ctabhi = 0; - - memset(id, 0, 0x100); - p = &t->prdt; - p->dba = PCIWADDR(id); - p->dbahi = 0; - p->count = 1<<31 | (0x200-2) | 1; - + memset(id, 0, 0x200); + identifyfis(pc->m, c); + mkalist(pc->m, 0, id, 0x200); return ahciwait(pc, 3*1000); } static vlong -ahciidentify(Aportc *pc, ushort *id) +ahciidentify(Aportc *pc, ushort *id, uint *ss, char *d) { - int i, sig; + int i, n; vlong s; Aportm *m; m = pc->m; - m->feat = 0; - m->smart = 0; - i = 0; - sig = pc->p->sig >> 16; - if(sig == 0xeb14){ - m->feat |= Datapi; - i = 1; + for(i = 0;; i++){ + if(i > 5 || ahciidentify0(pc, id) != 0) + return -1; + n = idpuis(id); + if(n & Pspinup && setfeatures(pc, 7, 20*1000) == -1) + print("%s: puis spinup fail\n", d); + if(n & Pidready) + break; + print("%s: puis waiting\n", d); } - if(ahciidentify0(pc, id, i) == -1) + s = idfeat(m, id); + *ss = idss(m, id); + if(s == -1 || (m->feat&Dlba) == 0){ + if((m->feat&Dlba) == 0) + dprint("%s: no lba support\n", d); return -1; - - i = gbit16(id+83) | gbit16(id+86); - if(i & (1<<10)){ - m->feat |= Dllba; - s = gbit64(id+100); - }else - s = gbit32(id+60); - - if(m->feat&Datapi){ - i = gbit16(id+0); - if(i&1) - m->feat |= Datapi16; - } - - i = gbit16(id+83); - if((i>>14) == 1) { - if(i & (1<<3)) - m->feat |= Dpower; - i = gbit16(id+82); - if(i & 1) - m->feat |= Dsmart; - if(i & (1<<14)) - m->feat |= Dnop; } return s; } @@ -614,10 +456,10 @@ stop: return -1; stop1: /* extra check */ - dprint("ahci: clo clear %lx\n", a->task); + dprint("ahci: clo clear %lux\n", a->task); if(a->task & ASbsy) return -1; - *p |= Ast; + *p |= Afre | Ast; return 0; } @@ -625,52 +467,28 @@ static int ahcicomreset(Aportc *pc) { uchar *c; - Actab *t; - Alist *l; - dprint("ahcicomreset\n"); - dreg("ahci: comreset ", pc->p); + dreg("comreset ", pc->p); if(ahciquiet(pc->p) == -1){ - dprint("ahciquiet failed\n"); + dprint("ahci: ahciquiet failed\n"); return -1; } dreg("comreset ", pc->p); - t = pc->m->ctab; - c = t->cfis; - - memset(c, 0, 0x20); - c[0] = 0x27; - c[1] = 0x00; - c[7] = 0xa0; /* obsolete device bits */ - c[15] = 1<<2; /* srst */ - - l = pc->m->list; - l->flags = Lclear | Lreset | 0x5; - l->len = 0; - l->ctab = PCIWADDR(t); - l->ctabhi = 0; - + c = pc->m->ctab->cfis; + nopfis(pc->m, c, 1); + mkalist(pc->m, Lclear | Lreset, 0, 0); if(ahciwait(pc, 500) == -1){ - dprint("ahcicomreset: first command failed\n"); + dprint("ahci: comreset1 failed\n"); return -1; } microdelay(250); dreg("comreset ", pc->p); - memset(c, 0, 0x20); - c[0] = 0x27; - c[1] = 0x00; - c[7] = 0xa0; /* obsolete device bits */ - - l = pc->m->list; - l->flags = Lwrite | 0x5; - l->len = 0; - l->ctab = PCIWADDR(t); - l->ctabhi = 0; - + nopfis(pc->m, c, 0); + mkalist(pc->m, Lwrite, 0, 0); if(ahciwait(pc, 150) == -1){ - dprint("ahcicomreset: second command failed\n"); + dprint("ahci: comreset2 failed\n"); return -1; } dreg("comreset ", pc->p); @@ -706,7 +524,7 @@ stop: } /* - * § 6.2.2.1 first part; comreset handled by reset disk. + * §6.2.2.1 first part; comreset handled by reset disk. * - remainder is handled by configdisk. * - ahcirecover is a quick recovery from a failed command. */ @@ -729,7 +547,7 @@ ahcirecover(Aportc *pc) { ahciswreset(pc); pc->p->cmd |= Ast; - if(setudmamode(pc, 5) == -1) + if(settxmode(pc, pc->m->udma) == -1) return -1; return 0; } @@ -756,21 +574,19 @@ setupfis(Afis *f) } static void -ahciwakeup(Aport *p) +ahciwakeup(Aportc *c, uint mode) { ushort s; - s = p->sstatus; - if((s & 0xF00) != 0x600) + s = c->p->sstatus; + if((s & Isleepy) == 0) return; - if((s & 7) != 1){ /* not (device, no phy) */ - iprint("ahci: slumbering drive unwakable %ux\n", s); + if((s & Smask) != Spresent){ + print("ahci: slumbering drive missing %.3ux\n", s); return; } - p->sctl = 3*Aipm | 0*Aspd | Adet; - delay(1); - p->sctl &= ~7; -// iprint("ahci: wake %ux -> %ux\n", s, p->sstatus); + ahciportreset(c, mode); +// iprint("ahci: wake %.3ux -> %.3lux\n", s, c->p->sstatus); } static int @@ -788,29 +604,34 @@ ahciconfigdrive(Ahba *h, Aportc *c, int mode) m->ctab = malign(sizeof *m->ctab, 128); } - if(p->sstatus & 3 && h->cap & Hsss){ - /* device connected & staggered spin-up */ - dprint("ahci: configdrive: spinning up ... [%lux]\n", - p->sstatus); - p->cmd |= Apod|Asud; - asleep(1400); + p->list = PCIWADDR(m->list); + p->listhi = Pciwaddrh(m->list); + p->fis = PCIWADDR(m->fis.base); + p->fishi = Pciwaddrh(m->fis.base); + + p->cmd |= Afre; + + if((p->sstatus & Sbist) == 0 && (p->cmd & Apwr) != Apwr) + if((p->sstatus & Sphylink) == 0 && h->cap & Hss){ + /* staggered spin-up? */ + dprint("ahci: spin up ... [%.3lux]\n", p->sstatus); + p->cmd |= Apwr; + for(int i = 0; i < 1400; i += 50){ + if(p->sstatus & (Sphylink | Sbist)) + break; + asleep(50); + } } p->serror = SerrAll; - p->list = PCIWADDR(m->list); - p->listhi = 0; - p->fis = PCIWADDR(m->fis.base); - p->fishi = 0; - p->cmd |= Afre|Ast; - - if((p->sstatus & 0xF0F) == 0x601) /* drive coming up in slumbering? */ - ahciwakeup(p); - + if((p->sstatus & SSmask) == (Isleepy | Spresent)) + ahciwakeup(c, mode); /* disable power managment sequence from book. */ - p->sctl = (3*Aipm) | (mode*Aspd) | (0*Adet); + p->sctl = 3*Aipm | mode*Aspd | 0*Adet; p->cmd &= ~Aalpe; + p->cmd |= Ast; p->ie = IEM; return 0; @@ -843,22 +664,22 @@ countbits(ulong u) } static int -ahciconf(Ctlr *ctlr) +ahciconf(Ctlr *c) { + uint u; Ahba *h; - ulong u; - h = ctlr->hba = (Ahba*)ctlr->mmio; + h = c->hba = (Ahba*)c->mmio; u = h->cap; - if((u&Hsam) == 0) + if((u & Ham) == 0) h->ghc |= Hae; - print("#S/sd%c: ahci %s port %#p: sss %ld ncs %ld coal %ld " - "mports %ld led %ld clo %ld ems %ld\n", - ctlr->sdev->idno, tname[ctlr->type], h, - (u>>27) & 1, (u>>8) & 0x1f, (u>>7) & 1, u & 0x1f, (u>>25) & 1, - (u>>24) & 1, (u>>6) & 1); +// print("#S/sd%c: ahci %s port %#p: sss %d ncs %d coal %d " +// "mport %d led %d clo %d ems %d\n", +// c->sdev->idno, Tname(c), h, +// (u>>27) & 1, (u>>8) & 0x1f, (u>>7) & 1, u & 0x1f, (u>>25) & 1, +// (u>>24) & 1, (u>>6) & 1); return countbits(h->pi); } @@ -867,7 +688,7 @@ ahcihbareset(Ahba *h) { int wait; - h->ghc |= 1; + h->ghc |= Hhr; for(wait = 0; wait < 1000; wait += 100){ if(h->ghc == 0) return 0; @@ -876,36 +697,27 @@ ahcihbareset(Ahba *h) return -1; } -static void -idmove(char *p, ushort *a, int n) +static char* +dnam(Drive *d) { - int i; - char *op, *e; + char *s; - op = p; - for(i = 0; i < n/2; i++){ - *p++ = a[i] >> 8; - *p++ = a[i]; - } - *p = 0; - while(p > op && *--p == ' ') - *p = 0; - e = p; - for (p = op; *p == ' '; p++) - ; - memmove(op, p, n - (e - p)); + s = d->name; + if(d->unit && d->unit->name) + s = d->unit->name; + return s; } static int identify(Drive *d) { + uchar oserial[21]; ushort *id; vlong osectors, s; - uchar oserial[21]; SDunit *u; id = d->info; - s = ahciidentify(&d->portc, id); + s = ahciidentify(&d->portc, id, &d->secsize, dnam(d)); if(s == -1){ d->state = Derror; return -1; @@ -913,25 +725,22 @@ identify(Drive *d) osectors = d->sectors; memmove(oserial, d->serial, sizeof d->serial); - u = d->unit; d->sectors = s; - d->secsize = u->secsize; - if(d->secsize == 0) - d->secsize = 512; /* default */ - d->smartrs = 0; idmove(d->serial, id+10, 20); idmove(d->firmware, id+23, 8); idmove(d->model, id+27, 40); + d->wwn = idwwn(d->portc.m, id); + u = d->unit; memset(u->inquiry, 0, sizeof u->inquiry); u->inquiry[2] = 2; u->inquiry[3] = 2; u->inquiry[4] = sizeof u->inquiry - 4; memmove(u->inquiry+8, d->model, 40); - if(osectors != s || memcmp(oserial, d->serial, sizeof oserial) != 0){ - d->mediachange = 1; + if(osectors != s || memcmp(oserial, d->serial, sizeof oserial)){ + d->drivechange = 1; u->sectors = 0; } return 0; @@ -940,88 +749,102 @@ identify(Drive *d) static void clearci(Aport *p) { - if(p->cmd & Ast) { + if(p->cmd & Ast){ p->cmd &= ~Ast; p->cmd |= Ast; } } +static int +intel(Ctlr *c) +{ + return c->pci->vid == 0x8086; +} + +static int +ignoreahdrs(Drive *d) +{ + return d->portm.feat & Datapi && d->ctlr->type == Tsb600; +} + static void updatedrive(Drive *d) { - ulong cause, serr, s0, pr, ewake; - char *name; + ulong f, cause, serr, s0, pr, ewake; Aport *p; static ulong last; pr = 1; ewake = 0; + f = 0; p = d->port; cause = p->isr; + if(d->ctlr->type == Tjmicron) + cause &= ~Aifs; serr = p->serror; p->isr = cause; - name = "??"; - if(d->unit && d->unit->name) - name = d->unit->name; if(p->ci == 0){ - d->portm.flag |= Fdone; - wakeup(&d->portm); + f |= Fdone; pr = 0; }else if(cause & Adps) pr = 0; if(cause & Ifatal){ ewake = 1; - dprint("ahci: updatedrive: fatal\n"); + dprint("%s: fatal\n", dnam(d)); } if(cause & Adhrs){ - if(p->task & (1<<5|1)){ - dprint("ahci: Adhrs cause %lux serr %lux task %lux\n", - cause, serr, p->task); - d->portm.flag |= Ferror; + if(p->task & 33){ + if(ignoreahdrs(d) && serr & ErrE) + f |= Fahdrs; + dprint("%s: Adhrs cause %lux serr %lux task %lux\n", + dnam(d), cause, serr, p->task); + f |= Ferror; ewake = 1; } pr = 0; } if(p->task & 1 && last != cause) - dprint("%s: err ca %lux serr %lux task %lux sstat %lux\n", - name, cause, serr, p->task, p->sstatus); + dprint("%s: err ca %lux serr %lux task %lux sstat %.3lux\n", + dnam(d), cause, serr, p->task, p->sstatus); if(pr) - dprint("%s: upd %lux ta %lux\n", name, cause, p->task); + dprint("%s: upd %lux ta %lux\n", dnam(d), cause, p->task); if(cause & (Aprcs|Aifs)){ s0 = d->state; - switch(p->sstatus & 7){ - case 0: /* no device */ + switch(p->sstatus & Smask){ + case Smissing: d->state = Dmissing; break; - case 1: /* device but no phy comm. */ - if((p->sstatus & 0xF00) == 0x600) - d->state = Dnew; /* slumbering */ + case Spresent: + if((p->sstatus & Imask) == Islumber) + d->state = Dnew; else d->state = Derror; break; - case 3: /* device & phy comm. estab. */ - /* power mgnt crap for surprise removal */ + case Sphylink: + /* power mgnt crap for suprise removal */ p->ie |= Aprcs|Apcs; /* is this required? */ d->state = Dreset; break; - case 4: /* phy off-line */ + case Sbist: d->state = Doffline; break; } - dprint("%s: %s → %s [Apcrs] %lux\n", name, - diskstates[s0], diskstates[d->state], p->sstatus); - /* print pulled message here. */ + dprint("%s: %s → %s [Apcrs] %.3lux\n", dnam(d), diskstates[s0], + diskstates[d->state], p->sstatus); if(s0 == Dready && d->state != Dready) - idprint("%s: pulled\n", name); + idprint("%s: pulled\n", dnam(d)); if(d->state != Dready) - d->portm.flag |= Ferror; - ewake = 1; + f |= Ferror; + if(d->state != Dready || p->ci) + ewake = 1; } p->serror = serr; - if(ewake){ + if(ewake) clearci(p); + if(f){ + d->portm.flag = f; wakeup(&d->portm); } last = cause; @@ -1032,27 +855,23 @@ pstatus(Drive *d, ulong s) { /* * bogus code because the first interrupt is currently dropped. - * likely my fault. serror may be cleared at the wrong time. + * likely my fault. serror is maybe cleared at the wrong time. */ switch(s){ - case 0: /* no device */ + default: + print("%s: pstatus: bad status %.3lux\n", dnam(d), s); + case Smissing: d->state = Dmissing; break; - case 1: /* device but no phy. comm. */ + case Spresent: break; - case 2: /* should this be missing? need testcase. */ - dprint("ahci: pstatus 2\n"); - /* fallthrough */ - case 3: /* device & phy. comm. */ + case Sphylink: d->wait = 0; d->state = Dnew; break; - case 4: /* offline */ + case Sbist: d->state = Doffline; break; - case 6: /* ? not sure this makes sense. TODO */ - d->state = Dnew; - break; } } @@ -1062,7 +881,7 @@ configdrive(Drive *d) if(ahciconfigdrive(d->ctlr->hba, &d->portc, d->mode) == -1) return -1; ilock(d); - pstatus(d, d->port->sstatus & 7); + pstatus(d, d->port->sstatus & Smask); iunlock(d); return 0; } @@ -1075,9 +894,9 @@ resetdisk(Drive *d) p = d->port; det = p->sctl & 7; - stat = p->sstatus & 7; + stat = p->sstatus & Smask; state = (p->cmd>>28) & 0xf; - dprint("ahci: resetdisk: icc %ux det %d sdet %d\n", state, det, stat); + dprint("%s: resetdisk: icc %ux det %.3ux sdet %.3ux\n", dnam(d), state, det, stat); ilock(d); state = d->state; @@ -1085,57 +904,52 @@ resetdisk(Drive *d) d->portm.flag |= Ferror; clearci(p); /* satisfy sleep condition. */ wakeup(&d->portm); - if(stat != 3){ /* device absent or phy not communicating? */ + d->state = Derror; + iunlock(d); + + if(stat != Sphylink){ + ilock(d); d->state = Dportreset; iunlock(d); return; } - d->state = Derror; - iunlock(d); qlock(&d->portm); if(p->cmd&Ast && ahciswreset(&d->portc) == -1){ ilock(d); d->state = Dportreset; /* get a bigger stick. */ iunlock(d); - } else { + }else{ ilock(d); d->state = Dmissing; iunlock(d); - configdrive(d); } - dprint("ahci: resetdisk: %s → %s\n", - diskstates[state], diskstates[d->state]); + dprint("%s: resetdisk: %s → %s\n", dnam(d), diskstates[state], diskstates[d->state]); qunlock(&d->portm); } static int newdrive(Drive *d) { - char *name; + char *s; Aportc *c; Aportm *m; c = &d->portc; m = &d->portm; - name = d->unit->name; - if(name == 0) - name = "??"; - - if(d->port->task == 0x80) - return -1; qlock(c->m); - if(setudmamode(c, 5) == -1){ - dprint("%s: can't set udma mode\n", name); - goto lose; - } + setfissig(m, c->p->sig); if(identify(d) == -1){ - dprint("%s: identify failure\n", name); + dprint("%s: identify failure\n", dnam(d)); goto lose; } - if(m->feat & Dpower && setfeatures(c, 0x85) == -1){ + if(settxmode(c, m->udma) == -1){ + dprint("%s: can't set udma mode\n", dnam(d)); + goto lose; + } + if(m->feat & Dpower && setfeatures(c, 0x85, 3*1000) == -1){ m->feat &= ~Dpower; if(ahcirecover(c) == -1) goto lose; @@ -1147,13 +961,16 @@ newdrive(Drive *d) qunlock(c->m); - idprint("%s: %sLBA %,llud sectors: %s %s %s %s\n", d->unit->name, - (m->feat & Dllba? "L": ""), d->sectors, d->model, d->firmware, - d->serial, d->mediachange? "[mediachange]": ""); + s = ""; + if(m->feat & Dllba) + s = "L"; + idprint("%s: %sLBA %,lld sectors\n", dnam(d), s, d->sectors); + idprint(" %s %s %s %s\n", d->model, d->firmware, d->serial, + d->drivechange? "[newdrive]": ""); return 0; lose: - idprint("%s: can't be initialized\n", d->unit->name); + idprint("%s: can't be initialized\n", dnam(d)); ilock(d); d->state = Dnull; iunlock(d); @@ -1169,12 +986,12 @@ enum { }; static void -westerndigitalhung(Drive *d) +hangck(Drive *d) { - if((d->portm.feat&Datapi) == 0 && d->active && - TK2MS(MACHP(0)->ticks - d->intick) > 5000){ - dprint("%s: drive hung; resetting [%lux] ci %lx\n", - d->unit->name, d->port->task, d->port->ci); + if((d->portm.feat & Datapi) == 0 && d->active && + d->totick != 0 && (long)(Ticks - d->totick) > 0){ + dprint("%s: drive hung; resetting [%lux] ci %lux\n", + dnam(d), d->port->task, d->port->ci); d->state = Dreset; } } @@ -1188,13 +1005,13 @@ doportreset(Drive *d) i = -1; qlock(&d->portm); - if(ahciportreset(&d->portc) == -1) - dprint("ahci: doportreset: fails\n"); + if(ahciportreset(&d->portc, d->mode) == -1) + dprint("ahci: ahciportreset fails\n"); else i = 0; qunlock(&d->portm); - dprint("ahci: doportreset: portreset → %s [task %lux]\n", - diskstates[d->state], d->port->task); + dprint("ahci: portreset → %s [task %.4lux ss %.3lux]\n", + diskstates[d->state], d->port->task, d->port->sstatus); return i; } @@ -1205,86 +1022,82 @@ statechange(Drive *d) switch(d->state){ case Dnull: case Doffline: + if(d->unit) if(d->unit->sectors != 0){ d->sectors = 0; - d->mediachange = 1; + d->drivechange = 1; } - /* fallthrough */ case Dready: d->wait = 0; - break; } } +static uint +maxmode(Ctlr *c) +{ + return (c->hba->cap & 0xf*Hiss)/Hiss; +} + static void checkdrive(Drive *d, int i) { - ushort s; - char *name; + ushort s, sig; - if(d == nil) { - print("checkdrive: nil d\n"); - return; - } ilock(d); - if(d->unit == nil || d->port == nil) { - if(0) - print("checkdrive: nil d->%s\n", - d->unit == nil? "unit": "port"); - iunlock(d); - return; - } - name = d->unit->name; s = d->port->sstatus; if(s) - d->lastseen = MACHP(0)->ticks; + d->lastseen = Ticks; if(s != olds[i]){ - dprint("%s: status: %04ux -> %04ux: %s\n", - name, olds[i], s, diskstates[d->state]); + dprint("%s: status: %.3ux -> %.3ux: %s\n", + dnam(d), olds[i], s, diskstates[d->state]); olds[i] = s; d->wait = 0; } - westerndigitalhung(d); + hangck(d); switch(d->state){ case Dnull: case Dready: break; case Dmissing: case Dnew: - switch(s & 0x107){ - case 1: /* no device (pm), device but no phy. comm. */ - ahciwakeup(d->port); - /* fall through */ - case 0: /* no device */ + switch(s & (Iactive|Smask)){ + case Spresent: + ahciwakeup(&d->portc, d->mode); + case Smissing: break; default: - dprint("%s: unknown status %04ux\n", name, s); + dprint("%s: unknown status %.3ux\n", dnam(d), s); /* fall through */ - case 0x100: /* active, no device */ + case Iactive: /* active, no device */ if(++d->wait&Mphywait) break; reset: - if(++d->mode > DMsataii) - d->mode = 0; - if(d->mode == DMsatai){ /* we tried everything */ + if(d->mode == 0) + d->mode = maxmode(d->ctlr); + else + d->mode--; + if(d->mode == DMautoneg){ d->state = Dportreset; goto portreset; } - dprint("%s: reset; new mode %s\n", name, - modename[d->mode]); + dprint("%s: reset; new mode %s\n", dnam(d), + modes[d->mode]); iunlock(d); resetdisk(d); ilock(d); break; - case 0x103: /* active, device, phy. comm. */ + case Iactive | Sphylink: + if(d->unit == nil) + break; if((++d->wait&Midwait) == 0){ - dprint("%s: slow reset %04ux task=%lux; %d\n", - name, s, d->port->task, d->wait); + dprint("%s: slow reset %.3ux task=%lux; %d\n", + dnam(d), s, d->port->task, d->wait); goto reset; } s = (uchar)d->port->task; - if(s == 0x7f || ((d->port->sig >> 16) != 0xeb14 && - (s & ~0x17) != (1<<6))) + sig = d->port->sig >> 16; + if(s == 0x7f || s&ASbsy || + (sig != 0xeb14 && (s & ASdrdy) == 0)) break; iunlock(d); newdrive(d); @@ -1298,23 +1111,22 @@ reset: /* fallthrough */ case Derror: case Dreset: - dprint("%s: reset [%s]: mode %d; status %04ux\n", - name, diskstates[d->state], d->mode, s); + dprint("%s: reset [%s]: mode %d; status %.3ux\n", + dnam(d), diskstates[d->state], d->mode, s); iunlock(d); resetdisk(d); ilock(d); break; case Dportreset: portreset: - if(d->wait++ & 0xff && (s & 0x100) == 0) + if(d->wait++ & 0xff && (s & Iactive) == 0) break; - /* device is active */ - dprint("%s: portreset [%s]: mode %d; status %04ux\n", - name, diskstates[d->state], d->mode, s); + dprint("%s: portreset [%s]: mode %d; status %.3ux\n", + dnam(d), diskstates[d->state], d->mode, s); d->portm.flag |= Ferror; clearci(d->port); wakeup(&d->portm); - if((s & 7) == 0){ /* no device */ + if((s & Smask) == 0){ d->state = Dmissing; break; } @@ -1350,10 +1162,11 @@ iainterrupt(Ureg*, void *a) c = a; ilock(c); cause = c->hba->isr; - for(i = 0; i < c->mport; i++){ + for(i = 0; cause; i++){ m = 1 << i; if((cause & m) == 0) continue; + cause &= ~m; d = c->rawdrive + i; ilock(d); if(d->port->isr && c->hba->pi & m) @@ -1364,6 +1177,229 @@ iainterrupt(Ureg*, void *a) iunlock(c); } +static int +ahciencreset(Ctlr *c) +{ + Ahba *h; + + if(c->enctype == Eesb) + return 0; + h = c->hba; + h->emctl |= Emrst; + while(h->emctl & Emrst) + delay(1); + return 0; +} + +/* + * from the standard: (http://en.wikipedia.org/wiki/IBPI) + * rebuild is preferred as locate+fail; alternate 1hz fail + * we're going to assume no locate led. + */ + +enum { + Ledsleep = 125, /* 8hz */ + + N0 = Ledon*Aled, + L0 = Ledon*Aled | Ledon*Locled, + L1 = Ledon*Aled | Ledoff*Locled, + R0 = Ledon*Aled | Ledon*Locled | Ledon*Errled, + R1 = Ledon*Aled | Ledoff*Errled, + S0 = Ledon*Aled | Ledon*Locled /*| Ledon*Errled*/, /* botch */ + S1 = Ledon*Aled | Ledoff*Errled, + P0 = Ledon*Aled | Ledon*Errled, + P1 = Ledon*Aled | Ledoff*Errled, + F0 = Ledon*Aled | Ledon*Errled, + C0 = Ledon*Aled | Ledon*Locled, + C1 = Ledon*Aled | Ledoff*Locled, + +}; + +//static ushort led3[Ibpilast*8] = { +//[Ibpinone*8] 0, 0, 0, 0, 0, 0, 0, 0, +//[Ibpinormal*8] N0, N0, N0, N0, N0, N0, N0, N0, +//[Ibpirebuild*8] R0, R0, R0, R0, R1, R1, R1, R1, +//[Ibpilocate*8] L0, L1, L0, L1, L0, L1, L0, L1, +//[Ibpispare*8] S0, S1, S0, S1, S1, S1, S1, S1, +//[Ibpipfa*8] P0, P1, P0, P1, P1, P1, P1, P1, /* first 1 sec */ +//[Ibpifail*8] F0, F0, F0, F0, F0, F0, F0, F0, +//[Ibpicritarray*8] C0, C0, C0, C0, C1, C1, C1, C1, +//[Ibpifailarray*8] C0, C1, C0, C1, C0, C1, C0, C1, +//}; + +static ushort led2[Ibpilast*8] = { +[Ibpinone*8] 0, 0, 0, 0, 0, 0, 0, 0, +[Ibpinormal*8] N0, N0, N0, N0, N0, N0, N0, N0, +[Ibpirebuild*8] R0, R0, R0, R0, R1, R1, R1, R1, +[Ibpilocate*8] L0, L0, L0, L0, L0, L0, L0, L0, +[Ibpispare*8] S0, S0, S0, S0, S1, S1, S1, S1, +[Ibpipfa*8] P0, P1, P0, P1, P1, P1, P1, P1, /* first 1 sec */ +[Ibpifail*8] F0, F0, F0, F0, F0, F0, F0, F0, +[Ibpicritarray*8] C0, C0, C0, C0, C1, C1, C1, C1, +[Ibpifailarray*8] C0, C1, C0, C1, C0, C1, C0, C1, +}; + +static int +ledstate(Ledport *p, uint seq) +{ + ushort i; + + if(p->led == Ibpipfa && seq%32 >= 8) + i = P1; + else + i = led2[8*p->led + seq%8]; + if(i != p->ledbits){ + p->ledbits = i; + ledprint("ledstate %,.011ub %ud\n", p->ledbits, seq); + return 1; + } + return 0; +} + +static int +blink(Drive *d, ulong t) +{ + Ahba *h; + Ctlr *c; + Aledmsg msg; + + if(ledstate(d, t) == 0) + return 0; + c = d->ctlr; + h = c->hba; + /* ensure last message has been transmitted */ + while(h->emctl & Tmsg) + microdelay(1); + switch(c->enctype){ + default: + panic("%s: bad led type %d\n", dnam(d), c->enctype); + case Elmt: + memset(&msg, 0, sizeof msg); + msg.type = Mled; + msg.dsize = 0; + msg.msize = sizeof msg - 4; + msg.led[0] = d->ledbits; + msg.led[1] = d->ledbits>>8; + msg.pm = 0; + msg.hba = d->driveno; + memmove(c->enctx, &msg, sizeof msg); + break; + } + h->emctl |= Tmsg; + return 1; +} + +enum { + Esbdrv0 = 4, /* start pos in bits */ + Esbiota = 3, /* shift in bits */ + Esbact = 1, + Esbloc = 2, + Esberr = 4, +}; + +uint +esbbits(uint s) +{ + uint i, e; /* except after c */ + + e = 0; + for(i = 0; i < 3; i++) + e |= ((s>>3*i & 7) != 0)<ndrive; i++){ + d = c->drive[i]; + s |= ledstate(d, t); /* no port mapping */ + } + if(s == 0) + return 0; + memset(u, 0, sizeof u); + for(i = 0; i < c->ndrive; i++){ + d = c->drive[i]; + s = Esbdrv0 + Esbiota*i; + v = esbbits(d->ledbits) * (1ull << s%32); + u[s/32 + 0] |= v; + u[s/32 + 1] |= v>>32; + } + for(i = 0; i < c->encsz; i++) + c->enctx[i] = u[i]; + return 1; +} + +static long +ahciledr(SDunit *u, Chan *ch, void *a, long n, vlong off) +{ + Ctlr *c; + Drive *d; + + c = u->dev->ctlr; + d = c->drive[u->subno]; + return ledr(d, ch, a, n, off); +} + +static long +ahciledw(SDunit *u, Chan *ch, void *a, long n, vlong off) +{ + Ctlr *c; + Drive *d; + + c = u->dev->ctlr; + d = c->drive[u->subno]; + return ledw(d, ch, a, n, off); +} + +static void +ledkproc(void*) +{ + uchar map[NCtlr]; + uint i, j, t0, t1; + Ctlr *c; + Drive *d; + + j = 0; + memset(map, 0, sizeof map); + for(i = 0; i < niactlr; i++) + if(iactlr[i].enctype != 0){ + ahciencreset(iactlr + i); + map[i] = 1; + j++; + } + if(j == 0) + pexit("no work", 1); + for(i = 0; i < niadrive; i++){ + iadrive[i]->nled = 3; /* hardcoded */ + if(iadrive[i]->ctlr->enctype == Eesb) + iadrive[i]->nled = 3; + iadrive[i]->ledbits = -1; + } + for(i = 0; ; i++){ + t0 = Ticks; + for(j = 0; j < niadrive; ){ + c = iadrive[j]->ctlr; + if(map[j] == 0) + j += c->enctype; + else if(c->enctype == Eesb){ + blinkesb(c, i); + j += c->ndrive; + }else{ + d = iadrive[j++]; + blink(d, i); + } + } + t1 = Ticks; + esleep(Ledsleep - TK2MS(t1 - t0)); + } +} + static int iaverify(SDunit *u) { @@ -1374,7 +1410,11 @@ iaverify(SDunit *u) d = c->drive[u->subno]; ilock(c); ilock(d); - d->unit = u; + if(d->unit == nil){ + d->unit = u; + if(c->enctype != 0) + sdaddfile(u, "led", 0644, eve, ahciledr, ahciledw); + } iunlock(d); iunlock(c); checkdrive(d, d->driveno); /* c->d0 + d->driveno */ @@ -1390,11 +1430,9 @@ iaenable(SDev *s) c = s->ctlr; ilock(c); - if(!c->enabled) { - if(once == 0) { - once = 1; + if(!c->enabled){ + if(once == 0) kproc("iasata", satakproc, 0); - } if(c->ndrive == 0) panic("iaenable: zero s->ctlr->ndrive"); pcisetbme(c->pci); @@ -1403,6 +1441,8 @@ iaenable(SDev *s) /* supposed to squelch leftover interrupts here. */ ahcienable(c->hba); c->enabled = 1; + if(++once == niactlr) + kproc("ialed", ledkproc, 0); } iunlock(c); return 1; @@ -1435,139 +1475,84 @@ iaonline(SDunit *unit) d = c->drive[unit->subno]; r = 0; - if(d->portm.feat & Datapi && d->mediachange){ + if(d->portm.feat & Datapi && d->drivechange){ r = scsionline(unit); if(r > 0) - d->mediachange = 0; + d->drivechange = 0; return r; } ilock(d); - if(d->mediachange){ + if(d->drivechange){ r = 2; - d->mediachange = 0; + d->drivechange = 0; /* devsd resets this after online is called; why? */ unit->sectors = d->sectors; - unit->secsize = 512; /* default size */ - } else if(d->state == Dready) + unit->secsize = d->secsize; + }else if(d->state == Dready) r = 1; iunlock(d); return r; } -/* returns locked list! */ static Alist* -ahcibuild(Drive *d, uchar *cmd, void *data, int n, vlong lba) +ahcibuild(Aportm *m, int rw, void *data, uint n, vlong lba) { - uchar *c, acmd, dir, llba; + uchar *c; + uint flags; Alist *l; - Actab *t; - Aportm *m; - Aprdt *p; - static uchar tab[2][2] = { 0xc8, 0x25, 0xca, 0x35, }; - m = &d->portm; - dir = *cmd != 0x28; - llba = m->feat&Dllba? 1: 0; - acmd = tab[dir][llba]; - qlock(m); l = m->list; - t = m->ctab; - c = t->cfis; - - c[0] = 0x27; - c[1] = 0x80; - c[2] = acmd; - c[3] = 0; - - c[4] = lba; /* sector lba low 7:0 */ - c[5] = lba >> 8; /* cylinder low lba mid 15:8 */ - c[6] = lba >> 16; /* cylinder hi lba hi 23:16 */ - c[7] = 0xa0 | 0x40; /* obsolete device bits + lba */ - if(llba == 0) - c[7] |= (lba>>24) & 7; - - c[8] = lba >> 24; /* sector (exp) lba 31:24 */ - c[9] = lba >> 32; /* cylinder low (exp) lba 39:32 */ - c[10] = lba >> 48; /* cylinder hi (exp) lba 48:40 */ - c[11] = 0; /* features (exp); */ - - c[12] = n; /* sector count */ - c[13] = n >> 8; /* sector count (exp) */ - c[14] = 0; /* r */ - c[15] = 0; /* control */ - - *(ulong*)(c + 16) = 0; - - l->flags = 1<<16 | Lpref | 0x5; /* Lpref ?? */ - if(dir == Write) - l->flags |= Lwrite; - l->len = 0; - l->ctab = PCIWADDR(t); - l->ctabhi = 0; - - p = &t->prdt; - p->dba = PCIWADDR(data); - p->dbahi = 0; - if(d->unit == nil) - panic("ahcibuild: nil d->unit"); - p->count = 1<<31 | (d->unit->secsize*n - 2) | 1; - + c = m->ctab->cfis; + rwfis(m, c, rw, n, lba); + flags = Lpref; + if(rw == SDwrite) + flags |= Lwrite; + mkalist(m, flags, data, 512*n); return l; } static Alist* ahcibuildpkt(Aportm *m, SDreq *r, void *data, int n) { - int fill, len; + uint flags; uchar *c; - Alist *l; Actab *t; - Aprdt *p; + Alist *l; - qlock(m); l = m->list; t = m->ctab; c = t->cfis; - - fill = m->feat&Datapi16? 16: 12; - if((len = r->clen) > fill) - len = fill; - memmove(t->atapi, r->cmd, len); - memset(t->atapi+len, 0, fill-len); - - c[0] = 0x27; - c[1] = 0x80; - c[2] = 0xa0; - if(n != 0) - c[3] = 1; /* dma */ - else - c[3] = 0; /* features (exp); */ - - c[4] = 0; /* sector lba low 7:0 */ - c[5] = n; /* cylinder low lba mid 15:8 */ - c[6] = n >> 8; /* cylinder hi lba hi 23:16 */ - c[7] = 0xa0; /* obsolete device bits */ - - *(ulong*)(c + 8) = 0; - *(ulong*)(c + 12) = 0; - *(ulong*)(c + 16) = 0; - - l->flags = 1<<16 | Lpref | Latapi | 0x5; + atapirwfis(m, c, r->cmd, r->clen, n); + flags = 1<<16 | Lpref | Latapi; if(r->write != 0 && data) - l->flags |= Lwrite; - l->len = 0; - l->ctab = PCIWADDR(t); - l->ctabhi = 0; + flags |= Lwrite; + mkalist(m, flags, data, n); + return l; +} - if(data == 0) - return l; - - p = &t->prdt; - p->dba = PCIWADDR(data); - p->dbahi = 0; - p->count = 1<<31 | (n - 2) | 1; +static Alist* +ahcibuildfis(Aportm *m, SDreq *r, void *data, uint n) +{ + uchar *c; + uint flags; + Alist *l; + l = m->list; + c = m->ctab->cfis; + if((r->ataproto & Pprotom) != Ppkt){ + memmove(c, r->cmd, r->clen); + flags = Lpref; + if(r->write || n == 0) + flags |= Lwrite; + mkalist(m, flags, data, n); + }else{ + atapirwfis(m, c, r->cmd, r->clen, n); + flags = 1<<16 | Lpref | Latapi; + if(r->write && data) + flags |= Lwrite; + mkalist(m, flags, data, n); + } return l; } @@ -1580,19 +1565,19 @@ waitready(Drive *d) if(d->state == Dreset || d->state == Dportreset || d->state == Dnew) return 1; - δ = MACHP(0)->ticks - d->lastseen; + δ = Ticks - d->lastseen; if(d->state == Dnull || δ > 10*1000) return -1; ilock(d); s = d->port->sstatus; iunlock(d); - if((s & 0x700) == 0 && δ > 1500) - return -1; /* no detect */ - if(d->state == Dready && (s & 7) == 3) - return 0; /* ready, present & phy. comm. */ + if((s & Imask) == 0 && δ > 1500) + return -1; + if(d->state == Dready && (s & Smask) == Sphylink) + return 0; esleep(250); } - print("%s: not responding; offline\n", d->unit->name); + print("%s: not responding; offline\n", dnam(d)); ilock(d); d->state = Doffline; iunlock(d); @@ -1626,55 +1611,43 @@ flushcache(Drive *d) } static int -iariopkt(SDreq *r, Drive *d) +io(Drive *d, uint proto, int to, int interrupt) { - int n, count, try, max, flag, task; - char *name; - uchar *cmd, *data; + uint task, flag, rv; Aport *p; Asleep as; - cmd = r->cmd; - name = d->unit->name; - p = d->port; - - aprint("ahci: iariopkt: %02ux %02ux %c %d %p\n", - cmd[0], cmd[2], "rw"[r->write], r->dlen, r->data); - if(cmd[0] == 0x5a && (cmd[2] & 0x3f) == 0x3f) - return sdmodesense(r, cmd, d->info, sizeof d->info); - r->rlen = 0; - count = r->dlen; - max = 65536; - - try = 0; -retry: - data = r->data; - n = count; - if(n > max) - n = max; - ahcibuildpkt(&d->portm, r, data, n); switch(waitready(d)){ case -1: - qunlock(&d->portm); return SDeio; case 1: - qunlock(&d->portm); - esleep(1); - goto retry; + return SDretry; } ilock(d); d->portm.flag = 0; iunlock(d); + p = d->port; p->ci = 1; as.p = p; as.i = 1; - d->intick = MACHP(0)->ticks; + d->totick = 0; + if(to > 0) + d->totick = Ticks + MS2TK(to) | 1; /* fix fencepost */ d->active++; while(waserror()) - ; + if(interrupt){ + d->active--; + d->port->ci = 0; + if(ahcicomreset(&d->portc) == -1){ + ilock(d); + d->state = Dreset; + iunlock(d); + } + return SDtimeout; + } sleep(&d->portm, ahciclear, &as); poperror(); @@ -1684,45 +1657,117 @@ retry: task = d->port->task; iunlock(d); - if(task & (Efatal<<8) || task & (ASbsy|ASdrq) && d->state == Dready){ + rv = SDok; + if(proto & Ppkt){ + rv = task >> 8 + 4 & 0xf; + flag &= ~Fahdrs; + flag |= Fdone; + }else if(task & (Efatal<<8) || task & (ASbsy|ASdrq) && d->state == Dready){ d->port->ci = 0; ahcirecover(&d->portc); task = d->port->task; flag &= ~Fdone; /* either an error or do-over */ } - qunlock(&d->portm); if(flag == 0){ - if(++try == 10){ - print("%s: bad disk\n", name); - r->status = SDcheck; - return SDcheck; - } - print("%s: retry\n", name); - goto retry; + print("%s: retry\n", dnam(d)); + return SDretry; } - if(flag & Ferror){ - if((task&Eidnf) == 0) - print("%s: i/o error %ux\n", name, task); - r->status = SDcheck; + if(flag & (Fahdrs | Ferror)){ + if((task & Eidnf) == 0) + print("%s: i/o error %ux\n", dnam(d), task); return SDcheck; } + return rv; +} - data += n; +static int +iariopkt(SDreq *r, Drive *d) +{ + int n, count, try, max; + uchar *cmd; - r->rlen = data - (uchar*)r->data; - r->status = SDok; - return SDok; + cmd = r->cmd; + aprint("%s: %.2ux %.2ux %c %d %p\n", dnam(d), cmd[0], cmd[2], + "rw"[r->write], r->dlen, r->data); + r->rlen = 0; + count = r->dlen; + max = 65536; + + for(try = 0; try < 10; try++){ + n = count; + if(n > max) + n = max; + qlock(&d->portm); + ahcibuildpkt(&d->portm, r, r->data, n); + r->status = io(d, Ppkt, 5000, 0); + qunlock(&d->portm); + switch(r->status){ + case SDeio: + return SDeio; + case SDretry: + continue; + } +// print("%.2ux :: %.2ux :: %.4ux\n", r->cmd[0], r->status, d->port->task); + r->rlen = d->portm.list->len; + return SDok; + } + print("%s: bad disk\n", dnam(d)); + return r->status = SDcheck; +} + +static long +ahcibio(SDunit *u, int lun, int write, void *a, long count, uvlong lba) +{ + int n, rw, try, status, max; + uchar *data; + Ctlr *c; + Drive *d; + + c = u->dev->ctlr; + d = c->drive[u->subno]; + if(d->portm.feat & Datapi) + return scsibio(u, lun, write, a, count, lba); + + max = 128; + if(d->portm.feat & Dllba){ + max = 8192; /* ahci maximum */ + if(c->type == Tsb600) + max = 255; /* errata */ + } + rw = write? SDwrite: SDread; + data = a; + for(try = 0; try < 10;){ + n = count; + if(n > max) + n = max; + qlock(&d->portm); + ahcibuild(&d->portm, rw, data, n, lba); + status = io(d, Pdma, 5000, 0); + qunlock(&d->portm); + switch(status){ + case SDeio: + return -1; + case SDretry: + try++; + continue; + } + try = 0; + count -= n; + lba += n; + data += n * u->secsize; + if(count == 0) + return data - (uchar*)a; + } + print("%s: bad disk\n", dnam(d)); + return -1; } static int iario(SDreq *r) { - int i, n, count, try, max, flag, task; - vlong lba; - char *name; - uchar *cmd, *data; - Aport *p; - Asleep as; + int i, n, count, rw; + uchar *cmd; + uvlong lba; Ctlr *c; Drive *d; SDunit *unit; @@ -1733,157 +1778,281 @@ iario(SDreq *r) if(d->portm.feat & Datapi) return iariopkt(r, d); cmd = r->cmd; - name = d->unit->name; - p = d->port; - if(r->cmd[0] == 0x35 || r->cmd[0] == 0x91){ + if(cmd[0] == 0x35 || cmd[0] == 0x91){ if(flushcache(d) == 0) return sdsetsense(r, SDok, 0, 0, 0); return sdsetsense(r, SDcheck, 3, 0xc, 2); } - if((i = sdfakescsi(r, d->info, sizeof d->info)) != SDnostatus){ + if((i = sdfakescsi(r)) != SDnostatus){ r->status = i; return i; } - if(*cmd != 0x28 && *cmd != 0x2a){ - print("%s: bad cmd 0x%.2ux\n", name, cmd[0]); - r->status = SDcheck; - return SDcheck; - } - - lba = cmd[2]<<24 | cmd[3]<<16 | cmd[4]<<8 | cmd[5]; - count = cmd[7]<<8 | cmd[8]; - if(r->data == nil) - return SDok; - if(r->dlen < count * unit->secsize) - count = r->dlen / unit->secsize; - max = 128; - - try = 0; -retry: - data = r->data; - while(count > 0){ - n = count; - if(n > max) - n = max; - ahcibuild(d, cmd, data, n, lba); - switch(waitready(d)){ - case -1: - qunlock(&d->portm); - return SDeio; - case 1: - qunlock(&d->portm); - esleep(1); - goto retry; - } - ilock(d); - d->portm.flag = 0; - iunlock(d); - p->ci = 1; - - as.p = p; - as.i = 1; - d->intick = MACHP(0)->ticks; - d->active++; - - while(waserror()) - ; - sleep(&d->portm, ahciclear, &as); - poperror(); - - d->active--; - ilock(d); - flag = d->portm.flag; - task = d->port->task; - iunlock(d); - - if(task & (Efatal<<8) || - task & (ASbsy|ASdrq) && d->state == Dready){ - d->port->ci = 0; - ahcirecover(&d->portc); - task = d->port->task; - } - qunlock(&d->portm); - if(flag == 0){ - if(++try == 10){ - print("%s: bad disk\n", name); - r->status = SDeio; - return SDeio; - } - iprint("%s: retry %lld\n", name, lba); - goto retry; - } - if(flag & Ferror){ - iprint("%s: i/o error %ux @%,lld\n", name, task, lba); - r->status = SDeio; - return SDeio; - } - - count -= n; - lba += n; - data += n * unit->secsize; - } - r->rlen = data - (uchar*)r->data; - r->status = SDok; + if((i = sdfakescsirw(r, &lba, &count, &rw)) != SDnostatus) + return i; + n = ahcibio(unit, r->lun, r->write, r->data, count, lba); + if(n == -1) + return SDeio; + r->rlen = n; return SDok; } +static uchar bogusrfis[16] = { +[Ftype] 0x34, +[Fioport] 0x40, +[Fstatus] 0x50, + +[Fdev] 0xa0, +}; + +static void +sdr0(Drive *d) +{ + uchar *c; + + c = d->portm.fis.r; + memmove(c, bogusrfis, sizeof bogusrfis); + coherence(); +} + +static int +sdr(SDreq *r, Drive *d, int st) +{ + uchar *c; + uint t; + + if((r->ataproto & Pprotom) == Ppkt){ + t = d->port->task; + if(t & ASerr) + st = t >> 8 + 4 & 0xf; + } + c = d->portm.fis.r; + memmove(r->cmd, c, 16); + r->status = st; + if(st == SDcheck) + st = SDok; + return st; +} + +static int +fisreqchk(Sfis *f, SDreq *r) +{ + if((r->ataproto & Pprotom) == Ppkt) + return SDnostatus; + /* + * handle oob requests; + * restrict & sanitize commands + */ + if(r->clen != 16) + error(Eio); + if(r->cmd[0] == 0xf0){ + sigtofis(f, r->cmd); + r->status = SDok; + return SDok; + } + r->cmd[0] = 0x27; + r->cmd[1] = 0x80; + r->cmd[7] |= 0xa0; + return SDnostatus; +} + +static int +iaataio(SDreq *r) +{ + int try; + Ctlr *c; + Drive *d; + SDunit *u; + + u = r->unit; + c = u->dev->ctlr; + d = c->drive[u->subno]; + + if((r->status = fisreqchk(&d->portm, r)) != SDnostatus) + return r->status; + r->rlen = 0; + sdr0(d); + for(try = 0; try < 10; try++){ + qlock(&d->portm); + ahcibuildfis(&d->portm, r, r->data, r->dlen); + r->status = io(d, r->ataproto & Pprotom, -1, 1); + qunlock(&d->portm); + switch(r->status){ + case SDtimeout: + return sdsetsense(r, SDcheck, 11, 0, 6); + case SDeio: + return SDeio; + case SDretry: + continue; + } + r->rlen = r->dlen; + if((r->ataproto & Pprotom) == Ppkt) + r->rlen = d->portm.list->len; + return sdr(r, d, r->status); + } + print("%s: bad disk\n", dnam(d)); + r->status = SDeio; + return SDeio; +} + /* * configure drives 0-5 as ahci sata (c.f. errata) */ static int iaahcimode(Pcidev *p) { - dprint("iaahcimode: %ux %ux %ux\n", pcicfgr8(p, 0x91), pcicfgr8(p, 92), - pcicfgr8(p, 93)); - pcicfgw16(p, 0x92, pcicfgr32(p, 0x92) | 0xf); /* ports 0-3 */ -// pcicfgw8(p, 0x93, pcicfgr32(p, 9x93) | 3); /* ports 4-5 */ + uint u; + + u = pcicfgr16(p, 0x92); + dprint("ahci: %T: iaahcimode %.2ux %.4ux\n", p->tbdf, pcicfgr8(p, 0x91), u); + pcicfgw16(p, 0x92, u | 0xf); /* ports 0-15 */ return 0; } +enum{ + Ghc = 0x04/4, /* global host control */ + Pi = 0x0c/4, /* ports implemented */ + Cmddec = 1<<15, /* enable command block decode */ + + /* Ghc bits */ + Ahcien = 1<<31, /* ahci enable */ +}; + static void iasetupahci(Ctlr *c) { - /* disable cmd block decoding. */ - pcicfgw16(c->pci, 0x40, pcicfgr16(c->pci, 0x40) & ~(1<<15)); - pcicfgw16(c->pci, 0x42, pcicfgr16(c->pci, 0x42) & ~(1<<15)); + pcicfgw16(c->pci, 0x40, pcicfgr16(c->pci, 0x40) & ~Cmddec); + pcicfgw16(c->pci, 0x42, pcicfgr16(c->pci, 0x42) & ~Cmddec); - c->lmmio[0x4/4] |= 1 << 31; /* enable ahci mode (ghc register) */ - c->lmmio[0xc/4] = (1 << 6) - 1; /* 5 ports. (supposedly ro pi reg.) */ + c->lmmio[Ghc] |= Ahcien; + c->lmmio[Pi] = (1 << 6) - 1; /* 5 ports (supposedly ro pi reg) */ /* enable ahci mode; from ich9 datasheet */ pcicfgw16(c->pci, 0x90, 1<<6 | 1<<5); } +static void +sbsetupahci(Pcidev *p) +{ + print("sbsetupahci: tweaking %.4ux ccru %.2ux ccrp %.2ux\n", + p->did, p->ccru, p->ccrp); + pcicfgw8(p, 0x40, pcicfgr8(p, 0x40) | 1); + pcicfgw8(p, PciCCRu, 6); + pcicfgw8(p, PciCCRp, 1); + p->ccru = 6; + p->ccrp = 1; +} + +static int +esbenc(Ctlr *c) +{ + c->encsz = 1; + c->enctx = (ulong*)(c->mmio + 0xa0); + c->enctype = Eesb; + c->enctx[0] = 0; + return 0; +} + +static int +ahciencinit(Ctlr *c) +{ + ulong type, sz, o, *bar; + Ahba *h; + + h = c->hba; + if(c->type == Tesb) + return esbenc(c); + if((h->cap & Hems) == 0) + return -1; + type = h->emctl & Emtype; + switch(type){ + case Esgpio: + case Eses2: + case Esafte: + return -1; + case Elmt: + break; + default: + return -1; + } + + sz = h->emloc & 0xffff; + o = h->emloc>>16; + if(sz == 0 || o == 0) + return -1; + bar = c->lmmio; + dprint("size = %.4lux; loc = %.4lux*4\n", sz, o); + c->encsz = sz; + c->enctx = bar + o; + if((h->emctl & Xonly) == 0){ + if(h->emctl & Smb) + c->encrx = bar + o; + else + c->encrx = bar + o*2; + } + c->enctype = type; + return 0; +} + static int didtype(Pcidev *p) { + int type; + + type = Tahci; switch(p->vid){ + default: + return -1; case 0x8086: if((p->did & 0xfffc) == 0x2680) return Tesb; - /* - * 0x27c4 is the intel 82801 in compatibility (not sata) mode. - */ - if ((p->did & 0xfffb) == 0x27c1 || /* 82801g[bh]m ich7 */ - p->did == 0x2821 || /* 82801h[roh] */ - (p->did & 0xfffe) == 0x2824 || /* 82801h[b] */ - (p->did & 0xfeff) == 0x2829 || /* ich8/9m */ - (p->did & 0xfffe) == 0x2922 || /* ich9 */ - p->did == 0x3a02 || /* 82801jd/do */ - (p->did & 0xfefe) == 0x3a22 || /* ich10, pch */ - (p->did & 0xfff7) == 0x3b28) /* pchm */ - return Tich; + if((p->did & 0xfffb) == 0x27c1) + return Tich; /* 82801g[bh]m */ + if((p->did & 0xffff) == 0x2821) + return Tich; /* 82801h[roh] */ + if((p->did & 0xfffe) == 0x2824) + return Tich; /* 82801h[b] */ + if((p->did & 0xfeff) == 0x2829) + return Tich; /* ich8 */ + if((p->did & 0xfffe) == 0x2922) + return Tich; /* ich9 */ + if((p->did & 0xffff) == 0x3a02) + return Tich; /* 82801jd/do */ + if((p->did & 0xfefe) == 0x3a22) + return Tich; /* ich10, pch */ + if((p->did & 0xfff7) == 0x3b28) + return Tich; /* pchm */ + if((p->did & 0xfffe) == 0x3b22) + return Tich; /* pch */ break; case 0x1002: - if(p->did == 0x4380) - return Tsb600; + if(p->ccru == 1 || p->ccrp != 1) + if(p->did == 0x4380 || p->did == 0x4390) + sbsetupahci(p); + type = Tsb600; + break; + case 0x1106: + /* + * unconfirmed report that the programming + * interface is set incorrectly. + */ + if(p->did == 0x3349) + return Tahci; + break; + case 0x10de: + case 0x1039: + case 0x1b4b: + case 0x11ab: + break; + case 0x197b: + case 0x10b9: + type = Tjmicron; break; } if(p->ccrb == Pcibcstore && p->ccru == 6 && p->ccrp == 1) - return Tunk; + return type; return -1; } @@ -1895,33 +2064,32 @@ iapnp(void) Ctlr *c; Drive *d; Pcidev *p; - SDev *head, *tail, *s; + SDev *s; static int done; - if(done++) + if(done) return nil; - + done = 1; memset(olds, 0xff, sizeof olds); p = nil; - head = tail = nil; loop: while((p = pcimatch(p, 0, 0)) != nil){ - type = didtype(p); - if (type == -1 || p->mem[Abar].bar == 0) + if((type = didtype(p)) == -1) + continue; + if(p->mem[Abar].bar == 0) continue; if(niactlr == NCtlr){ - print("ahci: iapnp: %s: too many controllers\n", - tname[type]); + print("iapnp: %s: too many controllers\n", tname[type]); break; } c = iactlr + niactlr; - s = sdevs + niactlr; + s = sdevs + niactlr; memset(c, 0, sizeof *c); memset(s, 0, sizeof *s); io = p->mem[Abar].bar & ~0xf; c->mmio = vmap(io, p->mem[Abar].size); if(c->mmio == 0){ - print("ahci: %s: address 0x%luX in use did=%x\n", + print("%s: address %#p in use did %.4ux\n", Tname(c), io, p->did); continue; } @@ -1930,39 +2098,33 @@ loop: c->type = type; s->ifc = &sdiahciifc; - s->idno = 'E' + niactlr; + s->idno = 'E'; s->ctlr = c; c->sdev = s; - if(Intel(c) && p->did != 0x2681) + if(intel(c) && p->did != 0x2681) iasetupahci(c); - nunit = ahciconf(c); // ahcihbareset((Ahba*)c->mmio); - if(Intel(c) && iaahcimode(p) == -1) - break; - if(nunit < 1){ + nunit = ahciconf(c); + if(intel(c) && iaahcimode(p) == -1 || nunit < 1){ vunmap(c->mmio, p->mem[Abar].size); continue; } c->ndrive = s->nunit = nunit; - c->mport = c->hba->cap & ((1<<5)-1); - - i = (c->hba->cap >> 21) & 1; - print("#S/sd%c: %s: sata-%s with %d ports\n", s->idno, - Tname(c), "I\0II" + i*2, nunit); /* map the drives -- they don't all need to be enabled. */ memset(c->rawdrive, 0, sizeof c->rawdrive); n = 0; - for(i = 0; i < NCtlrdrv; i++) { + for(i = 0; i < NCtlrdrv; i++){ d = c->rawdrive + i; d->portno = i; d->driveno = -1; d->sectors = 0; d->serial[0] = ' '; d->ctlr = c; - if((c->hba->pi & (1<hba->pi & 1<name, sizeof d->name, "iahci%d.%d", niactlr, i); d->port = (Aport*)(c->mmio + 0x80*i + 0x100); d->portc.p = d->port; d->portc.m = &d->portm; @@ -1972,58 +2134,69 @@ loop: } for(i = 0; i < n; i++) if(ahciidle(c->drive[i]->port) == -1){ - dprint("ahci: %s: port %d wedged; abort\n", + print("%s: port %d wedged; abort\n", Tname(c), i); goto loop; } for(i = 0; i < n; i++){ - c->drive[i]->mode = DMsatai; + c->drive[i]->mode = DMautoneg; configdrive(c->drive[i]); } + ahciencinit(c); niadrive += n; niactlr++; - if(head) - tail->next = s; - else - head = s; - tail = s; + sdadddevs(s); + i = (c->hba->cap >> 21) & 1; + print("#S/%s: %s: sata-%s with %d ports\n", s->name, + Tname(c), "I\0II" + i*2, nunit); } - return head; + return nil; } -static char* smarttab[] = { - "unset", - "error", - "threshold exceeded", - "normal" +static Htab ctab[] = { + Aasp, "asp", + Aalpe , "alpe ", + Adlae, "dlae", + Aatapi, "atapi", + Apste, "pste", + Afbsc, "fbsc", + Aesp, "esp", + Acpd, "cpd", + Ampsp, "mpsp", + Ahpcp, "hpcp", + Apma, "pma", + Acps, "cps", + Acr, "cr", + Afr, "fr", + Ampss, "mpss", + Apod, "pod", + Asud, "sud", + Ast, "st", }; -static char * -pflag(char *s, char *e, uchar f) +static char* +capfmt(char *p, char *e, Htab *t, int n, ulong cap) { - uchar i; + uint i; - for(i = 0; i < 8; i++) - if(f & (1 << i)) - s = seprint(s, e, "%s ", flagname[i]); - return seprint(s, e, "\n"); + *p = 0; + for(i = 0; i < n; i++) + if(cap & t[i].bit) + p = seprint(p, e, "%s ", t[i].name); + return p; } static int iarctl(SDunit *u, char *p, int l) { - char buf[32]; - char *e, *op; + char buf[32], *e, *op; Aport *o; Ctlr *c; Drive *d; - c = u->dev->ctlr; - if(c == nil) { -print("iarctl: nil u->dev->ctlr\n"); + if((c = u->dev->ctlr) == nil) return 0; - } d = c->drive[u->subno]; o = d->port; @@ -2033,69 +2206,40 @@ print("iarctl: nil u->dev->ctlr\n"); p = seprint(p, e, "model\t%s\n", d->model); p = seprint(p, e, "serial\t%s\n", d->serial); p = seprint(p, e, "firm\t%s\n", d->firmware); - if(d->smartrs == 0xff) - p = seprint(p, e, "smart\tenable error\n"); - else if(d->smartrs == 0) - p = seprint(p, e, "smart\tdisabled\n"); - else - p = seprint(p, e, "smart\t%s\n", - smarttab[d->portm.smart]); + if(d->wwn != 0) + p = seprint(p, e, "wwn\t%ullx\n", d->wwn); p = seprint(p, e, "flag\t"); - p = pflag(p, e, d->portm.feat); + p = pflag(p, e, &d->portm); + p = seprint(p, e, "udma\t%d\n", d->portm.udma); }else p = seprint(p, e, "no disk present [%s]\n", diskstates[d->state]); serrstr(o->serror, buf, buf + sizeof buf - 1); - p = seprint(p, e, "reg\ttask %lux cmd %lux serr %lux %s ci %lux is %lux; " - "sig %lux sstatus %04lux\n", o->task, o->cmd, o->serror, buf, + p = seprint(p, e, "reg\ttask %lux cmd %lux serr %lux %s ci %lux is %lux " + "sig %lux sstatus %.3lux\n", o->task, o->cmd, o->serror, buf, o->ci, o->isr, o->sig, o->sstatus); - if(d->unit == nil) - panic("iarctl: nil d->unit"); - p = seprint(p, e, "geometry %llud %lud\n", d->sectors, d->unit->secsize); + p = seprint(p, e, "cmd\t"); + p = capfmt(p, e, ctab, nelem(ctab), o->cmd); + p = seprint(p, e, "\n"); + p = seprint(p, e, "mode\t%s %s\n", modes[d->mode], modes[maxmode(c)]); + p = seprint(p, e, "geometry %llud %lud\n", d->sectors, u->secsize); return p - op; } -static void -runflushcache(Drive *d) -{ - long t0; - - t0 = MACHP(0)->ticks; - if(flushcache(d) != 0) - error(Eio); - dprint("ahci: flush in %ld ms\n", MACHP(0)->ticks - t0); -} - static void forcemode(Drive *d, char *mode) { int i; - for(i = 0; i < nelem(modename); i++) - if(strcmp(mode, modename[i]) == 0) + for(i = 0; i < nelem(modes); i++) + if(strcmp(mode, modes[i]) == 0) break; - if(i == nelem(modename)) + if(i == nelem(modes)) i = 0; ilock(d); d->mode = i; iunlock(d); } -static void -runsmartable(Drive *d, int i) -{ - if(waserror()){ - qunlock(&d->portm); - d->smartrs = 0; - nexterror(); - } - if(lockready(d) == -1) - error(Eio); - d->smartrs = smart(&d->portc, i); - d->portm.smart = 0; - qunlock(&d->portm); - poperror(); -} - static void forcestate(Drive *d, char *state) { @@ -2108,9 +2252,30 @@ forcestate(Drive *d, char *state) error(Ebadctl); ilock(d); d->state = i; +// statechange(d); iunlock(d); } +static int +runsettxmode(Drive *d, char *s) +{ + int i; + Aportc *c; + Aportm *m; + + c = &d->portc; + m = &d->portm; + + i = 1; + if(lockready(d) == 0){ + m->udma = atoi(s); + if(settxmode(c, m->udma) == 0) + i = 0; + } + qunlock(m); + return i; +} + static int iawctl(SDunit *u, Cmdbuf *cmd) @@ -2118,62 +2283,20 @@ iawctl(SDunit *u, Cmdbuf *cmd) char **f; Ctlr *c; Drive *d; - uint i; c = u->dev->ctlr; d = c->drive[u->subno]; f = cmd->f; - if(strcmp(f[0], "flushcache") == 0) - runflushcache(d); - else if(strcmp(f[0], "identify") == 0){ - i = strtoul(f[1]? f[1]: "0", 0, 0); - if(i > 0xff) - i = 0; - dprint("ahci: %04d %ux\n", i, d->info[i]); - }else if(strcmp(f[0], "mode") == 0) + if(strcmp(f[0], "mode") == 0) forcemode(d, f[1]? f[1]: "satai"); - else if(strcmp(f[0], "nop") == 0){ - if((d->portm.feat & Dnop) == 0){ - cmderror(cmd, "no drive support"); - return -1; - } - if(waserror()){ - qunlock(&d->portm); - nexterror(); - } - if(lockready(d) == -1) - error(Eio); - nop(&d->portc); - qunlock(&d->portm); - poperror(); - }else if(strcmp(f[0], "reset") == 0) - forcestate(d, "reset"); - else if(strcmp(f[0], "smart") == 0){ - if(d->smartrs == 0){ - cmderror(cmd, "smart not enabled"); - return -1; - } - if(waserror()){ - qunlock(&d->portm); - d->smartrs = 0; - nexterror(); - } - if(lockready(d) == -1) - error(Eio); - d->portm.smart = 2 + smartrs(&d->portc); - qunlock(&d->portm); - poperror(); - }else if(strcmp(f[0], "smartdisable") == 0) - runsmartable(d, 1); - else if(strcmp(f[0], "smartenable") == 0) - runsmartable(d, 0); else if(strcmp(f[0], "state") == 0) forcestate(d, f[1]? f[1]: "null"); - else{ + else if(strcmp(f[0], "txmode") == 0){ + if(runsettxmode(d, f[1]? f[1]: "0")) + cmderror(cmd, "bad txmode / stuck port"); + }else cmderror(cmd, Ebadctl); - return -1; - } return 0; } @@ -2202,42 +2325,63 @@ portr(char *p, char *e, uint x) return p; } -/* must emit exactly one line per controller (sd(3)) */ +static Htab htab[] = { + H64a, "64a", + Hncq, "ncq", + Hsntf, "ntf", + Hmps, "mps", + Hss, "ss", + Halp, "alp", + Hal, "led", + Hclo, "clo", + Ham, "am", + Hpm, "pm", + Hfbs, "fbs", + Hpmb, "pmb", + Hssc, "slum", + Hpsc, "pslum", + Hcccs, "coal", + Hems, "ems", + Hxs, "xs", +}; + +static Htab htab2[] = { + Apts, "apts", + Nvmp, "nvmp", + Boh, "boh", +}; + +static Htab emtab[] = { + Pm, "pm", + Alhd, "alhd", + Xonly, "xonly", + Smb, "smb", + Esgpio, "esgpio", + Eses2, "eses2", + Esafte, "esafte", + Elmt, "elmt", +}; + static char* -iartopctl(SDev *sdev, char *p, char *e) +iartopctl(SDev *s, char *p, char *e) { - ulong cap; char pr[25]; - Ahba *hba; - Ctlr *ctlr; + ulong cap; + Ahba *h; + Ctlr *c; -#define has(x, str) if(cap & (x)) p = seprint(p, e, "%s ", (str)) - - ctlr = sdev->ctlr; - hba = ctlr->hba; - p = seprint(p, e, "sd%c ahci port %#p: ", sdev->idno, hba); - cap = hba->cap; - has(Hs64a, "64a"); - has(Hsalp, "alp"); - has(Hsam, "am"); - has(Hsclo, "clo"); - has(Hcccs, "coal"); - has(Hems, "ems"); - has(Hsal, "led"); - has(Hsmps, "mps"); - has(Hsncq, "ncq"); - has(Hssntf, "ntf"); - has(Hspm, "pm"); - has(Hpsc, "pslum"); - has(Hssc, "slum"); - has(Hsss, "ss"); - has(Hsxs, "sxs"); - portr(pr, pr + sizeof pr, hba->pi); + c = s->ctlr; + h = c->hba; + cap = h->cap; + p = seprint(p, e, "sd%c ahci %s port %#p: ", s->idno, Tname(c), h); + p = capfmt(p, e, htab, nelem(htab), cap); + p = capfmt(p, e, htab2, nelem(htab2), h->cap2); + p = capfmt(p, e, emtab, nelem(emtab), h->emctl); + portr(pr, pr + sizeof pr, h->pi); return seprint(p, e, - "iss %ld ncs %ld np %ld; ghc %lux isr %lux pi %lux %s ver %lux\n", + "iss %ld ncs %ld np %ld ghc %lux isr %lux pi %lux %s ver %lux\n", (cap>>20) & 0xf, (cap>>8) & 0x1f, 1 + (cap & 0x1f), - hba->ghc, hba->isr, hba->pi, pr, hba->ver); -#undef has + h->ghc, h->isr, h->pi, pr, h->ver); } static int @@ -2249,14 +2393,14 @@ iawtopctl(SDev *, Cmdbuf *cmd) f = cmd->f; v = 0; - if (f[0] == nil) - return 0; if(strcmp(f[0], "debug") == 0) v = &debug; else if(strcmp(f[0], "idprint") == 0) v = &prid; else if(strcmp(f[0], "aprint") == 0) v = &datapi; + else if(strcmp(f[0], "ledprint") == 0) + v = &dled; else cmderror(cmd, Ebadctl); @@ -2277,7 +2421,7 @@ iawtopctl(SDev *, Cmdbuf *cmd) } SDifc sdiahciifc = { - "iahci", + "ahci", iapnp, nil, /* legacy */ @@ -2290,9 +2434,10 @@ SDifc sdiahciifc = { iarctl, iawctl, - scsibio, + ahcibio, nil, /* probe */ nil, /* clear */ iartopctl, iawtopctl, + iaataio, }; diff --git a/sys/src/9/pc/sdata.c b/sys/src/9/pc/sdide.c similarity index 66% rename from sys/src/9/pc/sdata.c rename to sys/src/9/pc/sdide.c index 574fb1a49..6d1294d63 100644 --- a/sys/src/9/pc/sdata.c +++ b/sys/src/9/pc/sdide.c @@ -8,11 +8,14 @@ #include "../port/error.h" #include "../port/sd.h" +#include #define HOWMANY(x, y) (((x)+((y)-1))/(y)) #define ROUNDUP(x, y) (HOWMANY((x), (y))*(y)) +#define uprint(...) snprint(up->genbuf, sizeof up->genbuf, __VA_ARGS__); +#pragma varargck argpos atadebug 3 -extern SDifc sdataifc; +extern SDifc sdideifc; enum { DbgCONFIG = 0x0001, /* detected drive config info */ @@ -23,6 +26,7 @@ enum { DbgINL = 0x0100, /* That Inil20+ message we hate */ Dbg48BIT = 0x0200, /* 48-bit LBA */ DbgBsy = 0x0400, /* interrupt but Bsy (shared IRQ) */ + DbgAtazz = 0x0800, /* debug raw ata io */ }; #define DEBUG (DbgDEBUG|DbgSTATE) @@ -76,6 +80,7 @@ enum { /* Interrupt Reason */ enum { /* Device/Head */ Dev0 = 0xA0, /* Master */ Dev1 = 0xB0, /* Slave */ + Devs = Dev0 | Dev1, Lba = 0x40, /* LBA mode */ }; @@ -93,16 +98,13 @@ enum { /* Status, Alternate Status */ enum { /* Command */ Cnop = 0x00, /* NOP */ - Cdr = 0x08, /* Device Reset */ Crs = 0x20, /* Read Sectors */ Crs48 = 0x24, /* Read Sectors Ext */ Crd48 = 0x25, /* Read w/ DMA Ext */ - Crdq48 = 0x26, /* Read w/ DMA Queued Ext */ Crsm48 = 0x29, /* Read Multiple Ext */ Cws = 0x30, /* Write Sectors */ Cws48 = 0x34, /* Write Sectors Ext */ Cwd48 = 0x35, /* Write w/ DMA Ext */ - Cwdq48 = 0x36, /* Write w/ DMA Queued Ext */ Cwsm48 = 0x39, /* Write Multiple Ext */ Cedd = 0x90, /* Execute Device Diagnostics */ Cpkt = 0xA0, /* Packet */ @@ -110,13 +112,9 @@ enum { /* Command */ Crsm = 0xC4, /* Read Multiple */ Cwsm = 0xC5, /* Write Multiple */ Csm = 0xC6, /* Set Multiple */ - Crdq = 0xC7, /* Read DMA queued */ Crd = 0xC8, /* Read DMA */ Cwd = 0xCA, /* Write DMA */ - Cwdq = 0xCC, /* Write DMA queued */ - Cstandby = 0xE2, /* Standby */ Cid = 0xEC, /* Identify Device */ - Csf = 0xEF, /* Set Features */ }; enum { /* Device Control */ @@ -214,7 +212,6 @@ enum { /* bit masks for capabilities identify info */ Mnoiordy = 0x0400, /* IORDY may be disabled */ Miordy = 0x0800, /* IORDY supported */ Msoftrst = 0x1000, /* needs soft reset when Bsy */ - Mstdby = 0x2000, /* standby supported */ Mqueueing = 0x4000, /* queueing overlap supported */ Midma = 0x8000, /* interleaved DMA supported */ }; @@ -239,7 +236,6 @@ enum { /* bit masks for supported/enabled features */ Mcfa = 0x0004, Mapm = 0x0008, Mnotify = 0x0010, - Mstandby = 0x0020, Mspinup = 0x0040, Mmaxsec = 0x0100, Mautoacoustic = 0x0200, @@ -285,26 +281,27 @@ typedef struct Ctlr { Drive* drive[2]; Prd* prdt; /* physical region descriptor table */ + void (*irqack)(Ctlr*); QLock; /* current command */ Drive* curdrive; int command; /* last command issued (debugging) */ Rendez; int done; + uint nrq; + uint nildrive; + uint bsy; Lock; /* register access */ } Ctlr; typedef struct Drive { Ctlr* ctlr; + SDunit *unit; int dev; ushort info[256]; - int c; /* cylinder */ - int h; /* head */ - int s; /* sector */ - vlong sectors; /* total */ - int secsize; /* sector size */ + Sfis; int dma; /* DMA R/W possible */ int dmactl; @@ -315,8 +312,11 @@ typedef struct Drive { uchar pktcmd[16]; int pktdma; /* this PACKET command using dma */ - uchar sense[18]; - uchar inquiry[48]; + uvlong sectors; + uint secsize; + char serial[20+1]; + char firmware[8+1]; + char model[40+1]; QLock; /* drive access */ int command; /* current command */ @@ -329,14 +329,15 @@ typedef struct Drive { int status; int error; int flags; /* internal flags */ + uint missirq; + uint spurloop; + uint irq; + uint bsy; } Drive; enum { /* internal flags */ - Lba48 = 0x1, /* LBA48 mode */ Lba48always = 0x2, /* ... */ -}; -enum { - Last28 = (1<<28) - 1 - 1, /* all-ones mask is not addressible */ + Online = 0x4, /* drive onlined */ }; static void @@ -358,34 +359,35 @@ pc87415ienable(Ctlr* ctlr) } static void -atadumpstate(Drive* drive, uchar* cmd, vlong lba, int count) +atadumpstate(Drive* drive, SDreq *r, uvlong lba, int count) { Prd *prd; Pcidev *p; Ctlr *ctlr; - int i, bmiba; + int i, bmiba, ccnt; + uvlong clba; - if(!(DEBUG & DbgSTATE)){ - USED(drive, cmd, lba, count); + if(!(DEBUG & DbgSTATE)) return; - } ctlr = drive->ctlr; - print("sdata: command %2.2uX\n", ctlr->command); + print("command %2.2uX\n", ctlr->command); print("data %8.8p limit %8.8p dlen %d status %uX error %uX\n", drive->data, drive->limit, drive->dlen, drive->status, drive->error); - if(cmd != nil){ - print("lba %d -> %lld, count %d -> %d (%d)\n", - (cmd[2]<<24)|(cmd[3]<<16)|(cmd[4]<<8)|cmd[5], lba, - (cmd[7]<<8)|cmd[8], count, drive->count); - } + if(r->clen == -16) + clba = fisrw(nil, r->cmd, &ccnt); + else + sdfakescsirw(r, &clba, &ccnt, 0); + print("lba %llud -> %llud, count %d -> %d (%d)\n", + clba, lba, ccnt, count, drive->count); if(!(inb(ctlr->ctlport+As) & Bsy)){ for(i = 1; i < 7; i++) print(" 0x%2.2uX", inb(ctlr->cmdport+i)); print(" 0x%2.2uX\n", inb(ctlr->ctlport+As)); } - if(drive->command == Cwd || drive->command == Crd){ + if(drive->command == Cwd || drive->command == Crd + || drive->command == (Pdma|Pin) || drive->command == (Pdma|Pout)){ bmiba = ctlr->bmiba; prd = ctlr->prdt; print("bmicx %2.2uX bmisx %2.2uX prdt %8.8p\n", @@ -400,56 +402,51 @@ atadumpstate(Drive* drive, uchar* cmd, vlong lba, int count) } if(ctlr->pcidev && ctlr->pcidev->vid == 0x8086){ p = ctlr->pcidev; - print("0x40: %4.4uX 0x42: %4.4uX", + print("0x40: %4.4uX 0x42: %4.4uX ", pcicfgr16(p, 0x40), pcicfgr16(p, 0x42)); print("0x48: %2.2uX\n", pcicfgr8(p, 0x48)); print("0x4A: %4.4uX\n", pcicfgr16(p, 0x4A)); } } -static int +static void atadebug(int cmdport, int ctlport, char* fmt, ...) { - int i, n; + char *p, *e, buf[PRINTSIZE]; + int i; va_list arg; - char buf[PRINTSIZE]; - if(!(DEBUG & DbgPROBE)){ - USED(cmdport, ctlport, fmt); - return 0; - } + if(!(DEBUG & DbgPROBE)) + return; + p = buf; + e = buf + sizeof buf; va_start(arg, fmt); - n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf; + p = vseprint(p, e, fmt, arg); va_end(arg); if(cmdport){ - if(buf[n-1] == '\n') - n--; - n += snprint(buf+n, PRINTSIZE-n, " ataregs 0x%uX:", - cmdport); + if(p > buf && p[-1] == '\n') + p--; + p = seprint(p, e, " ataregs 0x%uX:", cmdport); for(i = Features; i < Command; i++) - n += snprint(buf+n, PRINTSIZE-n, " 0x%2.2uX", - inb(cmdport+i)); + p = seprint(p, e, " 0x%2.2uX", inb(cmdport+i)); if(ctlport) - n += snprint(buf+n, PRINTSIZE-n, " 0x%2.2uX", - inb(ctlport+As)); - n += snprint(buf+n, PRINTSIZE-n, "\n"); + p = seprint(p, e, " 0x%2.2uX", inb(ctlport+As)); + p = seprint(p, e, "\n"); } - putstrn(buf, n); - - return n; + putstrn(buf, p - buf); } static int -ataready(int cmdport, int ctlport, int dev, int reset, int ready, int micro) +ataready(int cmdport, int ctlport, int dev, int reset, int ready, int m) { - int as; + int as, m0; - atadebug(cmdport, ctlport, "ataready: dev %uX reset %uX ready %uX", - dev, reset, ready); - - for(;;){ + atadebug(cmdport, ctlport, "ataready: dev %ux:%ux reset %ux ready %ux", + cmdport, dev, reset, ready); + m0 = m; + do{ /* * Wait for the controller to become not busy and * possibly for a status bit to become true (usually @@ -467,47 +464,15 @@ ataready(int cmdport, int ctlport, int dev, int reset, int ready, int micro) dev = 0; } else if(ready == 0 || (as & ready)){ - atadebug(0, 0, "ataready: %d 0x%2.2uX\n", micro, as); + atadebug(0, 0, "ataready: %d:%d %#.2ux\n", m, m0, as); return as; } - - if(micro-- <= 0){ - atadebug(0, 0, "ataready: %d 0x%2.2uX\n", micro, as); - break; - } microdelay(1); - } - atadebug(cmdport, ctlport, "ataready: timeout"); - + }while(m-- > 0); + atadebug(0, 0, "ataready: timeout %d %#.2ux\n", m0, as); return -1; } -/* -static int -atacsf(Drive* drive, vlong csf, int supported) -{ - ushort *info; - int cmdset, i, x; - - if(supported) - info = &drive->info[Icsfs]; - else - info = &drive->info[Icsfe]; - - for(i = 0; i < 3; i++){ - x = (csf>>(16*i)) & 0xFFFF; - if(x == 0) - continue; - cmdset = info[i]; - if(cmdset == 0 || cmdset == 0xFFFF) - return 0; - return cmdset & x; - } - - return 0; -} -*/ - static int atadone(void* arg) { @@ -519,7 +484,7 @@ atarwmmode(Drive* drive, int cmdport, int ctlport, int dev) { int as, maxrwm, rwm; - maxrwm = (drive->info[Imaxrwm] & 0xFF); + maxrwm = drive->info[Imaxrwm] & 0xFF; if(maxrwm == 0) return 0; @@ -529,7 +494,7 @@ atarwmmode(Drive* drive, int cmdport, int ctlport, int dev) * the value in Irwm if the 0x100 bit is set. */ if(drive->info[Irwm] & 0x100) - rwm = (drive->info[Irwm] & 0xFF); + rwm = drive->info[Irwm] & 0xFF; else rwm = 0; if(rwm == 0) @@ -552,8 +517,9 @@ atarwmmode(Drive* drive, int cmdport, int ctlport, int dev) } static int -atadmamode(Drive* drive) +atadmamode(SDunit *unit, Drive* drive) { + char buf[32], *s; int dma; /* @@ -570,13 +536,18 @@ atadmamode(Drive* drive) if(drive->dma) drive->dma |= 'U'<<16; } - if(!getconf("*nodma")) - drive->dmactl = drive->dma; + if(unit != nil){ + snprint(buf, sizeof buf, "*%sdma", unit->name); + if((s = getconf(buf)) && strcmp(s, "on") == 0){ + print("set %s dma\n", unit->name); + drive->dmactl = drive->dma; + } + } return dma; } static int -ataidentify(int cmdport, int ctlport, int dev, int pkt, void* info) +ataidentify(Ctlr*, int cmdport, int ctlport, int dev, int pkt, void* info) { int as, command, drdy; @@ -588,6 +559,7 @@ ataidentify(int cmdport, int ctlport, int dev, int pkt, void* info) command = Cid; drdy = Drdy; } + dev &= ~Lba; as = ataready(cmdport, ctlport, dev, Bsy|Drq, drdy, 103*1000); if(as < 0) return as; @@ -602,37 +574,35 @@ ataidentify(int cmdport, int ctlport, int dev, int pkt, void* info) memset(info, 0, 512); inss(cmdport+Data, info, 256); + ataready(cmdport, ctlport, dev, Bsy|Drq, Drdy, 3*1000); inb(cmdport+Status); - if(DEBUG & DbgIDENTIFY){ - int i; - ushort *sp; - - sp = (ushort*)info; - for(i = 0; i < 256; i++){ - if(i && (i%16) == 0) - print("\n"); - print(" %4.4uX", *sp); - sp++; - } - print("\n"); - } - return 0; } static Drive* -atadrive(int cmdport, int ctlport, int dev) +atadrive(SDunit *unit, Drive *drive, int cmdport, int ctlport, int dev) { - Drive *drive; - int as, i, pkt; - uchar buf[512], *p; - ushort iconfig, *sp; + int as, pkt; + uchar buf[512], oserial[21]; + uvlong osectors; + Ctlr *ctlr; + if(DEBUG & DbgIDENTIFY) + print("identify: port %ux dev %.2ux\n", cmdport, dev & ~Lba); atadebug(0, 0, "identify: port 0x%uX dev 0x%2.2uX\n", cmdport, dev); pkt = 1; + if(drive != nil){ + osectors = drive->sectors; + memmove(oserial, drive->serial, sizeof drive->serial); + ctlr = drive->ctlr; + }else{ + osectors = 0; + memset(oserial, 0, sizeof drive->serial); + ctlr = nil; + } retry: - as = ataidentify(cmdport, ctlport, dev, pkt, buf); + as = ataidentify(ctlr, cmdport, ctlport, dev, pkt, buf); if(as < 0) return nil; if(as & Err){ @@ -642,79 +612,54 @@ retry: goto retry; } - if((drive = malloc(sizeof(Drive))) == nil) - return nil; - drive->dev = dev; + if(drive == 0){ + if((drive = malloc(sizeof(Drive))) == nil) + return nil; + drive->serial[0] = ' '; + drive->dev = dev; + } + memmove(drive->info, buf, sizeof(drive->info)); - drive->sense[0] = 0x70; - drive->sense[7] = sizeof(drive->sense)-7; - drive->inquiry[2] = 2; - drive->inquiry[3] = 2; - drive->inquiry[4] = sizeof(drive->inquiry)-4; - p = &drive->inquiry[8]; - sp = &drive->info[Imodel]; - for(i = 0; i < 20; i++){ - *p++ = *sp>>8; - *p++ = *sp++; + setfissig(drive, pkt? 0xeb140000: 0x0101); + drive->sectors = idfeat(drive, drive->info); + drive->secsize = idss(drive, drive->info); + + idmove(drive->serial, drive->info+10, 20); + idmove(drive->firmware, drive->info+23, 8); + idmove(drive->model, drive->info+27, 40); + if(unit != nil){ + memset(unit->inquiry, 0, sizeof unit->inquiry); + unit->inquiry[2] = 2; + unit->inquiry[3] = 2; + unit->inquiry[4] = sizeof unit->inquiry - 4; + memmove(unit->inquiry+8, drive->model, 40); } - drive->secsize = 512; - - /* - * Beware the CompactFlash Association feature set. - * Now, why this value in Iconfig just walks all over the bit - * definitions used in the other parts of the ATA/ATAPI standards - * is a mystery and a sign of true stupidity on someone's part. - * Anyway, the standard says if this value is 0x848A then it's - * CompactFlash and it's NOT a packet device. - */ - iconfig = drive->info[Iconfig]; - if(iconfig != 0x848A && (iconfig & 0xC000) == 0x8000){ - if(iconfig & 0x01) + if(pkt){ + drive->pkt = 12; + if(drive->feat & Datapi16) drive->pkt = 16; - else - drive->pkt = 12; - } - else{ - if(drive->info[Ivalid] & 0x0001){ - drive->c = drive->info[Iccyl]; - drive->h = drive->info[Ichead]; - drive->s = drive->info[Icsec]; - } - else{ - drive->c = drive->info[Ilcyl]; - drive->h = drive->info[Ilhead]; - drive->s = drive->info[Ilsec]; - } - if(drive->info[Icapabilities] & Mlba){ - if(drive->info[Icsfs+1] & Maddr48){ - drive->sectors = drive->info[Ilba48] - | (drive->info[Ilba48+1]<<16) - | ((vlong)drive->info[Ilba48+2]<<32); - drive->flags |= Lba48; - } - else{ - drive->sectors = (drive->info[Ilba+1]<<16) - |drive->info[Ilba]; - } + }else{ + if(drive->feat & Dlba) drive->dev |= Lba; - } - else - drive->sectors = drive->c*drive->h*drive->s; atarwmmode(drive, cmdport, ctlport, dev); } - atadmamode(drive); + atadmamode(unit, drive); + if(osectors != 0 && memcmp(oserial, drive->serial, sizeof oserial) != 0) + if(unit) + unit->sectors = 0; + drive->unit = unit; if(DEBUG & DbgCONFIG){ print("dev %2.2uX port %uX config %4.4uX capabilities %4.4uX", - dev, cmdport, iconfig, drive->info[Icapabilities]); + dev, cmdport, drive->info[Iconfig], drive->info[Icapabilities]); print(" mwdma %4.4uX", drive->info[Imwdma]); if(drive->info[Ivalid] & 0x04) print(" udma %4.4uX", drive->info[Iudma]); print(" dma %8.8uX rwm %ud", drive->dma, drive->rwm); - if(drive->flags&Lba48) - print("\tLLBA sectors %lld", drive->sectors); + if(drive->feat&Dllba) + print("\tLLBA sectors %llud", drive->sectors); print("\n"); } @@ -724,28 +669,41 @@ retry: static void atasrst(int ctlport) { + int dc0; + /* * Srst is a big stick and may cause problems if further * commands are tried before the drives become ready again. * Also, there will be problems here if overlapped commands * are ever supported. */ + dc0 = inb(ctlport+Dc); microdelay(5); - outb(ctlport+Dc, Srst); + outb(ctlport+Dc, Srst|dc0); microdelay(5); - outb(ctlport+Dc, 0); + outb(ctlport+Dc, dc0); microdelay(2*1000); } +static int +seldev(int dev, int map) +{ + if((dev & Devs) == Dev0 && map&1) + return dev; + if((dev & Devs) == Dev1 && map&2) + return dev; + return -1; +} + static SDev* -ataprobe(int cmdport, int ctlport, int irq) +ataprobe(int cmdport, int ctlport, int irq, int map) { Ctlr* ctlr; SDev *sdev; Drive *drive; int dev, error, rhi, rlo; static int nonlegacy = 'C'; - + if(ioalloc(cmdport, 8, 0, "atacmd") < 0) { print("ataprobe: Cannot allocate %X\n", cmdport); return nil; @@ -769,7 +727,9 @@ ataprobe(int cmdport, int ctlport, int irq) * single-drive configuration the registers of the existing drive * are often seen, only command execution fails. */ - dev = Dev0; + if((dev = seldev(Dev0, map)) == -1) + if((dev = seldev(Dev1, map)) == -1) + goto release; if(inb(ctlport+As) & Bsy){ outb(cmdport+Dh, dev); microdelay(1); @@ -781,13 +741,15 @@ trydev1: rlo = inb(cmdport+Cyllo); rhi = inb(cmdport+Cylhi); if(rlo != 0xAA && (rlo == 0xFF || rhi != 0x55)){ - if(dev == Dev1){ + if(dev == Dev1 || (dev = seldev(Dev1, map)) == -1){ release: + outb(cmdport+Dc, Nien); + inb(cmdport+Status); + /* further measures to prevent irqs? */ iofree(cmdport); iofree(ctlport+As); return nil; } - dev = Dev1; if(ataready(cmdport, ctlport, dev, Bsy, 0, 20*1000) < 0) goto trydev1; } @@ -834,7 +796,8 @@ tryedd1: if((error & ~0x80) != 0x01){ if(dev == Dev1) goto release; - dev = Dev1; + if((dev = seldev(Dev1, map)) == -1) + goto release; goto tryedd1; } @@ -845,19 +808,17 @@ tryedd1: * If the one drive found is Dev0 and the EDD command * didn't indicate Dev1 doesn't exist, check for it. */ - if((drive = atadrive(cmdport, ctlport, dev)) == nil) + if((drive = atadrive(0, 0, cmdport, ctlport, dev)) == nil) goto release; if((ctlr = malloc(sizeof(Ctlr))) == nil){ free(drive); goto release; } - memset(ctlr, 0, sizeof(Ctlr)); if((sdev = malloc(sizeof(SDev))) == nil){ free(ctlr); free(drive); goto release; } - memset(sdev, 0, sizeof(SDev)); drive->ctlr = ctlr; if(dev == Dev0){ ctlr->drive[0] = drive; @@ -869,7 +830,7 @@ tryedd1: * Ataprobe is the only place possibly invalid * drives should be selected. */ - drive = atadrive(cmdport, ctlport, Dev1); + drive = atadrive(0, 0, cmdport, ctlport, Dev1); if(drive != nil){ drive->ctlr = ctlr; ctlr->drive[1] = drive; @@ -902,7 +863,7 @@ tryedd1: nonlegacy = 'E'; break; } - sdev->ifc = &sdataifc; + sdev->ifc = &sdideifc; sdev->ctlr = ctlr; sdev->nunit = 2; ctlr->sdev = sdev; @@ -936,10 +897,13 @@ ataclear(SDev *sdev) static char * atastat(SDev *sdev, char *p, char *e) { - Ctlr *ctlr = sdev->ctlr; + Ctlr *ctlr; + ctlr = sdev->ctlr; +// return seprint(p, e, "%s ata port %X ctl %X irq %d %T\n", +// sdev->name, ctlr->cmdport, ctlr->ctlport, ctlr->irq, ctlr->tbdf); return seprint(p, e, "%s ata port %X ctl %X irq %d\n", - sdev->name, ctlr->cmdport, ctlr->ctlport, ctlr->irq); + sdev->name, ctlr->cmdport, ctlr->ctlport, ctlr->irq); } static SDev* @@ -957,82 +921,38 @@ ataprobew(DevConf *cf) if((p=strchr(cf->type, '/')) == nil || pcmspecial(p+1, &isa) < 0) error("cannot find controller"); - return ataprobe(cf->ports[0].port, cf->ports[1].port, cf->intnum); + return ataprobe(cf->ports[0].port, cf->ports[1].port, cf->intnum, 3); } -/* - * These are duplicated with sdsetsense, etc., in devsd.c, but - * those assume that the disk is not SCSI while in fact here - * ata drives are not SCSI but ATAPI ones kind of are. - */ -static int -atasetsense(Drive* drive, int status, int key, int asc, int ascq) -{ - drive->sense[2] = key; - drive->sense[12] = asc; - drive->sense[13] = ascq; - - return status; -} +static void atainterrupt(Ureg*, void*); static int -atamodesense(Drive* drive, uchar* cmd) +iowait(Drive *drive, int ms, int interrupt) { - int len; - - /* - * Fake a vendor-specific request with page code 0, - * return the drive info. - */ - if((cmd[2] & 0x3F) != 0 && (cmd[2] & 0x3F) != 0x3F) - return atasetsense(drive, SDcheck, 0x05, 0x24, 0); - len = (cmd[7]<<8)|cmd[8]; - if(len == 0) - return SDok; - if(len < 8+sizeof(drive->info)) - return atasetsense(drive, SDcheck, 0x05, 0x1A, 0); - if(drive->data == nil || drive->dlen < len) - return atasetsense(drive, SDcheck, 0x05, 0x20, 1); - memset(drive->data, 0, 8); - drive->data[0] = sizeof(drive->info)>>8; - drive->data[1] = sizeof(drive->info); - memmove(drive->data+8, drive->info, sizeof(drive->info)); - drive->data += 8+sizeof(drive->info); - - return SDok; -} - -static int -atastandby(Drive* drive, int period) -{ - Ctlr* ctlr; - int cmdport, done; + int msec, step; + Ctlr *ctlr; + step = 1000; + if(drive->missirq > 10) + step = 50; ctlr = drive->ctlr; - drive->command = Cstandby; - qlock(ctlr); - - cmdport = ctlr->cmdport; - ilock(ctlr); - outb(cmdport+Count, period); - outb(cmdport+Dh, drive->dev); - ctlr->done = 0; - ctlr->curdrive = drive; - ctlr->command = Cstandby; /* debugging */ - outb(cmdport+Command, Cstandby); - iunlock(ctlr); - - while(waserror()) - ; - tsleep(ctlr, atadone, ctlr, 60*1000); - poperror(); - - done = ctlr->done; - qunlock(ctlr); - - if(!done || (drive->status & Err)) - return atasetsense(drive, SDcheck, 4, 8, drive->error); - return SDok; + for(msec = 0; msec < ms; msec += step){ + while(waserror()) + if(interrupt) + return -1; + tsleep(ctlr, atadone, ctlr, step); + poperror(); + if(ctlr->done) + break; + atainterrupt(nil, ctlr); + if(ctlr->done){ + if(drive->missirq++ < 3) + print("ide: caught missed irq\n"); + break; + }else + drive->spurloop++; + } + return ctlr->done; } static void @@ -1072,12 +992,12 @@ static void ataabort(Drive* drive, int dolock) { /* - * If NOP is available (packet commands) use it otherwise + * If NOP is available use it otherwise * must try a software reset. */ if(dolock) ilock(drive->ctlr); - if(drive->info[Icsfs] & Mnop) + if(drive->feat & Dnop) atanop(drive, 0); else{ atasrst(drive->ctlr->ctlport); @@ -1132,9 +1052,9 @@ atadmasetup(Drive* drive, int len) bmiba = ctlr->bmiba; outl(bmiba+Bmidtpx, PCIWADDR(ctlr->prdt)); if(drive->write) - outb(ctlr->bmiba+Bmicx, 0); + outb(bmiba+Bmicx, 0); else - outb(ctlr->bmiba+Bmicx, Rwcon); + outb(bmiba+Bmicx, Rwcon); bmisx = inb(bmiba+Bmisx); outb(bmiba+Bmisx, bmisx|Ideints|Idedmae); @@ -1243,33 +1163,27 @@ atapktinterrupt(Drive* drive) } static int -atapktio(Drive* drive, uchar* cmd, int clen) +atapktio0(Drive *drive, SDreq *r) { + uchar *cmd; + int as, cmdport, ctlport, len, rv, timeo; Ctlr *ctlr; - int as, cmdport, ctlport, len, r, timeo; - - if(cmd[0] == 0x5A && (cmd[2] & 0x3F) == 0) - return atamodesense(drive, cmd); - - r = SDok; + rv = SDok; + cmd = r->cmd; drive->command = Cpkt; - memmove(drive->pktcmd, cmd, clen); - memset(drive->pktcmd+clen, 0, drive->pkt-clen); + memmove(drive->pktcmd, cmd, r->clen); + memset(drive->pktcmd+r->clen, 0, drive->pkt-r->clen); drive->limit = drive->data+drive->dlen; ctlr = drive->ctlr; cmdport = ctlr->cmdport; ctlport = ctlr->ctlport; - qlock(ctlr); - as = ataready(cmdport, ctlport, drive->dev, Bsy|Drq, Drdy, 107*1000); /* used to test as&Chk as failure too, but some CD readers use that for media change */ - if(as < 0){ - qunlock(ctlr); - return -1; - } + if(as < 0) + return SDnostatus; ilock(ctlr); if(drive->dlen && drive->dmactl && !atadmasetup(drive, drive->dlen)) @@ -1298,7 +1212,7 @@ atapktio(Drive* drive, uchar* cmd, int clen) drive->status = as<0 ? 0 : as; ctlr->curdrive = nil; ctlr->done = 1; - r = SDtimeout; + rv = SDtimeout; }else atapktinterrupt(drive); } @@ -1328,25 +1242,37 @@ atapktio(Drive* drive, uchar* cmd, int clen) } poperror(); - qunlock(ctlr); - if(drive->status & Chk) - r = SDcheck; + rv = SDcheck; + return rv; +} - return r; +static int +atapktio(Drive* drive, SDreq *r) +{ + int n; + Ctlr *ctlr; + + ctlr = drive->ctlr; + qlock(ctlr); + n = atapktio0(drive, r); + qunlock(ctlr); + return n; } static uchar cmd48[256] = { [Crs] Crs48, [Crd] Crd48, - [Crdq] Crdq48, [Crsm] Crsm48, [Cws] Cws48, [Cwd] Cwd48, - [Cwdq] Cwdq48, [Cwsm] Cwsm48, }; +enum{ + Last28 = (1<<28) - 1 - 1, +}; + static int atageniostart(Drive* drive, uvlong lba) { @@ -1356,19 +1282,21 @@ atageniostart(Drive* drive, uvlong lba) use48 = 0; if((drive->flags&Lba48always) || lba > Last28 || drive->count > 256){ - if(!(drive->flags & Lba48)) + if((drive->feat & Dllba) == 0) return -1; use48 = 1; c = h = s = 0; - } - else if(drive->dev & Lba){ + }else if(drive->dev & Lba){ c = (lba>>8) & 0xFFFF; h = (lba>>24) & 0x0F; s = lba & 0xFF; - } - else{ + }else{ + if (drive->s == 0 || drive->h == 0){ + print("sdide: chs address botch"); + return -1; + } c = lba/(drive->s*drive->h); - h = ((lba/drive->s) % drive->h); + h = (lba/drive->s) % drive->h; s = (lba % drive->s) + 1; } @@ -1415,8 +1343,7 @@ atageniostart(Drive* drive, uvlong lba) if(DEBUG & Dbg48BIT) print("using 48-bit commands\n"); - } - else{ + }else{ outb(cmdport+Count, drive->count); outb(cmdport+Sector, s); outb(cmdport+Cyllo, c); @@ -1455,142 +1382,42 @@ atageniostart(Drive* drive, uvlong lba) } static int -atagenioretry(Drive* drive) +atagenioretry(Drive* drive, SDreq *r, uvlong lba, int count) { + char *s; + int rv, count0, rw; + uvlong lba0; + if(drive->dmactl){ drive->dmactl = 0; - print("atagenioretry: disabling dma\n"); - } - else if(drive->rwmctl) + s = "disabling dma"; + rv = SDretry; + }else if(drive->rwmctl){ drive->rwmctl = 0; - else - return atasetsense(drive, SDcheck, 4, 8, drive->error); - - return SDretry; + s = "disabling rwm"; + rv = SDretry; + }else{ + s = "nondma"; + rv = sdsetsense(r, SDcheck, 4, 8, drive->error); + } + sdfakescsirw(r, &lba0, &count0, &rw); + print("atagenioretry: %s %c:%llud:%d @%llud:%d\n", + s, "rw"[rw], lba0, count0, lba, count); + return rv; } static int -atagenio(Drive* drive, uchar* cmd, int clen) +atagenio(Drive* drive, SDreq *r) { - uchar *p; Ctlr *ctlr; - vlong lba, len; - int count, maxio; - - /* - * Map SCSI commands into ATA commands for discs. - * Fail any command with a LUN except INQUIRY which - * will return 'logical unit not supported'. - */ - if((cmd[1]>>5) && cmd[0] != 0x12) - return atasetsense(drive, SDcheck, 0x05, 0x25, 0); - - switch(cmd[0]){ - default: - return atasetsense(drive, SDcheck, 0x05, 0x20, 0); - - case 0x00: /* test unit ready */ - return SDok; - - case 0x03: /* request sense */ - if(cmd[4] < sizeof(drive->sense)) - len = cmd[4]; - else - len = sizeof(drive->sense); - if(drive->data && drive->dlen >= len){ - memmove(drive->data, drive->sense, len); - drive->data += len; - } - return SDok; - - case 0x12: /* inquiry */ - if(cmd[4] < sizeof(drive->inquiry)) - len = cmd[4]; - else - len = sizeof(drive->inquiry); - if(drive->data && drive->dlen >= len){ - memmove(drive->data, drive->inquiry, len); - drive->data += len; - } - return SDok; - - case 0x1B: /* start/stop unit */ - /* - * NOP for now, can use the power management feature - * set later. - */ - return SDok; - - case 0x25: /* read capacity */ - if((cmd[1] & 0x01) || cmd[2] || cmd[3]) - return atasetsense(drive, SDcheck, 0x05, 0x24, 0); - if(drive->data == nil || drive->dlen < 8) - return atasetsense(drive, SDcheck, 0x05, 0x20, 1); - /* - * Read capacity returns the LBA of the last sector. - */ - len = drive->sectors-1; - p = drive->data; - *p++ = len>>24; - *p++ = len>>16; - *p++ = len>>8; - *p++ = len; - len = drive->secsize; - *p++ = len>>24; - *p++ = len>>16; - *p++ = len>>8; - *p = len; - drive->data += 8; - return SDok; - - case 0x9E: /* long read capacity */ - if((cmd[1] & 0x01) || cmd[2] || cmd[3]) - return atasetsense(drive, SDcheck, 0x05, 0x24, 0); - if(drive->data == nil || drive->dlen < 8) - return atasetsense(drive, SDcheck, 0x05, 0x20, 1); - /* - * Read capacity returns the LBA of the last sector. - */ - len = drive->sectors-1; - p = drive->data; - *p++ = len>>56; - *p++ = len>>48; - *p++ = len>>40; - *p++ = len>>32; - *p++ = len>>24; - *p++ = len>>16; - *p++ = len>>8; - *p++ = len; - len = drive->secsize; - *p++ = len>>24; - *p++ = len>>16; - *p++ = len>>8; - *p = len; - drive->data += 12; - return SDok; - - case 0x28: /* read */ - case 0x88: - case 0x2a: /* write */ - case 0x8a: - break; - - case 0x5A: - return atamodesense(drive, cmd); - } + uvlong lba; + int i, rw, count, maxio; + if((i = sdfakescsi(r)) != SDnostatus) + return i; + if((i = sdfakescsirw(r, &lba, &count, &rw)) != SDnostatus) + return i; ctlr = drive->ctlr; - if(clen == 16){ - /* ata commands only go to 48-bit lba */ - if(cmd[2] || cmd[3]) - return atasetsense(drive, SDcheck, 3, 0xc, 2); - lba = (uvlong)cmd[4]<<40 | (uvlong)cmd[5]<<32; - lba |= cmd[6]<<24 | cmd[7]<<16 | cmd[8]<<8 | cmd[9]; - count = cmd[10]<<24 | cmd[11]<<16 | cmd[12]<<8 | cmd[13]; - }else{ - lba = cmd[2]<<24 | cmd[3]<<16 | cmd[4]<<8 | cmd[5]; - count = cmd[7]<<8 | cmd[8]; - } if(drive->data == nil) return SDok; if(drive->dlen < count*drive->secsize) @@ -1598,7 +1425,7 @@ atagenio(Drive* drive, uchar* cmd, int clen) qlock(ctlr); if(ctlr->maxio) maxio = ctlr->maxio; - else if(drive->flags & Lba48) + else if(drive->feat & Dllba) maxio = 65536; else maxio = 256; @@ -1612,13 +1439,9 @@ atagenio(Drive* drive, uchar* cmd, int clen) atanop(drive, 0); iunlock(ctlr); qunlock(ctlr); - return atagenioretry(drive); + return atagenioretry(drive, r, lba, count); } - - while(waserror()) - ; - tsleep(ctlr, atadone, ctlr, 60*1000); - poperror(); + iowait(drive, 60*1000, 0); if(!ctlr->done){ /* * What should the above timeout be? In @@ -1626,15 +1449,16 @@ atagenio(Drive* drive, uchar* cmd, int clen) * long as 30 seconds for a drive to respond. * Very hard to get out of this cleanly. */ - atadumpstate(drive, cmd, lba, count); + atadumpstate(drive, r, lba, count); ataabort(drive, 1); qunlock(ctlr); - return atagenioretry(drive); + return atagenioretry(drive, r, lba, count); } if(drive->status & Err){ qunlock(ctlr); - return atasetsense(drive, SDcheck, 4, 8, drive->error); +print("atagenio: %llud:%d\n", lba, drive->count); + return sdsetsense(r, SDcheck, 4, 8, drive->error); } count -= drive->count; lba += drive->count; @@ -1647,11 +1471,11 @@ atagenio(Drive* drive, uchar* cmd, int clen) static int atario(SDreq* r) { + uchar *p; + int status; Ctlr *ctlr; Drive *drive; SDunit *unit; - uchar cmd10[10], *cmdp, *p; - int clen, reqstatus, status; unit = r->unit; if((ctlr = unit->dev->ctlr) == nil || ctlr->drive[unit->subno] == nil){ @@ -1659,54 +1483,25 @@ atario(SDreq* r) return SDtimeout; } drive = ctlr->drive[unit->subno]; - - /* - * Most SCSI commands can be passed unchanged except for - * the padding on the end. The few which require munging - * are not used internally. Mode select/sense(6) could be - * converted to the 10-byte form but it's not worth the - * effort. Read/write(6) are easy. - */ - switch(r->cmd[0]){ - case 0x08: /* read */ - case 0x0A: /* write */ - cmdp = cmd10; - memset(cmdp, 0, sizeof(cmd10)); - cmdp[0] = r->cmd[0]|0x20; - cmdp[1] = r->cmd[1] & 0xE0; - cmdp[5] = r->cmd[3]; - cmdp[4] = r->cmd[2]; - cmdp[3] = r->cmd[1] & 0x0F; - cmdp[8] = r->cmd[4]; - clen = sizeof(cmd10); - break; - - default: - cmdp = r->cmd; - clen = r->clen; - break; - } - qlock(drive); -retry: - drive->write = r->write; - drive->data = r->data; - drive->dlen = r->dlen; - - drive->status = 0; - drive->error = 0; - if(drive->pkt) - status = atapktio(drive, cmdp, clen); - else - status = atagenio(drive, cmdp, clen); - if(status == SDretry){ + for(;;){ + drive->write = r->write; + drive->data = r->data; + drive->dlen = r->dlen; + drive->status = 0; + drive->error = 0; + if(drive->pkt) + status = atapktio(drive, r); + else + status = atagenio(drive, r); + if(status != SDretry) + break; if(DbgDEBUG) print("%s: retry: dma %8.8uX rwm %4.4uX\n", unit->name, drive->dmactl, drive->rwmctl); - goto retry; } - if(status == SDok){ - atasetsense(drive, SDok, 0, 0, 0); + if(status == SDok && r->rlen == 0 && (r->flags & SDvalidsense) == 0){ + sdsetsense(r, SDok, 0, 0, 0); if(drive->data){ p = r->data; r->rlen = drive->data - p; @@ -1714,48 +1509,359 @@ retry: else r->rlen = 0; } - else if(status == SDcheck && !(r->flags & SDnosense)){ - drive->write = 0; - memset(cmd10, 0, sizeof(cmd10)); - cmd10[0] = 0x03; - cmd10[1] = r->lun<<5; - cmd10[4] = sizeof(r->sense)-1; - drive->data = r->sense; - drive->dlen = sizeof(r->sense)-1; - drive->status = 0; - drive->error = 0; - if(drive->pkt) - reqstatus = atapktio(drive, cmd10, 6); - else - reqstatus = atagenio(drive, cmd10, 6); - if(reqstatus == SDok){ - r->flags |= SDvalidsense; - atasetsense(drive, SDok, 0, 0, 0); - } - } qunlock(drive); - r->status = status; - if(status != SDok) - return status; + return status; +} - /* - * Fix up any results. - * Many ATAPI CD-ROMs ignore the LUN field completely and - * return valid INQUIRY data. Patch the response to indicate - * 'logical unit not supported' if the LUN is non-zero. - */ - switch(cmdp[0]){ - case 0x12: /* inquiry */ - if((p = r->data) == nil) - break; - if((cmdp[1]>>5) && (!drive->pkt || (p[0] & 0x1F) == 0x05)) - p[0] = 0x7F; - /*FALLTHROUGH*/ +/**/ +static int +isdmacmd(Drive *d, SDreq *r) +{ + switch(r->ataproto & Pprotom){ default: + return 0; + case Pdmq: + error("no queued support"); + case Pdma: + if(!(d->dmactl || d->rwmctl)) + error("dma in non dma mode\n"); + return 1; + } +} + +static int +atagenatastart(Drive* d, SDreq *r) +{ + uchar u; + int as, cmdport, ctlport, len, pr, isdma; + Ctlr *ctlr; + + isdma = isdmacmd(d, r); + ctlr = d->ctlr; + cmdport = ctlr->cmdport; + ctlport = ctlr->ctlport; + if(ataready(cmdport, ctlport, d->dev, Bsy|Drq, d->pkt? 0: Drdy, 101*1000) < 0) + return -1; + + ilock(ctlr); + if(isdma && atadmasetup(d, d->block)){ + iunlock(ctlr); + return -1; + + } + if(d->feat & Dllba && (r->ataproto & P28) == 0){ + outb(cmdport+Features, r->cmd[Ffeat8]); + outb(cmdport+Features, r->cmd[Ffeat]); + outb(cmdport+Count, r->cmd[Fsc8]); + outb(cmdport+Count, r->cmd[Fsc]); + outb(cmdport+Lbalo, r->cmd[Flba24]); + outb(cmdport+Lbalo, r->cmd[Flba0]); + outb(cmdport+Lbamid, r->cmd[Flba32]); + outb(cmdport+Lbamid, r->cmd[Flba8]); + outb(cmdport+Lbahi, r->cmd[Flba40]); + outb(cmdport+Lbahi, r->cmd[Flba16]); + u = r->cmd[Fdev] & ~0xb0; + outb(cmdport+Dh, d->dev|u); + }else{ + outb(cmdport+Features, r->cmd[Ffeat]); + outb(cmdport+Count, r->cmd[Fsc]); + outb(cmdport+Lbalo, r->cmd[Flba0]); + outb(cmdport+Lbamid, r->cmd[Flba8]); + outb(cmdport+Lbahi, r->cmd[Flba16]); + u = r->cmd[Fdev] & ~0xb0; + outb(cmdport+Dh, d->dev|u); + } + ctlr->done = 0; + ctlr->curdrive = d; + d->command = r->ataproto & (Pprotom|Pdatam); + ctlr->command = r->cmd[Fcmd]; + outb(cmdport+Command, r->cmd[Fcmd]); + + pr = r->ataproto & Pprotom; + if(pr == Pnd || pr == Preset) + USED(d); + else if(!isdma){ + microdelay(1); + /* 10*1000 for flash ide drives - maybe detect them? */ + as = ataready(cmdport, ctlport, 0, Bsy, Drq|Err, 10*1000); + if(as < 0 || (as & Err)){ + iunlock(ctlr); + return -1; + } + len = d->block; + if(r->write && len > 0) + outss(cmdport+Data, d->data, len/2); + }else + atadmastart(ctlr, d->write); + iunlock(ctlr); + return 0; +} + +static void +mkrfis(Drive *d, SDreq *r) +{ + uchar *u; + int cmdport; + Ctlr *ctlr; + + ctlr = d->ctlr; + cmdport = ctlr->cmdport; + u = r->cmd; + + ilock(ctlr); + u[Ftype] = 0x34; + u[Fioport] = 0; + if((d->feat & Dllba) && (r->ataproto & P28) == 0){ + u[Frerror] = inb(cmdport+Error); + u[Fsc8] = inb(cmdport+Count); + u[Fsc] = inb(cmdport+Count); + u[Flba24] = inb(cmdport+Lbalo); + u[Flba0] = inb(cmdport+Lbalo); + u[Flba32] = inb(cmdport+Lbamid); + u[Flba8] = inb(cmdport+Lbamid); + u[Flba40] = inb(cmdport+Lbahi); + u[Flba16] = inb(cmdport+Lbahi); + u[Fdev] = inb(cmdport+Dh); + u[Fstatus] = inb(cmdport+Status); + }else{ + u[Frerror] = inb(cmdport+Error); + u[Fsc] = inb(cmdport+Count); + u[Flba0] = inb(cmdport+Lbalo); + u[Flba8] = inb(cmdport+Lbamid); + u[Flba16] = inb(cmdport+Lbahi); + u[Fdev] = inb(cmdport+Dh); + u[Fstatus] = inb(cmdport+Status); + } + iunlock(ctlr); +} + +static int +atarstdone(Drive *d) +{ + int as; + Ctlr *c; + + c = d->ctlr; + as = ataready(c->cmdport, c->ctlport, 0, Bsy|Drq, 0, 5*1000); + c->done = as >= 0; + return c->done; +} + +static uint +cmdss(Drive *d, SDreq *r) +{ + switch(r->cmd[Fcmd]){ + case Cid: + case Cidpkt: + return 512; + default: + return d->secsize; + } +} + +/* + * various checks. we should be craftier and + * avoid figuring out how big stuff is supposed to be. + */ +static uint +patasizeck(Drive *d, SDreq *r) +{ + uint count, maxio, secsize; + Ctlr *ctlr; + + secsize = cmdss(d, r); /* BOTCH */ + if(secsize == 0) + error(Eio); + count = r->dlen / secsize; + ctlr = d->ctlr; + if(ctlr->maxio) + maxio = ctlr->maxio; + else if((d->feat & Dllba) && (r->ataproto & P28) == 0) + maxio = 65536; + else + maxio = 256; + if(count > maxio){ + uprint("i/o too large, lim %d", maxio); + error(up->genbuf); + } + if(r->ataproto&Ppio && count > 1) + error("invalid # of sectors"); + return count; +} + +static int +atapataio(Drive *d, SDreq *r) +{ + int rv; + Ctlr *ctlr; + + d->count = 0; + if(r->ataproto & Pdatam) + d->count = patasizeck(d, r); + d->block = r->dlen; + d->limit = d->data + r->dlen; + + ctlr = d->ctlr; + qlock(ctlr); + if(waserror()){ + qunlock(ctlr); + nexterror(); + } + rv = atagenatastart(d, r); + poperror(); + if(rv){ + if(DEBUG & DbgAtazz) + print("sdide: !atageatastart\n"); + ilock(ctlr); + atanop(d, 0); + iunlock(ctlr); + qunlock(ctlr); + return sdsetsense(r, SDcheck, 4, 8, d->error); + } + + if((r->ataproto & Pprotom) == Preset) + atarstdone(d); + else + while(iowait(d, 30*1000, 1) == 0) + ; + if(!ctlr->done){ + if(DEBUG & DbgAtazz){ + print("sdide: !done\n"); + atadumpstate(d, r, 0, d->count); + } + ataabort(d, 1); + qunlock(ctlr); + return sdsetsense(r, SDcheck, 11, 0, 6); /* aborted; i/o process terminated */ + } + mkrfis(d, r); + if(d->status & Err){ + if(DEBUG & DbgAtazz) + print("sdide: status&Err\n"); + qunlock(ctlr); + return sdsetsense(r, SDcheck, 4, 8, d->error); + } + qunlock(ctlr); + return SDok; +} + +static int +ataataio0(Drive *d, SDreq *r) +{ + int i; + + if((r->ataproto & Pprotom) == Ppkt){ + if(r->clen > d->pkt) + error(Eio); + qlock(d->ctlr); + i = atapktio0(d, r); + d->block = d->data - (uchar*)r->data; + mkrfis(d, r); + qunlock(d->ctlr); + return i; + }else + return atapataio(d, r); +} + +/* + * hack to allow udma mode to be set or unset + * via direct ata command. it would be better + * to move the assumptions about dma mode out + * of some of the helper functions. + */ +static int +isudm(SDreq *r) +{ + uchar *c; + + c = r->cmd; + if(c[Fcmd] == 0xef && c[Ffeat] == 0x03){ + if(c[Fsc]&0x40) + return 1; + return -1; + } + return 0; +} + +static int +fisreqchk(Sfis *f, SDreq *r) +{ + if((r->ataproto & Pprotom) == Ppkt) + return SDnostatus; + /* + * handle oob requests; + * restrict & sanitize commands + */ + if(r->clen != 16) + error(Eio); + if(r->cmd[0] == 0xf0){ + sigtofis(f, r->cmd); + r->status = SDok; + return SDok; + } + r->cmd[0] = 0x27; + r->cmd[1] = 0x80; + r->cmd[7] |= 0xa0; + return SDnostatus; +} + +static int +ataataio(SDreq *r) +{ + int status, udm; + Ctlr *c; + Drive *d; + SDunit *u; + + u = r->unit; + if((c = u->dev->ctlr) == nil || (d = c->drive[u->subno]) == nil){ + r->status = SDtimeout; + return SDtimeout; + } + if((status = fisreqchk(d, r)) != SDnostatus) + return status; + udm = isudm(r); + + qlock(d); + if(waserror()){ + qunlock(d); + nexterror(); + } +retry: + d->write = r->write; + d->data = r->data; + d->dlen = r->dlen; + d->status = 0; + d->error = 0; + + switch(status = ataataio0(d, r)){ + case SDretry: + if(DbgDEBUG) + print("%s: retry: dma %.8ux rwm %.4ux\n", + u->name, d->dmactl, d->rwmctl); + goto retry; + case SDok: + if(udm == 1) + d->dmactl = d->dma; + else if(udm == -1) + d->dmactl = 0; + sdsetsense(r, SDok, 0, 0, 0); + r->rlen = d->block; break; } + poperror(); + qunlock(d); + r->status = status; + return status; +} +/**/ - return SDok; +static void +ichirqack(Ctlr *ctlr) +{ + int bmiba; + + if(bmiba = ctlr->bmiba) + outb(bmiba+Bmisx, inb(bmiba+Bmisx)); } static void @@ -1768,7 +1874,13 @@ atainterrupt(Ureg*, void* arg) ctlr = arg; ilock(ctlr); + ctlr->nrq++; + if(ctlr->curdrive) + ctlr->curdrive->irq++; if(inb(ctlr->ctlport+As) & Bsy){ + ctlr->bsy++; + if(ctlr->curdrive) + ctlr->curdrive->bsy++; iunlock(ctlr); if(DEBUG & DbgBsy) print("IBsy+"); @@ -1777,6 +1889,9 @@ atainterrupt(Ureg*, void* arg) cmdport = ctlr->cmdport; status = inb(cmdport+Status); if((drive = ctlr->curdrive) == nil){ + ctlr->nildrive++; + if(ctlr->irqack != nil) + ctlr->irqack(ctlr); iunlock(ctlr); if((DEBUG & DbgINL) && ctlr->command != Cedd) print("Inil%2.2uX+", ctlr->command); @@ -1792,6 +1907,7 @@ atainterrupt(Ureg*, void* arg) case Crs: case Crsm: + case Ppio|Pin: if(!(status & Drq)){ drive->error = Abrt; break; @@ -1807,6 +1923,7 @@ atainterrupt(Ureg*, void* arg) case Cws: case Cwsm: + case Ppio|Pout: len = drive->block; if(drive->data+len > drive->limit) len = drive->limit-drive->data; @@ -1826,18 +1943,25 @@ atainterrupt(Ureg*, void* arg) break; case Cpkt: + case Ppkt|Pin: + case Ppkt|Pout: atapktinterrupt(drive); break; case Crd: case Cwd: + case Pdma|Pin: + case Pdma|Pout: atadmainterrupt(drive, drive->count*drive->secsize); break; - case Cstandby: + case Pnd: + case Preset: ctlr->done = 1; break; } + if(ctlr->irqack != nil) + ctlr->irqack(ctlr); iunlock(ctlr); if(drive->error){ @@ -1852,30 +1976,44 @@ atainterrupt(Ureg*, void* arg) } } +typedef struct Lchan Lchan; +struct Lchan { + int cmdport; + int ctlport; + int irq; + int probed; +}; +static Lchan lchan[2] = { + 0x1f0, 0x3f4, IrqATA0, 0, + 0x170, 0x374, IrqATA1, 0, +}; + +static int +badccru(Pcidev *p) +{ + switch(p->did<<16 | p->did){ + case 0x439c<<16 | 0x1002: + case 0x438c<<16 | 0x1002: +print("hi, anothy\n"); +print("%T: allowing bad ccru %.2ux for suspected ide controller\n", p->tbdf, p->ccru); + return 1; + default: + return 0; + } +} + static SDev* atapnp(void) { + char *s; + int channel, map, ispc87415, maxio, pi, r, span, tbdf; Ctlr *ctlr; Pcidev *p; - SDev *legacy[2], *sdev, *head, *tail; - int channel, ispc87415, maxio, pi, r, span; + SDev *sdev, *head, *tail; + void (*irqack)(Ctlr*); - legacy[0] = legacy[1] = head = tail = nil; - if(sdev = ataprobe(0x1F0, 0x3F4, IrqATA0)){ - head = tail = sdev; - legacy[0] = sdev; - } - if(sdev = ataprobe(0x170, 0x374, IrqATA1)){ - if(head != nil) - tail->next = sdev; - else - head = sdev; - tail = sdev; - legacy[1] = sdev; - } - - p = nil; - while(p = pcimatch(p, 0, 0)){ + head = tail = nil; + for(p = nil; p = pcimatch(p, 0, 0); ){ /* * Look for devices with the correct class and sub-class * code and known device and vendor ID; add native-mode @@ -1895,12 +2033,17 @@ atapnp(void) */ if(p->ccrb != 0x01) continue; + if(!badccru(p)) if(p->ccru != 0x01 && p->ccru != 0x04 && p->ccru != 0x80) continue; pi = p->ccrp; + map = 3; ispc87415 = 0; maxio = 0; + if(s = getconf("*idemaxio")) + maxio = atoi(s); span = BMspan; + irqack = nil; switch((p->did<<16)|p->vid){ default: @@ -1930,12 +2073,12 @@ atapnp(void) case (0x4D69<<16)|0x105A: /* Promise Ultra/133 TX2 */ case (0x3373<<16)|0x105A: /* Promise 20378 RAID */ case (0x3149<<16)|0x1106: /* VIA VT8237 SATA/RAID */ - case (0x3112<<16)|0x1095: /* SiI 3112 SATA/RAID */ + case (0x3112<<16)|0x1095: /* SiL 3112 SATA/RAID */ maxio = 15; span = 8*1024; /*FALLTHROUGH*/ + case (0x3114<<16)|0x1095: /* SiL 3114 SATA/RAID */ case (0x0680<<16)|0x1095: /* SiI 0680/680A PATA133 ATAPI/RAID */ - case (0x3114<<16)|0x1095: /* SiI 3114 SATA/RAID */ pi = 0x85; break; case (0x0004<<16)|0x1103: /* HighPoint HPT366 */ @@ -1970,15 +2113,6 @@ atapnp(void) r = pcicfgr8(p, 0x46); pcicfgw8(p, 0x46, (r & 0x0C)|0xF0); /*FALLTHROUGH*/ - case (0x7401<<16)|0x1022: /* AMD 755 Cobra */ - case (0x7409<<16)|0x1022: /* AMD 756 Viper */ - case (0x7410<<16)|0x1022: /* AMD 766 Viper Plus */ - case (0x7469<<16)|0x1022: /* AMD 3111 */ - /* - * This can probably be lumped in with the 768 above. - */ - /*FALLTHROUGH*/ - case (0x209A<<16)|0x1022: /* AMD CS5536 */ case (0x01BC<<16)|0x10DE: /* nVidia nForce1 */ case (0x0065<<16)|0x10DE: /* nVidia nForce2 */ case (0x0085<<16)|0x10DE: /* nVidia nForce2 MCP */ @@ -1991,8 +2125,10 @@ atapnp(void) case (0x0054<<16)|0x10DE: /* nVidia nForce4 SATA */ case (0x0055<<16)|0x10DE: /* nVidia nForce4 SATA */ case (0x0266<<16)|0x10DE: /* nVidia nForce4 430 SATA */ + case (0x0265<<16)|0x10DE: /* nVidia nForce 51 MCP */ case (0x0267<<16)|0x10DE: /* nVidia nForce 55 MCP SATA */ - case (0x03EC<<16)|0x10DE: /* nVidia nForce 61 MCP SATA */ + case (0x03ec<<16)|0x10DE: /* nVidia nForce 61 MCP SATA */ + case (0x03f6<<16)|0x10DE: /* nVidia nForce 61 MCP PATA */ case (0x0448<<16)|0x10DE: /* nVidia nForce 65 MCP SATA */ case (0x0560<<16)|0x10DE: /* nVidia nForce 69 MCP SATA */ /* @@ -2000,10 +2136,16 @@ atapnp(void) * address for the registers (0x50?). */ /*FALLTHROUGH*/ - case (0x4376<<16)|0x1002: /* ATI SB400 PATA */ - case (0x4379<<16)|0x1002: /* ATI SB400 SATA */ - case (0x437a<<16)|0x1002: /* ATI SB400 SATA */ - case (0x438c<<16)|0x1002: /* ATI SB600 PATA */ + case (0x209A<<16)|0x1022: /* AMD CS5536 */ + case (0x7401<<16)|0x1022: /* AMD 755 Cobra */ + case (0x7409<<16)|0x1022: /* AMD 756 Viper */ + case (0x7410<<16)|0x1022: /* AMD 766 Viper Plus */ + case (0x7469<<16)|0x1022: /* AMD 3111 */ + case (0x4376<<16)|0x1002: /* SB4xx pata */ + case (0x4379<<16)|0x1002: /* SB4xx sata */ + case (0x437a<<16)|0x1002: /* SB4xx sata ctlr #2 */ + case (0x437c<<16)|0x1002: /* Rx6xx pata */ + case (0x439c<<16)|0x1002: /* SB7xx pata */ break; case (0x0211<<16)|0x1166: /* ServerWorks IB6566 */ { @@ -2018,17 +2160,25 @@ atapnp(void) } span = 32*1024; break; - case (0x0502<<17)|0x100B: /* NS SC1100/SCx200 */ case (0x5229<<16)|0x10B9: /* ALi M1543 */ case (0x5288<<16)|0x10B9: /* ALi M5288 SATA */ + /*FALLTHROUGH*/ case (0x5513<<16)|0x1039: /* SiS 962 */ case (0x0646<<16)|0x1095: /* CMD 646 */ case (0x0571<<16)|0x1106: /* VIA 82C686 */ - case (0x9001<<16)|0x1106: /* VIA chipset in VIA PV530 */ - case (0x2363<<16)|0x197b: /* JMicron SATA */ + case (0x0502<<16)|0x100b: /* National Semiconductor SC1100/SCx200 */ + break; + case (0x2360<<16)|0x197b: /* jmicron jmb360 */ + case (0x2361<<16)|0x197b: /* jmicron jmb361 */ + case (0x2363<<16)|0x197b: /* jmicron jmb363 */ + case (0x2365<<16)|0x197b: /* jmicron jmb365 */ + case (0x2366<<16)|0x197b: /* jmicron jmb366 */ + case (0x2368<<16)|0x197b: /* jmicron jmb368 */ + break; case (0x1230<<16)|0x8086: /* 82371FB (PIIX) */ case (0x7010<<16)|0x8086: /* 82371SB (PIIX3) */ case (0x7111<<16)|0x8086: /* 82371[AE]B (PIIX4[E]) */ + break; case (0x2411<<16)|0x8086: /* 82801AA (ICH) */ case (0x2421<<16)|0x8086: /* 82801AB (ICH0) */ case (0x244A<<16)|0x8086: /* 82801BA (ICH2, Mobile) */ @@ -2037,59 +2187,96 @@ atapnp(void) case (0x248B<<16)|0x8086: /* 82801CA (ICH3, High-End) */ case (0x24CA<<16)|0x8086: /* 82801DBM (ICH4, Mobile) */ case (0x24CB<<16)|0x8086: /* 82801DB (ICH4, High-End) */ + case (0x24D1<<16)|0x8086: /* 82801er (ich5) */ case (0x24DB<<16)|0x8086: /* 82801EB (ICH5) */ + case (0x25A2<<16)|0x8086: /* 6300ESB pata */ case (0x25A3<<16)|0x8086: /* 6300ESB (E7210) */ - case (0x2653<<16)|0x8086: /* 82801FBM SATA */ case (0x266F<<16)|0x8086: /* 82801FB (ICH6) */ - case (0x27DF<<16)|0x8086: /* 82801G SATA (ICH7) */ - case (0x27C0<<16)|0x8086: /* 82801GB SATA AHCI (ICH7) */ -// case (0x27C4<<16)|0x8086: /* 82801GBM SATA (ICH7) */ + case (0x2653<<16)|0x8086: /* 82801FBM (ICH6, Mobile) */ + case (0x269e<<16)|0x8086: /* 63xxESB (intel 5000) */ + case (0x27DF<<16)|0x8086: /* 82801G PATA (ICH7) */ + case (0x27C0<<16)|0x8086: /* 82801GB SATA (ICH7) */ + case (0x27C4<<16)|0x8086: /* 82801GBM SATA (ICH7) */ case (0x27C5<<16)|0x8086: /* 82801GBM SATA AHCI (ICH7) */ case (0x2820<<16)|0x8086: /* 82801HB/HR/HH/HO SATA IDE */ - case (0x2850<<16)|0x8086: /* 82801HBM/HEM PATA */ case (0x2828<<16)|0x8086: /* 82801HBM SATA (ICH8-M) */ - case (0x2829<<16)|0x8086: /* 82801HBM SATA AHCI (ICH8-M) */ - case (0x2920<<16)|0x8086: /* 82801(IB)/IR/IH/IO SATA IDE (ICH9) */ - case (0x3a20<<16)|0x8086: /* 82801JI (ICH10) */ - case (0x3a26<<16)|0x8086: /* 82801JI (ICH10) */ + case (0x2920<<16)|0x8086: /* 82801(IB)/IR/IH/IO SATA (ICH9) port 0-3 */ + case (0x2921<<16)|0x8086: /* 82801(IB)/IR/IH/IO SATA (ICH9) port 0-1 */ + case (0x2926<<16)|0x8086: /* 82801(IB)/IR/IH/IO SATA (ICH9) port 4-5 */ + case (0x2928<<16)|0x8086: /* 82801(IB)/IR/IH/IO SATA (ICH9m) port 0-1 */ + case (0x2929<<16)|0x8086: /* 82801(IB)/IR/IH/IO SATA (ICH9m) port 0-1, 4-5 */ + case (0x292d<<16)|0x8086: /* 82801(IB)/IR/IH/IO SATA (ICH9m) port 4-5*/ + case (0x3a20<<16)|0x8086: /* 82801ji (ich10) */ + case (0x3a26<<16)|0x8086: /* 82801ji (ich10) */ + case (0x3b20<<16)|0x8086: /* 34x0 (pch) port 0-3 */ + case (0x3b21<<16)|0x8086: /* 34x0 (pch) port 4-5 */ + case (0x3b28<<16)|0x8086: /* 34x0pm (pch) port 0-1, 4-5 */ + case (0x3b2e<<16)|0x8086: /* 34x0pm (pch) port 0-3 */ + map = 0; + if(pcicfgr16(p, 0x40) & 0x8000) + map |= 1; + if(pcicfgr16(p, 0x42) & 0x8000) + map |= 2; + irqack = ichirqack; break; } - for(channel = 0; channel < 2; channel++){ - if(pi & (1<<(2*channel))){ + if((map & 1<mem[0+2*channel].bar & ~0x01, p->mem[1+2*channel].bar & ~0x01, - p->intl); - if(sdev == nil) - continue; - - ctlr = sdev->ctlr; - if(ispc87415) { - ctlr->ienable = pc87415ienable; - print("pc87415disable: not yet implemented\n"); - } - - if(head != nil) - tail->next = sdev; - else - head = sdev; - tail = sdev; - ctlr->tbdf = p->tbdf; + p->intl, 3); + tbdf = p->tbdf; + } + else if(lchan[channel].probed == 0){ + sdev = ataprobe(lchan[channel].cmdport, + lchan[channel].ctlport, lchan[channel].irq, 3); + lchan[channel].probed = 1; + tbdf = BUSUNKNOWN; } - else if((sdev = legacy[channel]) == nil) - continue; else - ctlr = sdev->ctlr; - + continue; + if(sdev == nil) + continue; + ctlr = sdev->ctlr; + if(ispc87415) { + ctlr->ienable = pc87415ienable; + print("pc87415disable: not yet implemented\n"); + } + ctlr->tbdf = tbdf; ctlr->pcidev = p; ctlr->maxio = maxio; ctlr->span = span; - if(!(pi & 0x80)) - continue; - ctlr->bmiba = (p->mem[4].bar & ~0x01) + channel*8; + ctlr->irqack = irqack; + if(pi & 0x80) + ctlr->bmiba = (p->mem[4].bar & ~0x01) + channel*8; + if(head != nil) + tail->next = sdev; + else + head = sdev; + tail = sdev; } } + if(lchan[0].probed + lchan[1].probed == 0) + for(channel = 0; channel < 2; channel++){ + sdev = nil; + if(lchan[channel].probed == 0){ + // print("sdide: blind probe %.3ux\n", lchan[channel].cmdport); + sdev = ataprobe(lchan[channel].cmdport, + lchan[channel].ctlport, lchan[channel].irq, 3); + lchan[channel].probed = 1; + } + if(sdev == nil) + continue; + if(head != nil) + tail->next = sdev; + else + head = sdev; + tail = sdev; + } + if(0){ int port; ISAConf isa; @@ -2119,7 +2306,7 @@ if(0){ port = isa.port+0x204; channel = pcmspecial("ATA/ATAPI", &isa); } - if(channel >= 0 && (sdev = ataprobe(isa.port, port, isa.irq)) != nil){ + if(channel >= 0 && (sdev = ataprobe(isa.port, port, isa.irq, 3)) != nil){ if(head != nil) tail->next = sdev; else @@ -2129,10 +2316,21 @@ if(0){ return head; } -static SDev* -atalegacy(int port, int irq) +static void +atadmaclr(Ctlr *ctlr) { - return ataprobe(port, port+0x204, irq); + int bmiba, bmisx; + + if(ctlr->curdrive) + ataabort(ctlr->curdrive, 1); + bmiba = ctlr->bmiba; + if(bmiba == 0) + return; + atadmastop(ctlr); + outl(bmiba+Bmidtpx, 0); + bmisx = inb(bmiba+Bmisx) & ~Bmidea; + outb(bmiba+Bmisx, bmisx|Ideints|Idedmae); +// pciintst(ctlr->pcidev); } static int @@ -2142,19 +2340,17 @@ ataenable(SDev* sdev) char name[32]; ctlr = sdev->ctlr; - if(ctlr->bmiba){ -#define ALIGN (4 * 1024) + atadmaclr(ctlr); if(ctlr->pcidev != nil) pcisetbme(ctlr->pcidev); - ctlr->prdt = mallocalign(Nprd*sizeof(Prd), 4, 0, 4*1024); + ctlr->prdt = mallocalign(Nprd*sizeof(Prd), 4, 0, 64*1024); } snprint(name, sizeof(name), "%s (%s)", sdev->name, sdev->ifc->name); intrenable(ctlr->irq, atainterrupt, ctlr, ctlr->tbdf, name); outb(ctlr->ctlport+Dc, 0); if(ctlr->ienable) ctlr->ienable(ctlr); - return 1; } @@ -2170,7 +2366,8 @@ atadisable(SDev *sdev) ctlr->idisable(ctlr); snprint(name, sizeof(name), "%s (%s)", sdev->name, sdev->ifc->name); intrdisable(ctlr->irq, atainterrupt, ctlr, ctlr->tbdf, name); - if (ctlr->bmiba) { + if(ctlr->bmiba) { +// atadmaclr(ctlr); if (ctlr->pcidev) pciclrbme(ctlr->pcidev); free(ctlr->prdt); @@ -2179,46 +2376,71 @@ atadisable(SDev *sdev) } static int -atarctl(SDunit* unit, char* p, int l) +ataonline(SDunit *unit) { - int n; Ctlr *ctlr; Drive *drive; if((ctlr = unit->dev->ctlr) == nil || ctlr->drive[unit->subno] == nil) return 0; drive = ctlr->drive[unit->subno]; - - qlock(drive); - n = snprint(p, l, "config %4.4uX capabilities %4.4uX", - drive->info[Iconfig], drive->info[Icapabilities]); - if(drive->dma) - n += snprint(p+n, l-n, " dma %8.8uX dmactl %8.8uX", - drive->dma, drive->dmactl); - if(drive->rwm) - n += snprint(p+n, l-n, " rwm %ud rwmctl %ud", - drive->rwm, drive->rwmctl); - if(drive->flags&Lba48) - n += snprint(p+n, l-n, " lba48always %s", - (drive->flags&Lba48always) ? "on" : "off"); - n += snprint(p+n, l-n, "\n"); - if(drive->sectors){ - n += snprint(p+n, l-n, "geometry %lld %d", - drive->sectors, drive->secsize); - if(drive->pkt == 0) - n += snprint(p+n, l-n, " %d %d %d", - drive->c, drive->h, drive->s); - n += snprint(p+n, l-n, "\n"); + if((drive->flags & Online) == 0){ + drive->flags |= Online; + atadrive(unit, drive, ctlr->cmdport, ctlr->ctlport, drive->dev); } + unit->sectors = drive->sectors; + unit->secsize = drive->secsize; + if(drive->feat & Datapi) + return scsionline(unit); + return 1; +} + +static int +atarctl(SDunit* unit, char* p, int l) +{ + Ctlr *ctlr; + Drive *drive; + char *e, *op; + + if((ctlr = unit->dev->ctlr) == nil || ctlr->drive[unit->subno] == nil) + return 0; + drive = ctlr->drive[unit->subno]; + + e = p+l; + op = p; + qlock(drive); + p = seprint(p, e, "config %4.4uX capabilities %4.4uX", drive->info[Iconfig], drive->info[Icapabilities]); + if(drive->dma) + p = seprint(p, e, " dma %8.8uX dmactl %8.8uX", drive->dma, drive->dmactl); + if(drive->rwm) + p = seprint(p, e, " rwm %ud rwmctl %ud", drive->rwm, drive->rwmctl); + if(drive->feat & Dllba) + p = seprint(p, e, " lba48always %s", (drive->flags&Lba48always) ? "on" : "off"); + p = seprint(p, e, "\n"); + p = seprint(p, e, "model %s\n", drive->model); + p = seprint(p, e, "serial %s\n", drive->serial); + p = seprint(p, e, "firm %s\n", drive->firmware); + p = seprint(p, e, "feat "); + p = pflag(p, e, drive); + if(drive->sectors){ + p = seprint(p, e, "geometry %llud %d", drive->sectors, drive->secsize); + if(drive->pkt == 0 && (drive->feat & Dlba) == 0) + p = seprint(p, e, " %d %d %d", drive->c, drive->h, drive->s); + p = seprint(p, e, "\n"); + } + p = seprint(p, e, "missirq %ud\n", drive->missirq); + p = seprint(p, e, "sloop %ud\n", drive->spurloop); + p = seprint(p, e, "irq %ud %ud\n", ctlr->nrq, drive->irq); + p = seprint(p, e, "bsy %ud %ud\n", ctlr->bsy, drive->bsy); + p = seprint(p, e, "nildrive %ud\n", ctlr->nildrive); qunlock(drive); - return n; + return p - op; } static int atawctl(SDunit* unit, Cmdbuf* cb) { - int period; Ctlr *ctlr; Drive *drive; @@ -2258,22 +2480,8 @@ atawctl(SDunit* unit, Cmdbuf* cb) else error(Ebadctl); } - else if(strcmp(cb->f[0], "standby") == 0){ - switch(cb->nf){ - default: - error(Ebadctl); - case 2: - period = strtol(cb->f[1], 0, 0); - if(period && (period < 30 || period > 240*5)) - error(Ebadctl); - period /= 5; - break; - } - if(atastandby(drive, period) != SDok) - error(Ebadctl); - } else if(strcmp(cb->f[0], "lba48always") == 0){ - if(cb->nf != 2 || !(drive->flags&Lba48)) + if(cb->nf != 2 || !(drive->feat & Dllba)) error(Ebadctl); if(strcmp(cb->f[1], "on") == 0) drive->flags |= Lba48always; @@ -2282,6 +2490,9 @@ atawctl(SDunit* unit, Cmdbuf* cb) else error(Ebadctl); } + else if(strcmp(cb->f[0], "identify") == 0){ + atadrive(unit, drive, ctlr->cmdport, ctlr->ctlport, drive->dev); + } else error(Ebadctl); qunlock(drive); @@ -2290,16 +2501,16 @@ atawctl(SDunit* unit, Cmdbuf* cb) return 0; } -SDifc sdataifc = { - "ata", /* name */ +SDifc sdideifc = { + "ide", /* name */ atapnp, /* pnp */ - atalegacy, /* legacy */ + nil, /* legacy */ ataenable, /* enable */ atadisable, /* disable */ scsiverify, /* verify */ - scsionline, /* online */ + ataonline, /* online */ atario, /* rio */ atarctl, /* rctl */ atawctl, /* wctl */ @@ -2309,4 +2520,5 @@ SDifc sdataifc = { ataclear, /* clear */ atastat, /* rtopctl */ nil, /* wtopctl */ + ataataio, }; diff --git a/sys/src/9/pc/sdmv50xx.c b/sys/src/9/pc/sdmv50xx.c index e9ba87c47..eb28df098 100644 --- a/sys/src/9/pc/sdmv50xx.c +++ b/sys/src/9/pc/sdmv50xx.c @@ -1,5 +1,5 @@ /* - * Marvell 88SX[56]0[48][01] fileserver Serial ATA (SATA) driver + * Marvell 88SX[56]0[48][01] Serial ATA (SATA) driver * * See MV-S101357-00 Rev B Marvell PCI/PCI-X to 8-Port/4-Port * SATA Host Controller, ATA-5 ANSI NCITS 340-2000. @@ -16,25 +16,18 @@ #include "fns.h" #include "io.h" #include "../port/error.h" +#include "../port/sd.h" +#include -#include "../port/sd.h" - -#define dprint if(!0){}else iprint -#define idprint if(!0){}else iprint -#define ioprint if(!0){}else iprint +#define dprint(...) // print(__VA_ARGS__) +#define idprint(...) print(__VA_ARGS__) +#define Ticks MACHP(0)->ticks enum { NCtlr = 4, - NCtlrdrv = 8, + NCtlrdrv = 8, NDrive = NCtlr*NCtlrdrv, - Read = 0, - Write, - - Coraiddebug = 0, -}; - -enum { SrbRing = 32, /* Addresses of ATA register */ @@ -48,62 +41,57 @@ enum { ARseccnt = 022, ARstat = 027, - ATAerr = (1<<0), - ATAdrq = (1<<3), - ATAdf = (1<<5), - ATAdrdy = (1<<6), - ATAbusy = (1<<7), - ATAabort = (1<<2), - ATAobs = (1<<1 | 1<<2 | 1<<4), - ATAeIEN = (1<<1), - ATAsrst = (1<<2), - ATAhob = (1<<7), - ATAbad = (ATAbusy|ATAdf|ATAdrq|ATAerr), + ATAerr = 1<<0, + ATAdrq = 1<<3, + ATAdf = 1<<5, + ATAdrdy = 1<<6, + ATAbusy = 1<<7, + ATAabort = 1<<2, + ATAobs = 1<<1 | 1<<2 | 1<<4, + ATAeIEN = 1<<1, + ATAbad = ATAbusy|ATAdf|ATAdrq|ATAerr, - SFdone = (1<<0), - SFerror = (1<<1), + SFdone = 1<<0, + SFerror = 1<<1, - SRBident = 0, - SRBread, - SRBwrite, - SRBsmart, - - SRBnodata = 0, - SRBdatain, - SRBdataout, - - RQread = 1, /* data coming IN from device */ - - PRDeot = (1<<15), + PRDeot = 1<<15, /* EDMA interrupt error cause register */ - - ePrtDataErr = (1<<0), - ePrtPRDErr = (1<<1), - eDevErr = (1<<2), - eDevDis = (1<<3), - eDevCon = (1<<4), - eOverrun = (1<<5), - eUnderrun = (1<<6), - eSelfDis = (1<<8), - ePrtCRQBErr = (1<<9), - ePrtCRPBErr = (1<<10), - ePrtIntErr = (1<<11), - eIORdyErr = (1<<12), + ePrtDataErr = 1<<0, + ePrtPRDErr = 1<<1, + eDevErr = 1<<2, + eDevDis = 1<<3, + eDevCon = 1<<4, + eOverrun = 1<<5, + eUnderrun = 1<<6, + eSelfDis = 1<<8, + ePrtCRQBErr = 1<<9, + ePrtCRPBErr = 1<<10, + ePrtIntErr = 1<<11, + eIORdyErr = 1<<12, /* flags for sata 2 version */ - eSelfDis2 = (1<<7), - SerrInt = (1<<5), + eSelfDis2 = 1<<7, + SerrInt = 1<<5, /* EDMA Command Register */ - - eEnEDMA = (1<<0), - eDsEDMA = (1<<1), - eAtaRst = (1<<2), + eEnEDMA = 1<<0, + eDsEDMA = 1<<1, + eAtaRst = 1<<2, /* Interrupt mask for errors we care about */ - IEM = (eDevDis | eDevCon | eSelfDis), - IEM2 = (eDevDis | eDevCon | eSelfDis2), + IEM = eDevDis | eDevCon | eSelfDis, + IEM2 = eDevDis | eDevCon | eSelfDis2, + + /* phyerrata magic */ + Mpreamp = 0x7e0, + Dpreamp = 0x720, + + REV60X1B2 = 0x7, + REV60X1C0 = 0x9, + + /* general mmio registers */ + Portswtch = 0x1d64/4, /* drive states */ Dnull = 0, @@ -114,33 +102,12 @@ enum { Dreset, Dlast, - /* drive flags */ - Dext = (1<<0), /* use ext commands */ - Dpio = (1<<1), /* doing pio */ - Dwanted = (1<<2), /* someone wants an srb entry */ - Dedma = (1<<3), /* device in edma mode */ - Dpiowant = (1<<4), /* some wants to use the pio mode */ - - /* phyerrata magic crap */ - Mpreamp = 0x7e0, - Dpreamp = 0x720, - - REV60X1B2 = 0x7, - REV60X1C0 = 0x9, - + /* sata mode */ + DMautoneg = 0, + DMsatai, + DMsataii, }; -static char* diskstates[Dlast] = { - "null", - "new", - "ready", - "error", - "missing", - "reset", -}; - -extern SDifc sdmv50xxifc; - typedef struct Arb Arb; typedef struct Bridge Bridge; typedef struct Chip Chip; @@ -162,12 +129,6 @@ struct Chip Edma *edma; }; -enum { - DMautoneg, - DMsatai, - DMsataii, -}; - struct Drive { Lock; @@ -175,25 +136,26 @@ struct Drive Ctlr *ctlr; SDunit *unit; char name[10]; - ulong magic; + Sfis; Bridge *bridge; Edma *edma; Chip *chip; int chipx; - int mediachange; + int drivechange; int state; - int flag; uvlong sectors; + uint secsize; ulong pm2; /* phymode 2 init state */ - ulong intick; /* check for hung western digital drives. */ + ulong intick; /* check for hung drives. */ int wait; int mode; /* DMautoneg, satai or sataii. */ char serial[20+1]; char firmware[8+1]; char model[40+1]; + uvlong wwn; ushort info[256]; @@ -205,7 +167,7 @@ struct Drive Srb *srbhead; Srb *srbtail; - int driveno; /* ctlr*NCtlrdrv + unit */ + int driveno; /* ctlr*NCtlrdrv + unit */ }; struct Ctlr @@ -252,7 +214,7 @@ struct Srb /* request buffer */ /* * Memory-mapped I/O registers in many forms. */ -struct Bridge /* memory-mapped per-Drive registers */ +struct Bridge /* memory-mapped per-drive registers */ { ulong status; ulong serror; @@ -268,9 +230,9 @@ struct Bridge /* memory-mapped per-Drive registers */ char fill2[0x34]; ulong phymode; char fill3[0x88]; -}; /* length must be 0x100 */ +}; /* must be 0x100 hex in length */ -struct Arb /* memory-mapped per-Chip registers */ +struct Arb /* memory-mapped per-chip registers */ { ulong config; /* satahc configuration register (sata2 only) */ ulong rqop; /* request queue out-pointer */ @@ -285,7 +247,7 @@ struct Arb /* memory-mapped per-Chip registers */ Bridge bridge[4]; }; -struct Edma /* memory-mapped per-Drive DMA-related registers */ +struct Edma /* memory-mapped per-drive DMA-related registers */ { ulong config; /* configuration register */ ulong timer; @@ -357,56 +319,28 @@ struct Rx /* command response block */ ulong ts; /* time stamp */ }; -static Drive *mvsatadrive[NDrive]; -static int nmvsatadrive; +static Ctlr *mvsatactlr[NCtlr]; +static Drive *mvsatadrive[NDrive]; +static int nmvsatadrive; +static char *diskstates[Dlast] = { + "null", + "new", + "ready", + "error", + "missing", + "reset", +}; -/* - * Little-endian parsing for drive data. - */ -static ushort -lhgets(void *p) -{ - uchar *a = p; - return ((ushort) a[1] << 8) | a[0]; -} - -static ulong -lhgetl(void *p) -{ - uchar *a = p; - return ((ulong) lhgets(a+2) << 16) | lhgets(a); -} - -static uvlong -lhgetv(void *p) -{ - uchar *a = p; - return ((uvlong) lhgetl(a+4) << 32) | lhgetl(a); -} - -static void -idmove(char *p, ushort *a, int n) -{ - char *op; - int i; - - op = p; - for(i=0; i>8; - *p++ = a[i]; - } - while(p>op && *--p == ' ') - *p = 0; -} +extern SDifc sdmv50xxifc; /* * Request buffers. */ -struct +static struct { Lock; - Srb *freechain; - int nalloc; + Srb *freechain; + int nalloc; } srblist; static Srb* @@ -435,9 +369,6 @@ freesrb(Srb *p) iunlock(&srblist); } -/* - * Wait for a byte to be a particular value. - */ static int satawait(uchar *p, uchar mask, uchar v, int ms) { @@ -448,32 +379,27 @@ satawait(uchar *p, uchar mask, uchar v, int ms) return (*p & mask) == v; } -/* - * Drive initialization - */ /* unmask in the pci registers err done */ static void -unmask(ulong *mmio, int port, int coal) +portswitch(ulong *mmio, int port, uint coal, uint on) { - port &= 7; - if(coal) - coal = 1; - if (port < 4) - mmio[0x1d64/4] |= (3 << (((port&3)*2)) | (coal<<8)); + ulong m; + + m = 3<<(port&3)*2 | coal<<8; + if((port&7) >= 4) + m <<= 9; + if(on) + mmio[Portswtch] |= m; else - mmio[0x1d64/4] |= (3 << (((port&3)*2+9)) | (coal<<17)); + mmio[Portswtch] &= m; } -static void -mask(ulong *mmio, int port, int coal) +static char* +dnam(Drive *d) { - port &= 7; - if(coal) - coal = 1; - if (port < 4) - mmio[0x1d64/4] &= ~(3 << (((port&3)*2)) | (coal<<8)); - else - mmio[0x1d64/4] &= ~(3 << (((port&3)*2+9)) | (coal<<17)); + if(d->unit) + return d->unit->name; + return d->name; } /* I give up, marvell. You win. */ @@ -483,17 +409,22 @@ phyerrata(Drive *d) ulong n, m; enum { BadAutoCal = 0xf << 26, }; - if (d->ctlr->type == 1) + if(d->ctlr->type == 1){ + /* set phyctrl bits [0:1] to 01 per MV-S102013-00 Rev C. */ + n = d->bridge->phyctrl; + n &= ~3; + d->bridge->phyctrl = n | 1; return; + } microdelay(200); n = d->bridge->phymode2; while ((n & BadAutoCal) == BadAutoCal) { - dprint("%s: badautocal\n", d->unit->name); + dprint("%s: badautocal\n", dnam(d)); n &= ~(1<<16); - n |= (1<<31); + n |= 1<<31; d->bridge->phymode2 = n; microdelay(200); - d->bridge->phymode2 &= ~((1<<16) | (1<<31)); + d->bridge->phymode2 &= ~(1<<16 | 1<<31); microdelay(200); n = d->bridge->phymode2; } @@ -552,6 +483,28 @@ edmacleanout(Drive *d) } } +static int +edmadisable(Drive *d, int reset) +{ + Edma *e; + + e = d->edma; + if(!reset && (e->ctl & eEnEDMA) == 0) + return 0; + e->ctl = eDsEDMA; + microdelay(1); + if(reset) + e->ctl = eAtaRst; + microdelay(25); + e->ctl = 0; + if (satawait((uchar *)&e->ctl, eEnEDMA, 0, 3*1000) == 0){ + print("%s: eEnEDMA never cleared on reset\n", dnam(d)); + return -1; + } + edmacleanout(d); + return 0; +} + static void resetdisk(Drive *d) { @@ -571,16 +524,10 @@ resetdisk(Drive *d) n = d->edma->sataconfig; /* flush */ USED(n); } - d->edma->ctl = eDsEDMA; - microdelay(1); - d->edma->ctl = eAtaRst; - microdelay(25); - d->edma->ctl = 0; - if (satawait((uchar *)&d->edma->ctl, eEnEDMA, 0, 3*1000) == 0) - print("%s: eEnEDMA never cleared on reset\n", d->unit->name); - edmacleanout(d); + if(edmadisable(d, 1) == -1){ + } phyerrata(d); - d->bridge->sctrl = 0x301 | (d->mode << 4); + d->bridge->sctrl = 0x301 | d->mode<<4; d->state = Dmissing; } @@ -596,7 +543,7 @@ edmainit(Drive *d) d->rx = xspanalloc(32*sizeof(Rx), 256, 0); d->prd = xspanalloc(32*sizeof(Prd), 32, 0); for(i = 0; i < 32; i++) - d->tx[i].prdpa = PADDR(&d->prd[i]); + d->tx[i].prdpa = PCIWADDR(&d->prd[i]); coherence(); } @@ -604,48 +551,31 @@ static int configdrive(Ctlr *ctlr, Drive *d, SDunit *unit) { dprint("%s: configdrive\n", unit->name); - if(d->driveno < 0) - panic("mv50xx: configdrive: unset driveno\n"); d->unit = unit; - edmainit(d); - d->mode = DMsatai; - if(d->ctlr->type == 1){ - d->edma->iem = IEM; - d->bridge = &d->chip->arb->bridge[d->chipx]; - }else{ - d->edma->iem = IEM2; - d->bridge = &d->chip->edma[d->chipx].port; - d->edma->iem = ~(1<<6); - d->pm2 = Dpreamp; - if(d->ctlr->lmmio[0x180d8/4] & 1) - d->pm2 = d->bridge->phymode2 & Mpreamp; - } resetdisk(d); - unmask(ctlr->lmmio, d->driveno, 0); + portswitch(ctlr->lmmio, d->driveno, 0, 1); delay(100); if(d->bridge->status){ - dprint("%s: configdrive: found drive %lx\n", unit->name, d->bridge->status); + dprint("%s: configdrive: found drive %lux\n", unit->name, d->bridge->status); return 0; } return -1; } static int -enabledrive(Drive *d) +edmaenable(Drive *d) { Edma *edma; - dprint("%s: enabledrive..", d->unit->name); + dprint("%s: enabledrive..", dnam(d)); if((d->bridge->status & 0xf) != 3){ - dprint("%s: not present\n", d->unit->name); - d->state = Dmissing; + dprint("%s: not present\n", dnam(d)); return -1; } edma = d->edma; if(satawait(&edma->cmdstat, ATAbusy, 0, 5*1000) == 0){ - dprint("%s: busy timeout\n", d->unit->name); - d->state = Dmissing; + dprint("%s: busy timeout\n", dnam(d)); return -1; } edma->iec = 0; @@ -653,40 +583,40 @@ enabledrive(Drive *d) edma->config = 0x51f; if (d->ctlr->type == 2) edma->config |= 7<<11; - edma->txi = PADDR(d->tx); + edma->txi = PCIWADDR(d->tx); edma->txo = (ulong)d->tx & 0x3e0; edma->rxi = (ulong)d->rx & 0xf8; - edma->rxo = PADDR(d->rx); + edma->rxo = PCIWADDR(d->rx); edma->ctl |= 1; /* enable dma */ - - if(d->bridge->status = 0x113){ - dprint("%s: new\n", d->unit->name); - d->state = Dnew; - }else - print("%s: status not forced (should be okay)\n", d->unit->name); return 0; } +static int +enabledrive(Drive *d) +{ + dprint("%s: enabledrive..", dnam(d)); + if(edmaenable(d) == 0){ + switch(d->bridge->status){ + case 0x113: + case 0x123: + d->state = Dnew; + break; + } + return 0; + } + print("mv50: enable reset\n"); + d->state = Dreset; + return -1; +} + static void disabledrive(Drive *d) { - int i; - ulong *r; - - dprint("%s: disabledrive\n", d->unit->name); - if(d->tx == nil) /* never enabled */ return; - d->edma->ctl = 0; d->edma->iem = 0; - - r = (ulong*)(d->ctlr->mmio + 0x1d64); - i = d->chipx; - if(d->chipx < 4) - *r &= ~(3 << (i*2)); - else - *r |= ~(3 << (i*2+9)); + portswitch(d->ctlr->lmmio, d->driveno, 0, 0); } static int @@ -694,15 +624,14 @@ setudmamode(Drive *d, uchar mode) { Edma *edma; - dprint("%s: setudmamode %d\n", d->unit->name, mode); - + dprint("%s: setudmamode %d\n", dnam(d), mode); edma = d->edma; - if (edma == nil) { + if(edma == nil) { iprint("setudamode(m%d): zero d->edma\m", d->driveno); return 0; } - if(satawait(&edma->cmdstat, ~ATAobs, ATAdrdy, 9*1000) == 0){ - iprint("%s: cmdstat 0x%.2ux ready timeout\n", d->unit->name, edma->cmdstat); + if(satawait(&edma->cmdstat, ~ATAobs, ATAdrdy, 250) == 0){ + iprint("%s: cmdstat 0x%.2ux ready timeout\n", dnam(d), edma->cmdstat); return 0; } edma->altstat = ATAeIEN; @@ -711,7 +640,7 @@ setudmamode(Drive *d, uchar mode) edma->cmdstat = 0xef; microdelay(1); if(satawait(&edma->cmdstat, ATAbusy, 0, 5*1000) == 0){ - iprint("%s: cmdstat 0x%.2ux busy timeout\n", d->unit->name, edma->cmdstat); + iprint("%s: cmdstat 0x%.2ux busy timeout\n", dnam(d), edma->cmdstat); return 0; } return 1; @@ -720,14 +649,15 @@ setudmamode(Drive *d, uchar mode) static int identifydrive(Drive *d) { + char *s; int i; ushort *id; Edma *edma; - SDunit *unit; + SDunit *u; - dprint("%s: identifydrive\n", d->unit->name); - - if(setudmamode(d, 5) == 0) /* do all SATA support 5? */ + dprint("%s: identifydrive\n", dnam(d)); + setfissig(d, 0); /* BOTCH; need to find and set signature */ + if(setudmamode(d, 5) == 0) /* BOTCH; run after identify */ goto Error; id = d->info; @@ -745,29 +675,27 @@ identifydrive(Drive *d) id[i] = edma->pio; if(edma->cmdstat & ATAbad) goto Error; - i = lhgets(id+83) | lhgets(id+86); - if(i & (1<<10)){ - d->flag |= Dext; - d->sectors = lhgetv(id+100); - }else{ - d->flag &= ~Dext; - d->sectors = lhgetl(id+60); - } + d->sectors = idfeat(d, id); + d->secsize = idss(d, id); idmove(d->serial, id+10, 20); idmove(d->firmware, id+23, 8); idmove(d->model, id+27, 40); + d->wwn = idwwn(d, id); - unit = d->unit; - memset(unit->inquiry, 0, sizeof unit->inquiry); - unit->inquiry[2] = 2; - unit->inquiry[3] = 2; - unit->inquiry[4] = sizeof(unit->inquiry)-4; - idmove((char*)unit->inquiry+8, id+27, 40); + u = d->unit; + memset(u->inquiry, 0, sizeof u->inquiry); + u->inquiry[2] = 2; + u->inquiry[3] = 2; + u->inquiry[4] = sizeof u->inquiry - 4; + idmove((char*)u->inquiry+8, id+27, 40); if(enabledrive(d) == 0) { d->state = Dready; - d->mediachange = 1; - idprint("%s: LLBA %lld sectors\n", d->unit->name, d->sectors); + d->drivechange = 1; + s = nil; + if(d->feat & Dllba) + s = "L"; + idprint("%s: %sLBA %llud sectors\n", dnam(d), s, d->sectors); } else d->state = Derror; if(d->state == Dready) @@ -779,20 +707,21 @@ Error: return -1; } -/* p. 163: - M recovered error - P protocol error - N PhyRdy change - W CommWake - B 8-to-10 encoding error - D disparity error - C crc error - H handshake error - S link sequence error - T transport state transition error - F unrecognized fis type - X device changed -*/ +/* + * p. 163: + * M recovered error + * P protocol error + * N PhyRdy change + * W CommWake + * B 8-to-10 encoding error + * D disparity error + * C crc error + * H handshake error + * S link sequence error + * T transport state transition error + * F unrecognized fis type + * X device changed + */ static char stab[] = { [1] 'M', @@ -800,7 +729,7 @@ static char stab[] = { [16] 'N', [18] 'W', 'B', 'D', 'C', 'H', 'S', 'T', 'F', 'X' }; -static ulong sbad = (7<<20)|(3<<23); +static ulong sbad = 7<<20 | 3<<23; static void serrdecode(ulong r, char *s, char *e) @@ -808,17 +737,16 @@ serrdecode(ulong r, char *s, char *e) int i; e -= 3; - for(i = 0; i < nelem(stab) && s < e; i++){ - if((r&(1<edma; - if((edma->ctl&eEnEDMA) == 0){ + if((edma->ctl & eEnEDMA) == 0){ /* FEr SATA#4 40xx */ x = d->edma->cmdstat; USED(x); @@ -866,24 +796,46 @@ updatedrive(Drive *d) cause = edma->iec; if(cause == 0) return; - dprint("%s: cause %08ulx [%s]\n", d->unit->name, cause, iecdecode(cause)); + dprint("%s: cause %.8lux [%s]\n", dnam(d), cause, iecdecode(cause)); if(cause & eDevCon) d->state = Dnew; - if(cause&eDevDis && d->state == Dready) - iprint("%s: pulled: st=%08ulx\n", d->unit->name, cause); + if(cause & eDevDis && d->state == Dready) + iprint("%s: pulled: st=%.8lux\n", dnam(d), cause); switch(d->ctlr->type){ case 1: - if(cause&eSelfDis) - d->state = Derror; + if(cause & eUnderrun){ + /* FEr SATA#5 50xx for revs A0, B0 */ + if(d->ctlr->rid < 2) + d->state = Dreset; + else{ + d->state = Derror; + dprint("%s: underrun\n", dnam(d)); + } + } + if(cause & (eDevErr | eSelfDis)){ + /* + * FEr SATA#7 60xx for refs A0, B0 + * check for IRC error. we only check the + * ABORT flag as we don't get the upper nibble + */ + if(d->ctlr->rid < 2) + if(edma->altstat & ATAerr && edma->err & ATAabort) + d->state = Dreset; + else + d->state = Derror; + } + if(cause & Cerror1) + d->state = Dreset; break; case 2: - if(cause&Cerror) - d->state = Derror; - if(cause&SerrInt){ + if(cause & Cerror2) + d->state = Dreset; + if(cause & SerrInt){ serrdecode(d->bridge->serror, buf, buf+sizeof buf); - dprint("%s: serror %08ulx [%s]\n", d->unit->name, (ulong)d->bridge->serror, buf); - d->bridge->serror = d->bridge->serror; + dprint("%s: serror %.8lux [%s]\n", dnam(d), d->bridge->serror, buf); + d->bridge->serror = ~0; /*d->bridge->serror;*/ } + break; } edma->iec = ~cause; } @@ -892,45 +844,35 @@ updatedrive(Drive *d) * Requests */ static Srb* -srbrw(int req, Drive *d, uchar *data, uint sectors, uvlong lba) +srbrw(int rw, Drive *d, uchar *data, uint sectors, uvlong lba) { int i; Srb *srb; static uchar cmd[2][2] = { 0xC8, 0x25, 0xCA, 0x35 }; srb = allocsrb(); - srb->req = req; + srb->req = rw; srb->drive = d; srb->blockno = lba; srb->sectors = sectors; - srb->count = sectors*512; + srb->count = sectors * d->secsize; srb->flag = 0; srb->data = data; for(i=0; i<6; i++) - srb->lba[i] = lba >> (8*i); - srb->cmd = cmd[srb->req!=SRBread][(d->flag&Dext)!=0]; + srb->lba[i] = lba >> 8*i; + srb->cmd = cmd[srb->req!=SDread][(d->feat&Dllba)!=0]; return srb; } -static uintptr -advance(uintptr pa, int shift) -{ - int n, mask; - - mask = 0x1F<sectors); *cmd++ = CMD(ARfea, 0); - if(ext){ + if(llba){ *cmd++ = CMD(ARlba0, srb->lba[3]); *cmd++ = CMD(ARlba0, srb->lba[0]); *cmd++ = CMD(ARlba1, srb->lba[4]); @@ -944,7 +886,17 @@ mvsatarequest(ushort *cmd, Srb *srb, int ext) *cmd++ = CMD(ARlba2, srb->lba[2]); *cmd++ = CMD(ARdev, srb->lba[3] | 0xe0); } - *cmd = CMD(ARcmd, srb->cmd) | (1<<15); + *cmd = CMD(ARcmd, srb->cmd) | 1<<15; +} + +static uintptr +advance(uintptr pa, int shift) +{ + int n, mask; + + mask = 0x1F<srb)) panic("sdmv50xx: no free srbs"); - d->intick = MACHP(0)->ticks; + d->intick = Ticks; d->srb[i] = srb; edma = d->edma; tx = (Tx*)KADDR(edma->txi); - tx->flag = (i<<1) | (srb->req == SRBread); + tx->flag = i<<1 | (srb->req == SDread); prd = KADDR(tx->prdpa); - prd->pa = PADDR(srb->data); + prd->pa = PCIWADDR(srb->data); prd->count = srb->count; prd->flag = PRDeot; - mvsatarequest(tx->regs, srb, d->flag&Dext); + mvsatarequest(tx->regs, srb, d->feat&Dllba); coherence(); edma->txi = advance(edma->txi, 5); - d->intick = MACHP(0)->ticks; + d->intick = Ticks; } enum{ @@ -1036,27 +988,37 @@ srbdone(void *v) * Interrupts */ static void -mv50interrupt(Ureg*, void *a) +mv50interrupt(Ureg*, void *v) { int i; - ulong cause; + ulong cause, tk0, m; + Arb *a; Ctlr *ctlr; Drive *drive; + static uint st; - ctlr = a; + ctlr = v; ilock(ctlr); cause = ctlr->lmmio[0x1d60/4]; -// dprint("sd%c: mv50interrupt: 0x%lux\n", ctlr->sdev->idno, cause); - for(i=0; indrive; i++) +// dprint("sd%c: mv50interrupt: %.8lux\n", ctlr->sdev->idno, cause); + for(i=0; cause && indrive; i++) if(cause & (3<<(i*2+i/4))){ drive = &ctlr->drive[i]; if(drive->edma == 0) continue; /* not ready yet. */ ilock(drive); updatedrive(drive); - while(ctlr->chip[i/4].arb->ic & (0x0101 << (i%4))){ - ctlr->chip[i/4].arb->ic = ~(0x101 << (i%4)); + tk0 = Ticks; + a = ctlr->chip[i/4].arb; + m = 0x0101 << i%4; + while(a->ic & m){ + a->ic = ~m; completesrb(drive); + if(TK2MS(Ticks - tk0) > 3000){ + print("%s: irq wedge\n", dnam(drive)); + drive->state = Dreset; + break; + } } iunlock(drive); } @@ -1065,20 +1027,20 @@ mv50interrupt(Ureg*, void *a) enum{ Nms = 256, - Midwait = 16*1024/Nms-1, - Mphywait = 512/Nms-1, + Midwait = 16*1024/Nms - 1, + Mphywait = 512/Nms - 1, }; static void -westerndigitalhung(Drive *d) +hangck(Drive *d) { Edma *e; e = d->edma; - if(d->srb - && TK2MS(MACHP(0)->ticks-d->intick) > 5*1000 + if(d->nsrb > 0 + && TK2MS(Ticks - d->intick) > 5*1000 && (e->rxo&Rpidx) == (e->rxi&Rpidx)){ - dprint("westerndigital drive hung; resetting\n"); + print("%s: drive hung; resetting\n", dnam(d)); d->state = Dreset; } } @@ -1087,16 +1049,14 @@ static void checkdrive(Drive *d, int i) { static ulong s, olds[NCtlr*NCtlrdrv]; - char *name; ilock(d); - name = d->unit->name; s = d->bridge->status; if(s != olds[i]){ - dprint("%s: status: %08lx -> %08lx: %s\n", name, olds[i], s, diskstates[d->state]); + dprint("%s: status: %.8lux -> %.8lux: %s\n", dnam(d), olds[i], s, diskstates[d->state]); olds[i] = s; } - /* westerndigitalhung(d); */ + hangck(d); switch(d->state){ case Dnew: case Dmissing: @@ -1104,12 +1064,12 @@ checkdrive(Drive *d, int i) case 0x000: break; default: - dprint("%s: unknown state %8lx\n", name, s); + dprint("%s: unknown state %.8lux\n", dnam(d), s); case 0x100: if(++d->wait&Mphywait) break; reset: d->mode ^= 1; - dprint("%s: reset; new mode %d\n", name, d->mode); + dprint("%s: reset; new mode %d\n", dnam(d), d->mode); resetdisk(d); break; case 0x123: @@ -1125,10 +1085,10 @@ checkdrive(Drive *d, int i) case Dready: if(s != 0) break; - iprint("%s: pulled: st=%08ulx\n", name, s); /* never happens */ + iprint("%s: pulled: st=%.8lux\n", dnam(d), s); /* never happens */ case Dreset: case Derror: - dprint("%s reset: mode %d\n", name, d->mode); + dprint("%s reset: mode %d\n", dnam(d), d->mode); resetdisk(d); break; } @@ -1140,9 +1100,6 @@ satakproc(void*) { int i; - while(waserror()) - ; - for(;;){ tsleep(&up->sleep, return0, 0, Nms); for(i = 0; i < nmvsatadrive; i++) @@ -1150,9 +1107,24 @@ satakproc(void*) } } -/* - * Device discovery - */ +static void +initdrive(Drive *d) +{ + edmainit(d); + d->mode = DMsatai; + if(d->ctlr->type == 1){ + d->edma->iem = IEM; + d->bridge = &d->chip->arb->bridge[d->chipx]; + }else{ + d->edma->iem = IEM2; + d->bridge = &d->chip->edma[d->chipx].port; +// d->edma->iem = ~(1<<6); + d->pm2 = Dpreamp; + if(d->ctlr->lmmio[0x180d8/4] & 1) + d->pm2 = d->bridge->phymode2 & Mpreamp; + } +} + static SDev* mv50pnp(void) { @@ -1160,12 +1132,11 @@ mv50pnp(void) uchar *base; ulong io, n, *mem; Ctlr *ctlr; + Drive *d; Pcidev *p; SDev *head, *tail, *sdev; - Drive *d; static int ctlrno, done; - dprint("mv50pnp\n"); if(done++) return nil; @@ -1173,6 +1144,8 @@ mv50pnp(void) head = nil; tail = nil; while((p = pcimatch(p, 0x11ab, 0)) != nil){ + if(p->ccrb != Pcibcstore || p->ccru + p->ccrp || p->did&0x0f00) + continue; switch(p->did){ case 0x5040: case 0x5041: @@ -1190,19 +1163,16 @@ mv50pnp(void) break; } nunit = (p->did&0xf0) >> 4; - print("Marvell 88SX%ux: %d SATA-%s ports with%s flash\n", - (ushort)p->did, nunit, + print("#S/sd%c: Marvell 88sx%ux: %d sata-%s ports with%s flash\n", + 'E' + ctlrno, (ushort)p->did, nunit, ((p->did&0xf000)==0x6000? "II": "I"), (p->did&1? "": "out")); - if((sdev = malloc(sizeof(SDev))) == nil) + if((sdev = malloc(sizeof *sdev)) == nil) continue; - if((ctlr = malloc(sizeof(Ctlr))) == nil){ + if((ctlr = malloc(sizeof *ctlr)) == nil){ free(sdev); continue; } - memset(sdev, 0, sizeof *sdev); - memset(ctlr, 0, sizeof *ctlr); - io = p->mem[0].bar & ~0x0F; mem = (ulong*)vmap(io, p->mem[0].size); if(mem == 0){ @@ -1242,6 +1212,7 @@ mv50pnp(void) } for (i = 0; i < nunit; i++) { d = &ctlr->drive[i]; + snprint(d->name, sizeof d->name, "mv50%d.%d", ctlrno, i); d->sectors = 0; d->ctlr = ctlr; d->driveno = ctlrno*NCtlrdrv + i; @@ -1249,7 +1220,9 @@ mv50pnp(void) d->chip = &ctlr->chip[i/4]; d->edma = &d->chip->edma[d->chipx]; mvsatadrive[d->driveno] = d; + initdrive(d); } + mvsatactlr[ctlrno] = ctlr; nmvsatadrive += nunit; ctlrno++; if(head) @@ -1261,10 +1234,6 @@ mv50pnp(void) return head; } -/* - * Enable the controller. Each disk has its own interrupt mask, - * and those get enabled as the disks are brought online. - */ static int mv50enable(SDev *sdev) { @@ -1276,15 +1245,13 @@ mv50enable(SDev *sdev) ctlr = sdev->ctlr; if (ctlr->enabled) return 1; + ctlr->enabled = 1; + kproc("mvsata", satakproc, 0); snprint(name, sizeof name, "%s (%s)", sdev->name, sdev->ifc->name); intrenable(ctlr->irq, mv50interrupt, ctlr, ctlr->tbdf, name); - ctlr->enabled = 1; return 1; } -/* - * Disable the controller. - */ static int mv50disable(SDev *sdev) { @@ -1309,30 +1276,6 @@ mv50disable(SDev *sdev) return 0; } -/* - * Clean up all disk structures. Already disabled. - * Could keep count of number of allocated controllers - * and free the srblist when it drops to zero. - */ -static void -mv50clear(SDev *sdev) -{ - int i; - Ctlr *ctlr; - Drive *d; - - dprint("sd%c: clear\n", sdev->idno); - - ctlr = sdev->ctlr; - for(i=0; indrive; i++){ - d = &ctlr->drive[i]; - free(d->tx); - free(d->rx); - free(d->prd); - } - free(ctlr); -} - /* * Check that there is a disk or at least a hot swap bay in the drive. */ @@ -1354,20 +1297,19 @@ mv50verify(SDunit *unit) /* * If ctlr->type == 1, then the drives spin up whenever - * the controller feels like it; if ctlr->type != 1, then + * the controller feels like it; if ctlr->type == 2, then * they spin up as a result of configdrive. * - * If there is a drive in the slot, give it 1.5s to spin up + * If there is a drive in the slot, give it 1.4s to spin up * before returning. There is a noticeable drag on the * power supply when spinning up fifteen drives * all at once (like in the Coraid enclosures). */ - if(ctlr->type != 1 && i == 0){ + if(ctlr->type == 2 && i == 0) if(!waserror()){ - tsleep(&up->sleep, return0, 0, 1500); + tsleep(&up->sleep, return0, 0, 1400); poperror(); } - } return 1; } @@ -1382,9 +1324,6 @@ mv50online(SDunit *unit) int r, s0; static int once; - if(once++ == 0) - kproc("mvsata", satakproc, 0); - ctlr = unit->dev->ctlr; d = &ctlr->drive[unit->subno]; r = 0; @@ -1393,12 +1332,12 @@ mv50online(SDunit *unit) USED(s0); if(d->state == Dnew) identifydrive(d); - if(d->mediachange){ + if(d->drivechange){ idprint("%s: online: %s -> %s\n", unit->name, diskstates[s0], diskstates[d->state]); r = 2; unit->sectors = d->sectors; - unit->secsize = 512; - d->mediachange = 0; + unit->secsize = d->secsize; + d->drivechange = 0; } else if(d->state == Dready) r = 1; iunlock(d); @@ -1483,22 +1422,10 @@ rdregs(char *p, char *e, void *base, Regs *r, int n, char *prefix) { int i; - for(i = 0; i < n; i++) - p = seprint(p, e, "%s%s%-19s %.8lux\n", - prefix? prefix: "", prefix? ": ": "", - r[i].name, *(ulong *)((uchar*)base + r[i].offset)); - return p; -} - -static char* -rdinfo(char *p, char *e, ushort *info) -{ - int i; - - p = seprint(p, e, "info"); - for(i = 0; i < 256; i++) - p = seprint(p, e, "%s%.4ux%s", i%8 == 0? "\t": "", info[i], - i%8 == 7? "\n": ""); + for(i=0; imodel); p = seprint(p, e, "serial %s\n", drive->serial); p = seprint(p, e, "firmware %s\n", drive->firmware); + p = seprint(p, e, "wwn\t%llux\n", drive->wwn); + p = seprint(p, e, "flag\t"); + p = pflag(p, e, drive); }else p = seprint(p, e, "no disk present\n"); - p = seprint(p, e, "geometry %llud 512\n", drive->sectors); - p = rdinfo(p, e, drive->info); - - p = rdregs(p, e, drive->chip->arb, regsarb, nelem(regsarb), nil); + p = seprint(p, e, "geometry %llud %ud\n", drive->sectors, drive->secsize); p = rdregs(p, e, drive->bridge, regsbridge, nelem(regsbridge), nil); - p = rdregs(p, e, drive->edma, regsedma, nelem(regsedma), nil); - + if(0){ + p = rdregs(p, e, drive->chip->arb, regsarb, nelem(regsarb), nil); + p = rdregs(p, e, drive->bridge, regsbridge, nelem(regsbridge), nil); + p = rdregs(p, e, drive->edma, regsedma, nelem(regsedma), nil); + } return p-op; } @@ -1537,7 +1467,6 @@ mv50wctl(SDunit *unit, Cmdbuf *cb) Ctlr *ctlr; Drive *drive; - USED(unit); if(strcmp(cb->f[0], "reset") == 0){ ctlr = unit->dev->ctlr; drive = &ctlr->drive[unit->subno]; @@ -1550,34 +1479,6 @@ mv50wctl(SDunit *unit, Cmdbuf *cb) return -1; } -/* - * sd(3): ``Reading /dev/sdctl yields information about each controller, - * one line per controller.'' - */ -static char* -mv50rtopctl(SDev *sdev, char *p, char *e) -{ - char name[10]; - Ctlr *ctlr; - - ctlr = sdev->ctlr; - if(ctlr == nil) - return p; - - snprint(name, sizeof name, "sd%c", sdev->idno); - p = rdregs(p, e, ctlr->mmio, regsctlr, nelem(regsctlr), name); - if (Coraiddebug) { - /* info for first disk. BUG: this shouldn't be here. */ - p = rdregs(p, e, ctlr->chip[0].arb, - regsarb, nelem(regsarb), name); - p = rdregs(p, e, &ctlr->chip[0].arb->bridge[0], - regsbridge, nelem(regsbridge), name); - p = rdregs(p, e, &ctlr->chip[0].edma[0], - regsedma, nelem(regsedma), name); - } - return p; -} - static int waitready(Drive *d) { @@ -1589,9 +1490,9 @@ waitready(Drive *d) iunlock(d); if(s == 0) return SDeio; - if (d->state == Dready) + if(d->state == Dready) return SDok; - if ((i+1)%60 == 0){ + if((i+1)%60 == 0){ ilock(d); resetdisk(d); iunlock(d); @@ -1601,87 +1502,42 @@ waitready(Drive *d) poperror(); } } - print("%s: not responding after 2 minutes\n", d->unit->name); + print("%s: not responding; error\n", dnam(d)); return SDeio; } -static int -mv50rio(SDreq *r) +static long +mv50bio(SDunit *u, int /*lun*/, int write, void *a, long count, uvlong lba) { - int count, max, n, status, try, flag; - uchar *cmd, *data; - uvlong lba; + int n, try, flag; + uchar *data; Ctlr *ctlr; - Drive *drive; - SDunit *unit; + Drive *d; Srb *srb; - unit = r->unit; - ctlr = unit->dev->ctlr; - drive = &ctlr->drive[unit->subno]; - cmd = r->cmd; - - if((status = sdfakescsi(r, drive->info, sizeof drive->info)) != SDnostatus){ - /* XXX check for SDcheck here */ - r->status = status; - return status; - } - - switch(cmd[0]){ - case 0x28: /* read */ - case 0x2A: /* write */ - break; - default: - iprint("%s: bad cmd 0x%.2ux\n", drive->unit->name, cmd[0]); - r->status = SDcheck; - return SDcheck; - } - - lba = (cmd[2]<<24)|(cmd[3]<<16)|(cmd[4]<<8)|cmd[5]; - count = (cmd[7]<<8)|cmd[8]; - if(r->data == nil) - return SDok; - if(r->dlen < count*unit->secsize) - count = r->dlen/unit->secsize; - + ctlr = u->dev->ctlr; + d = ctlr->drive + u->subno; try = 0; + data = a; retry: - if(waitready(drive) != SDok) - return SDeio; - /* - * Could arrange here to have an Srb always outstanding: - * - * lsrb = nil; - * while(count > 0 || lsrb != nil){ - * srb = nil; - * if(count > 0){ - * srb = issue next srb; - * } - * if(lsrb){ - * sleep on lsrb and handle it - * } - * } - * - * On the disks I tried, this didn't help. If anything, - * it's a little slower. -rsc - */ - data = r->data; + if(waitready(d) != SDok) + return -1; while(count > 0){ /* * Max is 128 sectors (64kB) because prd->count is 16 bits. */ - max = 128; n = count; - if(n > max) - n = max; - if((drive->edma->ctl&eEnEDMA) == 0) + if(n > 128) + n = 128; + ilock(d); + if((d->edma->ctl&eEnEDMA) == 0 && edmaenable(d) == -1){ + iunlock(d); goto tryagain; - srb = srbrw(cmd[0]==0x28 ? SRBread : SRBwrite, drive, data, n, lba); - ilock(drive); - startsrb(drive, srb); - iunlock(drive); + } + srb = srbrw(write, d, data, n, lba); + startsrb(d, srb); + iunlock(d); - /* Don't let user interrupt DMA. */ while(waserror()) ; sleep(srb, srbdone, srb); @@ -1690,30 +1546,279 @@ retry: flag = srb->flag; freesrb(srb); if(flag == 0){ -tryagain: - if(++try == 10){ - print("%s: bad disk\n", drive->unit->name); - return SDeio; - } - dprint("%s: retry\n", drive->unit->name); - if(!waserror()){ - tsleep(&up->sleep, return0, 0, 1000); - poperror(); + tryagain: if(++try == 10){ + print("%s: bad disk\n", dnam(d)); + return -1; } + dprint("%s: retry\n", dnam(d)); goto retry; } if(flag & SFerror){ - print("%s: i/o error\n", drive->unit->name); - return SDeio; + print("%s: i/o error\n", dnam(d)); + return -1; } count -= n; lba += n; - data += n*unit->secsize; + data += n*u->secsize; } - r->rlen = data - (uchar*)r->data; + return data - (uchar*)a; +} + +static int +mv50rio(SDreq *r) +{ + int count, n, status, rw; + uvlong lba; + Ctlr *ctlr; + Drive *d; + SDunit *unit; + + unit = r->unit; + ctlr = unit->dev->ctlr; + d = &ctlr->drive[unit->subno]; + + if((status = sdfakescsi(r)) != SDnostatus) + return r->status = status; + if((status = sdfakescsirw(r, &lba, &count, &rw)) == SDcheck) + return status; + n = mv50bio(r->unit, r->lun, rw, r->data, count, lba); + if(n == -1) + return SDeio; + r->rlen = n; return SDok; } +static void +mkrfis(SDreq *r, Drive *d, Edma *e) +{ + uchar *u; + + u = r->cmd; + u[Ftype] = 0x34; + u[Fioport] = 0; + if((d->feat & Dllba) && (r->ataproto & P28) == 0){ + u[Frerror] = e->err; + u[Fsc8] = e->seccnt; + u[Fsc] = e->seccnt; + u[Flba24] = e->lba0; + u[Flba0] = e->lba0; + u[Flba32] = e->lba1; + u[Flba8] = e->lba1; + u[Flba40] = e->lba2; + u[Flba16] = e->lba2; + u[Fdev] = e->lba3; + u[Fstatus] = e->cmdstat; + }else{ + u[Frerror] = e->err; + u[Fsc] = e->seccnt; + u[Flba0] = e->lba0; + u[Flba8] = e->lba1; + u[Flba16] = e->lba2; + u[Fdev] = e->lba3; + u[Fstatus] = e->cmdstat; + } +} + +static int +piocmd(SDreq *r, Drive *d) +{ + uchar *p, *c; + int n, nsec, i, err; + Edma *e; + SDunit *u; + + u = r->unit; + + if(waitready(d) != SDok) + return SDeio; + nsec = 0; + if(u->secsize != 0) + nsec = r->dlen / u->secsize; + if(r->dlen < nsec*u->secsize) + nsec = r->dlen/u->secsize; + if(nsec > 256) + error("can't do more than 256 sectors"); + + ilock(d); + e = d->edma; + if(edmadisable(d, 0) == -1) { + iunlock(d); + error("can't disable edma"); + } + n = satawait(&e->cmdstat, ATAdrdy|ATAbusy, ATAdrdy, 3*1000); + if(n == 0) { +print("piocmd: notready %.2ux\n", e->cmdstat); + iunlock(d); + return sdsetsense(r, SDcheck, 4, 8, 0); + } + c = r->cmd; + if(r->ataproto & P28){ + e->altstat = ATAeIEN; + e->seccnt = c[Fsc]; + e->err = c[Ffeat]; + e->lba0 = c[Flba0]; + e->lba1 = c[Flba8]; + e->lba2 = c[Flba16]; + e->lba3 = c[Fdev]; + e->cmdstat = c[Fcmd]; + }else{ + e->altstat = ATAeIEN; + e->seccnt = c[Fsc8]; + e->seccnt = c[Fsc]; + e->err = c[Ffeat]; + e->lba0 = c[Flba24]; + e->lba0 = c[Flba0]; + e->lba1 = c[Flba32]; + e->lba1 = c[Flba8]; + e->lba1 = c[Flba40]; + e->lba2 = c[Flba16]; + e->lba3 = c[Fdev]; + e->cmdstat = c[Fcmd]; + } + err = 0; + + if((r->ataproto & Pdatam) == Pnd) + n = satawait(&e->cmdstat, ATAbusy, 0, 3*1000); + else + n = satawait(&e->cmdstat, ATAbusy|ATAdrq, ATAdrq, 3*1000); + if(n == 0 || e->cmdstat & ATAerr){ + err = 1; + goto lose; + } + p = r->data; + for(; nsec > 0; nsec--) + for (i = 0; i < u->secsize; i += 2) { + n = satawait(&e->cmdstat, ATAbusy|ATAdrq, ATAdrq, 300); + if (n == 0) { + d->state = Dreset; + err = 1; + goto lose; + } + if(r->ataproto & Pout){ + n = (ushort)p[i + 1] << 8; + e->pio = n | p[i]; + } else { + n = e->pio; + p[i] = n; + p[i + 1] = n >> 8; + } + microdelay(1); + } +lose: + if(nsec == 0) + r->rlen = r->dlen; + mkrfis(r, d, e); + iunlock(d); + if(err) + return sdsetsense(r, SDcheck, 4, 8, 0); + else + return sdsetsense(r, SDok, 0, 0, 0); +} + +/* + * hack to allow udma mode to be set or unset + * via direct ata command. it would be better + * to move the assumptions about dma mode out + * of some of the helper functions. + */ +static int +isudm(SDreq *r) +{ + uchar *c; + + c = r->cmd; + if(c[Fcmd] == 0xef && c[Ffeat] == 0x03){ + if(c[Fsc]&0x40) + return 1; + return -1; + } + return 0; +} +static int +fisreqchk(Sfis *f, SDreq *r) +{ + if((r->ataproto & Pprotom) == Ppkt) + return SDnostatus; + /* + * handle oob requests; + * restrict & sanitize commands + */ + if(r->clen != 16) + error(Eio); + if(r->cmd[0] == 0xf0){ + sigtofis(f, r->cmd); + r->status = SDok; + return SDok; + } + r->cmd[0] = 0x27; + r->cmd[1] = 0x80; + r->cmd[7] |= 0xa0; + return SDnostatus; +} + +static int +badf(SDreq *r, Drive*) +{ +print("badf %.2ux %2ux\n", r->cmd[2], r->ataproto); + return sdsetsense(r, SDcheck, 2, 24, 0); +} + +static int +ataio0(SDreq *r, Drive *d) +{ + int (*f)(SDreq*, Drive*); + + f = badf; + switch(r->ataproto & Pprotom){ + default: + break; + case Ppio: + case Pnd: + f = piocmd; + break; + } + return f(r, d); +} + +static int +mv50ata(SDreq *r) +{ + int status, udm; + Ctlr *c; + Drive *d; + SDunit *u; + + u = r->unit; + c = u->dev->ctlr; + d = c->drive + u->subno; + if((status = fisreqchk(d, r)) != SDnostatus) + return status; + udm = isudm(r); + USED(udm); /* botch */ + +// qlock(d); + if(waserror()){ +// qunlock(d); + nexterror(); + } +retry: + switch(status = ataio0(r, d)){ + default: + dprint("%s: status %d\n", dnam(d), status); + break; + case SDretry: + dprint("%s: retry\n", dnam(d)); + goto retry; + case SDok: + sdsetsense(r, SDok, 0, 0, 0); + break; + } + poperror(); +// qunlock(d); + return r->status = status; +} + + SDifc sdmv50xxifc = { "mv50xx", /* name */ @@ -1725,13 +1830,15 @@ SDifc sdmv50xxifc = { mv50verify, /* verify */ mv50online, /* online */ mv50rio, /* rio */ - mv50rctl, /* rctl */ + mv50rctl, /* rctl */ mv50wctl, /* wctl */ - scsibio, /* bio */ + mv50bio, /* bio */ nil, /* probe */ - mv50clear, /* clear */ - mv50rtopctl, /* rtopctl */ + nil, /* clear */ + nil, /* rtopctl */ + nil, + mv50ata, }; /* diff --git a/sys/src/9/pc/sdmylex.c b/sys/src/9/pc/sdmylex.c index 201cdaa08..75cf5ac5b 100644 --- a/sys/src/9/pc/sdmylex.c +++ b/sys/src/9/pc/sdmylex.c @@ -840,7 +840,6 @@ mylexprobe(int port, int irq) Ctlr *ctlr; uchar cmd[6], data[256]; int clen, dlen, timeo; - static int count; if(ioalloc(port, 0x3, 0, "mylex") < 0) return nil; @@ -894,18 +893,13 @@ buggery: if(issue(ctlr, cmd, clen, data, dlen)){ if(data[0] == 'E') ctlr->bus = 32; + print("mylex ctlr @ port 0x%ux: 32-bit ", ctlr->port); ctlr->wide = data[0x0D] & 0x01; - /* - * devsd doesn't pass us the `spec' argument, so - * we'll assume that sd0 goes to the first scsi host - * adapter found, etc. - */ - print("#S/sd%d: mylex SCSI: port 0x%ux: %d-bit, ", - count++, ctlr->port, ctlr->bus); if (ctlr->wide) - print("wide\n"); + print("wide "); else - print("narrow\n"); + print("narrow "); + print("SCSI host adapter\n"); } else{ /* @@ -1187,8 +1181,9 @@ mylex32enable(Ctlr* ctlr) cmd[1] = 1; if(!issue(ctlr, cmd, 2, 0, 0)) { ctlr->wide = 0; - print("mylex32enable: port 0x%ux: scsi wide-mode setup " - "failed on wide host adapter", ctlr->port); + print( +"mylex32enable: ctlr @ port 0x%ux: scsi wide-mode setup failed on wide host adapter", + ctlr->port); } } diff --git a/sys/src/9/pc/sdodin.c b/sys/src/9/pc/sdodin.c new file mode 100644 index 000000000..9b2f716bf --- /dev/null +++ b/sys/src/9/pc/sdodin.c @@ -0,0 +1,2841 @@ +/* + * marvell odin ii 88se64xx sata/sas controller + * copyright © 2009 erik quanstrom + * coraid, inc. + */ + +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" +#include "../port/sd.h" +#include +#include "../port/led.h" + +#define dprint(...) if(debug) print(__VA_ARGS__); else USED(debug) +#define idprint(...) if(idebug) print(__VA_ARGS__); else USED(idebug) +#define aprint(...) if(adebug) print(__VA_ARGS__); else USED(adebug) +#define Pciwaddrh(a) 0 +#define Pciw64(x) (uvlong)PCIWADDR(x) +#define Ticks MACHP(0)->ticks + +/* copied from sdiahci */ +enum { + Dnull = 0, + Dmissing = 1<<0, + Dnopower = 1<<1, + Dnew = 1<<2, + Dready = 1<<3, + Derror = 1<<4, + Dreset = 1<<5, + Doffline = 1<<6, + Dportreset = 1<<7, + Dlast = 9, +}; + +static char *diskstates[Dlast] = { + "null", + "missing", + "nopower", + "new", + "ready", + "error", + "reset", + "offline", + "portreset", +}; + +static char *type[] = { + "offline", + "sas", + "sata", +}; + +enum{ + Nctlr = 4, + Nctlrdrv = 8, + Ndrive = Nctlr*Nctlrdrv, + Mbar = 2, + Mebar = 4, + Nqueue = 32, /* cmd queue size */ + Qmask = Nqueue - 1, + Nregset = 8, + Rmask = 0xffff, + Nms = 256, /* drive check rate */ + + Sas = 1, + Sata, + + /* cmd bits */ + Error = 1<<31, + Done = 1<<30, + Noverdict = 1<<29, + Creset = 1<<28, + Atareset = 1<<27, + Sense = 1<<26, + Timeout = 1<<25, + Response = 1<<24, + Active = 1<<23, + + /* pci registers */ + Phy0 = 0x40, + Gpio = 0x44, + Phy1 = 0x90, + Gpio1 = 0x94, + Dctl = 0xe8, + + /* phy offests */ + Phydisable = 1<<12, + Phyrst = 1<<16, + Phypdwn = 1<<20, + Phyen = 1<<24, + + /* bar4 registers */ + Gctl = 0x004/4, + Gis = 0x008/4, /* global interrupt status */ + Pi = 0x00c/4, /* ports implemented */ + Flashctl = 0x030/4, /* spi flash control */ + Flashcmd = 0x034/4, /* flash wormhole */ + Flashdata = 0x038/4, + I²cctl = 0x040/4, /* i²c control */ + I²ccmd = 0x044/4, + I²cdata = 0x048/4, + Ptype = 0x0a0/4, /* 15:8 auto detect enable; 7:0 sas=1. sata=0 */ + Portcfg0 = 0x100/4, /* 31:16 register sets 31:16 */ + Portcfg1 = 0x104/4, /* 31:16 register sets 15:8 tx enable; 7 rx enable */ + Clbase = 0x108/4, /* cmd list base; 64 bits */ + Fisbase = 0x110/4, /* 64 bits */ + Dqcfg = 0x120/4, /* bits 11:0 specify size */ + Dqbase = 0x124/4, + Dqwp = 0x12c/4, /* delivery queue write pointer */ + Dqrp = 0x130/4, + Cqcfg = 0x134/4, + Cqbase = 0x138/4, + Cqwp = 0x140/4, /* hw */ + Coal = 0x148/4, + Coalto = 0x14c/4, /* coal timeout µs */ + Cis = 0x150/4, /* centeral irq status */ + Cie = 0x154/4, /* centeral irq enable */ + Csis = 0x158/4, /* cmd set irq status */ + Csie = 0x15c/4, + Cmda = 0x1b8/4, + Cmdd = 0x1bc/4, + Gpioa = 0x270/4, + Gpiod = 0x274/4, + Gpiooff = 0x100, /* second gpio offset */ + + /* port conf registers; mapped through wormhole */ + Pinfo = 0x000, + Paddr = 0x004, + Painfo = 0x00c, /* attached device info */ + Pawwn = 0x010, + Psatactl = 0x018, + Pphysts = 0x01c, + Psig = 0x020, /* 16 bytes */ + Perr = 0x030, + Pcrcerr = 0x034, + Pwidecfg = 0x038, + Pwwn = 0x080, /* 12 wwn + ict */ + + /* port cmd registers; mapped through “cmd” wormhole */ + Ci = 0x040, /* cmd active (16) */ + Task = 0x080, + Rassoc = 0x0c0, + Pfifo0 = 0x1a8, + Pfifo1 = 0x1c4, + Pwdtimer = 0x13c, + + /* “vendor specific” wormhole */ + Phymode = 0x001, + + /* gpio wormhole */ + Sgconf0 = 0x000, + Sgconf1 = 0x004, + Sgclk = 0x008, + Sgconf3 = 0x00c, + Sgis = 0x010, /* interrupt set */ + Sgie = 0x014, /* interrupt enable */ + Drivesrc = 0x020, /* 4 drives/register; 4 bits/drive */ + Drivectl = 0x038, /* same deal */ + + /* Gctl bits */ + Reset = 1<<0, + Intenable = 1<<1, + + /* Portcfg0/1 bits */ + Regen = 1<<16, /* enable sata regsets 31:16 or 15:0 */ + Xmten = 1<<8, /* enable port n transmission */ + Dataunke = 1<<3, + Rsple = 1<<2, /* response frames in le format */ + Oabe = 1<<1, /* oa frame in big endian format */ + Framele = 1<<0, /* frame contents in le format */ + + Allresrx = 1<<7, /* receive all responses */ + Stpretry = 1<<6, + Cmdirq = 1<<5, /* 1 == self clearing */ + Fisen = 1<<4, /* enable fis rx */ + Errstop = 1<<3, /* set -> stop on ssp/smp error */ + Resetiss = 1<<1, /* reset cmd issue; self clearing */ + Issueen = 1<<0, + + /* Dqcfg bits */ + Dqen = 1<<16, + + /* Cqcfg bits */ + Noattn = 1<<17, /* don't post entries with attn bit */ + Cqen = 1<<16, + + /* Cis bits */ + I2cirq = 1<<31, + Swirq1 = 1<<30, + Swirq0 = 1<<29, + Prderr = 1<<28, + Dmato = 1<<27, + Parity = 1<<28, /* parity error; fatal */ + Slavei2c = 1<<25, + Portstop = 1<<16, /* bitmapped */ + Portirq = 1<<8, /* bitmapped */ + Srsirq = 1<<3, + Issstop = 1<<1, + Cdone = 1<<0, + Iclr = Swirq1 | Swirq0, + + /* Pis bits */ + Caf = 1<<29, /* clear affiliation fail */ + Sync = 1<<25, /* sync during fis rx */ + Phyerr = 1<<24, + Stperr = 1<<23, + Crcerr = 1<<22, + Linktx = 1<<21, + Linkrx = 1<<20, + Martianfis = 1<<19, + Anot = 1<<18, /* async notification */ + Bist = 1<<17, + Sigrx = 1<<16, /* native sata signature rx */ + Phyunrdy = 1<<12, /* phy went offline*/ + Uilong = 1<<11, + Uishort = 1<<10, + Martiantag = 1<<9, + Bnot = 1<<8, /* broadcast noticication */ + Comw = 1<<7, + Portsel = 1<<6, + Hreset = 1<<5, + Phyidto = 1<<4, + Phyidfail = 1<<3, + Phyidok = 1<<2, + Hresetok = 1<<1, + Phyrdy = 1<<0, + + Pisataup = Phyrdy | Comw | Sigrx, + Pisasup = Phyrdy | Comw | Phyidok, + Piburp = Sync | Phyerr | Stperr | Crcerr | Linktx | + Linkrx | Martiantag, + Pireset = Phyidfail | Bnot | Phyunrdy | Bist | + Anot | Martianfis | Bist | Phyidto | + Hreset, + Piunsupp = Portsel, + + /* Psc bits */ + Sphyrdy = 1<<20, + Linkrate = 1<<18, /* 4 bits */ + Maxrate = 1<<12, + Minrate = 1<<8, + Sreset = 1<<3, + Sbnote = 1<<2, + Shreset = 1<<1, + Sphyrst = 1<<0, + + /* Painfo bits */ + Issp = 1<<19, + Ismp = 1<<18, + Istp = 1<<17, + Itype = 1<<0, /* two bits */ + + /* Psatactl bits */ + Powerctl = 1<<30, /* 00 wake; 10 partial 01 slumb */ + Srst = 1<<29, /* soft reset */ + Power = 1<<28, + Sportsel = 1<<24, + Dmahon = 1<<22, + Srsten = 1<<20, + Dmaxfr = 1<<18, + + /* phy status bits */ + Phylock = 1<<9, + Nspeed = 1<<4, + Psphyrdy = 1<<2, + + /* Task bits; modeled after ahci */ + Eestatus = 0xff<<24, + Asdbs = 1<<18, + Apio = 1<<17, + Adhrs = 1<<16, + Eerror = 0xff<<8, + Estatus = 0xff, + + /* Phymode bits */ + Pmnotify = 1<<24, + Pmnotifyen = 1<<23, + + /* Sgconf0 bits */ + Autolen = 1<<24, /* 8 bits */ + Manlen = 1<<16, /* 8 bits */ + Sdincapt = 1<<8, /* capture sdatain on + edge */ + Sdoutch = 1<<7, /* change sdataout on - edge + Sldch = 1<<6, /* change sload on - edge + Sdoutivt = 1<<5, /* invert sdataout polarity */ + Ldivt = 1<<4, + Sclkivt = 1<<3, + Blinkben = 1<<2, /* enable blink b */ + Blinkaen = 1<<1, /* enable blink a */ + Sgpioen = 1<<0, + + /* Sgconf1 bits; 4 bits each */ + Sactoff = 1<<28, /* stretch activity off; 0/64 - 15/64 */ + Sacton = 1<<24, /* 1/64th - 16/64 */ + Factoff = 1<<20, /* 0/8 - 15/8; default 1 */ + Facton = 1<<16, /* 0/4 - 15/4; default 2 */ + Bhi = 1<<12, /* 1/8 - 16/8 */ + Blo = 1<<8, /* 1/8 - 16/8 */ + Ahi = 1<<4, /* 1/8 - 16/8 */ + Alo = 1<<0, /* 1/8 - 16/8 */ + + /* Sgconf3 bits */ + Autopat = 1<<20, /* 4 bits of start pattern */ + Manpat = 1<<16, + Manrep = 1<<4, /* repeats; 7ff ≡ ∞ */ + Sdouthalt = 0<<2, + Sdoutman = 1<<2, + Sdoutauto = 2<<2, + Sdoutma = 3<<2, + Sdincapoff = 0<<0, + Sdinoneshot = 1<<0, + Sdinrep = 2<<0, + + /* Sgie Sgis bits */ + Sgreprem = 1<<8, /* 12 bits; not irq related */ + Manrep0 = 1<<1, /* write 1 to clear */ + Capdone = 1<<0, /* capture done */ + + /* drive control bits (repeated 4x per drive) */ + Aled = 1<<5, /* 3 bits */ + Locled = 1<<3, /* 2 bits */ + Errled = 1<<0, /* 3 bits */ + Llow = 0, + Lhigh = 1, + Lblinka = 2, + Lblinkaneg = 3, + Lsof = 4, + Leof = 5, + Lblinkb = 6, + Lblinkbneg = 7, + + /* cmd queue bits */ + Dssp = 1<<29, + Dsmp = 2<<29, + Dsata = 3<<29, /* also stp */ + Ditor = 1<<28, /* initiator */ + Dsatareg = 1<<20, + Dphyno = 1<<12, + Dcslot = 1, + + /* completion queue bits */ + Cgood = 1<<23, /* ssp only */ + Cresetdn = 1<<21, + Crx = 1<<20, /* target mode */ + Cattn = 1<<19, + Crxfr = 1<<18, + Cerr = 1<<17, + Cqdone = 1<<16, + Cslot = 1<<0, /* 12 bits */ + + /* error bits — first word */ + Eissuestp = 1<<31, /* cmd issue stopped */ + Epi = 1<<30, /* protection info error */ + Eoflow = 1<<29, /* buffer overflow */ + Eretry = 1<<28, /* retry limit exceeded */ + Eufis = 1<<27, + Edmat = 1<<26, /* dma terminate */ + Esync = 1<<25, /* sync rx during tx */ + Etask = 1<<24, + Ererr = 1<<23, /* r error received */ + + Eroff = 1<<20, /* read data offset error */ + Exoff = 1<<19, /* xfer rdy offset error */ + Euxr = 1<<18, /* unexpected xfer rdy */ + Exflow = 1<<16, /* buffer over/underflow */ + Elock = 1<<15, /* interlock error */ + Enak = 1<<14, + Enakto = 1<<13, + Enoak = 1<<12, /* conn closed wo nak */ + Eopento = 1<<11, /* open conn timeout */ + Epath = 1<<10, /* open reject - path blocked */ + Enodst = 1<<9, /* open reject - no dest */ + Estpbsy = 1<<8, /* stp resource busy */ + Ebreak = 1<<7, /* break while sending */ + Ebaddst = 1<<6, /* open reject - bad dest */ + Ebadprot = 1<<5, /* open reject - proto not supp */ + Erate = 1<<4, /* open reject - rate not supp */ + Ewdest = 1<<3, /* open reject - wrong dest */ + Ecreditto = 1<<2, /* credit timeout */ + Edog = 1<<1, /* watchdog timeout */ + Eparity = 1<<0, /* buffer parity error */ + + /* sas ctl cmd header bits */ + Ssptype = 1<<5, /* 3 bits */ + Ssppt = 1<<4, /* build your own header *. + Firstburst = 1<<3, /* first burst */ + Vrfylen = 1<<2, /* verify length */ + Tlretry = 1<<1, /* transport layer retry */ + Piren = 1<<0, /* pir present */ + + /* sata ctl cmd header bits */ + Lreset = 1<<7, + Lfpdma = 1<<6, /* first-party dma. (what's that?) */ + Latapi = 1<<5, + Lpm = 1<<0, /* 4 bits */ + + Sspcmd = 0*Ssptype, + Ssptask = 1*Ssptype, + Sspxfrdy = 4*Ssptype, + Ssprsp = 5*Ssptype, + Sspread = 6*Ssptype, + Sspwrite = 7*Ssptype, +}; + +/* following ahci */ +typedef struct { + ulong dba; + ulong dbahi; + ulong pad; + ulong count; +} Aprdt; + +typedef struct { + union{ + struct{ + uchar cfis[0x40]; + uchar atapi[0x20]; + }; + struct{ + uchar mfis[0x40]; + }; + struct{ + uchar sspfh[0x18]; + uchar sasiu[0x40]; + }; + }; +} Ctab; + +/* protection information record */ +typedef struct { + uchar ctl; + uchar pad; + uchar size[2]; + uchar rtag[4]; + uchar atag[2]; + uchar mask[2]; +} Pir; + +/* open address frame */ +typedef struct { + uchar oaf[0x28]; + uchar fb[4]; +} Oaf; + +/* status buffer */ +typedef struct { + uchar error[8]; + uchar rsp[0x400]; +} Statb; + +typedef struct { + uchar satactl; + uchar sasctl; + uchar len[2]; + + uchar fislen[2]; + uchar maxrsp; + uchar d0; + + uchar tag[2]; + uchar ttag[2]; + + uchar dlen[4]; + uchar ctab[8]; + uchar oaf[8]; + uchar statb[8]; + uchar prd[8]; + + uchar d3[16]; +} Cmdh; + +typedef struct Cmd Cmd; +struct Cmd { + Rendez; + uint cflag; + + Cmdh *cmdh; + Ctab; + Oaf; + Statb; + Aprdt; +}; + +typedef struct Drive Drive; +typedef struct Ctlr Ctlr; + +struct Drive { + Lock; + QLock; + Ctlr *ctlr; + SDunit *unit; + char name[16]; + + Cmd *cmd; + + /* sdscsi doesn't differentiate drivechange/mediachange */ + uchar drivechange; + uchar state; + uchar type; + ushort info[0x100]; + + Sfis; /* sata and media info*/ + Cfis; /* sas and media info */ + Ledport; /* led */ + + /* hotplug info */ + uint lastseen; + uint intick; + uint wait; + + char serial[20+1]; + char firmware[8+1]; + char model[40+1]; + uvlong wwn; + uvlong sectors; + uint secsize; + + uint driveno; +}; + +struct Ctlr { + Lock; + uchar enabled; + SDev *sdev; + Pcidev *pci; + uint *reg; + + uint dq[Nqueue]; + uint dqwp; + uint cq[Nqueue + 1]; + uint cqrp; + Cmdh *cl; + uchar *fis; + Cmd *cmdtab; + + Drive drive[Nctlrdrv]; + uint ndrive; +}; + +static Ctlr msctlr[Nctlr]; +static SDev sdevs[Nctlr]; +static uint nmsctlr; +static Drive *msdrive[Ndrive]; +static uint nmsdrive; +static int debug=0; +static int idebug=1; +static int adebug; +static uint olds[Nctlr*Nctlrdrv]; + SDifc sdodinifc; + +/* a good register is hard to find */ +static int pis[] = { + 0x160/4, 0x168/4, 0x170/4, 0x178/4, + 0x200/4, 0x208/4, 0x210/4, 0x218/4, +}; +static int pcfg[] = { + 0x1c0/4, 0x1c8/4, 0x1d0/4, 0x1d8/4, + 0x230/4, 0x238/4, 0x240/4, 0x248/4, +}; +static int psc[] = { + 0x180/4, 0x184/4, 0x188/4, 0x18c/4, + 0x220/4, 0x224/4, 0x228/4, 0x22c/4, +}; +static int vscfg[] = { + 0x1e0/4, 0x1e8/4, 0x1f0/4, 0x1f8/4, + 0x250/4, 0x258/4, 0x260/4, 0x268/4, +}; +#define sstatus(d) (d)->ctlr->reg[psc[(d)->driveno]] + +static char* +dstate(uint s) +{ + int i; + + for(i = 0; s; i++) + s >>= 1; + return diskstates[i]; +} + +static char* +dnam(Drive *d) +{ + if(d->unit) + return d->unit->name; + return d->name; +} + +static uvlong border = 0x0001020304050607ull; +static uvlong lorder = 0x0706050403020100ull; + +static uvlong +getle(uchar *t, int w) +{ + uint i; + uvlong r; + + r = 0; + for(i = w; i != 0; ) + r = r<<8 | t[--i]; + return r; +} + +static void +putle(uchar *t, uvlong r, int w) +{ + uchar *o, *f; + uint i; + + f = (uchar*)&r; + o = (uchar*)&lorder; + for(i = 0; i < w; i++) + t[o[i]] = f[i]; +} + +static uvlong +getbe(uchar *t, int w) +{ + uint i; + uvlong r; + + r = 0; + for(i = 0; i < w; i++) + r = r<<8 | t[i]; + return r; +} + +static void +putbe(uchar *t, uvlong r, int w) +{ + uchar *o, *f; + uint i; + + f = (uchar*)&r; + o = (uchar*)&border + (sizeof border-w); + for(i = 0; i < w; i++) + t[i] = f[o[i]]; +} + +static int phyrtab[] = {Phy0, Phy1}; +static void +phyenable(Ctlr *c, Drive *d) +{ + uint i, u, reg, m; + + i = d->driveno; + reg = phyrtab[i > 3]; + i &= 3; + i = 1<pci, reg); + m = i*(Phypdwn | Phydisable | Phyen); + if((u & m) == Phyen) + return; + m = i*(Phypdwn | Phydisable); + u &= ~m; + u |= i*Phyen; + pcicfgw32(c->pci, reg, u); +} + +static void +regtxreset(Drive *d) +{ + uint i, u, m; + Ctlr *c = d->ctlr; + + i = d->driveno; + u = c->reg[Portcfg1]; + m = (Regen|Xmten)<reg[Portcfg1] = u; + delay(1); + c->reg[Portcfg1] = u | m; +} + +/* aka comreset? */ +static void +phyreset(Drive *d) +{ + uint i, u, reg; + Ctlr *c; + + c = d->ctlr; + phyenable(c, d); + + i = d->driveno; + reg = phyrtab[i > 3]; + i &= 3; + i = 1<pci, reg); + pcicfgw32(c->pci, reg, u | i*Phyrst); + delay(5); + pcicfgw32(c->pci, reg, u); + + sstatus(d) |= Shreset; + while(sstatus(d) & Shreset); + ; +} + +static void +reset(Drive *d) +{ + regtxreset(d); + phyreset(d); +} + +/* + * sata/sas register reads through wormhole + */ +static uint +ssread(Ctlr *c, int port, uint r) +{ + c->reg[Cmda] = r + 4*port; + return c->reg[Cmdd]; +} + +static void +sswrite(Ctlr *c, int port, int r, uint u) +{ + c->reg[Cmda] = r + 4*port; + c->reg[Cmdd] = u; +} + +/* + * port configuration r/w through wormhole + */ +static uint +pcread(Ctlr *c, uint port, uint r) +{ + c->reg[pcfg[port]] = r; + return c->reg[pcfg[port] + 1]; +} + +static void +pcwrite(Ctlr *c, uint port, uint r, uint u) +{ + c->reg[pcfg[port] + 0] = r; + c->reg[pcfg[port] + 1] = u; +} + +/* + * vendor specific r/w through wormhole + */ +static uint +vsread(Ctlr *c, uint port, uint r) +{ + c->reg[vscfg[port]] = r; + return c->reg[vscfg[port] + 1]; +} + +static void +vswrite(Ctlr *c, uint port, uint r, uint u) +{ + c->reg[vscfg[port] + 0] = r; + c->reg[vscfg[port] + 1] = u; +} + +/* + * gpio wormhole + */ +static uint +gpread(Ctlr *c, uint r) +{ + c->reg[Gpioa] = r; + return c->reg[Gpiod]; +} + +static void +gpwrite(Ctlr *c, uint r, uint u) +{ + c->reg[Gpioa] = r; + c->reg[Gpiod] = u; +} + +static uint* +getsigfis(Drive *d, uint *fis) +{ + uint i; + + for(i = 0; i < 4; i++) + fis[i] = pcread(d->ctlr, d->driveno, Psig + 4*i); + return fis; +} + +static uint +getsig(Drive *d) +{ + uint fis[4]; + + return fistosig((uchar*)getsigfis(d, fis)); +} + +static uint +ci(Drive *d) +{ + return ssread(d->ctlr, d->driveno, Ci); +} + +static void +unsetci(Drive *d) +{ + uint i; + + i = 1<driveno; + sswrite(d->ctlr, d->driveno, Ci, i); + while(ci(d) & i) + microdelay(1); +} + +static uint +gettask(Drive *d) +{ + return ssread(d->ctlr, d->driveno, Task); +} + +static void +tprint(Drive *d, uint t) +{ + uint s; + + s = sstatus(d); + dprint("%s: err task %ux sstat %ux\n", dnam(d), t, s); +} + +static int +cmdactive(void *v) +{ + Cmd *x; + + x = v; + return (x->cflag & Done) != 0; +} + +static int +mswait(Cmd *x, int ms) +{ + uint u, tk0; + + if(up){ + tk0 = Ticks; + while(waserror()) + ; + tsleep(x, cmdactive, x, ms); + poperror(); + ms -= TK2MS(Ticks - tk0); + }else + while(ms-- && cmdactive(x)) + delay(1); +// ilock(cmd->d); + u = x->cflag; + x->cflag = 0; +// iunlock(cmd->d) + + if(u == (Done | Active)) + return 0; + if((u & Done) == 0){ + u |= Noverdict | Creset | Timeout; + print("cmd timeout ms:%d %ux\n", ms, u); + } + return u; +} + +static void +setstate(Drive *d, int state) +{ + ilock(d); + d->state = state; + iunlock(d); +} + +static void +esleep(int ms) +{ + if(waserror()) + return; + tsleep(&up->sleep, return0, 0, ms); + poperror(); +} + +static int +waitready(Drive *d) +{ + ulong s, i, δ; + + for(i = 0; i < 15000; i += 250){ + if(d->state & (Dreset | Dportreset | Dnew)) + return 1; + δ = Ticks - d->lastseen; + if(d->state == Dnull || δ > 10*1000) + return -1; + ilock(d); + s = sstatus(d); + iunlock(d); + if((s & Sphyrdy) == 0 && δ > 1500) + return -1; + if(d->state == Dready && (s & Sphyrdy)) + return 0; + esleep(250); + } + print("%s: not responding; offline: %.8ux\n", dnam(d), sstatus(d)); + setstate(d, Doffline); + return -1; +} + +static int +lockready(Drive *d) +{ + int i, r; + + for(i = 0; ; i++){ + qlock(d); + if((r = waitready(d)) != 1) + return r; + qunlock(d); + if(i == Nms*10) + break; + esleep(1); + } + return -1; +} + +static int +command(Drive *d, uint cmd, int ms) +{ + uint s, n, m, i; + Ctlr *c; + + c = d->ctlr; + i = d->driveno; + m = 1<cq[0], n, i); + /* + * xinc doesn't return the previous value and i can't + * figure out how to do this without a lock + * s = _xinc(&c->dqwp); + */ + d->cmd->cflag = Active; + ilock(c); + s = c->dqwp++; + c->dq[s&Qmask] = n; + c->reg[Dqwp] = s&Qmask; + iunlock(c); +// print(" dq slot %d\n", s); + d->intick = Ticks; /* move to mswait? */ + return mswait(d->cmd, ms); +} + +static int +buildfis(Drive *d, SDreq *r, void *data, int n) +{ + Aprdt *p; + Cmd *x; + Cmdh *h; + + x = d->cmd; + memmove(x->cfis, r->cmd, r->clen); + + h = x->cmdh; + memset(h, 0, 16); + h->fislen[0] = 5; + h->len[0] = 0; + + if(data != nil){ + h->len[0] = 1; + p = x; + p->dba = PCIWADDR(data); + p->dbahi = Pciwaddrh(data); + p->count = n; + } + return command(d, Dsata, 10*1000); +} + +static int +build(Drive *d, int rw, void *data, int n, vlong lba) +{ + Aprdt *p; + Cmd *x; + Cmdh *h; + + x = d->cmd; + rwfis(d, x->cfis, rw, n, lba); + + h = x->cmdh; + memset(h, 0, 16); + h->fislen[0] = 5; + h->len[0] = 1; /* one prdt entry */ + + p = x; + p->dba = PCIWADDR(data); + p->dbahi = Pciwaddrh(data); + p->count = d->secsize*n; + + return command(d, Dsata, 10*1000); +} + +enum{ + Rnone = 1, + Rdma = 0x00, /* dma setup; length 0x1b */ + Rpio = 0x20, /* pio setup; length 0x13 */ + Rd2h = 0x40, /* d2h register;length 0x13 */ + Rsdb = 0x58, /* set device bits; length 0x08 */ +}; + +static uint fisotab[8] = { +[0] Rnone, +[1] Rd2h, +[2] Rpio, +[3] Rnone, +[4] Rsdb, +[5] Rnone, +[6] Rnone, +[7] Rnone, +}; + +static uint +fisoffset(Drive *d, int mustbe) +{ + uint t, r; + + t = gettask(d) & 0x70000; + r = fisotab[t >> 16]; + if(r == Rnone || (mustbe != 0 && r != mustbe)) + return 0; + return 0x800 + 0x100*d->driveno + r; +} + +/* need to find a non-atapi-specific way of doing this */ +static uint +atapixfer(Drive *d, uint n) +{ + uchar *u; + uint i, x; + + if((i = fisoffset(d, Rd2h)) == 0) + return 0; + u = d->ctlr->fis + i; + x = u[Flba16]<<8 | u[Flba8]; + if(x > n){ + x = n; + print("%s: atapixfer %ux %ux\n", dnam(d), x, n); + } + return x; +} + +static int +buildpkt(Drive *d, SDreq *r, void *data, int n) +{ + int rv; + Aprdt *p; + Cmd *x; + Cmdh *h; + + x = d->cmd; + atapirwfis(d, x->cfis, r->cmd, r->clen, n); + + h = x->cmdh; + memset(h, 0, 16); + h->satactl = Latapi; + h->fislen[0] = 5; + h->len[0] = 1; /* one prdt entry */ + + if(data != nil){ + p = x; + p->dba = PCIWADDR(data); + p->dbahi = Pciwaddrh(data); + p->count = n; + } + rv = command(d, Dsata, 10*1000); + if(rv == 0) + r->rlen = atapixfer(d, n); + return rv; +} + +/* + * ata 7, required for sata, requires that all devices "support" + * udma mode 5, however sata:pata bridges allow older devices + * which may not. the innodisk satadom, for example allows + * only udma mode 2. on the assumption that actual udma is + * taking place on these bridges, we set the highest udma mode + * available, or pio if there is no udma mode available. + */ +static int +settxmode(Drive *d, uchar f) +{ + Cmd *x; + Cmdh *h; + + x = d->cmd; + if(txmodefis(d, x->cfis, f) == -1) + return 0; + + h = x->cmdh; + memset(h, 0, 16); + h->fislen[0] = 5; + + return command(d, Dsata, 3*1000); +} + +static int +setfeatures(Drive *d, uchar f, uint w) +{ + Cmd *x; + Cmdh *h; + + x = d->cmd; + featfis(d, x->cfis, f); + + h = x->cmdh; + memset(h, 0, 16); + h->fislen[0] = 5; + + return command(d, Dsata, w); +} + +static int +mvflushcache(Drive *d) +{ + Cmd *x; + Cmdh *h; + + x = d->cmd; + flushcachefis(d, x->cfis); + + h = x->cmdh; + memset(h, 0, 16); + h->fislen[0] = 5; + + return command(d, Dsata, 60*1000); +} + +static int +identify0(Drive *d, void *id) +{ + Aprdt *p; + Cmd *x; + Cmdh *h; + + x = d->cmd; + identifyfis(d, x->cfis); + + h = x->cmdh; + memset(h, 0, 16); + h->fislen[0] = 5; + h->len[0] = 1; /* one prdt entry */ + + memset(id, 0, 0x200); + p = x; + p->dba = PCIWADDR(id); + p->dbahi = Pciwaddrh(id); + p->count = 0x200; + + return command(d, Dsata, 3*1000); +} + +static int +identify(Drive *d) +{ + int i, n; + vlong osectors, s; + uchar oserial[21]; + ushort *id; + SDunit *u; + + id = d->info; + for(i = 0;; i++){ + if(i > 5 || identify0(d, id) != 0) + return -1; + n = idpuis(id); + if(n & Pspinup && setfeatures(d, 7, 20*1000) == -1) + dprint("%s: puis spinup fail\n", dnam(d)); + if(n & Pidready) + break; + } + + s = idfeat(d, id); + if(s == -1) + return -1; + if((d->feat&Dlba) == 0){ + dprint("%s: no lba support\n", dnam(d)); + return -1; + } + osectors = d->sectors; + memmove(oserial, d->serial, sizeof d->serial); + + d->sectors = s; + d->secsize = idss(d, id); + + idmove(d->serial, id+10, 20); + idmove(d->firmware, id+23, 8); + idmove(d->model, id+27, 40); + d->wwn = idwwn(d, id); + + u = d->unit; + memset(u->inquiry, 0, sizeof u->inquiry); + u->inquiry[2] = 2; + u->inquiry[3] = 2; + u->inquiry[4] = sizeof u->inquiry - 4; + memmove(u->inquiry+8, d->model, 40); + + if(osectors != s || memcmp(oserial, d->serial, sizeof oserial)){ + d->drivechange = 1; + u->sectors = 0; + } + return 0; +} + +/* open address fises */ +enum{ + Initiator = 0x80, + Openaddr = 1, + Awms = 0x8000, + Smp = 0, + Ssp = 1, + Stp = 2, + Spd15 = 8, + Spd30 = 9, +}; + +static void +oafis(Cfis *f, uchar *c, int type) +{ + c[0] = Initiator | type<<4 | Openaddr; + c[1] = Spd30; /* botch; just try 3gbps */ + if(type == Smp) + memset(c + 2, 0xff, 2); + else + memmove(c + 2, f->ict, 2); + memmove(c + 4, f->tsasaddr, 8); /* dest "port identifier" §4.2.6 */ + memmove(c + 12, f->ssasaddr, 8); +} + +/* sas fises */ +static int +sasfis(Cfis*, uchar *c, SDreq *r) +{ + memmove(c, r->cmd, r->clen); + if(r->clen < 16) + memset(c + r->clen, 0, 16 - r->clen); + return 0; +} + +/* sam3 §4.9.4 single-level lun structure */ +static void +scsilun8(uchar *c, int l) +{ + memset(c, 0, 8); + if(l < 255) + c[1] = l; + else if(l < 16384){ + c[0] = 1<<6 | l>>8; + c[1] = l; + }else + print("bad lun %d\n", l); +} + +static void +iuhdr(SDreq *r, uchar *c, int fburst) +{ + scsilun8(c, r->lun); + c[8] = 0; + c[9] = 0; + if(fburst) + c[9] = 0x80; +} + +static void +ssphdr(Cfis *x, uchar *c, int ftype) +{ + memset(c, 0, 0x18); + c[0] = ftype; + sasbhash(c + 1, x->tsasaddr); + sasbhash(c + 5, x->ssasaddr); +} + +/* debugging */ +static void +dump(uchar *u, uint n) +{ + uint i; + + if(n > 100) + n = 100; + for(i = 0; i < n; i += 4){ + print("%.2d %.2ux%.2ux%.2ux%.2ux", i, u[i], u[i + 1], u[i + 2], u[i + 3]); + print("\n"); + } +} + +static void +prsense(uchar *u, uint n) +{ + print("sense data %d: \n", n); + dump(u, n); +} + +static void +priu(uchar *u, uint n) +{ + print("iu %d: \n", n); + dump(u, n); +} + +/* + * other suspects: + * key asc/q + * 02 0401 becoming ready + * 040b target port in standby state + * 0b01 overtemp + * 0b0[345] background * + * 0c01 write error - recovered with auto reallocation + * 0c02 write error - auto reallocation failed + * 0c03 write error - recommend reassignment + * 17* recovered data + * 18* recovered data + * 5d* smart-style reporting (disk/smart handles) + * 5e* power state change + */ + +static int +classifykey(int asckey) +{ + if(asckey == 0x062901 || asckey == 0x062900){ + /* power on */ + dprint("power on sense\n"); + return SDretry; + } + return SDcheck; +} + +/* spc3 §4.5 */ +static int +sasrspck(Drive *d, SDreq *r, int min) +{ + char *p; + int rv; + uchar *u, *s; + uint l, fmt, n, keyasc; + + u = d->cmd->rsp; + s = u + 24; + dprint("status %d datapres %d\n", u[11], u[10]); + switch(u[10]){ + case 1: + l = getbe(u + 20, 4); + /* + * this is always a bug because we don't do + * task mgmt + */ + print("%s: bug: task data %d min %d\n", dnam(d), l, min); + return SDcheck; + case 2: + l = getbe(u + 16, 4); + n = sizeof r->sense; + if(l < n) + n = l; + memmove(r->sense, s, n); + fmt = s[0] & 0x7f; + keyasc = (s[2] & 0xf)<<16 | s[12]<<8 | s[13]; + rv = SDcheck; + /* spc3 §4.5.3; 0x71 is deferred. */ + if(n >= 18 && (fmt == 0x70 || fmt == 0x71)){ + rv = classifykey(keyasc); + p = ""; + if(rv == SDcheck){ + r->flags |= SDvalidsense; + p = "valid"; + } + dprint("sense %.6ux %s\n", keyasc, p); + }else + prsense(s, l); + return rv; + default: + print("%s: sasrspck: spurious\n", dnam(d)); + priu(u, 24); + prsense(s, 0x30); + return SDcheck; + } +} + +static int +buildsas(Drive *d, SDreq *r, uchar *data, int n) +{ + int w, try, fburst; + Aprdt *p; + Cmd *x; + Cmdh *h; + + try = 0; +top: + fburst = 0; /* Firstburst? */ + x = d->cmd; + /* ssphdr(d, x->sspfh, 6); */ + iuhdr(r, x->sasiu, fburst); + w = 0; + if(r->clen > 16) + w = r->clen - 16 + 3>> 2; + x->sasiu[11] = w; + sasfis(d, x->sasiu + 12, r); + + h = x->cmdh; + memset(h, 0, 16); + h->sasctl = Tlretry | /*Vrfylen |*/ Sspcmd | fburst; + h->fislen[0] = sizeof x->sspfh + 12 + 16 + 4*w >> 2; + h->maxrsp = 0xff; + if(n) + h->len[0] = 1; + h->ttag[0] = 1; + *(uint*)h->dlen = n; + + if(n){ + p = x; + p->dba = PCIWADDR(data); + p->dbahi = Pciwaddrh(data); + p->count = n; + } + switch(w = command(d, Dssp, 10*1000)){ + case 0: + r->status = sdsetsense(r, SDok, 0, 0, 0); + return 0; + case Response | Done | Active: + r->status = sasrspck(d, r, 0); + if(r->status == SDok) + return 0; + if(r->status == SDretry){ + if(try++ < 2) + goto top; + r->status |= SDvalidsense; + } + return w | Sense; + default: + r->status = sdsetsense(r, SDcheck, 4, 24, 0); + return w; + } +} + +static uint +analyze(Drive *d, Statb *b) +{ + uint u, r, t; + + r = 0; + u = *(uint*)b->error; + if(u & Eissuestp){ + r |= Error; + unsetci(d); + } + if(u & Etask && (d->feat & Datapi) == 0){ + t = gettask(d); + if(t & 1) + tprint(d, t); + if(t & Efatal<<8 || t & (ASbsy|ASdrq)) + r |= Noverdict|Atareset; + if(t&Adhrs && t&33) + r |= Noverdict|Atareset; + else + r |= Error; + } + if(u & (Ererr | Ebadprot)){ + /* sas thing */ + print("%s: sas error %.8ux\n", dnam(d), u); + r |= Error; + } + if(u & ~(Ebadprot | Ererr | Etask | Eissuestp)) + print("%s: analyze %.8ux\n", dnam(d), u); + + return r; +} + +static void +updatedone(Ctlr *c) +{ + uint a, e, i, u, slot; + Cmd *x; + Drive *d; + + e = c->cq[0]; + if(e == 0xfff) + return; + if(e > Qmask) + print("sdodin: bug: bad cqrp %ux\n", e); + e = e+1 & Qmask; + for(i = c->cqrp; i != e; i = i+1 & Qmask){ + u = c->cq[1 + i]; + c->cq[1 + i] = 0; + slot = u & 0xfff; + u &= ~slot; + d = c->drive + slot; + x = d->cmd; + if(u & Cqdone){ + x->cflag |= Done; + u &= ~Cqdone; + } + if(u & (Crxfr | Cgood)){ + if((u & Cgood) == 0) + x->cflag |= Response; + u &= ~(Crxfr | Cgood); + } + if(u & Cerr){ + dprint("%s: Cerr ..\n", dnam(d)); + a = analyze(d, x); + x->cflag |= Done | a; + u &= ~Cerr; + } + if(x->cflag & Done) + wakeup(x); + if(u) + print("%s: odd bits %.8ux\n", dnam(d), u); + } +if(i == c->cqrp)print("odin: spur done\n"); + c->cqrp = i; +} + +static void +updatedrive(Drive *d) +{ + uint cause, s0, ewake; + char *name; + Cmd *x; + static uint last, tk; + + ewake = 0; + cause = d->ctlr->reg[pis[d->driveno]]; + d->ctlr->reg[pis[d->driveno]] = cause; + x = d->cmd; + name = dnam(d); + + if(last != cause || Ticks - tk > 5*1000){ + dprint("%s: ca %ux ta %ux\n", name, cause, gettask(d)); + tk = Ticks; + } + if(cause & (Phyunrdy | Phyidto | Pisataup | Pisasup)){ + s0 = d->state; + if(cause == (Phyrdy | Comw)){ + d->type = 0; + d->state = Dnopower; + } + switch(cause & (Phyunrdy | Phyidto | Phyidok | Sigrx)){ + case Phyunrdy: + d->state = Dmissing; + if(sstatus(d) & Sphyrdy){ + if(d->type != 0) + d->state = Dnew; + else + d->state = Dreset; + } + break; + case Phyidto: + d->type = 0; + d->state = Dmissing; + break; + case Phyidok: + d->type = Sas; + d->state = Dnew; + break; + case Sigrx: + d->type = Sata; + d->state = Dnew; + break; + } + dprint("%s: %s → %s [Apcrs] %s %ux\n", name, dstate(s0), + dstate(d->state), type[d->type], sstatus(d)); + if(s0 == Dready && d->state != Dready) + idprint("%s: pulled\n", name); + if(d->state != Dready || ci(d)) + ewake |= Done | Noverdict; + }else if(cause & Piburp) + ewake |= Done | Noverdict; + else if(cause & Pireset) + ewake |= Done | Noverdict | Creset; + else if(cause & Piunsupp){ + print("%s: unsupported h/w: %.8ux\n", name, cause); + ewake |= Done | Error; + d->type = 0; + d->state = Doffline; + } + if(ewake){ + dprint("%s: ewake %.8ux\n", name, ewake); + unsetci(d); + x->cflag |= ewake; + wakeup(x); + } + last = cause; +} + +static int +satareset(Drive *d) +{ + ilock(d->ctlr); + unsetci(d); + iunlock(d->ctlr); + if(gettask(d) & (ASdrq|ASbsy)) + return -1; + if(settxmode(d, d->udma) != 0) + return -1; + return 0; +} + +static int +msriopkt(SDreq *r, Drive *d) +{ + int n, count, try, max, flag, task; + uchar *cmd; + + cmd = r->cmd; + aprint("%02ux %02ux %c %d %p\n", cmd[0], cmd[2], "rw"[r->write], + r->dlen, r->data); + r->rlen = 0; + count = r->dlen; + max = 65536; + + for(try = 0; try < 10; try++){ + n = count; + if(n > max) + n = max; + if(lockready(d) == -1) + return SDeio; + flag = buildpkt(d, r, r->data, n); + task = gettask(d); + if(flag & Atareset && satareset(d) == -1) + setstate(d, Dreset); + qunlock(d); + if(flag & Noverdict){ + if(flag & Creset) + setstate(d, Dreset); + print("%s: retry\n", dnam(d)); + continue; + } + if(flag & Error){ + if((task & Eidnf) == 0) + print("%s: i/o error %ux\n", dnam(d), task); + return r->status = SDcheck; + } + return r->status = SDok; + } + print("%s: bad disk\n", dnam(d)); + return r->status = SDcheck; +} + +static int +msriosas(SDreq *r, Drive *d) +{ + int try, flag; + + for(try = 0; try < 10; try++){ + if(lockready(d) == -1) + return SDeio; + flag = buildsas(d, r, r->data, r->dlen); + qunlock(d); + if(flag & Noverdict){ + if(flag & Creset) + setstate(d, Dreset); + print("%s: retry\n", dnam(d)); + continue; + } + if(flag & Error){ + print("%s: i/o error\n", dnam(d)); + return r->status = SDcheck; + } + r->rlen = r->dlen; /* fishy */ + return r->status; /* set in sasrspck */ + + } + print("%s: bad disk\n", dnam(d)); + sdsetsense(r, SDcheck, 3, r->write? 0xc00: 0x11, 0); + return r->status = SDcheck; +} + +static int +flushcache(Drive *d) +{ + int i; + + i = -1; + if(lockready(d) == 0) + i = mvflushcache(d); + qunlock(d); + return i; +} + +static int +msriosata(SDreq *r, Drive *d) +{ + char *name; + int i, n, count, try, max, flag, task; + uvlong lba; + uchar *cmd, *data; + SDunit *unit; + + unit = r->unit; + cmd = r->cmd; + name = dnam(d); + + if(cmd[0] == 0x35 || cmd[0] == 0x91){ + if(flushcache(d) == 0) + return sdsetsense(r, SDok, 0, 0, 0); + return sdsetsense(r, SDcheck, 3, 0xc, 2); + } + if((i = sdfakescsi(r)) != SDnostatus){ + r->status = i; + return i; + } + if((i = sdfakescsirw(r, &lba, &count, nil)) != SDnostatus) + return i; + max = 128; + if(d->feat & Dllba) + max = 65536; + try = 0; + data = r->data; + while(count > 0){ + n = count; + if(n > max) + n = max; + if(lockready(d) == -1) + return SDeio; + flag = build(d, r->write, data, n, lba); + task = gettask(d); + if(flag & Atareset && satareset(d) == -1) + setstate(d, Dreset); + qunlock(d); + if(flag & Noverdict){ + if(flag & Creset) + setstate(d, Dreset); + if(++try == 2){ + print("%s: bad disk\n", name); + return r->status = SDeio; + } + iprint("%s: retry %lld [%.8ux]\n", name, lba, task); + continue; + } + if(flag & Error){ + iprint("%s: i/o error %ux @%,lld\n", name, task, lba); + return r->status = SDeio; + } + count -= n; + lba += n; + data += n*unit->secsize; + } + r->rlen = data - (uchar*)r->data; + r->status = SDok; + return SDok; +} + +static int +msrio(SDreq *r) +{ + Ctlr *c; + Drive *d; + SDunit *u; + + u = r->unit; + c = u->dev->ctlr; + d = c->drive + u->subno; + if(d->feat & Datapi) + return msriopkt(r, d); + if(d->type == Sas) + return msriosas(r, d); + if(d->type == Sata) + return msriosata(r, d); + return sdsetsense(r, SDcheck, 3, 0x04, 0x24); +} + +/* + * §6.1.9.5 + * not clear that this is necessary + * we should know that it's a d2h from the status. + * pio returns pio setup fises. hw bug? + */ +static int +sdr(SDreq *r, Drive *d, int st) +{ + uint i; + + if(i = fisoffset(d, 0/*Rd2h*/)) + memmove(r->cmd, d->ctlr->fis + i, 16); + else + memset(r->cmd, 0xff, 16); + r->status = st; + return st; +} + +/* + * handle oob requests; + * restrict & sanitize commands + */ +static int +fisreqchk(Sfis *f, SDreq *r) +{ + uchar *c; + + if((r->ataproto & Pprotom) == Ppkt) + return SDnostatus; + if(r->clen != 16) + error("bad command length"); //error(Eio); + c = r->cmd; + if(c[0] == 0xf0){ + sigtofis(f, r->cmd); + return r->status = SDok; + } + c[0] = H2dev; + c[1] = Fiscmd; + c[7] |= Ataobs; + return SDnostatus; +} + +static int +msataio(SDreq *r) +{ + char *name; + int try, flag, task; + Ctlr *c; + Drive *d; + SDunit *u; + int (*build)(Drive*, SDreq*, void*, int); + + u = r->unit; + c = u->dev->ctlr; + d = c->drive + u->subno; + name = dnam(d); + + if(d->type != Sata) + error("not sata"); + if(r->cmd[0] == 0xf1){ + d->state = Dreset; + return r->status = SDok; + } + if((r->status = fisreqchk(d, r)) != SDnostatus) + return r->status; + build = buildfis; + if((r->ataproto & Pprotom) == Ppkt) + build = buildpkt; + + for(try = 0; try < 10; try++){ + if(lockready(d) == -1) + return SDeio; + flag = build(d, r, r->data, r->dlen); + task = gettask(d); + if(flag & Atareset && satareset(d) == -1) + setstate(d, Dreset); + qunlock(d); + if(flag & Noverdict){ + if(flag & (Timeout | Creset)) + setstate(d, Dreset); + else if(task & Eabrt<<8){ + /* assume bad cmd */ + r->status = SDeio; + return SDeio; + } + print("%s: retry\n", name); + continue; + } + if(flag & Error){ + print("%s: i/o error %.8ux\n", name, task); + r->status = SDeio; + return SDeio; + } + if(build != buildpkt) + r->rlen = r->dlen; + return sdr(r, d, SDok); + } + print("%s: bad disk\n", name); + return sdr(r, d, SDeio); +} + +static void +msinterrupt(Ureg *, void *a) +{ + Ctlr *c; + uint u, i; + static uint cnt; + + c = a; + ilock(c); + u = c->reg[Cis]; + if(u == 0){ + iunlock(c); + return; + } + c->reg[Cis] = u & ~Iclr; + if(u != Cdone && cnt++ < 15) + print("sdodin: irq %s %.8ux\n", c->sdev->ifc->name, u); + for(i = 0; i < 8; i++) + if(u & (1<drive + i); + if(u & Srsirq){ + u = c->reg[Csis]; + c->reg[Csis] = u; + for(i = 0; i < 8; i++) + if(u & 1<drive + i); + } + if(u & Cdone){ + updatedone(c); + c->reg[Cis] = Cdone; + } + iunlock(c); +} + +static char* +mc(Drive *d) +{ + char *s; + + s = ""; + if(d->drivechange) + s = "[newdrive]"; + return s; +} + +static int +newsatadrive(Drive *d) +{ + uint task; + + task = gettask(d); + if((task & 0xffff) == 0x80) + return SDretry; + setfissig(d, getsig(d)); + if(identify(d) != 0){ + dprint("%s: identify failure\n", dnam(d)); + return SDeio; + } + if(d->feat & Dpower && setfeatures(d, 0x85, 3*1000) != 0){ + d->feat &= ~Dpower; + if(satareset(d) == -1) + return SDeio; + } + if(settxmode(d, d->udma) != 0){ + dprint("%s: can't set tx mode\n", dnam(d)); + return SDeio; + } + return SDok; +} + +static void +newoaf(Drive *d, int type) +{ + uint ict, i; + uvlong sa; + Ctlr *c; + + i = d->driveno; + c = d->ctlr; + + sa = pcread(c, i, Pawwn + 0); + sa |= (uvlong)pcread(c, i, Pawwn + 4)<<32; + putbe(d->tsasaddr, sa, 8); + memmove(d->ssasaddr, d->ssasaddr, 8); + ict = pcread(c, i, Pwwn + 8); + putbe(d->ict, ict, 2); + oafis(d, d->cmd->oaf, type); +} + +static int +sasinquiry(Drive *d) +{ + SDreq r; + SDunit *u; + + u = d->unit; + memset(&r, 0, sizeof r); + r.cmd[0] = 0x12; + r.cmd[4] = 0xff; + r.clen = 6; + r.unit = u; + + return buildsas(d, &r, u->inquiry, sizeof u->inquiry); +} + +static int +sastur(Drive *d) +{ + SDreq r; + SDunit *u; + + u = d->unit; + memset(&r, 0, sizeof r); + r.clen = 6; + r.unit = u; + return buildsas(d, &r, 0, 0); +} + +static int +sasvpd(Drive *d, uchar *buf, int l) +{ + SDreq r; + SDunit *u; + + u = d->unit; + memset(&r, 0, sizeof r); + r.cmd[0] = 0x12; + r.cmd[1] = 1; + r.cmd[2] = 0x80; + r.cmd[4] = l; + r.clen = 6; + r.unit = u; + return buildsas(d, &r, buf, l); +} + +static int +sascapacity10(Drive *d, uchar *buf, int l) +{ + SDreq r; + SDunit *u; + + u = d->unit; + memset(&r, 0, sizeof r); + r.cmd[0] = 0x25; + r.clen = 10; + r.unit = u; + return buildsas(d, &r, buf, l); +} + +static int +sascapacity16(Drive *d, uchar *buf, int l) +{ + SDreq r; + SDunit *u; + + u = d->unit; + memset(&r, 0, sizeof r); + r.cmd[0] = 0x9e; + r.cmd[1] = 0x10; + r.cmd[13] = l; + r.clen = 16; + r.unit = u; + return buildsas(d, &r, buf, l); +} + +static void +frmove(char *p, uchar *c, int n) +{ + char *op, *e; + + memmove(p, c, n); + op = p; + p[n] = 0; + for(p = p + n - 1; p > op && *p == ' '; p--) + *p = 0; + e = p; + p = op; + while(*p == ' ') + p++; + memmove(op, p, n - (e - p)); +} + +static void +chkinquiry(Drive *d, uchar *c) +{ + char buf[32], buf2[32], omod[sizeof d->model]; + + memmove(omod, d->model, sizeof d->model); + frmove(buf, c + 8, 8); + frmove(buf2, c + 16, 16); + snprint(d->model, sizeof d->model, "%s %s", buf, buf2); + frmove(d->firmware, c + 23, 4); + if(memcmp(omod, d->model, sizeof omod) != 0) + d->drivechange = 1; +} + +static void +chkvpd(Drive *d, uchar *c, int n) +{ + char buf[sizeof d->serial]; + int l; + + l = c[3]; + if(l > n) + l = n; + frmove(buf, c + 4, l); + if(strcmp(buf, d->serial) != 0) + d->drivechange = 1; + memmove(d->serial, buf, sizeof buf); +} + +static int +adjcapacity(Drive *d, uvlong ns, uint nss) +{ + if(ns != 0) + ns++; + if(nss == 2352) + nss = 2048; + if(d->sectors != ns || d->secsize != nss){ + d->drivechange = 1; + d->sectors = ns; + d->secsize = nss; + } + return 0; +} + +static int +chkcapacity10(uchar *p, uvlong *ns, uint *nss) +{ + *ns = getbe(p, 4); + *nss = getbe(p + 4, 4); + return 0; +} + +static int +chkcapacity16(uchar *p, uvlong *ns, uint *nss) +{ + *ns = getbe(p, 8); + *nss = getbe(p + 8, 4); + return 0; +} + +static int +sasprobe(Drive *d) +{ + uchar buf[0x40]; + int r; + uint nss; + uvlong ns; + + if((r = sastur(d)) != 0) + return r; + if((r = sasinquiry(d)) != 0) + return r; + chkinquiry(d, d->unit->inquiry); + /* vpd 0x80 (unit serial) is not mandatory */ + if((r = sasvpd(d, buf, sizeof buf)) == 0) + chkvpd(d, buf, sizeof buf); + else if(r & (Error | Timeout)) + return r; + else{ + if(d->serial[0]) + d->drivechange = 1; + d->serial[0] = 0; + } + if((r = sascapacity10(d, buf, sizeof buf)) != 0) + return r; + chkcapacity10(buf, &ns, &nss); + if(ns == 0xffffffff){ + if((r = sascapacity16(d, buf, sizeof buf)) != 0) + return r; + chkcapacity16(buf, &ns, &nss); + } + adjcapacity(d, ns, nss); + + return 0; +} + +static int +newsasdrive(Drive *d) +{ + memset(d->cmd->rsp, 0, sizeof d->cmd->rsp); + newoaf(d, Ssp); + switch(sasprobe(d) & (Error | Noverdict | Timeout | Sense)){ + case Error: + case Timeout: + return SDeio; + case Sense: + case Noverdict: + return SDretry; + } + return SDok; +} + +static int +newdrive(Drive *d) +{ + char *t; + int r; + + memset(&d->Sfis, 0, sizeof d->Sfis); + memset(&d->Cfis, 0, sizeof d->Cfis); + qlock(d); + switch(d->type){ + case Sata: + r = newsatadrive(d); + break; + case Sas: + r = newsasdrive(d); + break; + default: + print("%s: bug: martian drive %d\n", dnam(d), d->type); + qunlock(d); + return -1; + } + t = type[d->type]; + switch(r){ + case SDok: + idprint("%s: %s %,lld sectors\n", dnam(d), t, d->sectors); + idprint(" %s %s %s %s\n", d->model, d->firmware, d->serial, mc(d)); + setstate(d, Dready); + break; + case SDeio: + idprint("%s: %s can't be initialized\n", dnam(d), t); + setstate(d, Derror); + case SDretry: + break; + } + qunlock(d); + return r; +} + +static void +statechange(Drive *d) +{ + switch(d->state){ + case Dmissing: + case Dnull: + case Doffline: + d->drivechange = 1; + d->unit->sectors = 0; + break; + case Dready: + d->wait = 0; + break; + } +} + +/* + * we don't respect running commands. botch? + */ +static void +checkdrive(Drive *d, int i) +{ + uint s; + + if(d->unit == nil) + return; + ilock(d); + s = sstatus(d); + d->wait++; + if(s & Sphyrdy) + d->lastseen = Ticks; + if(s != olds[i]){ + dprint("%s: status: %.6ux -> %.6ux: %s\n", + dnam(d), olds[i], s, dstate(d->state)); + olds[i] = s; + statechange(d); + } + switch(d->state){ + case Dnull: + case Dmissing: + if(d->type != 0 && s & Sphyrdy) + d->state = Dnew; + break; + case Dnopower: + phyreset(d); /* spinup */ + break; + case Dnew: + if(d->wait % 6 != 0) + break; + iunlock(d); + newdrive(d); + ilock(d); + break; + case Dready: + d->wait = 0; + break; + case Derror: + d->wait = 0; + d->state = Dreset; + case Dreset: + if(d->wait % 40 != 0) + break; + reset(d); + break; + case Doffline: + case Dportreset: + break; + } + iunlock(d); +} + +static void +mskproc(void*) +{ + int i; + + for(;;){ + tsleep(&up->sleep, return0, 0, Nms); + for(i = 0; i < nmsdrive; i++) + checkdrive(msdrive[i], i); + } +} + +static void +ledcfg(Ctlr *c, int port, uint cfg) +{ + uint u, r, s; + + r = Drivectl + (port>>2)*Gpiooff; + s = 15 - port & 3; + s *= 8; + u = gpread(c, r); + u &= ~(0xff << s); + u |= cfg<pci, Gpio, pcicfgr32(c->pci, Gpio) | 1<<7); + + /* + * configure a for 4hz (1/8s on and 1/8s off) + * configure b for 1hz (2/8s on and 6/8s off) + */ + l = 3 + c->ndrive >> 2; + blen = 3*24 - 1; + for(i = 0; i < l*Gpiooff; i += Gpiooff){ + gpwrite(c, Sgconf0 + i, blen*Autolen | Blinkben | Blinkaen | Sgpioen); + gpwrite(c, Sgconf1 + i, 1*Bhi | 1*Blo | 1*Ahi | 7*Alo); + gpwrite(c, Sgconf3 + i, 7<<20 | Sdoutauto); + } +} + +static void +trebuild(Ctlr *c, Drive *d, int dno, uint i) +{ + uchar bits; + + if(0 && d->led == Ibpirebuild){ + switch(i%19){ + case 0: + bits = 0; + break; + case 1: + bits = ses2led[Ibpirebuild] | Lblinka*Locled; + break; + case 3: + bits = ses2led[Ibpirebuild] | Lblinkb*Locled; + break; + } + }else + bits = ses2led[d->led]; + if(d->ledbits != bits) + ledcfg(c, dno, bits); +} + +static long +odinledr(SDunit *u, Chan *ch, void *a, long n, vlong off) +{ + Ctlr *c; + Drive *d; + + c = u->dev->ctlr; + d = c->drive + u->subno; + return ledr(d, ch, a, n, off); +} + +static long +odinledw(SDunit *u, Chan *ch, void *a, long n, vlong off) +{ + Ctlr *c; + Drive *d; + + c = u->dev->ctlr; + d = c->drive + u->subno; + return ledw(d, ch, a, n, off); +} + +/* + * this kproc can probablly go when i figure out + * how to program the manual blinker + */ +static void +ledkproc(void*) +{ + uint i, j; + Drive *d; + + for(i = 0; i < nmsdrive; i++){ + d = msdrive[i]; + d->nled = 2; /* how to know? */ + } + for(i = 0; i < nmsctlr; i++) + pcicfgw32(msctlr[i].pci, Gpio, pcicfgr32(msctlr[i].pci, Gpio) | 1<<7); + for(i = 0; i < nmsctlr; i++) + setupled(msctlr + i); + for(i = 0; ; i++){ + esleep(Nms); + for(j = 0; j < nmsdrive; j++){ + d = msdrive[j]; + trebuild(d->ctlr, d, j, i); + } + } +} + +static int +msenable(SDev *s) +{ + char buf[32]; + Ctlr *c; + static int once; + + c = s->ctlr; + ilock(c); + if(!c->enabled){ + if(once++ == 0) + kproc("odin", mskproc, 0); + pcisetbme(c->pci); + snprint(buf, sizeof buf, "%s (%s)", s->name, s->ifc->name); + intrenable(c->pci->intl, msinterrupt, c, c->pci->tbdf, buf); +// c->reg[Cis] |= Swirq1; /* force initial interrupt. */ + c->enabled = 1; + } + iunlock(c); + return 1; +} + +static int +msdisable(SDev *s) +{ + char buf[32]; + Ctlr *c; + + c = s->ctlr; + ilock(c); +// disable(c->hba); + snprint(buf, sizeof buf, "%s (%s)", s->name, s->ifc->name); + intrdisable(c->pci->intl, msinterrupt, c, c->pci->tbdf, buf); + c->enabled = 0; + iunlock(c); + return 1; +} + +static int +scsiish(Drive *d) +{ + return d->type == Sas || d->feat & Datapi; +} + +static int +msonline(SDunit *u) +{ + int r; + Ctlr *c; + Drive *d; + + c = u->dev->ctlr; + d = c->drive + u->subno; + r = 0; + + if(scsiish(d)){ + if(!d->drivechange) + return r; + r = scsionline(u); + if(r > 0) + d->drivechange = 0; + return r; + } + ilock(d); + if(d->drivechange){ + r = 2; + d->drivechange = 0; + u->sectors = d->sectors; + u->secsize = d->secsize; + } else if(d->state == Dready) + r = 1; + iunlock(d); + return r; +} + +static void +verifychk(Drive *d) +{ + int w; + + if(!up) + checkdrive(d, d->driveno); + for(w = 0; w < 12000; w += 210){ + if(d->state == Dready) + break; + if(w > 2000 && d->state != Dnew) + break; + if((sstatus(d) & Sphyrdy) == 0) + break; + if(!up) + checkdrive(d, d->driveno); + esleep(210); + } +} + +static int +msverify(SDunit *u) +{ + int chk; + Ctlr *c; + Drive *d; + static int once; + + c = u->dev->ctlr; + d = c->drive + u->subno; + ilock(c); + ilock(d); + chk = 0; + if(d->unit == nil){ + d->unit = u; + sdaddfile(u, "led", 0644, eve, odinledr, odinledw); + once++; + if(once == nmsctlr) + kproc("mvled", ledkproc, 0); + chk = 1; + } + iunlock(d); + iunlock(c); + + /* + * since devsd doesn't know much about hot-plug drives, + * we need to give detected drives a chance. + */ + if(chk){ + reset(d); + verifychk(d); + } + return 1; +} + +static uint* +map(Pcidev *p, int bar) +{ + uintptr io; + + io = p->mem[bar].bar & ~0xf; + return (uint*)vmap(io, p->mem[bar].size); +} + +/* §5.1.3 */ +static void +initmem(Ctlr *c) +{ + c->fis = smalloc(0x800 + 0x100*16); /* §6.1.9.3 */ + c->reg[Fisbase + 0] = PCIWADDR(c->fis); + c->reg[Fisbase + 1] = Pciwaddrh(c->fis); + c->reg[Cqbase + 0] = PCIWADDR(c->cq); + c->reg[Cqbase + 1] = Pciwaddrh(c->cq); + c->reg[Cqcfg] = Cqen | Noattn | nelem(c->cq) - 1; + c->reg[Dqbase + 0] = PCIWADDR(c->dq); + c->reg[Dqbase + 1] = Pciwaddrh(c->dq); + c->reg[Dqcfg] = Dqen | nelem(c->dq); + c->cl = smalloc(nelem(c->cq)*sizeof *c->cl); + c->reg[Clbase + 0] = PCIWADDR(c->cl); + c->reg[Clbase + 1] = Pciwaddrh(c->cl); + c->cmdtab = smalloc(Nctlrdrv*sizeof *c->cmdtab); +} + +/* §5.1.2 */ +static void +startup(Ctlr *c) +{ + c->reg[Gctl] |= Reset; + while(c->reg[Gctl] & Reset) + ; + initmem(c); + c->reg[Cie] = Swirq1 | 0xff*Portstop | 0xff*Portirq | Srsirq | Issstop | Cdone; + c->reg[Gctl] |= Intenable; + c->reg[Portcfg0] = Rmask*Regen | Dataunke | Rsple | Framele; + c->reg[Portcfg1] = Rmask*Regen | 0xff*Xmten | /*Cmdirq |*/ Fisen | Resetiss | Issueen; + c->reg[Csie] = ~0; + sswrite(c, 0, Pwdtimer, 0x7fffff); +} + +static void +forcetype(Ctlr*) +{ + /* + * if we want to force sas/sata, here's where to do it. + */ +} + +static void +setupcmd(Drive *d) +{ + int i; + Ctlr *c; + Cmd *cmd; + Cmdh *h; + + i = d->driveno; + c = d->ctlr; + d->cmd = c->cmdtab + i; + d->cmd->cmdh = c->cl + i; + cmd = d->cmd; + h = cmd->cmdh; + + /* prep the precomputable bits in the cmd hdr §6.1.4 */ + putle(h->ctab, Pciw64(&cmd->Ctab), sizeof h->ctab); + putle(h->oaf, Pciw64(&cmd->Oaf), sizeof h->oaf); + putle(h->statb, Pciw64(&cmd->Statb), sizeof h->statb); + putle(h->prd, Pciw64(&cmd->Aprdt), sizeof h->prd); + + /* finally, set up the wide-port participating bit */ + pcwrite(c, i, Pwidecfg, 1<driveno; + memmove(d->ssasaddr, src, 8); + u = getbe(d->ssasaddr, 8); + pcwrite(c, i, Paddr + 0, u); + pcwrite(c, i, Paddr + 4, u>>32); +} + +static SDev* +mspnp(void) +{ + int i, nunit; + Ctlr *c; + Drive *d; + Pcidev *p; + SDev **ll, *s, *s0; + static int done; + + if(done++) + return nil; + s0 = nil; + ll = &s0; + for(p = nil; (p = pcimatch(p, 0x11ab, 0x6485)) != nil; ){ + if(nmsctlr == Nctlr){ + print("sdodin: too many controllers\n"); + break; + } + c = msctlr + nmsctlr; + s = sdevs + nmsctlr; + memset(c, 0, sizeof *c); + memset(s, 0, sizeof *s); + if((c->reg = map(p, Mebar)) == 0){ + print("sdodin: bar %#p in use\n", c->reg); + continue; + } + nunit = p->did>>4 & 0xf; + s->ifc = &sdodinifc; + s->idno = 'a' + nmsctlr; + s->ctlr = c; + c->sdev = s; + c->pci = p; + c->ndrive = s->nunit = nunit; + i = pcicfgr32(p, Dctl) & ~(7<<12); + pcicfgw32(p, Dctl, i | 4<<12); + + print("#S/sd%c: odin ii sata/sas with %d ports\n", s->idno, nunit); + startup(c); + forcetype(c); + for(i = 0; i < nunit; i++){ + d = c->drive + i; + d->driveno = i; + d->sectors = 0; + d->ctlr = c; + setupcmd(d); + snprint(d->name, sizeof d->name, "odin%d.%d", nmsctlr, i); + msdrive[nmsdrive + i] = d; +// phychk(c, d); + c->reg[pis[i] + 1] = + Sync | Phyerr | Stperr | Crcerr | + Linkrx | Martianfis | Anot | Bist | Sigrx | + Phyunrdy | Martiantag | Bnot | Comw | + Portsel | Hreset | Phyidto | Phyidok | + Hresetok | Phyrdy; + } + nmsdrive += nunit; + nmsctlr++; + *ll = s; + ll = &s->next; + } + return s0; +} + +static char* +msrctlsata(Drive *d, char *p, char *e) +{ + p = seprint(p, e, "flag\t"); + p = pflag(p, e, d); + p = seprint(p, e, "udma\t%d\n", d->udma); + return p; +} + +static char* +rctldebug(char *p, char *e, Ctlr *c, Drive *d) +{ + int i; + uvlong sasid; + + i = d->driveno; + p = seprint(p, e, "sstatus\t%.8ux\n", sstatus(d)); +// p = seprint(p, e, "cis\t%.8ux %.8ux\n", c->reg[Cis], c->reg[Cie]); +// p = seprint(p, e, "gis\t%.8ux\n", c->reg[Gis]); + p = seprint(p, e, "pis\t%.8ux %.8ux\n", c->reg[pis[i]], c->reg[pis[i] + 1]); + p = seprint(p, e, "sis\t%.8ux\n", c->reg[Csis]); + p = seprint(p, e, "cqwp\t%.8ux\n", c->cq[0]); + p = seprint(p, e, "cerror\t%.8ux %.8ux\n", *(uint*)d->cmd->error, *(uint*)(d->cmd->error+4)); + p = seprint(p, e, "task\t%.8ux\n", gettask(d)); + p = seprint(p, e, "ptype\t%.8ux\n", c->reg[Ptype]); + p = seprint(p, e, "satactl\t%.8ux\n", pcread(c, i, Psatactl)); /* appears worthless */ + p = seprint(p, e, "info %.8ux %.8ux\n", pcread(c, i, Pinfo), pcread(c, i, Painfo)); + p = seprint(p, e, "physts %.8ux\n", pcread(c, i, Pphysts)); + p = seprint(p, e, "widecfg %.8ux\n", pcread(c, i, Pwidecfg)); + sasid = pcread(c, i, Pwwn + 0); + sasid |= (uvlong)pcread(c, i, Pwwn + 4)<<32; + p = seprint(p, e, "wwn %.16llux %.8ux\n", sasid, pcread(c, i, Pwwn + 8)); + sasid = pcread(c, i, Pawwn + 0); + sasid |= (uvlong)pcread(c, i, Pawwn + 4)<<32; + p = seprint(p, e, "awwn %.16llux\n", sasid); + sasid = pcread(c, i, Paddr + 0); + sasid |= (uvlong)pcread(c, i, Paddr + 4)<<32; + p = seprint(p, e, "sasid %.16llux\n", sasid); + return p; +} + +static int +msrctl(SDunit *u, char *p, int l) +{ + char *e, *op; + Ctlr *c; + Drive *d; + + if((c = u->dev->ctlr) == nil) + return 0; + d = c->drive + u->subno; + e = p + l; + op = p; + p = seprint(p, e, "state\t%s\n", dstate(d->state)); + p = seprint(p, e, "type\t%s", type[d->type]); + if(d->type == Sata) + p = seprint(p, e, " sig %.8ux", getsig(d)); + p = seprint(p, e, "\n"); + if(d->state == Dready){ + p = seprint(p, e, "model\t%s\n", d->model); + p = seprint(p, e, "serial\t%s\n", d->serial); + p = seprint(p, e, "firm\t%s\n", d->firmware); + p = seprint(p, e, "wwn\t%llux\n", d->wwn); + p = msrctlsata(d, p, e); + } + p = rctldebug(p, e, c, d); + p = seprint(p, e, "geometry %llud %lud\n", d->sectors, u->secsize); + return p - op; +} + +static void +forcestate(Drive *d, char *state) +{ + int i; + + for(i = 1; i < nelem(diskstates); i++) + if(strcmp(state, diskstates[i]) == 0) + break; + if(i == nelem(diskstates)) + error(Ebadctl); + ilock(d); + d->state = 1 << i - 1; + statechange(d); + iunlock(d); +} + +static int +mswctl(SDunit *u, Cmdbuf *cmd) +{ + char **f; + Ctlr *c; + Drive *d; + + c = u->dev->ctlr; + d = c->drive + u->subno; + f = cmd->f; + if(strcmp(f[0], "state") == 0) + forcestate(d, f[1]? f[1]: "null"); + else + cmderror(cmd, Ebadctl); + return 0; +} + +static int +mswtopctl(SDev*, Cmdbuf *cmd) +{ + char **f; + int *v; + + f = cmd->f; + v = 0; + if(strcmp(f[0], "debug") == 0) + v = &debug; + else if(strcmp(f[0], "idprint") == 0) + v = &idebug; + else if(strcmp(f[0], "aprint") == 0) + v = &adebug; + else + cmderror(cmd, Ebadctl); + if(cmd->nf == 1) + *v ^= 1; + else if(cmd->nf == 2) + *v = strcmp(f[1], "on") == 0; + else + cmderror(cmd, Ebadarg); + return 0; +} + +SDifc sdodinifc = { + "odin", + mspnp, + nil, + msenable, + msdisable, + msverify, + msonline, + msrio, + msrctl, + mswctl, + scsibio, + nil, /* probe */ + nil, /* clear */ + nil, + mswtopctl, + msataio, +}; diff --git a/sys/src/9/port/aoe.h b/sys/src/9/port/aoe.h index 6a8895ccc..3b2723a08 100644 --- a/sys/src/9/port/aoe.h +++ b/sys/src/9/port/aoe.h @@ -1,9 +1,8 @@ -/* - * ATA-over-Ethernet (AoE) protocol - */ enum { ACata, ACconfig, + ACmask, + ACres, }; enum { @@ -15,23 +14,55 @@ enum { }; enum { - AEcmd = 1, - AEarg, - AEdev, - AEcfg, - AEver, + AEunk, + AEcmd, /* bad command */ + AEarg, /* bad argument */ + AEoff, /* device offline */ + AEcfg, /* config string already set */ + AEver, /* unsupported version */ + AEres, /* target reserved */ }; enum { - Aoetype = 0x88a2, - Aoesectsz = 512, /* standard sector size */ - Aoever = 1, + /* mask commands */ + Mread = 0, + Medit, - AFerr = 1<<2, - AFrsp = 1<<3, + /* mask directives */ + MDnop = 0, + MDadd, + MDdel, - AAFwrite= 1, - AAFext = 1<<6, + /* mask errors */ + MEunk = 1, + MEbad, + MEfull, + + /* reserve / release */ + Rrread = 0, + Rrset, + Rrforce, +}; + +enum { + Aoetype = 0x88a2, + Aoesectsz = 512, + Aoemaxcfg = 1024, + + Aoehsz = 24, + Aoeatasz = 12, + Aoecfgsz = 8, + Aoerrsz = 2, + Aoemsz = 4, + Aoemdsz = 8, + + Aoever = 1, + + AFerr = 1<<2, + AFrsp = 1<<3, + + AAFwrite = 1, + AAFext = 1<<6, }; typedef struct { @@ -44,35 +75,43 @@ typedef struct { uchar minor; uchar cmd; uchar tag[4]; - uchar payload[]; } Aoehdr; -#define AOEHDRSZ offsetof(Aoehdr, payload[0]) - typedef struct { - Aoehdr; uchar aflag; uchar errfeat; uchar scnt; uchar cmdstat; uchar lba[6]; uchar res[2]; - uchar payload[]; } Aoeata; -#define AOEATASZ offsetof(Aoeata, payload[0]) - typedef struct { - Aoehdr; uchar bufcnt[2]; uchar fwver[2]; uchar scnt; uchar verccmd; uchar cslen[2]; - uchar payload[]; -} Aoeqc; +} Aoecfg; -#define AOEQCSZ offsetof(Aoeqc, payload[0]) +typedef struct { + uchar dres; + uchar dcmd; + uchar ea[Eaddrlen]; +} Aoemd; + +typedef struct { + uchar mres; + uchar mcmd; + uchar merr; + uchar mcnt; +} Aoem; + +typedef struct { + uchar rcmd; + uchar nea; + uchar ea0[]; +} Aoerr; extern char Echange[]; extern char Enotup[]; diff --git a/sys/src/9/port/devaoe.c b/sys/src/9/port/devaoe.c index 66f67f86a..1040b080a 100644 --- a/sys/src/9/port/devaoe.c +++ b/sys/src/9/port/devaoe.c @@ -1,5 +1,5 @@ /* - * © 2005-2010 coraid + * © 2005-10 coraid * aoe storage initiator */ @@ -15,6 +15,7 @@ #include "etherif.h" #include "../ip/ip.h" #include "../port/aoe.h" +#include #pragma varargck argpos eventlog 1 @@ -24,6 +25,7 @@ enum { Maxunits = 0xff, Maxframes = 128, + Maxmtu = 100000, Ndevlink = 6, Nea = 6, Nnetlink = 6, @@ -35,13 +37,10 @@ enum { #define QID(u, t) ((u)<<4 | (t)) #define Q3(l, u, t) ((l)<<8 | QID(u, t)) #define UP(d) ((d)->flag & Dup) -/* - * would like this to depend on the chan (srb). - * not possible in the current structure. - */ -#define Nofail(d, s) ((d)->flag & Dnofail) -#define MS2TK(t) ((t)/MS2HZ) +#define Ticks MACHP(0)->ticks +#define Ms2tk(t) (((t)*HZ)/1000) +#define Tk2ms(t) (((t)*1000)/HZ) enum { Qzero, @@ -67,22 +66,17 @@ enum { Qdevlinkfiles = Qdevlinkend-Qdevlinkbase, Eventlen = 256, - Nevents = 64, /* must be power of 2 */ + Nevents = 64, Fread = 0, Fwrite, Tfree = -1, Tmgmt, - /* - * round trip bounds, timeouts, in ticks. - * timeouts should be long enough that rebooting - * the coraid (which usually takes under two minutes) - * doesn't trigger a timeout. - */ - Rtmax = MS2TK(320), - Rtmin = MS2TK(20), - Maxreqticks = 4*60*HZ, /* was 45*HZ */ + /* round trip bounds, timeouts, in ticks */ + Rtmax = Ms2tk(320), + Rtmin = Ms2tk(20), + Srbtimeout = 45*HZ, Dbcnt = 1024, @@ -91,6 +85,9 @@ enum { Cwr = 0x30, Cwrext = 0x34, Cid = 0xec, + + Alloc = 0x01234567, + Free = 0x89abcdef, }; enum { @@ -104,37 +101,20 @@ enum { * to send jumbograms to that interface. */ enum { - /* sync with ahci.h */ - Dllba = 1<<0, - Dsmart = 1<<1, - Dpower = 1<<2, - Dnop = 1<<3, - Datapi = 1<<4, - Datapi16= 1<<5, - - /* aoe specific */ - Dup = 1<<6, - Djumbo = 1<<7, - Dnofail = 1<<8, + Dup = 1<<0, + Djumbo = 1<<1, + Dnofail = 1<<2, }; static char *flagname[] = { - "llba", - "smart", - "power", - "nop", - "atapi", - "atapi16", - "up", "jumbo", "nofail", }; typedef struct { - ushort flag; + uchar flag; uint lostjumbo; - int datamtu; Chan *cc; Chan *dc; @@ -148,9 +128,10 @@ typedef struct { int nea; ulong eaidx; uchar eatab[Nea][Eaddrlen]; + int datamtu; ulong npkt; ulong resent; - ushort flag; + uchar flag; ulong rttavg; ulong mintimer; @@ -159,6 +140,7 @@ typedef struct { typedef struct Srb Srb; struct Srb { Rendez; + uint state; Srb *next; ulong ticksent; ulong len; @@ -197,11 +179,11 @@ struct Aoedev { Devlink *dl; Devlink dltab[Ndevlink]; + uchar flag; ushort fwver; - ushort flag; int nopen; - int major; - int minor; + uint major; + uint minor; int unit; int lasttag; int nframes; @@ -210,6 +192,8 @@ struct Aoedev { vlong realbsize; uint maxbcnt; + uint maxmtu; + ulong lostjumbo; ushort nout; ushort maxout; ulong lastwadj; @@ -217,7 +201,7 @@ struct Aoedev { Srb *tail; Srb *inprocess; - /* magic numbers 'R' us */ + Sfis; char serial[20+1]; char firmware[8+1]; char model[40+1]; @@ -250,15 +234,14 @@ static struct { Netlink nl[Nnetlink]; } netlinks; -extern Dev aoedevtab; -static Ref units; -static Ref drivevers; -static int debug; -static int autodiscover = 1; -static int rediscover; - -char Enotup[] = "aoe device is down"; -char Echange[] = "media or partition has changed"; +extern Dev aoedevtab; +static Ref units; +static Ref drivevers; +static int debug; +static int autodiscover = 1; +static int rediscover; +extern char Enotup[] = "aoe device is down"; +extern char Echange[] = "media or partition has changed"; static Srb* srballoc(ulong sz) @@ -266,8 +249,9 @@ srballoc(ulong sz) Srb *srb; srb = malloc(sizeof *srb+sz); + srb->state = Alloc; srb->dp = srb->data = srb+1; - srb->ticksent = MACHP(0)->ticks; + srb->ticksent = Ticks; return srb; } @@ -277,20 +261,63 @@ srbkalloc(void *db, ulong) Srb *srb; srb = malloc(sizeof *srb); + srb->state = Alloc; srb->dp = srb->data = db; - srb->ticksent = MACHP(0)->ticks; + srb->ticksent = Ticks; return srb; } -#define srbfree(srb) free(srb) +static int +srbready(void *v) +{ + Srb *s; + + s = v; + return s->nout == 0 && (s->len == 0 || s->error != nil); +} static void -srberror(Srb *srb, char *s) +srbfree(Srb *srb) { - srb->error = s; - srb->nout--; - if (srb->nout == 0) + int n; + + for(n = 0; srb->state != Free; n++) + sched(); + free(srb); +} + +/* under Aoedev qlock() so setting of srb->state is safe */ +static void +srbwakeup(Srb *srb) +{ + if(srbready(srb)){ + assert(srb->state == Alloc); wakeup(srb); + srb->state = Free; + } +} + +static void +srbcleanout(Aoedev *d, Srb *srb) +{ + Srb *x, **ll; + + if(srb == d->inprocess) + d->inprocess = nil; + else + for(ll = &d->head; x = *ll; ll = &x->next){ + d->tail = x; + if(x == srb) + *ll = x->next; + } +} + +static void +srberror(Aoedev *d, Srb *srb, char *s) +{ + srbcleanout(d, srb); + srb->error = s; + srbwakeup(srb); } static void @@ -298,28 +325,25 @@ frameerror(Aoedev *d, Frame *f, char *s) { Srb *srb; - srb = f->srb; - if(f->tag == Tfree || !srb) + if(f->tag == Tfree) return; + srb = f->srb; f->srb = nil; f->tag = Tfree; /* don't get fooled by way-slow responses */ - srberror(srb, s); + if(!srb) + return; + srb->nout--; + srberror(d, srb, s); d->nout--; } static char* unitname(Aoedev *d) { - uprint("%d.%d", d->major, d->minor); + uprint("%ud.%ud", d->major, d->minor); return up->genbuf; } -static int -eventlogready(void*) -{ - return *events.rp; -} - static long eventlogread(void *a, long n) { @@ -385,13 +409,13 @@ eventlog(char *fmt, ...) static int eventcount(void) { - int n; + uint n; lock(&events); if(*events.rp == 0) n = 0; else - n = (events.wp - events.rp) & (Nevents - 1); + n = events.wp - events.rp & Nevents - 1; unlock(&events); return n/Eventlen; } @@ -401,7 +425,7 @@ tsince(int tag) { int n; - n = MACHP(0)->ticks & 0xffff; + n = Ticks & 0xffff; n -= tag & 0xffff; if(n < 0) n += 1<<16; @@ -415,7 +439,7 @@ newtag(Aoedev *d) do { t = ++d->lasttag << 16; - t |= MACHP(0)->ticks & 0xffff; + t |= Ticks & 0xffff; } while (t == Tfree || t == Tmgmt); return t; } @@ -428,7 +452,7 @@ downdev(Aoedev *d, char *err) d->flag &= ~Dup; f = d->frames; e = f + d->nframes; - for(; f < e; f->tag = Tfree, f->srb = nil, f++) + for(; f < e; f++) frameerror(d, f, Enotup); d->inprocess = nil; eventlog("%æ: removed; %s\n", d, err); @@ -490,16 +514,23 @@ pickea(Devlink *l) return l->eaidx++ % l->nea; } +/* + * would like this to depend on the chan (srb). + * not possible in the current structure. + */ +#define Nofail(d, s) (((d)->flag&Dnofail) == Dnofail) + static int hset(Aoedev *d, Frame *f, Aoehdr *h, int cmd) { int i; Devlink *l; - if(f->srb && MACHP(0)->ticks - f->srb->ticksent > Maxreqticks){ + if(f->srb) + if((long)(Ticks-f->srb->ticksent) > Srbtimeout){ eventlog("%æ: srb timeout\n", d); - if(cmd == ACata && f->srb && Nofail(d, s)) - f->srb->ticksent = MACHP(0)->ticks; + if(cmd == ACata && Nofail(d, s)) + f->srb->ticksent = Ticks; else frameerror(d, f, Etimedout); return -1; @@ -507,7 +538,7 @@ hset(Aoedev *d, Frame *f, Aoehdr *h, int cmd) l = pickdevlink(d); i = pickea(l); if(i == -1){ - if(cmd != ACata || f->srb == nil || !Nofail(d, s)) + if(!(cmd == ACata && f->srb && Nofail(d, s))) downdev(d, "resend fails; no netlink/ea"); return -1; } @@ -524,7 +555,7 @@ hset(Aoedev *d, Frame *f, Aoehdr *h, int cmd) f->dl = l; f->nl = l->nl; f->eaidx = i; - f->ticksent = MACHP(0)->ticks; + f->ticksent = Ticks; return f->tag; } @@ -534,10 +565,12 @@ resend(Aoedev *d, Frame *f) { ulong n; Aoeata *a; + Aoehdr *h; - a = (Aoeata*)f->hdr; - if(hset(d, f, a, a->cmd) == -1) + h = (Aoehdr*)f->hdr; + if(hset(d, f, h, h->cmd) == -1) return -1; + a = (Aoeata*)(f->hdr + Aoehsz); n = f->bcnt; if(n > d->maxbcnt){ n = d->maxbcnt; /* mtu mismatch (jumbo fail?) */ @@ -548,6 +581,7 @@ resend(Aoedev *d, Frame *f) f->dl->resent++; f->dl->npkt++; if(waserror()) + /* should remove the netlink */ return -1; devtab[f->nl->dc->type]->bwrite(f->nl->dc, allocfb(f), 0); poperror(); @@ -555,7 +589,7 @@ resend(Aoedev *d, Frame *f) } static void -discover(int major, int minor) +discover(uint major, uint minor) { Aoehdr *h; Block *b; @@ -582,7 +616,6 @@ discover(int major, int minor) h->minor = minor; h->cmd = ACconfig; poperror(); - /* send b down the queue */ devtab[nl->dc->type]->bwrite(nl->dc, b, 0); } } @@ -596,7 +629,7 @@ aoesweepproc(void*) { ulong i, tx, timeout, nbc; vlong starttick; - enum { Nms = 100, Nbcms = 30*1000, }; /* magic */ + enum { Nms = 100, Nbcms = 30*1000, }; uchar *ea; Aoeata *a; Aoedev *d; @@ -612,7 +645,7 @@ loop: } nbc = Nbcms/Nms; } - starttick = MACHP(0)->ticks; + starttick = Ticks; rlock(&devs); for(d = devs.d; d; d = d->next){ if(!canqlock(d)) @@ -635,13 +668,13 @@ loop: if(d->nout == d->maxout){ if(d->maxout > 1) d->maxout--; - d->lastwadj = MACHP(0)->ticks; + d->lastwadj = Ticks; } - a = (Aoeata*)f->hdr; + a = (Aoeata*)(f->hdr + Aoehsz); if(a->scnt > Dbcnt / Aoesectsz && ++f->nl->lostjumbo > (d->nframes << 1)){ ea = f->dl->eatab[f->eaidx]; - eventlog("%æ: jumbo failure on %s:%E; lba%lld\n", + eventlog("%æ: jumbo failure on %s:%E; %llud\n", d, f->nl->path, ea, f->lba); d->maxbcnt = Dbcnt; d->flag &= ~Djumbo; @@ -650,18 +683,18 @@ loop: if(tx++ == 0){ if((l->rttavg <<= 1) > Rtmax) l->rttavg = Rtmax; - eventlog("%æ: rtt %ldms\n", d, TK2MS(l->rttavg)); + eventlog("%æ: rtt %ldms\n", d, Tk2ms(l->rttavg)); } } if(d->nout == d->maxout && d->maxout < d->nframes && - TK2MS(MACHP(0)->ticks - d->lastwadj) > 10*1000){ /* more magic */ + TK2MS(Ticks-d->lastwadj) > 10*1000){ d->maxout++; - d->lastwadj = MACHP(0)->ticks; + d->lastwadj = Ticks; } qunlock(d); } runlock(&devs); - i = Nms - TK2MS(MACHP(0)->ticks - starttick); + i = Nms - TK2MS(Ticks - starttick); if(i > 0) tsleep(&up->sleep, return0, 0, i); goto loop; @@ -674,7 +707,7 @@ fmtæ(Fmt *f) Aoedev *d; d = va_arg(f->args, Aoedev*); - snprint(buf, sizeof buf, "aoe%d.%d", d->major, d->minor); + snprint(buf, sizeof buf, "aoe%ud.%ud", d->major, d->minor); return fmtstrcpy(f, buf); } @@ -683,10 +716,13 @@ static void netbind(char *path); static void aoecfg(void) { + char *p, *f[32], buf[24], ifbuf[64]; int n, i; - char *p, *f[32], buf[24]; - if((p = getconf("aoeif")) == nil || (n = tokenize(p, f, nelem(f))) < 1) + if((p = getconf("aoeif")) == nil) + return; + strncpy(ifbuf, p, sizeof buf); + if((n = tokenize(ifbuf, f, nelem(f))) < 1) return; /* goo! */ for(i = 0; i < n; i++){ @@ -736,21 +772,33 @@ aoeattach(char *spec) } static Aoedev* -unit2dev(ulong unit) +unitseq(ulong unit) { int i; Aoedev *d; - rlock(&devs); i = 0; + rlock(&devs); for(d = devs.d; d; d = d->next) - if(i++ == unit){ + if(i++ == unit) + break; + runlock(&devs); + return d; +} + +static Aoedev* +unit2dev(ulong unit) +{ + Aoedev *d; + + rlock(&devs); + for(d = devs.d; d; d = d->next) + if(d->unit == unit){ runlock(&devs); return d; } runlock(&devs); - uprint("unit lookup failure: %lux pc %#p", unit, getcallerpc(&unit)); - error(up->genbuf); + error("unit lookup failure"); return nil; } @@ -867,11 +915,9 @@ aoegen(Chan *c, char *, Dirtab *, int, int s, Dir *dp) if(s < Qtopfiles) return topgen(c, Qtopbase + s, dp); s -= Qtopfiles; - if(s >= units.ref) + if((d = unitseq(s)) == 0) return -1; - mkqid(&q, QID(s, Qunitdir), 0, QTDIR); - d = unit2dev(s); - assert(d != nil); + mkqid(&q, QID(d->unit, Qunitdir), 0, QTDIR); devdir(c, q, unitname(d), 0, eve, 0555, dp); return 1; case Qtopctl: @@ -972,6 +1018,7 @@ atarw(Aoedev *d, Frame *f) ulong bcnt; char extbit, writebit; Aoeata *ah; + Aoehdr *h; Srb *srb; extbit = 0x4; @@ -981,13 +1028,14 @@ atarw(Aoedev *d, Frame *f) bcnt = d->maxbcnt; if(bcnt > srb->len) bcnt = srb->len; - f->nhdr = AOEATASZ; + f->nhdr = Aoehsz + Aoeatasz; memset(f->hdr, 0, f->nhdr); - ah = (Aoeata*)f->hdr; - if(hset(d, f, ah, ACata) == -1) { + h = (Aoehdr*)f->hdr; + if(hset(d, f, h, ACata) == -1){ d->inprocess = nil; return; } + ah = (Aoeata*)(f->hdr + Aoehsz); f->dp = srb->dp; f->bcnt = bcnt; f->lba = srb->sector; @@ -995,7 +1043,7 @@ atarw(Aoedev *d, Frame *f) ah->scnt = bcnt / Aoesectsz; putlba(ah, f->lba); - if(d->flag & Dllba) + if(d->feat & Dllba) ah->aflag |= AAFext; else { extbit = 0; @@ -1020,13 +1068,12 @@ atarw(Aoedev *d, Frame *f) d->inprocess = nil; d->nout++; f->dl->npkt++; - if(waserror()){ - f->tag = Tfree; - d->inprocess = nil; - nexterror(); + if(waserror()) + frameerror(d, f, "write error"); + else{ + devtab[f->nl->dc->type]->bwrite(f->nl->dc, allocfb(f), 0); + poperror(); } - devtab[f->nl->dc->type]->bwrite(f->nl->dc, allocfb(f), 0); - poperror(); } static char* @@ -1074,15 +1121,6 @@ rtupdate(Devlink *l, int rtt) l->rttavg += n >> 2; } -static int -srbready(void *v) -{ - Srb *s; - - s = v; - return s->error || (!s->nout && !s->len); -} - static Frame* getframe(Aoedev *d, int tag) { @@ -1109,7 +1147,7 @@ work(Aoedev *d) { Frame *f; - while ((f = freeframe(d)) != nil) { + while(f = freeframe(d)) { if(d->inprocess == nil){ if(d->head == nil) return; @@ -1130,6 +1168,8 @@ strategy(Aoedev *d, Srb *srb) qunlock(d); nexterror(); } + if(!UP(d)) + error(Eio); srb->next = nil; if(d->tail) d->tail->next = srb; @@ -1140,8 +1180,11 @@ strategy(Aoedev *d, Srb *srb) poperror(); qunlock(d); - while(waserror()) - ; + while(waserror()){ + qlock(d); + srberror(d, srb, "interrupted"); + qunlock(d); + } sleep(srb, srbready, srb); poperror(); } @@ -1152,12 +1195,14 @@ static long rw(Aoedev *d, int write, uchar *db, long len, uvlong off) { long n, nlen, copy; - enum { Srbsz = 1<<19, }; /* magic allocation */ + enum { Srbsz = 1<<19, }; Srb *srb; if((off|len) & (Aoesectsz-1)) error("offset and length must be sector multiple.\n"); - if(off > d->bsize || len == 0) + if(!UP(d)) + error(Eio); + if(off >= d->bsize) return 0; if(off + len > d->bsize) len = d->bsize - off; @@ -1171,11 +1216,8 @@ rw(Aoedev *d, int write, uchar *db, long len, uvlong off) srbfree(srb); nexterror(); } - nlen = len; srb->write = write; - do { - if(!UP(d)) - error(Eio); + for(nlen = len; nlen; nlen -= n){ srb->sector = off / Aoesectsz; srb->dp = srb->data; n = nlen; @@ -1189,10 +1231,9 @@ rw(Aoedev *d, int write, uchar *db, long len, uvlong off) error(srb->error); if(!write && !copy) memmove(db, srb->data, n); - nlen -= n; db += n; off += n; - } while (nlen > 0); + } poperror(); srbfree(srb); return len; @@ -1209,16 +1250,14 @@ readmem(ulong off, void *dst, long n, void *src, long size) return n; } -static char * -pflag(char *s, char *e, uchar f) +static char* +aoeflag(char *s, char *e, uchar f) { - uchar i, m; + uchar i; - for(i = 0; i < 8; i++){ - m = 1 << i; - if(f & m) - s = seprint(s, e, "%s ", flagname[i]? flagname[i]: "oops"); - } + for(i = 0; i < nelem(flagname); i++) + if(f & 1 << i) + s = seprint(s, e, "%s ", flagname[i]); return seprint(s, e, "\n"); } @@ -1237,15 +1276,17 @@ pstat(Aoedev *d, char *db, int len, int off) p = seprint(p, e, "state: %s\n" "nopen: %d\n" "nout: %d\n" - "nmaxout: %d\n" "nframes: %d\n" "maxbcnt: %d\n" + "nmaxout: %d\n" "nframes: %d\n" "maxbcnt: %d [maxmtu %d]\n" "fw: %.4ux\n" "model: %s\n" "serial: %s\n" "firmware: %s\n", state, d->nopen, d->nout, - d->maxout, d->nframes, d->maxbcnt, + d->maxout, d->nframes, d->maxbcnt, d->maxmtu, d->fwver, d->model, d->serial, d->firmware); p = seprint(p, e, "flag: "); - p = pflag(p, e, d->flag); + p = pflag(p, e, d); + p[-1] = ' '; /* horrid */ + p = aoeflag(p, e, d->flag); if(p - s < len) len = p - s; @@ -1270,16 +1311,34 @@ unitread(Chan *c, void *db, long len, vlong off) case Qdata: return rw(d, Read, db, len, off); case Qconfig: - if (!UP(d)) + if(!UP(d)) error(Enotup); return readmem(off, db, len, d->config, d->nconfig); case Qident: - if (!UP(d)) + if(!UP(d)) error(Enotup); return readmem(off, db, len, d->ident, sizeof d->ident); } } +static int +getmtu(Chan *m) +{ + int n, mtu; + char buf[36]; + + mtu = 1514; + if(m == nil || waserror()) + return mtu; + n = devtab[m->type]->read(m, buf, sizeof buf - 1, 0); + poperror(); + if(n > 12){ + buf[n] = 0; + mtu = strtoul(buf + 12, 0, 0); + } + return mtu; +} + static int devlinkread(Chan *c, void *db, int len, int off) { @@ -1303,15 +1362,18 @@ devlinkread(Chan *c, void *db, int len, int off) p = seprint(p, e, "\n"); p = seprint(p, e, "npkt: %uld\n", l->npkt); p = seprint(p, e, "resent: %uld\n", l->resent); - p = seprint(p, e, "flag: "); p = pflag(p, e, l->flag); - p = seprint(p, e, "rttavg: %uld\n", TK2MS(l->rttavg)); - p = seprint(p, e, "mintimer: %uld\n", TK2MS(l->mintimer)); + p = seprint(p, e, "flag: "); + p = aoeflag(p, e, l->flag); + p = seprint(p, e, "rttavg: %uld\n", Tk2ms(l->rttavg)); + p = seprint(p, e, "mintimer: %uld\n", Tk2ms(l->mintimer)); + p = seprint(p, e, "datamtu: %d\n", l->datamtu); p = seprint(p, e, "nl path: %s\n", l->nl->path); p = seprint(p, e, "nl ea: %E\n", l->nl->ea); - p = seprint(p, e, "nl flag: "); p = pflag(p, e, l->flag); + p = seprint(p, e, "nl flag: "); + p = aoeflag(p, e, l->flag); p = seprint(p, e, "nl lostjumbo: %d\n", l->nl->lostjumbo); - p = seprint(p, e, "nl datamtu: %d\n", l->nl->datamtu); + p = seprint(p, e, "nl datamtu: %d\n", getmtu(l->nl->mtu)); if(p - s < len) len = p - s; @@ -1340,9 +1402,10 @@ topctlread(Chan *, void *db, int len, int off) continue; p = seprint(p, e, "if%d path: %s\n", i, n->path); p = seprint(p, e, "if%d ea: %E\n", i, n->ea); - p = seprint(p, e, "if%d flag: ", i); p = pflag(p, e, n->flag); + p = seprint(p, e, "if%d flag: ", i); + p = aoeflag(p, e, n->flag); p = seprint(p, e, "if%d lostjumbo: %d\n", i, n->lostjumbo); - p = seprint(p, e, "if%d datamtu: %d\n", i, n->datamtu); + p = seprint(p, e, "if%d datamtu: %d\n", i, getmtu(n->mtu)); } if(p - s < len) @@ -1381,13 +1444,14 @@ static long configwrite(Aoedev *d, void *db, long len) { char *s; - Aoeqc *ch; + Aoehdr *h; + Aoecfg *ch; Frame *f; Srb *srb; if(!UP(d)) error(Enotup); - if(len > ETHERMAXTU - AOEQCSZ) + if(len > sizeof d->config) error(Etoobig); srb = srballoc(len); s = malloc(len); @@ -1413,11 +1477,12 @@ configwrite(Aoedev *d, void *db, long len) tsleep(&up->sleep, return0, 0, 100); poperror(); } - f->nhdr = AOEQCSZ; + f->nhdr = Aoehsz + Aoecfgsz; memset(f->hdr, 0, f->nhdr); - ch = (Aoeqc*)f->hdr; - if(hset(d, f, ch, ACconfig) == -1) + h = (Aoehdr*)f->hdr; + if(hset(d, f, h, ACconfig) == -1) return 0; + ch = (Aoecfg*)(f->hdr + Aoehsz); f->srb = srb; f->dp = s; ch->verccmd = AQCfset; @@ -1456,37 +1521,45 @@ configwrite(Aoedev *d, void *db, long len) return len; } -static int getmtu(Chan*); - static int devmaxdata(Aoedev *d) { - int i, m, mtu; + int i, m, mtu, datamtu; Devlink *l; Netlink *n; mtu = 100000; + datamtu = 100000; for(i = 0; i < d->ndl; i++){ l = d->dl + i; n = l->nl; if((l->flag & Dup) == 0 || (n->flag & Dup) == 0) continue; m = getmtu(n->mtu); + if(l->datamtu < datamtu) + datamtu = l->datamtu; if(m < mtu) mtu = m; } if(mtu == 100000) - mtu = 0; - mtu -= AOEATASZ; + mtu = 1514; + mtu -= Aoehsz + Aoeatasz; + mtu -= mtu % Aoesectsz; + if(mtu > datamtu) + mtu = datamtu; return mtu; } static int -toggle(char *s, int init) +toggle(char *s, uint f, uint bit) { if(s == nil) - return init ^ 1; - return strcmp(s, "on") == 0; + f = f^bit; + else if(strcmp(s, "on") == 0) + f |= bit; + else + f &= ~bit; + return f; } static void ataident(Aoedev*); @@ -1513,7 +1586,7 @@ unitctlwrite(Aoedev *d, void *db, long n) {Jumbo, "jumbo", 0 }, {Maxbno, "maxbno", 0 }, {Mtu, "mtu", 0 }, - {Nofailf, "nofail", 0 }, + {Nofailf, "nofail", 0 }, {Setsize, "setsize", 0 }, }; @@ -1533,14 +1606,7 @@ unitctlwrite(Aoedev *d, void *db, long n) ataident(d); break; case Jumbo: - m = 0; - if(d->flag & Djumbo) - m = 1; - toggle(cb->f[1], m); - if(m) - d->flag |= Djumbo; - else - d->flag &= ~Djumbo; + d->flag = toggle(cb->f[1], d->flag, Djumbo); break; case Maxbno: case Mtu: @@ -1552,20 +1618,19 @@ unitctlwrite(Aoedev *d, void *db, long n) if(ct->index == Maxbno) m *= Aoesectsz; else{ - m -= AOEATASZ; + m -= Aoehsz + Aoeatasz; m &= ~(Aoesectsz-1); } if(m == 0 || m > maxbcnt) - cmderror(cb, "mtu out of legal range"); + cmderror(cb, "invalid mtu"); maxbcnt = m; - } + d->maxmtu = m; + } else + d->maxmtu = Maxmtu; d->maxbcnt = maxbcnt; break; case Nofailf: - if (toggle(cb->f[1], (d->flag & Dnofail) != 0)) - d->flag |= Dnofail; - else - d->flag &= ~Dnofail; + d->flag = toggle(cb->f[1], d->flag, Dnofail); break; case Setsize: bsize = d->realbsize; @@ -1578,8 +1643,6 @@ unitctlwrite(Aoedev *d, void *db, long n) } d->bsize = bsize; break; - default: - cmderror(cb, "unknown aoe control message"); } poperror(); qunlock(d); @@ -1635,7 +1698,7 @@ addnet(char *path, Chan *cc, Chan *dc, Chan *mtu, uchar *ea) e = nl + nelem(netlinks.nl); for(; nl < e && nl->cc; nl++) continue; - if (nl >= e) + if(nl == e) error("out of netlink structures"); nl->cc = cc; nl->dc = dc; @@ -1677,27 +1740,28 @@ dropunit(void) * always allocate max frames. maxout may change. */ static Aoedev* -newdev(long major, long minor, int n) +newdev(uint major, uint minor, int n) { Aoedev *d; Frame *f, *e; - d = mallocz(sizeof *d, 1); - f = mallocz(sizeof *f * Maxframes, 1); - if (!d || !f) { + d = malloc(sizeof *d); + f = malloc(sizeof *f*Maxframes); + if(!d || !f) { free(d); free(f); error("aoe device allocation failure"); } d->nframes = n; d->frames = f; - for (e = f + n; f < e; f++) + for (e = f + Maxframes; f < e; f++) f->tag = Tfree; d->maxout = n; d->major = major; d->minor = minor; d->maxbcnt = Dbcnt; d->flag = Djumbo; + d->maxmtu = Maxmtu; d->unit = newunit(); /* bzzt. inaccurate if units removed */ if(d->unit == -1){ free(d); @@ -1709,7 +1773,7 @@ newdev(long major, long minor, int n) } static Aoedev* -mm2dev(int major, int minor) +mm2dev(uint major, uint minor) { Aoedev *d; @@ -1720,13 +1784,13 @@ mm2dev(int major, int minor) return d; } runlock(&devs); - eventlog("mm2dev: %d.%d not found\n", major, minor); + eventlog("mm2dev: %ud.%ud not found\n", major, minor); return nil; } /* Find the device in our list. If not known, add it */ static Aoedev* -getdev(long major, long minor, int n) +getdev(uint major, uint minor, int n) { Aoedev *d; @@ -1740,7 +1804,7 @@ getdev(long major, long minor, int n) for(d = devs.d; d; d = d->next) if(d->major == major && d->minor == minor) break; - if (d == nil) { + if(d == nil) { d = newdev(major, minor, n); d->next = devs.d; devs.d = d; @@ -1750,53 +1814,23 @@ getdev(long major, long minor, int n) return d; } -static ushort -gbit16(void *a) -{ - uchar *i; - - i = a; - return i[1] << 8 | i[0]; -} - -static ulong -gbit32(void *a) -{ - ulong j; - uchar *i; - - i = a; - j = i[3] << 24; - j |= i[2] << 16; - j |= i[1] << 8; - j |= i[0]; - return j; -} - -static uvlong -gbit64(void *a) -{ - uchar *i; - - i = a; - return (uvlong)gbit32(i+4) << 32 | gbit32(a); -} - static void ataident(Aoedev *d) { Aoeata *a; - Block *b; + Aoehdr *h; Frame *f; f = freeframe(d); if(f == nil) return; - f->nhdr = AOEATASZ; + f->nhdr = Aoehsz + Aoeatasz; memset(f->hdr, 0, f->nhdr); - a = (Aoeata*)f->hdr; - if(hset(d, f, a, ACata) == -1) + h = (Aoehdr*)f->hdr; + if(hset(d, f, h, ACata) == -1) return; + a = (Aoeata*)(f->hdr + Aoehsz); + f->srb = srbkalloc(0, 0); a->cmdstat = Cid; /* ata 6, page 110 */ a->scnt = 1; a->lba[3] = 0xa0; @@ -1804,26 +1838,14 @@ ataident(Aoedev *d) f->dl->npkt++; f->bcnt = 512; f->dlen = 0; - b = allocfb(f); - devtab[f->nl->dc->type]->bwrite(f->nl->dc, b, 0); -} - -static int -getmtu(Chan *m) -{ - int n, mtu; - char buf[36]; - - mtu = 1514; - if(m == nil || waserror()) - return mtu; - n = devtab[m->type]->read(m, buf, sizeof buf - 1, 0); - if(n > 12){ - buf[n] = 0; - mtu = strtoul(buf + 12, 0, 0); + if(waserror()){ + srbfree(f->srb); + d->nout--; + f->tag = Tfree; + }else{ + devtab[f->nl->dc->type]->bwrite(f->nl->dc, allocfb(f), 0); + poperror(); } - poperror(); - return mtu; } static int @@ -1845,29 +1867,33 @@ newdlea(Devlink *l, uchar *ea) } static Devlink* -newdevlink(Aoedev *d, Netlink *n, Aoeqc *c) +newdevlink(Aoedev *d, Netlink *n, Aoehdr *h) { int i; + Aoecfg *c; Devlink *l; + c = (Aoecfg*)((uchar*)h + Aoehsz); for(i = 0; i < Ndevlink; i++){ l = d->dl + i; if(i == d->ndl){ d->ndl++; - newdlea(l, c->src); + newdlea(l, h->src); + l->datamtu = c->scnt*Aoesectsz; l->nl = n; l->flag |= Dup; l->mintimer = Rtmin; l->rttavg = Rtmax; return l; } - if(l->nl == n) { - newdlea(l, c->src); + if(l->nl == n){ + newdlea(l, h->src); + l->datamtu = c->scnt*Aoesectsz; l->flag |= Dup; return l; } } - eventlog("%æ: out of links: %s:%E to %E\n", d, n->path, n->ea, c->src); + eventlog("%æ: out of links: %s:%E to %E\n", d, n->path, n->ea, h->src); return 0; } @@ -1893,18 +1919,21 @@ errrsp(Block *b, char *s) static void qcfgrsp(Block *b, Netlink *nl) { - int major, cmd, cslen, blen; - unsigned n; + int cmd, cslen, blen; + uint n, major; Aoedev *d; - Aoeqc *ch; + Aoehdr *h; + Aoecfg *ch; Devlink *l; Frame *f; + Srb *srb; - ch = (Aoeqc*)b->rp; - major = nhgets(ch->major); - n = nhgetl(ch->tag); + h = (Aoehdr*)b->rp; + ch = (Aoecfg*)(b->rp + Aoehsz); + major = nhgets(h->major); + n = nhgetl(h->tag); if(n != Tmgmt){ - d = mm2dev(major, ch->minor); + d = mm2dev(major, h->minor); if(d == nil) return; qlock(d); @@ -1915,7 +1944,7 @@ qcfgrsp(Block *b, Netlink *nl) return; } cslen = nhgets(ch->cslen); - blen = BLEN(b) - AOEQCSZ; + blen = BLEN(b) - (Aoehsz + Aoecfgsz); if(cslen < blen && BLEN(b) > 60) eventlog("%æ: cfgrsp: tag %.8ux oversized %d %d\n", d, n, cslen, blen); @@ -1924,19 +1953,23 @@ qcfgrsp(Block *b, Netlink *nl) d, n, cslen, blen); cslen = blen; } - memmove(f->dp, ch + 1, cslen); - f->srb->nout--; - wakeup(f->srb); - d->nout--; + memmove(f->dp, (uchar*)ch + Aoehsz + Aoecfgsz, cslen); + srb = f->srb; + f->dp = nil; f->srb = nil; - f->tag = Tfree; + if(srb){ + srb->nout--; + srbwakeup(srb); + d->nout--; + f->tag = Tfree; + } qunlock(d); return; } cmd = ch->verccmd & 0xf; if(cmd != 0){ - eventlog("aoe%d.%d: cfgrsp: bad command %d\n", major, ch->minor, cmd); + eventlog("aoe%ud.%ud: cfgrsp: bad command %d\n", major, h->minor, cmd); return; } n = nhgets(ch->bufcnt); @@ -1944,10 +1977,10 @@ qcfgrsp(Block *b, Netlink *nl) n = Maxframes; if(waserror()){ - eventlog("getdev: %d.%d ignored: %s\n", major, ch->minor, up->errstr); + eventlog("getdev: %ud.%ud ignored: %s\n", major, h->minor, up->errstr); return; } - d = getdev(major, ch->minor, n); + d = getdev(major, h->minor, n); poperror(); if(d == 0) return; @@ -1960,22 +1993,24 @@ qcfgrsp(Block *b, Netlink *nl) nexterror(); } - l = newdevlink(d, nl, ch); /* add this interface. */ + l = newdevlink(d, nl, h); /* add this interface. */ d->fwver = nhgets(ch->fwver); n = nhgets(ch->cslen); if(n > sizeof d->config) n = sizeof d->config; d->nconfig = n; - memmove(d->config, ch + 1, n); - if(l != 0 && d->flag & Djumbo){ - n = getmtu(nl->mtu) - AOEATASZ; - n /= Aoesectsz; - if(n > ch->scnt) - n = ch->scnt; - n = n? n * Aoesectsz: Dbcnt; + memmove(d->config, (uchar*)ch + Aoehsz + Aoecfgsz, n); + + /* manually set mtu may be reset lower if conditions warrant */ + if(l){ + n = devmaxdata(d); + if((d->flag & Djumbo) == 0) + n = Dbcnt; + if(n > d->maxmtu) + n = d->maxmtu; if(n != d->maxbcnt){ - eventlog("%æ: setting %d byte data frames on %s:%E\n", + eventlog("%æ: setting %d byte mtu on %s:%E\n", d, n, nl->path, nl->ea); d->maxbcnt = n; } @@ -1986,59 +2021,18 @@ qcfgrsp(Block *b, Netlink *nl) qunlock(d); } -void -aoeidmove(char *p, ushort *u, unsigned n) -{ - int i; - char *op, *e, *s; - - op = p; - /* - * the ushort `*u' is sometimes not aligned on a short boundary, - * so dereferencing u[i] causes an alignment exception on - * some machines. - */ - s = (char *)u; - for(i = 0; i < n; i += 2){ - *p++ = s[i + 1]; - *p++ = s[i]; - } - *p = 0; - while(p > op && *--p == ' ') - *p = 0; - e = p; - p = op; - while(*p == ' ') - p++; - memmove(op, p, n - (e - p)); -} - static vlong aoeidentify(Aoedev *d, ushort *id) { - int i; vlong s; - d->flag &= ~(Dllba|Dpower|Dsmart|Dnop|Dup); - - i = gbit16(id+83) | gbit16(id+86); - if(i & (1<<10)){ - d->flag |= Dllba; - s = gbit64(id+100); - }else - s = gbit32(id+60); - - i = gbit16(id+83); - if((i>>14) == 1) { - if(i & (1<<3)) - d->flag |= Dpower; - i = gbit16(id+82); - if(i & 1) - d->flag |= Dsmart; - if(i & (1<<14)) - d->flag |= Dnop; + s = idfeat(d, id); + if(s == -1) + return -1; + if((d->feat&Dlba) == 0){ + dprint("%æ: no lba support\n", d); + return -1; } -// eventlog("%æ up\n", d); d->flag |= Dup; memmove(d->ident, id, sizeof d->ident); return s; @@ -2064,13 +2058,14 @@ identify(Aoedev *d, ushort *id) osectors = d->realbsize; memmove(oserial, d->serial, sizeof d->serial); - aoeidmove(d->serial, id+10, 20); - aoeidmove(d->firmware, id+23, 8); - aoeidmove(d->model, id+27, 40); + idmove(d->serial, id+10, 20); + idmove(d->firmware, id+23, 8); + idmove(d->model, id+27, 40); + /* idss() */ + /* d->wwn = idwwn(d, id); */ s *= Aoesectsz; - if((osectors == 0 || osectors != s) && - memcmp(oserial, d->serial, sizeof oserial) != 0){ + if(osectors != s || memcmp(oserial, d->serial, sizeof oserial)){ d->bsize = s; d->realbsize = s; // d->mediachange = 1; @@ -2082,31 +2077,33 @@ identify(Aoedev *d, ushort *id) static void atarsp(Block *b) { - unsigned n; - short major; + uint n; + ushort major; Aoeata *ahin, *ahout; + Aoehdr *h; Aoedev *d; Frame *f; Srb *srb; - ahin = (Aoeata*)b->rp; - major = nhgets(ahin->major); - d = mm2dev(major, ahin->minor); + h = (Aoehdr*)b->rp; + major = nhgets(h->major); + d = mm2dev(major, h->minor); if(d == nil) return; + ahin = (Aoeata*)(b->rp + Aoehsz); qlock(d); if(waserror()){ qunlock(d); nexterror(); } - n = nhgetl(ahin->tag); + n = nhgetl(h->tag); f = getframe(d, n); if(f == nil){ dprint("%æ: unexpected response; tag %ux\n", d, n); goto bail; } rtupdate(f->dl, tsince(f->tag)); - ahout = (Aoeata*)f->hdr; + ahout = (Aoeata*)(f->hdr + Aoehsz); srb = f->srb; if(ahin->cmdstat & 0xa9){ @@ -2119,12 +2116,12 @@ atarsp(Block *b) switch(ahout->cmdstat){ case Crd: case Crdext: - if(BLEN(b) - AOEATASZ < n){ - eventlog("%æ: runt read blen %ld expect %d\n", + if(BLEN(b) - (Aoehsz + Aoeatasz) != n){ + eventlog("%æ: misread blen %ld expect %d\n", d, BLEN(b), n); goto bail; } - memmove(f->dp, (uchar *)ahin + AOEATASZ, n); + memmove(f->dp, b->rp + Aoehsz + Aoeatasz, n); case Cwr: case Cwrext: if(n > Dbcnt) @@ -2137,12 +2134,14 @@ atarsp(Block *b) } break; case Cid: - if(BLEN(b) - AOEATASZ < 512){ + if(BLEN(b) - (Aoehsz + Aoeatasz) < 512){ eventlog("%æ: runt identify blen %ld expect %d\n", - d, BLEN(b), n); + d, BLEN(b), 512 + Aoehsz + Aoeatasz); goto bail; } - identify(d, (ushort*)((uchar *)ahin + AOEATASZ)); + identify(d, (ushort*)(b->rp + Aoehsz + Aoeatasz)); + free(srb); /* BOTCH */ + srb = nil; break; default: eventlog("%æ: unknown ata command %.2ux \n", @@ -2150,9 +2149,11 @@ atarsp(Block *b) } } - if(srb && --srb->nout == 0 && srb->len == 0) - wakeup(srb); f->srb = nil; + if(srb){ + srb->nout--; + srbwakeup(srb); + } f->tag = Tfree; d->nout--; @@ -2177,7 +2178,7 @@ netrdaoeproc(void *v) kstrcpy(name, nl->path, Maxpath); if(waserror()){ - eventlog("netrdaoe exiting: %s\n", up->errstr); + eventlog("netrdaoe@%s: exiting: %s\n", name, up->errstr); netlinks.reader[idx] = 0; wakeup(netlinks.rendez + idx); pexit(up->errstr, 1); @@ -2185,38 +2186,27 @@ netrdaoeproc(void *v) if(autodiscover) discover(0xffff, 0xff); for (;;) { - if(!(nl->flag & Dup)) { - uprint("%s: netlink is down", name); - error(up->genbuf); - } - if (nl->dc == nil) + if((nl->flag & Dup) == 0) + error("netlink is down"); + if(nl->dc == nil) panic("netrdaoe: nl->dc == nil"); b = devtab[nl->dc->type]->bread(nl->dc, 1<<16, 0); - if(b == nil) { - uprint("%s: nil read from network", name); - error(up->genbuf); - } + if(b == nil) + error("network read"); h = (Aoehdr*)b->rp; if(h->verflag & AFrsp) if(s = aoeerror(h)){ - eventlog("%s: %s\n", nl->path, up->errstr); + eventlog("%s: %s\n", nl->path, s); errrsp(b, s); - }else - switch(h->cmd){ - case ACata: - atarsp(b); - break; - case ACconfig: - qcfgrsp(b, nl); - break; - default: - if((h->cmd & 0xf0) == 0){ - eventlog("%s: unknown cmd %d\n", - nl->path, h->cmd); - errrsp(b, "unknown command"); - } - break; - } + }else if(h->cmd == ACata) + atarsp(b); + else if(h->cmd == ACconfig) + qcfgrsp(b, nl); + else if((h->cmd & 0xf0) == 0){ + eventlog("%s: unknown cmd %d\n", + nl->path, h->cmd); + errrsp(b, "unknown command"); + } freeb(b); } } @@ -2234,7 +2224,7 @@ getaddr(char *path, uchar *ea) cclose(c); nexterror(); } - if (c == nil) + if(c == nil) panic("æ: getaddr: c == nil"); n = devtab[c->type]->read(c, buf, sizeof buf-1, 0); poperror(); @@ -2252,7 +2242,7 @@ netbind(char *path) Chan *dc, *cc, *mtu; Netlink *nl; - snprint(addr, sizeof addr, "%s!%#x", path, Aoetype); + snprint(addr, sizeof addr, "%s!0x%x", path, Aoetype); dc = chandial(addr, nil, nil, &cc); snprint(addr, sizeof addr, "%s/mtu", path); if(waserror()) @@ -2302,7 +2292,7 @@ netunbind(char *path) if(n->dc && strcmp(n->path, path) == 0) break; unlock(&netlinks); - if (n >= e) + if(n == e) error("device not bound"); /* @@ -2371,7 +2361,7 @@ netunbind(char *path) wlock(&devs); for(p = d = devs.d; d; d = next){ next = d->next; - if(d->ndl > 0) { + if(d->ndl > 0){ p = d; continue; } @@ -2392,7 +2382,36 @@ netunbind(char *path) } static void -removeaoedev(Aoedev *d) +strtoss(char *f, uint *shelf, uint *slot) +{ + char *s; + + *shelf = 0xffff; + *slot = 0xff; + if(!f) + return; + *shelf = strtol(f, &s, 0); + if(s == f || *shelf > 0xffff) + error("bad shelf"); + f = s; + if(*f++ == '.'){ + *slot = strtol(f, &s, 0); + if(s == f || *slot > 0xff) + error("bad slot"); + } +} + +static void +discoverstr(char *f) +{ + uint shelf, slot; + + strtoss(f, &shelf, &slot); + discover(shelf, slot); +} + +static void +removedev(Aoedev *d) { int i; Aoedev *p; @@ -2400,9 +2419,9 @@ removeaoedev(Aoedev *d) wlock(&devs); p = 0; if(d != devs.d) - for(p = devs.d; p; p = p->next) - if(p->next == d) - break; + for(p = devs.d; p; p = p->next) + if(p->next == d) + break; qlock(d); d->flag &= ~Dup; newvers(d); @@ -2421,60 +2440,45 @@ removeaoedev(Aoedev *d) wunlock(&devs); } -static void -removedev(char *name) -{ - Aoedev *d, *p; - - wlock(&devs); - for(p = d = devs.d; d; p = d, d = d->next) - if(strcmp(name, unitname(d)) == 0) { - wunlock(&devs); - removeaoedev(p); - return; - } - wunlock(&devs); - error("device not bound"); -} - -static void -discoverstr(char *f) -{ - ushort shelf, slot; - ulong sh; - char *s; - - if(f == 0){ - discover(0xffff, 0xff); - return; - } - - shelf = sh = strtol(f, &s, 0); - if(s == f || sh > 0xffff) - error("bad shelf"); - f = s; - if(*f++ == '.'){ - slot = strtol(f, &s, 0); - if(s == f || slot > 0xff) - error("bad shelf"); - }else - slot = 0xff; - discover(shelf, slot); -} - static void aoeremove(Chan *c) { switch(TYPE(c->qid)){ default: + case Qzero: + case Qtopdir: + case Qtoplog: + case Qtopctl: + case Qctl: + case Qdata: + case Qconfig: + case Qident: error(Eperm); case Qunitdir: - removeaoedev(unit2dev(UNIT(c->qid))); + removedev(unit2dev(UNIT(c->qid))); break; } } +static void +removestr(char *f) +{ + uint shelf, slot; + Aoedev *d; + + strtoss(f, &shelf, &slot); + wlock(&devs); + for(d = devs.d; d; d = d->next) + if(shelf == d->major && slot == d->minor){ + wunlock(&devs); /* BOTCH */ + removedev(d); + return; + } + wunlock(&devs); + error("device not bound"); +} + static long topctlwrite(void *db, long n) { @@ -2509,28 +2513,26 @@ topctlwrite(void *db, long n) f = cb->f[1]; switch(ct->index){ case Autodiscover: - autodiscover = toggle(f, autodiscover); + autodiscover = toggle(f, autodiscover, 1); break; case Bind: netbind(f); break; case Debug: - debug = toggle(f, debug); + debug = toggle(f, debug, 1); break; case Discover: discoverstr(f); break; case Rediscover: - rediscover = toggle(f, rediscover); + rediscover = toggle(f, rediscover, 1); break; case Remove: - removedev(f); + removestr(f); /* depricated */ break; case Unbind: netunbind(f); break; - default: - cmderror(cb, "unknown aoe control message"); } poperror(); free(cb); diff --git a/sys/src/9/port/devsd.c b/sys/src/9/port/devsd.c index 2753f2bbd..38b10f6a9 100644 --- a/sys/src/9/port/devsd.c +++ b/sys/src/9/port/devsd.c @@ -15,7 +15,9 @@ extern Dev sddevtab; extern SDifc* sdifc[]; -static char Echange[] = "media or partition has changed"; +static char Echange[] = "media or partition has changed"; +static char Enoata[] = "raw ata commands not supported"; +static char Enoscsi[] = "raw scsi commands not supported"; static char devletters[] = "0123456789" "abcdefghijklmnopqrstuvwxyz" @@ -23,6 +25,11 @@ static char devletters[] = "0123456789" static SDev *devs[sizeof devletters-1]; static QLock devslock; +static SDunit topctlunit; + +enum { + Ahdrsz = 2, +}; enum { Rawcmd, @@ -40,6 +47,7 @@ enum { Qctl = Qunitbase, Qraw, Qpart, + Qextra, TypeLOG = 4, NType = (1<unitflg[subno] = 1; - snprint(buf, sizeof(buf), "%s%d", sdev->name, subno); + snprint(buf, sizeof buf, "%s%x", sdev->name, subno); kstrdup(&unit->name, buf); kstrdup(&unit->user, eve); unit->perm = 0555; @@ -323,15 +331,14 @@ static void sdreset(void) { int i; - SDev *sdev; /* * Probe all known controller types and register any devices found. */ for(i = 0; sdifc[i] != nil; i++){ - if(sdifc[i]->pnp == nil || (sdev = sdifc[i]->pnp()) == nil) + if(sdifc[i]->pnp == nil) continue; - sdadddevs(sdev); + sdadddevs(sdifc[i]->pnp()); } } @@ -344,8 +351,8 @@ sdadddevs(SDev *sdev) for(; sdev; sdev=next){ next = sdev->next; - sdev->unit = (SDunit**)malloc(sdev->nunit * sizeof(SDunit*)); - sdev->unitflg = (int*)malloc(sdev->nunit * sizeof(int)); + sdev->unit = malloc(sdev->nunit * sizeof(SDunit*)); + sdev->unitflg = malloc(sdev->nunit * sizeof(int)); if(sdev->unit == nil || sdev->unitflg == nil){ print("sdadddevs: out of memory\n"); giveup: @@ -392,11 +399,12 @@ sd2gen(Chan* c, int i, Dir* dp) { Qid q; uvlong l; + SDfile *e; SDpart *pp; SDperm *perm; SDunit *unit; SDev *sdev; - int rv; + int rv, t; sdev = sdgetdev(DEV(c->qid)); assert(sdev); @@ -438,6 +446,18 @@ sd2gen(Chan* c, int i, Dir* dp) devdir(c, q, pp->name, l, pp->user, pp->perm, dp); rv = 1; break; + case Qextra: + t = PART(c->qid); + if(t >= unit->nefile) + break; + mkqid(&q, QID(DEV(c->qid), UNIT(c->qid), PART(c->qid), Qextra), + unit->vers, QTFILE); + e = unit->efile + t; + if(emptystr(e->user)) + kstrdup(&e->user, eve); + devdir(c, q, e->name, 0, e->user, e->perm, dp); + rv = 1; + break; } decref(&sdev->r); @@ -448,16 +468,45 @@ static int sd1gen(Chan* c, int i, Dir* dp) { Qid q; + SDperm *p; switch(i){ case Qtopctl: mkqid(&q, QID(0, 0, 0, Qtopctl), 0, QTFILE); - devdir(c, q, "sdctl", 0, eve, 0640, dp); + qlock(&topctlunit.ctl); + p = &topctlunit.ctlperm; + if(p->user == nil || p->user[0] == 0){ + kstrdup(&p->name, "sdctl"); + kstrdup(&p->user, eve); + p->perm = 0640; + } + devdir(c, q, p->name, 0, p->user, p->perm, dp); + qunlock(&topctlunit.ctl); return 1; } return -1; } +static int +efilegen(Chan *c, SDunit *unit, int i, Dir *dp) +{ + Qid q; + SDfile *e; + + i -= SDnpart; + if(unit->nefile == 0 || i >= unit->nefile) + return -1; + if(i < 0) + return 0; + e = unit->efile + i; + if(emptystr(e->user)) + kstrdup(&e->user, eve); + mkqid(&q, QID(DEV(c->qid), UNIT(c->qid), i, Qextra), + unit->vers, QTFILE); + devdir(c, q, e->name, 0, e->user, e->perm, dp); + return 1; +} + static int sdgen(Chan* c, char*, Dirtab*, int, int s, Dir* dp) { @@ -553,17 +602,18 @@ sdgen(Chan* c, char*, Dirtab*, int, int s, Dir* dp) } i -= Qpart; if(unit->part == nil || i >= unit->npart){ + r = efilegen(c, unit, i, dp); qunlock(&unit->ctl); decref(&sdev->r); - break; + return r; } pp = &unit->part[i]; - if(!pp->valid){ + if(!pp->valid || unit->sectors == 0){ qunlock(&unit->ctl); decref(&sdev->r); return 0; } - l = (pp->end - pp->start) * unit->secsize; + l = (pp->end - pp->start) * (uvlong)unit->secsize; mkqid(&q, QID(DEV(c->qid), UNIT(c->qid), i, Qpart), unit->vers+pp->vers, QTFILE); if(emptystr(pp->user)) @@ -575,6 +625,7 @@ sdgen(Chan* c, char*, Dirtab*, int, int s, Dir* dp) case Qraw: case Qctl: case Qpart: + case Qextra: if((sdev = sdgetdev(DEV(c->qid))) == nil){ devdir(c, q, "unavailable", 0, eve, 0, dp); return 1; @@ -715,10 +766,12 @@ sdclose(Chan* c) } } +#define iskaddr(a) ((uintptr)(a) > KZERO) + static long sdbio(Chan* c, int write, char* a, long len, uvlong off) { - int nchange; + int nchange, hard, allocd; long l; uchar *b; SDpart *pp; @@ -785,21 +838,30 @@ sdbio(Chan* c, int write, char* a, long len, uvlong off) poperror(); } - b = sdmalloc(nb*unit->secsize); - if(b == nil) - error(Enomem); + offset = off%unit->secsize; + if(offset+len > nb*unit->secsize) + len = nb*unit->secsize - offset; + hard = offset || write && len%unit->secsize; + + if(iskaddr(a) && !hard) { + b = (uchar*)a; + allocd = 0; + }else{ + b = sdmalloc(nb*unit->secsize); + if(b == nil) + error(Enomem); + allocd = 1; + } if(waserror()){ - sdfree(b); + if(allocd) + sdfree(b); if(!(unit->inquiry[1] & 0x80)) decref(&sdev->r); /* gadverdamme! */ nexterror(); } - offset = off%unit->secsize; - if(offset+len > nb*unit->secsize) - len = nb*unit->secsize - offset; if(write){ - if(offset || (len%unit->secsize)){ + if(hard){ l = unit->dev->ifc->bio(unit, 0, 0, b, nb, bno); if(l < 0) error(Eio); @@ -810,7 +872,8 @@ sdbio(Chan* c, int write, char* a, long len, uvlong off) len = l; } } - memmove(b+offset, a, len); + if(allocd) + memmove(b+offset, a, len); l = unit->dev->ifc->bio(unit, 0, 1, b, nb, bno); if(l < 0) error(Eio); @@ -827,9 +890,11 @@ sdbio(Chan* c, int write, char* a, long len, uvlong off) len = 0; else if(len > l - offset) len = l - offset; - memmove(a, b+offset, len); + if(allocd) + memmove(a, b+offset, len); } - sdfree(b); + if(allocd) + sdfree(b); poperror(); if(unit->inquiry[1] & 0x80){ @@ -844,41 +909,69 @@ sdbio(Chan* c, int write, char* a, long len, uvlong off) static long sdrio(SDreq* r, void* a, long n) { + char *errstr; + int rv; void *data; + SDunit *u; + int (*f)(SDreq*); if(n >= SDmaxio || n < 0) error(Etoobig); + u = r->unit; + if(u->haversense && r->cmd[0] == 0x03){ + u->haversense = 0; + r->rlen = sizeof u->rsense; + if(r->rlen > n) + r->rlen = n; + memmove(a, u->rsense, r->rlen); + r->status = SDok; + return r->rlen; + } data = nil; - if(n){ - if((data = sdmalloc(n)) == nil) - error(Enomem); - if(r->write) - memmove(data, a, n); - } - r->data = data; - r->dlen = n; - + if(n > 0 && (data = sdmalloc(n)) == nil) + error(Enomem); if(waserror()){ sdfree(data); r->data = nil; nexterror(); } + if(r->write && n > 0) + memmove(data, a, n); + r->data = data; + r->dlen = n; - if(r->unit->dev->ifc->rio(r) != SDok) + if(r->proto == SData){ + f = u->dev->ifc->ataio; + errstr = Enoata; + }else{ + f = u->dev->ifc->rio; + errstr = Enoscsi; + } + if(f == nil) + error(errstr); + rv = f(r); + if(r->flags & SDvalidsense){ + memmove(u->rsense, r->sense, sizeof u->rsense); + u->haversense = 1; + } + if(rv != SDok) error(Eio); if(!r->write && r->rlen > 0) memmove(a, data, r->rlen); + poperror(); sdfree(data); r->data = nil; - poperror(); return r->rlen; } /* * SCSI simulation for non-SCSI devices + * + * see /sys/src/cmd/scuzz/sense.c for information on key. + * see /sys/lib/scsicodes for asc:ascq codes */ int sdsetsense(SDreq *r, int status, int key, int asc, int ascq) @@ -908,36 +1001,7 @@ sdsetsense(SDreq *r, int status, int key, int asc, int ascq) } int -sdmodesense(SDreq *r, uchar *cmd, void *info, int ilen) -{ - int len; - uchar *data; - - /* - * Fake a vendor-specific request with page code 0, - * return the drive info. - */ - if((cmd[2] & 0x3F) != 0 && (cmd[2] & 0x3F) != 0x3F) - return sdsetsense(r, SDcheck, 0x05, 0x24, 0); - len = (cmd[7]<<8)|cmd[8]; - if(len == 0) - return SDok; - if(len < 8+ilen) - return sdsetsense(r, SDcheck, 0x05, 0x1A, 0); - if(r->data == nil || r->dlen < len) - return sdsetsense(r, SDcheck, 0x05, 0x20, 1); - data = r->data; - memset(data, 0, 8); - data[0] = ilen>>8; - data[1] = ilen; - if(ilen) - memmove(data+8, info, ilen); - r->rlen = 8+ilen; - return sdsetsense(r, SDok, 0, 0, 0); -} - -int -sdfakescsi(SDreq *r, void *info, int ilen) +sdfakescsi(SDreq *r) { uchar *cmd, *p; uvlong len; @@ -947,25 +1011,6 @@ sdfakescsi(SDreq *r, void *info, int ilen) r->rlen = 0; unit = r->unit; - /* - * Rewrite read(6)/write(6) into read(10)/write(10). - */ - switch(cmd[0]){ - case 0x08: /* read */ - case 0x0A: /* write */ - cmd[9] = 0; - cmd[8] = cmd[4]; - cmd[7] = 0; - cmd[6] = 0; - cmd[5] = cmd[3]; - cmd[4] = cmd[2]; - cmd[3] = cmd[1] & 0x0F; - cmd[2] = 0; - cmd[1] &= 0xE0; - cmd[0] |= 0x20; - break; - } - /* * Map SCSI commands into ATA commands for discs. * Fail any command with a LUN except INQUIRY which @@ -1018,13 +1063,15 @@ sdfakescsi(SDreq *r, void *info, int ilen) /* * Read capacity returns the LBA of the last sector. */ - len = unit->sectors - 1; + len = unit->sectors; + if(len > 0) + len--; p = r->data; *p++ = len>>24; *p++ = len>>16; *p++ = len>>8; *p++ = len; - len = 512; + len = unit->secsize; *p++ = len>>24; *p++ = len>>16; *p++ = len>>8; @@ -1040,7 +1087,9 @@ sdfakescsi(SDreq *r, void *info, int ilen) /* * Read capcity returns the LBA of the last sector. */ - len = unit->sectors - 1; + len = unit->sectors; + if(len > 0) + len--; p = r->data; *p++ = len>>56; *p++ = len>>48; @@ -1050,32 +1099,124 @@ sdfakescsi(SDreq *r, void *info, int ilen) *p++ = len>>16; *p++ = len>>8; *p++ = len; - len = 512; + len = unit->secsize; *p++ = len>>24; *p++ = len>>16; *p++ = len>>8; *p++ = len; r->rlen = p - (uchar*)r->data; return sdsetsense(r, SDok, 0, 0, 0); - - case 0x5A: /* mode sense */ - return sdmodesense(r, cmd, info, ilen); - - case 0x28: /* read */ - case 0x2A: /* write */ + case 0x08: /* read6 */ + case 0x0a: /* write6 */ + case 0x28: /* read10 */ + case 0x2a: /* write10 */ + case 0xa8: /* read12 */ + case 0xaa: /* write12 */ case 0x88: /* read16 */ case 0x8a: /* write16 */ return SDnostatus; } } +int +sdfakescsirw(SDreq *r, uvlong *llba, int *nsec, int *rwp) +{ + uchar *c; + int rw, count; + uvlong lba; + + c = r->cmd; + rw = SDread; + if((c[0] & 0xf) == 0xa) + rw = SDwrite; + switch(c[0]){ + case 0x08: /* read6 */ + case 0x0a: + lba = (c[1] & 0xf)<<16 | c[2]<<8 | c[3]; + count = c[4]; + break; + case 0x28: /* read10 */ + case 0x2a: + lba = c[2]<<24 | c[3]<<16 | c[4]<<8 | c[5]; + count = c[7]<<8 | c[8]; + break; + case 0xa8: /* read12 */ + case 0xaa: + lba = c[2]<<24 | c[3]<<16 | c[4]<<8 | c[5]; + count = c[6]<<24 | c[7]<<16 | c[8]<<8 | c[9]; + break; + case 0x88: /* read16 */ + case 0x8a: + /* ata commands only go to 48-bit lba */ + if(c[2] || c[3]) + return sdsetsense(r, SDcheck, 3, 0xc, 2); + lba = (uvlong)c[4]<<40 | (uvlong)c[5]<<32; + lba |= c[6]<<24 | c[7]<<16 | c[8]<<8 | c[9]; + count = c[10]<<24 | c[11]<<16 | c[12]<<8 | c[13]; + break; + default: + print("%s: bad cmd 0x%.2ux\n", r->unit->name, c[0]); + r->status = sdsetsense(r, SDcheck, 0x05, 0x20, 0); + return SDcheck; + } + if(r->data == nil) + return SDok; + if(r->dlen < count * r->unit->secsize) + count = r->dlen/r->unit->secsize; + if(rwp) + *rwp = rw; + *llba = lba; + *nsec = count; + return SDnostatus; +} + +static long +extrarw(int write, Chan *c, void *a, long n, vlong off) +{ + int i; + SDrw *f; + SDev *sdev; + SDunit *unit; + + sdev = sdgetdev(DEV(c->qid)); + if(sdev == nil) + error(Enonexist); + if(waserror()){ + decref(&sdev->r); + nexterror(); + } + unit = sdev->unit[UNIT(c->qid)]; + if(unit->vers != c->qid.vers) + error(Echange); + unit = sdev->unit[UNIT(c->qid)]; + i = PART(c->qid); + if(i >= unit->nefile) + error(Enonexist); + f = unit->efile[i].r; + if(write) + f = unit->efile[i].w; + if(i >= unit->nefile || f == nil) + error(Eperm); + n = f(unit, c, a, n, off); + poperror(); + decref(&sdev->r); + return n; +} + +static char* +deftopctl(SDev *s, char *p, char *e) +{ + return seprint(p, e, "sd%c %s %d units\n", s->idno, s->ifc->name, s->nunit); +} + static long sdread(Chan *c, void *a, long n, vlong off) { char *p, *e, *buf; - SDpart *pp; - SDunit *unit; SDev *sdev; + SDpart *pp; + SDreq *r; + SDunit *unit; ulong offset; int i, l, m, status; @@ -1093,6 +1234,8 @@ sdread(Chan *c, void *a, long n, vlong off) sdev = devs[i]; if(sdev && sdev->ifc->rtopctl) p = sdev->ifc->rtopctl(sdev, p, e); + else if(sdev) + p = deftopctl(sdev, p, e); } qunlock(&devslock); n = readstr(off, a, n, buf); @@ -1157,23 +1300,38 @@ sdread(Chan *c, void *a, long n, vlong off) } if(unit->state == Rawdata){ unit->state = Rawstatus; - i = sdrio(unit->req, a, n); + r = unit->req; + r->timeout = 0; + i = sdrio(r, a, n); } else if(unit->state == Rawstatus){ - status = unit->req->status; - unit->state = Rawcmd; - free(unit->req); + r = unit->req; unit->req = nil; - i = readnum(0, a, n, status, NUMSIZE); + unit->state = Rawcmd; + status = r->status; + if(r->proto == SData){ + p = a; + i = 16 + Ahdrsz; + if(n < i) + i = n; + if(i > 0) + p[0] = status; + if(i > Ahdrsz) + memmove(p + Ahdrsz, r->cmd, i - Ahdrsz); + }else + i = readnum(0, a, n, status, NUMSIZE); + free(r); } else i = 0; + poperror(); qunlock(&unit->raw); decref(&sdev->r); - poperror(); return i; case Qpart: return sdbio(c, 0, a, n, off); + case Qextra: + return extrarw(0, c, a, n, off); } } @@ -1183,7 +1341,8 @@ static long sdwrite(Chan* c, void* a, long n, vlong off) { char *f0; - int i; + int i, atacdb, proto, ataproto; + uchar *u; uvlong end, start; Cmdbuf *cb; SDifc *ifc; @@ -1250,7 +1409,7 @@ sdwrite(Chan* c, void* a, long n, vlong off) error(Ebadctl); poperror(); poperror(); - if (sdev) + if(sdev) decref(&sdev->r); free(cb); break; @@ -1299,6 +1458,9 @@ sdwrite(Chan* c, void* a, long n, vlong off) break; case Qraw: + proto = SDcdb; + ataproto = 0; + atacdb = 0; sdev = sdgetdev(DEV(c->qid)); if(sdev == nil) error(Enonexist); @@ -1311,18 +1473,34 @@ sdwrite(Chan* c, void* a, long n, vlong off) } switch(unit->state){ case Rawcmd: + /* sneaky ata commands */ + u = a; + if(n > 1 && *u == 0xff){ + proto = SData; + ataproto = u[1]; + a = u + 2; + atacdb = Ahdrsz; + n -= Ahdrsz; + } if(n < 6 || n > sizeof(req->cmd)) error(Ebadarg); if((req = malloc(sizeof(SDreq))) == nil) error(Enomem); req->unit = unit; + if(waserror()){ + free(req); + nexterror(); + } memmove(req->cmd, a, n); + poperror(); req->clen = n; - req->flags = SDnosense; + /* req->flags = SDnosense; */ req->status = ~0; - + req->proto = proto; + req->ataproto = ataproto; unit->req = req; unit->state = Rawdata; + n += atacdb; break; case Rawstatus: @@ -1333,15 +1511,18 @@ sdwrite(Chan* c, void* a, long n, vlong off) case Rawdata: unit->state = Rawstatus; - unit->req->write = 1; - n = sdrio(unit->req, a, n); + req = unit->req; + req->write = 1; + n = sdrio(req, a, n); } + poperror(); qunlock(&unit->raw); decref(&sdev->r); - poperror(); break; case Qpart: return sdbio(c, 1, a, n, off); + case Qextra: + return extrarw(1, c, a, n, off); } return n; @@ -1358,23 +1539,30 @@ sdwstat(Chan* c, uchar* dp, int n) if(c->qid.type & QTDIR) error(Eperm); - - sdev = sdgetdev(DEV(c->qid)); - if(sdev == nil) - error(Enonexist); - unit = sdev->unit[UNIT(c->qid)]; + if(TYPE(c->qid) == Qtopctl){ + unit = &topctlunit; + sdev = nil; + }else{ + sdev = sdgetdev(DEV(c->qid)); + if(sdev == nil) + error(Enonexist); + unit = sdev->unit[UNIT(c->qid)]; + } qlock(&unit->ctl); + d = nil; if(waserror()){ free(d); qunlock(&unit->ctl); - decref(&sdev->r); + if(sdev != nil) + decref(&sdev->r); nexterror(); } switch(TYPE(c->qid)){ default: error(Eperm); + case Qtopctl: case Qctl: perm = &unit->ctlperm; break; @@ -1396,14 +1584,22 @@ sdwstat(Chan* c, uchar* dp, int n) n = convM2D(dp, n, &d[0], (char*)&d[1]); if(n == 0) error(Eshortstat); + if(d->atime != ~0 || d->mtime != ~0 || d->length != ~0) + error(Eperm); + if(!emptystr(d[0].muid) || !emptystr(d[0].name)) + error(Eperm); if(!emptystr(d[0].uid)) kstrdup(&perm->user, d[0].uid); + if(!emptystr(d[0].gid) && strcmp(d[0].gid, eve) != 0) + error(Eperm); if(d[0].mode != ~0UL) perm->perm = (perm->perm & ~0777) | (d[0].mode & 0777); free(d); + d = nil; USED(d); qunlock(&unit->ctl); - decref(&sdev->r); + if(sdev != nil) + decref(&sdev->r); poperror(); return n; } @@ -1487,13 +1683,62 @@ sdconfig(int on, char* spec, DevConf* cf) return unconfigure(spec); } +int +sdaddfile(SDunit *unit, char *s, int perm, char *u, SDrw *r, SDrw *w) +{ + int i; + SDfile *e; + static Lock lk; + + if(unit == nil) + return -1; + lock(&lk); + for(i = 0; i < unit->nefile; i++) + if(strcmp(unit->efile[i].name, s) == 0) + break; + if(i >= nelem(unit->efile)){ + unlock(&lk); + return -1; + } + if(i >= unit->nefile) + unit->nefile = i + 1; + e = unit->efile + i; + if(e->name == nil) + kstrdup(&e->name, s); + if(e->user == nil) + kstrdup(&e->user, u); + e->perm = perm; + e->r = r; + e->w = w; + unlock(&lk); + return 0; +} + +static void +sdshutdown(void) +{ + int i; + SDev *sd; + + for(i = 0; i < nelem(devs); i++){ + sd = devs[i]; + if(sd == nil) + continue; + if(sd->ifc->disable == nil){ + print("#S/sd%c: no disable function\n", devletters[i]); + continue; + } + sd->ifc->disable(sd); + } +} + Dev sddevtab = { 'S', "sd", sdreset, devinit, - devshutdown, + sdshutdown, sdattach, sdwalk, sdstat, @@ -1544,7 +1789,7 @@ getnewport(DevConf* dc) { Devport *p; - p = (Devport *)malloc((dc->nports + 1) * sizeof(Devport)); + p = malloc((dc->nports + 1) * sizeof(Devport)); if(dc->nports > 0){ memmove(p, dc->ports, dc->nports * sizeof(Devport)); free(dc->ports); @@ -1635,7 +1880,6 @@ legacytopctl(Cmdbuf *cb) if(j == nelem(options)) error(Ebadarg); } - /* this has been rewritten to accomodate sdaoe */ if(cd.on < 0 || cd.spec == 0) error(Ebadarg); if(cd.on && cd.cf.type == nil) diff --git a/sys/src/9/port/led.c b/sys/src/9/port/led.c new file mode 100644 index 000000000..8456d32cc --- /dev/null +++ b/sys/src/9/port/led.c @@ -0,0 +1,62 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "../port/error.h" +#include "fns.h" +#include "led.h" + +static char *ibpinames[Ibpilast] = { +[Ibpinone] "none", +[Ibpinormal] "normal", +[Ibpilocate] "locate", +[Ibpifail] "fail", +[Ibpirebuild] "rebuild", +[Ibpipfa] "pfa", +[Ibpispare] "spare", +[Ibpicritarray] "critarray", +[Ibpifailarray] "failarray", +}; + +char* +ledname(int c) +{ + if(c >= 0 && c < Ibpilast) + return ibpinames[c]; + return "bad index"; +} + + int +name2led(char *s) +{ + int i; + + for(i = 0; i < nelem(ibpinames); i++) + if(strcmp(ibpinames[i], s) == 0) + return i; + return -1; +} + +long +ledr(Ledport *p, Chan*, void *a, long n, vlong off) +{ + char buf[64]; + + snprint(buf, sizeof buf, "%s\n", ledname(p->led)); + return readstr(off, a, n, buf); +} + +long +ledw(Ledport *p, Chan*, void *a, long n, vlong) +{ + int i; + Cmdbuf *cb; + + cb = parsecmd(a, n); + i = name2led(cb->f[0]); + free(cb); + if(i == -1) + error(Ebadarg); + p->led = i; + return n; +} diff --git a/sys/src/9/port/led.h b/sys/src/9/port/led.h new file mode 100644 index 000000000..99173c5d8 --- /dev/null +++ b/sys/src/9/port/led.h @@ -0,0 +1,26 @@ +typedef struct Ledport Ledport; + +struct Ledport { + uchar nled; + uchar led; + ushort ledbits; /* implementation dependent */ +}; + +/* http://en.wikipedia.org/wiki/IBPI */ +enum { + Ibpinone, + Ibpinormal, + Ibpilocate, + Ibpifail, + Ibpirebuild, + Ibpipfa, + Ibpispare, + Ibpicritarray, + Ibpifailarray, + Ibpilast, +}; + +char *ledname(int); +int name2led(char*); +long ledr(Ledport*, Chan*, void*, long, vlong); +long ledw(Ledport*, Chan*, void*, long, vlong); diff --git a/sys/src/9/port/lib.h b/sys/src/9/port/lib.h index b72b3ed97..dce00b07a 100644 --- a/sys/src/9/port/lib.h +++ b/sys/src/9/port/lib.h @@ -85,18 +85,24 @@ extern int sprint(char*, char*, ...); #pragma varargck argpos snprint 3 #pragma varargck argpos sprint 2 +#pragma varargck type "llb" vlong #pragma varargck type "lld" vlong #pragma varargck type "llx" vlong +#pragma varargck type "llb" uvlong #pragma varargck type "lld" uvlong #pragma varargck type "llx" uvlong +#pragma varargck type "lb" long #pragma varargck type "ld" long #pragma varargck type "lx" long +#pragma varargck type "lb" ulong #pragma varargck type "ld" ulong #pragma varargck type "lx" ulong +#pragma varargck type "b" int #pragma varargck type "d" int #pragma varargck type "x" int #pragma varargck type "c" int #pragma varargck type "C" int +#pragma varargck type "b" uint #pragma varargck type "d" uint #pragma varargck type "x" uint #pragma varargck type "c" uint diff --git a/sys/src/9/port/sd.h b/sys/src/9/port/sd.h index 13e16a5b5..2d11ffef5 100644 --- a/sys/src/9/port/sd.h +++ b/sys/src/9/port/sd.h @@ -2,6 +2,7 @@ * Storage Device. */ typedef struct SDev SDev; +typedef struct SDfile SDfile; typedef struct SDifc SDifc; typedef struct SDpart SDpart; typedef struct SDperm SDperm; @@ -22,11 +23,20 @@ struct SDpart { ulong vers; }; +typedef long SDrw(SDunit*, Chan*, void*, long, vlong); +struct SDfile { + SDperm; + SDrw *r; + SDrw *w; +}; + struct SDunit { SDev* dev; int subno; uchar inquiry[255]; /* format follows SCSI spec */ uchar sense[18]; /* format follows SCSI spec */ + uchar rsense[18]; /* support seperate rq sense and inline return */ + uchar haversense; SDperm; QLock ctl; @@ -42,6 +52,8 @@ struct SDunit { int state; SDreq* req; SDperm rawperm; + SDfile efile[5]; + int nefile; }; /* @@ -67,7 +79,7 @@ struct SDifc { char* name; SDev* (*pnp)(void); - SDev* (*legacy)(int, int); + SDev* (*xxlegacy)(int, int); /* unused. remove me */ int (*enable)(SDev*); int (*disable)(SDev*); @@ -82,22 +94,26 @@ struct SDifc { void (*clear)(SDev*); char* (*rtopctl)(SDev*, char*, char*); int (*wtopctl)(SDev*, Cmdbuf*); + int (*ataio)(SDreq*); }; struct SDreq { SDunit* unit; int lun; - int write; - uchar cmd[16]; + char write; + char proto; + char ataproto; + uchar cmd[0x20]; int clen; void* data; int dlen; int flags; + ulong timeout; /* in ticks */ int status; long rlen; - uchar sense[256]; + uchar sense[32]; }; enum { @@ -119,6 +135,12 @@ enum { SDmaxio = 2048*1024, SDnpart = 16, + + SDread = 0, + SDwrite, + + SData = 1, + SDcdb = 2, }; #define sdmalloc(n) malloc(n) @@ -127,11 +149,11 @@ enum { /* devsd.c */ extern void sdadddevs(SDev*); extern int sdsetsense(SDreq*, int, int, int, int); -extern int sdmodesense(SDreq*, uchar*, void*, int); -extern int sdfakescsi(SDreq*, void*, int); +extern int sdfakescsi(SDreq*); +extern int sdfakescsirw(SDreq*, uvlong*, int*, int*); +extern int sdaddfile(SDunit*, char*, int, char*, SDrw*, SDrw*); /* sdscsi.c */ extern int scsiverify(SDunit*); extern int scsionline(SDunit*); extern long scsibio(SDunit*, int, int, void*, long, uvlong); -extern SDev* scsiid(SDev*, SDifc*); diff --git a/sys/src/9/port/sdaoe.c b/sys/src/9/port/sdaoe.c index 633b7020f..90259705d 100644 --- a/sys/src/9/port/sdaoe.c +++ b/sys/src/9/port/sdaoe.c @@ -1,5 +1,5 @@ /* - * aoe sd driver, copyright © 2007 coraid + * aoe sd driver, copyright © 2007-9 coraid */ #include "u.h" @@ -12,6 +12,7 @@ #include "../port/sd.h" #include "../port/netif.h" #include "../port/aoe.h" +#include extern char Echange[]; extern char Enotup[]; @@ -19,34 +20,14 @@ extern char Enotup[]; #define uprint(...) snprint(up->genbuf, sizeof up->genbuf, __VA_ARGS__); enum { - Nctlr = 32, - Maxpath = 128, + Maxpath = 128, Probeintvl = 100, /* ms. between probes */ - Probemax = 20, /* max probes */ -}; - -enum { - /* sync with ahci.h */ - Dllba = 1<<0, - Dsmart = 1<<1, - Dpower = 1<<2, - Dnop = 1<<3, - Datapi = 1<<4, - Datapi16= 1<<5, -}; - -static char *flagname[] = { - "llba", - "smart", - "power", - "nop", - "atapi", - "atapi16", + Probemax = 10*1000, /* max ms. to wait */ }; typedef struct Ctlr Ctlr; -struct Ctlr{ +struct Ctlr { QLock; Ctlr *next; @@ -56,11 +37,8 @@ struct Ctlr{ Chan *c; ulong vers; - uchar mediachange; - uchar flag; - uchar smart; - uchar smartrs; - uchar feat; + uchar drivechange; + Sfis; uvlong sectors; char serial[20+1]; @@ -69,88 +47,60 @@ struct Ctlr{ char ident[0x100]; }; -void aoeidmove(char *p, ushort *a, unsigned n); - static Lock ctlrlock; static Ctlr *head; static Ctlr *tail; SDifc sdaoeifc; -static ushort -gbit16(void *a) -{ - uchar *i; - - i = a; - return i[1] << 8 | i[0]; -} - -static ulong -gbit32(void *a) -{ - ulong j; - uchar *i; - - i = a; - j = i[3] << 24; - j |= i[2] << 16; - j |= i[1] << 8; - j |= i[0]; - return j; -} - -static uvlong -gbit64(void *a) -{ - uchar *i; - - i = a; - return (uvlong)gbit32(i+4)<<32 | gbit32(i); -} - static int identify(Ctlr *c, ushort *id) { - int i; uchar oserial[21]; - uvlong osectors, s; + vlong osectors, s; osectors = c->sectors; memmove(oserial, c->serial, sizeof c->serial); - - c->feat &= ~(Dllba|Dpower|Dsmart|Dnop); - i = gbit16(id+83) | gbit16(id+86); - if(i & (1<<10)){ - c->feat |= Dllba; - s = gbit64(id+100); - }else - s = gbit32(id+60); - - i = gbit16(id+83); - if((i>>14) == 1) { - if(i & (1<<3)) - c->feat |= Dpower; - i = gbit16(id+82); - if(i & 1) - c->feat |= Dsmart; - if(i & (1<<14)) - c->feat |= Dnop; + s = idfeat(c, id); + if(s == -1){ + uprint("%s: identify fails", c->unit->name); + print("%s\n", up->genbuf); + error(up->genbuf); } - - aoeidmove(c->serial, id+10, 20); - aoeidmove(c->firmware, id+23, 8); - aoeidmove(c->model, id+27, 40); + idmove(c->serial, id+10, 20); + idmove(c->firmware, id+23, 8); + idmove(c->model, id+27, 40); if((osectors == 0 || osectors != s) && memcmp(oserial, c->serial, sizeof oserial) != 0){ c->sectors = s; - c->mediachange = 1; + c->drivechange = 1; c->vers++; } return 0; } +static void +aoectl(Ctlr *d, char *s) +{ + Chan *c; + + c = nil; + if(waserror()){ + if(c) + cclose(c); + print("sdaoectl: %s\n", up->errstr); + nexterror(); + } + + uprint("%s/ctl", d->path); + c = namec(up->genbuf, Aopen, OWRITE, 0); + devtab[c->type]->write(c, s, strlen(s), 0); + + poperror(); + cclose(c); +} + /* must call with d qlocked */ static int aoeidentify(Ctlr *d, SDunit *u) @@ -173,7 +123,6 @@ aoeidentify(Ctlr *d, SDunit *u) cclose(c); d->feat = 0; - d->smart = 0; identify(d, (ushort*)d->ident); memset(u->inquiry, 0, sizeof u->inquiry); @@ -203,7 +152,6 @@ newctlr(char *path) { Ctlr *c; - /* race? */ if(ctlrlookup(path)) error(Eexist); @@ -248,7 +196,6 @@ delctlr(Ctlr *c) free(x); } -/* don't call aoeprobe from within a loop; it loops internally retrying open. */ static SDev* aoeprobe(char *path, SDev *s) { @@ -273,21 +220,24 @@ aoeprobe(char *path, SDev *s) poperror(); cclose(c); - for(i = 0; i < Probemax; i++){ + for(i = 0;; i += Probeintvl){ + if(i > Probemax || waserror()) + error(Etimedout); tsleep(&up->sleep, return0, 0, Probeintvl); + poperror(); + uprint("%s/ident", path); - if(!waserror()) { - c = namec(up->genbuf, Aopen, OREAD, 0); - poperror(); - cclose(c); - break; - } + if(waserror()) + continue; + c = namec(up->genbuf, Aopen, OREAD, 0); + poperror(); + cclose(c); + + ctlr = newctlr(path); + break; } - if(i >= Probemax) - error(Etimedout); - uprint("%s/ident", path); - ctlr = newctlr(path); - if(ctlr == nil || s == nil && (s = malloc(sizeof *s)) == nil) + + if(s == nil && (s = malloc(sizeof *s)) == nil) return nil; s->ctlr = ctlr; s->ifc = &sdaoeifc; @@ -296,14 +246,20 @@ aoeprobe(char *path, SDev *s) } static char *probef[32]; +static char *probebuf; static int nprobe; static int pnpprobeid(char *s) { + int id; + if(strlen(s) < 2) return 0; - return s[1] == '!'? s[0]: 'e'; + id = 'e'; + if(s[1] == '!') + id = s[0]; + return id; } static SDev* @@ -315,7 +271,8 @@ aoepnp(void) if((p = getconf("aoedev")) == 0) return 0; - nprobe = tokenize(p, probef, nelem(probef)); + kstrdup(&probebuf, p); + nprobe = tokenize(probebuf, probef, nelem(probef)); h = t = 0; for(i = 0; i < nprobe; i++){ id = pnpprobeid(probef[i]); @@ -341,7 +298,7 @@ aoepnp(void) static Ctlr* pnpprobe(SDev *sd) { - ulong start; + int j; char *p; static int i; @@ -353,17 +310,21 @@ pnpprobe(SDev *sd) if(p[1] == '!') p += 2; - start = TK2MS(MACHP(0)->ticks); - if(waserror()){ - print("#æ: pnpprobe failed in %lud ms: %s: %s\n", - TK2MS(MACHP(0)->ticks) - start, probef[i-1], - up->errstr); - return nil; + for(j = 0;; j += Probeintvl){ + if(j > Probemax){ + print("#æ: pnpprobe: %s: %s\n", probef[i-1], up->errstr); + return 0; + } + if(waserror()){ + tsleep(&up->sleep, return0, 0, Probeintvl); + continue; + } + sd = aoeprobe(p, sd); + poperror(); + break; } - sd = aoeprobe(p, sd); /* does a round of probing */ - poperror(); - print("#æ: pnpprobe established %s in %lud ms\n", - probef[i-1], TK2MS(MACHP(0)->ticks) - start); + print("#æ: pnpprobe establishes %s in %dms\n", probef[i-1], j); + aoectl(sd->ctlr, "nofail on"); return sd->ctlr; } @@ -378,7 +339,7 @@ aoeverify(SDunit *u) c = s->ctlr; if(c == nil && (s->ctlr = c = pnpprobe(s)) == nil) return 0; - c->mediachange = 1; + c->drivechange = 1; return 1; } @@ -412,17 +373,17 @@ aoeonline(SDunit *u) c = u->dev->ctlr; r = 0; - if((c->feat&Datapi) && c->mediachange){ + if((c->feat&Datapi) && c->drivechange){ if(aoeconnect(u, c) == 0 && (r = scsionline(u)) > 0) - c->mediachange = 0; + c->drivechange = 0; return r; } - if(c->mediachange){ + if(c->drivechange){ if(aoeconnect(u, c) == -1) return 0; r = 2; - c->mediachange = 0; + c->drivechange = 0; u->sectors = c->sectors; u->secsize = Aoesectsz; } else @@ -431,103 +392,70 @@ aoeonline(SDunit *u) return r; } -static int -aoerio(SDreq *r) +static long +aoebio(SDunit *u, int, int write, void *a, long count, uvlong lba) { - int i, count; - uvlong lba; - char *name; - uchar *cmd; + uchar *data; + int n; long (*rio)(Chan*, void*, long, vlong); Ctlr *c; - SDunit *unit; - unit = r->unit; - c = unit->dev->ctlr; + c = u->dev->ctlr; // if(c->feat & Datapi) -// return aoeriopkt(r, d); - - cmd = r->cmd; - name = unit->name; - - if(r->cmd[0] == 0x35 || r->cmd[0] == 0x91){ -// qlock(c); -// i = flushcache(); -// qunlock(c); -// if(i == 0) -// return sdsetsense(r, SDok, 0, 0, 0); - return sdsetsense(r, SDcheck, 3, 0xc, 2); - } - - if((i = sdfakescsi(r, c->ident, sizeof c->ident)) != SDnostatus){ - r->status = i; - return i; - } - - switch(*cmd){ - case 0x88: - case 0x28: - rio = devtab[c->c->type]->read; - break; - case 0x8a: - case 0x2a: +// return scsibio(u, lun, write, a, count, lba); + data = a; + if(write) rio = devtab[c->c->type]->write; - break; - default: - print("%s: bad cmd %#.2ux\n", name, cmd[0]); - r->status = SDcheck; - return SDcheck; - } - - if(r->data == nil) - return SDok; - - if(r->clen == 16){ - if(cmd[2] || cmd[3]) - return sdsetsense(r, SDcheck, 3, 0xc, 2); - lba = (uvlong)cmd[4]<<40 | (uvlong)cmd[5]<<32; - lba |= cmd[6]<<24 | cmd[7]<<16 | cmd[8]<<8 | cmd[9]; - count = cmd[10]<<24 | cmd[11]<<16 | cmd[12]<<8 | cmd[13]; - }else{ - lba = cmd[2]<<24 | cmd[3]<<16 | cmd[4]<<8 | cmd[5]; - count = cmd[7]<<8 | cmd[8]; - } - - count *= Aoesectsz; - - if(r->dlen < count) - count = r->dlen & ~0x1ff; + else + rio = devtab[c->c->type]->read; if(waserror()){ if(strcmp(up->errstr, Echange) == 0 || strcmp(up->errstr, Enotup) == 0) - unit->sectors = 0; + u->sectors = 0; nexterror(); } - r->rlen = rio(c->c, r->data, count, Aoesectsz * lba); + n = rio(c->c, data, Aoesectsz * count, Aoesectsz * lba); poperror(); - r->status = SDok; - return SDok; + return n; } -static char *smarttab[] = { - "unset", - "error", - "threshold exceeded", - "normal" -}; - -static char * -pflag(char *s, char *e, uchar f) +static int +flushcache(Ctlr *) { - uchar i, m; + return -1; +} - for(i = 0; i < 8; i++){ - m = 1 << i; - if(f & m) - s = seprint(s, e, "%s ", flagname[i]); +static int +aoerio(SDreq *r) +{ + int i, count, rw; + uvlong lba; + Ctlr *c; + SDunit *u; + + u = r->unit; + c = u->dev->ctlr; +// if(c->feat & Datapi) +// return aoeriopkt(r, d); + + if(r->cmd[0] == 0x35 || r->cmd[0] == 0x91){ + qlock(c); + i = flushcache(c); + qunlock(c); + if(i == 0) + return sdsetsense(r, SDok, 0, 0, 0); + return sdsetsense(r, SDcheck, 3, 0xc, 2); } - return seprint(s, e, "\n"); + + if((i = sdfakescsi(r)) != SDnostatus){ + r->status = i; + return i; + } + if((i = sdfakescsirw(r, &lba, &count, &rw)) != SDnostatus) + return i; + r->rlen = aoebio(u, r->lun, rw == SDwrite, r->data, count, lba); + return r->status = SDok; } static int @@ -544,14 +472,8 @@ aoerctl(SDunit *u, char *p, int l) p = seprint(p, e, "model\t%s\n", c->model); p = seprint(p, e, "serial\t%s\n", c->serial); p = seprint(p, e, "firm %s\n", c->firmware); - if(c->smartrs == 0xff) - p = seprint(p, e, "smart\tenable error\n"); - else if(c->smartrs == 0) - p = seprint(p, e, "smart\tdisabled\n"); - else - p = seprint(p, e, "smart\t%s\n", smarttab[c->smart]); p = seprint(p, e, "flag "); - p = pflag(p, e, c->feat); + p = pflag(p, e, c); p = seprint(p, e, "geometry %llud %d\n", c->sectors, Aoesectsz); return p-op; } @@ -590,7 +512,7 @@ aoertopctl(SDev *s, char *p, char *e) Ctlr *c; c = s->ctlr; - return seprint(p, e, "%s aoe %s\n", s->name, c->path); + return seprint(p, e, "%s aoe %s\n", s->name, c? c->path: ""); } static int @@ -617,7 +539,7 @@ SDifc sdaoeifc = { aoerctl, aoewctl, - scsibio, + aoebio, aoeprobew, /* probe */ aoeclear, /* clear */ aoertopctl, diff --git a/sys/src/9/port/sdloop.c b/sys/src/9/port/sdloop.c new file mode 100644 index 000000000..80118369d --- /dev/null +++ b/sys/src/9/port/sdloop.c @@ -0,0 +1,415 @@ +/* + * sd loopback driver, + * copyright © 2009-10 erik quanstrom + */ + +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" +#include "../port/sd.h" +#include "../port/netif.h" + +extern char Echange[]; +extern char Enotup[]; + +#define uprint(...) snprint(up->genbuf, sizeof up->genbuf, __VA_ARGS__); + +enum { + Maxpath = 256, + Devsectsize = 512, +}; + +typedef struct Ctlr Ctlr; +struct Ctlr { + QLock; + + Ctlr *next; + SDunit *unit; + + char path[Maxpath]; + Chan *c; + + uint vers; + uchar drivechange; + + uvlong sectors; + uint sectsize; +}; + +static Lock ctlrlock; +static Ctlr *head; +static Ctlr *tail; + +SDifc sdloopifc; + +/* must call with c qlocked */ +static void +identify(Ctlr *c, SDunit *u) +{ + int n; + uvlong s, osectors; + uchar buf[sizeof(Dir) + 100]; + Dir dir; + + if(waserror()){ + iprint("sdloop: identify: %s\n", up->errstr); + nexterror(); + } + osectors = c->sectors; + n = devtab[c->c->type]->stat(c->c, buf, sizeof buf); + if(convM2D(buf, n, &dir, nil) == 0) + error("internal error: stat error in seek"); + s = dir.length / c->sectsize; + poperror(); + + memset(u->inquiry, 0, sizeof u->inquiry); + u->inquiry[2] = 2; + u->inquiry[3] = 2; + u->inquiry[4] = sizeof u->inquiry - 4; + memmove(u->inquiry+8, c->path, 40); + + if(osectors == 0 || osectors != s){ + c->sectors = s; + c->drivechange = 1; + c->vers++; + } +} + +static Ctlr* +ctlrlookup(char *path) +{ + Ctlr *c; + + lock(&ctlrlock); + for(c = head; c; c = c->next) + if(strcmp(c->path, path) == 0) + break; + unlock(&ctlrlock); + return c; +} + +static Ctlr* +newctlr(char *path) +{ + Ctlr *c; + + if(ctlrlookup(path)) + error(Eexist); + if((c = malloc(sizeof *c)) == nil) + error(Enomem); + if(waserror()){ + free(c); + nexterror(); + } + c->c = namec(path, Aopen, ORDWR, 0); + poperror(); + kstrcpy(c->path, path, sizeof c->path); + lock(&ctlrlock); + if(head != nil) + tail->next = c; + else + head = c; + tail = c; + unlock(&ctlrlock); + return c; +} + +static void +delctlr(Ctlr *c) +{ + Ctlr *x, *prev; + + lock(&ctlrlock); + + for(prev = 0, x = head; x; prev = x, x = c->next) + if(strcmp(c->path, x->path) == 0) + break; + if(x == 0){ + unlock(&ctlrlock); + error(Enonexist); + } + + if(prev) + prev->next = x->next; + else + head = x->next; + if(x->next == nil) + tail = prev; + unlock(&ctlrlock); + + if(x->c) + cclose(x->c); + free(x); +} + +static SDev* +probe(char *path, SDev *s) +{ + char *p; + uint sectsize; + Ctlr *c; + + sectsize = 0; + if(p = strchr(path, '!')){ + *p = 0; + sectsize = strtoul(p + 1, 0, 0); + } + c = newctlr(path); + c->sectsize = sectsize? sectsize: Devsectsize; + if(s == nil && (s = malloc(sizeof *s)) == nil) + return nil; + s->ctlr = c; + s->ifc = &sdloopifc; + s->nunit = 1; + return s; +} + +static char *probef[32]; +static int nprobe; + +static int +pnpprobeid(char *s) +{ + int id; + + if(strlen(s) < 2) + return 0; + id = 'l'; + if(s[1] == '!') + id = s[0]; + return id; +} + +static SDev* +pnp(void) +{ + int i, id; + char *p; + SDev *h, *t, *s; + + if((p = getconf("loopdev")) == 0) + return 0; + nprobe = tokenize(p, probef, nelem(probef)); + h = t = 0; + for(i = 0; i < nprobe; i++){ + id = pnpprobeid(probef[i]); + if(id == 0) + continue; + s = malloc(sizeof *s); + if(s == nil) + break; + s->ctlr = 0; + s->idno = id; + s->ifc = &sdloopifc; + s->nunit = 1; + + if(h) + t->next = s; + else + h = s; + t = s; + } + return h; +} + +static Ctlr* +pnpprobe(SDev *s) +{ + char *p; + static int i; + + if(i > nprobe) + return 0; + p = probef[i++]; + if(strlen(p) < 2) + return 0; + if(p[1] == '!') + p += 2; + s = probe(p, s); + return s->ctlr; +} + + +static int +loopverify(SDunit *u) +{ + SDev *s; + Ctlr *c; + + s = u->dev; + c = s->ctlr; + if(c == nil){ + if(waserror()) + return 0; + s->ctlr = c = pnpprobe(s); + poperror(); + } + c->drivechange = 1; + return 1; +} + +static int +connect(SDunit *u, Ctlr *c) +{ + qlock(c); + if(waserror()){ + qunlock(c); + return -1; + } + identify(u->dev->ctlr, u); + qunlock(c); + poperror(); + return 0; +} + +static int +looponline(SDunit *u) +{ + Ctlr *c; + int r; + + c = u->dev->ctlr; + if(c->drivechange){ + if(connect(u, c) == -1) + return 0; + r = 2; + c->drivechange = 0; + u->sectors = c->sectors; + u->secsize = c->sectsize; + } else + r = 1; + return r; +} + +static long +loopbio(SDunit *u, int, int write, void *a, long count, uvlong lba) +{ + uchar *data; + int n; + long (*rio)(Chan*, void*, long, vlong); + Ctlr *c; + + c = u->dev->ctlr; + data = a; + if(write) + rio = devtab[c->c->type]->write; + else + rio = devtab[c->c->type]->read; + + if(waserror()){ + if(strcmp(up->errstr, Echange) == 0 || + strcmp(up->errstr, Enotup) == 0) + u->sectors = 0; + nexterror(); + } + n = rio(c->c, data, c->sectsize * count, c->sectsize * lba); + poperror(); + return n; +} + +static int +looprio(SDreq *r) +{ + int i, count, rw; + uvlong lba; + SDunit *u; + + u = r->unit; + + if(r->cmd[0] == 0x35 || r->cmd[0] == 0x91) + return sdsetsense(r, SDok, 0, 0, 0); + + if((i = sdfakescsi(r)) != SDnostatus) + return r->status = i; + if((i = sdfakescsirw(r, &lba, &count, &rw)) != SDnostatus) + return i; + r->rlen = loopbio(u, r->lun, rw == SDwrite, r->data, count, lba); + return r->status = SDok; +} + +static int +looprctl(SDunit *u, char *p, int l) +{ + Ctlr *c; + char *e, *op; + + if((c = u->dev->ctlr) == nil) + return 0; + e = p+l; + op = p; + + p = seprint(p, e, "path\t%s\n", c->path); + p = seprint(p, e, "geometry %llud %d\n", c->sectors, c->sectsize); + return p - op; +} + +static int +loopwctl(SDunit *, Cmdbuf *cmd) +{ + cmderror(cmd, Ebadarg); + return 0; +} + +static SDev* +loopprobew(DevConf *c) +{ + char *p; + + p = strchr(c->type, '/'); + if(p == nil || strlen(p) > Maxpath - 1) + error(Ebadarg); + p++; + if(ctlrlookup(p)) + error(Einuse); + return probe(p, 0); +} + +static void +loopclear(SDev *s) +{ + delctlr((Ctlr *)s->ctlr); +} + +static char* +looprtopctl(SDev *s, char *p, char *e) +{ + Ctlr *c; + + c = s->ctlr; + return seprint(p, e, "%s loop %s\n", s->name, c? c->path: ""); +} + +static int +loopwtopctl(SDev *, Cmdbuf *cmd) +{ + switch(cmd->nf){ + default: + cmderror(cmd, Ebadarg); + } + return 0; +} + +SDifc sdloopifc = { + "loop", + + pnp, + nil, /* legacy */ + nil, /* enable */ + nil, /* disable */ + + loopverify, + looponline, + looprio, + looprctl, + loopwctl, + + loopbio, + loopprobew, /* probe */ + loopclear, /* clear */ + looprtopctl, + loopwtopctl, +}; diff --git a/sys/src/9/port/sdscsi.c b/sys/src/9/port/sdscsi.c index fe182caac..e4e3b20cb 100644 --- a/sys/src/9/port/sdscsi.c +++ b/sys/src/9/port/sdscsi.c @@ -153,7 +153,8 @@ scsirio(SDreq* r) /* * If no medium present, bail out. * If unit is becoming ready, rather than not - * not ready, wait a little then poke it again. */ + * not ready, wait a little then poke it again. + */ if(r->sense[12] == 0x3A) break; if(r->sense[12] != 0x04 || r->sense[13] != 0x01) @@ -175,22 +176,91 @@ scsirio(SDreq* r) return -1; } +static void +cap10(SDreq *r) +{ + r->cmd[0] = 0x25; + r->cmd[1] = r->lun<<5; + r->clen = 10; + r->dlen = 8; +} + +static void +cap16(SDreq *r) +{ + uint i; + + i = 32; + r->cmd[0] = 0x9e; + r->cmd[1] = 0x10; + r->cmd[10] = i>>24; + r->cmd[11] = i>>16; + r->cmd[12] = i>>8; + r->cmd[13] = i; + r->clen = 16; + r->dlen = i; +} + +static uint +belong(uchar *u) +{ + return u[0]<<24 | u[1]<<16 | u[2]<<8 | u[3]; +} + +static uvlong +capreply(SDreq *r, ulong *secsize) +{ + uchar *u; + ulong ss; + uvlong s; + + *secsize = 0; + u = r->data; + if(r->clen == 16){ + s = (uvlong)belong(u)<<32 | belong(u + 4); + ss = belong(u + 8); + }else{ + s = belong(u); + ss = belong(u + 4); + } + /* + * Some ATAPI CD readers lie about the block size. + * Since we don't read audio via this interface + * it's okay to always fudge this. + */ + if(ss == 2352) + ss = 2048; + /* + * Devices with removable media may return 0 sectors + * when they have empty media (e.g. sata dvd writers); + * if so, keep the count zero. + * + * Read-capacity returns the LBA of the last sector, + * therefore the number of sectors must be incremented. + */ + if(s != 0) + s++; + *secsize = ss; + return s; +} + int scsionline(SDunit* unit) { SDreq *r; uchar *p; int ok, retries; + void (*cap)(SDreq*); - if((r = malloc(sizeof(SDreq))) == nil) + if((r = malloc(sizeof *r)) == nil) return 0; - if((p = sdmalloc(8)) == nil){ + if((p = sdmalloc(32)) == nil){ free(r); return 0; } ok = 0; - + cap = cap10; r->unit = unit; r->lun = 0; /* ??? */ for(retries = 0; retries < 10; retries++){ @@ -201,39 +271,21 @@ scsionline(SDunit* unit) * plain slow getting their act together after a reset. */ r->write = 0; - memset(r->cmd, 0, sizeof(r->cmd)); - r->cmd[0] = 0x25; - r->cmd[1] = r->lun<<5; - r->clen = 10; r->data = p; - r->dlen = 8; r->flags = 0; + memset(r->cmd, 0, sizeof r->cmd); + cap(r); r->status = ~0; switch(scsirio(r)){ default: break; case 0: - unit->sectors = (p[0]<<24)|(p[1]<<16)|(p[2]<<8)|p[3]; - unit->secsize = (p[4]<<24)|(p[5]<<16)|(p[6]<<8)|p[7]; - - /* - * Some ATAPI CD readers lie about the block size. - * Since we don't read audio via this interface - * it's okay to always fudge this. - */ - if(unit->secsize == 2352) - unit->secsize = 2048; - /* - * Devices with removable media may return 0 sectors - * when they have empty media (e.g. sata dvd writers); - * if so, keep the count zero. - * - * Read-capacity returns the LBA of the last sector, - * therefore the number of sectors must be incremented. - */ - if(unit->sectors != 0) - unit->sectors++; + unit->sectors = capreply(r, &unit->secsize); + if(unit->sectors == 0xffffffff && cap == cap10){ + cap = cap16; + continue; + } ok = 1; break; case 1: @@ -253,56 +305,6 @@ scsionline(SDunit* unit) return 0; } -int -scsiexec(SDunit* unit, int write, uchar* cmd, int clen, void* data, int* dlen) -{ - SDreq *r; - int status; - - if((r = malloc(sizeof(SDreq))) == nil) - return SDmalloc; - r->unit = unit; - r->lun = cmd[1]>>5; /* ??? */ - r->write = write; - memmove(r->cmd, cmd, clen); - r->clen = clen; - r->data = data; - if(dlen) - r->dlen = *dlen; - r->flags = 0; - - r->status = ~0; - - /* - * Call the device-specific I/O routine. - * There should be no calls to 'error()' below this - * which percolate back up. - */ - switch(status = unit->dev->ifc->rio(r)){ - case SDok: - if(dlen) - *dlen = r->rlen; - /*FALLTHROUGH*/ - case SDcheck: - /*FALLTHROUGH*/ - default: - /* - * It's more complicated than this. There are conditions - * which are 'ok' but for which the returned status code - * is not 'SDok'. - * Also, not all conditions require a reqsense, might - * need to do a reqsense here and make it available to the - * caller somehow. - * - * Mañana. - */ - break; - } - sdfree(r); - - return status; -} - static void scsifmt10(SDreq *r, int write, int lun, ulong nb, uvlong bno) { @@ -367,7 +369,7 @@ scsibio(SDunit* unit, int lun, int write, void* data, long nb, uvlong bno) r->lun = lun; again: r->write = write; - if(bno >= (1ULL<<32)) + if(bno > 0xffffffff) scsifmt16(r, write, lun, nb, bno); else scsifmt10(r, write, lun, nb, bno); @@ -381,8 +383,19 @@ again: rlen = -1; break; case 0: - rlen = r->rlen; - break; + /* + * scsi allows commands to return successfully + * but return sense data, indicating that the + * operation didn't proceed as expected. + * (confusing, no). this allows the raw commands + * to successfully return errors. but any sense + * data bio sees must be an error. bomb out. + */ + if(r->status == SDok && r->rlen > 0 + && ((r->flags & SDvalidsense) == 0 || r->sense[2] == 0)){ + rlen = r->rlen; + break; + } case 2: rlen = -1; if(!(r->flags & SDvalidsense)) @@ -415,6 +428,10 @@ again: goto again; break; } + snprint(up->genbuf, sizeof up->genbuf, "%s %.2ux%.2ux%.2ux %lld", + Eio, r->sense[2], r->sense[12], r->sense[13], bno); + free(r); + error(up->genbuf); break; } free(r); diff --git a/sys/src/libfis/fis.c b/sys/src/libfis/fis.c new file mode 100644 index 000000000..cb6c73407 --- /dev/null +++ b/sys/src/libfis/fis.c @@ -0,0 +1,545 @@ +/* + * sata fises and sas frames + * copyright © 2009-2010 erik quanstrom + */ +#include +#include +#include + +static char *flagname[9] = { + "lba", + "llba", + "smart", + "power", + "nop", + "atapi", + "atapi16", + "ata8", + "sct", +}; + +/* + * ata8 standard (llba) cmd layout + * + * feature 16 bits + * count 16 bits + * lba 48 bits + * device 8 bits + * command 8 bits + * + * response: + * + * status 8 bits + * error 8 bits + * reason 8 bits + * count 8 bits + * sstatus 8 bits + * sactive 8 bits +*/ + +/* + * sata fis layout for fistype 0x27: host-to-device: + * + * 0 fistype + * 1 fis flags + * 2 ata command + * 3 features + * 4 sector lba low 7:0 + * 5 cyl low lba mid 15:8 + * 6 cyl hi lba hi 23:16 + * 7 device / head + * 8 sec exp lba 31:24 + * 9 cy low e lba 39:32 + * 10 cy hi e lba 48:40 + * 11 features (exp) + * 12 sector count + * 13 sector count (exp) + * 14 r + * 15 control + */ + +void +setfissig(Sfis *x, uint sig) +{ + x->sig = sig; +} + +void +skelfis(uchar *c) +{ + memset(c, 0, Fissize); + c[Ftype] = H2dev; + c[Fflags] = Fiscmd; + c[Fdev] = Ataobs; +} + +int +nopfis(Sfis*, uchar *c, int srst) +{ + skelfis(c); + if(srst){ + c[Fflags] &= ~Fiscmd; + c[Fcontrol] = 1<<2; + return Preset|P28; + } + return Pnd|P28; +} + +int +txmodefis(Sfis *f, uchar *c, uchar d) +{ + int m; + + /* hack */ + if((f->sig >> 16) == 0xeb14) + return -1; + m = 0x40; + if(d == 0xff){ + d = 0; + m = 0; + } + skelfis(c); + c[Fcmd] = 0xef; + c[Ffeat] = 3; /* set transfer mode */ + c[Fsc] = m | d; /* sector count */ + return Pnd|P28; +} + +int +featfis(Sfis*, uchar *c, uchar f) +{ + skelfis(c); + c[Fcmd] = 0xef; + c[Ffeat] = f; + return Pnd|P28; +} + +int +identifyfis(Sfis *f, uchar *c) +{ + static uchar tab[] = { 0xec, 0xa1, }; + + skelfis(c); + c[Fcmd] = tab[f->sig>>16 == 0xeb14]; + return Pin|Ppio|P28|P512; +} + +int +flushcachefis(Sfis *f, uchar *c) +{ + static uchar tab[2] = {0xe7, 0xea}; + static uchar ptab[2] = {Pnd|P28, Pnd|P48}; + int llba; + + llba = (f->feat & Dllba) != 0; + skelfis(c); + c[Fcmd] = tab[llba]; + return ptab[llba]; +} + +static ushort +gbit16(void *a) +{ + ushort j; + uchar *i; + + i = a; + j = i[1] << 8; + j |= i[0]; + return j; +} + +static uint +gbit32(void *a) +{ + uint j; + uchar *i; + + i = a; + j = i[3] << 24; + j |= i[2] << 16; + j |= i[1] << 8; + j |= i[0]; + return j; +} + +static uvlong +gbit64(void *a) +{ + uchar *i; + + i = a; + return (uvlong)gbit32(i+4) << 32 | gbit32(a); +} + +ushort +id16(ushort *id, int i) +{ + return gbit16(id+i); +} + +uint +id32(ushort *id, int i) +{ + return gbit32(id+i); +} + +uvlong +id64(ushort *id, int i) +{ + return gbit64(id+i); +} + +/* acs-2 §7.18.7.4 */ +static ushort puistab[] = { + 0x37c8, Pspinup, + 0x738c, Pspinup | Pidready, + 0x8c73, 0, + 0xc837, Pidready, +}; + +int +idpuis(ushort *id) +{ + ushort u, i; + + u = gbit16(id + 2); + for(i = 0; i < nelem(puistab); i += 2) + if(u == puistab[i]) + return puistab[i + 1]; + return Pidready; /* annoying cdroms */ +} + +static ushort +onesc(ushort *id) +{ + ushort u; + + u = gbit16(id); + if(u == 0xffff) + u = 0; + return u; +} + +enum{ + Idmasp = 1<<8, + Ilbasp = 1<<9, + Illba = 1<<10, +}; + +vlong +idfeat(Sfis *f, ushort *id) +{ + int i, j; + vlong s; + + f->feat = 0; + if(f->sig>>16 == 0xeb14) + f->feat |= Datapi; + i = gbit16(id + 49); + if((i & Ilbasp) == 0){ + if(gbit16(id + 53) & 1){ + f->c = gbit16(id + 1); + f->h = gbit16(id + 3); + f->s = gbit16(id + 6); + }else{ + f->c = gbit16(id + 54); + f->h = gbit16(id + 55); + f->s = gbit16(id + 56); + } + s = f->c*f->h*f->s; + }else{ + f->c = f->h = f->s = 0; + f->feat |= Dlba; + j = gbit16(id + 83) | gbit16(id + 86); + if(j & Illba){ + f->feat |= Dllba; + s = gbit64(id + 100); + }else + s = gbit32(id + 60); + } + f->udma = 0xff; + if(i & Idmasp) + if(gbit16(id + 53) & 4) + for(i = gbit16(id + 88) & 0x7f; i; i >>= 1) + f->udma++; + + if(f->feat & Datapi){ + i = gbit16(id + 0); + if(i & 1) + f->feat |= Datapi16; + } + + i = gbit16(id+83); + if((i>>14) == 1){ + if(i & (1<<3)) + f->feat |= Dpower; + i = gbit16(id + 82); + if(i & 1) + f->feat |= Dsmart; + if(i & (1<<14)) + f->feat |= Dnop; + } + i = onesc(id + 80); + if(i & 1<<8){ + f->feat |= Data8; + i = onesc(id + 222); /* sata? */ + j = onesc(id + 76); + if(i != 0 && i >> 12 == 1 && j != 0){ + j >>= 1; + f->speeds = j & 7; + i = gbit16(id + 78) & gbit16(id + 79); + /* + * not acceptable for comreset to + * wipe out device configuration. + * reject drive. + */ + if((i & 1<<6) == 0) + return -1; + } + } + if(gbit16(id + 206) & 1) + f->feat |= Dsct; + idss(f, id); + return s; +} + +int +idss(Sfis *f, ushort *id) +{ + uint sw, i; + + if(f->sig>>16 == 0xeb14) + return 0; + f->lsectsz = 512; + f->physshift = 0; + i = gbit16(id + 106); + if(i >> 14 != 1) + return f->lsectsz; + if((sw = gbit32(id + 117)) >= 256) + f->lsectsz = sw * 2; + if(i & 1<<13) + f->physshift = i & 7; + return f->lsectsz * (1<physshift); +} + +uvlong +idwwn(Sfis*, ushort *id) +{ + uvlong u; + + u = 0; + if(id[108]>>12 == 5){ + u |= (uvlong)gbit16(id + 108) << 48; + u |= (uvlong)gbit16(id + 109) << 32; + u |= gbit16(id + 110) << 16; + u |= gbit16(id + 111) << 0; + } + return u; +} + +void +idmove(char *p, ushort *u, int n) +{ + int i; + char *op, *e, *s; + + op = p; + s = (char*)u; + for(i = 0; i < n; i += 2){ + *p++ = s[i + 1]; + *p++ = s[i + 0]; + } + *p = 0; + while(p > op && *--p == ' ') + *p = 0; + e = p; + p = op; + while(*p == ' ') + p++; + memmove(op, p, n - (e - p)); +} + +char* +pflag(char *s, char *e, Sfis *f) +{ + ushort i, u; + + u = f->feat; + for(i = 0; i < Dnflag; i++) + if(u & (1 << i)) + s = seprint(s, e, "%s ", flagname[i]); + return seprint(s, e, "\n"); +} + +int +atapirwfis(Sfis *f, uchar *c, uchar *cdb, int cdblen, int ndata) +{ + int fill, len; + + fill = f->feat&Datapi16? 16: 12; + if((len = cdblen) > fill) + len = fill; + memmove(c + 0x40, cdb, len); + memset(c + 0x40 + len, 0, fill - len); + + c[Ftype] = H2dev; + c[Fflags] = Fiscmd; + c[Fcmd] = Ataobs; + if(ndata != 0) + c[Ffeat] = 1; /* dma */ + else + c[Ffeat] = 0; /* features (exp); */ + c[Flba0] = 0; + c[Flba8] = ndata; + c[Flba16] = ndata >> 8; + c[Fdev] = Ataobs; + memset(c + 8, 0, Fissize - 8); + return P28|Ppkt; +} + +int +rwfis(Sfis *f, uchar *c, int rw, int nsect, uvlong lba) +{ + uchar acmd, llba, udma; + static uchar tab[2][2][2] = { 0x20, 0x24, 0x30, 0x34, 0xc8, 0x25, 0xca, 0x35, }; + static uchar ptab[2][2][2] = { + Pin|Ppio|P28, Pin|Ppio|P48, + Pout|Ppio|P28, Pout|Ppio|P48, + Pin|Pdma|P28, Pin|Pdma|P48, + Pout|Pdma|P28, Pout|Pdma|P48, + }; + + nsect >>= f->physshift; + lba >>= f->physshift; + + udma = f->udma != 0xff; + llba = (f->feat & Dllba) != 0; + acmd = tab[udma][rw][llba]; + + c[Ftype] = 0x27; + c[Fflags] = 0x80; + c[Fcmd] = acmd; + c[Ffeat] = 0; + + c[Flba0] = lba; + c[Flba8] = lba >> 8; + c[Flba16] = lba >> 16; + c[Fdev] = Ataobs | Atalba; + if(llba == 0) + c[Fdev] |= (lba>>24) & 0xf; + + c[Flba24] = lba >> 24; + c[Flba32] = lba >> 32; + c[Flba40] = lba >> 48; + c[Ffeat8] = 0; + + c[Fsc] = nsect; + c[Fsc8] = nsect >> 8; + c[Ficc] = 0; + c[Fcontrol] = 0; + + memset(c + 16, 0, Fissize - 16); + return ptab[udma][rw][llba]; +} + +uvlong +fisrw(Sfis *f, uchar *c, int *n) +{ + uvlong lba; + + lba = c[Flba0]; + lba |= c[Flba8] << 8; + lba |= c[Flba16] << 16; + lba |= c[Flba24] << 24; + lba |= (uvlong)(c[Flba32] | c[Flba40]<<8) << 32; + + *n = c[Fsc]; + *n |= c[Fsc8] << 8; + + *n >>= f->physshift; + lba >>= f->physshift; + + return lba; +} + +void +sigtofis(Sfis *f, uchar *c) +{ + uint u; + + u = f->sig; + memset(c, 0, Fissize); + c[Ftype] = 0x34; + c[Fflags] = 0x00; + c[Fcmd] = 0x50; + c[Ffeat] = 0x01; + c[Flba0] = u >> 8; + c[Flba8] = u >> 16; + c[Flba16] = u >> 24; + c[Fdev] = Ataobs; + c[Fsc] = u; +} + +uint +fistosig(uchar *u) +{ + return u[Fsc] | u[Flba0]<<8 | u[Flba8]<<16 | u[Flba16]<<24; +} + + +/* sas smp */ +void +smpskelframe(Cfis *f, uchar *c, int m) +{ + memset(c, 0, Fissize); + c[Ftype] = 0x40; + c[Fflags] = m; + if(f->phyid) + c[Flba32] = f->phyid; +} + +uint +sashash(uvlong u) +{ + uint poly, msb, l, r; + uvlong m; + + r = 0; + poly = 0x01db2777; + msb = 0x01000000; + for(m = 1ull<<63; m > 0; m >>= 1){ + l = 0; + if(m & u) + l = msb; + r <<= 1; + r ^= l; + if(r & msb) + r ^= poly; + } + return r & 0xffffff; +} + +uchar* +sasbhash(uchar *t, uchar *s) +{ + uint poly, msb, l, r, i, j; + + r = 0; + poly = 0x01db2777; + msb = 0x01000000; + for(i = 0; i < 8; i++) + for(j = 0x80; j != 0; j >>= 1){ + l = 0; + if(s[i] & j) + l = msb; + r <<= 1; + r ^= l; + if(r & msb) + r ^= poly; + } + t[0] = r>>16; + t[1] = r>>8; + t[2] = r; + return t; +} diff --git a/sys/src/libfis/mkfile b/sys/src/libfis/mkfile new file mode 100644 index 000000000..c336749b5 --- /dev/null +++ b/sys/src/libfis/mkfile @@ -0,0 +1,15 @@ +