ahci Reference: /n/patches.lsub.org/patch/ahci Date: Mon Apr 23 22:02:49 CES 2012 Signed-off-by: quanstro@quanstro.net --- /sys/include/fis.h Thu Jan 1 00:00:00 1970 +++ /sys/include/fis.h Mon Jan 17 20:31:00 2011 @@ -0,0 +1,163 @@ +#pragma lib "libfis.a" +#pragma src "/sys/src/libfis" + +/* ata errors */ +enum { + Emed = 1<<0, /* media error */ + Enm = 1<<1, /* no media */ + Eabrt = 1<<2, /* abort */ + Emcr = 1<<3, /* media change request */ + Eidnf = 1<<4, /* no user-accessible address */ + Emc = 1<<5, /* media change */ + Eunc = 1<<6, /* data error */ + Ewp = 1<<6, /* write protect */ + Eicrc = 1<<7, /* interface crc error */ + + Efatal = Eidnf|Eicrc, /* must sw reset */ +}; + +/* ata status */ +enum { + ASerr = 1<<0, /* error */ + ASdrq = 1<<3, /* request */ + ASdf = 1<<5, /* fault */ + ASdrdy = 1<<6, /* ready */ + ASbsy = 1<<7, /* busy */ + + ASobs = 1<<1|1<<2|1<<4, +}; + +enum { + /* fis types */ + H2dev = 0x27, + D2host = 0x34, + + /* fis flags bits */ + Fiscmd = 0x80, + + /* ata bits */ + Ataobs = 0xa0, + Atalba = 0x40, + + /* nominal fis size (fits any fis) */ + Fissize = 0x20, +}; + +/* sata device-to-host (0x27) fis layout */ +enum { + Ftype, + Fflags, + Fcmd, + Ffeat, + Flba0, + Flba8, + Flba16, + Fdev, + Flba24, + Flba32, + Flba40, + Ffeat8, + Fsc, + Fsc8, + Ficc, /* isochronous cmd completion */ + Fcontrol, +}; + +/* sata host-to-device fis (0x34) differences */ +enum{ + Fioport = 1, + Fstatus, + Frerror, +}; + +/* ata protcol type */ +enum{ + Pnd = 0<<0, /* data direction */ + Pin = 1<<0, + Pout = 2<<0, + Pdatam = 3<<0, + + Ppio = 1<<2, /* ata protocol */ + Pdma = 2<<2, + Pdmq = 3<<2, + Preset = 4<<2, + Pdiag = 5<<2, + Ppkt = 6<<2, + Pprotom = 7<<2, + + P48 = 0<<5, /* command “size” */ + P28 = 1<<5, + Pcmdszm = 1<<5, + + Pssn = 0<<6, /* sector size */ + P512 = 1<<6, + Pssm = 1<<6, +}; + +typedef struct Sfis Sfis; +struct Sfis { + ushort feat; + uchar udma; + uchar speeds; + uint sig; + uint lsectsz; + uint physshift; /* log2(log/phys) */ + uint c; /* disgusting, no? */ + uint h; + uint s; +}; + +enum { + Dlba = 1<<0, /* required for sata */ + Dllba = 1<<1, + Dsmart = 1<<2, + Dpower = 1<<3, + Dnop = 1<<4, + Datapi = 1<<5, + Datapi16= 1<<6, + Data8 = 1<<7, + Dsct = 1<<8, + Dnflag = 9, +}; + +enum { + Pspinup = 1<<0, + Pidready = 1<<1, +}; + +void setfissig(Sfis*, uint); +int txmodefis(Sfis*, uchar*, uchar); +int atapirwfis(Sfis*, uchar*, uchar*, int, int); +int featfis(Sfis*, uchar*, uchar); +int flushcachefis(Sfis*, uchar*); +int identifyfis(Sfis*, uchar*); +int nopfis(Sfis*, uchar*, int); +int rwfis(Sfis*, uchar*, int, int, uvlong); +void skelfis(uchar*); +void sigtofis(Sfis*, uchar*); +uvlong fisrw(Sfis*, uchar*, int*); + +void idmove(char*, ushort*, int); +vlong idfeat(Sfis*, ushort*); +uvlong idwwn(Sfis*, ushort*); +int idss(Sfis*, ushort*); +int idpuis(ushort*); +ushort id16(ushort*, int); +uint id32(ushort*, int); +uvlong id64(ushort*, int); +char *pflag(char*, char*, Sfis*); +uint fistosig(uchar*); + +/* scsi */ +typedef struct Cfis Cfis; +struct Cfis { + uchar phyid; + uchar encid[8]; + uchar tsasaddr[8]; + uchar ssasaddr[8]; + uchar ict[2]; +}; + +void smpskelframe(Cfis*, uchar*, int); +uint sashash(uvlong); +uchar *sasbhash(uchar*, uchar*); --- /sys/src/libfis/mkfile Thu Jan 1 00:00:00 1970 +++ /sys/src/libfis/mkfile Tue Mar 24 02:56:37 2009 @@ -0,0 +1,15 @@ + +#include +#include + +static char *flagname[9] = { + "lba", + "llba", + "smart", + "power", + "nop", + "atapi", + "atapi16", + "ata8", + "sct", +}; + +/* + * ata8 standard (llba) cmd layout + * + * feature 16 bits + * count 16 bits + * lba 48 bits + * device 8 bits + * command 8 bits + * + * response: + * + * status 8 bits + * error 8 bits + * reason 8 bits + * count 8 bits + * sstatus 8 bits + * sactive 8 bits +*/ + +/* + * sata fis layout for fistype 0x27: host-to-device: + * + * 0 fistype + * 1 fis flags + * 2 ata command + * 3 features + * 4 sector lba low 7:0 + * 5 cyl low lba mid 15:8 + * 6 cyl hi lba hi 23:16 + * 7 device / head + * 8 sec exp lba 31:24 + * 9 cy low e lba 39:32 + * 10 cy hi e lba 48:40 + * 11 features (exp) + * 12 sector count + * 13 sector count (exp) + * 14 r + * 15 control + */ + +void +setfissig(Sfis *x, uint sig) +{ + x->sig = sig; +} + +void +skelfis(uchar *c) +{ + memset(c, 0, Fissize); + c[Ftype] = H2dev; + c[Fflags] = Fiscmd; + c[Fdev] = Ataobs; +} + +int +nopfis(Sfis*, uchar *c, int srst) +{ + skelfis(c); + if(srst){ + c[Fflags] &= ~Fiscmd; + c[Fcontrol] = 1<<2; + return Preset|P28; + } + return Pnd|P28; +} + +int +txmodefis(Sfis *f, uchar *c, uchar d) +{ + int m; + + /* hack */ + if((f->sig >> 16) == 0xeb14) + return -1; + m = 0x40; + if(d == 0xff){ + d = 0; + m = 0; + } + skelfis(c); + c[Fcmd] = 0xef; + c[Ffeat] = 3; /* set transfer mode */ + c[Fsc] = m | d; /* sector count */ + return Pnd|P28; +} + +int +featfis(Sfis*, uchar *c, uchar f) +{ + skelfis(c); + c[Fcmd] = 0xef; + c[Ffeat] = f; + return Pnd|P28; +} + +int +identifyfis(Sfis *f, uchar *c) +{ + static uchar tab[] = { 0xec, 0xa1, }; + + skelfis(c); + c[Fcmd] = tab[f->sig>>16 == 0xeb14]; + return Pin|Ppio|P28|P512; +} + +int +flushcachefis(Sfis *f, uchar *c) +{ + static uchar tab[2] = {0xe7, 0xea}; + static uchar ptab[2] = {Pnd|P28, Pnd|P48}; + int llba; + + llba = (f->feat & Dllba) != 0; + skelfis(c); + c[Fcmd] = tab[llba]; + return ptab[llba]; +} + +static ushort +gbit16(void *a) +{ + ushort j; + uchar *i; + + i = a; + j = i[1] << 8; + j |= i[0]; + return j; +} + +static uint +gbit32(void *a) +{ + uint j; + uchar *i; + + i = a; + j = i[3] << 24; + j |= i[2] << 16; + j |= i[1] << 8; + j |= i[0]; + return j; +} + +static uvlong +gbit64(void *a) +{ + uchar *i; + + i = a; + return (uvlong)gbit32(i+4) << 32 | gbit32(a); +} + +ushort +id16(ushort *id, int i) +{ + return gbit16(id+i); +} + +uint +id32(ushort *id, int i) +{ + return gbit32(id+i); +} + +uvlong +id64(ushort *id, int i) +{ + return gbit64(id+i); +} + +/* acs-2 §7.18.7.4 */ +static ushort puistab[] = { + 0x37c8, Pspinup, + 0x738c, Pspinup | Pidready, + 0x8c73, 0, + 0xc837, Pidready, +}; + +int +idpuis(ushort *id) +{ + ushort u, i; + + u = gbit16(id + 2); + for(i = 0; i < nelem(puistab); i += 2) + if(u == puistab[i]) + return puistab[i + 1]; + return Pidready; /* annoying cdroms */ +} + +static ushort +onesc(ushort *id) +{ + ushort u; + + u = gbit16(id); + if(u == 0xffff) + u = 0; + return u; +} + +enum{ + Idmasp = 1<<8, + Ilbasp = 1<<9, + Illba = 1<<10, +}; + +vlong +idfeat(Sfis *f, ushort *id) +{ + int i, j; + vlong s; + + f->feat = 0; + if(f->sig>>16 == 0xeb14) + f->feat |= Datapi; + i = gbit16(id + 49); + if((i & Ilbasp) == 0){ + if(gbit16(id + 53) & 1){ + f->c = gbit16(id + 1); + f->h = gbit16(id + 3); + f->s = gbit16(id + 6); + }else{ + f->c = gbit16(id + 54); + f->h = gbit16(id + 55); + f->s = gbit16(id + 56); + } + s = f->c*f->h*f->s; + }else{ + f->c = f->h = f->s = 0; + f->feat |= Dlba; + j = gbit16(id + 83) | gbit16(id + 86); + if(j & Illba){ + f->feat |= Dllba; + s = gbit64(id + 100); + }else + s = gbit32(id + 60); + } + f->udma = 0xff; + if(i & Idmasp) + if(gbit16(id + 53) & 4) + for(i = gbit16(id + 88) & 0x7f; i; i >>= 1) + f->udma++; + + if(f->feat & Datapi){ + i = gbit16(id + 0); + if(i & 1) + f->feat |= Datapi16; + } + + i = gbit16(id+83); + if((i>>14) == 1){ + if(i & (1<<3)) + f->feat |= Dpower; + i = gbit16(id + 82); + if(i & 1) + f->feat |= Dsmart; + if(i & (1<<14)) + f->feat |= Dnop; + } + i = onesc(id + 80); + if(i & 1<<8){ + f->feat |= Data8; + i = onesc(id + 222); /* sata? */ + j = onesc(id + 76); + if(i != 0 && i >> 12 == 1 && j != 0){ + j >>= 1; + f->speeds = j & 7; + i = gbit16(id + 78) & gbit16(id + 79); + /* + * not acceptable for comreset to + * wipe out device configuration. + * reject drive. + */ + if((i & 1<<6) == 0) + return -1; + } + } + if(gbit16(id + 206) & 1) + f->feat |= Dsct; + idss(f, id); + return s; +} + +int +idss(Sfis *f, ushort *id) +{ + uint sw, i; + + if(f->sig>>16 == 0xeb14) + return 0; + f->lsectsz = 512; + f->physshift = 0; + i = gbit16(id + 106); + if(i >> 14 != 1) + return f->lsectsz; + if((sw = gbit32(id + 117)) >= 256) + f->lsectsz = sw * 2; + if(i & 1<<13) + f->physshift = i & 7; +// return f->lsectsz * (1<physshift); /* not ready for this */ + return f->lsectsz; +} + +uvlong +idwwn(Sfis*, ushort *id) +{ + uvlong u; + + u = 0; + if(id[108]>>12 == 5){ + u |= (uvlong)gbit16(id + 108) << 48; + u |= (uvlong)gbit16(id + 109) << 32; + u |= gbit16(id + 110) << 16; + u |= gbit16(id + 111) << 0; + } + return u; +} + +void +idmove(char *p, ushort *u, int n) +{ + int i; + char *op, *e, *s; + + op = p; + s = (char*)u; + for(i = 0; i < n; i += 2){ + *p++ = s[i + 1]; + *p++ = s[i + 0]; + } + *p = 0; + while(p > op && *--p == ' ') + *p = 0; + e = p; + p = op; + while(*p == ' ') + p++; + memmove(op, p, n - (e - p)); +} + +char* +pflag(char *s, char *e, Sfis *f) +{ + ushort i, u; + + u = f->feat; + for(i = 0; i < Dnflag; i++) + if(u & (1 << i)) + s = seprint(s, e, "%s ", flagname[i]); + return seprint(s, e, "\n"); +} + +int +atapirwfis(Sfis *f, uchar *c, uchar *cdb, int cdblen, int ndata) +{ + int fill, len; + + fill = f->feat&Datapi16? 16: 12; + if((len = cdblen) > fill) + len = fill; + memmove(c + 0x40, cdb, len); + memset(c + 0x40 + len, 0, fill - len); + + c[Ftype] = H2dev; + c[Fflags] = Fiscmd; + c[Fcmd] = Ataobs; + if(ndata != 0) + c[Ffeat] = 1; /* dma */ + else + c[Ffeat] = 0; /* features (exp); */ + c[Flba0] = 0; + c[Flba8] = ndata; + c[Flba16] = ndata >> 8; + c[Fdev] = Ataobs; + memset(c + 8, 0, Fissize - 8); + return P28|Ppkt; +} + +int +rwfis(Sfis *f, uchar *c, int rw, int nsect, uvlong lba) +{ + uchar acmd, llba, udma; + static uchar tab[2][2][2] = { 0x20, 0x24, 0x30, 0x34, 0xc8, 0x25, 0xca, 0x35, }; + static uchar ptab[2][2][2] = { + Pin|Ppio|P28, Pin|Ppio|P48, + Pout|Ppio|P28, Pout|Ppio|P48, + Pin|Pdma|P28, Pin|Pdma|P48, + Pout|Pdma|P28, Pout|Pdma|P48, + }; + + nsect >>= f->physshift; + lba >>= f->physshift; + + udma = f->udma != 0xff; + llba = (f->feat & Dllba) != 0; + acmd = tab[udma][rw][llba]; + + c[Ftype] = 0x27; + c[Fflags] = 0x80; + c[Fcmd] = acmd; + c[Ffeat] = 0; + + c[Flba0] = lba; + c[Flba8] = lba >> 8; + c[Flba16] = lba >> 16; + c[Fdev] = Ataobs | Atalba; + if(llba == 0) + c[Fdev] |= (lba>>24) & 0xf; + + c[Flba24] = lba >> 24; + c[Flba32] = lba >> 32; + c[Flba40] = lba >> 48; + c[Ffeat8] = 0; + + c[Fsc] = nsect; + c[Fsc8] = nsect >> 8; + c[Ficc] = 0; + c[Fcontrol] = 0; + + memset(c + 16, 0, Fissize - 16); + return ptab[udma][rw][llba]; +} + +uvlong +fisrw(Sfis *f, uchar *c, int *n) +{ + uvlong lba; + + lba = c[Flba0]; + lba |= c[Flba8] << 8; + lba |= c[Flba16] << 16; + lba |= c[Flba24] << 24; + lba |= (uvlong)(c[Flba32] | c[Flba40]<<8) << 32; + + *n = c[Fsc]; + *n |= c[Fsc8] << 8; + + *n >>= f->physshift; + lba >>= f->physshift; + + return lba; +} + +void +sigtofis(Sfis *f, uchar *c) +{ + uint u; + + u = f->sig; + memset(c, 0, Fissize); + c[Ftype] = 0x34; + c[Fflags] = 0x00; + c[Fcmd] = 0x50; + c[Ffeat] = 0x01; + c[Flba0] = u >> 8; + c[Flba8] = u >> 16; + c[Flba16] = u >> 24; + c[Fdev] = Ataobs; + c[Fsc] = u; +} + +uint +fistosig(uchar *u) +{ + return u[Fsc] | u[Flba0]<<8 | u[Flba8]<<16 | u[Flba16]<<24; +} + + +/* sas smp */ +void +smpskelframe(Cfis *f, uchar *c, int m) +{ + memset(c, 0, Fissize); + c[Ftype] = 0x40; + c[Fflags] = m; + if(f->phyid) + c[Flba32] = f->phyid; +} + +uint +sashash(uvlong u) +{ + uint poly, msb, l, r; + uvlong m; + + r = 0; + poly = 0x01db2777; + msb = 0x01000000; + for(m = 1ull<<63; m > 0; m >>= 1){ + l = 0; + if(m & u) + l = msb; + r <<= 1; + r ^= l; + if(r & msb) + r ^= poly; + } + return r & 0xffffff; +} + +uchar* +sasbhash(uchar *t, uchar *s) +{ + uint poly, msb, l, r, i, j; + + r = 0; + poly = 0x01db2777; + msb = 0x01000000; + for(i = 0; i < 8; i++) + for(j = 0x80; j != 0; j >>= 1){ + l = 0; + if(s[i] & j) + l = msb; + r <<= 1; + r ^= l; + if(r & msb) + r ^= poly; + } + t[0] = r>>16; + t[1] = r>>8; + t[2] = r; + return t; +} --- /sys/src/nix/port/lib.h Thu Apr 12 12:26:28 2012 +++ /sys/src/nix/port/lib.h Mon Apr 23 19:38:33 2012 @@ -117,20 +117,26 @@ #pragma varargck type "lld" vlong #pragma varargck type "llx" vlong +#pragma varargck type "llb" vlong #pragma varargck type "lld" uvlong #pragma varargck type "llx" uvlong +#pragma varargck type "llb" uvlong #pragma varargck type "ld" long #pragma varargck type "lx" long +#pragma varargck type "lb" long #pragma varargck type "ld" ulong #pragma varargck type "lx" ulong +#pragma varargck type "lb" ulong #pragma varargck type "d" int #pragma varargck type "x" int #pragma varargck type "c" int #pragma varargck type "C" int +#pragma varargck type "b" int #pragma varargck type "d" uint #pragma varargck type "x" uint #pragma varargck type "c" uint #pragma varargck type "C" uint +#pragma varargck type "b" uint #pragma varargck type "s" char* #pragma varargck type "q" char* #pragma varargck type "S" Rune* --- /sys/src/nix/port/sd.h Thu Apr 12 12:26:29 2012 +++ /sys/src/nix/port/sd.h Mon Apr 23 19:27:51 2012 @@ -2,11 +2,14 @@ * Storage Device. */ typedef struct SDev SDev; +typedef struct SDfile SDfile; typedef struct SDifc SDifc; typedef struct SDpart SDpart; typedef struct SDperm SDperm; typedef struct SDreq SDreq; typedef struct SDunit SDunit; +typedef struct Devport Devport; +typedef struct Devconf Devconf; struct SDperm { char* name; @@ -22,11 +25,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; @@ -42,6 +54,8 @@ int state; SDreq* req; SDperm rawperm; + SDfile efile[5]; + int nefile; }; /* @@ -67,7 +81,7 @@ char* name; SDev* (*pnp)(void); - SDev* (*legacy)(int, int); + SDev* (*xxlegacy)(int, int); /* unused. remove me */ int (*enable)(SDev*); int (*disable)(SDev*); @@ -77,27 +91,31 @@ int (*rctl)(SDunit*, char*, int); int (*wctl)(SDunit*, Cmdbuf*); - long (*bio)(SDunit*, int, int, void*, long, vlong); + long (*bio)(SDunit*, int, int, void*, long, uvlong); SDev* (*probe)(DevConf*); void (*clear)(SDev*); char* (*rtopctl)(SDev*, char*, char*); int (*wtopctl)(SDev*, Cmdbuf*); + int (*ataio)(SDreq*); }; struct SDreq { SDunit* unit; int lun; - int write; - uchar cmd[16]; + char write; + char proto; + char ataproto; + uchar cmd[0x20]; int clen; void* data; int dlen; int flags; + ulong timeout; /* in ticks */ int status; long rlen; - uchar sense[256]; + uchar sense[32]; }; enum { @@ -119,6 +137,12 @@ SDmaxio = 2048*1024, SDnpart = 16, + + SDread = 0, + SDwrite, + + SData = 1, + SDcdb = 2, }; #define sdmalloc(n) malloc(n) @@ -127,25 +151,25 @@ /* devsd.c */ extern void sdadddevs(SDev*); extern int sdsetsense(SDreq*, int, int, int, int); -extern int sdmodesense(SDreq*, uchar*, void*, int); -extern int sdfakescsi(SDreq*, void*, int); +extern int sdfakescsi(SDreq*); +extern int sdfakescsirw(SDreq*, uvlong*, int*, int*); +extern int sdaddfile(SDunit*, char*, int, char*, SDrw*, SDrw*); /* sdscsi.c */ extern int scsiverify(SDunit*); extern int scsionline(SDunit*); -extern long scsibio(SDunit*, int, int, void*, long, vlong); -extern SDev* scsiid(SDev*, SDifc*); +extern long scsibio(SDunit*, int, int, void*, long, uvlong); + /* * hardware info about a device */ -typedef struct { +struct Devport { ulong port; int size; -} Devport; +}; -struct DevConf -{ +struct DevConf { ulong intnum; /* interrupt number */ char *type; /* card type, malloced */ int nports; /* Number of ports */ --- /sys/src/nix/port/sdfis.h Thu Jan 1 00:00:00 1970 +++ /sys/src/nix/port/sdfis.h Mon Apr 23 19:31:54 2012 @@ -0,0 +1,67 @@ +#define Ms2tk(t) (((t)*HZ)/1000) + +enum { + Sas = 1, + Sata, + + Spd15 = 8, + Spd30 = 9, + Spd60 = 10, + + SDspinup = SDretry - 1, + SDrate = SDretry - 2, +}; + +/* + * the lock allows ataonline to exclude other commands + * during the online process. we could extend this to allow + * for exclusive access for periods of time. + */ +typedef struct Sfisx Sfisx; +struct Sfisx{ + uchar type; + Sfis; + Cfis; /* sas and media info */ + uint sasspd; /* botch; move to fis.h */ + uchar *oaf; + + RWlock; + int drivechange; + char serial[20+1]; + char firmware[8+1]; + char model[40+1]; + uvlong wwn; + uvlong sectors; + ulong secsize; + uint tler; /* time limit for error recovery */ + uint atamaxxfr; + uint maxspd; +}; + +int tur(SDunit *, int, uint*); + +int ataonline(SDunit*, Sfisx*); +//long atabio(SDunit*, int, int, void*, long, uvlong); +long atabio(SDunit*, Sfisx*, int, int, void*, long, uvlong); + +int scsionlinex(SDunit*, Sfisx*); +//long scsibio(SDunit*, int, int, void*, long, uvlong); +long scsibiox(SDunit*, Sfisx*, int, int, void*, long, uvlong); + +//int atariosata(SDunit*, SDreq*); +int atariosata(SDunit*, Sfisx*, SDreq*); + +char *sfisxrdctl(Sfisx*, char*, char*); +void pronline(SDunit*, Sfisx*); + +ulong totk(ulong); +int setreqto(SDreq*, ulong); +ulong gettotk(Sfisx*); + +uvlong getle(uchar*, int); +void putle(uchar*, uvlong, int); +uvlong getbe(uchar*, int); +void putbe(uchar*, uvlong, int); + +/* junk to make private later */ +void edelay(ulong, ulong); --- /sys/src/nix/port/led.h Thu Jan 1 00:00:00 1970 +++ /sys/src/nix/port/led.h Mon Apr 23 19:32:17 2012 @@ -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); --- /sys/src/nix/port/devsd.c Thu Apr 12 12:26:28 2012 +++ /sys/src/nix/port/devsd.c Mon Apr 23 19:26:41 2012 @@ -6,6 +6,8 @@ #include "mem.h" #include "dat.h" #include "fns.h" +#include "io.h" +#include "ureg.h" #include "../port/error.h" #include "../port/sd.h" @@ -13,7 +15,9 @@ extern Dev sddevtab; extern SDifc* sdifc[]; -static char Echange[] = "media or partition has changed"; +static char Echange[] = "media or partition has changed"; +static char Enoata[] = "raw ata commands not supported"; +static char Enoscsi[] = "raw scsi commands not supported"; static char devletters[] = "0123456789" "abcdefghijklmnopqrstuvwxyz" @@ -21,6 +25,11 @@ static SDev *devs[sizeof devletters-1]; static QLock devslock; +static SDunit topctlunit; + +enum { + Ahdrsz = 2, +}; enum { Rawcmd, @@ -38,6 +47,7 @@ Qctl = Qunitbase, Qraw, Qpart, + Qextra, TypeLOG = 4, NType = (1<vers; if(unit->sectors > 0){ unit->sectors = unit->secsize = 0; sdincvers(unit); @@ -200,7 +212,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. @@ -290,7 +303,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; @@ -321,15 +334,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()); } } @@ -342,8 +354,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: @@ -376,16 +388,26 @@ } } +// void +// sdrmdevs(SDev *sdev) +// { +// char buf[2]; +// +// snprint(buf, sizeof buf, "%c", sdev->idno); +// unconfigure(buf); +// } + static int sd2gen(Chan* c, int i, Dir* dp) { Qid q; - vlong l; + uvlong l; + SDfile *e; SDpart *pp; SDperm *perm; SDunit *unit; SDev *sdev; - int rv; + int rv, t; sdev = sdgetdev(DEV(c->qid)); assert(sdev); @@ -399,7 +421,7 @@ perm = &unit->ctlperm; if(emptystr(perm->user)){ kstrdup(&perm->user, eve); - perm->perm = 0640; + perm->perm = 0644; } devdir(c, q, "ctl", 0, perm->user, perm->perm, dp); rv = 1; @@ -419,7 +441,7 @@ case Qpart: pp = &unit->part[PART(c->qid)]; - l = (pp->end - pp->start) * (vlong)unit->secsize; + l = (pp->end - pp->start) * unit->secsize; mkqid(&q, QID(DEV(c->qid), UNIT(c->qid), PART(c->qid), Qpart), unit->vers+pp->vers, QTFILE); if(emptystr(pp->user)) @@ -427,6 +449,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); @@ -437,21 +471,50 @@ sd1gen(Chan* c, int i, Dir* dp) { Qid q; + SDperm *p; switch(i){ case Qtopctl: mkqid(&q, QID(0, 0, 0, Qtopctl), 0, QTFILE); - devdir(c, q, "sdctl", 0, eve, 0640, dp); + qlock(&topctlunit.ctl); + p = &topctlunit.ctlperm; + if(p->user == nil || p->user[0] == 0){ + kstrdup(&p->name, "sdctl"); + kstrdup(&p->user, eve); + p->perm = 0640; + } + devdir(c, q, p->name, 0, p->user, p->perm, dp); + qunlock(&topctlunit.ctl); return 1; } return -1; } static int +efilegen(Chan *c, SDunit *unit, int i, Dir *dp) +{ + Qid q; + SDfile *e; + + i -= SDnpart; + if(unit->nefile == 0 || i >= unit->nefile) + return -1; + if(i < 0) + return 0; + e = unit->efile + i; + if(emptystr(e->user)) + kstrdup(&e->user, eve); + mkqid(&q, QID(DEV(c->qid), UNIT(c->qid), i, Qextra), + unit->vers, QTFILE); + devdir(c, q, e->name, 0, e->user, e->perm, dp); + return 1; +} + +static int sdgen(Chan* c, char*, Dirtab*, int, int s, Dir* dp) { Qid q; - vlong l; + uvlong l; int i, r; SDpart *pp; SDunit *unit; @@ -542,17 +605,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)) @@ -564,6 +628,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; @@ -600,7 +665,7 @@ if(spec[0] != 's' || spec[1] != 'd') error(Ebadspec); idno = spec[2]; - subno = strtol(&spec[3], &p, 0); + subno = strtol(&spec[3], &p, 16); if(p == &spec[3]) error(Ebadspec); @@ -654,7 +719,7 @@ break; case Qraw: c->qid.vers = unit->vers; - if(TAS(&unit->rawinuse) != 0){ + if(tas32(&unit->rawinuse) != 0){ c->flag &= ~COPEN; decref(&sdev->r); error(Einuse); @@ -704,16 +769,19 @@ } } +#define iskaddr(a) ((uintptr)(a) > KZERO) + static long -sdbio(Chan* c, int write, char* a, long len, vlong off) +sdbio(Chan* c, int write, char* a, long len, uvlong off) { - int nchange; + int nchange, hard, allocd; + long l; uchar *b; SDpart *pp; SDunit *unit; SDev *sdev; - vlong bno; - long l, max, nb, offset; + ulong max, nb, offset; + uvlong bno; sdev = sdgetdev(DEV(c->qid)); if(sdev == nil){ @@ -773,21 +841,30 @@ poperror(); } - b = sdmalloc(nb*unit->secsize); - if(b == nil) - error(Enomem); + offset = off%unit->secsize; + if(offset+len > nb*unit->secsize) + len = nb*unit->secsize - offset; + hard = offset || write && len%unit->secsize; + + if(iskaddr(a) && !hard) { + b = (uchar*)a; + allocd = 0; + }else{ + b = sdmalloc(nb*unit->secsize); + if(b == nil) + error(Enomem); + allocd = 1; + } if(waserror()){ - sdfree(b); + if(allocd) + sdfree(b); if(!(unit->inquiry[1] & 0x80)) decref(&sdev->r); /* gadverdamme! */ nexterror(); } - offset = off%unit->secsize; - if(offset+len > nb*unit->secsize) - len = nb*unit->secsize - offset; if(write){ - if(offset || (len%unit->secsize)){ + if(hard){ l = unit->dev->ifc->bio(unit, 0, 0, b, nb, bno); if(l < 0) error(Eio); @@ -798,7 +875,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); @@ -815,9 +893,11 @@ len = 0; else if(len > l - offset) len = l - offset; - memmove(a, b+offset, len); + if(allocd) + memmove(a, b+offset, len); } - sdfree(b); + if(allocd) + sdfree(b); poperror(); if(unit->inquiry[1] & 0x80){ @@ -832,41 +912,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) @@ -875,6 +983,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; @@ -896,36 +1005,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; @@ -936,25 +1016,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'. @@ -1006,13 +1067,15 @@ /* * Read capacity returns the LBA of the last sector. */ - len = unit->sectors - 1; + len = unit->sectors; + if(len > 0) + len--; p = r->data; *p++ = len>>24; *p++ = len>>16; *p++ = len>>8; *p++ = len; - len = 512; + len = unit->secsize; *p++ = len>>24; *p++ = len>>16; *p++ = len>>8; @@ -1028,7 +1091,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; @@ -1038,33 +1103,125 @@ *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; - long offset; + ulong offset; int i, l, m, status; offset = off; @@ -1081,9 +1238,11 @@ 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(offset, a, n, buf); + n = readstr(off, a, n, buf); free(buf); return n; @@ -1145,23 +1304,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); } } @@ -1171,13 +1345,14 @@ 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; SDreq *req; SDunit *unit; SDev *sdev; - uvlong end, start; switch(TYPE(c->qid)){ default: @@ -1238,7 +1413,8 @@ error(Ebadctl); poperror(); poperror(); - decref(&sdev->r); + if(sdev) + decref(&sdev->r); free(cb); break; @@ -1286,6 +1462,9 @@ break; case Qraw: + proto = SDcdb; + ataproto = 0; + atacdb = 0; sdev = sdgetdev(DEV(c->qid)); if(sdev == nil) error(Enonexist); @@ -1298,18 +1477,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: @@ -1320,15 +1515,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; @@ -1345,23 +1543,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; @@ -1383,14 +1588,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; } @@ -1474,13 +1687,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, @@ -1531,7 +1793,7 @@ { Devport *p; - p = (Devport *)malloc((dc->nports + 1) * sizeof(Devport)); + p = malloc((dc->nports + 1) * sizeof(Devport)); if(dc->nports > 0){ memmove(p, dc->ports, dc->nports * sizeof(Devport)); free(dc->ports); @@ -1622,7 +1884,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/nix/port/led.c Thu Jan 1 00:00:00 1970 +++ /sys/src/nix/port/led.c Mon Apr 23 19:32:29 2012 @@ -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/nix/port/sdscsifis.c Thu Jan 1 00:00:00 1970 +++ /sys/src/nix/port/sdscsifis.c Mon Apr 23 19:33:29 2012 @@ -0,0 +1,662 @@ +/* + * sas-able sdscsi + * copyright © 2010 erik quanstrom + */ + +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" +#include "../port/sd.h" +#include +#include "sdfis.h" + +#define reqio(r) (r)->unit->dev->ifc->rio(r) +#define dprint(...) print(__VA_ARGS__) +#define Ticks sys->ticks +#define generror(...) snprint(up->genbuf, sizeof up->genbuf, __VA_ARGS__) + +enum { + Xtimeout = 5*1000, + Deftimeout = 30*1000, /* default timeout */ + Forever = 600*1000, /* default raw timeout */ + + /* oob signals */ + Oobspinup = 0, + Oobpowerloss = 1, +}; + +static uvlong border = 0x0001020304050607ull; +static uvlong lorder = 0x0706050403020100ull; + +uvlong +getle(uchar *t, int w) +{ + uint i; + uvlong r; + + r = 0; + for(i = w; i != 0; ) + r = r<<8 | t[--i]; + return r; +} + +void +putle(uchar *t, uvlong r, int w) +{ + uchar *o, *f; + uint i; + + f = (uchar*)&r; + o = (uchar*)&lorder; + for(i = 0; i < w; i++) + t[o[i]] = f[i]; +} + +uvlong +getbe(uchar *t, int w) +{ + uint i; + uvlong r; + + r = 0; + for(i = 0; i < w; i++) + r = r<<8 | t[i]; + return r; +} + +void +putbe(uchar *t, uvlong r, int w) +{ + uchar *o, *f; + uint i; + + f = (uchar*)&r; + o = (uchar*)&border + (sizeof border-w); + for(i = 0; i < w; i++) + t[i] = f[o[i]]; +} + +static char* +unam(SDunit *u) +{ + return u->name; +} + +static uint +asckey(SDreq *r) +{ + uint fmt, n; + uchar *s; + + s = r->sense; +// if((s[0] & 0x80) == 0){ +// dprint("%s: non-scsi sense %.2ux\n", unam(r->unit), s[0]); +// return ~0; +// } + fmt = s[0] & 0x7f; + n = 18; /* botch should be r->slen; */ + /* spc3 §4.5.3; 0x71 is deferred. */ + if(n >= 18 && (fmt == 0x70 || fmt == 0x71)) + return (s[2] & 0xf)<<16 | s[12]<<8 | s[13]; + dprint("%s: cmd %.2ux unknown sense fmt %.2ux\n", unam(r->unit), r->cmd[0], fmt); + return (s[2] & 0xf)<<16 | s[12]<<8 | s[13]; +} + +/* + * other suspects: + * key asc/q + * 02 0401 becoming ready + * 040b target port in standby state + * 0b01 overtemp + * 0b0[345] background * + * 0c01 write error - recovered with auto reallocation + * 0c02 write error - auto reallocation failed + * 0c03 write error - recommend reassignment + * 17* recovered data + * 18* recovered data + * 5d* smart-style reporting (disk/smart handles) + * 5e* power state change + */ +static int +classify(SDunit *u, int key) +{ + switch(key>>16){ + case 0x00: + case 0x01: + return SDretry; + } + + if(key == 0x062902 || key == 0x062901 || key == 0x062900){ + dprint("%s: power on sense\n", unam(u)); + return SDretry; + } + if(key == 0x062800 && u->inquiry[1] & 0x80){ + dprint("%s: media change\n", unam(u)); + u->sectors = 0; + } + if(key == 0x020401){ + dprint("%s: becoming ready\n", unam(u)); + return SDretry; + } + if(key == 0x020411){ + dprint("%s: need notify (enable spinup)\n", unam(u)); + return SDspinup; + } + return SDcheck; +} + +ulong +totk(ulong u) +{ + if(u == 0) + u = Deftimeout; + return Ticks + u; +} + +ulong +gettotk(Sfisx *f) +{ + return totk(f->tler); +} + +int +setreqto(SDreq *r, ulong tk) +{ + long ms; + + ms = TK2MS(tk - Ticks); + if(ms < 2) + return -1; + if(ms > 750) + ms = 750; + r->timeout = Ticks + Ms2tk(ms); + return 0; +} + +static int +ereqio(Sfisx *f, SDreq *r) +{ + int rv; + + if(f == nil) + return reqio(r); + rv = -1; + rlock(f); + if(!waserror()){ + rv = reqio(r); + poperror(); + } + runlock(f); + return rv; +} + +void +oob(Sfisx *f, SDunit *u, int oobmsg) +{ + SDreq *r; + + r = malloc(sizeof r); + if(r == nil) + return; + r->cmd[0] = 0xf0; + r->cmd[1] = 0xca; + r->cmd[2] = 0xfe; + r->cmd[3] = 0xba; + putbe(r->cmd + 4, oobmsg, 4); + r->clen = 16; + r->unit = u; + r->timeout = totk(Ms2tk(Xtimeout)); + if(!waserror()){ + ereqio(f, r); + poperror(); + } + free(r); +} + +int +scsiriox(Sfisx *f, SDreq *r) +{ + int t, s; + + r->status = ~0; + if(r->timeout == 0) + r->timeout = totk(Ms2tk(Forever)); + for(t = r->timeout; setreqto(r, t) != -1;){ + s = ereqio(f, r); + if(s == SDcheck && r->flags & SDvalidsense) + s = classify(r->unit, asckey(r)); + switch(s){ + default: + return s; + case SDspinup: + print("%s: OOB\n", unam(r->unit)); + /* don't acknowledge oobspinup */ + oob(f, r->unit, Oobspinup); + } + } + sdsetsense(r, SDcheck, 0x02, 0x3e, 0x02); + return SDtimeout; +} + +void +edelay(ulong ms, ulong tk) +{ + int d; + + d = TK2MS(tk - Ticks); + if(d <= 0) + return; + if(d < ms) + ms = d/2; + if(ms <= 0) + return; + if(up){ + while(waserror()) + ; + tsleep(&up->sleep, return0, 0, ms); + poperror(); + }else + delay(ms); +} + +static int +scsiexec(Sfisx *f, SDreq *r) +{ + ulong s, t; + + for(t = r->timeout; setreqto(r, t) != -1; edelay(250, t)){ + if((s = scsiriox(f, r)) != SDok) + return s; + switch(r->status){ + default: + return r->status; + case SDtimeout: + case SDretry: + continue; + } + } + return -1; +} + +/* open address fises */ +enum{ + Initiator = 0x80, + Openaddr = 1, + Awms = 0x8000, + Smp = 0, + Ssp = 1, + Stp = 2, +}; + +static void +oafis(Cfis *f, uchar *c, int type, int spd) +{ + c[0] = Initiator | type<<4 | Openaddr; + c[1] = spd; + if(type == Smp) + memset(c + 2, 0xff, 2); + else + memmove(c + 2, f->ict, 2); + memmove(c + 4, f->tsasaddr, 8); /* dest "port identifier" §4.2.6 */ + memmove(c + 12, f->ssasaddr, 8); +} + +static int +inquiry(SDunit *u) +{ + SDreq r; + + memset(&r, 0, sizeof r); + r.cmd[0] = 0x12; + r.cmd[4] = 0xff; + r.clen = 6; + r.unit = u; + r.timeout = totk(Ms2tk(Xtimeout)); + r.data = u->inquiry; + r.dlen = sizeof u->inquiry; + return scsiexec(nil, &r); +} + +int +tur(SDunit *u, int timeout, uint *key) +{ + int rv; + SDreq r; + + memset(&r, 0, sizeof r); + r.clen = 6; + r.unit = u; + r.timeout = totk(timeout); + rv = scsiexec(nil, &r); + *key = r.status; + if(r.flags & SDvalidsense) + *key = asckey(&r); + return rv; +} + +static int +sasvpd(SDunit *u, uchar *buf, int l) +{ + SDreq r; + + memset(&r, 0, sizeof r); + r.cmd[0] = 0x12; + r.cmd[1] = 1; + r.cmd[2] = 0x80; + r.cmd[4] = l; + r.clen = 6; + r.data = buf; + r.dlen = l; + r.unit = u; + r.timeout = totk(Ms2tk(Xtimeout)); + return scsiexec(nil, &r); +} + +static int +capacity10(SDunit *u, uchar *buf, int l) +{ + SDreq r; + + memset(&r, 0, sizeof r); + r.cmd[0] = 0x25; + r.clen = 10; + r.data = buf; + r.dlen = l; + r.unit = u; + r.timeout = totk(Ms2tk(Xtimeout)); + return scsiexec(nil, &r); +} + +static int +capacity16(SDunit *u, uchar *buf, int l) +{ + SDreq r; + + memset(&r, 0, sizeof r); + r.cmd[0] = 0x9e; + r.cmd[1] = 0x10; + r.cmd[13] = l; + r.clen = 16; + r.data = buf; + r.dlen = l; + r.unit = u; + r.timeout = totk(Ms2tk(Xtimeout)); + return scsiexec(nil, &r); +} + +startstop(SDunit *u, int code) +{ + SDreq r; + + memset(&r, 0, sizeof r); + r.cmd[0] = 0x1b; + r.cmd[1] = 0<<5 | 1; /* lun depricated */ + r.cmd[4] = code; + r.clen = 6; + r.unit = u; + r.timeout = totk(Ms2tk(Xtimeout)); + return scsiexec(nil, &r); +} + +static void +frmove(char *p, uchar *c, int n) +{ + char *s, *e; + + memmove(p, c, n); + p[n] = 0; + for(e = p + n - 1; e > p && *e == ' '; e--) + *e = 0; + for(s = p; *s == ' '; ) + s++; + memmove(p, s, (e - s) + 2); +} + +static void +chkinquiry(Sfisx *f, uchar *c) +{ + char buf[32], buf2[32], omod[sizeof f->model]; + + memmove(omod, f->model, sizeof f->model); + frmove(buf, c + 8, 8); + frmove(buf2, c + 16, 16); + snprint(f->model, sizeof f->model, "%s %s", buf, buf2); + frmove(f->firmware, c + 23, 4); + if(memcmp(omod, f->model, sizeof omod) != 0) + f->drivechange = 1; +} + +static void +chkvpd(Sfisx *f, uchar *c, int n) +{ + char buf[sizeof f->serial]; + int l; + + if(n > sizeof buf - 1) + n = sizeof buf - 1; + l = c[3]; + if(l > n) + l = n; + frmove(buf, c + 4, l); + if(strcmp(buf, f->serial) != 0) + f->drivechange = 1; + memmove(f->serial, buf, sizeof buf); +} + +static int +adjcapacity(Sfisx *f, uvlong ns, uint nss) +{ + if(ns != 0) + ns++; + if(nss == 2352) + nss = 2048; + if(f->sectors != ns || f->secsize != nss){ + f->drivechange = 1; + f->sectors = ns; + f->secsize = nss; + } + return 0; +} + +static int +chkcapacity10(uchar *p, uvlong *ns, uint *nss) +{ + *ns = getbe(p, 4); + *nss = getbe(p + 4, 4); + return 0; +} + +static int +chkcapacity16(uchar *p, uvlong *ns, uint *nss) +{ + *ns = getbe(p, 8); + *nss = getbe(p + 8, 4); + return 0; +} + +typedef struct Spdtab Spdtab; +struct Spdtab { + int spd; + char *s; +}; +Spdtab spdtab[] = { +[1] Spd60, "6.0gbps", + Spd30, "3.0gbps", + Spd15, "1.5gbps", +}; + +static int +sasspd(SDunit *u, Sfisx *f) +{ + int i, r; + uint key; + + for(i = 1;; i++){ + if(i == nelem(spdtab)) + return SDrate; + if(spdtab[i].spd > f->maxspd) + continue; + oafis(f, f->oaf, Ssp, spdtab[i].spd); + dprint("%s: rate %s\n", unam(u), spdtab[i].s); + /* this timeout is too long */ + if((r = tur(u, 2*Xtimeout, &key)) == SDok){ + f->sasspd = i; + return SDok; + } + dprint("%s: key is %d / key=%.6ux\n", unam(u), key, key); + if(key != SDrate || ++i == nelem(spdtab)) + return r; + } +} + +static int +scsionline0(SDunit *u, Sfisx *f) +{ + uchar buf[0x40]; + int r; + uint nss; + uvlong ns; + + /* todo: cap the total sasprobe time, not just cmds */ + f->sasspd = 0; + if(f->maxspd != 0 && (r = sasspd(u, f)) != 0) + return r; + if((r = inquiry(u)) != SDok) + return r; + chkinquiry(f, u->inquiry); + /* vpd 0x80 (unit serial) is not mandatory; spc-4 §7.7 */ + memset(buf, 0, sizeof buf); + if(sasvpd(u, buf, sizeof buf) == SDok) + chkvpd(f, buf, sizeof buf); + else{ + if(f->serial[0]) + f->drivechange = 1; + f->serial[0] = 0; + } + if((r = capacity10(u, buf, 8)) != SDok) + return r; + chkcapacity10(buf, &ns, &nss); + if(ns == 0xffffffff){ + if((r = capacity16(u, buf, 16)) != SDok) + return r; + chkcapacity16(buf, &ns, &nss); + } + adjcapacity(f, ns, nss); + startstop(u, 0<<4 | 1); + return 0; +} + +int +scsionlinex(SDunit *u, Sfisx *f) +{ + int r; + + wlock(f); + if(waserror()) + r = SDeio; + else{ + r = scsionline0(u, f); + poperror(); + } + wunlock(f); + return r; +} + +static int +rwcdb(Sfis*, uchar *c, int write, ulong count, uvlong lba) +{ + int is16; + static uchar tab[2][2] = {0x28, 0x88, 0x2a, 0x8a}; + + is16 = lba>0xffffffffull; + c[0] = tab[write][is16]; + if(is16){ + putbe(c + 2, lba, 8); + putbe(c + 10, count, 4); + c[14] = 0; + c[15] = 0; + return 16; + }else{ + putbe(c + 2, lba, 4); + c[6] = 0; + putbe(c + 7, count, 2); + return 10; + } +} + +static void +setlun(uchar *c, int lun) +{ + c[1] = lun<<5; /* wrong for anything but ancient h/w */ +} + +long +scsibiox(SDunit *u, Sfisx *f, int lun, int write, void *data, long count, uvlong lba) +{ + SDreq r; + + memset(&r, 0, sizeof r); + r.unit = u; + r.lun = lun; + r.data = data; + r.dlen = count * u->secsize; + r.clen = rwcdb(f, r.cmd, write, count, lba); + setlun(r.cmd, lun); + r.timeout = gettotk(f); + + switch(scsiexec(f, &r)){ + case 0: + if((r.flags & SDvalidsense) == 0 || r.sense[2] == 0) + return r.rlen; + default: + generror("%s cmd %.2ux sense %.6ux", Eio, r.cmd[0], asckey(&r)); + error(up->genbuf); + return -1; + case SDtimeout: + generror("%s cmd %.2ux timeout", Eio, r.cmd[0]); + error(up->genbuf); + return -1; + } +} + +static char* +rctlsata(Sfis *f, char *p, char *e) +{ + p = seprint(p, e, "flag\t"); + p = pflag(p, e, f); + p = seprint(p, e, "udma\t%d\n", f->udma); + return p; +} + +static char* +rctlsas(Sfisx *f, char *p, char *e) +{ + char *s; + + s = "none"; + if(f->sasspd < nelem(spdtab)) + if(spdtab[f->sasspd].s != nil) +// if(f->state == Dnew || f->state == Dready) + s = spdtab[f->sasspd].s; + p = seprint(p, e, "sasspd %s\n", s); + return p; +} + +char* +sfisxrdctl(Sfisx *f, char *p, char *e) +{ + p = seprint(p, e, "model\t%s\n", f->model); + p = seprint(p, e, "serial\t%s\n", f->serial); + p = seprint(p, e, "firm\t%s\n", f->firmware); + p = seprint(p, e, "wwn\t%llux\n", f->wwn); + p = seprint(p, e, "tler\t%ud\n", f->tler); + if(f->type == Sata) + p = rctlsata(f, p, e); + if(f->type == Sas) + p = rctlsas(f, p, e); + return p; +} --- /sys/src/nix/port/sdatafis.c Thu Jan 1 00:00:00 1970 +++ /sys/src/nix/port/sdatafis.c Mon Apr 23 19:33:29 2012 @@ -0,0 +1,292 @@ +/* + * ata analog to sdscsi + * copyright © 2010 erik quanstrom + */ + +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" +#include "../port/sd.h" +#include +#include "sdfis.h" + +#define reqio(r) (r)->unit->dev->ifc->ataio(r) +#define dprint(...) print(__VA_ARGS__) + +static char* +dnam(SDunit *u) +{ + return u->name; +} + +static int +settxmode(SDunit *u, Sfis *f, uchar x) +{ + int t; + SDreq r; + + memset(&r, 0, sizeof r); + r.unit = u; + if((t = txmodefis(f, r.cmd, x)) == -1) + return 0; + r.clen = 16; + r.ataproto = t; + r.timeout = totk(Ms2tk(1*1000)); + return reqio(&r); +} + +static int +flushcache(SDunit *u, Sfis *f) +{ + SDreq r; + + memset(&r, 0, sizeof r); + r.unit = u; + r.clen = 16; + r.ataproto = flushcachefis(f, r.cmd); + r.timeout = totk(Ms2tk(60*1000)); + return reqio(&r); +} + +static int +setfeatures(SDunit *u, Sfis *f, uchar x, uint w) +{ + SDreq r; + + memset(&r, 0, sizeof r); + r.unit = u; + r.clen = 16; + r.ataproto = featfis(f, r.cmd, x); + r.timeout = totk(w); + return reqio(&r); +} + +static int +identify1(SDunit *u, Sfis *f, void *id) +{ + SDreq r; + + memset(&r, 0, sizeof r); + r.unit = u; + r.clen = 16; + r.ataproto = identifyfis(f, r.cmd); + r.data = id; + r.dlen = 0x200; + r.timeout = totk(Ms2tk(3*1000)); + return reqio(&r); +} + +static int +identify0(SDunit *u, Sfisx *f, ushort *id) +{ + int i, n; + vlong osectors, s; + uchar oserial[21]; + + for(i = 0;; i++){ + if(i > 5 || identify1(u, f, id) != 0) + return -1; + n = idpuis(id); + if(n & Pspinup && setfeatures(u, f, 7, 20*1000) == -1) + dprint("%s: puis spinup fail\n", dnam(u)); + if(n & Pidready) + break; + } + + s = idfeat(f, id); + if(s == -1) + return -1; + if((f->feat&Dlba) == 0){ + dprint("%s: no lba support\n", dnam(u)); + return -1; + } + osectors = u->sectors; + memmove(oserial, f->serial, sizeof f->serial); + + f->sectors = s; + f->secsize = idss(f, id); + + idmove(f->serial, id+10, 20); + idmove(f->firmware, id+23, 8); + idmove(f->model, id+27, 40); + f->wwn = idwwn(f, id); + 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, f->model, 40); + + if(osectors != s || memcmp(oserial, f->serial, sizeof oserial)){ + f->drivechange = 1; + u->sectors = 0; + } + return 0; +} + +static int +identify(SDunit *u, Sfisx *f) +{ + int r; + ushort *id; + + id = malloc(0x200); + if(id == nil) + error(Enomem); + r = identify0(u, f, id); + free(id); + return r; +} + +void +pronline(SDunit *u, Sfisx *f) +{ + char *s, *t; + + if(f->type == Sas) + s = "sas"; + else{ + s = "lba"; + if(f->feat & Dllba) + s = "llba"; + if(f->feat & Datapi) + s = "atapi"; + } + t = ""; + if(f->drivechange) + t = "[newdrive]"; + print("%s: %s %,lld sectors\n", dnam(u), s, f->sectors); + print(" %s %s %s %s\n", f->model, f->firmware, f->serial, t); +} + +int +ataonline0(SDunit *u, Sfisx *f) +{ + if(identify(u, f) != 0){ + dprint("%s: identify failure\n", dnam(u)); + return SDeio; + } + if(f->feat & Dpower && setfeatures(u, f, 0x85, 3*1000) != 0) + f->feat &= ~Dpower; + if(settxmode(u, f, f->udma) != 0){ + dprint("%s: can't set tx mode udma %d\n", dnam(u), f->udma); + return SDeio; + } + return SDok; +} + + +int +ataonline(SDunit *u, Sfisx *f) +{ + int r; + + wlock(f); + if(waserror()) + r = SDeio; + else{ + r = ataonline0(u, f); + poperror(); + } + wunlock(f); + return r; +} + +static int +ereqio(Sfisx *f, SDreq *r) +{ + int rv; + + rv = -1; + rlock(f); + if(!waserror()){ + rv = reqio(r); + poperror(); + } + runlock(f); + return rv; +} + +static int +ataexec(Sfisx *f, SDreq *r) +{ + ulong s, t; + + for(t = r->timeout; setreqto(r, t) != -1; edelay(250, t)){ + if((s = ereqio(f, r)) != SDok) + return s; + switch(r->status){ + default: + return r->status; + case SDtimeout: + case SDretry: + continue; + } + } + return -1; +} + +long +atabio(SDunit* u, Sfisx *f, int lun, int write, void *d0, long count0, uvlong lba) +{ + char *data; + uint llba, n, count; + SDreq r; +// Sfisx *f; + +// f = u->f; + memset(&r, 0, sizeof r); + r.unit = u; + r.lun = lun; + llba = (f->feat & Dlba) != 0; + r.clen = 16; + data = d0; + r.timeout = gettotk(f); + for(count = count0; count > 0; count -= n){ + n = count; + if(llba && n > 65536) + n = 65536; + else if(!llba && n > 256) + n = 256; + if(n > f->atamaxxfr) + n = f->atamaxxfr; + r.data = data; + r.dlen = n*f->secsize; + r.ataproto = rwfis(f, r.cmd, write, n, lba); + r.write = (r.ataproto & Pout) != 0; + if(ataexec(f, &r) != SDok) + return -1; + data += r.dlen; + lba += n; + } + return count0 * f->secsize; +} + +int +atariosata(SDunit *u, Sfisx *f, SDreq *r) +{ + uchar *cmd; + int i, n, count, rw; + uvlong lba; + + cmd = r->cmd; + if(cmd[0] == 0x35 || cmd[0] == 0x91){ + if(flushcache(u, f) == 0) + return sdsetsense(r, SDok, 0, 0, 0); /* stupid scuzz */ + 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; + n = atabio(u, f, r->lun, r->write, r->data, count, lba); + if(n == -1) + return SDeio; + r->rlen = n; + return sdsetsense(r, SDok, 0, 0, 0); /* stupid scuzz */ +} --- /sys/src/nix/k10/sdahci.c Thu Jan 1 00:00:00 1970 +++ /sys/src/nix/k10/sdahci.c Mon Apr 23 19:36:10 2012 @@ -0,0 +1,2133 @@ +/* + * intel/amd ahci sata controller + * copyright © 2007-12 coraid, inc. + */ + +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" +#include "../port/sd.h" +#include +#include "../port/sdfis.h" +#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) 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 Ticks sys->ticks +#define Pciwaddrh(va) ((u32int)(PCIWADDR(va)>>32)) + +enum { + NCtlr = 4, + NCtlrdrv = 32, + NDrive = NCtlr*NCtlrdrv, + + Fahdrs = 4, + + Read = 0, + Write, + + Eesb = 1<<0, /* must have (Eesb & Emtype) == 0 */ + + /* pci space configuration */ + Pmap = 0x90, + Ppcs = 0x91, + + Nms = 256, + Mphywait = 2*1024/Nms - 1, + Midwait = 16*1024/Nms - 1, + Mcomrwait = 64*1024/Nms - 1, +}; + +enum { + Tesb, + Tsb600, + Tjmicron, + Tahci, + Tlast, +}; + +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 = 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] = { + "null", + "missing", + "new", + "ready", + "error", + "reset", + "offline", + "portreset", +}; + +extern SDifc sdahciifc; + +enum { + DMautoneg, + DMsatai, + DMsataii, + DMsataiii, + DMlast, +}; + +static char *modes[DMlast] = { + "auto", + "satai", + "sataii", + "sataiii", +}; + +typedef struct Htab Htab; +struct Htab { + uint bit; + char *name; +}; + +struct Drive { + Lock; + + Ctlr *ctlr; + SDunit *unit; + char name[10]; + Aport *port; + Aportm portm; + Aportc portc; /* redundant ptr to port and portm. */ + Ledport; + + ulong totick; + ulong lastseen; + uint wait; + uchar mode; + uchar state; + + /* + * 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; + + Ctlrtype *type; + int enabled; + SDev *sdev; + Pcidev *pci; + + uchar *mmio; + u32int *lmmio; + Ahba *hba; + Aenc; + uint enctype; + + Drive rawdrive[NCtlrdrv]; + Drive* drive[NCtlrdrv]; + int ndrive; + uint pi; +}; + +static Ctlr iactlr[NCtlr]; +static SDev sdevs[NCtlr]; +static int niactlr; +static ushort olds[NCtlr*NCtlrdrv]; + +static Drive *iadrive[NDrive]; +static int niadrive; + +static int debug; +static int prid = 1; +static int datapi; +static int dled; + +static char stab[] = { +[0] 'i', 'm', +[8] 't', 'c', 'p', 'e', +[16] 'N', 'I', 'W', 'B', 'D', 'C', 'H', 'S', 'T', 'F', 'X' +}; + +static void +serrstr(u32int r, char *s, char *e) +{ + int i; + + e -= 3; + for(i = 0; i < nelem(stab) && s < e; i++) + if(r & (1<task, p->cmd, p->ci, p->isr); +} + +static void +esleep(int ms) +{ + if(waserror()) + return; + tsleep(&up->sleep, return0, 0, ms); + poperror(); +} + +typedef struct { + Aport *p; + int i; +} Asleep; + +static int +ahciclear(void *v) +{ + Asleep *s; + + s = v; + return (s->p->ci & s->i) == 0; +} + +static void +aesleep(Aportm *m, Asleep *a, int ms) +{ + if(waserror()) + return; + tsleep(m, ahciclear, a, ms); + poperror(); +} + +static int +ahciwait(Aportc *c, int ms) +{ + Aport *p; + Asleep as; + + p = c->p; + p->ci = 1; + as.p = p; + as.i = 1; + aesleep(c->m, &as, ms); + if((p->task & 1) == 0 && p->ci == 0) + return 0; + dreg("ahciwait fail/timeout ", c->p); + return -1; +} + +static void +mkalist(Aportm *m, uint flags, uchar *data, int len) +{ + Actab *t; + Alist *l; + Aprdt *p; + + t = m->ctab; + l = m->list; + l->flags = flags | 0x5; + l->len = 0; + l->ctab = PCIWADDR(t); + l->ctabhi = Pciwaddrh(t); + if(data){ + l->flags |= 1<<16; + p = &t->prdt; + p->dba = PCIWADDR(data); + p->dbahi = Pciwaddrh(data); + p->count = 1<<31 | len - 2 | 1; + } +} + +static int +settxmode(Aportc *pc, uchar f) +{ + uchar *c; + + c = pc->m->ctab->cfis; + if(txmodefis(pc->m, c, f) == -1) + return 0; + mkalist(pc->m, Lwrite, 0, 0); + return ahciwait(pc, 3*1000); +} + +static void +asleep(int ms) +{ + if(up == nil) + delay(ms); + else + esleep(ms); +} + +static int +ahciportreset(Aportc *c, uint mode) +{ + 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) + break; + asleep(25); + } + p->sctl = 3*Aipm | 0*Aspd | Adet; + delay(1); + p->sctl = 3*Aipm | mode*Aspd; + return 0; +} + +static int +ahciquiet(Aport *a) +{ + int i; + u32int *p; + + p = &a->cmd; + *p &= ~Ast; + for(i = 0; i < 500; i += 50){ + if((*p & Acr) == 0) + goto stop; + asleep(50); + } + return -1; +stop: + if((a->task & (ASdrq|ASbsy)) == 0){ + *p |= Ast; + return 0; + } + + *p |= Aclo; + for(i = 0; i < 500; i += 50){ + if((*p & Aclo) == 0) + goto stop1; + asleep(50); + } + return -1; +stop1: + /* extra check */ + dprint("ahci: clo clear %ux\n", a->task); + if(a->task & ASbsy) + return -1; + *p |= Afre | Ast; + return 0; +} + +static int +ahcicomreset(Aportc *pc) +{ + uchar *c; + + dreg("comreset ", pc->p); + if(ahciquiet(pc->p) == -1){ + dprint("ahci: ahciquiet failed\n"); + return -1; + } + dreg("comreset ", pc->p); + + c = pc->m->ctab->cfis; + nopfis(pc->m, c, 1); + mkalist(pc->m, Lclear | Lreset, 0, 0); + if(ahciwait(pc, 500) == -1){ + dprint("ahci: comreset1 failed\n"); + return -1; + } + microdelay(250); + dreg("comreset ", pc->p); + + nopfis(pc->m, c, 0); + mkalist(pc->m, Lwrite, 0, 0); + if(ahciwait(pc, 150) == -1){ + dprint("ahci: comreset2 failed\n"); + return -1; + } + dreg("comreset ", pc->p); + return 0; +} + +static int +ahciidle(Aport *port) +{ + int i, r; + u32int *p; + + p = &port->cmd; + if((*p & Arun) == 0) + return 0; + *p &= ~Ast; + r = 0; + for(i = 0; i < 500; i += 25){ + if((*p & Acr) == 0) + goto stop; + asleep(25); + } + r = -1; +stop: + if((*p & Afre) == 0) + return r; + *p &= ~Afre; + for(i = 0; i < 500; i += 25){ + if((*p & Afre) == 0) + return 0; + asleep(25); + } + return -1; +} + +/* + * §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. + */ +static int +ahciswreset(Aportc *pc) +{ + int i; + + i = ahciidle(pc->p); + pc->p->cmd |= Afre; + if(i == -1) + return -1; + if(pc->p->task & (ASdrq|ASbsy)) + return -1; + return 0; +} + +static int +ahcirecover(Aportc *pc) +{ + ahciswreset(pc); + pc->p->cmd |= Ast; + if(settxmode(pc, pc->m->udma) == -1) + return -1; + return 0; +} + +static void +setupfis(Afis *f) +{ + 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 = (u32int*)(f->base + 0x58); +} + +static void +ahciwakeup(Aportc *c, uint mode) +{ + ushort s; + + s = c->p->sstatus; + if((s & Isleepy) == 0) + return; + if((s & Smask) != Spresent){ + print("ahci: slumbering drive missing %.3ux\n", s); + return; + } + ahciportreset(c, mode); +// iprint("ahci: wake %.3ux -> %.3lux\n", s, c->p->sstatus); +} + +static int +ahciconfigdrive(Ahba *h, Aportc *c, int mode) +{ + Aportm *m; + Aport *p; + + p = c->p; + m = c->m; + + 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 = PCIWADDR(m->list); + p->listhi = Pciwaddrh(m->list); + p->fis = PCIWADDR(m->fis.base); + p->fishi = Pciwaddrh(m->fis.base); + + p->cmd |= Afre; + + if((p->sstatus & Sbist) == 0 && (p->cmd & Apwr) != Apwr) + if((p->sstatus & Sphylink) == 0 && h->cap & Hss){ + 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; + + 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; +} + +static void +ahcidisable(Ahba *h) +{ + h->ghc &= ~Hie; +} + +static int +countbits(u32int u) +{ + int i, n; + + n = 0; + for(i = 0; i < 32; i++) + if(u & (1<hba = (Ahba*)c->mmio; + u = h->cap; + if((u & Ham) == 0) + h->ghc |= Hae; + return countbits(h->pi); +} + +static int +ahcihbareset(Ahba *h) +{ + int wait; + + h->ghc |= Hhr; + for(wait = 0; wait < 1000; wait += 100){ + if(h->ghc == 0) + return 0; + delay(100); + } + return -1; +} + +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 void +clearci(Aport *p) +{ + 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) +{ + u32int f, cause, serr, s0, pr, ewake; + Aport *p; + 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; + + if(p->ci == 0){ + f |= Fdone; + pr = 0; + }else if(cause & Adps) + pr = 0; + if(cause & Ifatal){ + ewake = 1; + dprint("%s: fatal\n", dnam(d)); + } + if(cause & Adhrs){ + 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 %ux serr %ux task %ux sstat %.3ux\n", + dnam(d), cause, serr, p->task, p->sstatus); + if(pr) + dprint("%s: upd %ux ta %ux\n", dnam(d), cause, p->task); + + if(cause & (Aprcs|Aifs)){ + s0 = d->state; + switch(p->sstatus & Smask){ + case Smissing: + d->state = Dmissing; + break; + case Spresent: + if((p->sstatus & Imask) == Islumber) + d->state = Dnew; + else + d->state = Derror; + break; + case Sphylink: + /* power mgnt crap for suprise removal */ + p->ie |= Aprcs|Apcs; /* is this required? */ + d->state = Dreset; + break; + case Sbist: + d->state = Doffline; + break; + } + 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", dnam(d)); + if(d->state != Dready) + f |= Ferror; + if(d->state != Dready || p->ci) + ewake = 1; + } + p->serror = serr; + if(ewake) + clearci(p); + if(f){ + d->portm.flag = f; + wakeup(&d->portm); + } + last = cause; +} + +static void +pstatus(Drive *d, u32int s) +{ + /* + * bogus code because the first interrupt is currently dropped. + * likely my fault. serror is maybe cleared at the wrong time. + */ + if(s) + d->lastseen = Ticks; + switch(s){ + default: + print("%s: pstatus: bad status %.3ux\n", dnam(d), s); + case Smissing: + d->state = Dmissing; + break; + case Spresent: + break; + case Sphylink: + d->wait = 0; + d->state = Dnew; + break; + case Sbist: + d->state = Doffline; + break; + } +} + +static int +configdrive(Drive *d) +{ + if(ahciconfigdrive(d->ctlr->hba, &d->portc, d->mode) == -1) + return -1; + ilock(d); + pstatus(d, d->port->sstatus & Smask); + iunlock(d); + return 0; +} + +static void +resetdisk(Drive *d) +{ + uint state, det, stat; + Aport *p; + + p = d->port; + det = p->sctl & 7; + stat = p->sstatus & Smask; + state = (p->cmd>>28) & 0xf; + dprint("%s: resetdisk: icc %ux det %.3ux sdet %.3ux\n", dnam(d), state, det, stat); + + ilock(d); + state = d->state; + if(d->state != Dready || d->state != Dnew) + d->portm.flag |= Ferror; + clearci(p); /* satisfy sleep condition. */ + wakeup(&d->portm); + 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{ + setstate(d, Dmissing); + configdrive(d); + } + dprint("%s: resetdisk: %s → %s\n", dnam(d), dstate(state), dstate(d->state)); + qunlock(&d->portm); +} + +static int +newdrive(Drive *d) +{ + Aportc *c; + Aportm *m; + + c = &d->portc; + m = &d->portm; + + qlock(c->m); + setfissig(m, c->p->sig); + qunlock(c->m); + + if(ataonline(d->unit, m) != 0) + goto lose; + m->atamaxxfr = 128; + if(d->portm.feat & Dllba) + m->atamaxxfr = d->ctlr->type->maxdmaxfr; + + setstate(d, Dready); + pronline(d->unit, m); + return 0; + +lose: + qlock(c->m); + idprint("%s: can't be initialized\n", dnam(d)); + setstate(d, Dnull); + qunlock(c->m); + return -1; +} + +static int +doportreset(Drive *d) +{ + int i; + + i = -1; + qlock(&d->portm); + if(ahciportreset(&d->portc, d->mode) == -1) + dprint("ahci: ahciportreset fails\n"); + else + i = 0; + qunlock(&d->portm); + dprint("ahci: portreset → %s [task %.4ux ss %.3ux]\n", + dstate(d->state), d->port->task, d->port->sstatus); + return i; +} + +static void +statechange(Drive *d) +{ + Aportm *m; + + m = &d->portm; + switch(d->state){ + case Dnull: + case Doffline: + if(d->unit) + if(d->unit->sectors != 0){ + m->sectors = 0; + m->drivechange = 1; + } + case Dready: + d->wait = 0; + } +} + +static uint +maxmode(Ctlr *c) +{ + return (c->hba->cap & 0xf*Hiss)/Hiss; +} + +static void +checkdrive(Drive *d, int i) +{ + ushort s, sig; + + ilock(d); + s = d->port->sstatus; + if(s) + d->lastseen = Ticks; + if(s != olds[i]){ + dprint("%s: status: %.3ux -> %.3ux: %s\n", + dnam(d), olds[i], s, dstate(d->state)); + olds[i] = s; + d->wait = 0; + } + switch(d->state){ + case Dnull: + case Dready: + break; + case Dmissing: + case Dnew: + switch(s & (Iactive|Smask)){ + case Spresent: + ahciwakeup(&d->portc, d->mode); + case Smissing: + break; + default: + dprint("%s: unknown status %.3ux\n", dnam(d), s); + /* fall through */ + case Iactive: /* active, no device */ + if(++d->wait&Mphywait) + break; +reset: + 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", dnam(d), + modes[d->mode]); + iunlock(d); + resetdisk(d); + ilock(d); + break; + case Iactive | Sphylink: + if(d->unit == nil) + break; + if((++d->wait&Midwait) == 0){ + dprint("%s: slow reset %.3ux task=%ux; %d\n", + dnam(d), s, d->port->task, d->wait); + goto reset; + } + s = (uchar)d->port->task; + sig = d->port->sig >> 16; + if(s == 0x7f || s&ASbsy || + (sig != 0xeb14 && (s & ASdrdy) == 0)) + break; + iunlock(d); + newdrive(d); + ilock(d); + break; + } + break; + case Doffline: + if(d->wait++ & Mcomrwait) + break; + /* fallthrough */ + case Derror: + case Dreset: + 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 & Iactive) == 0) + break; + 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 & Smask) == 0){ + d->state = Dmissing; + break; + } + iunlock(d); + doportreset(d); + ilock(d); + break; + } + statechange(d); + iunlock(d); +} + +static void +satakproc(void*) +{ + int i; + + for(;;){ + tsleep(&up->sleep, return0, 0, Nms); + for(i = 0; i < niadrive; i++) + checkdrive(iadrive[i], i); + } +} + +static void +iainterrupt(Ureg*, void *a) +{ + int i; + u32int cause, m; + Ctlr *c; + Drive *d; + + c = a; + ilock(c); + cause = c->hba->isr; + for(i = 0; cause; i++){ + m = 1 << i; + if((cause & m) == 0) + continue; + cause &= ~m; + d = c->rawdrive + i; + ilock(d); + if(d->port->isr && c->pi & m) + updatedrive(d); + c->hba->isr = m; + iunlock(d); + } + iunlock(c); +} + +static int +ahciencreset(Ctlr *c) +{ + Ahba *h; + + if(c->enctype == Eesb) + return 0; + h = c->hba; + h->emctl |= Emrst; + while(h->emctl & Emrst) + delay(1); + return 0; +} + +/* + * from the standard: (http://en.wikipedia.org/wiki/IBPI) + * rebuild is preferred as locate+fail; alternate 1hz fail + * we're going to assume no locate led. + */ +enum { + Ledsleep = 125, /* 8hz */ + + N0 = Ledon*Aled, + L0 = Ledon*Aled | Ledon*Locled, + L1 = Ledon*Aled | Ledoff*Locled, + R0 = Ledon*Aled | Ledon*Locled | Ledon*Errled, + R1 = Ledon*Aled | Ledoff*Errled, + S0 = Ledon*Aled | Ledon*Locled /*| Ledon*Errled*/, /* botch */ + S1 = Ledon*Aled | Ledoff*Errled, + P0 = Ledon*Aled | Ledon*Errled, + P1 = Ledon*Aled | Ledoff*Errled, + F0 = Ledon*Aled | Ledon*Errled, + C0 = Ledon*Aled | Ledon*Locled, + C1 = Ledon*Aled | Ledoff*Locled, + +}; + +//static ushort led3[Ibpilast*8] = { +//[Ibpinone*8] 0, 0, 0, 0, 0, 0, 0, 0, +//[Ibpinormal*8] N0, N0, N0, N0, N0, N0, N0, N0, +//[Ibpirebuild*8] R0, R0, R0, R0, R1, R1, R1, R1, +//[Ibpilocate*8] L0, L1, L0, L1, L0, L1, L0, L1, +//[Ibpispare*8] S0, S1, S0, S1, S1, S1, S1, S1, +//[Ibpipfa*8] P0, P1, P0, P1, P1, P1, P1, P1, /* first 1 sec */ +//[Ibpifail*8] F0, F0, F0, F0, F0, F0, F0, F0, +//[Ibpicritarray*8] C0, C0, C0, C0, C1, C1, C1, C1, +//[Ibpifailarray*8] C0, C1, C0, C1, C0, C1, C0, C1, +//}; + +static ushort led2[Ibpilast*8] = { +[Ibpinone*8] 0, 0, 0, 0, 0, 0, 0, 0, +[Ibpinormal*8] N0, N0, N0, N0, N0, N0, N0, N0, +[Ibpirebuild*8] R0, R0, R0, R0, R1, R1, R1, R1, +[Ibpilocate*8] L0, L0, L0, L0, L0, L0, L0, L0, +[Ibpispare*8] S0, S0, S0, S0, S1, S1, S1, S1, +[Ibpipfa*8] P0, P1, P0, P1, P1, P1, P1, P1, /* first 1 sec */ +[Ibpifail*8] F0, F0, F0, F0, F0, F0, F0, F0, +[Ibpicritarray*8] C0, C0, C0, C0, C1, C1, C1, C1, +[Ibpifailarray*8] C0, C1, C0, C1, C0, C1, C0, C1, +}; + +static int +ledstate(Ledport *p, uint seq) +{ + ushort i; + + if(p->led == Ibpipfa && seq%32 >= 8) + i = P1; + else + i = led2[8*p->led + seq%8]; + if(i != p->ledbits){ + p->ledbits = i; + ledprint("ledstate %,.011ub %ud\n", p->ledbits, seq); + return 1; + } + return 0; +} + +static int +blink(Drive *d, 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 = sizeof msg - 4; + msg.led[0] = d->ledbits; + msg.led[1] = d->ledbits>>8; + msg.pm = 0; + msg.hba = d->driveno; + memmove(c->enctx, &msg, sizeof msg); + break; + } + h->emctl |= Tmsg; + return 1; +} + +enum { + Esbdrv0 = 4, /* start pos in bits */ + Esbiota = 3, /* shift in bits */ + Esbact = 1, + Esbloc = 2, + Esberr = 4, +}; + +uint +esbbits(uint s) +{ + uint i, e; /* except after c */ + + e = 0; + for(i = 0; i < 3; i++) + e |= ((s>>3*i & 7) != 0)<ndrive; i++){ + d = c->drive[i]; + s |= ledstate(d, t); /* no port mapping */ + } + if(s == 0) + return 0; + memset(u, 0, sizeof u); + for(i = 0; i < c->ndrive; i++){ + d = c->drive[i]; + s = Esbdrv0 + Esbiota*i; + v = esbbits(d->ledbits) * (1ull << s%32); + u[s/32 + 0] |= v; + u[s/32 + 1] |= v>>32; + } + for(i = 0; i < c->encsz; i++) + c->enctx[i] = u[i]; + return 1; +} + +static long +ahciledr(SDunit *u, Chan *ch, void *a, long n, vlong off) +{ + Ctlr *c; + Drive *d; + + c = u->dev->ctlr; + d = c->drive[u->subno]; + return ledr(d, ch, a, n, off); +} + +static long +ahciledw(SDunit *u, Chan *ch, void *a, long n, vlong off) +{ + Ctlr *c; + Drive *d; + + c = u->dev->ctlr; + d = c->drive[u->subno]; + return ledw(d, ch, a, n, off); +} + +static void +ledkproc(void*) +{ + uchar map[NCtlr]; + uint i, j, t0, t1; + Ctlr *c; + Drive *d; + + j = 0; + memset(map, 0, sizeof map); + for(i = 0; i < niactlr; i++) + if(iactlr[i].enctype != 0){ + ahciencreset(iactlr + i); + map[i] = 1; + j++; + } + if(j == 0) + pexit("no work", 1); + for(i = 0; i < niadrive; i++){ + iadrive[i]->nled = 3; /* hardcoded */ + if(iadrive[i]->ctlr->enctype == Eesb) + iadrive[i]->nled = 3; + iadrive[i]->ledbits = -1; + } + for(i = 0; ; i++){ + t0 = Ticks; + for(j = 0; j < niadrive; ){ + c = iadrive[j]->ctlr; + if(map[j] == 0) + j += c->enctype; + else if(c->enctype == Eesb){ + blinkesb(c, i); + j += c->ndrive; + }else{ + d = iadrive[j++]; + blink(d, i); + } + } + t1 = Ticks; + esleep(Ledsleep - TK2MS(t1 - t0)); + } +} + +static int +iaverify(SDunit *u) +{ + Ctlr *c; + Drive *d; + + c = u->dev->ctlr; + d = c->drive[u->subno]; + ilock(c); + ilock(d); + 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 */ + return 1; +} + +static int +iaenable(SDev *s) +{ + char name[32]; + Ctlr *c; + static int once; + + c = s->ctlr; + ilock(c); + if(!c->enabled){ + if(once == 0) + kproc("iasata", satakproc, 0); + if(c->ndrive == 0) + panic("iaenable: zero s->ctlr->ndrive"); + pcisetbme(c->pci); + snprint(name, sizeof name, "%s (%s)", s->name, s->ifc->name); + intrenable(c->pci->intl, iainterrupt, c, c->pci->tbdf, name); + /* supposed to squelch leftover interrupts here. */ + ahcienable(c->hba); + c->enabled = 1; + if(++once == niactlr) + kproc("ialed", ledkproc, 0); + } + iunlock(c); + return 1; +} + +static int +iadisable(SDev *s) +{ + char name[32]; + Ctlr *c; + + c = s->ctlr; + ilock(c); + ahcidisable(c->hba); + snprint(name, sizeof name, "%s (%s)", s->name, s->ifc->name); + print("missing the intrdisable because intrdisable is wierd\n"); +// intrdisable(c->pci->intl, iainterrupt, c, c->pci->tbdf, name); + c->enabled = 0; + iunlock(c); + return 1; +} + +static int +iaonline(SDunit *u) +{ + int r; + Ctlr *c; + Drive *d; + Aportm *m; + + c = u->dev->ctlr; + d = c->drive[u->subno]; + m = &d->portm; + r = 0; + + if(m->feat & Datapi && m->drivechange){ + r = scsionlinex(u, m) == SDok; + if(r > 0) + m->drivechange = 0; + return r; + } + + ilock(d); + if(m->drivechange){ + r = 2; + m->drivechange = 0; + /* devsd resets this after online is called; why? */ + u->sectors = m->sectors; + u->secsize = m->secsize; + }else if(d->state == Dready) + r = 1; + iunlock(d); + return r; +} + +static Alist* +ahcibuildpkt(Aportm *m, SDreq *r, void *data, int n) +{ + uint flags; + uchar *c; + Actab *t; + Alist *l; + + l = m->list; + t = m->ctab; + c = t->cfis; + atapirwfis(m, c, r->cmd, r->clen, n); + flags = 1<<16 | Lpref | Latapi; + if(r->write != 0 && data) + flags |= Lwrite; + mkalist(m, flags, data, n); + return l; +} + +static Alist* +ahcibuildfis(Aportm *m, SDreq *r, void *data, uint n) +{ + uchar *c; + uint flags, dir; + Alist *l; + + l = m->list; + c = m->ctab->cfis; + if((r->ataproto & Pprotom) != Ppkt){ + memmove(c, r->cmd, r->clen); + flags = Lpref; + if(r->ataproto&Pout && n > 0) + flags |= Lwrite; + dir = r->ataproto&Pdatam; + if(dir == Pnd && 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 +isready(Drive *d) +{ + u32int s; + ulong δ; + + if(d->state & (Dreset | Dportreset /*| Dnew*/)) + return 1; + δ = TK2MS(Ticks - d->lastseen); + if(d->state == Dnull || δ > 10*1000){ + dprint("%s: last seen too long ago: %ld\n", dnam(d), δ); + return -1; + } + ilock(d); + s = d->port->sstatus; + iunlock(d); + if((s & Imask) == 0 && δ > 1500){ + dprint("%s: phy off %ldms\n", dnam(d), δ); + return -1; + } + if(d->state & (Dready | Dnew) && (s & Smask) == Sphylink) + return 0; + return 1; +} + +static int +waitready(Drive *d, int tk) +{ + int r; + + for(;;){ + r = isready(d); + if(r <= 0) + return r; + if(tk - Ticks - 10 < 1ul<<31) + return -1; + esleep(10); + } +} + +static int +io(Drive *d, uint proto, int totk, int interrupt) +{ + uint task, flag, rv; + Aport *p; + Asleep as; + + switch(waitready(d, totk)){ + case -1: + return SDeio; + case 1: + return SDretry; + } + + ilock(d); + d->portm.flag = 0; + iunlock(d); + p = d->port; + p->ci = 1; + + as.p = p; + as.i = 1; + d->totick = 0; + if(totk > 0) + d->totick = totk | 1; /* fix fencepost */ + + while(waserror()) + if(interrupt){ + d->port->ci = 0; + if(ahcicomreset(&d->portc) == -1) + setstate(d, Dreset); + return SDtimeout; + } + sleep(&d->portm, ahciclear, &as); + poperror(); + + ilock(d); + flag = d->portm.flag; + task = p->task; + iunlock(d); + + 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){ + p->ci = 0; + ahcirecover(&d->portc); + task = p->task; + flag &= ~Fdone; /* either an error or do-over */ + } + if(flag == 0){ + print("%s: retry\n", dnam(d)); + return SDretry; + } + if(flag & (Fahdrs | Ferror)){ + if((task & Eidnf) == 0) + print("%s: i/o error %ux\n", dnam(d), task); + return SDcheck; + } + return rv; +} + +static int +iariopkt(SDreq *r, Drive *d) +{ + int n, count, t, max, δ; + uchar *cmd; + + 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; + δ = r->timeout - Ticks; + + for(t = r->timeout; setreqto(r, t) != -1;){ + n = count; + if(n > max) + n = max; + qlock(&d->portm); + ahcibuildpkt(&d->portm, r, r->data, n); + r->status = io(d, Ppkt, r->timeout, 0); + qunlock(&d->portm); + switch(r->status){ + case SDeio: + return r->status = SDcheck; + case SDretry: + continue; + } +// aprint("%s: OK %.2ux :: %d :: %.4lux\n", dnam(d), r->cmd[0], r->status, d->port->task); + r->rlen = d->portm.list->len; + return SDok; + } + print("%s: atapi timeout %dms\n", dnam(d), TK2MS(δ)); + return r->status = SDcheck; +} + +static long +ahcibio(SDunit *u, int lun, int write, void *a, long count0, uvlong lba) +{ + Ctlr *c; + Drive *d; + + c = u->dev->ctlr; + d = c->drive[u->subno]; + if(d->portm.feat & Datapi) + return scsibiox(u, &d->portm, lun, write, a, count0, lba); + return atabio(u, &d->portm, lun, write, a, count0, lba); +} + +static int +iario(SDreq *r) +{ + Ctlr *c; + Drive *d; + SDunit *u; + + u = r->unit; + c = u->dev->ctlr; + d = c->drive[u->subno]; + if((d->state & (Dnew | Dready)) == 0) + return sdsetsense(r, SDcheck, 3, 0x04, 0x24); + if(r->timeout == 0) + r->timeout = totk(Ms2tk(600*1000)); + if(d->portm.feat & Datapi) + return iariopkt(r, d); + return atariosata(u, &d->portm, r); +} + +static uchar bogusrfis[16] = { +[Ftype] 0x34, +[Fioport] 0x40, +[Fstatus] 0x50, +[Fdev] 0xa0, +}; + +static void +sdr0(Drive *d) +{ + uchar *c; + + c = d->portm.fis.r; + memmove(c, bogusrfis, sizeof bogusrfis); + coherence(); +} + +static int +sdr(SDreq *r, Drive *d, int st) +{ + uchar *c; + uint t; + + if((r->ataproto & Pprotom) == Ppkt){ + t = d->port->task; + if(t & ASerr) + st = t >> 8 + 4 & 0xf; + } + c = d->portm.fis.r; + memmove(r->cmd, c, 16); + r->status = st; + if(st == SDcheck) + st = SDok; + return st; +} + +static int +fisreqchk(Sfis *f, SDreq *r) +{ + if((r->ataproto & Pprotom) == Ppkt) + return SDnostatus; + /* + * handle oob requests; + * restrict & sanitize commands + */ + if(r->clen != 16) + error(Eio); + if(r->cmd[0] == 0xf0){ + sigtofis(f, r->cmd); + r->status = SDok; + return SDok; + } + r->cmd[0] = 0x27; + r->cmd[1] = 0x80; + r->cmd[7] |= 0xa0; + return SDnostatus; +} + +static int +iaataio(SDreq *r) +{ + Ctlr *c; + Drive *d; + SDunit *u; + + u = r->unit; + c = u->dev->ctlr; + d = c->drive[u->subno]; + + if(r->timeout == 0) + r->timeout = totk(Ms2tk(600*1000)); + if((r->status = fisreqchk(&d->portm, r)) != SDnostatus) + return r->status; + r->rlen = 0; + sdr0(d); + + qlock(&d->portm); + ahcibuildfis(&d->portm, r, r->data, r->dlen); + r->status = io(d, r->ataproto & Pprotom, -1, 1); + qunlock(&d->portm); + if(r->status != SDok) + return r->status; + r->rlen = r->dlen; + if((r->ataproto & Pprotom) == Ppkt) + r->rlen = d->portm.list->len; + return sdr(r, d, r->status); +} + +/* configure drives 0-5 as ahci sata (c.f. errata) */ +static int +iaahcimode(Pcidev *p) +{ + 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) +{ + pcicfgw16(c->pci, 0x40, pcicfgr16(c->pci, 0x40) & ~Cmddec); + pcicfgw16(c->pci, 0x42, pcicfgr16(c->pci, 0x42) & ~Cmddec); + + c->lmmio[Ghc] |= Ahcien; + c->lmmio[Pi] = (1 << 6) - 1; /* 5 ports (supposedly ro pi reg) */ + + /* enable ahci mode; from ich9 datasheet */ + pcicfgw16(c->pci, 0x90, 1<<6 | 1<<5); +} + +static void +sbsetupahci(Pcidev *p) +{ + print("sbsetupahci: tweaking %.4ux ccru %.2ux ccrp %.2ux\n", + p->did, p->ccru, p->ccrp); + pcicfgw8(p, 0x40, pcicfgr8(p, 0x40) | 1); + pcicfgw8(p, PciCCRu, 6); + pcicfgw8(p, PciCCRp, 1); + p->ccru = 6; + p->ccrp = 1; +} + +static int +esbenc(Ctlr *c) +{ + c->encsz = 1; + c->enctx = (u32int*)(c->mmio + 0xa0); + c->enctype = Eesb; + c->enctx[0] = 0; + return 0; +} + +static int +ahciencinit(Ctlr *c) +{ + uint type, sz, o; + u32int *bar; + Ahba *h; + + h = c->hba; + if(c->type == Tesb) + return esbenc(c); + if((h->cap & Hems) == 0) + return -1; + type = h->emctl & Emtype; + switch(type){ + case Esgpio: + case Eses2: + case Esafte: + return -1; + case Elmt: + break; + default: + return -1; + } + + sz = h->emloc & 0xffff; + o = h->emloc>>16; + if(sz == 0 || o == 0) + return -1; + bar = c->lmmio; + 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; + } + 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: + return -1; + 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 0x10de: + case 0x1039: + case 0x1b4b: + case 0x11ab: + break; + case 0x197b: + case 0x10b9: + type = Tjmicron; + break; + } + if(p->ccrb == 1 && p->ccru == 6 && p->ccrp == 1) + return type; + return -1; +} + +static SDev* +iapnp(void) +{ + int i, n, nunit, type; + uintptr io; + Ctlr *c; + Drive *d; + Pcidev *p; + SDev *s; + static int done; + + if(done) + return nil; + done = 1; + memset(olds, 0xff, sizeof olds); + p = nil; +loop: + while((p = pcimatch(p, 0, 0)) != nil){ + if((type = didtype(p)) == -1) + continue; + if(p->mem[Abar].bar == 0) + continue; + if(niactlr == NCtlr){ + print("iapnp: %s: too many controllers\n", cttab[type].name); + break; + } + c = iactlr + niactlr; + s = sdevs + niactlr; + memset(c, 0, sizeof *c); + memset(s, 0, sizeof *s); + io = p->mem[Abar].bar & ~0xf; + c->mmio = vmap(io, p->mem[Abar].size); + if(c->mmio == 0){ + print("%s: address %#p in use did %.4ux\n", + tnam(c), io, p->did); + continue; + } + c->lmmio = (u32int*)c->mmio; + c->pci = p; + c->type = cttab + type; + + s->ifc = &sdahciifc; + s->idno = 'E'; + s->ctlr = c; + c->sdev = s; + + if(intel(c) && p->did != 0x2681) + iasetupahci(c); +// ahcihbareset((Ahba*)c->mmio); + nunit = ahciconf(c); + c->pi = c->hba->pi; + if(0 && p->vid == 0x1002 && p->did == 0x4391){ + c->pi = 0x3f; /* noah's opteron */ + nunit = 6; + } + if(intel(c) && iaahcimode(p) == -1 || nunit < 1){ + vunmap(c->mmio, p->mem[Abar].size); + 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->portm.tler = 5000; + d->portm.sectors = 0; + d->portm.serial[0] = ' '; + d->led = Ibpinormal; + d->ctlr = c; + if((c->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++; + 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* +capfmt(char *p, char *e, Htab *t, int n, u32int cap) +{ + uint i; + + *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], *e, *op; + Aport *o; + Ctlr *c; + Drive *d; + + if((c = u->dev->ctlr) == nil) + return 0; + d = c->drive[u->subno]; + o = d->port; + + e = p+l; + op = p; + if(d->state == Dready) + p = sfisxrdctl(&d->portm, p, e); + else + 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 %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); + 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", u->sectors, u->secsize); + return p - op; +} + +static void +forcemode(Drive *d, char *mode) +{ + int i; + + for(i = 0; i < nelem(modes); i++) + if(strcmp(mode, modes[i]) == 0) + break; + if(i == nelem(modes)) + i = 0; + ilock(d); + d->mode = i; + iunlock(d); +} + +static void +forcestate(Drive *d, char *state) +{ + int i; + + for(i = 1; i < nelem(diskstates); i++) + if(strcmp(state, diskstates[i]) == 0) + break; + if(i == nelem(diskstates)) + error(Ebadctl); + setstate(d, 1 << i-1); +} + +static int +iawctl(SDunit *u, Cmdbuf *cmd) +{ + char **f; + Ctlr *c; + Drive *d; + + c = u->dev->ctlr; + d = c->drive[u->subno]; + f = cmd->f; + + if(strcmp(f[0], "mode") == 0) + forcemode(d, f[1]? f[1]: "satai"); + else if(strcmp(f[0], "state") == 0) + forcestate(d, f[1]? f[1]: "null"); + else + cmderror(cmd, Ebadctl); + return 0; +} + +static char * +portr(char *p, char *e, uint x) +{ + int i, a; + + p[0] = 0; + a = -1; + for(i = 0; i < 32; i++){ + if((x & (1< 0) + p = seprint(p, e, ", "); + p = seprint(p, e, "%d", a = i); + } + } + if(a != -1 && i - 1 != a) + p = seprint(p, e, "-%d", i - 1); + return p; +} + +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 *s, char *p, char *e) +{ + char pr[25]; + u32int cap; + Ahba *h; + Ctlr *c; + + 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 %d ncs %d np %d ghc %ux isr %ux pi %ux %s ver %ux\n", + (cap>>20) & 0xf, (cap>>8) & 0x1f, 1 + (cap & 0x1f), + h->ghc, h->isr, h->pi, pr, h->ver); +} + +static int +iawtopctl(SDev *, Cmdbuf *cmd) +{ + int *v; + char **f; + + f = cmd->f; + v = 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); + + switch(cmd->nf){ + default: + cmderror(cmd, Ebadarg); + case 1: + *v ^= 1; + return 0; + case 2: + *v = strcmp(f[1], "on") == 0; + return 0; + } +} + +SDifc sdahciifc = { + "ahci", + + iapnp, + nil, /* legacy */ + iaenable, + iadisable, + + iaverify, + iaonline, + iario, + iarctl, + iawctl, + + ahcibio, + nil, /* probe */ + nil, /* clear */ + iartopctl, + iawtopctl, + iaataio, +}; --- /sys/src/nix/k10/ahci.h Thu Jan 1 00:00:00 1970 +++ /sys/src/nix/k10/ahci.h Mon Apr 23 19:34:39 2012 @@ -0,0 +1,328 @@ +/* + * advanced host controller interface (sata) + * © 2007-9 coraid, inc + */ + +/* pci configuration */ +enum { + Abar = 5, +}; + +/* + * ahci memory configuration + * + * 0000-0023 generic host control + * 0024-009f reserved + * 00a0-00ff vendor specific. + * 0100-017f port 0 + * ... + * 1080-1100 port 31 + */ + +/* cap bits: supported features */ +enum { + 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 */ + 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. */ + Hxs = 1<<5, /* external sata */ + Hnp = 1<<0, /* n ports */ +}; + +/* ghc bits */ +enum { + Hae = 1<<31, /* enable ahci */ + Hie = 1<<1, /* " interrupts */ + Hhr = 1<<0, /* hba reset */ +}; + +/* cap2 bits */ +enum { + Apts = 1<<2, /* automatic partial to slumber */ + Nvmp = 1<<1, /* nvmhci present; nvram */ + Boh = 1<<0, /* bios/os handoff supported */ +}; + +/* emctl bits */ +enum { + Pm = 1<<27, /* port multiplier support */ + Alhd = 1<<26, /* activity led hardware driven */ + Xonly = 1<<25, /* rx messages not supported */ + Smb = 1<<24, /* single msg buffer; rx limited */ + Esgpio = 1<<19, /* sgpio messages supported */ + Eses2 = 1<<18, /* ses-2 supported */ + Esafte = 1<<17, /* saf-te supported */ + Elmt = 1<<16, /* led msg types support */ + Emrst = 1<<9, /* reset all em logic */ + Tmsg = 1<<8, /* transmit message */ + Mr = 1<<0, /* message rx'd */ + Emtype = Esgpio | Eses2 | Esafte | Elmt, +}; + +typedef struct { + 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 { + Acpds = 1<<31, /* cold port detect status */ + Atfes = 1<<30, /* task file error status */ + Ahbfs = 1<<29, /* hba fatal */ + Ahbds = 1<<28, /* hba error (parity error) */ + Aifs = 1<<27, /* interface fatal §6.1.2 */ + Ainfs = 1<<26, /* interface error (recovered) */ + Aofs = 1<<24, /* too many bytes from disk */ + Aipms = 1<<23, /* incorrect prt mul status */ + Aprcs = 1<<22, /* PhyRdy change status Pxserr.diag.n */ + Adpms = 1<<7, /* mechanical presence status */ + Apcs = 1<<6, /* port connect diag.x */ + Adps = 1<<5, /* descriptor processed */ + Aufs = 1<<4, /* unknown fis diag.f */ + Asdbs = 1<<3, /* set device bits fis received w/ i bit set */ + Adss = 1<<2, /* dma setup */ + Apio = 1<<1, /* pio setup fis */ + Adhrs = 1<<0, /* device to host register fis */ + + IEM = Acpds|Atfes|Ahbds|Ahbfs|Ahbds|Aifs|Ainfs|Aprcs|Apcs|Adps| + Aufs|Asdbs|Adss|Adhrs, + Ifatal = Atfes|Ahbfs|Ahbds|Aifs, +}; + +/* serror bits */ +enum { + SerrX = 1<<26, /* exchanged */ + SerrF = 1<<25, /* unknown fis */ + SerrT = 1<<24, /* transition error */ + SerrS = 1<<23, /* link sequence */ + SerrH = 1<<22, /* handshake */ + SerrC = 1<<21, /* crc */ + SerrD = 1<<20, /* not used by ahci */ + SerrB = 1<<19, /* 10-tp-8 decode */ + SerrW = 1<<18, /* comm wake */ + SerrI = 1<<17, /* phy internal */ + SerrN = 1<<16, /* phyrdy change */ + + ErrE = 1<<11, /* internal */ + ErrP = 1<<10, /* ata protocol violation */ + ErrC = 1<<9, /* communication */ + ErrT = 1<<8, /* transient */ + ErrM = 1<<1, /* recoverd comm */ + ErrI = 1<<0, /* recovered data integrety */ + + ErrAll = ErrE|ErrP|ErrC|ErrT|ErrM|ErrI, + SerrAll = SerrX|SerrF|SerrT|SerrS|SerrH|SerrC|SerrD|SerrB|SerrW| + SerrI|SerrN|ErrAll, + SerrBad = 0x7f<<19, +}; + +/* cmd register bits */ +enum { + Aicc = 1<<28, /* interface communcations control. 4 bits */ + Aasp = 1<<27, /* aggressive slumber & partial sleep */ + 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. */ + Ahpcp = 1<<18, /* hot plug capable */ + Apma = 1<<17, /* pm attached */ + Acps = 1<<16, /* cold presence state */ + Acr = 1<<15, /* cmdlist running */ + Afr = 1<<14, /* fis running */ + Ampss = 1<<13, /* mechanical presence switch state */ + Accs = 1<<8, /* current command slot 12:08 */ + Afre = 1<<4, /* fis enable receive */ + Aclo = 1<<3, /* command list override */ + Apod = 1<<2, /* power on dev (requires cold-pres. detect) */ + Asud = 1<<1, /* spin-up device; requires ss capability */ + Ast = 1<<0, /* start */ + + Arun = Ast|Acr|Afre|Afr, + Apwr = Apod|Asud, +}; + +/* ctl 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 { + 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; + +/* in host's memory; not memory mapped */ +typedef struct { + uchar *base; + uchar *d; + uchar *p; + uchar *r; + uchar *u; + u32int *devicebits; +} Afis; + +enum { + Lprdtl = 1<<16, /* physical region descriptor table len */ + Lpmp = 1<<12, /* port multiplier port */ + Lclear = 1<<10, /* clear busy on R_OK */ + Lbist = 1<<9, + Lreset = 1<<8, + Lpref = 1<<7, /* prefetchable */ + Lwrite = 1<<6, + Latapi = 1<<5, + Lcfl = 1<<0, /* command fis length in double words */ +}; + +/* in hosts memory; memory mapped */ +typedef struct { + u32int flags; + u32int len; + u32int ctab; + u32int ctabhi; + uchar reserved[16]; +} Alist; + +typedef struct { + u32int dba; + u32int dbahi; + u32int pad; + u32int count; +} Aprdt; + +typedef struct { + uchar cfis[0x40]; + uchar atapi[0x10]; + uchar pad[0x30]; + Aprdt prdt; +} Actab; + +/* enclosure message header */ +enum { + Mled = 0, + Msafte = 1, + Mses2 = 2, + Msgpio = 3, +}; + +typedef struct { + uchar dummy; + uchar msize; + uchar dsize; + uchar type; + uchar hba; /* bits 0:4 are the port */ + uchar pm; + uchar led[2]; +} Aledmsg; + +enum { + Aled = 1<<0, + Locled = 1<<3, + Errled = 1<<6, + + Ledoff = 0, + Ledon = 1, +}; + +typedef struct { + uint encsz; + u32int *enctx; + u32int *encrx; +} Aenc; + +enum { + Ferror = 1, + Fdone = 2, +}; + +typedef struct { + QLock; + Rendez; + uchar flag; + Sfisx; + Afis fis; + Alist *list; + Actab *ctab; +} Aportm; + +typedef struct { + Aport *p; + Aportm *m; +} Aportc; --- /sys/src/nix/k10/k8cpu Sun Apr 15 19:46:43 2012 +++ /sys/src/nix/k10/k8cpu Mon Apr 23 19:46:02 2012 @@ -9,6 +9,7 @@ srv dup rtc + sd ssl cap kprof @@ -51,6 +52,9 @@ # ht +sd +dev + sdahci sdscsifis sdatafis led + misc +dev # cache mp apic ioapic msi pci sipi @@ -196,3 +200,4 @@ libc libip libsec + libfis