]> git.lizzy.rs Git - plan9front.git/blob - sys/src/boot/efi/pxe.c
kernel: implement portable userinit() and simplify process creation
[plan9front.git] / sys / src / boot / efi / pxe.c
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
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
154 gets(void *x)
155 {
156         uchar *p = x;
157
158         return p[1]<<8 | p[0];
159 }
160
161 static void
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
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
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
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
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
270 pxeclose(void *f)
271 {
272         Tftp *t = f;
273         t->eof = 1;
274 }
275
276
277 static int
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*
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
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
364 parsedhcp(EFI_PXE_BASE_CODE_DHCPV4_PACKET *dhcp)
365 {
366         uchar *p, *e;
367         char *x;
368         int opt;
369         int len;
370
371         memset(mymac, 0, sizeof(mymac));
372         memset(serverip, 0, sizeof(serverip));
373
374         /* DHCPv4 */
375         if(pxe->Mode->UsingIpv6 == 0){
376                 memmove(mymac, dhcp->BootpHwAddr, 6);
377                 memmove(serverip, dhcp->BootpSiAddr, 4);
378                 return;
379         }
380
381         /* DHCPv6 */
382         e = (uchar*)dhcp + sizeof(*dhcp);
383         p = (uchar*)dhcp + 4;
384         while(p+4 <= e){
385                 opt = p[0]<<8 | p[1];
386                 len = p[2]<<8 | p[3];
387                 p += 4;
388                 if(p + len > e)
389                         break;
390                 switch(opt){
391                 case 1: /* Client DUID */
392                         memmove(mymac, p+len-6, 6);
393                         break;
394                 case 59: /* Boot File URL */
395                         for(x = (char*)p; x < (char*)p+len; x++){
396                                 if(*x == '['){
397                                         parseipv6(serverip, x+1);
398                                         break;
399                                 }
400                         }
401                         break;
402                 }
403                 p += len;
404         }
405 }
406
407 int
408 pxeinit(void **pf)
409 {
410         EFI_PXE_BASE_CODE_DHCPV4_PACKET *dhcp;
411         EFI_PXE_BASE_CODE_MODE *mode;
412         EFI_HANDLE *Handles;
413         UINTN Count;
414         int i;
415
416         pxe = nil;
417         Count = 0;
418         Handles = nil;
419         if(eficall(ST->BootServices->LocateHandleBuffer,
420                 ByProtocol, &EFI_PXE_BASE_CODE_PROTOCOL_GUID, nil, &Count, &Handles))
421                 return -1;
422
423         for(i=0; i<Count; i++){
424                 pxe = nil;
425                 if(eficall(ST->BootServices->HandleProtocol,
426                         Handles[i], &EFI_PXE_BASE_CODE_PROTOCOL_GUID, &pxe))
427                         continue;
428                 mode = pxe->Mode;
429                 if(mode == nil || mode->Started == 0)
430                         continue;
431                 if(mode->DhcpAckReceived){
432                         dhcp = (EFI_PXE_BASE_CODE_DHCPV4_PACKET*)mode->DhcpAck;
433                         goto Found;
434                 }
435                 if(mode->PxeReplyReceived){
436                         dhcp = (EFI_PXE_BASE_CODE_DHCPV4_PACKET*)mode->PxeReply;
437                         goto Found;
438                 }
439         }
440         return -1;
441
442 Found:
443         parsedhcp(dhcp);
444         memmove(myip, mode->StationIp, 16);
445
446         open = pxeopen;
447         read = pxeread;
448         close = pxeclose;
449
450         if(pf != nil){
451                 char ini[24];
452
453                 memmove(ini, "/cfg/pxe/", 9);
454                 for(i=0; i<6; i++){
455                         ini[9+i*2+0] = hex[mymac[i] >> 4];
456                         ini[9+i*2+1] = hex[mymac[i] & 0xF];
457                 }
458                 ini[9+12] = '\0';
459                 if((*pf = pxeopen(ini)) == nil)
460                         *pf = pxeopen("/cfg/pxe/default");
461         }
462
463         return 0;
464 }