1 /*
2 * aoe sd bootstrap driver, copyright © 2007 coraid
3 */
4
5 #include "u.h"
6 #include "mem.h"
7 #include "lib.h"
8 #include "dat.h"
9 #include "fns.h"
10 #include "io.h"
11 #include "sd.h"
12 #include "aoe.h"
13
14
15 enum {
16 Nctlr = 4,
17 };
18
19 enum {
20 /* sync with ahci.h */
21 Dllba = 1<<0,
22 Dsmart = 1<<1,
23 Dpower = 1<<2,
24 Dnop = 1<<3,
25 Datapi = 1<<4,
26 Datapi16= 1<<5,
27 };
28
29 enum {
30 Tfree = -1,
31 Tmgmt,
32 };
33
34 typedef struct Ctlr Ctlr;
35 struct Ctlr{
36 Ctlr *next;
37 SDunit *unit;
38
39 int ctlrno;
40 int major;
41 int minor;
42 uchar ea[Eaddrlen];
43 ushort lasttag;
44
45 ulong vers;
46 uchar mediachange;
47 uchar flag;
48 uchar smart;
49 uchar smartrs;
50 uchar feat;
51
52 uvlong sectors;
53 char serial[20+1];
54 char firmware[8+1];
55 char model[40+1];
56 char ident[0x100];
57 };
58
59 static Ctlr *head;
60 static Ctlr *tail;
61
62 static int aoeether[10];
63
64 SDifc sdaoeifc;
65
66 static void
hnputs(uchar * p,ushort i)67 hnputs(uchar *p, ushort i)
68 {
69 p[0] = i >> 8;
70 p[1] = i;
71 }
72
73 static void
hnputl(uchar * p,ulong i)74 hnputl(uchar *p, ulong i)
75 {
76 p[0] = i >> 24;
77 p[1] = i >> 16;
78 p[2] = i >> 8;
79 p[3] = i;
80 }
81
82 static ushort
nhgets(uchar * p)83 nhgets(uchar *p)
84 {
85 return *p<<8 | p[1];
86 }
87
88 static ulong
nhgetl(uchar * p)89 nhgetl(uchar *p)
90 {
91 return p[0]<<24 | p[1]<<16 | p[2]<<8 | p[3];
92 }
93
94 static int
newtag(Ctlr * d)95 newtag(Ctlr *d)
96 {
97 int t;
98
99 for(;;){
100 t = ++d->lasttag << 16;
101 t |= m->ticks & 0xffff;
102 switch(t) {
103 case Tfree:
104 case Tmgmt:
105 break;
106 default:
107 return t;
108 }
109 }
110 }
111
112 static int
hset(Ctlr * d,Aoehdr * h,int cmd)113 hset(Ctlr *d, Aoehdr *h, int cmd)
114 {
115 int tag;
116
117 memmove(h->dst, d->ea, Eaddrlen);
118 hnputs(h->type, Aoetype);
119 h->verflag = Aoever << 4;
120 hnputs(h->major, d->major);
121 h->minor = d->minor;
122 h->cmd = cmd;
123 hnputl(h->tag, tag = newtag(d));
124 return tag;
125 }
126
127 static void
idmove(char * p,ushort * a,int n)128 idmove(char *p, ushort *a, int n)
129 {
130 int i;
131 char *op, *e;
132
133 op = p;
134 for(i = 0; i < n / 2; i++){
135 *p++ = a[i] >> 8;
136 *p++ = a[i];
137 }
138 *p = 0;
139 while(p > op && *--p == ' ')
140 *p = 0;
141 e = p;
142 p = op;
143 while(*p == ' ')
144 p++;
145 memmove(op, p, n - (e - p));
146 }
147
148 static ushort
gbit16(void * a)149 gbit16(void *a)
150 {
151 uchar *i;
152
153 i = a;
154 return i[1]<<8 | i[0];
155 }
156
157 static ulong
gbit32(void * a)158 gbit32(void *a)
159 {
160 uchar *i;
161 ulong j;
162
163 i = a;
164 j = i[3] << 24;
165 j |= i[2] << 16;
166 j |= i[1] << 8;
167 j |= i[0];
168 return j;
169 }
170
171 static uvlong
gbit64(void * a)172 gbit64(void *a)
173 {
174 uchar *i;
175
176 i = a;
177 return (uvlong)gbit32(i+4) << 32 | gbit32(a);
178 }
179
180 static int
ataidentify(Ctlr * c,ushort * id)181 ataidentify(Ctlr *c, ushort *id)
182 {
183 vlong s;
184 int i;
185
186 i = gbit16(id+83) | gbit16(id+86);
187 if(i & (1 << 10)){
188 c->feat |= Dllba;
189 s = gbit64(id+100);
190 }else
191 s = gbit32(id+60);
192
193 idmove(c->serial, id+10, 20);
194 idmove(c->firmware, id+23, 8);
195 idmove(c->model, id+27, 40);
196
197 print("aoe discovers %d.%d: %s %s\n", c->major, c->minor, c->model, c->serial);
198
199 c->sectors = s;
200 c->mediachange = 1;
201 return 0;
202 }
203
204 static void
identifydump(Aoeata * a)205 identifydump(Aoeata *a)
206 {
207 print("%E %E type=%.4ux verflag=%x error=%x %d.%d cmd=%d tag=%.8lux\n",
208 a->dst, a->src, nhgets(a->type), a->verflag, a->error,
209 nhgets(a->major), a->minor, a->cmd, nhgetl(a->tag));
210 print(" aflag=%x errfeat=%ux scnt=%d cmdstat=%ux, lba=%d? res=%.4ux\n",
211 a->aflag, a->errfeat, a->scnt, a->cmdstat, 0, nhgets(a->res));
212 }
213
214 static int
idpkt(Ctlr * c,Aoeata * a)215 idpkt(Ctlr *c, Aoeata *a)
216 {
217 memset(a, 0, sizeof *a);
218 a->cmdstat = Cid;
219 a->scnt = 1;
220 a->lba[3] = 0xa0;
221 return hset(c, a, ACata);
222 }
223
224 static int
chktag(int * out,int nout,int tag)225 chktag(int *out, int nout, int tag)
226 {
227 int j;
228
229 for(j = 0; j <= nout; j++)
230 if(out[j] == tag)
231 return 0;
232 print("wrong tag\n");
233 for(j = 0; j <= nout; j++)
234 print("%.8ux != %.8ux\n", out[j], tag);
235 return -1;
236 }
237
238 /*
239 * ignore the tag for identify. better than ignoring
240 * a response to the wrong identify request
241 */
242 static int
identify(Ctlr * c)243 identify(Ctlr *c)
244 {
245 int tag[5], i, n;
246 Aoeata *a;
247 Etherpkt p;
248
249 memset(&p, 0, sizeof p);
250 a = (Aoeata*)&p;
251 i = 0;
252 do {
253 if(i == 5){
254 print("aoe: identify timeout\n");
255 return -1;
256 }
257 tag[i] = idpkt(c, a);
258 ethertxpkt(c->ctlrno, &p, sizeof *a, 0);
259 memset(&p, 0, sizeof p);
260 next:
261 n = etherrxpkt(c->ctlrno, &p, 125);
262 if(n == 0){
263 i++;
264 continue;
265 }
266 if(nhgets(a->type) != Aoetype)
267 goto next;
268 if(nhgets(a->major) != c->major || a->minor != c->minor){
269 print("wrong device %d.%d want %d.%d; %d\n",
270 nhgets(a->major), a->minor,
271 c->major, c->minor, n);
272 goto next;
273 }
274 if(chktag(tag, i, nhgetl(a->tag)) == -1)
275 goto next;
276 if(a->cmdstat & 0xa9){
277 print("aoe: ata error on identify: %2ux\n", a->cmdstat);
278 return -1;
279 }
280 } while (a->scnt != 1);
281
282 c->feat = 0;
283 ataidentify(c, (ushort*)(a+1));
284 return 0;
285 }
286
287 static Ctlr*
ctlrlookup(int major,int minor)288 ctlrlookup(int major, int minor)
289 {
290 Ctlr *c;
291
292 for(c = head; c; c = c->next)
293 if(c->major == major && c->minor == minor)
294 break;
295 return c;
296 }
297
298 static Ctlr*
newctlr(Etherpkt * p)299 newctlr(Etherpkt *p)
300 {
301 int major, minor;
302 Aoeqc *q;
303 Ctlr *c;
304
305 q = (Aoeqc*)p;
306 if(nhgets(q->type) != Aoetype)
307 return 0;
308 major = nhgets(q->major);
309 minor = q->minor;
310
311 if(major == 0xffff || minor == 0xff)
312 return 0;
313
314 if(ctlrlookup(major, minor)){
315 print("duplicate shelf.slot\n");
316 return 0;
317 }
318
319 if((c = malloc(sizeof *c)) == 0)
320 return 0;
321 c->major = major;
322 c->minor = minor;
323 memmove(c->ea, q->src, Eaddrlen);
324
325 if(head != 0)
326 tail->next = c;
327 else
328 head = c;
329 tail = c;
330 return c;
331 }
332
333 static void
discover(int major,int minor)334 discover(int major, int minor)
335 {
336 int i;
337 Aoehdr *h;
338 Etherpkt p;
339
340 for(i = 0; i < nelem(aoeether); i++){
341 if(aoeether[i] == 0)
342 continue;
343 memset(&p, 0, ETHERMINTU);
344 h = (Aoehdr*)&p;
345 memset(h->dst, 0xff, sizeof h->dst);
346 hnputs(h->type, Aoetype);
347 h->verflag = Aoever << 4;
348 hnputs(h->major, major);
349 h->minor = minor;
350 h->cmd = ACconfig;
351 ethertxpkt(i, &p, ETHERMINTU, 0);
352 }
353 }
354
355 static int
rxany(Etherpkt * p,int t)356 rxany(Etherpkt *p, int t)
357 {
358 int i, n;
359
360 for(i = 0; i < nelem(aoeether); i++){
361 if(aoeether[i] == 0)
362 continue;
363 while ((n = etherrxpkt(i, p, t)) != 0)
364 if(nhgets(p->type) == Aoetype)
365 return n;
366 }
367 return 0;
368 }
369
370 static int
aoeprobe(int major,int minor,SDev * s)371 aoeprobe(int major, int minor, SDev *s)
372 {
373 Ctlr *ctlr;
374 Etherpkt p;
375 int n, i;
376
377 for(i = 0;; i += 200){
378 if(i > 8000)
379 return -1;
380 discover(major, minor);
381 again:
382 n = rxany(&p, 100);
383 if(n > 0 && (ctlr = newctlr(&p)))
384 break;
385 if(n > 0)
386 goto again;
387 }
388
389 s->ctlr = ctlr;
390 s->ifc = &sdaoeifc;
391 s->nunit = 1;
392 return 0;
393 }
394
395 static char *probef[32];
396 static int nprobe;
397
398 int
pnpprobeid(char * s)399 pnpprobeid(char *s)
400 {
401 int id;
402
403 if(strlen(s) < 2)
404 return 0;
405 id = 'e';
406 if(s[1] == '!')
407 id = s[0];
408 return id;
409 }
410
411 int
tokenize(char * s,char ** args,int maxargs)412 tokenize(char *s, char **args, int maxargs)
413 {
414 int nargs;
415
416 for(nargs = 0; nargs < maxargs; nargs++){
417 while(*s != '\0' && strchr("\t\n ", *s) != nil)
418 s++;
419 if(*s == '\0')
420 break;
421 args[nargs] = s;
422 while(*s != '\0' && strchr("\t\n ", *s) == nil)
423 s++;
424 if(*s != '\0')
425 *s++ = 0;
426 }
427 return nargs;
428 }
429
430 int
aoepnp0(void)431 aoepnp0(void)
432 {
433 int i;
434 char *p, c;
435
436 if((p = getconf("aoeif")) == nil)
437 return 0;
438 print("aoeif = %s\n", p);
439 nprobe = tokenize(p, probef, nelem(probef));
440 for(i = 0; i < nprobe; i++){
441 if(strncmp(probef[i], "ether", 5) != 0)
442 continue;
443 c = probef[i][5];
444 if(c > '9' || c < '0')
445 continue;
446 aoeether[c - '0'] = 1;
447 }
448
449 if((p = getconf("aoedev")) == nil)
450 return 0;
451 return nprobe = tokenize(p, probef, nelem(probef));
452 }
453
454 int
probeshelf(char * s,int * shelf,int * slot)455 probeshelf(char *s, int *shelf, int *slot)
456 {
457 int a, b;
458 char *r;
459
460 for(r = s + strlen(s) - 1; r > s; r--)
461 if((*r < '0' || *r > '9') && *r != '.'){
462 r++;
463 break;
464 }
465 a = strtoul(r, &r, 10);
466 if(*r++ != '.')
467 return -1;
468 b = strtoul(r, 0, 10);
469
470 *shelf = a;
471 *slot = b;
472 print(" shelf=%d.%d\n", a, b);
473 return 0;
474 }
475
476 Ctlr*
pnpprobe(SDev * sd)477 pnpprobe(SDev *sd)
478 {
479 int shelf, slot;
480 char *p;
481 static int i;
482
483 if(i >= nprobe)
484 return 0;
485 p = probef[i++];
486 if(strlen(p) < 2)
487 return 0;
488 if(p[1] == '!'){
489 sd->idno = p[0];
490 p += 2;
491 }
492 if(probeshelf(p, &shelf, &slot) == -1 ||
493 aoeprobe(shelf, slot, sd) == -1 ||
494 identify(sd->ctlr) == -1)
495 return 0;
496 return sd->ctlr;
497 }
498
499 /*
500 * we may need to pretend we found something
501 */
502
503 SDev*
aoepnp(void)504 aoepnp(void)
505 {
506 int n, i, id;
507 char *p;
508 SDev *h, *t, *s;
509
510 p = getconf("aoeif");
511 if (p)
512 print("aoepnp: aoeif=%s\n", p);
513
514 if((n = aoepnp0()) == 0)
515 n = 2;
516 t = h = 0;
517 for(i = 0; i < n; i++){
518 id = 'e';
519 s = malloc(sizeof *s);
520 if(s == 0)
521 break;
522 s->ctlr = 0;
523 s->idno = id;
524 s->ifc = &sdaoeifc;
525 s->nunit = 1;
526 pnpprobe(s);
527
528 if(h)
529 t->next = s;
530 else
531 h = s;
532 t = s;
533 }
534 return h;
535 }
536
537 static int
aoeverify(SDunit * u)538 aoeverify(SDunit *u)
539 {
540 Ctlr *c;
541 SDev *s;
542
543 s = u->dev;
544 c = s->ctlr;
545 if(c == 0){
546 aoepnp0();
547 if((s->ctlr = c = pnpprobe(s)) == nil)
548 return 0;
549 }
550 c->mediachange = 1;
551 return 1;
552 }
553
554 static int
aoeonline(SDunit * u)555 aoeonline(SDunit *u)
556 {
557 int r;
558 Ctlr *c;
559
560 c = u->dev->ctlr;
561 if(c->mediachange){
562 r = 2;
563 c->mediachange = 0;
564 u->sectors = c->sectors;
565 u->secsize = 512;
566 } else
567 r = 1;
568 return r;
569 }
570
571 static int
rio(Ctlr * c,Aoeata * a,int n,int scnt)572 rio(Ctlr *c, Aoeata *a, int n, int scnt)
573 {
574 int i, tag, cmd;
575
576 for(i = 0; i < 5; i++){
577 tag = hset(c, a, ACata);
578 cmd = a->cmdstat;
579 ethertxpkt(c->ctlrno, (Etherpkt*)a, n, 0);
580 memset(a, 0, sizeof *a);
581 again:
582 n = etherrxpkt(c->ctlrno, (Etherpkt*)a, 125);
583 if(n == 0)
584 continue;
585 if(nhgets(a->type) != Aoetype || nhgetl(a->tag) != tag ||
586 nhgets(a->major) != c->major || a->minor != c->minor)
587 goto again;
588 if(a->cmdstat & 0xa9){
589 print("aoe: ata rio error: %2ux\n", a->cmdstat);
590 return 0;
591 }
592 switch(cmd){
593 case Crd:
594 case Crdext:
595 if(n - sizeof *a < scnt * 512){
596 print("aoe: runt expect %d got %d\n",
597 scnt*512 + sizeof *a, n);
598 return 0;
599 }
600 return n - sizeof *a;
601 case Cwr:
602 case Cwrext:
603 return scnt * 512;
604 default:
605 print("unknown cmd %ux\n", cmd);
606 break;
607 }
608 }
609 print("aoe: rio timeout\n");
610 return 0;
611 }
612
613 static void
putlba(Aoeata * a,vlong lba)614 putlba(Aoeata *a, vlong lba)
615 {
616 uchar *c;
617
618 c = a->lba;
619 c[0] = lba;
620 c[1] = lba >> 8;
621 c[2] = lba >> 16;
622 c[3] = lba >> 24;
623 c[4] = lba >> 32;
624 c[5] = lba >> 40;
625 }
626
627 /*
628 * you'll need to loop if you want to read more than 2 sectors. for now
629 * i'm cheeting and not bothering with a loop.
630 */
631 static uchar pktbuf[1024 + sizeof(Aoeata)];
632
633 static int
aoebuild(Ctlr * c,uchar * cmd,char * data,vlong lba,int scnt)634 aoebuild(Ctlr *c, uchar *cmd, char *data, vlong lba, int scnt)
635 {
636 int n;
637 Aoeata *a;
638
639 memset(pktbuf, 0, sizeof pktbuf);
640 a = (Aoeata*)pktbuf;
641 hset(c, a, ACata);
642 putlba(a, lba);
643
644 a->cmdstat = 0x20;
645 if(c->flag & Dllba){
646 a->aflag |= AAFext;
647 a->cmdstat |= 4;
648 }else{
649 a->lba[3] &= 0xf;
650 a->lba[3] |= 0xe0; /* LBA bit+obsolete 0xa0 */
651 }
652
653 n = scnt;
654 if(n > 2)
655 n = 2;
656 a->scnt = n;
657
658 switch(*cmd){
659 case 0x2a:
660 a->aflag |= AAFwrite;
661 a->cmdstat |= 10;
662 memmove(a+1, data, n*512);
663 n = sizeof *a + n*512;
664 break;
665 case 0x28:
666 n = sizeof *a;
667 break;
668 default:
669 print("aoe: bad cmd 0x%.2ux\n", cmd[0]);
670 return -1;
671 }
672 return n;
673 }
674
675 static int
aoerio(SDreq * r)676 aoerio(SDreq *r)
677 {
678 int size, nsec, n;
679 vlong lba;
680 char *data;
681 uchar *cmd;
682 Aoeata *a;
683 Ctlr *c;
684 SDunit *unit;
685
686 unit = r->unit;
687 c = unit->dev->ctlr;
688 if(r->data == nil)
689 return SDok;
690 cmd = r->cmd;
691
692 lba = cmd[2]<<24 | cmd[3]<<16 | cmd[4]<<8 | cmd[5]; /* sic. */
693 nsec = cmd[7]<<8 | cmd[8];
694 a = (Aoeata*)pktbuf;
695 data = r->data;
696 r->rlen = 0;
697
698 for(; nsec > 0; nsec -= n){
699 // print("aoebuild(%2x, %p, %lld, %d)\n", *cmd, data, lba, nsec);
700 size = aoebuild(c, cmd, data, lba, nsec);
701 if(size < 0){
702 r->status = SDcheck;
703 return SDcheck;
704 }
705 n = a->scnt;
706 r->rlen += rio(c, a, size, n);
707 if(*cmd == 0x28)
708 memmove(r->data, a + 1, n * 512);
709 data += n * 512;
710 lba += n;
711 }
712
713 r->status = SDok;
714 return SDok;
715 }
716
717 SDifc sdaoeifc = {
718 "aoe",
719
720 aoepnp,
721 nil, /* legacy */
722 nil, /* id */
723 nil, /* enable */
724 nil, /* disable */
725
726 aoeverify,
727 aoeonline,
728 aoerio,
729 nil,
730 nil,
731
732 scsibio,
733 };
734