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