summaryrefslogtreecommitdiff
path: root/os/boot/pc/sdaoe.c
diff options
context:
space:
mode:
authorCharles.Forsyth <devnull@localhost>2008-06-11 14:21:44 +0000
committerCharles.Forsyth <devnull@localhost>2008-06-11 14:21:44 +0000
commit8a8c2d742b51525f66c2210e3c8a251de10022ff (patch)
tree8282ce595e5fbe2e487dc20f54891d9e9e7cbf37 /os/boot/pc/sdaoe.c
parent31a18a6996a6b5927e39cc553696c167e6c88e3d (diff)
20080611-1520
Diffstat (limited to 'os/boot/pc/sdaoe.c')
-rw-r--r--os/boot/pc/sdaoe.c734
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,
+};