xref: /plan9-contrib/sys/src/nboot/efi/pxe.c (revision 529c1f209803c78c4f2cda11b13818a57f01c872)
1*529c1f20SDavid du Colombier #include <u.h>
2*529c1f20SDavid du Colombier #include "fns.h"
3*529c1f20SDavid du Colombier #include "efi.h"
4*529c1f20SDavid du Colombier 
5*529c1f20SDavid du Colombier typedef UINT16	EFI_PXE_BASE_CODE_UDP_PORT;
6*529c1f20SDavid du Colombier 
7*529c1f20SDavid du Colombier typedef struct {
8*529c1f20SDavid du Colombier 	UINT8		Addr[4];
9*529c1f20SDavid du Colombier } EFI_IPv4_ADDRESS;
10*529c1f20SDavid du Colombier 
11*529c1f20SDavid du Colombier typedef struct {
12*529c1f20SDavid du Colombier 	UINT8		Addr[16];
13*529c1f20SDavid du Colombier } EFI_IPv6_ADDRESS;
14*529c1f20SDavid du Colombier 
15*529c1f20SDavid du Colombier typedef union {
16*529c1f20SDavid du Colombier 	UINT32			Addr[4];
17*529c1f20SDavid du Colombier 	EFI_IPv4_ADDRESS	v4;
18*529c1f20SDavid du Colombier 	EFI_IPv6_ADDRESS	v6;
19*529c1f20SDavid du Colombier } EFI_IP_ADDRESS;
20*529c1f20SDavid du Colombier 
21*529c1f20SDavid du Colombier typedef struct {
22*529c1f20SDavid du Colombier 	UINT8		Addr[32];
23*529c1f20SDavid du Colombier } EFI_MAC_ADDRESS;
24*529c1f20SDavid du Colombier 
25*529c1f20SDavid du Colombier typedef struct {
26*529c1f20SDavid du Colombier 	UINT8		BootpOpcode;
27*529c1f20SDavid du Colombier 	UINT8		BootpHwType;
28*529c1f20SDavid du Colombier 	UINT8		BootpHwAddrLen;
29*529c1f20SDavid du Colombier 	UINT8		BootpGateHops;
30*529c1f20SDavid du Colombier 	UINT32		BootpIdent;
31*529c1f20SDavid du Colombier 	UINT16		BootpSeconds;
32*529c1f20SDavid du Colombier 	UINT16		BootpFlags;
33*529c1f20SDavid du Colombier 	UINT8		BootpCiAddr[4];
34*529c1f20SDavid du Colombier 	UINT8		BootpYiAddr[4];
35*529c1f20SDavid du Colombier 	UINT8		BootpSiAddr[4];
36*529c1f20SDavid du Colombier 	UINT8		BootpGiAddr[4];
37*529c1f20SDavid du Colombier 	UINT8		BootpHwAddr[16];
38*529c1f20SDavid du Colombier 	UINT8		BootpSrvName[64];
39*529c1f20SDavid du Colombier 	UINT8		BootpBootFile[128];
40*529c1f20SDavid du Colombier 	UINT32		DhcpMagik;
41*529c1f20SDavid du Colombier 	UINT8		DhcpOptions[56];
42*529c1f20SDavid du Colombier } EFI_PXE_BASE_CODE_DHCPV4_PACKET;
43*529c1f20SDavid du Colombier 
44*529c1f20SDavid du Colombier typedef struct {
45*529c1f20SDavid du Colombier 	BOOLEAN		Started;
46*529c1f20SDavid du Colombier 	BOOLEAN		Ipv6Available;
47*529c1f20SDavid du Colombier 	BOOLEAN		Ipv6Supported;
48*529c1f20SDavid du Colombier 	BOOLEAN		UsingIpv6;
49*529c1f20SDavid du Colombier 	BOOLEAN		BisSupported;
50*529c1f20SDavid du Colombier 	BOOLEAN		BisDetected;
51*529c1f20SDavid du Colombier 	BOOLEAN		AutoArp;
52*529c1f20SDavid du Colombier 	BOOLEAN		SendGUID;
53*529c1f20SDavid du Colombier 	BOOLEAN		DhcpDiscoverValid;
54*529c1f20SDavid du Colombier 	BOOLEAN		DhcpAckReceived;
55*529c1f20SDavid du Colombier 	BOOLEAN		ProxyOfferReceived;
56*529c1f20SDavid du Colombier 	BOOLEAN		PxeDiscoverValid;
57*529c1f20SDavid du Colombier 	BOOLEAN		PxeReplyReceived;
58*529c1f20SDavid du Colombier 	BOOLEAN		PxeBisReplyReceived;
59*529c1f20SDavid du Colombier 	BOOLEAN		IcmpErrorReceived;
60*529c1f20SDavid du Colombier 	BOOLEAN		TftpErrorReceived;
61*529c1f20SDavid du Colombier 	BOOLEAN		MakeCallbacks;
62*529c1f20SDavid du Colombier 
63*529c1f20SDavid du Colombier 	UINT8		TTL;
64*529c1f20SDavid du Colombier 	UINT8		ToS;
65*529c1f20SDavid du Colombier 
66*529c1f20SDavid du Colombier 	UINT8		Reserved;
67*529c1f20SDavid du Colombier 
68*529c1f20SDavid du Colombier 	UINT8		StationIp[16];
69*529c1f20SDavid du Colombier 	UINT8		SubnetMask[16];
70*529c1f20SDavid du Colombier 
71*529c1f20SDavid du Colombier 	UINT8		DhcpDiscover[1472];
72*529c1f20SDavid du Colombier 	UINT8		DhcpAck[1472];
73*529c1f20SDavid du Colombier 	UINT8		ProxyOffer[1472];
74*529c1f20SDavid du Colombier 	UINT8		PxeDiscover[1472];
75*529c1f20SDavid du Colombier 	UINT8		PxeReply[1472];
76*529c1f20SDavid du Colombier 	UINT8		PxeBisReply[1472];
77*529c1f20SDavid du Colombier 
78*529c1f20SDavid du Colombier } EFI_PXE_BASE_CODE_MODE;
79*529c1f20SDavid du Colombier 
80*529c1f20SDavid du Colombier typedef struct {
81*529c1f20SDavid du Colombier 	UINT64		Revision;
82*529c1f20SDavid du Colombier 	void		*Start;
83*529c1f20SDavid du Colombier 	void		*Stop;
84*529c1f20SDavid du Colombier 	void		*Dhcp;
85*529c1f20SDavid du Colombier 	void		*Discover;
86*529c1f20SDavid du Colombier 	void		*Mtftp;
87*529c1f20SDavid du Colombier 	void		*UdpWrite;
88*529c1f20SDavid du Colombier 	void		*UdpRead;
89*529c1f20SDavid du Colombier 	void		*SetIpFilter;
90*529c1f20SDavid du Colombier 	void		*Arp;
91*529c1f20SDavid du Colombier 	void		*SetParameters;
92*529c1f20SDavid du Colombier 	void		*SetStationIp;
93*529c1f20SDavid du Colombier 	void		*SetPackets;
94*529c1f20SDavid du Colombier 	EFI_PXE_BASE_CODE_MODE	*Mode;
95*529c1f20SDavid du Colombier } EFI_PXE_BASE_CODE_PROTOCOL;
96*529c1f20SDavid du Colombier 
97*529c1f20SDavid du Colombier 
98*529c1f20SDavid du Colombier enum {
99*529c1f20SDavid du Colombier 	Tftp_READ	= 1,
100*529c1f20SDavid du Colombier 	Tftp_WRITE	= 2,
101*529c1f20SDavid du Colombier 	Tftp_DATA	= 3,
102*529c1f20SDavid du Colombier 	Tftp_ACK	= 4,
103*529c1f20SDavid du Colombier 	Tftp_ERROR	= 5,
104*529c1f20SDavid du Colombier 	Tftp_OACK	= 6,
105*529c1f20SDavid du Colombier 
106*529c1f20SDavid du Colombier 	TftpPort	= 69,
107*529c1f20SDavid du Colombier 
108*529c1f20SDavid du Colombier 	Segsize		= 512,
109*529c1f20SDavid du Colombier };
110*529c1f20SDavid du Colombier 
111*529c1f20SDavid du Colombier static
112*529c1f20SDavid du Colombier EFI_GUID EFI_PXE_BASE_CODE_PROTOCOL_GUID = {
113*529c1f20SDavid du Colombier 	0x03C4E603, 0xAC28, 0x11D3,
114*529c1f20SDavid du Colombier 	0x9A, 0x2D, 0x00, 0x90,
115*529c1f20SDavid du Colombier 	0x27, 0x3F, 0xC1, 0x4D,
116*529c1f20SDavid du Colombier };
117*529c1f20SDavid du Colombier 
118*529c1f20SDavid du Colombier static
119*529c1f20SDavid du Colombier EFI_PXE_BASE_CODE_PROTOCOL *pxe;
120*529c1f20SDavid du Colombier 
121*529c1f20SDavid du Colombier static uchar mymac[6];
122*529c1f20SDavid du Colombier static uchar myip[16];
123*529c1f20SDavid du Colombier static uchar serverip[16];
124*529c1f20SDavid du Colombier 
125*529c1f20SDavid du Colombier typedef struct Tftp Tftp;
126*529c1f20SDavid du Colombier struct Tftp
127*529c1f20SDavid du Colombier {
128*529c1f20SDavid du Colombier 	EFI_IP_ADDRESS sip;
129*529c1f20SDavid du Colombier 	EFI_IP_ADDRESS dip;
130*529c1f20SDavid du Colombier 
131*529c1f20SDavid du Colombier 	EFI_PXE_BASE_CODE_UDP_PORT sport;
132*529c1f20SDavid du Colombier 	EFI_PXE_BASE_CODE_UDP_PORT dport;
133*529c1f20SDavid du Colombier 
134*529c1f20SDavid du Colombier 	char *rp;
135*529c1f20SDavid du Colombier 	char *ep;
136*529c1f20SDavid du Colombier 
137*529c1f20SDavid du Colombier 	int seq;
138*529c1f20SDavid du Colombier 	int eof;
139*529c1f20SDavid du Colombier 
140*529c1f20SDavid du Colombier 	char pkt[2+2+Segsize];
141*529c1f20SDavid du Colombier 	char nul;
142*529c1f20SDavid du Colombier };
143*529c1f20SDavid du Colombier 
144*529c1f20SDavid du Colombier static void
puts(void * x,ushort v)145*529c1f20SDavid du Colombier puts(void *x, ushort v)
146*529c1f20SDavid du Colombier {
147*529c1f20SDavid du Colombier 	uchar *p = x;
148*529c1f20SDavid du Colombier 
149*529c1f20SDavid du Colombier 	p[1] = (v>>8) & 0xFF;
150*529c1f20SDavid du Colombier 	p[0] = v & 0xFF;
151*529c1f20SDavid du Colombier }
152*529c1f20SDavid du Colombier 
153*529c1f20SDavid du Colombier static ushort
gets(void * x)154*529c1f20SDavid du Colombier gets(void *x)
155*529c1f20SDavid du Colombier {
156*529c1f20SDavid du Colombier 	uchar *p = x;
157*529c1f20SDavid du Colombier 
158*529c1f20SDavid du Colombier 	return p[1]<<8 | p[0];
159*529c1f20SDavid du Colombier }
160*529c1f20SDavid du Colombier 
161*529c1f20SDavid du Colombier static void
hnputs(void * x,ushort v)162*529c1f20SDavid du Colombier hnputs(void *x, ushort v)
163*529c1f20SDavid du Colombier {
164*529c1f20SDavid du Colombier 	uchar *p = x;
165*529c1f20SDavid du Colombier 
166*529c1f20SDavid du Colombier 	p[0] = (v>>8) & 0xFF;
167*529c1f20SDavid du Colombier 	p[1] = v & 0xFF;
168*529c1f20SDavid du Colombier }
169*529c1f20SDavid du Colombier 
170*529c1f20SDavid du Colombier static ushort
nhgets(void * x)171*529c1f20SDavid du Colombier nhgets(void *x)
172*529c1f20SDavid du Colombier {
173*529c1f20SDavid du Colombier 	uchar *p = x;
174*529c1f20SDavid du Colombier 
175*529c1f20SDavid du Colombier 	return p[0]<<8 | p[1];
176*529c1f20SDavid du Colombier }
177*529c1f20SDavid du Colombier 
178*529c1f20SDavid du Colombier enum {
179*529c1f20SDavid du Colombier 	ANY_SRC_IP	= 0x0001,
180*529c1f20SDavid du Colombier 	ANY_SRC_PORT	= 0x0002,
181*529c1f20SDavid du Colombier 	ANY_DEST_IP	= 0x0004,
182*529c1f20SDavid du Colombier 	ANY_DEST_PORT	= 0x0008,
183*529c1f20SDavid du Colombier 	USE_FILTER	= 0x0010,
184*529c1f20SDavid du Colombier 	MAY_FRAGMENT	= 0x0020,
185*529c1f20SDavid du Colombier };
186*529c1f20SDavid du Colombier 
187*529c1f20SDavid du Colombier static int
udpread(EFI_IP_ADDRESS * sip,EFI_IP_ADDRESS * dip,EFI_PXE_BASE_CODE_UDP_PORT * sport,EFI_PXE_BASE_CODE_UDP_PORT dport,int * len,void * data)188*529c1f20SDavid du Colombier udpread(EFI_IP_ADDRESS *sip, EFI_IP_ADDRESS *dip,
189*529c1f20SDavid du Colombier 	EFI_PXE_BASE_CODE_UDP_PORT *sport,
190*529c1f20SDavid du Colombier 	EFI_PXE_BASE_CODE_UDP_PORT dport,
191*529c1f20SDavid du Colombier 	int *len, void *data)
192*529c1f20SDavid du Colombier {
193*529c1f20SDavid du Colombier 	UINTN size;
194*529c1f20SDavid du Colombier 
195*529c1f20SDavid du Colombier 	size = *len;
196*529c1f20SDavid du Colombier 	if(eficall(pxe->UdpRead, pxe, (UINTN)ANY_SRC_PORT, dip, &dport, sip, sport, nil, nil, &size, data))
197*529c1f20SDavid du Colombier 		return -1;
198*529c1f20SDavid du Colombier 
199*529c1f20SDavid du Colombier 	*len = size;
200*529c1f20SDavid du Colombier 	return 0;
201*529c1f20SDavid du Colombier }
202*529c1f20SDavid du Colombier 
203*529c1f20SDavid du Colombier static int
udpwrite(EFI_IP_ADDRESS * dip,EFI_PXE_BASE_CODE_UDP_PORT sport,EFI_PXE_BASE_CODE_UDP_PORT dport,int len,void * data)204*529c1f20SDavid du Colombier udpwrite(EFI_IP_ADDRESS *dip,
205*529c1f20SDavid du Colombier 	EFI_PXE_BASE_CODE_UDP_PORT sport,
206*529c1f20SDavid du Colombier 	EFI_PXE_BASE_CODE_UDP_PORT dport,
207*529c1f20SDavid du Colombier 	int len, void *data)
208*529c1f20SDavid du Colombier {
209*529c1f20SDavid du Colombier 	UINTN size;
210*529c1f20SDavid du Colombier 
211*529c1f20SDavid du Colombier 	size = len;
212*529c1f20SDavid du Colombier 	if(eficall(pxe->UdpWrite, pxe, (UINTN)MAY_FRAGMENT, dip, &dport, nil, nil, &sport, nil, nil, &size, data))
213*529c1f20SDavid du Colombier 		return -1;
214*529c1f20SDavid du Colombier 
215*529c1f20SDavid du Colombier 	return 0;
216*529c1f20SDavid du Colombier }
217*529c1f20SDavid du Colombier 
218*529c1f20SDavid du Colombier static int
pxeread(void * f,void * data,int len)219*529c1f20SDavid du Colombier pxeread(void *f, void *data, int len)
220*529c1f20SDavid du Colombier {
221*529c1f20SDavid du Colombier 	Tftp *t = f;
222*529c1f20SDavid du Colombier 	int seq, n;
223*529c1f20SDavid du Colombier 
224*529c1f20SDavid du Colombier 	while(!t->eof && t->rp >= t->ep){
225*529c1f20SDavid du Colombier 		for(;;){
226*529c1f20SDavid du Colombier 			n = sizeof(t->pkt);
227*529c1f20SDavid du Colombier 			if(udpread(&t->dip, &t->sip, &t->dport, t->sport, &n, t->pkt))
228*529c1f20SDavid du Colombier 				continue;
229*529c1f20SDavid du Colombier 			if(n >= 4)
230*529c1f20SDavid du Colombier 				break;
231*529c1f20SDavid du Colombier 		}
232*529c1f20SDavid du Colombier 		switch(nhgets(t->pkt)){
233*529c1f20SDavid du Colombier 		case Tftp_DATA:
234*529c1f20SDavid du Colombier 			seq = nhgets(t->pkt+2);
235*529c1f20SDavid du Colombier 			if(seq > t->seq){
236*529c1f20SDavid du Colombier 				putc('?');
237*529c1f20SDavid du Colombier 				continue;
238*529c1f20SDavid du Colombier 			}
239*529c1f20SDavid du Colombier 			hnputs(t->pkt, Tftp_ACK);
240*529c1f20SDavid du Colombier 			while(udpwrite(&t->dip, t->sport, t->dport, 4, t->pkt))
241*529c1f20SDavid du Colombier 				putc('!');
242*529c1f20SDavid du Colombier 			if(seq < t->seq){
243*529c1f20SDavid du Colombier 				putc('@');
244*529c1f20SDavid du Colombier 				continue;
245*529c1f20SDavid du Colombier 			}
246*529c1f20SDavid du Colombier 			t->seq = seq+1;
247*529c1f20SDavid du Colombier 			n -= 4;
248*529c1f20SDavid du Colombier 			t->rp = t->pkt + 4;
249*529c1f20SDavid du Colombier 			t->ep = t->rp + n;
250*529c1f20SDavid du Colombier 			t->eof = n < Segsize;
251*529c1f20SDavid du Colombier 			break;
252*529c1f20SDavid du Colombier 		case Tftp_ERROR:
253*529c1f20SDavid du Colombier 			print(t->pkt+4);
254*529c1f20SDavid du Colombier 			print("\n");
255*529c1f20SDavid du Colombier 		default:
256*529c1f20SDavid du Colombier 			t->eof = 1;
257*529c1f20SDavid du Colombier 			return -1;
258*529c1f20SDavid du Colombier 		}
259*529c1f20SDavid du Colombier 		break;
260*529c1f20SDavid du Colombier 	}
261*529c1f20SDavid du Colombier 	n = t->ep - t->rp;
262*529c1f20SDavid du Colombier 	if(len > n)
263*529c1f20SDavid du Colombier 		len = n;
264*529c1f20SDavid du Colombier 	memmove(data, t->rp, len);
265*529c1f20SDavid du Colombier 	t->rp += len;
266*529c1f20SDavid du Colombier 	return len;
267*529c1f20SDavid du Colombier }
268*529c1f20SDavid du Colombier 
269*529c1f20SDavid du Colombier static void
pxeclose(void * f)270*529c1f20SDavid du Colombier pxeclose(void *f)
271*529c1f20SDavid du Colombier {
272*529c1f20SDavid du Colombier 	Tftp *t = f;
273*529c1f20SDavid du Colombier 	t->eof = 1;
274*529c1f20SDavid du Colombier }
275*529c1f20SDavid du Colombier 
276*529c1f20SDavid du Colombier 
277*529c1f20SDavid du Colombier static int
tftpopen(Tftp * t,char * path)278*529c1f20SDavid du Colombier tftpopen(Tftp *t, char *path)
279*529c1f20SDavid du Colombier {
280*529c1f20SDavid du Colombier 	static EFI_PXE_BASE_CODE_UDP_PORT xport = 6666;
281*529c1f20SDavid du Colombier 	int r, n;
282*529c1f20SDavid du Colombier 	char *p;
283*529c1f20SDavid du Colombier 
284*529c1f20SDavid du Colombier 	t->sport = xport++;
285*529c1f20SDavid du Colombier 	t->dport = 0;
286*529c1f20SDavid du Colombier 	t->rp = t->ep = 0;
287*529c1f20SDavid du Colombier 	t->seq = 1;
288*529c1f20SDavid du Colombier 	t->eof = 0;
289*529c1f20SDavid du Colombier 	t->nul = 0;
290*529c1f20SDavid du Colombier 	p = t->pkt;
291*529c1f20SDavid du Colombier 	hnputs(p, Tftp_READ); p += 2;
292*529c1f20SDavid du Colombier 	n = strlen(path)+1;
293*529c1f20SDavid du Colombier 	memmove(p, path, n); p += n;
294*529c1f20SDavid du Colombier 	memmove(p, "octet", 6); p += 6;
295*529c1f20SDavid du Colombier 	n = p - t->pkt;
296*529c1f20SDavid du Colombier 	for(;;){
297*529c1f20SDavid du Colombier 		if(r = udpwrite(&t->dip, t->sport, TftpPort, n, t->pkt))
298*529c1f20SDavid du Colombier 			break;
299*529c1f20SDavid du Colombier 		if(r = pxeread(t, 0, 0))
300*529c1f20SDavid du Colombier 			break;
301*529c1f20SDavid du Colombier 		return 0;
302*529c1f20SDavid du Colombier 	}
303*529c1f20SDavid du Colombier 	pxeclose(t);
304*529c1f20SDavid du Colombier 	return r;
305*529c1f20SDavid du Colombier }
306*529c1f20SDavid du Colombier 
307*529c1f20SDavid du Colombier static void*
pxeopen(char * name)308*529c1f20SDavid du Colombier pxeopen(char *name)
309*529c1f20SDavid du Colombier {
310*529c1f20SDavid du Colombier 	static uchar buf[sizeof(Tftp)+8];
311*529c1f20SDavid du Colombier 	Tftp *t = (Tftp*)((uintptr)(buf+7)&~7);
312*529c1f20SDavid du Colombier 
313*529c1f20SDavid du Colombier 	memset(t, 0, sizeof(Tftp));
314*529c1f20SDavid du Colombier 	memmove(&t->sip, myip, sizeof(myip));
315*529c1f20SDavid du Colombier 	memmove(&t->dip, serverip, sizeof(serverip));
316*529c1f20SDavid du Colombier 	if(tftpopen(t, name))
317*529c1f20SDavid du Colombier 		return nil;
318*529c1f20SDavid du Colombier 	return t;
319*529c1f20SDavid du Colombier }
320*529c1f20SDavid du Colombier 
321*529c1f20SDavid du Colombier static int
parseipv6(uchar to[16],char * from)322*529c1f20SDavid du Colombier parseipv6(uchar to[16], char *from)
323*529c1f20SDavid du Colombier {
324*529c1f20SDavid du Colombier 	int i, dig, elipsis;
325*529c1f20SDavid du Colombier 	char *p;
326*529c1f20SDavid du Colombier 
327*529c1f20SDavid du Colombier 	elipsis = 0;
328*529c1f20SDavid du Colombier 	memset(to, 0, 16);
329*529c1f20SDavid du Colombier 	for(i = 0; i < 16; i += 2){
330*529c1f20SDavid du Colombier 		dig = 0;
331*529c1f20SDavid du Colombier 		for(p = from;; p++){
332*529c1f20SDavid du Colombier 			if(*p >= '0' && *p <= '9')
333*529c1f20SDavid du Colombier 				dig = (dig << 4) | (*p - '0');
334*529c1f20SDavid du Colombier 			else if(*p >= 'a' && *p <= 'f')
335*529c1f20SDavid du Colombier 				dig = (dig << 4) | (*p - 'a'+10);
336*529c1f20SDavid du Colombier 			else if(*p >= 'A' && *p <= 'F')
337*529c1f20SDavid du Colombier 				dig = (dig << 4) | (*p - 'A'+10);
338*529c1f20SDavid du Colombier 			else
339*529c1f20SDavid du Colombier 				break;
340*529c1f20SDavid du Colombier 			if(dig > 0xFFFF)
341*529c1f20SDavid du Colombier 				return -1;
342*529c1f20SDavid du Colombier 		}
343*529c1f20SDavid du Colombier 		to[i]   = dig>>8;
344*529c1f20SDavid du Colombier 		to[i+1] = dig;
345*529c1f20SDavid du Colombier 		if(*p == ':'){
346*529c1f20SDavid du Colombier 			if(*++p == ':'){	/* :: is elided zero short(s) */
347*529c1f20SDavid du Colombier 				if (elipsis)
348*529c1f20SDavid du Colombier 					return -1;	/* second :: */
349*529c1f20SDavid du Colombier 				elipsis = i+2;
350*529c1f20SDavid du Colombier 				p++;
351*529c1f20SDavid du Colombier 			}
352*529c1f20SDavid du Colombier 		} else if (p == from)
353*529c1f20SDavid du Colombier 			break;
354*529c1f20SDavid du Colombier 		from = p;
355*529c1f20SDavid du Colombier 	}
356*529c1f20SDavid du Colombier 	if(i < 16){
357*529c1f20SDavid du Colombier 		memmove(&to[elipsis+16-i], &to[elipsis], i-elipsis);
358*529c1f20SDavid du Colombier 		memset(&to[elipsis], 0, 16-i);
359*529c1f20SDavid du Colombier 	}
360*529c1f20SDavid du Colombier 	return 0;
361*529c1f20SDavid du Colombier }
362*529c1f20SDavid du Colombier 
363*529c1f20SDavid du Colombier static void
parsedhcp(EFI_PXE_BASE_CODE_DHCPV4_PACKET * dhcp)364*529c1f20SDavid du Colombier parsedhcp(EFI_PXE_BASE_CODE_DHCPV4_PACKET *dhcp)
365*529c1f20SDavid du Colombier {
366*529c1f20SDavid du Colombier 	uchar *p, *e;
367*529c1f20SDavid du Colombier 	char *x;
368*529c1f20SDavid du Colombier 	int opt;
369*529c1f20SDavid du Colombier 	int len;
370*529c1f20SDavid du Colombier 	uint type;
371*529c1f20SDavid du Colombier 
372*529c1f20SDavid du Colombier 	memset(mymac, 0, sizeof(mymac));
373*529c1f20SDavid du Colombier 	memset(serverip, 0, sizeof(serverip));
374*529c1f20SDavid du Colombier 
375*529c1f20SDavid du Colombier 	/* DHCPv4 */
376*529c1f20SDavid du Colombier 	if(pxe->Mode->UsingIpv6 == 0){
377*529c1f20SDavid du Colombier 		memmove(mymac, dhcp->BootpHwAddr, 6);
378*529c1f20SDavid du Colombier 		memmove(serverip, dhcp->BootpSiAddr, 4);
379*529c1f20SDavid du Colombier 		return;
380*529c1f20SDavid du Colombier 	}
381*529c1f20SDavid du Colombier 
382*529c1f20SDavid du Colombier 	/* DHCPv6 */
383*529c1f20SDavid du Colombier 
384*529c1f20SDavid du Colombier 	/*
385*529c1f20SDavid du Colombier 	 * some UEFI implementations use random UUID based DUID instead of
386*529c1f20SDavid du Colombier 	 * ethernet address, but use ethernet derived link-local addresses.
387*529c1f20SDavid du Colombier 	 * so extract the MAC from our IPv6 address as a fallback.
388*529c1f20SDavid du Colombier 	 */
389*529c1f20SDavid du Colombier 	p = pxe->Mode->StationIp;
390*529c1f20SDavid du Colombier 	mymac[0] = p[8] ^ 2;
391*529c1f20SDavid du Colombier 	mymac[1] = p[9];
392*529c1f20SDavid du Colombier 	mymac[2] = p[10];
393*529c1f20SDavid du Colombier 	mymac[3] = p[13];
394*529c1f20SDavid du Colombier 	mymac[4] = p[14];
395*529c1f20SDavid du Colombier 	mymac[5] = p[15];
396*529c1f20SDavid du Colombier 
397*529c1f20SDavid du Colombier 	e = (uchar*)dhcp + sizeof(*dhcp);
398*529c1f20SDavid du Colombier 	p = (uchar*)dhcp + 4;
399*529c1f20SDavid du Colombier 	while(p+4 <= e){
400*529c1f20SDavid du Colombier 		opt = p[0]<<8 | p[1];
401*529c1f20SDavid du Colombier 		len = p[2]<<8 | p[3];
402*529c1f20SDavid du Colombier 		p += 4;
403*529c1f20SDavid du Colombier 		if(p + len > e)
404*529c1f20SDavid du Colombier 			break;
405*529c1f20SDavid du Colombier 		switch(opt){
406*529c1f20SDavid du Colombier 		case 1:	/* Client DUID */
407*529c1f20SDavid du Colombier 			if(len < 4+6)
408*529c1f20SDavid du Colombier 				break;
409*529c1f20SDavid du Colombier 			type = p[0]<<24 | p[1]<<16 | p[2]<<8 | p[3];
410*529c1f20SDavid du Colombier 			switch(type){
411*529c1f20SDavid du Colombier 			case 0x00010001:
412*529c1f20SDavid du Colombier 			case 0x00030001:
413*529c1f20SDavid du Colombier 				memmove(mymac, p+len-6, 6);
414*529c1f20SDavid du Colombier 				break;
415*529c1f20SDavid du Colombier 			}
416*529c1f20SDavid du Colombier 			break;
417*529c1f20SDavid du Colombier 		case 59: /* Boot File URL */
418*529c1f20SDavid du Colombier 			for(x = (char*)p; x < (char*)p+len; x++){
419*529c1f20SDavid du Colombier 				if(*x == '['){
420*529c1f20SDavid du Colombier 					parseipv6(serverip, x+1);
421*529c1f20SDavid du Colombier 					break;
422*529c1f20SDavid du Colombier 				}
423*529c1f20SDavid du Colombier 			}
424*529c1f20SDavid du Colombier 			break;
425*529c1f20SDavid du Colombier 		}
426*529c1f20SDavid du Colombier 		p += len;
427*529c1f20SDavid du Colombier 	}
428*529c1f20SDavid du Colombier }
429*529c1f20SDavid du Colombier 
430*529c1f20SDavid du Colombier int
pxeinit(void ** pf)431*529c1f20SDavid du Colombier pxeinit(void **pf)
432*529c1f20SDavid du Colombier {
433*529c1f20SDavid du Colombier 	EFI_PXE_BASE_CODE_DHCPV4_PACKET	*dhcp;
434*529c1f20SDavid du Colombier 	EFI_PXE_BASE_CODE_MODE *mode;
435*529c1f20SDavid du Colombier 	EFI_HANDLE *Handles;
436*529c1f20SDavid du Colombier 	UINTN Count;
437*529c1f20SDavid du Colombier 	int i;
438*529c1f20SDavid du Colombier 
439*529c1f20SDavid du Colombier 	pxe = nil;
440*529c1f20SDavid du Colombier 	Count = 0;
441*529c1f20SDavid du Colombier 	Handles = nil;
442*529c1f20SDavid du Colombier 	if(eficall(ST->BootServices->LocateHandleBuffer,
443*529c1f20SDavid du Colombier 		ByProtocol, &EFI_PXE_BASE_CODE_PROTOCOL_GUID, nil, &Count, &Handles))
444*529c1f20SDavid du Colombier 		return -1;
445*529c1f20SDavid du Colombier 
446*529c1f20SDavid du Colombier 	for(i=0; i<Count; i++){
447*529c1f20SDavid du Colombier 		pxe = nil;
448*529c1f20SDavid du Colombier 		if(eficall(ST->BootServices->HandleProtocol,
449*529c1f20SDavid du Colombier 			Handles[i], &EFI_PXE_BASE_CODE_PROTOCOL_GUID, &pxe))
450*529c1f20SDavid du Colombier 			continue;
451*529c1f20SDavid du Colombier 		mode = pxe->Mode;
452*529c1f20SDavid du Colombier 		if(mode == nil || mode->Started == 0)
453*529c1f20SDavid du Colombier 			continue;
454*529c1f20SDavid du Colombier 		if(mode->DhcpAckReceived){
455*529c1f20SDavid du Colombier 			dhcp = (EFI_PXE_BASE_CODE_DHCPV4_PACKET*)mode->DhcpAck;
456*529c1f20SDavid du Colombier 			goto Found;
457*529c1f20SDavid du Colombier 		}
458*529c1f20SDavid du Colombier 		if(mode->PxeReplyReceived){
459*529c1f20SDavid du Colombier 			dhcp = (EFI_PXE_BASE_CODE_DHCPV4_PACKET*)mode->PxeReply;
460*529c1f20SDavid du Colombier 			goto Found;
461*529c1f20SDavid du Colombier 		}
462*529c1f20SDavid du Colombier 	}
463*529c1f20SDavid du Colombier 	return -1;
464*529c1f20SDavid du Colombier 
465*529c1f20SDavid du Colombier Found:
466*529c1f20SDavid du Colombier 	parsedhcp(dhcp);
467*529c1f20SDavid du Colombier 	memmove(myip, mode->StationIp, 16);
468*529c1f20SDavid du Colombier 
469*529c1f20SDavid du Colombier 	open = pxeopen;
470*529c1f20SDavid du Colombier 	read = pxeread;
471*529c1f20SDavid du Colombier 	close = pxeclose;
472*529c1f20SDavid du Colombier 
473*529c1f20SDavid du Colombier 	if(pf != nil){
474*529c1f20SDavid du Colombier 		char ini[24];
475*529c1f20SDavid du Colombier 
476*529c1f20SDavid du Colombier 		memmove(ini, "/cfg/pxe/", 9);
477*529c1f20SDavid du Colombier 		for(i=0; i<6; i++){
478*529c1f20SDavid du Colombier 			ini[9+i*2+0] = hex[mymac[i] >> 4];
479*529c1f20SDavid du Colombier 			ini[9+i*2+1] = hex[mymac[i] & 0xF];
480*529c1f20SDavid du Colombier 		}
481*529c1f20SDavid du Colombier 		ini[9+12] = '\0';
482*529c1f20SDavid du Colombier 		if((*pf = pxeopen(ini)) == nil)
483*529c1f20SDavid du Colombier 			*pf = pxeopen("/cfg/pxe/default");
484*529c1f20SDavid du Colombier 	}
485*529c1f20SDavid du Colombier 
486*529c1f20SDavid du Colombier 	return 0;
487*529c1f20SDavid du Colombier }
488