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