--- /n/sources/plan9/sys/src/9/pc/ahci.h Mon Mar 17 22:11:51 2014 +++ /sys/src/9/pc/ahci.h Tue Apr 22 00:00:00 2014 @@ -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 @@ /* 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,16 +50,50 @@ 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, +}; + +/* bios bits */ +enum { + Bb = 1<<4, /* bios cleaning up for change */ + Ooc = 1<<3, /* os ownership change */ + Sooe = 1<<2, /* smi on ownership change enable */ + Oos = 1<<1, /* os owned semaphore */ + Bos = 1<<0, /* bios owned semaphore */ +}; + typedef struct { - ulong cap; - ulong ghc; - ulong isr; - ulong pi; /* ports implemented */ - ulong ver; - ulong ccc; /* coaleasing control */ - ulong cccports; - ulong emloc; - ulong emctl; + u32int cap; + u32int ghc; + u32int isr; + u32int pi; /* ports implemented */ + u32int ver; + u32int ccc; /* coaleasing control */ + u32int cccports; + u32int emloc; + u32int emctl; + u32int cap2; + u32int bios; } Ahba; enum { @@ -147,6 +154,8 @@ 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,59 +173,71 @@ Ast = 1<<0, /* start */ Arun = Ast|Acr|Afre|Afr, + Apwr = Apod|Asud, }; -/* ctl register bits */ +/* sctl register bits */ enum { Aipm = 1<<8, /* interface power mgmt. 3=off */ Aspd = 1<<4, 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 listhi; - ulong fis; /* 256-byte aligned */ - ulong fishi; - ulong isr; - ulong ie; /* interrupt enable */ - ulong cmd; - ulong res1; - ulong task; - ulong sig; - ulong scr0; - ulong scr2; - ulong scr1; - ulong scr3; - ulong ci; /* command issue */ - ulong ntf; - uchar res2[8]; - ulong vendor; + u32int list; /* PxCLB must be 1kb aligned */ + u32int listhi; + u32int fis; /* 256-byte aligned */ + u32int fishi; + u32int isr; + u32int ie; /* interrupt enable */ + u32int cmd; + u32int res1; + u32int task; + u32int sig; + u32int scr0; + u32int scr2; + u32int scr1; + u32int scr3; + u32int ci; /* command issue */ + u32int scr4; + u32int fbs; + u32int res2[11]; + u32int vendor[4]; } Aport; -enum { - /* - * Aport sstatus bits (actually states): - * 11-8 interface power management - * 7-4 current interface speed (generation #) - * 3-0 device detection - */ - Intslumber = 0x600, - Intpartpwr = 0x200, - Intactive = 0x100, - Intpm = 0xf00, - - Devphyoffline = 4, - Devphycomm = 2, /* phy communication established */ - Devpresent = 1, - Devdet = Devpresent | Devphycomm | Devphyoffline, -}; - /* in host's memory; not memory mapped */ typedef struct { uchar *base; @@ -224,7 +245,7 @@ uchar *p; uchar *r; uchar *u; - ulong *devicebits; + u32int *devicebits; } Afis; enum { @@ -241,18 +262,18 @@ /* in hosts memory; memory mapped */ typedef struct { - ulong flags; - ulong len; - ulong ctab; - ulong ctabhi; + u32int flags; + u32int len; + u32int ctab; + u32int ctabhi; uchar reserved[16]; } Alist; typedef struct { - ulong dba; - ulong dbahi; - ulong pad; - ulong count; + u32int dba; + u32int dbahi; + u32int pad; + u32int count; } Aprdt; typedef struct { @@ -262,26 +283,53 @@ Aprdt prdt; } Actab; +/* enclosure message header */ enum { - Ferror = 1, - Fdone = 2, + Mled = 0, + Msafte = 1, + Mses2 = 2, + Msgpio = 3, }; enum { - Dllba = 1, - Dsmart = 1<<1, - Dpower = 1<<2, - Dnop = 1<<3, - Datapi = 1<<4, - Datapi16= 1<<5, + Ledmsz = 8, +}; + +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; + u32int *enctx; + u32int *encrx; +} Aenc; + +enum { + Ferror = 1, + Fdone = 2, }; typedef struct { QLock; Rendez; uchar flag; - uchar feat; - uchar smart; + Sfis; Afis fis; Alist *list; Actab *ctab; @@ -289,5 +337,5 @@ typedef struct { Aport *p; - Aportm *pm; + Aportm *m; } Aportc; --- /n/sources/plan9/sys/src/9/pc/mkfile Wed Mar 12 03:07:49 2014 +++ /sys/src/9/pc/mkfile Tue Apr 22 00:00:00 2014 @@ -74,6 +74,7 @@ /$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'} @@ -120,6 +121,7 @@ $SDEV: ../port/sd.h sd53c8xx.$O: sd53c8xx.i sdiahci.$O: ahci.h +sdiahci.$O: ../port/led.h devaoe.$O sdaoe.$O: ../port/aoe.h main.$O: init.h reboot.h wavelan.$O: wavelan.c ../pc/wavelan.c ../pc/wavelan.h --- /n/sources/plan9/sys/src/9/pc/pc Fri Aug 17 21:44:14 2012 +++ /sys/src/9/pc/pc Tue Apr 22 00:00:00 2014 @@ -90,7 +90,7 @@ sdata pci sdscsi sd53c8xx pci sdscsi sdmylex pci sdscsi - sdiahci pci sdscsi + sdiahci pci sdscsi led uarti8250 uartpci pci --- /n/sources/plan9/sys/src/9/pc/pccd Fri Aug 17 22:02:44 2012 +++ /sys/src/9/pc/pccd Tue Apr 22 00:00:00 2014 @@ -86,7 +86,7 @@ sdata pci sdscsi sd53c8xx pci sdscsi sdmylex pci sdscsi - sdiahci pci sdscsi + sdiahci pci sdscsi led uarti8250 uartpci pci --- /n/sources/plan9/sys/src/9/pc/pccpu Fri Aug 17 21:44:45 2012 +++ /sys/src/9/pc/pccpu Tue Apr 22 00:00:00 2014 @@ -88,7 +88,7 @@ sd53c8xx pci sdscsi sdmv50xx pci sdscsi sdmylex pci sdscsi - sdiahci pci sdscsi + sdiahci pci sdscsi led sdaoe sdscsi ip --- /n/sources/plan9/sys/src/9/pc/pccpuf Fri Aug 17 22:15:05 2012 +++ /sys/src/9/pc/pccpuf Tue Apr 22 00:00:00 2014 @@ -86,7 +86,7 @@ sdata pci sdscsi sd53c8xx pci sdscsi sdmylex pci sdscsi - sdiahci pci sdscsi + sdiahci pci sdscsi led sdaoe vga3dfx +cur --- /n/sources/plan9/sys/src/9/pc/pcdisk Fri Aug 17 21:45:40 2012 +++ /sys/src/9/pc/pcdisk Tue Apr 22 00:00:00 2014 @@ -85,7 +85,7 @@ sdata pci sdscsi sd53c8xx pci sdscsi sdmylex pci sdscsi - sdiahci pci sdscsi + sdiahci pci sdscsi led sdaoe uarti8250 --- /n/sources/plan9/sys/src/9/pc/pcf Fri Aug 17 21:44:56 2012 +++ /sys/src/9/pc/pcf Tue Apr 22 00:00:00 2014 @@ -85,7 +85,7 @@ sdata pci sdscsi sd53c8xx pci sdscsi sdmylex pci sdscsi - sdiahci pci sdscsi + sdiahci pci sdscsi led sdaoe uarti8250 --- /n/sources/plan9/sys/src/9/pc/pcflop Fri Aug 17 22:05:59 2012 +++ /sys/src/9/pc/pcflop Tue Apr 22 00:00:00 2014 @@ -75,7 +75,7 @@ sdata pci sdscsi sd53c8xx pci sdscsi sdmylex pci sdscsi -# sdiahci pci sdscsi +# sdiahci pci sdscsi led uarti8250 --- /n/sources/plan9/sys/src/9/pc/pcfs Fri Aug 17 21:49:37 2012 +++ /sys/src/9/pc/pcfs Tue Apr 22 00:00:00 2014 @@ -56,7 +56,7 @@ sdaoe sdata pci sdscsi - sdiahci pci sdscsi + sdiahci pci sdscsi led sd53c8xx pci sdscsi sdmylex pci sdscsi --- /n/sources/plan9/sys/src/9/pc/sdiahci.c Mon Mar 17 22:11:51 2014 +++ /sys/src/9/pc/sdiahci.c Tue Apr 22 00:00:00 2014 @@ -1,6 +1,6 @@ /* - * ahci serial ata driver - * copyright © 2007-8 coraid, inc. + * intel/amd ahci sata controller + * copyright © 2007-13 coraid, inc. */ #include "u.h" @@ -11,68 +11,73 @@ #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 Tname(c) tname[(c)->type] -#define Intel(x) ((x)->pci->vid == Vintel) +#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(va) (sizeof(uintptr)>4? (uvlong)PCIWADDR(va)>>32: 0) +#define Pciwaddrl(va) PCIWADDR(va) +#define Ticks MACHP(0)->ticks +#define MS2TK(t) (((ulong)(t)*HZ)/1000) enum { - NCtlr = 16, - NCtlrdrv= 32, - NDrive = NCtlr*NCtlrdrv, - - Read = 0, - Write, + NCtlr = 4, + NCtlrdrv = 32, + NDrive = NCtlr*NCtlrdrv, - Nms = 256, /* ms. between drive checks */ - Mphywait= 2*1024/Nms - 1, - Midwait = 16*1024/Nms - 1, - Mcomrwait= 64*1024/Nms - 1, + Fahdrs = 4, - Obs = 0xa0, /* obsolete device bits */ + Read = 0, + Write, - /* - * if we get more than this many interrupts per tick for a drive, - * either the hardware is broken or we've got a bug in this driver. - */ - Maxintrspertick = 2000, /* was 1000 */ -}; + Eesb = 1<<0, /* must have (Eesb & Emtype) == 0 */ -/* pci space configuration */ -enum { - Pmap = 0x90, - Ppcs = 0x91, - Prev = 0xa8, + /* pci space configuration */ + Pmap = 0x90, + Ppcs = 0x91, }; enum { Tesb, - Tich, Tsb600, - Tunk, + Tjmicron, + Tahci, + Tlast, }; -static char *tname[] = { - "63xxesb", - "ich", - "sb600", - "unknown", +typedef struct Ctlrtype Ctlrtype; +typedef struct Ctlr Ctlr; +typedef struct Drive Drive; + +struct Ctlrtype { + uint type; + uint maxdmaxfr; + uint flags; + char *name; +}; + +Ctlrtype cttab[Tlast] = { +[Tesb] Tesb, 8192, 0, "63xxesb", +[Tsb600] Tsb600, 256, 0, "sb600", +[Tjmicron] Tjmicron, 8192, 0, "jmicron", +[Tahci] Tahci, 8192, 0, "ahci", }; enum { - Dnull, - Dmissing, - Dnew, - Dready, - Derror, - Dreset, - Doffline, - Dportreset, - Dlast, + Dnull = 0, + Dmissing = 1<<0, + Dnew = 1<<1, + Dready = 1<<2, + Derror = 1<<3, + Dreset = 1<<4, + Doffline = 1<<5, + Dportreset = 1<<6, + Dlast = 8, }; static char *diskstates[Dlast] = { @@ -86,38 +91,28 @@ "portreset", }; +extern SDifc sdiahciifc; + enum { DMautoneg, DMsatai, DMsataii, - DMsata3, + DMsataiii, + DMlast, }; -static char *modename[] = { /* used in control messages */ +static char *modes[DMlast] = { "auto", "satai", "sataii", - "sata3", + "sataiii", +}; + +typedef struct Htab Htab; +struct Htab { + uint bit; + char *name; }; -static char *descmode[] = { /* only printed */ - "auto", - "sata 1", - "sata 2", - "sata 3", -}; - -static char *flagname[] = { - "llba", - "smart", - "power", - "nop", - "atapi", - "atapi16", -}; - -typedef struct Asleep Asleep; -typedef struct Ctlr Ctlr; -typedef struct Drive Drive; struct Drive { Lock; @@ -127,68 +122,56 @@ 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; - int infosz; - ushort *info; - ushort tinyinfo[2]; /* used iff malloc fails */ - - int driveno; /* ctlr*NCtlrdrv + unit */ - /* controller port # != driveno when not all ports are enabled */ - int portno; + ushort info[0x200]; - ulong lastintr0; - ulong intrs; + /* + * ahci allows non-sequential ports. + * to avoid this hassle, we let + * driveno ctlr*NCtlrdrv + unit + * portno nth available port + */ + uint driveno; + uint portno; }; struct Ctlr { Lock; - int type; + Ctlrtype *type; int enabled; SDev *sdev; Pcidev *pci; - /* virtual register addresses */ uchar *mmio; - ulong *lmmio; + u32int *lmmio; Ahba *hba; + Aenc; + uint enctype; - /* phyical register address */ - uchar *physio; - - Drive *rawdrive; - Drive *drive[NCtlrdrv]; + Drive rawdrive[NCtlrdrv]; + Drive* drive[NCtlrdrv]; int ndrive; - int mport; /* highest drive # (0-origin) on ich9 at least */ - - ulong lastintr0; - ulong intrs; /* not attributable to any drive */ -}; - -struct Asleep { - Aport *p; - int i; }; -extern SDifc sdiahciifc; - static Ctlr iactlr[NCtlr]; static SDev sdevs[NCtlr]; static int niactlr; @@ -196,10 +179,10 @@ 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', @@ -208,7 +191,7 @@ }; static void -serrstr(ulong r, char *s, char *e) +serrstr(u32int r, char *s, char *e) { int i; @@ -222,29 +205,10 @@ *s = 0; } -static char ntab[] = "0123456789abcdef"; - -static void -preg(uchar *reg, int n) -{ - int i; - char buf[25*3+1], *e; - - e = buf; - for(i = 0; i < n; i++){ - *e++ = ntab[reg[i]>>4]; - *e++ = ntab[reg[i]&0xf]; - *e++ = ' '; - } - *e++ = '\n'; - *e = 0; - dprint(buf); -} - static void dreg(char *s, Aport *p) { - dprint("ahci: %stask=%#lux; cmd=%#lux; ci=%#lux; is=%#lux\n", + dprint("%stask=%ux; cmd=%ux; ci=%ux; is=%ux\n", s, p->task, p->cmd, p->ci, p->isr); } @@ -257,6 +221,11 @@ poperror(); } +typedef struct { + Aport *p; + int i; +} Asleep; + static int ahciclear(void *v) { @@ -267,56 +236,51 @@ } static void -aesleep(Aportm *pm, Asleep *a, int ms) +aesleep(Aportm *m, Asleep *a, int ms) { if(waserror()) return; - tsleep(pm, ahciclear, a, ms); + tsleep(m, ahciclear, a, ms); poperror(); } 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->pm, &as, ms); - if((p->task&1) == 0 && p->ci == 0) + aesleep(c->m, &as, ms); + if((p->task & 1) == 0 && p->ci == 0) return 0; - dreg("ahciwait timeout ", c->p); + dreg("ahciwait fail/timeout ", c->p); return -1; } -/* fill in cfis boilerplate */ -static uchar * -cfissetup(Aportc *pc) -{ - uchar *cfis; - - cfis = pc->pm->ctab->cfis; - memset(cfis, 0, 0x20); - cfis[0] = 0x27; - cfis[1] = 0x80; - cfis[7] = Obs; - return cfis; -} - -/* initialise pc's list */ static void -listsetup(Aportc *pc, int flags) +mkalist(Aportm *m, uint flags, uchar *data, int len) { - Alist *list; + Actab *t; + Alist *l; + Aprdt *p; - list = pc->pm->list; - list->flags = flags | 5; - list->len = 0; - list->ctab = PCIWADDR(pc->pm->ctab); - list->ctabhi = 0; + t = m->ctab; + l = m->list; + l->flags = flags | 0x5; + l->len = 0; + l->ctab = Pciwaddrl(t); + l->ctabhi = Pciwaddrh(t); + if(data){ + l->flags |= 1<<16; + p = &t->prdt; + p->dba = Pciwaddrl(data); + p->dbahi = Pciwaddrh(data); + p->count = 1<<31 | len - 2 | 1; + } } static int @@ -324,39 +288,34 @@ { uchar *c; - if((pc->pm->feat & Dnop) == 0) + if((pc->m->feat & Dnop) == 0) return -1; - c = cfissetup(pc); - c[2] = 0; - listsetup(pc, Lwrite); + 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; - c = cfissetup(pc); - c[2] = 0xef; - c[3] = f; - listsetup(pc, Lwrite); - 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); } static int -setudmamode(Aportc *pc, uchar f) +settxmode(Aportc *pc, uchar f) { uchar *c; - /* hack */ - if((pc->p->sig >> 16) == 0xeb14) + c = pc->m->ctab->cfis; + if(txmodefis(pc->m, c, f) == -1) return 0; - c = cfissetup(pc); - c[2] = 0xef; - c[3] = 3; /* set transfer mode */ - c[12] = 0x40 | f; /* sector count */ - listsetup(pc, Lwrite); + mkalist(pc->m, Lwrite, 0, 0); return ahciwait(pc, 3*1000); } @@ -370,68 +329,23 @@ } static int -ahciportreset(Aportc *c) +ahciportreset(Aportc *c, uint mode) { - ulong *cmd, i; + int i; + u32int *cmd; Aport *p; p = c->p; 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; - - if((pc->pm->feat&Dsmart) == 0) - return -1; - c = cfissetup(pc); - c[2] = 0xb0; - c[3] = 0xd8 + n; /* able smart */ - c[5] = 0x4f; - c[6] = 0xc2; - listsetup(pc, Lwrite); - 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; - - c = cfissetup(pc); - c[2] = 0xb0; - c[3] = 0xda; /* return smart status */ - c[5] = 0x4f; - c[6] = 0xc2; - listsetup(pc, Lwrite); - - c = pc->pm->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; } @@ -440,109 +354,55 @@ { uchar *c; - c = cfissetup(pc); - c[2] = pc->pm->feat & Dllba? 0xea: 0xe7; - listsetup(pc, Lwrite); + 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 %ux\n", pc->p->task); 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; - Aprdt *p; - static uchar tab[] = { 0xec, 0xa1, }; + Actab *t; - c = cfissetup(pc); - c[2] = tab[atapi]; - listsetup(pc, 1<<16); - - memset(id, 0, 0x100); /* magic */ - p = &pc->pm->ctab->prdt; - p->dba = PCIWADDR(id); - p->dbahi = 0; - p->count = 1<<31 | (0x200-2) | 1; + t = pc->m->ctab; + c = t->cfis; + 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 *pm; + Aportm *m; - pm = pc->pm; - pm->feat = 0; - pm->smart = 0; - i = 0; - sig = pc->p->sig >> 16; - if(sig == 0xeb14){ - pm->feat |= Datapi; - i = 1; + m = pc->m; + 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)){ - pm->feat |= Dllba; - s = gbit64(id+100); - }else - s = gbit32(id+60); - - if(pm->feat&Datapi){ - i = gbit16(id+0); - if(i&1) - pm->feat |= Datapi16; - } - - i = gbit16(id+83); - if((i>>14) == 1) { - if(i & (1<<3)) - pm->feat |= Dpower; - i = gbit16(id+82); - if(i & 1) - pm->feat |= Dsmart; - if(i & (1<<14)) - pm->feat |= Dnop; } return s; } @@ -550,7 +410,8 @@ static int ahciquiet(Aport *a) { - ulong *p, i; + int i; + u32int *p; p = &a->cmd; *p &= ~Ast; @@ -575,10 +436,10 @@ return -1; stop1: /* extra check */ - dprint("ahci: clo clear %#lx\n", a->task); + dprint("ahci: clo clear %ux\n", a->task); if(a->task & ASbsy) return -1; - *p |= Ast; + *p |= Afre | Ast; return 0; } @@ -587,30 +448,27 @@ { uchar *c; - 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); - c = cfissetup(pc); - c[1] = 0; - c[15] = 1<<2; /* srst */ - listsetup(pc, Lclear | Lreset); + 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); - c = cfissetup(pc); - c[1] = 0; - listsetup(pc, Lwrite); + 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); @@ -620,7 +478,8 @@ static int ahciidle(Aport *port) { - ulong *p, i, r; + int i, r; + u32int *p; p = &port->cmd; if((*p & Arun) == 0) @@ -646,7 +505,7 @@ } /* - * § 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. */ @@ -669,103 +528,94 @@ { ahciswreset(pc); pc->p->cmd |= Ast; - if(setudmamode(pc, 5) == -1) + if(settxmode(pc, pc->m->udma) == -1) return -1; return 0; } -static void* -malign(int size, int align) -{ - void *v; - - v = xspanalloc(size, align, 0); - memset(v, 0, size); - return v; -} - static void setupfis(Afis *f) { - f->base = malign(0x100, 0x100); /* magic */ + f->base = mallocalign(0x100, 0x100, 0, 0); f->d = f->base + 0; f->p = f->base + 0x20; f->r = f->base + 0x40; f->u = f->base + 0x60; - f->devicebits = (ulong*)(f->base + 0x58); + f->devicebits = (u32int*)(f->base + 0x58); } static void -ahciwakeup(Aport *p) +ahciwakeup(Aportc *c, uint mode) { ushort s; - s = p->sstatus; - if((s & Intpm) != Intslumber && (s & Intpm) != Intpartpwr) + s = c->p->sstatus; + if((s & Isleepy) == 0) return; - if((s & Devdet) != Devpresent){ /* 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 -ahciconfigdrive(Drive *d) +ahciconfigdrive(Ahba *h, Aportc *c, int mode) { - char *name; - Ahba *h; + Aportm *m; Aport *p; - Aportm *pm; - h = d->ctlr->hba; - p = d->portc.p; - pm = d->portc.pm; - if(pm->list == 0){ - setupfis(&pm->fis); - pm->list = malign(sizeof *pm->list, 1024); - pm->ctab = malign(sizeof *pm->ctab, 128); - } + p = c->p; + m = c->m; - if (d->unit) - name = d->unit->name; - else - name = nil; - if(p->sstatus & (Devphycomm|Devpresent) && h->cap & Hsss){ - /* device connected & staggered spin-up */ - dprint("ahci: configdrive: %s: spinning up ... [%#lux]\n", - name, p->sstatus); - p->cmd |= Apod|Asud; - asleep(1400); + if(m->list == 0){ + setupfis(&m->fis); + m->list = mallocalign(sizeof *m->list, 1024, 0, 0); + m->ctab = mallocalign(sizeof *m->ctab, 128, 0, 0); + } + + p->list = Pciwaddrl(m->list); + p->listhi = Pciwaddrh(m->list); + p->fis = Pciwaddrl(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){ + dprint("ahci: spin up ... [%.3ux]\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(pm->list); - p->listhi = 0; - p->fis = PCIWADDR(pm->fis.base); - p->fishi = 0; - p->cmd |= Afre|Ast; - - /* drive coming up in slumbering? */ - if((p->sstatus & Devdet) == Devpresent && - ((p->sstatus & Intpm) == Intslumber || - (p->sstatus & Intpm) == Intpartpwr)) - ahciwakeup(p); - - /* "disable power managment" sequence from book. */ - p->sctl = (3*Aipm) | (d->mode*Aspd) | (0*Adet); + if((p->sstatus & SSmask) == (Isleepy | Spresent)) + ahciwakeup(c, mode); + /* disable power managment sequence from book. */ + p->sctl = 3*Aipm | mode*Aspd | 0*Adet; p->cmd &= ~Aalpe; + p->cmd |= Ast; p->ie = IEM; return 0; } static void +setstate(Drive *d, int state) +{ + ilock(d); + d->state = state; + iunlock(d); +} + +static void ahcienable(Ahba *h) { h->ghc |= Hie; @@ -778,34 +628,27 @@ } static int -countbits(ulong u) +countbits(u32int u) { - int n; + int i, n; n = 0; - for (; u != 0; u >>= 1) - if(u & 1) + for(i = 0; i < 32; i++) + if(u & (1<hba = (Ahba*)ctlr->mmio; + h = c->hba = (Ahba*)c->mmio; u = h->cap; - - if((u&Hsam) == 0) + if((u & Ham) == 0) h->ghc |= Hae; - - dprint("#S/sd%c: type %s port %#p: sss %ld ncs %ld coal %ld " - "%ld ports, 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) + 1, (u>>25) & 1, (u>>24) & 1, (u>>6) & 1); return countbits(h->pi); } @@ -814,7 +657,7 @@ { int wait; - h->ghc |= 1; + h->ghc |= Hhr; for(wait = 0; wait < 1000; wait += 100){ if(h->ghc == 0) return 0; @@ -823,44 +666,74 @@ return -1; } -static void -idmove(char *p, ushort *a, int n) +/* under development */ +static int +ahcibioshandoff(Ahba *h) { - int i; - char *op, *e; + int i, wait; - op = p; - for(i = 0; i < n/2; i++){ - *p++ = a[i] >> 8; - *p++ = a[i]; + if((h->cap2 & Boh) == 0) + return 0; + if((h->bios & Bos) == 0) + return 0; + + print("ahcibioshandoff: claim\n"); + h->bios |= Oos; + + wait = 25; + for(i = 0; i < wait; i++){ + delay(1); + if((h->bios & Bos) == 0) + break; + if(i < 25 && h->bios & Bb){ + print("ahcibioshandoff: busy\n"); + wait = 2000; + } } - *p = 0; - while(p > op && *--p == ' ') - *p = 0; - e = p; - for (p = op; *p == ' '; p++) - ; - memmove(op, p, n - (e - p)); + if(i == wait){ + print("ahcibioshandoff: timeout %.1ux\n", h->bios); + h->bios = Oos; + } + return 0; +} + +static char* +dstate(uint s) +{ + int i; + + for(i = 0; s; i++) + s >>= 1; + return diskstates[i]; +} + +static char* +tnam(Ctlr *c) +{ + return c->type->name; +} + +static char* +dnam(Drive *d) +{ + char *s; + + 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; - if(d->info == nil) { - d->infosz = 512 * sizeof(ushort); - d->info = malloc(d->infosz); - } - if(d->info == nil) { - d->info = d->tinyinfo; - d->infosz = sizeof d->tinyinfo; - } 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; @@ -868,25 +741,22 @@ 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; @@ -895,145 +765,146 @@ 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->type == Tsb600; +} + static void updatedrive(Drive *d) { - ulong cause, serr, s0, pr, ewake; - char *name; + u32int f, cause, serr, s0, pr, ewake; Aport *p; - static ulong last; + static u32int last; pr = 1; ewake = 0; + f = 0; p = d->port; cause = p->isr; + if(d->ctlr->type->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: %s: fatal\n", name); + dprint("%s: fatal\n", dnam(d)); } if(cause & Adhrs){ - if(p->task & (1<<5|1)){ - dprint("ahci: %s: Adhrs cause %#lux serr %#lux task %#lux\n", - name, cause, serr, p->task); - d->portm.flag |= Ferror; + if(p->task & 33){ + if(ignoreahdrs(d) && serr & ErrE) + f |= Fahdrs; + dprint("%s: Adhrs cause %ux serr %ux task %ux\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 %ux serr %ux task %ux sstat %.3ux\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 %ux ta %ux\n", dnam(d), cause, p->task); if(cause & (Aprcs|Aifs)){ s0 = d->state; - switch(p->sstatus & Devdet){ - case 0: /* no device */ + switch(p->sstatus & Smask){ + case Smissing: d->state = Dmissing; break; - case Devpresent: /* device but no phy comm. */ - if((p->sstatus & Intpm) == Intslumber || - (p->sstatus & Intpm) == Intpartpwr) - d->state = Dnew; /* slumbering */ + case Spresent: + if((p->sstatus & Imask) == Islumber) + d->state = Dnew; else d->state = Derror; break; - case Devpresent|Devphycomm: - /* 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 Devphyoffline: + 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] %.3ux\n", dnam(d), dstate(s0), + dstate(d->state), p->sstatus); if(s0 == Dready && d->state != Dready) - idprint("%s: pulled\n", name); /* wtf? */ + 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; } static void -pstatus(Drive *d, ulong s) +pstatus(Drive *d, u32int s) { /* - * s is masked with Devdet. - * * 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. */ + if(s) + d->lastseen = Ticks; switch(s){ - case 0: /* no device */ + default: + print("%s: pstatus: bad status %.3ux\n", dnam(d), s); + case Smissing: d->state = Dmissing; break; - case Devpresent: /* device but no phy. comm. */ + case Spresent: break; - case Devphycomm: /* should this be missing? need testcase. */ - dprint("ahci: pstatus 2\n"); - /* fallthrough */ - case Devpresent|Devphycomm: + case Sphylink: d->wait = 0; d->state = Dnew; break; - case Devphyoffline: + case Sbist: d->state = Doffline; break; - case Devphyoffline|Devphycomm: /* does this make sense? */ - d->state = Dnew; - break; } } static int configdrive(Drive *d) { - if(ahciconfigdrive(d) == -1) + if(ahciconfigdrive(d->ctlr->hba, &d->portc, d->mode) == -1) return -1; ilock(d); - pstatus(d, d->port->sstatus & Devdet); + pstatus(d, d->port->sstatus & Smask); iunlock(d); return 0; } static void -setstate(Drive *d, int state) -{ - ilock(d); - d->state = state; - iunlock(d); -} - -static void resetdisk(Drive *d) { uint state, det, stat; @@ -1041,9 +912,9 @@ p = d->port; det = p->sctl & 7; - stat = p->sstatus & Devdet; + 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; @@ -1051,79 +922,88 @@ d->portm.flag |= Ferror; clearci(p); /* satisfy sleep condition. */ wakeup(&d->portm); - if(stat != (Devpresent|Devphycomm)){ - /* device absent or phy not communicating */ - d->state = Dportreset; - iunlock(d); - return; - } d->state = Derror; iunlock(d); + if(stat != Sphylink){ + setstate(d, Dportreset); + return; + } + qlock(&d->portm); if(p->cmd&Ast && ahciswreset(&d->portc) == -1) setstate(d, Dportreset); /* get a bigger stick. */ - else { + else{ setstate(d, Dmissing); configdrive(d); } - dprint("ahci: %s: resetdisk: %s → %s\n", (d->unit? d->unit->name: nil), - diskstates[state], diskstates[d->state]); + dprint("%s: resetdisk: %s → %s\n", dnam(d), dstate(state), dstate(d->state)); qunlock(&d->portm); } static int newdrive(Drive *d) { - char *name; + char *s; Aportc *c; - Aportm *pm; + Aportm *m; c = &d->portc; - pm = &d->portm; + m = &d->portm; - name = d->unit->name; - if(name == 0) - name = "??"; - - if(d->port->task == 0x80) - return -1; - qlock(c->pm); - if(setudmamode(c, 5) == -1){ - dprint("%s: can't set udma mode\n", name); + qlock(c->m); + setfissig(m, c->p->sig); + if(identify(d) == -1){ + dprint("%s: identify failure\n", dnam(d)); goto lose; } - if(identify(d) == -1){ - dprint("%s: identify failure\n", name); + if(settxmode(c, m->udma) == -1){ + dprint("%s: can't set udma mode\n", dnam(d)); goto lose; } - if(pm->feat & Dpower && setfeatures(c, 0x85) == -1){ - pm->feat &= ~Dpower; + if(m->feat & Dpower && setfeatures(c, 0x85, 3*1000) == -1){ + m->feat &= ~Dpower; if(ahcirecover(c) == -1) goto lose; } - setstate(d, Dready); - qunlock(c->pm); - idprint("%s: %sLBA %,llud sectors: %s %s %s %s\n", d->unit->name, - (pm->feat & Dllba? "L": ""), d->sectors, d->model, d->firmware, - d->serial, d->mediachange? "[mediachange]": ""); + ilock(d); + d->state = Dready; + iunlock(d); + + qunlock(c->m); + + 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); - setstate(d, Dnull); - qunlock(c->pm); + idprint("%s: can't be initialized\n", dnam(d)); + ilock(d); + d->state = Dnull; + iunlock(d); + qunlock(c->m); return -1; } +enum { + Nms = 256, + Mphywait = 2*1024/Nms - 1, + Midwait = 16*1024/Nms - 1, + Mcomrwait = 64*1024/Nms - 1, +}; + 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 [%ux] ci %ux\n", + dnam(d), d->port->task, d->port->ci); d->state = Dreset; } } @@ -1137,13 +1017,13 @@ 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 %.4ux ss %.3ux]\n", + dstate(d->state), d->port->task, d->port->sstatus); return i; } @@ -1154,87 +1034,82 @@ 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: %06#ux -> %06#ux: %s\n", - name, olds[i], s, diskstates[d->state]); + dprint("%s: status: %.3ux -> %.3ux: %s\n", + dnam(d), olds[i], s, dstate(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 & (Intactive | Devdet)){ - case Devpresent: /* 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 %06#ux\n", name, s); + dprint("%s: unknown status %.3ux\n", dnam(d), s); /* fall through */ - case Intactive: /* 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 Intactive|Devphycomm|Devpresent: + case Iactive | Sphylink: + if(d->unit == nil) + break; if((++d->wait&Midwait) == 0){ - dprint("%s: slow reset %06#ux task=%#lux; %d\n", - name, s, d->port->task, d->wait); + dprint("%s: slow reset %.3ux task=%ux; %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); @@ -1248,23 +1123,22 @@ /* fallthrough */ case Derror: case Dreset: - dprint("%s: reset [%s]: mode %d; status %06#ux\n", - name, diskstates[d->state], d->mode, s); + dprint("%s: reset [%s]: mode %d; status %.3ux\n", + dnam(d), dstate(d->state), d->mode, s); iunlock(d); resetdisk(d); ilock(d); break; case Dportreset: portreset: - if(d->wait++ & 0xff && (s & Intactive) == 0) + if(d->wait++ & 0xff && (s & Iactive) == 0) break; - /* device is active */ - dprint("%s: portreset [%s]: mode %d; status %06#ux\n", - name, diskstates[d->state], d->mode, s); + dprint("%s: portreset [%s]: mode %d; status %.3ux\n", + dnam(d), dstate(d->state), d->mode, s); d->portm.flag |= Ferror; clearci(d->port); wakeup(&d->portm); - if((s & Devdet) == 0){ /* no device */ + if((s & Smask) == 0){ d->state = Dmissing; break; } @@ -1285,43 +1159,7 @@ for(;;){ tsleep(&up->sleep, return0, 0, Nms); for(i = 0; i < niadrive; i++) - if(iadrive[i] != nil) - checkdrive(iadrive[i], i); - } -} - -static void -isctlrjabbering(Ctlr *c, ulong cause) -{ - ulong now; - - now = TK2MS(MACHP(0)->ticks); - if (now > c->lastintr0) { - c->intrs = 0; - c->lastintr0 = now; - } - if (++c->intrs > Maxintrspertick) { - iprint("sdiahci: %lud intrs per tick for no serviced " - "drive; cause %#lux mport %d\n", - c->intrs, cause, c->mport); - c->intrs = 0; - } -} - -static void -isdrivejabbering(Drive *d) -{ - ulong now; - - now = TK2MS(MACHP(0)->ticks); - if (now > d->lastintr0) { - d->intrs = 0; - d->lastintr0 = now; - } - if (++d->intrs > Maxintrspertick) { - iprint("sdiahci: %lud interrupts per tick for %s\n", - d->intrs, d->unit->name); - d->intrs = 0; + checkdrive(iadrive[i], i); } } @@ -1329,91 +1167,250 @@ iainterrupt(Ureg*, void *a) { int i; - ulong cause, mask; + u32int cause, m; Ctlr *c; Drive *d; c = a; ilock(c); cause = c->hba->isr; - if (cause == 0) { - isctlrjabbering(c, cause); - // iprint("sdiahci: interrupt for no drive\n"); - iunlock(c); - return; - } - for(i = 0; cause && i <= c->mport; i++){ - mask = 1 << i; - if((cause & mask) == 0) + for(i = 0; cause; i++){ + m = 1 << i; + if((cause & m) == 0) continue; + cause &= ~m; d = c->rawdrive + i; ilock(d); - isdrivejabbering(d); - if(d->port->isr && c->hba->pi & mask) + if(d->port->isr && c->hba->pi & m) updatedrive(d); - c->hba->isr = mask; + c->hba->isr = m; iunlock(d); - - cause &= ~mask; - } - if (cause) { - isctlrjabbering(c, cause); - iprint("sdiachi: intr cause unserviced: %#lux\n", cause); } iunlock(c); } -/* checkdrive, called from satakproc, will prod the drive while we wait */ -static void -awaitspinup(Drive *d) +static int +ahciencreset(Ctlr *c) { - int ms; - ushort s; - char *name; + Ahba *h; - ilock(d); - if(d->unit == nil || d->port == nil) { - panic("awaitspinup: nil d->unit or d->port"); - iunlock(d); - return; - } - name = (d->unit? d->unit->name: nil); - s = d->port->sstatus; - if(!(s & Devpresent)) { /* never going to be ready */ - dprint("awaitspinup: %s absent, not waiting\n", name); - iunlock(d); - return; - } + if(c->enctype == Eesb) + return 0; + h = c->hba; + h->emctl |= Emrst; + while(h->emctl & Emrst) + delay(1); + return 0; +} - for (ms = 20000; ms > 0; ms -= 50) - switch(d->state){ - case Dnull: - /* absent; done */ - iunlock(d); - dprint("awaitspinup: %s in null state\n", name); - return; - case Dready: - case Dnew: - if(d->sectors || d->mediachange) { - /* ready to use; done */ - iunlock(d); - dprint("awaitspinup: %s ready!\n", name); - return; - } - /* fall through */ - default: - case Dmissing: /* normal waiting states */ - case Dreset: - case Doffline: /* transitional states */ - case Derror: - case Dportreset: - iunlock(d); - asleep(50); - ilock(d); - break; - } - print("awaitspinup: %s didn't spin up after 20 seconds\n", name); - iunlock(d); +/* + * 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(Drive *d, uint seq) +{ + ushort i; + Ledport *p; + + p = d; + 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("%s: led %,.011ub %ud\n", dnam(d), p->ledbits, seq%8); + return 1; + } + return 0; +} + +static int +blink(Drive *d, uint 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", dnam(d), c->enctype); + case Elmt: + memset(&msg, 0, sizeof msg); + msg.type = Mled; + msg.dsize = 0; + msg.msize = Ledmsz - 4; + msg.led[0] = d->ledbits; + msg.led[1] = d->ledbits>>8; + msg.pm = 0; + msg.hba = d->driveno; + memmove(c->enctx, &msg, Ledmsz); + 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[c - iactlr] == 0) + j += c->ndrive; + 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 @@ -1426,17 +1423,14 @@ 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 */ - - /* - * hang around until disks are spun up and thus available as - * nvram, dos file systems, etc. you wouldn't expect it, but - * the intel 330 ssd takes a while to `spin up'. - */ - awaitspinup(d); return 1; } @@ -1449,11 +1443,9 @@ c = s->ctlr; ilock(c); - if(!c->enabled) { - if(once == 0) { - once = 1; - kproc("ahci", satakproc, 0); - } + if(!c->enabled){ + if(once == 0) + kproc("iasata", satakproc, 0); if(c->ndrive == 0) panic("iaenable: zero s->ctlr->ndrive"); pcisetbme(c->pci); @@ -1462,6 +1454,8 @@ /* supposed to squelch leftover interrupts here. */ ahcienable(c->hba); c->enabled = 1; + if(++once == niactlr) + kproc("ialed", ledkproc, 0); } iunlock(c); return 1; @@ -1494,166 +1488,113 @@ 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 ss, uint n, vlong lba) { - uchar *c, acmd, dir, llba; + uchar *c; + uint flags; Alist *l; - Actab *t; - Aportm *pm; - Aprdt *p; - static uchar tab[2][2] = { 0xc8, 0x25, 0xca, 0x35, }; - - pm = &d->portm; - dir = *cmd != 0x28; - llba = pm->feat&Dllba? 1: 0; - acmd = tab[dir][llba]; - qlock(pm); - l = pm->list; - t = pm->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] = Obs | 0x40; /* 0x40 == 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; + l = m->list; + c = m->ctab->cfis; + rwfis(m, c, rw, n, lba); + flags = Lpref; + if(rw == SDwrite) + flags |= Lwrite; + mkalist(m, flags, data, ss*n); return l; } static Alist* -ahcibuildpkt(Aportm *pm, SDreq *r, void *data, int n) +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(pm); - l = pm->list; - t = pm->ctab; + l = m->list; + t = m->ctab; c = t->cfis; - - fill = pm->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] = Obs; - - *(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; - - if(data == 0) - return l; + flags |= Lwrite; + mkalist(m, flags, data, n); + 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; } static int waitready(Drive *d) { - ulong s, i, δ; + u32int s; + ulong i, δ; for(i = 0; i < 15000; i += 250){ 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 & Intpm) == 0 && δ > 1500) - return -1; /* no detect */ - if(d->state == Dready && - (s & Devdet) == (Devphycomm|Devpresent)) - 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); - setstate(d, Doffline); + print("%s: not responding; offline\n", dnam(d)); + ilock(d); + d->state = Doffline; + iunlock(d); return -1; } @@ -1663,7 +1604,7 @@ int i; qlock(&d->portm); - while ((i = waitready(d)) == 1) { /* could wait forever? */ + while ((i = waitready(d)) == 1) { qunlock(&d->portm); esleep(1); qlock(&d->portm); @@ -1684,65 +1625,42 @@ } static int -iariopkt(SDreq *r, Drive *d) +io(Drive *d, uint proto, int to, int interrupt) { - int n, count, try, max, flag, task, wormwrite; - 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: %04#ux %04#ux %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, d->infosz); - 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; } - /* d->portm qlock held here */ 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()) - ; - /* don't sleep here forever */ - tsleep(&d->portm, ahciclear, &as, 3*1000); + if(interrupt){ + d->active--; + d->port->ci = 0; + if(ahcicomreset(&d->portc) == -1) + setstate(d, Dreset); + return SDtimeout; + } + sleep(&d->portm, ahciclear, &as); poperror(); - if(!ahciclear(&as)) { - qunlock(&d->portm); - print("%s: ahciclear not true after 3 seconds\n", name); - r->status = SDcheck; - return SDcheck; - } d->active--; ilock(d); @@ -1750,65 +1668,117 @@ 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; - } - /* - * write retries cannot succeed on write-once media, - * so just accept any failure. - */ - wormwrite = 0; - switch(d->unit->inquiry[0] & SDinq0periphtype){ - case SDperworm: - case SDpercd: - switch(cmd[0]){ - case 0x0a: /* write (6?) */ - case 0x2a: /* write (10) */ - case 0x8a: /* long write (16) */ - case 0x2e: /* write and verify (10) */ - wormwrite = 1; - break; - } - break; - } - if (!wormwrite) { - 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 task=%#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->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, d->secsize, 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; @@ -1819,330 +1789,416 @@ 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, d->infosz)) != SDnostatus){ + if((i = sdfakescsi(r)) != SDnostatus){ r->status = i; return i; } - if(*cmd != 0x28 && *cmd != 0x2a){ - print("%s: bad cmd %.2#ux\n", name, cmd[0]); - r->status = SDcheck; - return SDcheck; + 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; +} - lba = cmd[2]<<24 | cmd[3]<<16 | cmd[4]<<8 | cmd[5]; - count = cmd[7]<<8 | cmd[8]; - if(r->data == nil) +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; - 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; - } - /* d->portm qlock held here */ - ilock(d); - d->portm.flag = 0; - iunlock(d); - p->ci = 1; + } + r->cmd[0] = 0x27; + r->cmd[1] = 0x80; + r->cmd[7] |= 0xa0; + return SDnostatus; +} - as.p = p; - as.i = 1; - d->intick = MACHP(0)->ticks; - d->active++; - - while(waserror()) - ; - /* don't sleep here forever */ - tsleep(&d->portm, ahciclear, &as, 3*1000); - poperror(); - if(!ahciclear(&as)) { - qunlock(&d->portm); - print("%s: ahciclear not true after 3 seconds\n", name); - r->status = SDcheck; - return SDcheck; - } +static int +iaataio(SDreq *r) +{ + int try; + Ctlr *c; + Drive *d; + SDunit *u; - d->active--; - ilock(d); - flag = d->portm.flag; - task = d->port->task; - iunlock(d); + u = r->unit; + c = u->dev->ctlr; + d = c->drive[u->subno]; - if(task & (Efatal<<8) || - task & (ASbsy|ASdrq) && d->state == Dready){ - d->port->ci = 0; - ahcirecover(&d->portc); - task = d->port->task; - } + 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); - if(flag == 0){ - if(++try == 10){ - print("%s: bad disk\n", name); - r->status = SDeio; - return SDeio; - } - print("%s: retry blk %lld\n", name, lba); - goto retry; - } - if(flag & Ferror){ - print("%s: i/o error task=%#ux @%,lld\n", - name, task, lba); - r->status = SDeio; + switch(r->status){ + case SDtimeout: + return sdsetsense(r, SDcheck, 11, 0, 6); + case SDeio: return SDeio; + case SDretry: + continue; } - - count -= n; - lba += n; - data += n * unit->secsize; + r->rlen = r->dlen; + if((r->ataproto & Pprotom) == Ppkt) + r->rlen = d->portm.list->len; + return sdr(r, d, r->status); } - r->rlen = data - (uchar*)r->data; - r->status = SDok; - return SDok; + print("%s: bad disk\n", dnam(d)); + r->status = SDeio; + return SDeio; } -/* - * configure drives 0-5 as ahci sata (c.f. errata). - * what about 6 & 7, as claimed by marvell 0x9123? - */ +/* 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, pcicfgr16(p, 0x92) | 0x3f); /* ports 0-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 (sic) */ 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 and 6 ports; from ich9 datasheet */ + /* enable ahci mode; from ich9 datasheet */ pcicfgw16(c->pci, 0x90, 1<<6 | 1<<5); } -static int -didtype(Pcidev *p) +static void +sbsetupahci(Pcidev *p) { - switch(p->vid){ - case Vintel: - if((p->did & 0xfffc) == 0x2680) - return Tesb; - /* - * 0x27c4 is the intel 82801 in compatibility (not sata) mode. - */ - if (p->did == 0x1e02 || /* c210 */ - p->did == 0x24d1 || /* 82801eb/er */ - (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 & 0xfff8) == 0x3b28) /* pchm */ - return Tich; - break; - case Vatiamd: - if(p->did == 0x4380 || p->did == 0x4390 || p->did == 0x4391){ - print("detected sb600 vid %#ux did %#ux\n", p->vid, p->did); - return Tsb600; - } - break; - case Vmarvell: - if (p->did == 0x9123) - print("ahci: marvell sata 3 controller has delusions " - "of something on unit 7\n"); - break; - } - if(p->ccrb == Pcibcstore && p->ccru == Pciscsata && p->ccrp == 1){ - print("ahci: Tunk: vid %#4.4ux did %#4.4ux\n", p->vid, p->did); - return Tunk; - } - return -1; + 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 -newctlr(Ctlr *ctlr, SDev *sdev, int nunit) +esbenc(Ctlr *c) { - int i, n; - Drive *drive; + c->encsz = 1; + c->enctx = (u32int*)(c->mmio + 0xa0); + c->enctype = Eesb; + c->enctx[0] = 0; + return 0; +} - ctlr->ndrive = sdev->nunit = nunit; - ctlr->mport = ctlr->hba->cap & ((1<<5)-1); +static int +ahciencinit(Ctlr *c) +{ + uint type, sz, o; + u32int *bar; + Ahba *h; - i = (ctlr->hba->cap >> 20) & ((1<<4)-1); /* iss */ - print("#S/sd%c: %s: %#p %s, %d ports, irq %d\n", sdev->idno, - Tname(ctlr), ctlr->physio, descmode[i], nunit, ctlr->pci->intl); - /* map the drives -- they don't all need to be enabled. */ - n = 0; - ctlr->rawdrive = malloc(NCtlrdrv * sizeof(Drive)); - if(ctlr->rawdrive == nil) { - print("ahci: out of memory\n"); + 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; } - for(i = 0; i < NCtlrdrv; i++) { - drive = ctlr->rawdrive + i; - drive->portno = i; - drive->driveno = -1; - drive->sectors = 0; - drive->serial[0] = ' '; - drive->ctlr = ctlr; - if((ctlr->hba->pi & (1<port = (Aport*)(ctlr->mmio + 0x80*i + 0x100); - drive->portc.p = drive->port; - drive->portc.pm = &drive->portm; - drive->driveno = n++; - ctlr->drive[drive->driveno] = drive; - iadrive[niadrive + drive->driveno] = drive; + + sz = h->emloc & 0xffff; + o = h->emloc>>16; + if(sz == 0 || o == 0) + return -1; + bar = c->lmmio; + ledprint("size = %#.4ux; loc = %#.4ux*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; } - for(i = 0; i < n; i++) - if(ahciidle(ctlr->drive[i]->port) == -1){ - dprint("ahci: %s: port %d wedged; abort\n", - Tname(ctlr), i); - return -1; - } - for(i = 0; i < n; i++){ - ctlr->drive[i]->mode = DMsatai; - configdrive(ctlr->drive[i]); + c->enctype = type; + return 0; +} + +static ushort itab[] = { + 0xfffc, 0x2680, Tesb, + 0xfffb, 0x27c1, Tahci, /* 82801g[bh]m */ + 0xffff, 0x2821, Tahci, /* 82801h[roh] */ + 0xfffe, 0x2824, Tahci, /* 82801h[b] */ + 0xfeff, 0x2829, Tahci, /* ich8 */ + 0xfffe, 0x2922, Tahci, /* ich9 */ + 0xffff, 0x3a02, Tahci, /* 82801jd/do */ + 0xfefe, 0x3a22, Tahci, /* ich10, pch */ + 0xfff7, 0x3b28, Tahci, /* pchm */ + 0xfffe, 0x3b22, Tahci, /* pch */ +}; + +static int +didtype(Pcidev *p) +{ + int type, i; + + type = Tahci; + switch(p->vid){ + default: + break; + case 0x8086: + for(i = 0; i < nelem(itab); i += 3) + if((p->did & itab[i]) == itab[i+1]) + return itab[i+2]; + break; + case 0x1002: + 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 0x197b: + case 0x10b9: + type = Tjmicron; + break; } - return n; + if(p->ccrb == Pcibcstore && p->ccru == 6 && p->ccrp == 1) + return type; + return -1; } static SDev* iapnp(void) { - int n, nunit, type; - ulong io; + int i, n, nunit, type; + uintptr io; 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", cttab[type].name); 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->physio = (uchar *)io; + c->type = cttab + type; + io = p->mem[Abar].bar & ~(uintptr)0xf; c->mmio = vmap(io, p->mem[Abar].size); - if(c->mmio == 0){ - print("ahci: %s: address %#luX in use did=%#x\n", - Tname(c), io, p->did); + if(c->mmio == nil){ + print("%s: %T: address %#P in use\n", + tnam(c), p->tbdf, io); continue; } - c->lmmio = (ulong*)c->mmio; + c->lmmio = (u32int*)c->mmio; c->pci = p; - 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); + ahcibioshandoff((Ahba*)c->mmio); // 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; } - n = newctlr(c, s, nunit); - if(n < 0) - continue; + c->ndrive = s->nunit = 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++){ + d = c->rawdrive + i; + d->portno = i; + d->driveno = -1; + d->sectors = 0; + d->serial[0] = ' '; + d->ctlr = c; + if((c->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; + d->driveno = n++; + c->drive[d->driveno] = d; + iadrive[niadrive + d->driveno] = d; + } + for(i = 0; i < n; i++) + if(ahciidle(c->drive[i]->port) == -1){ + print("%s: port %d wedged; abort\n", + tnam(c), i); + goto loop; + } + for(i = 0; i < n; i++){ + c->drive[i]->mode = DMautoneg; + configdrive(c->drive[i]); + } + ahciencinit(c); + niadrive += n; niactlr++; - if(head) - tail->next = s; - else - head = s; - tail = s; - } - return head; -} - -static char* smarttab[] = { - "unset", - "error", - "threshold exceeded", - "normal" + sdadddevs(s); + i = (c->hba->cap >> 21) & 1; + print("#S/%s: %s: sata-%s with %d ports\n", s->name, + tnam(c), "I\0II" + i*2, nunit); + } + return nil; +} + +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, u32int 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; @@ -2152,48 +2208,34 @@ 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%llux\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]); + p = seprint(p, e, "no disk present [%s]\n", dstate(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 %06#lux\n", - o->task, o->cmd, o->serror, buf, + p = seprint(p, e, "reg\ttask %ux cmd %ux serr %ux %s ci %ux is %ux " + "sig %ux sstatus %.3ux\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; @@ -2201,50 +2243,16 @@ } 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) { int i; - for(i = 0; i < nelem(diskstates); i++) + for(i = 1; i < nelem(diskstates); i++) if(strcmp(state, diskstates[i]) == 0) break; if(i == nelem(diskstates)) error(Ebadctl); - setstate(d, i); -} - -/* - * force this driver to notice a change of medium if the hardware doesn't - * report it. - */ -static void -changemedia(SDunit *u) -{ - Ctlr *c; - Drive *d; - - c = u->dev->ctlr; - d = c->drive[u->subno]; - ilock(d); - d->mediachange = 1; - u->sectors = 0; - iunlock(d); + setstate(d, 1 << i-1); } static int @@ -2253,64 +2261,17 @@ char **f; Ctlr *c; Drive *d; - uint i; c = u->dev->ctlr; d = c->drive[u->subno]; f = cmd->f; - if(strcmp(f[0], "change") == 0) - changemedia(u); - else 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 cmderror(cmd, Ebadctl); - return -1; - } return 0; } @@ -2339,42 +2300,63 @@ 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; - -#define has(x, str) if(cap & (x)) p = seprint(p, e, "%s ", (str)) + u32int cap; + Ahba *h; + Ctlr *c; - ctlr = sdev->ctlr; - hba = ctlr->hba; - p = seprint(p, e, "sd%c ahci port %#p: ", sdev->idno, ctlr->physio); - 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, tnam(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 %d ncs %d np %d ghc %ux isr %ux pi %ux %s ver %ux\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 @@ -2386,14 +2368,14 @@ 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); @@ -2402,19 +2384,15 @@ cmderror(cmd, Ebadarg); case 1: *v ^= 1; - break; + return 0; case 2: - if(f[1]) - *v = strcmp(f[1], "on") == 0; - else - *v ^= 1; - break; + *v = strcmp(f[1], "on") == 0; + return 0; } - return 0; } SDifc sdiahciifc = { - "iahci", + "ahci", iapnp, nil, /* legacy */ @@ -2427,9 +2405,10 @@ iarctl, iawctl, - scsibio, + ahcibio, nil, /* probe */ nil, /* clear */ iartopctl, iawtopctl, + iaataio, }; --- /n/sources/plan9/sys/src/9/pc/sdmv50xx.c Thu Feb 28 21:02:44 2008 +++ /sys/src/9/pc/sdmv50xx.c Tue Apr 22 00:00:00 2014 @@ -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 @@ 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), - - SFdone = (1<<0), - SFerror = (1<<1), - - SRBident = 0, - SRBread, - SRBwrite, - SRBsmart, - - SRBnodata = 0, - SRBdatain, - SRBdataout, + 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, - RQread = 1, /* data coming IN from device */ + SFdone = 1<<0, + SFerror = 1<<1, - 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 @@ 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, - -}; - -static char* diskstates[Dlast] = { - "null", - "new", - "ready", - "error", - "missing", - "reset", + /* sata mode */ + DMautoneg = 0, + DMsatai, + DMsataii, }; -extern SDifc sdmv50xxifc; - typedef struct Arb Arb; typedef struct Bridge Bridge; typedef struct Chip Chip; @@ -162,12 +129,6 @@ Edma *edma; }; -enum { - DMautoneg, - DMsatai, - DMsataii, -}; - struct Drive { Lock; @@ -175,25 +136,26 @@ 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 @@ Srb *srbhead; Srb *srbtail; - int driveno; /* ctlr*NCtlrdrv + unit */ + int driveno; /* ctlr*NCtlrdrv + unit */ }; struct Ctlr @@ -252,7 +214,7 @@ /* * 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 @@ 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 @@ 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 @@ ulong ts; /* time stamp */ }; -static Drive *mvsatadrive[NDrive]; -static int nmvsatadrive; - -/* - * 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; +static Ctlr *mvsatactlr[NCtlr]; +static Drive *mvsatadrive[NDrive]; +static int nmvsatadrive; +static char *diskstates[Dlast] = { + "null", + "new", + "ready", + "error", + "missing", + "reset", +}; - 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 @@ 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 @@ 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 @@ 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 @@ } } +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 @@ 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 @@ 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 @@ 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 @@ 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 @@ { 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ [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 @@ 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 @@ 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 @@ * 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 @@ *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 @@ * 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ { int i; - while(waserror()) - ; - for(;;){ tsleep(&up->sleep, return0, 0, Nms); for(i = 0; i < nmvsatadrive; i++) @@ -1150,22 +1107,37 @@ } } -/* - * 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) { int i, nunit; uchar *base; - ulong io, n, *mem; + ulong n, *mem; + uintptr io; 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 +1145,8 @@ 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,23 +1164,20 @@ 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; + io = p->mem[0].bar & ~(uintptr)0xf; mem = (ulong*)vmap(io, p->mem[0].size); if(mem == 0){ - print("sdmv50xx: address 0x%luX in use\n", io); + print("sdmv50xx: address %#P in use\n", io); free(sdev); free(ctlr); continue; @@ -1242,6 +1213,7 @@ } 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 +1221,9 @@ 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 +1235,6 @@ 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 +1246,13 @@ 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) { @@ -1310,30 +1278,6 @@ } /* - * 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. */ static int @@ -1354,20 +1298,19 @@ /* * 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 +1325,6 @@ 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 +1333,12 @@ 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 +1423,10 @@ { 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 +1468,6 @@ Ctlr *ctlr; Drive *drive; - USED(unit); if(strcmp(cb->f[0], "reset") == 0){ ctlr = unit->dev->ctlr; drive = &ctlr->drive[unit->subno]; @@ -1550,34 +1480,6 @@ 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 +1491,9 @@ 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 +1503,42 @@ 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 +1547,272 @@ 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; + + 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 +1824,15 @@ 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, }; /* --- /n/sources/plan9/sys/src/9/pcboot/bootmkfile Thu Mar 13 20:55:03 2014 +++ /sys/src/9/pcboot/bootmkfile Tue Apr 22 00:00:00 2014 @@ -63,6 +63,7 @@ /$objtype/lib/libflate.a\ /$objtype/lib/libip.a\ /$objtype/lib/libc.a\ + /$objtype/lib/libfis.a\ ETHER=`{echo devether.c ether*.c | sed 's/\.c/.'$O'/g'} --- /n/sources/plan9/sys/src/9/pcboot/load Thu May 10 22:40:08 2012 +++ /sys/src/9/pcboot/load Tue Apr 22 00:00:00 2014 @@ -29,7 +29,7 @@ sdata pci sdscsi sd53c8xx pci sdscsi sdmylex pci sdscsi - sdiahci pci sdscsi + sdiahci pci sdscsi led # sdflop port --- /n/sources/plan9/sys/src/9/port/aoe.h Fri Jun 28 21:35:19 2013 +++ /sys/src/9/port/aoe.h Tue Apr 22 00:00:00 2014 @@ -1,9 +1,8 @@ -/* - * ATA-over-Ethernet (AoE) protocol - */ enum { ACata, ACconfig, + ACmask, + ACres, }; enum { @@ -15,26 +14,65 @@ }; 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, + + /* mask directives */ + MDnop = 0, + MDadd, + MDdel, + + /* 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, + AFerr = 1<<2, + AFrsp = 1<<3, - AAFwrite= 1, - AAFext = 1<<6, + AAFwrite = 1, + AAFext = 1<<6, }; -typedef struct { +typedef struct Aoehdr Aoehdr; +typedef struct Aoeata Aoeata; +typedef struct Aoecfg Aoecfg; +typedef struct Aoemd Aoemd; +typedef struct Aoem Aoem; +typedef struct Aoerr Aoerr; + +struct Aoehdr { uchar dst[Eaddrlen]; uchar src[Eaddrlen]; uchar type[2]; @@ -44,32 +82,43 @@ uchar minor; uchar cmd; uchar tag[4]; - uchar payload[]; -} Aoehdr; - -#define AOEHDRSZ offsetof(Aoehdr, payload[0]) +}; -typedef struct { - Aoehdr; +struct Aoeata { 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; +struct Aoecfg { uchar bufcnt[2]; uchar fwver[2]; uchar scnt; uchar verccmd; uchar cslen[2]; - uchar payload[]; -} Aoeqc; +}; + +struct Aoemd { + uchar dres; + uchar dcmd; + uchar ea[Eaddrlen]; +}; + +struct Aoem { + uchar mres; + uchar mcmd; + uchar merr; + uchar mcnt; +}; + +typedef struct Aoerr { + uchar rcmd; + uchar nea; + uchar ea0[]; +}; -#define AOEQCSZ offsetof(Aoeqc, payload[0]) +extern char Echange[]; +extern char Enotup[]; --- /n/sources/plan9/sys/src/9/port/devaoe.c Thu Sep 5 18:37:09 2013 +++ /sys/src/9/port/devaoe.c Tue Apr 22 00:00:00 2014 @@ -1,6 +1,6 @@ /* - * © 2005-2010 coraid - * ATA-over-Ethernet (AoE) storage initiator + * © 2005-13 coraid + * aoe storage initiator */ #include "u.h" @@ -15,6 +15,7 @@ #include "etherif.h" #include "../ip/ip.h" #include "../port/aoe.h" +#include #pragma varargck argpos eventlog 1 @@ -22,26 +23,29 @@ #define uprint(...) snprint(up->genbuf, sizeof up->genbuf, __VA_ARGS__); enum { - Maxunits = 0xff, + Typebits = 4, + Unitbits = 12, + L3bits = 4, + Maxtype = (1<>4) & 0xff) -#define L(q) (((ulong)(q).path>>12) & 0xf) -#define QID(u, t) ((u)<<4 | (t)) -#define Q3(l, u, t) ((l)<<8 | QID(u, t)) +#define TYPE(q) ((ulong)(q).path & Maxtype) +#define UNIT(q) (((ulong)(q).path>>Typebits) & Maxunits) +#define L(q) (((ulong)(q).path>>Typebits+Unitbits) & Maxl3) +#define QID(u, t) ((u)<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 +71,17 @@ 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 +90,9 @@ Cwr = 0x30, Cwrext = 0x34, Cid = 0xec, + + Alloc = 0x01234567, + Free = 0x89abcdef, }; enum { @@ -104,37 +106,20 @@ * 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 +133,10 @@ int nea; ulong eaidx; uchar eatab[Nea][Eaddrlen]; + int datamtu; ulong npkt; ulong resent; - ushort flag; + uchar flag; ulong rttavg; ulong mintimer; @@ -159,8 +145,8 @@ typedef struct Srb Srb; struct Srb { Rendez; + uint state; Srb *next; - int shared; /* Srb shared with kproc (don't free) */ ulong ticksent; ulong len; vlong sector; @@ -198,11 +184,11 @@ 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; @@ -211,6 +197,8 @@ vlong realbsize; uint maxbcnt; + uint maxmtu; + ulong lostjumbo; ushort nout; ushort maxout; ulong lastwadj; @@ -218,7 +206,7 @@ Srb *tail; Srb *inprocess; - /* magic numbers 'R' us */ + Sfis; char serial[20+1]; char firmware[8+1]; char model[40+1]; @@ -251,12 +239,13 @@ Netlink nl[Nnetlink]; } netlinks; -extern Dev aoedevtab; -static Ref units; -static Ref drivevers; -static int debug; -static int autodiscover = 1; -static int rediscover; +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"; static Srb* srballoc(ulong sz) @@ -264,11 +253,9 @@ Srb *srb; srb = malloc(sizeof *srb+sz); - if(srb == nil) - error(Enomem); + srb->state = Alloc; srb->dp = srb->data = srb+1; - srb->ticksent = MACHP(0)->ticks; - srb->shared = 0; + srb->ticksent = Ticks; return srb; } @@ -278,29 +265,63 @@ Srb *srb; srb = malloc(sizeof *srb); - if(srb == nil) - error(Enomem); + srb->state = Alloc; srb->dp = srb->data = db; - srb->ticksent = MACHP(0)->ticks; - srb->shared = 0; + srb->ticksent = Ticks; return srb; } +static int +srbready(void *v) +{ + Srb *s; + + s = v; + return s->nout == 0 && (s->len == 0 || s->error != nil); +} + static void srbfree(Srb *srb) { - while(srb->shared) + int n; + + for(n = 0; srb->state != Free; n++) sched(); free(srb); } +/* under Aoedev qlock() so setting of srb->state is safe */ static void -srberror(Srb *srb, char *s) +srbwakeup(Srb *srb) { - srb->error = s; - srb->nout--; - if(srb->nout == 0) + 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 @@ -308,28 +329,25 @@ { 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) { @@ -395,13 +413,13 @@ 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; } @@ -411,7 +429,7 @@ { int n; - n = MACHP(0)->ticks & 0xffff; + n = Ticks & 0xffff; n -= tag & 0xffff; if(n < 0) n += 1<<16; @@ -425,7 +443,7 @@ do { t = ++d->lasttag << 16; - t |= MACHP(0)->ticks & 0xffff; + t |= Ticks & 0xffff; } while (t == Tfree || t == Tmgmt); return t; } @@ -438,8 +456,8 @@ d->flag &= ~Dup; f = d->frames; e = f + d->nframes; - for(; f < e; f->tag = Tfree, f->srb = nil, f++) - frameerror(d, f, Eaoedown); + for(; f < e; f++) + frameerror(d, f, Enotup); d->inprocess = nil; eventlog("%æ: removed; %s\n", d, err); } @@ -500,16 +518,23 @@ 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) +hset(Aoedev *d, Frame *f, Aoehdr *h, int cmd, int new) { 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; @@ -517,7 +542,7 @@ 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; } @@ -530,11 +555,13 @@ h->minor = d->minor; h->cmd = cmd; - hnputl(h->tag, f->tag = newtag(d)); + if(new) + f->tag = newtag(d); + hnputl(h->tag, f->tag); f->dl = l; f->nl = l->nl; f->eaidx = i; - f->ticksent = MACHP(0)->ticks; + f->ticksent = Ticks; return f->tag; } @@ -544,10 +571,12 @@ { 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, 0) == -1) return -1; + a = (Aoeata*)(f->hdr + Aoehsz); n = f->bcnt; if(n > d->maxbcnt){ n = d->maxbcnt; /* mtu mismatch (jumbo fail?) */ @@ -558,6 +587,7 @@ 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(); @@ -565,7 +595,7 @@ } static void -discover(int major, int minor) +discover(uint major, uint minor) { Aoehdr *h; Block *b; @@ -592,7 +622,6 @@ h->minor = minor; h->cmd = ACconfig; poperror(); - /* send b down the queue */ devtab[nl->dc->type]->bwrite(nl->dc, b, 0); } } @@ -606,7 +635,7 @@ { 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; @@ -622,7 +651,7 @@ } nbc = Nbcms/Nms; } - starttick = MACHP(0)->ticks; + starttick = Ticks; rlock(&devs); for(d = devs.d; d; d = d->next){ if(!canqlock(d)) @@ -645,13 +674,13 @@ 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; @@ -660,18 +689,18 @@ 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; @@ -684,7 +713,7 @@ 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); } @@ -693,10 +722,14 @@ 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 ifbuf-1); + ifbuf[sizeof ifbuf-1] = 0; + if((n = tokenize(ifbuf, f, nelem(f))) < 1) return; /* goo! */ for(i = 0; i < n; i++){ @@ -745,22 +778,40 @@ return c; } +static int +unitseq(Chan *c, uint unit, Dir *dp) +{ + int i, rv; + Qid q; + Aoedev *d; + + i = 0; + rv = -1; + rlock(&devs); + for(d = devs.d; d; d = d->next) + if(i++ == unit){ + mkqid(&q, QID(d->unit, Qunitdir), 0, QTDIR); + devdir(c, q, unitname(d), 0, eve, 0555, dp); + rv = 1; + break; + } + runlock(&devs); + return rv; +} + static Aoedev* unit2dev(ulong unit) { - int i; Aoedev *d; rlock(&devs); - i = 0; for(d = devs.d; d; d = d->next) - if(i++ == unit){ + 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; } @@ -877,13 +928,7 @@ if(s < Qtopfiles) return topgen(c, Qtopbase + s, dp); s -= Qtopfiles; - if(s >= units.ref) - return -1; - mkqid(&q, QID(s, Qunitdir), 0, QTDIR); - d = unit2dev(s); - assert(d != nil); - devdir(c, q, unitname(d), 0, eve, 0555, dp); - return 1; + return unitseq(c, s, dp); case Qtopctl: case Qtoplog: return topgen(c, TYPE(c->qid), dp); @@ -951,7 +996,7 @@ nexterror(); } if(!UP(d)) - error(Eaoedown); + error(Enotup); c = devopen(c, omode, 0, 0, aoegen); d->nopen++; poperror(); @@ -982,6 +1027,7 @@ ulong bcnt; char extbit, writebit; Aoeata *ah; + Aoehdr *h; Srb *srb; extbit = 0x4; @@ -991,13 +1037,14 @@ 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) == -1){ d->inprocess = nil; return; } + ah = (Aoeata*)(f->hdr + Aoehsz); f->dp = srb->dp; f->bcnt = bcnt; f->lba = srb->sector; @@ -1005,7 +1052,7 @@ ah->scnt = bcnt / Aoesectsz; putlba(ah, f->lba); - if(d->flag & Dllba) + if(d->feat & Dllba) ah->aflag |= AAFext; else { extbit = 0; @@ -1030,13 +1077,12 @@ 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* @@ -1084,15 +1130,6 @@ l->rttavg += n >> 2; } -static int -srbready(void *v) -{ - Srb *s; - - s = v; - return s->error || (s->nout == 0 && s->len == 0); -} - static Frame* getframe(Aoedev *d, int tag) { @@ -1119,7 +1156,7 @@ { Frame *f; - while ((f = freeframe(d)) != nil) { + while(f = freeframe(d)) { if(d->inprocess == nil){ if(d->head == nil) return; @@ -1140,19 +1177,23 @@ qunlock(d); nexterror(); } + if(!UP(d)) + error(Eio); srb->next = nil; if(d->tail) d->tail->next = srb; d->tail = srb; if(d->head == nil) d->head = srb; - srb->shared = 1; work(d); poperror(); qunlock(d); - while(waserror()) - ; + while(waserror()){ + qlock(d); + srberror(d, srb, "interrupted"); + qunlock(d); + } sleep(srb, srbready, srb); poperror(); } @@ -1163,12 +1204,14 @@ 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; @@ -1182,11 +1225,8 @@ 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; @@ -1200,10 +1240,9 @@ 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; @@ -1220,14 +1259,14 @@ return n; } -static char * -pflag(char *s, char *e, uchar f) +static char* +aoeflag(char *s, char *e, uchar f) { uchar i; - for(i = 0; i < 8; i++) - if(f & (1 << i)) - 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"); } @@ -1238,8 +1277,6 @@ char *state, *s, *p, *e; s = p = malloc(READSTR); - if(s == nil) - error(Enomem); e = p + READSTR; state = "down"; @@ -1248,15 +1285,17 @@ 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; @@ -1281,17 +1320,35 @@ case Qdata: return rw(d, Read, db, len, off); case Qconfig: - if (!UP(d)) - error(Eaoedown); + if(!UP(d)) + error(Enotup); return readmem(off, db, len, d->config, d->nconfig); case Qident: - if (!UP(d)) - error(Eaoedown); + 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) { int i; @@ -1306,8 +1363,6 @@ l = d->dl + i; s = p = malloc(READSTR); - if(s == nil) - error(Enomem); e = s + READSTR; p = seprint(p, e, "addr: "); @@ -1316,15 +1371,18 @@ 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; @@ -1341,8 +1399,6 @@ Netlink *n; s = p = malloc(READSTR); - if(s == nil) - error(Enomem); e = s + READSTR; p = seprint(p, e, "debug: %d\n", debug); @@ -1355,9 +1411,10 @@ 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) @@ -1396,20 +1453,18 @@ configwrite(Aoedev *d, void *db, long len) { char *s; - Aoeqc *ch; + Aoehdr *h; + Aoecfg *ch; Frame *f; Srb *srb; if(!UP(d)) - error(Eaoedown); - if(len > ETHERMAXTU - AOEQCSZ) + error(Enotup); + if(len > sizeof d->config) error(Etoobig); srb = srballoc(len); s = malloc(len); - if(s == nil) - error(Enomem); memmove(s, db, len); - if(waserror()){ srbfree(srb); free(s); @@ -1426,25 +1481,17 @@ break; poperror(); qunlock(d); - if(waserror()) nexterror(); 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) { - /* - * these refer to qlock & waserror in the above for loop. - * there's still the first waserror outstanding. - */ - poperror(); - qunlock(d); + h = (Aoehdr*)f->hdr; + if(hset(d, f, h, ACconfig, 1) == -1) return 0; - } - srb->shared = 1; + ch = (Aoecfg*)(f->hdr + Aoehsz); f->srb = srb; f->dp = s; ch->verccmd = AQCfset; @@ -1453,7 +1500,10 @@ srb->nout++; f->dl->npkt++; f->dlen = len; - /* these too */ + /* + * these refer to qlock & waserror in the above for loop. + * there's still the first waserror outstanding. + */ poperror(); qunlock(d); @@ -1480,39 +1530,40 @@ return len; } -static int getmtu(Chan*); - static int -devmaxdata(Aoedev *d) /* return aoe mtu (excluding headers) */ +devmaxdata(Aoedev *d) { - int i, nmtu, 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; - nmtu = getmtu(n->mtu); - if(mtu > nmtu) - mtu = nmtu; + m = getmtu(n->mtu); + if(l->datamtu < datamtu) + datamtu = l->datamtu; + if(m < mtu) + mtu = m; } if(mtu == 100000) - mtu = ETHERMAXTU; /* normal ethernet mtu */ - mtu -= AOEATASZ; - mtu -= (uint)mtu % Aoesectsz; - if(mtu < 2*Aoesectsz) /* sanity */ - mtu = 2*Aoesectsz; + mtu = 1514; + mtu -= Aoehsz + Aoeatasz; + mtu -= mtu % Aoesectsz; + if(mtu > datamtu) + mtu = datamtu; return mtu; } static int -toggle(char *s, int f, int bit) +toggle(char *s, uint f, uint bit) { if(s == nil) - f ^= bit; + f = f^bit; else if(strcmp(s, "on") == 0) f |= bit; else @@ -1525,7 +1576,7 @@ static long unitctlwrite(Aoedev *d, void *db, long n) { - uint maxbcnt, mtu; + uint maxbcnt, m; uvlong bsize; enum { Failio, @@ -1544,7 +1595,7 @@ {Jumbo, "jumbo", 0 }, {Maxbno, "maxbno", 0 }, {Mtu, "mtu", 0 }, - {Nofailf, "nofail", 0 }, + {Nofailf, "nofail", 0 }, {Setsize, "setsize", 0 }, }; @@ -1572,17 +1623,19 @@ if(cb->nf > 2) error(Ecmdargs); if(cb->nf == 2){ - mtu = strtoul(cb->f[1], 0, 0); + m = strtoul(cb->f[1], 0, 0); if(ct->index == Maxbno) - mtu *= Aoesectsz; + m *= Aoesectsz; else{ - mtu -= AOEATASZ; - mtu &= ~(Aoesectsz-1); + m -= Aoehsz + Aoeatasz; + m &= ~(Aoesectsz-1); } - if(mtu == 0 || mtu > maxbcnt) - cmderror(cb, "mtu out of legal range"); - maxbcnt = mtu; - } + if(m == 0 || m > maxbcnt) + cmderror(cb, "invalid mtu"); + maxbcnt = m; + d->maxmtu = m; + } else + d->maxmtu = Maxmtu; d->maxbcnt = maxbcnt; break; case Nofailf: @@ -1599,8 +1652,6 @@ } d->bsize = bsize; break; - default: - cmderror(cb, "unknown aoe control message"); } poperror(); qunlock(d); @@ -1629,8 +1680,6 @@ if(off + n > sizeof d->config) error(Etoobig); buf = malloc(sizeof d->config); - if(buf == nil) - error(Enomem); if(waserror()){ free(buf); nexterror(); @@ -1658,7 +1707,7 @@ 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; @@ -1700,31 +1749,32 @@ * 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 == nil || f == nil) { 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->frames); free(d); + free(d->frames); error("too many units"); } d->dl = d->dltab; @@ -1732,7 +1782,7 @@ } static Aoedev* -mm2dev(int major, int minor) +mm2dev(uint major, uint minor) { Aoedev *d; @@ -1743,13 +1793,13 @@ 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; @@ -1763,7 +1813,7 @@ 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; @@ -1773,53 +1823,23 @@ 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) == -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; @@ -1827,26 +1847,14 @@ 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 *mtuch) -{ - int n, mtu; - char buf[36]; - - mtu = ETHERMAXTU; - if(mtuch == nil || waserror()) - return mtu; - n = devtab[mtuch->type]->read(mtuch, 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 @@ -1868,29 +1876,33 @@ } 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; } @@ -1907,7 +1919,7 @@ if(n == Tmgmt || n == Tfree) return; d = mm2dev(nhgets(h->major), h->minor); - if(d == 0) + if(d == nil) return; if(f = getframe(d, n)) frameerror(d, f, s); @@ -1916,18 +1928,21 @@ 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, *h0; + Aoecfg *ch; Devlink *l; Frame *f; + Srb *srb; - ch = (Aoeqc*)b->rp; - major = nhgets(ch->major); - n = nhgetl(ch->tag); - if(n != Tmgmt){ - d = mm2dev(major, ch->minor); + h = (Aoehdr*)b->rp; + ch = (Aoecfg*)(b->rp + Aoehsz); + major = nhgets(h->major); + n = nhgetl(h->tag); + if(n != Tmgmt && n != Tfree){ + d = mm2dev(major, h->minor); if(d == nil) return; qlock(d); @@ -1937,8 +1952,15 @@ eventlog("%æ: unknown response tag %ux\n", d, n); return; } + h0 = (Aoehdr*)f->hdr; + cmd = h0->cmd; + if(cmd != ACconfig){ + qunlock(d); + eventlog("%æ: malicious server got ACconfig want %d; tag %ux\n", d, cmd, n); + 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); @@ -1947,20 +1969,23 @@ d, n, cslen, blen); cslen = blen; } - memmove(f->dp, ch + 1, cslen); - f->srb->nout--; - wakeup(f->srb); - f->srb->shared = 0; - d->nout--; + memmove(f->dp, b->rp + 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); @@ -1968,10 +1993,10 @@ 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; @@ -1984,22 +2009,26 @@ 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; + cslen = nhgets(ch->cslen); + if(cslen > sizeof d->config) + cslen = sizeof d->config; + if(Aoehsz + Aoecfgsz + cslen > BLEN(b)) + cslen = BLEN(b) - (Aoehsz + Aoecfgsz); + d->nconfig = cslen; + memmove(d->config, b->rp + Aoehsz + Aoecfgsz, cslen); + + /* 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; } @@ -2010,59 +2039,20 @@ 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){ + eventlog("%æ: idfeat returns -1\n", d); + return -1; + } + if((d->feat&Dlba) == 0){ + eventlog("%æ: no lba support\n", d); + return -1; } -// eventlog("%æ up\n", d); d->flag |= Dup; memmove(d->ident, id, sizeof d->ident); return s; @@ -2088,13 +2078,14 @@ 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; @@ -2106,31 +2097,42 @@ static void atarsp(Block *b) { - unsigned n; - short major; + uint n, cmd; + ushort major; Aoeata *ahin, *ahout; + Aoehdr *h, *h0; 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); + if(n == Tfree || n == Tmgmt) + goto bail; f = getframe(d, n); if(f == nil){ - dprint("%æ: unexpected response; tag %ux\n", d, n); + eventlog("%æ: unexpected response; tag %ux\n", d, n); + goto bail; + } + h0 = (Aoehdr*)f->hdr; + cmd = h0->cmd; + if(cmd != ACata){ + eventlog("%æ: malicious server got ACata want %d; tag %ux\n", d, cmd, 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){ @@ -2143,12 +2145,12 @@ 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) @@ -2161,12 +2163,14 @@ } 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", @@ -2174,11 +2178,11 @@ } } - if(srb && --srb->nout == 0 && srb->len == 0){ - wakeup(srb); - srb->shared = 0; - } f->srb = nil; + if(srb){ + srb->nout--; + srbwakeup(srb); + } f->tag = Tfree; d->nout--; @@ -2203,7 +2207,7 @@ 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); @@ -2211,38 +2215,28 @@ 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: %d.%d %s\n", nl->path, + h->major[0]<<8 | h->major[1], h->minor, 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) != 0xf0){ + eventlog("%s: unknown cmd %d\n", + nl->path, h->cmd); + errrsp(b, "unknown command"); + } freeb(b); } } @@ -2260,7 +2254,7 @@ 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(); @@ -2278,7 +2272,7 @@ 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()) @@ -2328,7 +2322,7 @@ if(n->dc && strcmp(n->path, path) == 0) break; unlock(&netlinks); - if (n >= e) + if(n == e) error("device not bound"); /* @@ -2397,7 +2391,7 @@ wlock(&devs); for(p = d = devs.d; d; d = next){ next = d->next; - if(d->ndl > 0) { + if(d->ndl > 0){ p = d; continue; } @@ -2418,7 +2412,36 @@ } 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; @@ -2426,27 +2449,16 @@ 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; - - /* - * Changing the version number is, strictly speaking, correct, - * but doing so means that deleting a LUN that is not in use - * invalidates all other LUNs too. If your file server has - * venti arenas or fossil file systems on 1.0, and you delete 1.1, - * since you no longer need it, 1.0 will become inaccessible to your - * file server, which will eventually panic. Note that newdev() - * does not change the version number. - */ - // newvers(d); - + newvers(d); d->ndl = 0; qunlock(d); for(i = 0; i < d->nframes; i++) - frameerror(d, d->frames+i, Eaoedown); + frameerror(d, d->frames+i, Enotup); if(p) p->next = d->next; @@ -2458,60 +2470,45 @@ 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) { @@ -2561,13 +2558,11 @@ 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); --- /n/sources/plan9/sys/src/9/port/devsd.c Fri Jun 28 21:35:19 2013 +++ /sys/src/9/port/devsd.c Tue Apr 22 00:00:00 2014 @@ -15,12 +15,20 @@ extern Dev sddevtab; extern SDifc* sdifc[]; +static char Enoata[] = "raw ata commands not supported"; +static char Enoscsi[] = "raw scsi commands not supported"; + static char devletters[] = "0123456789" "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; static SDev *devs[sizeof devletters-1]; static QLock devslock; +static SDunit topctlunit; + +enum { + Ahdrsz = 2, +}; enum { Rawcmd, @@ -38,6 +46,7 @@ Qctl = Qunitbase, Qraw, Qpart, + Qextra, TypeLOG = 4, NType = (1<vers; if(unit->sectors > 0){ unit->sectors = unit->secsize = 0; sdincvers(unit); } - /* device must be connected or not; other values are trouble */ - if(unit->inquiry[0] & 0xC0) /* see SDinq0periphqual */ + if(unit->inquiry[0] & 0xC0) return 0; - switch(unit->inquiry[0] & SDinq0periphtype){ - case SDperdisk: - case SDperworm: - case SDpercd: - case SDpermo: + switch(unit->inquiry[0] & 0x1F){ + case 0x00: /* DA */ + case 0x04: /* WORM */ + case 0x05: /* CD-ROM */ + case 0x07: /* MO */ break; default: return 0; @@ -201,7 +211,8 @@ if(unit->sectors){ sdincvers(unit); sdaddpart(unit, "data", 0, unit->sectors); - + } + if(unit->sectors && vers0 == 0){ /* * Use partitions passed from boot program, * e.g. @@ -291,7 +302,7 @@ } sdev->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; @@ -322,15 +333,14 @@ 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()); } } @@ -343,8 +353,8 @@ 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: @@ -403,11 +413,12 @@ { 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); @@ -421,7 +432,7 @@ perm = &unit->ctlperm; if(emptystr(perm->user)){ kstrdup(&perm->user, eve); - perm->perm = 0644; /* nothing secret in ctl */ + perm->perm = 0644; } devdir(c, q, "ctl", 0, perm->user, perm->perm, dp); rv = 1; @@ -449,6 +460,18 @@ 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); @@ -459,17 +482,46 @@ 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, 0644, dp); /* no secrets */ + 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) { Qid q; @@ -483,8 +535,7 @@ case Qtopdir: if(s == DEVDOTDOT){ mkqid(&q, QID(0, 0, 0, Qtopdir), 0, QTDIR); - snprint(up->genbuf, sizeof up->genbuf, "#%C", - sddevtab.dc); + sprint(up->genbuf, "#%C", sddevtab.dc); devdir(c, q, up->genbuf, 0, eve, 0555, dp); return 1; } @@ -532,8 +583,7 @@ case Qunitdir: if(s == DEVDOTDOT){ mkqid(&q, QID(0, 0, 0, Qtopdir), 0, QTDIR); - snprint(up->genbuf, sizeof up->genbuf, "#%C", - sddevtab.dc); + sprint(up->genbuf, "#%C", sddevtab.dc); devdir(c, q, up->genbuf, 0, eve, 0555, dp); return 1; } @@ -566,17 +616,18 @@ } 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)) @@ -588,6 +639,7 @@ 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; @@ -728,10 +780,12 @@ } } +#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; @@ -741,13 +795,13 @@ uvlong bno; sdev = sdgetdev(DEV(c->qid)); - if(sdev == nil){ - decref(&sdev->r); + if(sdev == nil) error(Enonexist); - } unit = sdev->unit[UNIT(c->qid)]; - if(unit == nil) + if(unit == nil){ + decref(&sdev->r); error(Enonexist); + } nchange = 0; qlock(&unit->ctl); @@ -793,26 +847,35 @@ poperror(); return 0; } - if(!(unit->inquiry[1] & SDinq1removable)){ + if(!(unit->inquiry[1] & 0x80)){ qunlock(&unit->ctl); 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(!(unit->inquiry[1] & SDinq1removable)) + 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); @@ -823,7 +886,8 @@ 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); @@ -840,12 +904,14 @@ 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] & SDinq1removable){ + if(unit->inquiry[1] & 0x80){ qunlock(&unit->ctl); poperror(); } @@ -857,41 +923,69 @@ 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); - - data = nil; - if(n){ - if((data = sdmalloc(n)) == nil) - error(Enomem); - if(r->write) - memmove(data, a, n); + 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; } - r->data = data; - r->dlen = n; + data = nil; + 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) @@ -900,6 +994,7 @@ SDunit *unit; unit = r->unit; + unit->sense[0] = 0x80 | 0x70; /* valid; fixed-format */ unit->sense[2] = key; unit->sense[12] = asc; unit->sense[13] = ascq; @@ -921,36 +1016,7 @@ } 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; @@ -961,25 +1027,6 @@ 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 * will return 'logical unit not supported'. @@ -1031,13 +1078,17 @@ /* * Read capacity returns the LBA of the last sector. */ - len = unit->sectors - 1; + len = unit->sectors; + if(len >= 0xffffffff) + len = 0xffffffff; + else 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; @@ -1053,7 +1104,9 @@ /* * 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; @@ -1063,32 +1116,124 @@ *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; + SDev *sdev; SDpart *pp; + SDreq *r; SDunit *unit; - SDev *sdev; ulong offset; int i, l, m, status; @@ -1099,14 +1244,15 @@ case Qtopctl: m = 64*1024; /* room for register dumps */ p = buf = malloc(m); - if(p == nil) - error(Enomem); + assert(p); e = p + m; qlock(&devslock); for(i = 0; i < nelem(devs); i++){ 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); @@ -1125,8 +1271,6 @@ unit = sdev->unit[UNIT(c->qid)]; m = 16*1024; /* room for register dumps */ p = malloc(m); - if(p == nil) - error(Enomem); l = snprint(p, m, "inquiry %.48s\n", (char*)unit->inquiry+8); qlock(&unit->ctl); @@ -1173,23 +1317,38 @@ } 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); } } @@ -1199,7 +1358,8 @@ 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; @@ -1266,7 +1426,7 @@ error(Ebadctl); poperror(); poperror(); - if (sdev) + if(sdev) decref(&sdev->r); free(cb); break; @@ -1315,6 +1475,9 @@ break; case Qraw: + proto = SDcdb; + ataproto = 0; + atacdb = 0; sdev = sdgetdev(DEV(c->qid)); if(sdev == nil) error(Enonexist); @@ -1327,18 +1490,34 @@ } 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: @@ -1349,15 +1528,18 @@ 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; @@ -1374,23 +1556,30 @@ 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; @@ -1412,14 +1601,22 @@ 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; } @@ -1428,7 +1625,7 @@ configure(char* spec, DevConf* cf) { SDev *s, *sdev; - char *p; + char *p, buf[64]; int i; if(sdindex(*spec) < 0) @@ -1440,10 +1637,12 @@ for(i = 0; sdifc[i] != nil; i++) if(strcmp(sdifc[i]->name, cf->type) == 0) break; - if(sdifc[i] == nil) - error("sd type not found"); - if(p) - *(p-1) = '/'; + if(sdifc[i] == nil){ + snprint(buf, sizeof buf, "sd: type %s: not found", cf->type); + error(buf); + } + if(p != nil) + p[-1] = '/'; if(sdifc[i]->probe == nil) error("sd type cannot probe"); @@ -1503,13 +1702,62 @@ 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, @@ -1523,7 +1771,7 @@ devremove, sdwstat, devpower, - sdconfig, /* probe; only called for pcmcia-like devices */ + sdconfig, }; /* @@ -1560,9 +1808,7 @@ { Devport *p; - p = (Devport *)malloc((dc->nports + 1) * sizeof(Devport)); - if(p == nil) - error(Enomem); + p = malloc((dc->nports + 1) * sizeof(Devport)); if(dc->nports > 0){ memmove(p, dc->ports, dc->nports * sizeof(Devport)); free(dc->ports); @@ -1653,7 +1899,6 @@ 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) --- /sys/src/9/port/led.c Thu Jan 1 00:00:00 1970 +++ /sys/src/9/port/led.c Tue Apr 22 00:00:00 2014 @@ -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; +} --- /sys/src/9/port/led.h Thu Jan 1 00:00:00 1970 +++ /sys/src/9/port/led.h Tue Apr 22 00:00:00 2014 @@ -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); --- /n/sources/plan9/sys/src/9/port/sd.h Wed Nov 14 00:41:23 2012 +++ /sys/src/9/port/sd.h Tue Apr 22 00:00:00 2014 @@ -2,6 +2,7 @@ * Storage Device. */ typedef struct SDev SDev; +typedef struct SDfile SDfile; typedef struct SDifc SDifc; typedef struct SDio SDio; typedef struct SDpart SDpart; @@ -23,11 +24,20 @@ 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; @@ -43,6 +53,8 @@ int state; SDreq* req; SDperm rawperm; + SDfile efile[5]; + int nefile; }; /* @@ -68,7 +80,7 @@ char* name; SDev* (*pnp)(void); - SDev* (*legacy)(int, int); + SDev* (*xxlegacy)(int, int); /* unused. remove me */ int (*enable)(SDev*); int (*disable)(SDev*); @@ -83,40 +95,31 @@ 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 { SDnosense = 0x00000001, SDvalidsense = 0x00010000, - - SDinq0periphqual= 0xe0, - SDinq0periphtype= 0x1f, - SDinq1removable = 0x80, - - /* periphtype values */ - SDperdisk = 0, /* Direct access (disk) */ - SDpertape = 1, /* Sequential eg, tape */ - SDperpr = 2, /* Printer */ - SDperworm = 4, /* Worm */ - SDpercd = 5, /* CD-ROM */ - SDpermo = 7, /* rewriteable MO */ - SDperjuke = 8, /* medium-changer */ }; enum { @@ -133,6 +136,12 @@ SDmaxio = 2048*1024, SDnpart = 16, + + SDread = 0, + SDwrite, + + SData = 1, + SDcdb = 2, }; /* @@ -162,17 +171,21 @@ extern SDio sdio; +/* sdalloc.c, or architecture replacement */ +extern void* sdmalloc(usize); +extern void sdfree(void*); + /* devsd.c */ extern void sdadddevs(SDev*); extern void sdaddconf(SDunit*); extern void sdaddallconfs(void (*f)(SDunit*)); extern void sdaddpart(SDunit*, char*, uvlong, uvlong); 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*); --- /n/sources/plan9/sys/src/9/port/sdaoe.c Fri Jun 28 21:34:51 2013 +++ /sys/src/9/port/sdaoe.c Tue Apr 22 00:00:00 2014 @@ -1,5 +1,5 @@ /* - * aoe sd driver, copyright © 2007 coraid + * aoe sd driver, copyright © 2007-9 coraid */ #include "u.h" @@ -12,38 +12,22 @@ #include "../port/sd.h" #include "../port/netif.h" #include "../port/aoe.h" +#include + +extern char Echange[]; +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; @@ -53,11 +37,8 @@ Chan *c; ulong vers; - uchar mediachange; - uchar flag; - uchar smart; - uchar smartrs; - uchar feat; + uchar drivechange; + Sfis; uvlong sectors; char serial[20+1]; @@ -66,88 +47,60 @@ 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; - } - - aoeidmove(c->serial, id+10, 20); - aoeidmove(c->firmware, id+23, 8); - aoeidmove(c->model, id+27, 40); + s = idfeat(c, id); + if(s == -1){ + uprint("%s: identify fails", c->unit->name); + print("%s\n", up->genbuf); + error(up->genbuf); + } + 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) @@ -170,7 +123,6 @@ cclose(c); d->feat = 0; - d->smart = 0; identify(d, (ushort*)d->ident); memset(u->inquiry, 0, sizeof u->inquiry); @@ -200,7 +152,6 @@ { Ctlr *c; - /* race? */ if(ctlrlookup(path)) error(Eexist); @@ -245,7 +196,6 @@ free(x); } -/* don't call aoeprobe from within a loop; it loops internally retrying open. */ static SDev* aoeprobe(char *path, SDev *s) { @@ -270,21 +220,24 @@ 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; @@ -293,14 +246,20 @@ } 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* @@ -312,7 +271,8 @@ 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]); @@ -338,7 +298,7 @@ static Ctlr* pnpprobe(SDev *sd) { - ulong start; + int j; char *p; static int i; @@ -350,17 +310,21 @@ 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; } @@ -375,7 +339,7 @@ c = s->ctlr; if(c == nil && (s->ctlr = c = pnpprobe(s)) == nil) return 0; - c->mediachange = 1; + c->drivechange = 1; return 1; } @@ -407,19 +371,19 @@ int r; c = u->dev->ctlr; - r = 0; - if((c->feat&Datapi) && c->mediachange){ - if(aoeconnect(u, c) == 0 && (r = scsionline(u)) > 0) - c->mediachange = 0; - return r; - } +// if((c->feat&Datapi) && c->drivechange){ +// r = 0; +// if(aoeconnect(u, c) == 0 && (r = scsionline(u)) > 0) +// c->drivechange = 0; +// return 0; +// } - 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 @@ -428,101 +392,70 @@ 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, Eaoedown) == 0) - unit->sectors = 0; + strcmp(up->errstr, Enotup) == 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 int +flushcache(Ctlr *) +{ + return -1; +} -static char * -pflag(char *s, char *e, uchar f) +static int +aoerio(SDreq *r) { - uchar i; + 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); - for(i = 0; i < 8; i++) - if(f & (1 << i)) - s = seprint(s, e, "%s ", flagname[i]); - return seprint(s, e, "\n"); + 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); + } + + 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 @@ -539,14 +472,8 @@ 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; } @@ -584,10 +511,8 @@ { Ctlr *c; - if(s == nil || (c = s->ctlr) == nil) - return p; - - return seprint(p, e, "%s aoe %s\n", s->name, c->path); + c = s->ctlr; + return seprint(p, e, "%s aoe %s\n", s->name, c? c->path: ""); } static int @@ -614,7 +539,7 @@ aoerctl, aoewctl, - scsibio, + aoebio, aoeprobew, /* probe */ aoeclear, /* clear */ aoertopctl, --- /n/sources/plan9/sys/src/9/port/sdscsi.c Fri Jul 13 18:51:44 2012 +++ /sys/src/9/port/sdscsi.c Tue Apr 22 00:00:00 2014 @@ -93,7 +93,7 @@ * Try to ensure a direct-access device is spinning. * Don't wait for completion, ignore the result. */ - if((unit->inquiry[0] & SDinq0periphtype) == SDperdisk){ + if((unit->inquiry[0] & 0x1F) == 0){ memset(r->cmd, 0, sizeof(r->cmd)); r->write = 0; r->cmd[0] = 0x1B; @@ -153,7 +153,8 @@ /* * 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 @@ 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 @@ * 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 @@ 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 @@ 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 @@ 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)) @@ -403,7 +416,7 @@ */ if(r->sense[12] != 0x28 || r->sense[13] != 0) break; - if(unit->inquiry[1] & SDinq1removable) + if(unit->inquiry[1] & 0x80) unit->sectors = 0; break; case 0x02: /* not ready */ @@ -415,6 +428,10 @@ 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);