Imported Upstream version 3.12
[debian/elilo] / fs / netfs.c
1 /*
2  *  Copyright (C) 2001-2009 Hewlett-Packard Co.
3  *      Contributed by Stephane Eranian <eranian@hpl.hp.com>
4  *      Contributed by Jason Fleischli <jason.fleischli@hp.com>
5  *  Copyright (C) 2006-2009 Intel Corporation
6  *      Contributed by Fenghua Yu <fenghua.yu@intel.com>
7  *      Contributed by Bibo Mao <bibo.mao@intel.com>
8  *      Contributed by Chandramouli Narayanan <mouli@linux.intel.com>
9  *
10  * This file is part of the ELILO, the EFI Linux boot loader.
11  *
12  *  ELILO is free software; you can redistribute it and/or modify
13  *  it under the terms of the GNU General Public License as published by
14  *  the Free Software Foundation; either version 2, or (at your option)
15  *  any later version.
16  *
17  *  ELILO is distributed in the hope that it will be useful,
18  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
19  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  *  GNU General Public License for more details.
21  *
22  *  You should have received a copy of the GNU General Public License
23  *  along with ELILO; see the file COPYING.  If not, write to the Free
24  *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
25  *  02111-1307, USA.
26  *
27  * Please check out the elilo.txt for complete documentation on how
28  * to use this program.
29  */
30
31 #include <efi.h>
32 #include <efilib.h>
33
34 #include "fs/netfs.h"
35
36 #include "elilo.h"
37
38 #define FS_NAME L"netfs"
39
40 #define NETFS_DEFAULT_BUFSIZE           16*MB
41 #define NETFS_DEFAULT_BUFSIZE_INC        8*MB
42 #define NETFS_DEFAULT_BLOCKSIZE         1024    /* setting to zero is supposed to default the underlying */
43                                                 /* pxe implementation to largest blocksize supported,... */
44                                                 /* in reality on original older efi implementations its */
45                                                 /* never set causing the pxe transfer to timeout. */
46                                                 /* the spec defines the minimum supported blocksize default */
47                                                 /* to be 512 bytes... a bit extreme, 1024 should work for */
48                                                 /* everything  */
49
50 #define NETFS_DEFAULT_SERVER_TYPE       EFI_PXE_BASE_CODE_BOOT_TYPE_BOOTSTRAP
51 #define NETFS_FD_MAX    2
52
53 typedef struct _netfs_fd {
54         struct _netfs_fd *next;
55         
56         CHAR8  *netbuf;
57         UINT64 netbuf_maxsize;  /* currently allocated buffer */
58         UINTN  netbuf_size;     /* number of bytes currently used in the buffer */
59         UINT64 netbuf_pos;      /* current position in the buffer */
60         BOOLEAN is_valid;       /* avoid conflicting opens */
61         BOOLEAN netbuf_reuse;
62
63         CHAR16 last_file[FILENAME_MAXLEN];
64 } netfs_fd_t;
65
66
67 typedef struct {
68         EFI_PXE_BASE_CODE *pxe;
69         EFI_HANDLE        dev;          /* handle to device we're attached to */
70         BOOLEAN           using_pxe;    /* true if downloaded using the PXE protocol vs. regular DHCP */
71
72         EFI_IP_ADDRESS  srv_ip;
73         EFI_IP_ADDRESS  cln_ip;
74         EFI_IP_ADDRESS  gw_ip;
75         EFI_IP_ADDRESS  netmask;
76         UINT8           hw_addr[16];
77
78         netfs_fd_t      fd_tab[NETFS_FD_MAX];
79         netfs_fd_t      *free_fd;
80         UINTN           free_fd_count;
81
82 } netfs_priv_state_t;
83
84 #define NETFS_F2FD(l,f)         (UINTN)((f)-(l)->fd_tab)
85 #define NETFS_FD2F(l,fd)        ((l)->fd_tab+fd)
86 #define NETFS_F_INVALID(f)      ((f)->is_valid == FALSE)
87
88
89 typedef union {
90         netfs_interface_t pub_intf;
91         struct {
92                 netfs_interface_t  pub_intf;
93                 netfs_priv_state_t priv_data;
94         } netfs_priv;
95 } netfs_t;
96
97 #define FS_PRIVATE(n)   (&(((netfs_t *)n)->netfs_priv.priv_data))
98
99 typedef union {
100         EFI_HANDLE *dev;
101         netfs_t  *intf;
102 } dev_tab_t;
103
104 static dev_tab_t *dev_tab;      /* holds all devices we found */
105 static UINTN ndev;              /* how many entries in dev_tab */
106
107 static EFI_GUID NetFsProtocol = NETFS_PROTOCOL;
108
109
110 #if 0
111 static EFI_PXE_BASE_CODE_CALLBACK_STATUS
112 netfs_callback_func(
113     IN EFI_PXE_BASE_CODE_CALLBACK *this,
114     IN EFI_PXE_BASE_CODE_FUNCTION function,
115     IN BOOLEAN received,
116     IN UINT32 packet_len,
117     IN EFI_PXE_BASE_CODE_PACKET *packet OPTIONAL
118 )
119 {
120         Print(L"netfs_callback called received=%d packet_len=%d\n", received, packet_len);
121         return EFI_ABORTED;
122 }
123
124 static EFI_PXE_BASE_CODE_CALLBACK netfs_callback = {
125     EFI_PXE_BASE_CODE_CALLBACK_INTERFACE_REVISION,
126     &netfs_callback_func
127 };
128 #endif
129
130 static netfs_fd_t *
131 netfs_fd_alloc(netfs_priv_state_t *nfs, CHAR16 *name)
132 {
133         netfs_fd_t *tmp = NULL, *prev = NULL, *match;
134         UINT8 netbuf_reuse = 0;
135
136         if (nfs->free_fd == NULL) {
137                 ERR_PRT((L"out of file descriptor"));
138                 return NULL;
139         }
140         match = nfs->free_fd; 
141         for (tmp = nfs->free_fd; tmp; tmp = tmp->next) {
142                 if (!StrCmp(name, tmp->last_file)) {
143                         DBG_PRT((L"Using cached file %s netbuf_size=%d", tmp->last_file, tmp->netbuf_size));
144                         netbuf_reuse = 1;
145                         match = tmp;
146                         break;
147                 }
148                 prev = tmp;
149         }
150         /* indicate whether or not we got a match in caching */
151         match->netbuf_reuse = netbuf_reuse;
152
153         if (match == nfs->free_fd) 
154                 nfs->free_fd = match->next;     
155         else 
156                 prev->next  = match->next;
157
158         nfs->free_fd_count--;
159
160         return match;
161 }
162
163 static VOID
164 netfs_fd_free(netfs_priv_state_t *nfs, netfs_fd_t *f)
165 {
166         if (f == NULL) {
167                 ERR_PRT((L"invalid fd"));
168                 return;
169         }
170         f->next    = nfs->free_fd;
171
172         /* we keep the netbuf, in case we can reuse it */
173         f->is_valid = FALSE;
174
175         nfs->free_fd = f;
176         nfs->free_fd_count++;
177
178         if (nfs->free_fd_count > NETFS_FD_MAX) {
179                 ERR_PRT((L"too many free descriptors %d", nfs->free_fd_count));
180         }
181 }
182
183
184 static INTN
185 netbuf_alloc(netfs_fd_t *f)
186 {
187         /* we will try to reuse the existing buffer first */
188         if (f->netbuf != 0) return 0;
189
190         f->netbuf_pos     = 0;
191
192         f->netbuf = (CHAR8 *)alloc_pages(EFI_SIZE_TO_PAGES(f->netbuf_maxsize), EfiLoaderData, AllocateAnyPages, 0);
193                 
194         return f->netbuf == 0 ? -1 : 0;
195 }
196
197 static EFI_STATUS
198 netfs_name(netfs_interface_t *this, CHAR16 *name, UINTN maxlen)
199 {
200         if (name == NULL || maxlen < 1) return EFI_INVALID_PARAMETER;
201
202         StrnCpy(name, FS_NAME, maxlen-1);
203
204         name[maxlen-1] = CHAR_NULL;
205
206         return EFI_SUCCESS;
207 }
208
209 static  VOID
210 netfs_extract_ip(netfs_priv_state_t *nfs)
211 {
212         EFI_PXE_BASE_CODE *pxe = nfs->pxe;
213
214         if (pxe->Mode->PxeDiscoverValid) {
215                 nfs->using_pxe = TRUE;
216                 Memcpy(&nfs->srv_ip, pxe->Mode->PxeReply.Dhcpv4.BootpSiAddr, sizeof(EFI_IP_ADDRESS)); 
217                 Memcpy(&nfs->hw_addr, pxe->Mode->PxeReply.Dhcpv4.BootpHwAddr, 16*sizeof(UINT8));
218         } else {
219                 Memcpy(&nfs->srv_ip, pxe->Mode->DhcpAck.Dhcpv4.BootpSiAddr, sizeof(EFI_IP_ADDRESS)); 
220                 Memcpy(&nfs->hw_addr, pxe->Mode->DhcpAck.Dhcpv4.BootpHwAddr, sizeof(nfs->hw_addr));
221         }
222
223         Memcpy(&nfs->cln_ip, &pxe->Mode->StationIp, sizeof(EFI_IP_ADDRESS)); 
224         Memcpy(&nfs->netmask, &pxe->Mode->SubnetMask, sizeof(EFI_IP_ADDRESS)); 
225
226         /*
227          * the fact that we use index 0, is just a guess
228          */
229         if (pxe->Mode->RouteTableEntries>0) 
230                 Memcpy(&nfs->gw_ip, &pxe->Mode->RouteTable[0].GwAddr, sizeof(EFI_IP_ADDRESS)); 
231
232         VERB_PRT(1, Print(L"PXE PxeDiscoverValid: %s\n", pxe->Mode->PxeDiscoverValid?  L"Yes (PXE-aware DHCPD)\n" : L"No (Regular DHCPD)\n"));
233 #if 0
234         status = BS->HandleProtocol(dev, &PxeCallbackProtocol, (VOID **)&netfs_callback);
235         status = LibInstallProtocolInterfaces(&dev, &PxeCallbackProtocol, &netfs_callback, NULL);
236         Print(L"PXE Callback support : %r\n", status);
237         if (status == EFI_SUCCESS) {
238                 BOOLEAN doit = TRUE;
239                 status = pxe->SetParameters(pxe, NULL, NULL, NULL, NULL, &doit);
240                 Print(L"PXE Callback SetParameters: %r\n", status);
241         }
242 #endif
243                 /*
244                  * XXX: TFTPD server not quite right when using PXE, need to extract bootservers...
245                  */
246         VERB_PRT(1, Print(L"Local IP: %d.%d.%d.%d\n",
247                 pxe->Mode->StationIp.v4.Addr[0] & 0xff,
248                 pxe->Mode->StationIp.v4.Addr[1] & 0xff,
249                 pxe->Mode->StationIp.v4.Addr[2] & 0xff,
250                 pxe->Mode->StationIp.v4.Addr[3] & 0xff));
251
252         VERB_PRT(1, Print(L"SM: %d.%d.%d.%d\n",
253                 pxe->Mode->SubnetMask.v4.Addr[0] & 0xff,
254                 pxe->Mode->SubnetMask.v4.Addr[1] & 0xff,
255                 pxe->Mode->SubnetMask.v4.Addr[2] & 0xff,
256                 pxe->Mode->SubnetMask.v4.Addr[3] & 0xff));
257
258         VERB_PRT(1, Print(L"TFTPD IP: %d.%d.%d.%d\n", 
259                 nfs->srv_ip.v4.Addr[0] & 0xff, 
260                 nfs->srv_ip.v4.Addr[1] & 0xff, 
261                 nfs->srv_ip.v4.Addr[2] & 0xff, 
262                 nfs->srv_ip.v4.Addr[3] & 0xff));
263
264         VERB_PRT(1, Print(L"Gateway IP: %d.%d.%d.%d\n",
265                         nfs->gw_ip.v4.Addr[0] & 0xff, 
266                         nfs->gw_ip.v4.Addr[1] & 0xff, 
267                         nfs->gw_ip.v4.Addr[2] & 0xff, 
268                         nfs->gw_ip.v4.Addr[3] & 0xff));
269
270 }
271
272 static EFI_STATUS
273 netfs_start(EFI_PXE_BASE_CODE *pxe)
274 {
275         EFI_STATUS status;
276
277         status = uefi_call_wrapper(pxe->Start, 2, pxe, FALSE);
278         if (EFI_ERROR(status)) return status;
279
280         return pxe->Dhcp(pxe, FALSE);
281 }
282
283 static EFI_STATUS
284 netfs_open(netfs_interface_t *this, CHAR16 *name, UINTN *fd)
285 {
286         netfs_priv_state_t *nfs;
287         netfs_fd_t         *f;
288         EFI_STATUS         status;
289         CHAR8              ascii_name[FILENAME_MAXLEN];
290         UINTN              blocksize = NETFS_DEFAULT_BLOCKSIZE;
291         UINTN              prev_netbufsize, retries = 0;
292         BOOLEAN            server_provided_filesize = FALSE;
293
294         if (this == NULL || name == NULL || fd == NULL) return EFI_INVALID_PARAMETER;
295
296         nfs = FS_PRIVATE(this);
297
298         if (nfs->pxe == NULL) return EFI_INVALID_PARAMETER;
299
300         /*
301          * Try to start protocol if not already active
302          */
303         if (nfs->pxe->Mode->Started == FALSE) {
304                 status = netfs_start(nfs->pxe);
305                 if (EFI_ERROR(status)) return  status;
306                 netfs_extract_ip(nfs);
307         }
308
309         if ((f=netfs_fd_alloc(nfs, name)) == NULL) return EFI_OUT_OF_RESOURCES;
310
311         if (f->netbuf_reuse) {
312                 f->netbuf_pos = 0;
313                 f->is_valid = TRUE;
314                 *fd = NETFS_F2FD(nfs, f);
315                 return EFI_SUCCESS;
316         }
317         f->netbuf_maxsize = NETFS_DEFAULT_BUFSIZE;
318         f->netbuf_size = 0;
319         
320         if (f->netbuf == NULL && netbuf_alloc(f) == -1) {
321                 netfs_fd_free(nfs, f);
322                 return EFI_OUT_OF_RESOURCES;
323         }
324
325         /* well, we need to download ! */
326
327         U2ascii(name, ascii_name, FILENAME_MAXLEN);
328
329         VERB_PRT(2, Print(L"downloading %a from %d.%d.%d.%d...\n", ascii_name, 
330                                 nfs->srv_ip.v4.Addr[0], 
331                                 nfs->srv_ip.v4.Addr[1], 
332                                 nfs->srv_ip.v4.Addr[2], 
333                                 nfs->srv_ip.v4.Addr[3]));
334 retry:
335         if (retries == 2) {
336                 netfs_fd_free(nfs, f);
337                 VERB_PRT(2, Print(L"Failed: %r\n", status));
338                 return status;
339         }
340
341 /*
342  * netboot bugfix SF tracker 2874380
343  * EFI 1.10 spec
344  * For read operations, the return data will be placed in the buffer specified by BufferPtr. If
345  * BufferSize is too small to contain the entire downloaded file, then
346  * EFI_BUFFER_TOO_SMALL will be returned and BufferSize will be set to zero or the size of
347  * the requested file (the size of the requested file is only returned if the TFTP server supports TFTP
348  * options). If BufferSize is large enough for the read operation, then BufferSize will be set to
349  * the size of the downloaded file, and EFI_SUCCESS will be returned. Applications using the
350  * PxeBc.Mtftp() services should use the get-file-size operations to determine the size of the
351  * downloaded file prior to using the read-file operations—especially when downloading large
352  * (greater than 64 MB) files—instead of making two calls to the read-file operation. Following this
353  * recommendation will save time if the file is larger than expected and the TFTP server does not
354  * support TFTP option extensions. Without TFTP option extension support, the client has to
355  * download the entire file, counting and discarding the received packets, to determine the file size.
356  * ...
357  * For TFTP “get file size” operations, the size of the requested file or directory is returned in
358  * BufferSize, and EFI_SUCCESS will be returned. If the TFTP server does not support options,
359  * the file will be downloaded into a bit bucket and the length of the downloaded file will be returned.
360  */
361         status = uefi_call_wrapper(nfs->pxe->Mtftp, 10, 
362                                    nfs->pxe, 
363                                    EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE, 
364                                    f->netbuf, 
365                                    FALSE,
366                                    &(f->netbuf_size),   // PXE writes size of file from server here
367                                    &blocksize, 
368                                    &nfs->srv_ip, 
369                                    ascii_name, 
370                                    NULL, 
371                                    FALSE);
372         /*
373          * If options are not supported by this tftp server, according to the spec the file will be
374          * downloaded into a bit bucket, the size calculated by efi fw and returned in the status
375          * field of this call. YUK!!... in this case we will default to currently allocated max
376          * if thats still not big enough it will be caught and increased following the read file attempt
377          * then retried. 
378          * XXX need to research how this is handled or changed in the latest UEFI spec.
379          */
380         if (status != EFI_SUCCESS) {
381                 f->netbuf_size = f->netbuf_maxsize;
382                 VERB_PRT(2, Print(L"setting default buffer size of %d for %a, no filesize recd from tftp server\n",
383                                   f->netbuf_size, ascii_name));
384         }
385
386         if (status == EFI_SUCCESS) {
387                 server_provided_filesize = TRUE;
388                 VERB_PRT(2, Print(L"received file size of %d for %a from tftp server.\n",
389                                   f->netbuf_size, ascii_name));
390         }
391
392         if (f->netbuf_size > f->netbuf_maxsize) {       // we need a bigger buffer
393                 VERB_PRT(2, Print(L"allocated buffer too small, attempting to increase\n"));
394                 f->netbuf_maxsize += f->netbuf_size;
395                 free(f->netbuf);
396                 f->netbuf = NULL;
397                 if (netbuf_alloc(f) == -1) return EFI_OUT_OF_RESOURCES;
398         }
399
400         /* paranoid catch any corner case missed */
401         if (f->netbuf_size == 0) f->netbuf_size = f->netbuf_maxsize;
402
403         DBG_PRT((L"\nbefore read: netbuf:" PTR_FMT " netbuf_size=%d blocksize=%d\n", 
404                 f->netbuf, 
405                 f->netbuf_size,
406                 blocksize));
407
408         prev_netbufsize = f->netbuf_size;
409
410         /* now try and download this file from the tftp server */
411         status = uefi_call_wrapper(nfs->pxe->Mtftp, 10, 
412                                    nfs->pxe, 
413                                    EFI_PXE_BASE_CODE_TFTP_READ_FILE, 
414                                    f->netbuf, 
415                                    FALSE,
416                                    &(f->netbuf_size), 
417                                    &blocksize, 
418                                    &nfs->srv_ip, 
419                                    ascii_name, 
420                                    NULL, 
421                                    FALSE);
422
423         DBG_PRT((L"after: status=%r netbuf:" PTR_FMT " netbuf_size=%d blocksize=%d\n", 
424                 status, 
425                 f->netbuf, 
426                 f->netbuf_size, 
427                 blocksize));
428
429         if ((status == EFI_TIMEOUT || status == EFI_BUFFER_TOO_SMALL) && !server_provided_filesize) {
430                 Print(L"buffer too small, need netbuf_size=%d\n", f->netbuf_size);
431                 /*
432                  * if the TFTP server supports TFTP options, then we should
433                  * get the required size. So we test to see if the size
434                  * we set has changed. If so, we got the required size.
435                  * If not, we increase the buffer size and retry.
436                  */
437                 if (f->netbuf_size == prev_netbufsize) {
438                         f->netbuf_maxsize += NETFS_DEFAULT_BUFSIZE_INC;
439                 } else {
440                         /* we got an answer from the TFTP server, let's try it */
441                         f->netbuf_maxsize = f->netbuf_size;
442                         server_provided_filesize = TRUE;
443                 }
444                 free(f->netbuf);
445
446                 f->netbuf = NULL; /* will force reallocation */
447
448                 if (netbuf_alloc(f) == 0) {
449                         retries++;
450                         goto retry;
451                 }
452
453         } else if (status == EFI_TIMEOUT) {     //if just a simple timeout, buffers are good just retry
454                 VERB_PRT(2, Print(L"TFTP returned EFI_TIMEOUT ERROR... %d retries left.\n", (2 - retries)));
455                 retries++;
456                 goto retry;
457         }
458         if (status == EFI_SUCCESS) {
459                 /* start at the beginning of the file */
460                 f->netbuf_pos = 0;
461
462                 /* cache file name */
463                 StrCpy(f->last_file, name);
464
465                 f->is_valid = 1;
466
467                 *fd = NETFS_F2FD(nfs, f);
468                 VERB_PRT(2, Print(L"Done\n"));
469         } else {
470                 netfs_fd_free(nfs, f);
471                 VERB_PRT(2, Print(L"Failed: %r\n", status));
472         }
473         DBG_PRT((L"File %s netbuf_size=%d: %r", name, f->netbuf_size, status));
474 #if 0
475         Print(L"\n---\n");
476         { INTN i; 
477                 for(i=0; i < netbuf_size; i++) {
478                         Print(L"%c", (CHAR16)netbuf[i]);
479                 }
480         }
481         Print(L"\n---\n");
482 #endif
483         return status;
484 }
485
486
487 static EFI_STATUS
488 netfs_read(netfs_interface_t *this, UINTN fd, VOID *buf, UINTN *size)
489 {
490         netfs_priv_state_t *nfs;
491         netfs_fd_t         *f;
492         UINTN              count;
493
494         if (this == NULL || fd >= NETFS_FD_MAX || buf == NULL || size == NULL) return EFI_INVALID_PARAMETER;
495
496         nfs = FS_PRIVATE(this);
497         f   = NETFS_FD2F(nfs, fd);
498
499         if (NETFS_F_INVALID(f)) return EFI_INVALID_PARAMETER;
500
501         count = MIN(*size, f->netbuf_size - f->netbuf_pos);
502
503         if (count) Memcpy(buf, f->netbuf+f->netbuf_pos, count);
504
505         *size = count;
506         f->netbuf_pos += count;
507
508         return EFI_SUCCESS;
509 }
510
511 static EFI_STATUS
512 netfs_close(netfs_interface_t *this, UINTN fd)
513 {
514         netfs_priv_state_t *nfs;
515         netfs_fd_t           *f;
516
517         if (this == NULL || fd >= NETFS_FD_MAX) return EFI_INVALID_PARAMETER;
518
519         nfs = FS_PRIVATE(this);
520         f   = NETFS_FD2F(nfs, fd);
521
522         if (NETFS_F_INVALID(f)) return EFI_INVALID_PARAMETER;
523
524         netfs_fd_free(nfs, f);
525
526         return EFI_SUCCESS;
527 }
528
529 static EFI_STATUS
530 netfs_seek(netfs_interface_t *this, UINTN fd, UINT64 newpos)
531 {
532         netfs_priv_state_t *nfs;
533         netfs_fd_t         *f;
534
535         if (this == NULL || fd >= NETFS_FD_MAX) return EFI_INVALID_PARAMETER;
536
537         nfs = FS_PRIVATE(this);
538         f   = NETFS_FD2F(nfs, fd);
539
540         if (NETFS_F_INVALID(f)) return EFI_INVALID_PARAMETER;
541
542         if (newpos > f->netbuf_size) return EFI_INVALID_PARAMETER;
543
544         f->netbuf_pos = newpos;
545
546         return EFI_SUCCESS;
547 }
548
549 static EFI_STATUS
550 netfs_infosize(netfs_interface_t *this, UINTN fd, UINT64 *sz)
551 {
552         netfs_priv_state_t *nfs;
553         netfs_fd_t         *f;
554
555         if (this == NULL || fd >= NETFS_FD_MAX || sz == NULL) return EFI_INVALID_PARAMETER;
556
557         nfs = FS_PRIVATE(this);
558         f   = NETFS_FD2F(nfs, fd);
559
560         if (NETFS_F_INVALID(f)) return EFI_INVALID_PARAMETER;
561
562         *sz = f->netbuf_size;
563
564         return EFI_SUCCESS;
565 }
566
567 static INTN
568 find_dhcp_option(EFI_PXE_BASE_CODE_PACKET *packet, UINT8 use_ipv6, UINT8 option, CHAR8 *str, INTN *len)
569 {
570         INTN i = 0;
571         UINT8 tag, length;
572         UINT8 *opts = packet->Dhcpv4.DhcpOptions;
573
574         *len = 0;
575
576         for(;;) {
577                 if (i >= 56) {
578                         DBG_PRT((L"reach end of options (no marker)\n"));
579                         break;
580                 }
581                 tag = opts[i++];
582
583                 if (tag == 0) continue;
584                 if (tag == 255) break;
585
586                 length = opts[i++];
587
588 #if 0
589                 { UINT8 l = length, k = 0;
590                         Print(L"found option %d len=%d: ", tag, length);
591                         while (l--) { Print(L"%c(%d)\n", (CHAR16)opts[k], opts[k]); k++; }
592                         Print(L"\n");
593                 }
594 #endif
595                 if (tag == option) {
596                         *len = length;
597                         while (length--) { *str++ = opts[i++]; }
598                         return 0;
599                 }
600                 i += length;
601         }
602         return -1;
603 }
604
605 static EFI_STATUS
606 netfs_getinfo(netfs_interface_t *this, netfs_info_t *info)
607 {
608         netfs_priv_state_t *nfs;
609         CHAR8 str[256];
610         INTN len, r;
611
612         if (this == NULL || info == NULL) return EFI_INVALID_PARAMETER;
613
614         nfs = FS_PRIVATE(this);
615
616         Memcpy(&info->cln_ipaddr, &nfs->cln_ip, sizeof(EFI_IP_ADDRESS));
617         Memcpy(&info->srv_ipaddr, &nfs->srv_ip, sizeof(EFI_IP_ADDRESS));
618         Memcpy(&info->netmask, &nfs->netmask, sizeof(EFI_IP_ADDRESS));
619         Memcpy(&info->gw_ipaddr, &nfs->gw_ip, sizeof(EFI_IP_ADDRESS));
620         Memcpy(&info->hw_addr, &nfs->hw_addr, sizeof(info->hw_addr));
621
622         info->using_pxe  = nfs->using_pxe;
623         info->started    = nfs->pxe->Mode->Started;
624         info->using_ipv6 = nfs->pxe->Mode->UsingIpv6;
625
626         if (nfs->pxe->Mode->UsingIpv6) goto skip_options;
627
628         r = find_dhcp_option(&nfs->pxe->Mode->DhcpAck,nfs->pxe->Mode->UsingIpv6, 15, str,  &len);
629         str[len] = '\0';
630         ascii2U(str, info->domainame, 255);
631
632         VERB_PRT(3, Print(L"domain(15): %a\n", str));
633
634         r = find_dhcp_option(&nfs->pxe->Mode->DhcpAck,nfs->pxe->Mode->UsingIpv6, 12, str,  &len);
635         str[len] = '\0';
636         ascii2U(str, info->hostname, 255);
637
638         VERB_PRT(3, Print(L"hostname(12): %a\n", str));
639
640         /*
641          * extract bootfile name from DHCP exchanges
642          */
643         if (nfs->using_pxe == 0) {
644                 ascii2U(nfs->pxe->Mode->DhcpAck.Dhcpv4.BootpBootFile, info->bootfile, NETFS_BOOTFILE_MAXLEN);
645                 VERB_PRT(3, Print(L"bootfile: %s\n", info->bootfile));
646         }
647
648 skip_options:
649         return EFI_SUCCESS;
650 }
651
652 static UINT16
653 find_pxe_server_type(EFI_PXE_BASE_CODE *pxe)
654 {
655         INTN i = 0, max;
656         UINT8 tag, length;
657         UINT8 *opts = pxe->Mode->PxeReply.Dhcpv4.DhcpOptions;
658         UINT16 server_type;
659
660         while(i < 55) {
661                 tag    = opts[i];
662                 length = opts[i+1];
663
664                 DBG_PRT((L"Tag #%d Length %d\n",tag, length));
665
666                 if (tag == 43) goto found;
667
668                 i += 2 + length;
669         }
670         return NETFS_DEFAULT_SERVER_TYPE;
671 found:
672         max = i+2+length;
673         i  += 2;
674         while (i < max) {
675                 tag    = opts[i];
676                 length = opts[i+1];
677                 if (tag == 71) {
678                         server_type =(opts[i+2]<<8) | opts[i+3];
679                         DBG_PRT((L"ServerType: %d\n", server_type));
680                         return server_type;
681                 }
682                 i+= 2 + length;
683         }
684         return NETFS_DEFAULT_SERVER_TYPE;
685 }
686
687 static EFI_STATUS
688 netfs_query_layer(netfs_interface_t *this, UINT16 server_type, UINT16 layer, UINTN maxlen, CHAR16 *str)
689 {
690         netfs_priv_state_t *nfs;
691         EFI_STATUS         status;
692
693         if (this == NULL || str == NULL) return EFI_INVALID_PARAMETER;
694
695         nfs = FS_PRIVATE(this);
696         
697         if (nfs->using_pxe == FALSE) return EFI_UNSUPPORTED;
698
699         if (server_type == 0) server_type = find_pxe_server_type(nfs->pxe);
700
701         status = uefi_call_wrapper(nfs->pxe->Discover, 5, nfs->pxe, server_type, &layer, FALSE, 0);
702         if(status == EFI_SUCCESS) {
703                 ascii2U(nfs->pxe->Mode->PxeReply.Dhcpv4.BootpBootFile, str, maxlen);
704         } 
705         return status;
706 }
707
708 static VOID
709 netfs_init_state(netfs_t *netfs, EFI_HANDLE dev, EFI_PXE_BASE_CODE *pxe)
710 {
711         netfs_priv_state_t *nfs = FS_PRIVATE(netfs);
712         UINTN i;
713
714         /* need to do some init here on netfs_intf */
715         Memset(netfs, 0, sizeof(*netfs));
716
717
718         netfs->pub_intf.netfs_name        = netfs_name;
719         netfs->pub_intf.netfs_open        = netfs_open;
720         netfs->pub_intf.netfs_read        = netfs_read;
721         netfs->pub_intf.netfs_close       = netfs_close;
722         netfs->pub_intf.netfs_infosize    = netfs_infosize;
723         netfs->pub_intf.netfs_seek        = netfs_seek;
724         netfs->pub_intf.netfs_query_layer = netfs_query_layer;
725         netfs->pub_intf.netfs_getinfo     = netfs_getinfo;
726
727         nfs->dev = dev;
728         nfs->pxe = pxe;
729
730         /*
731          * we defer DHCP request until it is really necessary (netfs_open)
732          */
733         if (pxe->Mode->Started == TRUE) netfs_extract_ip(nfs);
734
735         Memset(nfs->fd_tab, 0, sizeof(nfs->fd_tab));
736
737         for (i=0; i < NETFS_FD_MAX-1; i++) {
738                 nfs->fd_tab[i].next = &nfs->fd_tab[i+1];
739         }
740         /* null on last element is done by memset */
741
742         nfs->free_fd        = nfs->fd_tab;
743         nfs->free_fd_count  = NETFS_FD_MAX;
744 }
745
746 static EFI_STATUS
747 netfs_install_one(EFI_HANDLE dev, VOID **intf)
748 {
749
750         EFI_STATUS status;
751         netfs_t *netfs;
752         EFI_PXE_BASE_CODE *pxe;
753
754         status = uefi_call_wrapper(BS->HandleProtocol, 3, dev, &NetFsProtocol, (VOID **)&netfs);
755         if (status == EFI_SUCCESS) {
756                 ERR_PRT((L"Warning: found existing %s protocol on device", FS_NAME));
757                 goto found;
758         }
759         
760         status = uefi_call_wrapper(BS->HandleProtocol, 3, dev, &PxeBaseCodeProtocol, (VOID **)&pxe);
761         if (EFI_ERROR(status)) return EFI_INVALID_PARAMETER;
762
763
764         netfs = (netfs_t *)alloc(sizeof(*netfs), EfiLoaderData);
765         if (netfs == NULL) {
766                 ERR_PRT((L"failed to allocate %s", FS_NAME));
767                 return EFI_OUT_OF_RESOURCES;
768         }
769
770         netfs_init_state(netfs, dev, pxe);
771
772         status = LibInstallProtocolInterfaces(&dev, &NetFsProtocol, netfs, NULL);
773         if (EFI_ERROR(status)) {
774                 ERR_PRT((L"Cannot install %s protocol: %r", FS_NAME, status));
775                 free(netfs);
776                 return status;
777         }
778
779 found:
780         if (intf) *intf = (VOID *)netfs;
781
782         VERB_PRT(3,
783                 { EFI_DEVICE_PATH *dp; CHAR16 *str;
784                   dp  = DevicePathFromHandle(dev);
785                   str = DevicePathToStr(dp);
786                   Print(L"attached %s to %s\n", FS_NAME, str);
787                   FreePool(str);
788                 });
789
790         return EFI_SUCCESS;
791 }
792         
793 EFI_STATUS
794 netfs_install(VOID)
795 {
796         UINTN size = 0;
797         UINTN i;
798         EFI_STATUS status;
799         VOID *intf;
800
801         uefi_call_wrapper(BS->LocateHandle, 5, ByProtocol, &PxeBaseCodeProtocol, NULL, &size, NULL);
802         if (size == 0) return EFI_UNSUPPORTED; /* no device found, oh well */
803
804         DBG_PRT((L"size=%d", size));
805
806         dev_tab = (dev_tab_t *)alloc(size, EfiLoaderData);
807         if (dev_tab == NULL) {
808                 ERR_PRT((L"failed to allocate handle table"));
809                 return EFI_OUT_OF_RESOURCES;
810         }
811         
812         status = uefi_call_wrapper(BS->LocateHandle, 5, ByProtocol, &PxeBaseCodeProtocol, NULL, &size, (VOID **)dev_tab);
813         if (status != EFI_SUCCESS) {
814                 ERR_PRT((L"failed to get handles: %r", status));
815                 free(dev_tab);
816                 return status;
817         }
818         ndev = size / sizeof(EFI_HANDLE);
819
820         for(i=0; i < ndev; i++) {
821                 intf = NULL;
822                 netfs_install_one(dev_tab[i].dev, &intf);
823                 /* override device handle with interface pointer */
824                 dev_tab[i].intf = intf;
825         }
826
827         return EFI_SUCCESS;
828 }
829         
830 EFI_STATUS
831 netfs_uninstall(VOID)
832 {
833         
834         netfs_priv_state_t *nfs;
835         EFI_STATUS status;
836         UINTN i;
837
838         for(i=0; i < ndev; i++) {
839                 if (dev_tab[i].intf == NULL) continue;
840                 nfs = FS_PRIVATE(dev_tab[i].intf);
841                 status = uefi_call_wrapper(BS->UninstallProtocolInterface, 3, nfs->dev, &NetFsProtocol, dev_tab[i].intf);
842                 if (EFI_ERROR(status)) {
843                         ERR_PRT((L"Uninstall %s error: %r", FS_NAME, status));
844                         continue;
845                 }
846                 VERB_PRT(3,
847                         { EFI_DEVICE_PATH *dp; CHAR16 *str;
848                         dp  = DevicePathFromHandle(nfs->dev);
849                         str = DevicePathToStr(dp);
850                         Print(L"uninstalled %s on %s\n", FS_NAME, str);
851                         FreePool(str);
852                         });
853
854                 if (nfs->pxe->Mode->Started == TRUE) 
855                         uefi_call_wrapper(nfs->pxe->Stop, 1, nfs->pxe);
856
857                 free(dev_tab[i].intf);
858         }
859         if (dev_tab) free(dev_tab);
860
861         return EFI_SUCCESS;
862 }