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