2 * Copyright (C) 2001-2003 Hewlett-Packard Co.
3 * Contributed by Stephane Eranian <eranian@hpl.hp.com>
5 * This file is part of the ELILO, the EFI Linux boot loader.
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)
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.
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
22 * Please check out the elilo.txt for complete documentation on how
23 * to use this program.
33 #define FS_NAME L"netfs"
35 #define NETFS_DEFAULT_BUFSIZE 16*MB
36 #define NETFS_DEFAULT_BUFSIZE_INC 8*MB
38 #define NETFS_DEFAULT_SERVER_TYPE EFI_PXE_BASE_CODE_BOOT_TYPE_BOOTSTRAP
39 #define NETFS_FD_MAX 2
41 typedef struct _netfs_fd {
42 struct _netfs_fd *next;
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 */
51 CHAR16 last_file[FILENAME_MAXLEN];
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 */
60 EFI_IP_ADDRESS srv_ip;
61 EFI_IP_ADDRESS cln_ip;
63 EFI_IP_ADDRESS netmask;
66 netfs_fd_t fd_tab[NETFS_FD_MAX];
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)
78 netfs_interface_t pub_intf;
80 netfs_interface_t pub_intf;
81 netfs_priv_state_t priv_data;
85 #define FS_PRIVATE(n) (&(((netfs_t *)n)->netfs_priv.priv_data))
92 static dev_tab_t *dev_tab; /* holds all devices we found */
93 static UINTN ndev; /* how many entries in dev_tab */
95 static EFI_GUID NetFsProtocol = NETFS_PROTOCOL;
99 static EFI_PXE_BASE_CODE_CALLBACK_STATUS
101 IN EFI_PXE_BASE_CODE_CALLBACK *this,
102 IN EFI_PXE_BASE_CODE_FUNCTION function,
104 IN UINT32 packet_len,
105 IN EFI_PXE_BASE_CODE_PACKET *packet OPTIONAL
108 Print(L"netfs_callback called received=%d packet_len=%d\n", received, packet_len);
112 static EFI_PXE_BASE_CODE_CALLBACK netfs_callback = {
113 EFI_PXE_BASE_CODE_CALLBACK_INTERFACE_REVISION,
119 netfs_fd_alloc(netfs_priv_state_t *nfs, CHAR16 *name)
121 netfs_fd_t *tmp = NULL, *prev = NULL, *match;
122 UINT8 netbuf_reuse = 0;
124 if (nfs->free_fd == NULL) {
125 ERR_PRT((L"out of file descriptor"));
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));
138 /* indicate whether or not we got a match in caching */
139 match->netbuf_reuse = netbuf_reuse;
141 if (match == nfs->free_fd)
142 nfs->free_fd = match->next;
144 prev->next = match->next;
146 nfs->free_fd_count--;
152 netfs_fd_free(netfs_priv_state_t *nfs, netfs_fd_t *f)
155 ERR_PRT((L"invalid fd"));
158 f->next = nfs->free_fd;
160 /* we keep the netbuf, in case we can reuse it */
164 nfs->free_fd_count++;
166 if (nfs->free_fd_count > NETFS_FD_MAX) {
167 ERR_PRT((L"too many free descriptors %d", nfs->free_fd_count));
173 netbuf_alloc(netfs_fd_t *f)
175 /* we will try to reuse the existing buffer first */
176 if (f->netbuf != 0) return 0;
180 f->netbuf = (CHAR8 *)alloc_pages(EFI_SIZE_TO_PAGES(f->netbuf_maxsize), EfiLoaderData, AllocateAnyPages, 0);
182 return f->netbuf == 0 ? -1 : 0;
186 netfs_name(netfs_interface_t *this, CHAR16 *name, UINTN maxlen)
188 if (name == NULL || maxlen < 1) return EFI_INVALID_PARAMETER;
190 StrnCpy(name, FS_NAME, maxlen-1);
192 name[maxlen-1] = CHAR_NULL;
198 netfs_extract_ip(netfs_priv_state_t *nfs)
200 EFI_PXE_BASE_CODE *pxe = nfs->pxe;
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));
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));
211 Memcpy(&nfs->cln_ip, &pxe->Mode->StationIp, sizeof(EFI_IP_ADDRESS));
212 Memcpy(&nfs->netmask, &pxe->Mode->SubnetMask, sizeof(EFI_IP_ADDRESS));
215 * the fact that we use index 0, is just a guess
217 if (pxe->Mode->RouteTableEntries>0)
218 Memcpy(&nfs->gw_ip, &pxe->Mode->RouteTable[0].GwAddr, sizeof(EFI_IP_ADDRESS));
220 VERB_PRT(1, Print(L"PXE PxeDiscoverValid: %s\n", pxe->Mode->PxeDiscoverValid? L"Yes (PXE-aware DHCPD)" : L"No (Regular DHCPD)"));
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) {
227 status = pxe->SetParameters(pxe, NULL, NULL, NULL, NULL, &doit);
228 Print(L"PXE Callback SetParameters: %r\n", status);
232 * XXX: TFTPD server not quite right when using PXE, need to extract bootservers...
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));
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));
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));
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));
261 netfs_start(EFI_PXE_BASE_CODE *pxe)
265 status = pxe->Start(pxe, FALSE);
266 if (EFI_ERROR(status)) return status;
268 return pxe->Dhcp(pxe, FALSE);
272 netfs_open(netfs_interface_t *this, CHAR16 *name, UINTN *fd)
274 netfs_priv_state_t *nfs;
277 CHAR8 ascii_name[FILENAME_MAXLEN];
278 UINTN blocksize = 0, prev_netbufsize;
280 if (this == NULL || name == NULL || fd == NULL) return EFI_INVALID_PARAMETER;
282 nfs = FS_PRIVATE(this);
284 if (nfs->pxe == NULL) return EFI_INVALID_PARAMETER;
287 * Try to start protocol if not already active
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);
295 if ((f=netfs_fd_alloc(nfs, name)) == NULL) return EFI_OUT_OF_RESOURCES;
297 if (f->netbuf_reuse) {
300 *fd = NETFS_F2FD(nfs, f);
303 f->netbuf_maxsize = NETFS_DEFAULT_BUFSIZE;
305 if (f->netbuf == NULL && netbuf_alloc(f) == -1) {
306 netfs_fd_free(nfs, f);
307 return EFI_OUT_OF_RESOURCES;
310 /* well, we need to download ! */
312 U2ascii(name, ascii_name, FILENAME_MAXLEN);
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]));
320 f->netbuf_size = f->netbuf_maxsize;
322 DBG_PRT((L"\nbefore netbuf:0x%lx netbuf_size=%ld\n", f->netbuf, f->netbuf_size));
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).
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.
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.
338 prev_netbufsize = f->netbuf_size;
340 status = nfs->pxe->Mtftp(nfs->pxe, EFI_PXE_BASE_CODE_TFTP_READ_FILE, f->netbuf, FALSE,
342 blocksize > 0 ? &blocksize : NULL,
348 DBG_PRT((L"after Mftp=%r netbuf:0x%lx netbuf_size=%ld blocksize=%ld\n",
354 if (status == EFI_TIMEOUT && blocksize == 0) {
356 * XXX: if blocksize is not adjusted we could loop forever here
359 status = EFI_BUFFER_TOO_SMALL;
362 * check if we need to increase our buffer size
364 if (status == EFI_BUFFER_TOO_SMALL) {
365 DBG_PRT((L"buffer too small, need netbuf_size=%d", f->netbuf_size));
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.
372 if (f->netbuf_size == prev_netbufsize) {
373 f->netbuf_maxsize += NETFS_DEFAULT_BUFSIZE_INC;
375 /* we got an answer from the TFTP server, let's try it */
376 f->netbuf_maxsize = f->netbuf_size;
380 f->netbuf = NULL; /* will force reallocation */
382 if (netbuf_alloc(f) == 0) goto retry;
384 /* fall through in case of error */
387 if (status == EFI_SUCCESS) {
388 /* start at the beginning of the file */
391 /* cache file name */
392 StrCpy(f->last_file, name);
396 *fd = NETFS_F2FD(nfs, f);
397 VERB_PRT(2, Print(L"Done\n"));
399 netfs_fd_free(nfs, f);
400 VERB_PRT(2, Print(L"Failed: %r\n", status));
402 DBG_PRT((L"File %s netbuf_size=%d: %r", name, f->netbuf_size, status));
406 for(i=0; i < netbuf_size; i++) {
407 Print(L"%c", (CHAR16)netbuf[i]);
417 netfs_read(netfs_interface_t *this, UINTN fd, VOID *buf, UINTN *size)
419 netfs_priv_state_t *nfs;
423 if (this == NULL || fd >= NETFS_FD_MAX || buf == NULL || size == NULL) return EFI_INVALID_PARAMETER;
425 nfs = FS_PRIVATE(this);
426 f = NETFS_FD2F(nfs, fd);
428 if (NETFS_F_INVALID(f)) return EFI_INVALID_PARAMETER;
430 count = MIN(*size, f->netbuf_size - f->netbuf_pos);
432 if (count) Memcpy(buf, f->netbuf+f->netbuf_pos, count);
435 f->netbuf_pos += count;
441 netfs_close(netfs_interface_t *this, UINTN fd)
443 netfs_priv_state_t *nfs;
446 if (this == NULL || fd >= NETFS_FD_MAX) return EFI_INVALID_PARAMETER;
448 nfs = FS_PRIVATE(this);
449 f = NETFS_FD2F(nfs, fd);
451 if (NETFS_F_INVALID(f)) return EFI_INVALID_PARAMETER;
453 netfs_fd_free(nfs, f);
459 netfs_seek(netfs_interface_t *this, UINTN fd, UINT64 newpos)
461 netfs_priv_state_t *nfs;
464 if (this == NULL || fd >= NETFS_FD_MAX) return EFI_INVALID_PARAMETER;
466 nfs = FS_PRIVATE(this);
467 f = NETFS_FD2F(nfs, fd);
469 if (NETFS_F_INVALID(f)) return EFI_INVALID_PARAMETER;
471 if (newpos > f->netbuf_size) return EFI_INVALID_PARAMETER;
473 f->netbuf_pos = newpos;
479 netfs_infosize(netfs_interface_t *this, UINTN fd, UINT64 *sz)
481 netfs_priv_state_t *nfs;
484 if (this == NULL || fd >= NETFS_FD_MAX || sz == NULL) return EFI_INVALID_PARAMETER;
486 nfs = FS_PRIVATE(this);
487 f = NETFS_FD2F(nfs, fd);
489 if (NETFS_F_INVALID(f)) return EFI_INVALID_PARAMETER;
491 *sz = f->netbuf_size;
497 find_dhcp_option(EFI_PXE_BASE_CODE_PACKET *packet, UINT8 use_ipv6, UINT8 option, CHAR8 *str, INTN *len)
501 UINT8 *opts = packet->Dhcpv4.DhcpOptions;
507 DBG_PRT((L"reach end of options (no marker)\n"));
512 if (tag == 0) continue;
513 if (tag == 255) break;
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++; }
526 while (length--) { *str++ = opts[i++]; }
535 netfs_getinfo(netfs_interface_t *this, netfs_info_t *info)
537 netfs_priv_state_t *nfs;
541 if (this == NULL || info == NULL) return EFI_INVALID_PARAMETER;
543 nfs = FS_PRIVATE(this);
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));
551 info->using_pxe = nfs->using_pxe;
552 info->started = nfs->pxe->Mode->Started;
553 info->using_ipv6 = nfs->pxe->Mode->UsingIpv6;
555 if (nfs->pxe->Mode->UsingIpv6) goto skip_options;
557 r = find_dhcp_option(&nfs->pxe->Mode->DhcpAck,nfs->pxe->Mode->UsingIpv6, 15, str, &len);
559 ascii2U(str, info->domainame, 255);
561 VERB_PRT(3, Print(L"domain(15): %a\n", str));
563 r = find_dhcp_option(&nfs->pxe->Mode->DhcpAck,nfs->pxe->Mode->UsingIpv6, 12, str, &len);
565 ascii2U(str, info->hostname, 255);
567 VERB_PRT(3, Print(L"hostname(12): %a\n", str));
570 * extract bootfile name from DHCP exchanges
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));
582 find_pxe_server_type(EFI_PXE_BASE_CODE *pxe)
586 UINT8 *opts = pxe->Mode->PxeReply.Dhcpv4.DhcpOptions;
593 DBG_PRT((L"Tag #%d Length %d\n",tag, length));
595 if (tag == 43) goto found;
599 return NETFS_DEFAULT_SERVER_TYPE;
607 server_type =(opts[i+2]<<8) | opts[i+3];
608 DBG_PRT((L"ServerType: %d\n", server_type));
613 return NETFS_DEFAULT_SERVER_TYPE;
617 netfs_query_layer(netfs_interface_t *this, UINT16 server_type, UINT16 layer, UINTN maxlen, CHAR16 *str)
619 netfs_priv_state_t *nfs;
622 if (this == NULL || str == NULL) return EFI_INVALID_PARAMETER;
624 nfs = FS_PRIVATE(this);
626 if (nfs->using_pxe == FALSE) return EFI_UNSUPPORTED;
628 if (server_type == 0) server_type = find_pxe_server_type(nfs->pxe);
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);
638 netfs_init_state(netfs_t *netfs, EFI_HANDLE dev, EFI_PXE_BASE_CODE *pxe)
640 netfs_priv_state_t *nfs = FS_PRIVATE(netfs);
643 /* need to do some init here on netfs_intf */
644 Memset(netfs, 0, sizeof(*netfs));
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;
660 * we defer DHCP request until it is really necessary (netfs_open)
662 if (pxe->Mode->Started == TRUE) netfs_extract_ip(nfs);
664 Memset(nfs->fd_tab, 0, sizeof(nfs->fd_tab));
666 for (i=0; i < NETFS_FD_MAX-1; i++) {
667 nfs->fd_tab[i].next = &nfs->fd_tab[i+1];
669 /* null on last element is done by memset */
671 nfs->free_fd = nfs->fd_tab;
672 nfs->free_fd_count = NETFS_FD_MAX;
676 netfs_install_one(EFI_HANDLE dev, VOID **intf)
681 EFI_PXE_BASE_CODE *pxe;
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));
689 status = BS->HandleProtocol (dev, &PxeBaseCodeProtocol, (VOID **)&pxe);
690 if (EFI_ERROR(status)) return EFI_INVALID_PARAMETER;
693 netfs = (netfs_t *)alloc(sizeof(*netfs), EfiLoaderData);
695 ERR_PRT((L"failed to allocate %s", FS_NAME));
696 return EFI_OUT_OF_RESOURCES;
699 netfs_init_state(netfs, dev, pxe);
701 status = LibInstallProtocolInterfaces(&dev, &NetFsProtocol, netfs, NULL);
702 if (EFI_ERROR(status)) {
703 ERR_PRT((L"Cannot install %s protocol: %r", FS_NAME, status));
709 if (intf) *intf = (VOID *)netfs;
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);
730 BS->LocateHandle(ByProtocol, &PxeBaseCodeProtocol, NULL, &size, NULL);
731 if (size == 0) return EFI_UNSUPPORTED; /* no device found, oh well */
733 DBG_PRT((L"size=%d", size));
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;
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));
747 ndev = size / sizeof(EFI_HANDLE);
749 for(i=0; i < ndev; i++) {
751 netfs_install_one(dev_tab[i].dev, &intf);
752 /* override device handle with interface pointer */
753 dev_tab[i].intf = intf;
760 netfs_uninstall(VOID)
763 netfs_priv_state_t *nfs;
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));
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);
783 if (nfs->pxe->Mode->Started == TRUE) nfs->pxe->Stop(nfs->pxe);
785 free(dev_tab[i].intf);
787 if (dev_tab) free(dev_tab);