jehanne/sys/src/kern/amd64/fis.c

549 lines
8.4 KiB
C

/*
* sata fises and sas frames
* copyright © 2009-2010 erik quanstrom
*/
/* Portions of this files are Copyright (C) Charles Forsyth
* See /doc/license/NOTICE.Plan9-9k.txt for details about the licensing.
*/
#include "u.h"
#include "../port/lib.h"
#include "fis.h"
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, uint32_t sig)
{
x->sig = sig;
}
void
skelfis(uint8_t *c)
{
jehanne_memset(c, 0, Fissize);
c[Ftype] = H2dev;
c[Fflags] = Fiscmd;
c[Fdev] = Ataobs;
}
int
nopfis(Sfis*, uint8_t *c, int srst)
{
skelfis(c);
if(srst){
c[Fflags] &= ~Fiscmd;
c[Fcontrol] = 1<<2;
return Preset|P28;
}
return Pnd|P28;
}
int
txmodefis(Sfis *f, uint8_t *c, uint8_t 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*, uint8_t *c, uint8_t f)
{
skelfis(c);
c[Fcmd] = 0xef;
c[Ffeat] = f;
return Pnd|P28;
}
int
identifyfis(Sfis *f, uint8_t *c)
{
static uint8_t tab[] = { 0xec, 0xa1, };
skelfis(c);
c[Fcmd] = tab[f->sig>>16 == 0xeb14];
return Pin|Ppio|P28|P512;
}
int
flushcachefis(Sfis *f, uint8_t *c)
{
static uint8_t tab[2] = {0xe7, 0xea};
static uint8_t ptab[2] = {Pnd|P28, Pnd|P48};
int llba;
llba = (f->feat & Dllba) != 0;
skelfis(c);
c[Fcmd] = tab[llba];
return ptab[llba];
}
static uint16_t
gbit16(void *a)
{
uint16_t j;
uint8_t *i;
i = a;
j = i[1] << 8;
j |= i[0];
return j;
}
static uint32_t
gbit32(void *a)
{
uint32_t j;
uint8_t *i;
i = a;
j = i[3] << 24;
j |= i[2] << 16;
j |= i[1] << 8;
j |= i[0];
return j;
}
static uint64_t
gbit64(void *a)
{
uint8_t *i;
i = a;
return (uint64_t)gbit32(i+4) << 32 | gbit32(a);
}
uint16_t
id16(uint16_t *id, int i)
{
return gbit16(id+i);
}
uint32_t
id32(uint16_t *id, int i)
{
return gbit32(id+i);
}
uint64_t
id64(uint16_t *id, int i)
{
return gbit64(id+i);
}
/* acs-2 §7.18.7.4 */
static uint16_t puistab[] = {
0x37c8, Pspinup,
0x738c, Pspinup | Pidready,
0x8c73, 0,
0xc837, Pidready,
};
int
idpuis(uint16_t *id)
{
uint16_t 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 uint16_t
onesc(uint16_t *id)
{
uint16_t u;
u = gbit16(id);
if(u == 0xffff)
u = 0;
return u;
}
enum{
Idmasp = 1<<8,
Ilbasp = 1<<9,
Illba = 1<<10,
};
int64_t
idfeat(Sfis *f, uint16_t *id)
{
int i, j;
int64_t 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) == 0){
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, uint16_t *id)
{
uint32_t 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<<f->physshift);
}
uint64_t
idwwn(Sfis*, uint16_t *id)
{
uint64_t u;
u = 0;
if(id[108]>>12 == 5){
u |= (uint64_t)gbit16(id + 108) << 48;
u |= (uint64_t)gbit16(id + 109) << 32;
u |= gbit16(id + 110) << 16;
u |= gbit16(id + 111) << 0;
}
return u;
}
void
idmove(char *p, uint16_t *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++;
jehanne_memmove(op, p, n - (e - p));
}
char*
pflag(char *s, char *e, Sfis *f)
{
uint16_t i, u;
u = f->feat;
for(i = 0; i < Dnflag; i++)
if(u & (1 << i))
s = jehanne_seprint(s, e, "%s ", flagname[i]);
return jehanne_seprint(s, e, "\n");
}
int
atapirwfis(Sfis *f, uint8_t *c, uint8_t *cdb, int cdblen, int ndata)
{
int fill, len;
fill = f->feat&Datapi16? 16: 12;
if((len = cdblen) > fill)
len = fill;
jehanne_memmove(c + 0x40, cdb, len);
jehanne_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;
jehanne_memset(c + 8, 0, Fissize - 8);
return P28|Ppkt;
}
int
rwfis(Sfis *f, uint8_t *c, int rw, int nsect, uint64_t lba)
{
uint8_t acmd, llba, udma;
static uint8_t tab[2][2][2] = { 0x20, 0x24, 0x30, 0x34, 0xc8, 0x25, 0xca, 0x35, };
static uint8_t 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;
jehanne_memset(c + 16, 0, Fissize - 16);
return ptab[udma][rw][llba];
}
uint64_t
fisrw(Sfis *f, uint8_t *c, int *n)
{
uint64_t lba;
lba = c[Flba0];
lba |= c[Flba8] << 8;
lba |= c[Flba16] << 16;
lba |= c[Flba24] << 24;
lba |= (uint64_t)(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, uint8_t *c)
{
uint32_t u;
u = f->sig;
jehanne_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;
}
uint32_t
fistosig(uint8_t *u)
{
return u[Fsc] | u[Flba0]<<8 | u[Flba8]<<16 | u[Flba16]<<24;
}
/* sas smp */
void
smpskelframe(Cfis *f, uint8_t *c, int m)
{
jehanne_memset(c, 0, Fissize);
c[Ftype] = 0x40;
c[Fflags] = m;
if(f->phyid)
c[Flba32] = f->phyid;
}
uint32_t
sashash(uint64_t u)
{
uint32_t poly, msb, l, r;
uint64_t 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;
}
uint8_t*
sasbhash(uint8_t *t, uint8_t *s)
{
uint32_t 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;
}