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