xref: /plan9-contrib/sys/src/nboot/zynq/net.c (revision 529c1f209803c78c4f2cda11b13818a57f01c872)
1 #include <u.h>
2 #include "dat.h"
3 #include "fns.h"
4 #include "mem.h"
5 
6 enum {
7 	ETHLEN = 1600,
8 	UDPLEN = 576,
9 	NRX = 64,
10 	RXBASE = 128 * 1024 * 1024,
11 
12 	ETHHEAD = 14,
13 	IPHEAD = 20,
14 	UDPHEAD = 8,
15 
16 	BOOTREQ = 1,
17 	DHCPDISCOVER = 1,
18 	DHCPOFFER,
19 	DHCPREQUEST,
20 	DHCPDECLINE,
21 };
22 
23 enum {
24 	NET_CTRL,
25 	NET_CFG,
26 	NET_STATUS,
27 	DMA_CFG = 4,
28 	TX_STATUS,
29 	RX_QBAR,
30 	TX_QBAR,
31 	RX_STATUS,
32 	INTR_STATUS,
33 	INTR_EN,
34 	INTR_DIS,
35 	INTR_MASK,
36 	PHY_MAINT,
37 	RX_PAUSEQ,
38 	TX_PAUSEQ,
39 	HASH_BOT = 32,
40 	HASH_TOP,
41 	SPEC_ADDR1_BOT,
42 	SPEC_ADDR1_TOP,
43 };
44 
45 enum {
46 	MDCTRL,
47 	MDSTATUS,
48 	MDID1,
49 	MDID2,
50 	MDAUTOADV,
51 	MDAUTOPART,
52 	MDAUTOEX,
53 	MDAUTONEXT,
54 	MDAUTOLINK,
55 	MDGCTRL,
56 	MDGSTATUS,
57 	MDPHYCTRL = 0x1f,
58 };
59 
60 enum {
61 	/* NET_CTRL */
62 	RXEN = 1<<2,
63 	TXEN = 1<<3,
64 	MDEN = 1<<4,
65 	STARTTX = 1<<9,
66 	/* NET_CFG */
67 	SPEED = 1<<0,
68 	FDEN = 1<<1,
69 	RX1536EN = 1<<8,
70 	GIGE_EN = 1<<10,
71 	RXCHKSUMEN = 1<<24,
72 	/* NET_STATUS */
73 	PHY_IDLE = 1<<2,
74 	/* DMA_CFG */
75 	TXCHKSUMEN  = 1<<11,
76 	/* TX_STATUS */
77 	TXCOMPL = 1<<5,
78 	/* MDCTRL */
79 	MDRESET = 1<<15,
80 	AUTONEG = 1<<12,
81 	FULLDUP = 1<<8,
82 	/* MDSTATUS */
83 	LINK = 1<<2,
84 	/* MDGSTATUS */
85 	RECVOK = 3<<12,
86 };
87 
88 typedef struct {
89 	uchar edest[6];
90 	uchar esrc[6];
91 	ulong idest;
92 	ulong isrc;
93 	ushort dport, sport;
94 	ushort len;
95 	uchar data[UDPLEN];
96 } udp;
97 
98 static ulong *eth0 = (ulong *) 0xe000b000;
99 static int phyaddr = 7;
100 
101 static u32int myip, dhcpip, tftpip, xid;
102 static uchar mac[6] = {0x0E, 0xA7, 0xDE, 0xAD, 0xBE, 0xEF};
103 static uchar tmac[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
104 static char file[128];
105 
106 static udp ubuf, urbuf;
107 static uchar txbuf[ETHLEN];
108 static ulong txdesc[4], *txact, *rxact;
109 static ulong rxdesc[NRX*2];
110 
111 void
mdwrite(ulong * r,int reg,u16int val)112 mdwrite(ulong *r, int reg, u16int val)
113 {
114 	while((r[NET_STATUS] & PHY_IDLE) == 0)
115 		;
116 	r[PHY_MAINT] = 1<<30 | 1<<28 | 1<<17 | phyaddr << 23 | reg << 18 | val;
117 	while((r[NET_STATUS] & PHY_IDLE) == 0)
118 		;
119 }
120 
121 u16int
mdread(ulong * r,int reg)122 mdread(ulong *r, int reg)
123 {
124 	while((r[NET_STATUS] & PHY_IDLE) == 0)
125 		;
126 	r[PHY_MAINT] = 1<<30 | 1<<29 | 1<<17 | phyaddr << 23 | reg << 18;
127 	while((r[NET_STATUS] & PHY_IDLE) == 0)
128 		;
129 	return r[PHY_MAINT];
130 }
131 
132 void
ethinit(ulong * r)133 ethinit(ulong *r)
134 {
135 	int v;
136 	ulong *p;
137 	ulong d;
138 
139 	r[NET_CTRL] = 0;
140 	r[RX_STATUS] = 0xf;
141 	r[TX_STATUS] = 0xff;
142 	r[INTR_DIS] = 0x7FFFEFF;
143 	r[RX_QBAR] = r[TX_QBAR] = 0;
144 	r[NET_CFG] = MDC_DIV << 18 | FDEN | SPEED | RX1536EN | GIGE_EN | RXCHKSUMEN;
145 	r[SPEC_ADDR1_BOT] = mac[0] | mac[1] << 8 | mac[2] << 16 | mac[3] << 24;
146 	r[SPEC_ADDR1_TOP] = mac[4] | mac[5] << 8;
147 	r[DMA_CFG] = TXCHKSUMEN | 0x18 << 16 | 1 << 10 | 3 << 8 | 0x10;
148 
149 	txdesc[0] = 0;
150 	txdesc[1] = 1<<31;
151 	txdesc[2] = 0;
152 	txdesc[3] = 1<<31 | 1<<30;
153 	txact = txdesc;
154 	r[TX_QBAR] = (ulong) txdesc;
155 	for(p = rxdesc, d = RXBASE; p < rxdesc + nelem(rxdesc); d += ETHLEN){
156 		*p++ = d;
157 		*p++ = 0;
158 	}
159 	p[-2] |= 2;
160 	rxact = rxdesc;
161 	r[RX_QBAR] = (ulong) rxdesc;
162 
163 	r[NET_CTRL] = MDEN;
164 //	mdwrite(r, MDCTRL, MDRESET);
165 	mdwrite(r, MDCTRL, AUTONEG);
166 	if((mdread(r, MDSTATUS) & LINK) == 0){
167 		puts("Waiting for Link ...\n");
168 		while((mdread(r, MDSTATUS) & LINK) == 0)
169 			;
170 	}
171 	*(u32int*)(SLCR_BASE + SLCR_UNLOCK) = UNLOCK_KEY;
172 	v = mdread(r, MDPHYCTRL);
173 	if((v & 0x40) != 0){
174 		puts("1000BASE-T");
175 		while((mdread(r, MDGSTATUS) & RECVOK) != RECVOK)
176 			;
177 		r[NET_CFG] |= GIGE_EN;
178 		*(u32int*)(SLCR_BASE + GEM0_CLK_CTRL) = 1 << 20 | 8 << 8 | 1;
179 	}else if((v & 0x20) != 0){
180 		puts("100BASE-TX");
181 		r[NET_CFG] = r[NET_CFG] & ~GIGE_EN | SPEED;
182 		*(u32int*)(SLCR_BASE + GEM0_CLK_CTRL) = 5 << 20 | 8 << 8 | 1;
183 	}else if((v & 0x10) != 0){
184 		puts("10BASE-T");
185 		r[NET_CFG] = r[NET_CFG] & ~(GIGE_EN | SPEED);
186 		*(u32int*)(SLCR_BASE + GEM0_CLK_CTRL) = 20 << 20 | 20 << 8 | 1;
187 	}else
188 		puts("???");
189 	*(u32int*)(SLCR_BASE + SLCR_UNLOCK) = LOCK_KEY;
190 	if((v & 0x08) != 0)
191 		puts(" Full Duplex\n");
192 	else{
193 		puts(" Half Duplex\n");
194 		r[NET_CFG] &= ~FDEN;
195 	}
196 	r[NET_CTRL] |= TXEN | RXEN;
197 }
198 
199 void
ethtx(ulong * r,uchar * buf,int len)200 ethtx(ulong *r, uchar *buf, int len)
201 {
202 	txact[0] = (ulong) buf;
203 	txact[1] = 1<<15 | len;
204 	if(txact == txdesc + nelem(txdesc) - 2){
205 		txact[1] |= 1<<30;
206 		txact = txdesc;
207 	}else
208 		txact += 2;
209 	r[TX_STATUS] = -1;
210 	r[NET_CTRL] |= STARTTX;
211 	while((r[TX_STATUS] & TXCOMPL) == 0)
212 		;
213 }
214 
215 void
udptx(ulong * r,udp * u)216 udptx(ulong *r, udp *u)
217 {
218 	uchar *p, *q;
219 	int n;
220 
221 	p = q = txbuf;
222 	memcpy(p, u->edest, 6);
223 	memcpy(p + 6, u->esrc, 6);
224 	q += 12;
225 	*q++ = 8;
226 	*q++ = 0;
227 
228 	*q++ = 5 | 4 << 4;
229 	*q++ = 0;
230 	n = IPHEAD + UDPHEAD + u->len;
231 	*q++ = n >> 8;
232 	*q++ = n;
233 
234 	*q++ = 0x13;
235 	*q++ = 0x37;
236 	*q++ = 1<<6;
237 	*q++ = 0;
238 
239 	*q++ = 1;
240 	*q++ = 0x11;
241 	*q++ = 0;
242 	*q++ = 0;
243 	q = u32put(q, u->isrc);
244 	q = u32put(q, u->idest);
245 
246 	*q++ = u->sport >> 8;
247 	*q++ = u->sport;
248 	*q++ = u->dport >> 8;
249 	*q++ = u->dport;
250 	n = UDPHEAD + u->len;
251 	*q++ = n >> 8;
252 	*q++ = n;
253 	*q++ = 0;
254 	*q++ = 0;
255 
256 	memcpy(q, u->data, u->len);
257 	ethtx(r, p, ETHHEAD + IPHEAD + UDPHEAD + u->len);
258 }
259 
260 void
dhcppkg(ulong * r,int t)261 dhcppkg(ulong *r, int t)
262 {
263 	uchar *p;
264 	udp *u;
265 
266 	u = &ubuf;
267 	p = u->data;
268 	*p++ = BOOTREQ;
269 	*p++ = 1;
270 	*p++ = 6;
271 	*p++ = 0;
272 	p = u32put(p, xid);
273 	p = u32put(p, 0x8000);
274 	memset(p, 0, 16);
275 	u32put(p + 8, dhcpip);
276 	p += 16;
277 	memcpy(p, mac, 6);
278 	p += 6;
279 	memset(p, 0, 202);
280 	p += 202;
281 	*p++ = 99;
282 	*p++ = 130;
283 	*p++ = 83;
284 	*p++ = 99;
285 
286 	*p++ = 53;
287 	*p++ = 1;
288 	*p++ = t;
289 	if(t == DHCPREQUEST){
290 		*p++ = 50;
291 		*p++ = 4;
292 		p = u32put(p, myip);
293 		*p++ = 54;
294 		*p++ = 4;
295 		p = u32put(p, dhcpip);
296 	}
297 
298 	*p++ = 0xff;
299 
300 	memset(u->edest, 0xff, 6);
301 	memcpy(u->esrc, mac, 6);
302 	u->sport = 68;
303 	u->dport = 67;
304 	u->idest = -1;
305 	u->isrc = 0;
306 	u->len = p - u->data;
307 	udptx(r, u);
308 }
309 
310 uchar *
ethrx(void)311 ethrx(void)
312 {
313 	while((*rxact & 1) == 0)
314 		if(timertrig())
315 			return nil;
316 	return (uchar *) (*rxact & ~3);
317 }
318 
319 void
ethnext(void)320 ethnext(void)
321 {
322 	*rxact &= ~1;
323 	if((*rxact & 2) != 0)
324 		rxact = rxdesc;
325 	else
326 		rxact += 2;
327 }
328 
329 void
arp(int op,uchar * edest,ulong idest)330 arp(int op, uchar *edest, ulong idest)
331 {
332 	uchar *p;
333 	static uchar broad[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
334 
335 	p = txbuf;
336 	if(edest == nil)
337 		edest = broad;
338 	memcpy(p, edest, 6);
339 	memcpy(p + 6, mac, 6);
340 	p[12] = 8;
341 	p[13] = 6;
342 	p += 14;
343 	p = u32put(p, 0x00010800);
344 	p = u32put(p, 0x06040000 | op);
345 	memcpy(p, mac, 6);
346 	p = u32put(p + 6, myip);
347 	memcpy(p, edest, 6);
348 	p = u32put(p + 6, idest);
349 	ethtx(eth0, txbuf, p - txbuf);
350 }
351 
352 void
arpc(uchar * p)353 arpc(uchar *p)
354 {
355 	p += 14;
356 	if(u32get(p) != 0x00010800 || p[4] != 6 || p[5] != 4 || p[6] != 0)
357 		return;
358 	switch(p[7]){
359 	case 1:
360 		if(myip != 0 && u32get(p + 24) == myip)
361 			arp(2, p + 8, u32get(p + 14));
362 		break;
363 	case 2:
364 		if(tftpip != 0 && u32get(p + 14) == tftpip)
365 			memcpy(tmac, p + 8, 6);
366 		break;
367 	}
368 }
369 
370 udp *
udprx(void)371 udprx(void)
372 {
373 	uchar *p;
374 	ulong v;
375 	udp *u;
376 
377 	u = &urbuf;
378 	for(;; ethnext()){
379 		p = ethrx();
380 		if(p == nil)
381 			return nil;
382 		if(p[12] != 8)
383 			continue;
384 		if(p[13] == 6){
385 			arpc(p);
386 			continue;
387 		}
388 		if(p[13] != 0)
389 			continue;
390 		p += ETHHEAD;
391 		if((p[0] >> 4) != 4 || p[9] != 0x11)
392 			continue;
393 		v = u32get(p + 16);
394 		if(v != (ulong) -1 && v != myip)
395 			continue;
396 		u->idest = v;
397 		u->isrc = u32get(p + 12);
398 		p += (p[0] & 0xf) << 2;
399 		u->sport = p[0] << 8 | p[1];
400 		u->dport = p[2] << 8 | p[3];
401 		u->len = p[4] << 8 | p[5];
402 		if(u->len < 8)
403 			continue;
404 		u->len -= 8;
405 		if(u->len >= sizeof(u->data))
406 			u->len = sizeof(u->data);
407 		memcpy(u->data, p + 8, u->len);
408 		ethnext();
409 		return u;
410 	}
411 }
412 
413 void
arpreq(void)414 arpreq(void)
415 {
416 	uchar *p;
417 
418 	arp(1, nil, tftpip);
419 	timeren(ARPTIMEOUT);
420 	for(;; ethnext()){
421 		p = ethrx();
422 		if(p == nil){
423 			print("ARP timeout\n");
424 			timeren(ARPTIMEOUT);
425 			arp(1, nil, tftpip);
426 		}
427 		if(p[12] != 8 || p[13] != 6)
428 			continue;
429 		arpc(p);
430 		if(tmac[0] != 0xff)
431 			break;
432 	}
433 	timeren(-1);
434 }
435 
436 void
dhcp(ulong * r)437 dhcp(ulong *r)
438 {
439 	udp *u;
440 	uchar *p;
441 	uchar type;
442 
443 	xid = 0xdeadbeef;
444 	tftpip = 0;
445 	dhcppkg(r, DHCPDISCOVER);
446 	timeren(DHCPTIMEOUT);
447 	for(;;){
448 		u = udprx();
449 		if(u == nil){
450 			timeren(DHCPTIMEOUT);
451 			dhcppkg(r, DHCPDISCOVER);
452 			print("DHCP timeout\n");
453 		}
454 		p = u->data;
455 		if(u->dport != 68 || p[0] != 2 || u32get(p + 4) != xid || u32get(p + 236) != 0x63825363)
456 			continue;
457 		p += 240;
458 		type = 0;
459 		dhcpip = 0;
460 		for(; p < u->data + u->len && *p != 0xff; p += 2 + p[1])
461 			switch(*p){
462 			case 53:
463 				type = p[2];
464 				break;
465 			case 54:
466 				dhcpip = u32get(p + 2);
467 				break;
468 			}
469 		if(type != DHCPOFFER)
470 			continue;
471 		p = u->data;
472 		if(p[108] == 0){
473 			print("Offer from %I for %I with no boot file\n", dhcpip, u32get(p + 16));
474 			continue;
475 		}
476 		myip = u32get(p + 16);
477 		tftpip = u32get(p + 20);
478 		memcpy(file, p + 108, 128);
479 		print("Offer from %I for %I with boot file '%s' at %I\n", dhcpip, myip, file, tftpip);
480 		break;
481 	}
482 	timeren(-1);
483 	dhcppkg(r, DHCPREQUEST);
484 }
485 
486 udp *
tftppkg(void)487 tftppkg(void)
488 {
489 	udp *u;
490 
491 	u = &ubuf;
492 	memcpy(u->edest, tmac, 6);
493 	memcpy(u->esrc, mac, 6);
494 	u->idest = tftpip;
495 	u->isrc = myip;
496 	u->sport = 69;
497 	u->dport = 69;
498 	return u;
499 }
500 
501 void
tftp(ulong * r,char * q,uintptr base)502 tftp(ulong *r, char *q, uintptr base)
503 {
504 	udp *u, *v;
505 	uchar *p;
506 	int bn, len;
507 
508 restart:
509 	u = tftppkg();
510 	p = u->data;
511 	*p++ = 0;
512 	*p++ = 1;
513 	do
514 		*p++ = *q;
515 	while(*q++ != 0);
516 	memcpy(p, "octet", 6);
517 	p += 6;
518 	u->len = p - u->data;
519 	udptx(r, u);
520 	timeren(TFTPTIMEOUT);
521 
522 	for(;;){
523 		v = udprx();
524 		if(v == nil){
525 			print("TFTP timeout");
526 			goto restart;
527 		}
528 		if(v->dport != 69 || v->isrc != tftpip || v->idest != myip)
529 			continue;
530 		if(v->data[0] != 0)
531 			continue;
532 		switch(v->data[1]){
533 		case 3:
534 			bn = v->data[2] << 8 | v->data[3];
535 			len = v->len - 4;
536 			if(len < 0)
537 				continue;
538 			if(len > 512)
539 				len = 512;
540 			memcpy((char*)base + ((bn - 1) << 9), v->data + 4, len);
541 			if((bn & 127) == 0)
542 				putc('.');
543 			p = u->data;
544 			*p++ = 0;
545 			*p++ = 4;
546 			*p++ = bn >> 8;
547 			*p = bn;
548 			u->len = 4;
549 			udptx(r, u);
550 			if(len < 512){
551 				putc(10);
552 				timeren(-1);
553 				return;
554 			}
555 			timeren(TFTPTIMEOUT);
556 			break;
557 		case 5:
558 			v->data[v->len - 1] = 0;
559 			print("TFTP error: %s\n", v->data + 4);
560 			timeren(-1);
561 			return;
562 		}
563 	}
564 }
565 
566 int
netboot(void)567 netboot(void)
568 {
569 	ethinit(eth0);
570 	myip = 0;
571 	dhcp(eth0);
572 	arpreq();
573 	tftp(eth0, file, TZERO);
574 	memset((void *) CONF, 0, CONFSIZE);
575 	tftp(eth0, "/cfg/pxe/0ea7deadbeef", CONF);
576 	return 1;
577 }
578