xref: /inferno-os/os/boot/pc/sdaoe.c (revision a17fe75d12acceb8fa829b70751afdba094d9739)
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