/* * aoe sd bootstrap driver, copyright © 2007-9 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" #include #define uprint(...) snprint(up->genbuf, sizeof up->genbuf, __VA_ARGS__); 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; Sfis; 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) { ushort i; i = *p<<8; return i|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; loop: t = ++d->lasttag<<16; t |= m->ticks&0xffff; switch(t) { case Tfree: case Tmgmt: goto loop; 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 int ataidentify(Ctlr *c, ushort *id) { vlong s; s = idfeat(c, id); 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) { Etherpkt p; Aoeata *a; int tag[5], i, n; memset(&p, 0, sizeof p); a = (Aoeata*)&p; for(i = 0;;){ 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; } if(a->scnt != 1) continue; break; } 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) { Ctlr *c; Aoeqc *q; int major, minor; 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 int aoeminor = -1; static int aoemajor = -1; int interestingaoe(uchar *u, int l) { Aoehdr *h; if(l < 60) return 0; h = (Aoehdr*)u; if(h->minor != aoeminor || nhgets(h->major) != aoemajor) return 0; return 1; } static void discover(int major, int minor) { Aoehdr *h; Etherpkt p; int i; aoemajor = major; aoeminor = minor; 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; again: if(n = etherrxpkt(i, p, t)){ if(nhgets(p->type) != Aoetype) goto again; 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 '9' || c < '0') continue; aoeether[c-'0'] = 1; } if((p = getconf("aoedev")) == 0) return 0; return nprobe = tokenize(p, probef, nelem(probef)); } int probeshelf(char *s, int *shelf, int *slot) { char *r; int a, b; 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) { static int i; char *p; int shelf, slot; 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) return 0; if(aoeprobe(shelf, slot, sd) == -1) return 0; if(identify(sd->ctlr) == -1) return 0; return sd->ctlr; } /* * we may need to pretend we found something; */ SDev* aoepnp(void) { SDev *h, *t, *s; int n, i, id; print("aoepnp(%s)\n", getconf("aoeif")); 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) { SDev *s; Ctlr *c; s = u->dev; c = s->ctlr; if(c == 0){ aoepnp0(); if((c = s->ctlr = pnpprobe(s)) == 0) return 0; } c->mediachange = 1; return 1; } static int aoeonline(SDunit *u) { Ctlr *c; int r; 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;;){ if(i == 5){ print("aoe: rio timeout\n"); return 0; } 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){ i++; continue; } if(nhgets(a->type) != Aoetype) goto again; if(nhgetl(a->tag) != tag) goto again; if(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; } i++; continue; } } 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; } static uchar pktbuf[1024 + sizeof(Aoeata)]; static int aoebuild(Ctlr *c, int write, char *data, vlong lba, int scnt) { Aoeata *a; int n; memset(pktbuf, 0, sizeof pktbuf); a = (Aoeata*)pktbuf; hset(c, a, ACata); putlba(a, lba); a->cmdstat = 0x20; if(c->feat & Dllba){ a->aflag |= AAFext; a->cmdstat |= 4; }else{ a->lba[3] &= 0xf; a->lba[3] |= Ataobs | Atalba; } n = scnt; if(n > 2) n = 2; a->scnt = n; if(write){ a->aflag |= AAFwrite; a->cmdstat |= 10; memmove(a + 1, data, n*512); n = sizeof *a + n*512; }else n = sizeof *a; return n; } static long aoebio(SDunit *unit, int, int write, void *v, long nsec, uvlong lba) { char *data; int size, n, rlen; Aoeata *a; Ctlr *c; c = unit->dev->ctlr; a = (Aoeata*)pktbuf; data = v; rlen = 0; for(; nsec > 0; nsec -= n){ // print("aoebuild(%2x, %p, %lld, %d)\n", *cmd, data, lba, nsec); size = aoebuild(c, write, data, lba, nsec); if(size < 0) return -1; n = a->scnt; rlen += rio(c, a, size, n); if(!write) memmove(data, a + 1, n*512); data += n*512; lba += n; } return rlen; } SDifc sdaoeifc = { "aoe", aoepnp, nil, /* legacy */ nil, /* id */ nil, /* enable */ nil, /* disable */ aoeverify, aoeonline, nil, nil, nil, aoebio, };