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