diff -Nru /sys/src/libfis/fis.c /sys/src/libfis/fis.c --- /sys/src/libfis/fis.c Thu Jan 1 00:00:00 1970 +++ /sys/src/libfis/fis.c Fri Jan 17 00:00:00 2014 @@ -0,0 +1,555 @@ +/* + * sata fises and sas frames + * copyright © 2009-2010 erik quanstrom + */ +#include +#include +#include + +static char *flagname[9] = { + "lba", + "llba", + "smart", + "power", + "nop", + "atapi", + "atapi16", + "ata8", + "sct", +}; + +/* + * ata8 standard (llba) cmd layout + * + * feature 16 bits + * count 16 bits + * lba 48 bits + * device 8 bits + * command 8 bits + * + * response: + * + * status 8 bits + * error 8 bits + * reason 8 bits + * count 8 bits + * sstatus 8 bits + * sactive 8 bits +*/ + +/* + * sata fis layout for fistype 0x27: host-to-device: + * + * 0 fistype + * 1 fis flags + * 2 ata command + * 3 features + * 4 sector lba low 7:0 + * 5 cyl low lba mid 15:8 + * 6 cyl hi lba hi 23:16 + * 7 device / head + * 8 sec exp lba 31:24 + * 9 cy low e lba 39:32 + * 10 cy hi e lba 48:40 + * 11 features (exp) + * 12 sector count + * 13 sector count (exp) + * 14 r + * 15 control + */ + +void +setfissig(Sfis *x, uint sig) +{ + x->sig = sig; +} + +void +skelfis(uchar *c) +{ + memset(c, 0, Fissize); + c[Ftype] = H2dev; + c[Fflags] = Fiscmd; + c[Fdev] = Ataobs; +} + +int +nopfis(Sfis*, uchar *c, int srst) +{ + skelfis(c); + if(srst){ + c[Fflags] &= ~Fiscmd; + c[Fcontrol] = 1<<2; + return Preset|P28; + } + return Pnd|P28; +} + +int +txmodefis(Sfis *f, uchar *c, uchar d) +{ + int m; + + /* hack */ + if((f->sig >> 16) == 0xeb14) + return -1; + m = 0x40; + if(d == 0xff){ + d = 0; + m = 0; + } + skelfis(c); + c[Fcmd] = 0xef; + c[Ffeat] = 3; /* set transfer mode */ + c[Fsc] = m | d; /* sector count */ + return Pnd|P28; +} + +int +featfis(Sfis*, uchar *c, uchar f) +{ + skelfis(c); + c[Fcmd] = 0xef; + c[Ffeat] = f; + return Pnd|P28; +} + +int +identifyfis(Sfis *f, uchar *c) +{ + static uchar tab[] = { 0xec, 0xa1, }; + + skelfis(c); + c[Fcmd] = tab[f->sig>>16 == 0xeb14]; + return Pin|Ppio|P28|P512; +} + +int +flushcachefis(Sfis *f, uchar *c) +{ + static uchar tab[2] = {0xe7, 0xea}; + static uchar ptab[2] = {Pnd|P28, Pnd|P48}; + int llba; + + llba = (f->feat & Dllba) != 0; + skelfis(c); + c[Fcmd] = tab[llba]; + return ptab[llba]; +} + +static ushort +gbit16(void *a) +{ + ushort j; + uchar *i; + + i = a; + j = i[1] << 8; + j |= i[0]; + return j; +} + +static uint +gbit32(void *a) +{ + uint j; + uchar *i; + + i = a; + j = i[3] << 24; + j |= i[2] << 16; + j |= i[1] << 8; + j |= i[0]; + return j; +} + +static uvlong +gbit64(void *a) +{ + uchar *i; + + i = a; + return (uvlong)gbit32(i+4) << 32 | gbit32(a); +} + +ushort +id16(ushort *id, int i) +{ + return gbit16(id+i); +} + +uint +id32(ushort *id, int i) +{ + return gbit32(id+i); +} + +uvlong +id64(ushort *id, int i) +{ + return gbit64(id+i); +} + +/* acs-2 §7.18.7.4 */ +static ushort puistab[] = { + 0x37c8, Pspinup, + 0x738c, Pspinup | Pidready, + 0x8c73, 0, + 0xc837, Pidready, +}; + +int +idpuis(ushort *id) +{ + ushort u, i; + + u = gbit16(id + 2); + for(i = 0; i < nelem(puistab); i += 2) + if(u == puistab[i]) + return puistab[i + 1]; + return Pidready; /* annoying cdroms */ +} + +static ushort +onesc(ushort *id) +{ + ushort u; + + u = gbit16(id); + if(u == 0xffff) + u = 0; + return u; +} + +enum{ + Idmasp = 1<<8, + Ilbasp = 1<<9, + Illba = 1<<10, + + Usepsec = 1, +}; + +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); + if(Usepsec) + return s>>f->physshift; + 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; + if(Usepsec) + return f->lsectsz << f->physshift; + 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, + }; + + if(Usepsec){ + 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 >> 40; + 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; + + if(Usepsec){ + *n >>= f->physshift; + lba >>= f->physshift; + } + + return lba; +} + +void +sigtofis(Sfis *f, uchar *c) +{ + uint u; + + u = f->sig; + memset(c, 0, Fissize); + c[Ftype] = 0x34; + c[Fflags] = 0x00; + c[Fcmd] = 0x50; + c[Ffeat] = 0x01; + c[Flba0] = u >> 8; + c[Flba8] = u >> 16; + c[Flba16] = u >> 24; + c[Fdev] = Ataobs; + c[Fsc] = u; +} + +uint +fistosig(uchar *u) +{ + return u[Fsc] | u[Flba0]<<8 | u[Flba8]<<16 | u[Flba16]<<24; +} + + +/* sas smp */ +void +smpskelframe(Cfis *f, uchar *c, int m) +{ + memset(c, 0, Fissize); + c[Ftype] = 0x40; + c[Fflags] = m; + if(f->phyid) + c[Flba32] = f->phyid; +} + +uint +sashash(uvlong u) +{ + uint poly, msb, l, r; + uvlong m; + + r = 0; + poly = 0x01db2777; + msb = 0x01000000; + for(m = 1ull<<63; m > 0; m >>= 1){ + l = 0; + if(m & u) + l = msb; + r <<= 1; + r ^= l; + if(r & msb) + r ^= poly; + } + return r & 0xffffff; +} + +uchar* +sasbhash(uchar *t, uchar *s) +{ + uint poly, msb, l, r, i, j; + + r = 0; + poly = 0x01db2777; + msb = 0x01000000; + for(i = 0; i < 8; i++) + for(j = 0x80; j != 0; j >>= 1){ + l = 0; + if(s[i] & j) + l = msb; + r <<= 1; + r ^= l; + if(r & msb) + r ^= poly; + } + t[0] = r>>16; + t[1] = r>>8; + t[2] = r; + return t; +} diff -Nru /sys/src/libfis/mkfile /sys/src/libfis/mkfile --- /sys/src/libfis/mkfile Thu Jan 1 00:00:00 1970 +++ /sys/src/libfis/mkfile Fri Jan 17 00:00:00 2014 @@ -0,0 +1,15 @@ + +#include +#include +.PB +enum { + Fissize = 0x20, /* nominal (fits all) fis size */ +.PB + Dlba = 1<<0, + Dllba = 1<<1, + Dsmart = 1<<2, + Dpower = 1<<3, + Dnop = 1<<4, + Datapi = 1<<5, + Datapi16 = 1<<6, + Data8 = 1<<7, + Dsct = 1<<8, +}; +.PB +enum { + Pspinup = 1<<0, + Pidready = 1<<1, +}; + +struct Sfis { + ushort feat; /* features supportd */ + uchar udma; /* modes supported */ + uchar speeds; /* sata: allowed speeds */ + uint sectsz; /* sector size */ + uint phystol; /* log2(logical/physical) */ + uint sig; /* signature */ + uint c; /* cylinder */ + uint h; /* head */ + uint s; /* sector */ +}; +.PB +void setfissig(Sfis *sfis, uint sig) +.PB +int txmodefis(Sfis *sfis, uchar *fis, uchar mode) +.PB +int featfis(Sfis *sfis, uchar *fis, uchar feat) +.PB +int flushcachefis(Sfis *sfis, uchar *fis) +.PB +int identifyfis(Sfis *sfis, uchar *fis) +.PB +int nopfis(Sfis *sfis, uchar *fis, int srst) +.PB +int fisrw(Sfis *f, uchar *fis, int *count) +.PB +int rwfis(Sfis *sfis, uchar *fis, int rw, int nsect, uvlong lba) +.PB +int atapirwfis(Sfis *sfis, uchar *fis, uchar *cdb, int cdblen, int ndata) +.PB +void skelfis(uchar *fis) +.PB +void sigtofis(Sfis *sfis, uchar *d2hfis) +.PB +uvlong fisrw(Sfis *sfis, uchar *fis, int *nsect) +.PB +ushort id16(ushort *id, int idx) +.PB +uint id32(ushort *id, int idx) +.PB +uvlong id64(ushort *id, int idx) +.PB +void idmove(char *buf, ushort *ididx, int n) +.PB +vlong idfeat(Sfis *sfis, ushort *id) +.PB +uvlong idwwn(Sfis*, ushort *id) +.PB +int idss(Sfis *sfis, ushort *id) +.PB +int idpuis(ushort *id) +.PB +char *pflag(char *p, char *e, Sfis *sbis) +.PB +uint fistosig(uchar *fis) +.PB +uint sashash(uvlong u) +.PB +uchar* sasbhash(uchar *t, uchar *s) +.SH DESCRIPTION +The +.B Sfis +type describes the connection between an ATA drive and +a Host Bus Adaptor (HBA). The details of this connection, +such as DMA and LBA support, and the logcal sectors per +physical sector are used to construct the proper SATA FISes. +.PP +Since ATA uses a seperate sector size for addressing +(the logical sector size) and for ECC calculations (the +physical sector size), the library returns values based on +the physical sector size. This allows clients to make +intelligent decisions about IO size without worrying about +this implementation botch. +.PP +.TP 10 +.B feat +selected features supported by the drive. Set by +.BR idfeat . +.TP +.B udma +the UDMA modes supported by the drive. Set by +.BR idfeat . +.TP +.B speeds +bitmaped signaling speeds supported by the device. +SATA drives may support 1.5gbit/s, 3.0gbit/s and +6.0gbit/s signaling. +.TP +.B sig +this field contains the drive's “signature.” +Drives that support the packet command +set have a signature of +.BR 0xeb140000 , +while conventional drives have a signature of +.BR 0x0101 . +The signature is set by +.BR setfissig . +See +.IR sd (3) +for more information. +.TP +.B "c\fP,\fP\ h\fP,\fP\ s" +the number of physical or logical cylinders, heads and sectors claimed +by the drive. CHS addressing is not allowed by SATA drivers. Set by +.BR idfeat . +.PP +Typically, a client starts by filling out +.BR sfis->sig . +Device drivers typically have an ideosyncratic method of obtaining +a signature. Clients of +.IR sd (3) +can use special commands to obtain a signature. Given a signature +the client can use +.B identifyfis +to identify the device and finally +.BR idfeat , +.B idss +and +.B idwwn +to fill out the structure and extract the maximum LBA+1 +and the sector size. +.PP +The +.IB xto fis +functions create FISes to be sent from the host to the device. +These are called H2D or Host to Device Register FISes. All +require a +.B fis +buffer of at least +.B Fislen +bytes. The return value is the ATA protocol of the returned +command. For example, +.B identifyfis +returns +.BR "Pin|Ppio|P28|P512" . +.TP +.BI setfissig( sfis\fP,\fP\ sig ) +sets the device's ATA signature. +.TP +.BI identifyfis( sfis\fP,\fP\ fis ) +builds an ATA +.B IDENTIFY +.B DEVICE +or +.B IDENTIFY +.B PACKET +.B DEVICE +FIS, based on +.IB sfis ->sig\fR. +.TP +.BI id16( id\fP,\fP\ idx "), id32, id64" +return id word, double-world or quad-word at position +.IR idx . +.TP +.BI idss( sfis\fP,\fP\ id ) +return the physical sector size of the disk. +The physical sector size is the quanta protected +by ECC and is never smaller than the logical +sector size, which is used in LBA calcuations. +.TP +.BI idpuis( id ) +checks for PUIS (power up in standby) modes. +Invalid id blocks return -1. Otherwise +.B Pspinup +is set if the drive needs +.B "SET FEATURES" +subcommand 7 to return complete data and +.B Pidready +bit set if the id block is complete. Both or neither bits +may be set. +.TP +.BI idfeat( sfis\fP,\fP\ id ) +parses the 512-byte data block returned by +the identify commands. On success, the last LBA+1 +is returned. Unparseable or unsupportable data +blocks return -1. Flags may be converted to a +descriptive string with +.BR pflag . +.B Idss +and +.B idwwn +return the sector size and World Wide Name from +the same data. +.TP +.BI idmove( buf\fP,\fP\ ididx\fP,\fP\ n ) +converts an ATA string of length +.I n +into a null-terminated string. Trailing +spaces are deleted. +.TP +.BI fistosig( fis ) +converts a D2H FIS into a device signature. +.TP +.BI txmodefis( sfis\fP,\fP\ fis\fP,\fP\ mode ) +builds a +.B "SET TRANSFER MODE" +FIS. +.TP +.BI featfis( sfis\fP,\fP\ fis\fP,\fP\ feat ) +builds a +.B "SET FEATURES" +FIS. +.TP +.BI flushcachefis( sfis\fP,\fP\ fis ) +builds a +.B "FLUSH CACHE" +FIS. +.TP +.BI nopfis( sfis\fP,\fP\ fis\fP,\fP\ srst ) +builds a +.B "NOP" +FIS. If +.I srst +is set, then out-of-band signaling is used to request +a soft reset. It is not expected that +.I srst +will be useful outside of device drivers. +.TP +.BI rwfis( sfis\fP,\fP\ fis\fP,\fP\ rw\fP,\fP\ nsect\fP,\fP\ lba ) +generates a FIS requesting an +.I nsect +i/o at +.IR lba . +If +.I rw +is non-zero, then a write is requested otherwise a +read is requested. The correct command to issue is +determined by +.I sfis . +DMA and 48-bit (LLBA) commands are preferred. +The inverse is +.B fisrw +which returns the +.I lba +and +.I nsect +given a FIS generated by +.BR rwfis. +.TP +.BI atapirwfis( sfis\fP,\fP\ fis\fP,\fP\ cdb\fP,\fP\ cdlen\fP,\fP\ ndata ) +generates an ATAPI read or write FIS. The +.I cdb +contains a +.IR cdblen -byte +SCSI Command Data Block describing an +.IR nbyte -byte +i/o operation. +.TP +.BI skelfis( fis ) +builds a skeleton +Host-to-Device (H2D) Register FIS. +.I Fis +must be a buffer of at least +.B Fislen +bytes. +.PP +The +.BI fis tox +functions create FISes to be sent from the device to the host. +These are called D2H or Device to Host Register FISes +and are used to generate faux responses where actual +communication with a device would be desirable. +All require a +.B fis +buffer of at least +.B Fislen +bytes. +.TP +.BI sigtofis( sfis\fP,\fP\ d2hfis ) +builds a device signature fis from based on a device's +.BR sfis . +.TP +.BI sashash( sasaddr ) +generates a 4-byte hashed SAS address from an 8 byte +WWN. +.TP +.BI sasbhash( sasaddr\fP,\fP\ wwn ) +fills the buffer +.I sasaddr +with the hashed SAS addresse from the 8 byte buffer +.IR wwn . +.TP +.BI sigtofis( sfis\fP,\fP\ d2hfis ) +.SH SOURCE +.B /sys/src/libfis +.SH "SEE ALSO" +.IR atazz (8) , +.IR sd (3) , +.IR sdide (3) , +.IR sdahci (3) , +.IR sdodin (3), +.IR smart (8) +.SH BUGS +Support for CHS-style addressing is somewhat weak. +In particular, +.B rwfis +does not do the usual LBA-to-CHS transations. This +is done by drivers that support CHS addressing. Currently +this is only the IDE driver. +.PP +The +.B Sfis +structure arguably could contain the LBA size, +it is required for the correct construction of FISes.