diff options
| author | Charles.Forsyth <devnull@localhost> | 2008-06-11 14:21:44 +0000 |
|---|---|---|
| committer | Charles.Forsyth <devnull@localhost> | 2008-06-11 14:21:44 +0000 |
| commit | 8a8c2d742b51525f66c2210e3c8a251de10022ff (patch) | |
| tree | 8282ce595e5fbe2e487dc20f54891d9e9e7cbf37 /os/boot/pc/sdaoe.c | |
| parent | 31a18a6996a6b5927e39cc553696c167e6c88e3d (diff) | |
20080611-1520
Diffstat (limited to 'os/boot/pc/sdaoe.c')
| -rw-r--r-- | os/boot/pc/sdaoe.c | 734 |
1 files changed, 734 insertions, 0 deletions
diff --git a/os/boot/pc/sdaoe.c b/os/boot/pc/sdaoe.c new file mode 100644 index 00000000..04375b30 --- /dev/null +++ b/os/boot/pc/sdaoe.c @@ -0,0 +1,734 @@ +/* + * aoe sd bootstrap driver, copyright © 2007 coraid + */ + +#include "u.h" +#include "mem.h" +#include "lib.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "sd.h" +#include "aoe.h" + +#define uprint(...) snprint(up->genbuf, sizeof up->genbuf, __VA_ARGS__); + +enum { + Nctlr = 4, +}; + +enum { + /* sync with ahci.h */ + Dllba = 1<<0, + Dsmart = 1<<1, + Dpower = 1<<2, + Dnop = 1<<3, + Datapi = 1<<4, + Datapi16= 1<<5, +}; + +enum { + Tfree = -1, + Tmgmt, +}; + +typedef struct Ctlr Ctlr; +struct Ctlr{ + Ctlr *next; + SDunit *unit; + + int ctlrno; + int major; + int minor; + uchar ea[Eaddrlen]; + ushort lasttag; + + ulong vers; + uchar mediachange; + uchar flag; + uchar smart; + uchar smartrs; + uchar feat; + + uvlong sectors; + char serial[20+1]; + char firmware[8+1]; + char model[40+1]; + char ident[0x100]; +}; + +static Ctlr *head; +static Ctlr *tail; + +static int aoeether[10]; + +SDifc sdaoeifc; + +static void +hnputs(uchar *p, ushort i) +{ + p[0] = i >> 8; + p[1] = i; +} + +static void +hnputl(uchar *p, ulong i) +{ + p[0] = i >> 24; + p[1] = i >> 16; + p[2] = i >> 8; + p[3] = i; +} + +static ushort +nhgets(uchar *p) +{ + return *p<<8 | p[1]; +} + +static ulong +nhgetl(uchar *p) +{ + return p[0]<<24 | p[1]<<16 | p[2]<<8 | p[3]; +} + +static int +newtag(Ctlr *d) +{ + int t; + + for(;;){ + t = ++d->lasttag << 16; + t |= m->ticks & 0xffff; + switch(t) { + case Tfree: + case Tmgmt: + break; + default: + return t; + } + } +} + +static int +hset(Ctlr *d, Aoehdr *h, int cmd) +{ + int tag; + + memmove(h->dst, d->ea, Eaddrlen); + hnputs(h->type, Aoetype); + h->verflag = Aoever << 4; + hnputs(h->major, d->major); + h->minor = d->minor; + h->cmd = cmd; + hnputl(h->tag, tag = newtag(d)); + return tag; +} + +static void +idmove(char *p, ushort *a, int n) +{ + int i; + char *op, *e; + + op = p; + for(i = 0; i < n / 2; i++){ + *p++ = a[i] >> 8; + *p++ = a[i]; + } + *p = 0; + while(p > op && *--p == ' ') + *p = 0; + e = p; + p = op; + while(*p == ' ') + p++; + memmove(op, p, n - (e - p)); +} + +static ushort +gbit16(void *a) +{ + uchar *i; + + i = a; + return i[1]<<8 | i[0]; +} + +static ulong +gbit32(void *a) +{ + uchar *i; + ulong j; + + i = a; + j = i[3] << 24; + j |= i[2] << 16; + j |= i[1] << 8; + j |= i[0]; + return j; +} + +static uvlong +gbit64(void *a) +{ + uchar *i; + + i = a; + return (uvlong)gbit32(i+4) << 32 | gbit32(a); +} + +static int +ataidentify(Ctlr *c, ushort *id) +{ + vlong s; + int i; + + i = gbit16(id+83) | gbit16(id+86); + if(i & (1 << 10)){ + c->feat |= Dllba; + s = gbit64(id+100); + }else + s = gbit32(id+60); + + idmove(c->serial, id+10, 20); + idmove(c->firmware, id+23, 8); + idmove(c->model, id+27, 40); + +print("aoe discovers %d.%d: %s %s\n", c->major, c->minor, c->model, c->serial); + + c->sectors = s; + c->mediachange = 1; + return 0; +} + +static void +identifydump(Aoeata *a) +{ + print("%E %E type=%.4ux verflag=%x error=%x %d.%d cmd=%d tag=%.8lux\n", + a->dst, a->src, nhgets(a->type), a->verflag, a->error, + nhgets(a->major), a->minor, a->cmd, nhgetl(a->tag)); + print(" aflag=%x errfeat=%ux scnt=%d cmdstat=%ux, lba=%d? res=%.4ux\n", + a->aflag, a->errfeat, a->scnt, a->cmdstat, 0, nhgets(a->res)); +} + +static int +idpkt(Ctlr *c, Aoeata *a) +{ + memset(a, 0, sizeof *a); + a->cmdstat = Cid; + a->scnt = 1; + a->lba[3] = 0xa0; + return hset(c, a, ACata); +} + +static int +chktag(int *out, int nout, int tag) +{ + int j; + + for(j = 0; j <= nout; j++) + if(out[j] == tag) + return 0; +print("wrong tag\n"); + for(j = 0; j <= nout; j++) + print("%.8ux != %.8ux\n", out[j], tag); + return -1; +} + +/* + * ignore the tag for identify. better than ignoring + * a response to the wrong identify request + */ +static int +identify(Ctlr *c) +{ + int tag[5], i, n; + Aoeata *a; + Etherpkt p; + + memset(&p, 0, sizeof p); + a = (Aoeata*)&p; + i = 0; + do { + if(i == 5){ + print("aoe: identify timeout\n"); + return -1; + } + tag[i] = idpkt(c, a); + ethertxpkt(c->ctlrno, &p, sizeof *a, 0); + memset(&p, 0, sizeof p); +next: + n = etherrxpkt(c->ctlrno, &p, 125); + if(n == 0){ + i++; + continue; + } + if(nhgets(a->type) != Aoetype) + goto next; + if(nhgets(a->major) != c->major || a->minor != c->minor){ + print("wrong device %d.%d want %d.%d; %d\n", + nhgets(a->major), a->minor, + c->major, c->minor, n); + goto next; + } + if(chktag(tag, i, nhgetl(a->tag)) == -1) + goto next; + if(a->cmdstat & 0xa9){ + print("aoe: ata error on identify: %2ux\n", a->cmdstat); + return -1; + } + } while (a->scnt != 1); + + c->feat = 0; + ataidentify(c, (ushort*)(a+1)); + return 0; +} + +static Ctlr* +ctlrlookup(int major, int minor) +{ + Ctlr *c; + + for(c = head; c; c = c->next) + if(c->major == major && c->minor == minor) + break; + return c; +} + +static Ctlr* +newctlr(Etherpkt *p) +{ + int major, minor; + Aoeqc *q; + Ctlr *c; + + q = (Aoeqc*)p; + if(nhgets(q->type) != Aoetype) + return 0; + major = nhgets(q->major); + minor = q->minor; + + if(major == 0xffff || minor == 0xff) + return 0; + + if(ctlrlookup(major, minor)){ + print("duplicate shelf.slot\n"); + return 0; + } + + if((c = malloc(sizeof *c)) == 0) + return 0; + c->major = major; + c->minor = minor; + memmove(c->ea, q->src, Eaddrlen); + + if(head != 0) + tail->next = c; + else + head = c; + tail = c; + return c; +} + +static void +discover(int major, int minor) +{ + int i; + Aoehdr *h; + Etherpkt p; + + for(i = 0; i < nelem(aoeether); i++){ + if(aoeether[i] == 0) + continue; + memset(&p, 0, ETHERMINTU); + h = (Aoehdr*)&p; + memset(h->dst, 0xff, sizeof h->dst); + hnputs(h->type, Aoetype); + h->verflag = Aoever << 4; + hnputs(h->major, major); + h->minor = minor; + h->cmd = ACconfig; + ethertxpkt(i, &p, ETHERMINTU, 0); + } +} + +static int +rxany(Etherpkt *p, int t) +{ + int i, n; + + for(i = 0; i < nelem(aoeether); i++){ + if(aoeether[i] == 0) + continue; + while ((n = etherrxpkt(i, p, t)) != 0) + if(nhgets(p->type) == Aoetype) + return n; + } + return 0; +} + +static int +aoeprobe(int major, int minor, SDev *s) +{ + Ctlr *ctlr; + Etherpkt p; + int n, i; + + for(i = 0;; i += 200){ + if(i > 8000) + return -1; + discover(major, minor); +again: + n = rxany(&p, 100); + if(n > 0 && (ctlr = newctlr(&p))) + break; + if(n > 0) + goto again; + } + + s->ctlr = ctlr; + s->ifc = &sdaoeifc; + s->nunit = 1; + return 0; +} + +static char *probef[32]; +static int nprobe; + +int +pnpprobeid(char *s) +{ + int id; + + if(strlen(s) < 2) + return 0; + id = 'e'; + if(s[1] == '!') + id = s[0]; + return id; +} + +int +tokenize(char *s, char **args, int maxargs) +{ + int nargs; + + for(nargs = 0; nargs < maxargs; nargs++){ + while(*s != '\0' && strchr("\t\n ", *s) != nil) + s++; + if(*s == '\0') + break; + args[nargs] = s; + while(*s != '\0' && strchr("\t\n ", *s) == nil) + s++; + if(*s != '\0') + *s++ = 0; + } + return nargs; +} + +int +aoepnp0(void) +{ + int i; + char *p, c; + + if((p = getconf("aoeif")) == nil) + return 0; +print("aoeif = %s\n", p); + nprobe = tokenize(p, probef, nelem(probef)); + for(i = 0; i < nprobe; i++){ + if(strncmp(probef[i], "ether", 5) != 0) + continue; + c = probef[i][5]; + if(c > '9' || c < '0') + continue; + aoeether[c - '0'] = 1; + } + + if((p = getconf("aoedev")) == nil) + return 0; + return nprobe = tokenize(p, probef, nelem(probef)); +} + +int +probeshelf(char *s, int *shelf, int *slot) +{ + int a, b; + char *r; + + for(r = s + strlen(s) - 1; r > s; r--) + if((*r < '0' || *r > '9') && *r != '.'){ + r++; + break; + } + a = strtoul(r, &r, 10); + if(*r++ != '.') + return -1; + b = strtoul(r, 0, 10); + + *shelf = a; + *slot = b; +print(" shelf=%d.%d\n", a, b); + return 0; +} + +Ctlr* +pnpprobe(SDev *sd) +{ + int shelf, slot; + char *p; + static int i; + + if(i >= nprobe) + return 0; + p = probef[i++]; + if(strlen(p) < 2) + return 0; + if(p[1] == '!'){ + sd->idno = p[0]; + p += 2; + } + if(probeshelf(p, &shelf, &slot) == -1 || + aoeprobe(shelf, slot, sd) == -1 || + identify(sd->ctlr) == -1) + return 0; + return sd->ctlr; +} + +/* + * we may need to pretend we found something + */ + +SDev* +aoepnp(void) +{ + int n, i, id; + char *p; + SDev *h, *t, *s; + + p = getconf("aoeif"); + if (p) + print("aoepnp: aoeif=%s\n", p); + + if((n = aoepnp0()) == 0) + n = 2; + t = h = 0; + for(i = 0; i < n; i++){ + id = 'e'; + s = malloc(sizeof *s); + if(s == 0) + break; + s->ctlr = 0; + s->idno = id; + s->ifc = &sdaoeifc; + s->nunit = 1; + pnpprobe(s); + + if(h) + t->next = s; + else + h = s; + t = s; + } + return h; +} + +static int +aoeverify(SDunit *u) +{ + Ctlr *c; + SDev *s; + + s = u->dev; + c = s->ctlr; + if(c == 0){ + aoepnp0(); + if((s->ctlr = c = pnpprobe(s)) == nil) + return 0; + } + c->mediachange = 1; + return 1; +} + +static int +aoeonline(SDunit *u) +{ + int r; + Ctlr *c; + + c = u->dev->ctlr; + if(c->mediachange){ + r = 2; + c->mediachange = 0; + u->sectors = c->sectors; + u->secsize = 512; + } else + r = 1; + return r; +} + +static int +rio(Ctlr *c, Aoeata *a, int n, int scnt) +{ + int i, tag, cmd; + + for(i = 0; i < 5; i++){ + tag = hset(c, a, ACata); + cmd = a->cmdstat; + ethertxpkt(c->ctlrno, (Etherpkt*)a, n, 0); + memset(a, 0, sizeof *a); +again: + n = etherrxpkt(c->ctlrno, (Etherpkt*)a, 125); + if(n == 0) + continue; + if(nhgets(a->type) != Aoetype || nhgetl(a->tag) != tag || + nhgets(a->major) != c->major || a->minor != c->minor) + goto again; + if(a->cmdstat & 0xa9){ + print("aoe: ata rio error: %2ux\n", a->cmdstat); + return 0; + } + switch(cmd){ + case Crd: + case Crdext: + if(n - sizeof *a < scnt * 512){ + print("aoe: runt expect %d got %d\n", + scnt*512 + sizeof *a, n); + return 0; + } + return n - sizeof *a; + case Cwr: + case Cwrext: + return scnt * 512; + default: +print("unknown cmd %ux\n", cmd); + break; + } + } + print("aoe: rio timeout\n"); + return 0; +} + +static void +putlba(Aoeata *a, vlong lba) +{ + uchar *c; + + c = a->lba; + c[0] = lba; + c[1] = lba >> 8; + c[2] = lba >> 16; + c[3] = lba >> 24; + c[4] = lba >> 32; + c[5] = lba >> 40; +} + +/* + * you'll need to loop if you want to read more than 2 sectors. for now + * i'm cheeting and not bothering with a loop. + */ +static uchar pktbuf[1024 + sizeof(Aoeata)]; + +static int +aoebuild(Ctlr *c, uchar *cmd, char *data, vlong lba, int scnt) +{ + int n; + Aoeata *a; + + memset(pktbuf, 0, sizeof pktbuf); + a = (Aoeata*)pktbuf; + hset(c, a, ACata); + putlba(a, lba); + + a->cmdstat = 0x20; + if(c->flag & Dllba){ + a->aflag |= AAFext; + a->cmdstat |= 4; + }else{ + a->lba[3] &= 0xf; + a->lba[3] |= 0xe0; /* LBA bit+obsolete 0xa0 */ + } + + n = scnt; + if(n > 2) + n = 2; + a->scnt = n; + + switch(*cmd){ + case 0x2a: + a->aflag |= AAFwrite; + a->cmdstat |= 10; + memmove(a+1, data, n*512); + n = sizeof *a + n*512; + break; + case 0x28: + n = sizeof *a; + break; + default: + print("aoe: bad cmd 0x%.2ux\n", cmd[0]); + return -1; + } + return n; +} + +static int +aoerio(SDreq *r) +{ + int size, nsec, n; + vlong lba; + char *data; + uchar *cmd; + Aoeata *a; + Ctlr *c; + SDunit *unit; + + unit = r->unit; + c = unit->dev->ctlr; + if(r->data == nil) + return SDok; + cmd = r->cmd; + + lba = cmd[2]<<24 | cmd[3]<<16 | cmd[4]<<8 | cmd[5]; /* sic. */ + nsec = cmd[7]<<8 | cmd[8]; + a = (Aoeata*)pktbuf; + data = r->data; + r->rlen = 0; + + for(; nsec > 0; nsec -= n){ +// print("aoebuild(%2x, %p, %lld, %d)\n", *cmd, data, lba, nsec); + size = aoebuild(c, cmd, data, lba, nsec); + if(size < 0){ + r->status = SDcheck; + return SDcheck; + } + n = a->scnt; + r->rlen += rio(c, a, size, n); + if(*cmd == 0x28) + memmove(r->data, a + 1, n * 512); + data += n * 512; + lba += n; + } + + r->status = SDok; + return SDok; +} + +SDifc sdaoeifc = { + "aoe", + + aoepnp, + nil, /* legacy */ + nil, /* id */ + nil, /* enable */ + nil, /* disable */ + + aoeverify, + aoeonline, + aoerio, + nil, + nil, + + scsibio, +}; |
