netfs.c
来自「EFI(Extensible Firmware Interface)是下一代BI」· C语言 代码 · 共 791 行 · 第 1/2 页
C
791 行
/* * Copyright (C) 2001-2003 Hewlett-Packard Co. * Contributed by Stephane Eranian <eranian@hpl.hp.com> * * This file is part of the ELILO, the EFI Linux boot loader. * * ELILO is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * ELILO is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with ELILO; see the file COPYING. If not, write to the Free * Software Foundation, 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Please check out the elilo.txt for complete documentation on how * to use this program. */#include <efi.h>#include <efilib.h>#include "fs/netfs.h"#include "elilo.h"#define FS_NAME L"netfs"#define NETFS_DEFAULT_BUFSIZE 16*MB#define NETFS_DEFAULT_BUFSIZE_INC 8*MB#define NETFS_DEFAULT_SERVER_TYPE EFI_PXE_BASE_CODE_BOOT_TYPE_BOOTSTRAP#define NETFS_FD_MAX 2typedef struct _netfs_fd { struct _netfs_fd *next; CHAR8 *netbuf; UINT64 netbuf_maxsize; /* currently allocated buffer */ UINTN netbuf_size; /* number of bytes currently used in the buffer */ UINT64 netbuf_pos; /* current position in the buffer */ BOOLEAN is_valid; /* avoid conflicting opens */ BOOLEAN netbuf_reuse; CHAR16 last_file[FILENAME_MAXLEN];} netfs_fd_t;typedef struct { EFI_PXE_BASE_CODE *pxe; EFI_HANDLE dev; /* handle to device we're attached to */ BOOLEAN using_pxe; /* true if downloaded using the PXE protocol vs. regular DHCP */ EFI_IP_ADDRESS srv_ip; EFI_IP_ADDRESS cln_ip; EFI_IP_ADDRESS gw_ip; EFI_IP_ADDRESS netmask; UINT8 hw_addr[16]; netfs_fd_t fd_tab[NETFS_FD_MAX]; netfs_fd_t *free_fd; UINTN free_fd_count;} netfs_priv_state_t;#define NETFS_F2FD(l,f) (UINTN)((f)-(l)->fd_tab)#define NETFS_FD2F(l,fd) ((l)->fd_tab+fd)#define NETFS_F_INVALID(f) ((f)->is_valid == FALSE)typedef union { netfs_interface_t pub_intf; struct { netfs_interface_t pub_intf; netfs_priv_state_t priv_data; } netfs_priv;} netfs_t;#define FS_PRIVATE(n) (&(((netfs_t *)n)->netfs_priv.priv_data))typedef union { EFI_HANDLE *dev; netfs_t *intf;} dev_tab_t;static dev_tab_t *dev_tab; /* holds all devices we found */static UINTN ndev; /* how many entries in dev_tab */static EFI_GUID NetFsProtocol = NETFS_PROTOCOL;#if 0static EFI_PXE_BASE_CODE_CALLBACK_STATUSnetfs_callback_func( IN EFI_PXE_BASE_CODE_CALLBACK *this, IN EFI_PXE_BASE_CODE_FUNCTION function, IN BOOLEAN received, IN UINT32 packet_len, IN EFI_PXE_BASE_CODE_PACKET *packet OPTIONAL){ Print(L"netfs_callback called received=%d packet_len=%d\n", received, packet_len); return EFI_ABORTED;}static EFI_PXE_BASE_CODE_CALLBACK netfs_callback = { EFI_PXE_BASE_CODE_CALLBACK_INTERFACE_REVISION, &netfs_callback_func};#endifstatic netfs_fd_t *netfs_fd_alloc(netfs_priv_state_t *nfs, CHAR16 *name){ netfs_fd_t *tmp = NULL, *prev = NULL, *match; UINT8 netbuf_reuse = 0; if (nfs->free_fd == NULL) { ERR_PRT((L"out of file descriptor")); return NULL; } match = nfs->free_fd; for (tmp = nfs->free_fd; tmp; tmp = tmp->next) { if (!StrCmp(name, tmp->last_file)) { DBG_PRT((L"Using cached file %s netbuf_size=%d", tmp->last_file, tmp->netbuf_size)); netbuf_reuse = 1; match = tmp; break; } prev = tmp; } /* indicate whether or not we got a match in caching */ match->netbuf_reuse = netbuf_reuse; if (match == nfs->free_fd) nfs->free_fd = match->next; else prev->next = match->next; nfs->free_fd_count--; return match;}static VOIDnetfs_fd_free(netfs_priv_state_t *nfs, netfs_fd_t *f){ if (f == NULL) { ERR_PRT((L"invalid fd")); return; } f->next = nfs->free_fd; /* we keep the netbuf, in case we can reuse it */ f->is_valid = FALSE; nfs->free_fd = f; nfs->free_fd_count++; if (nfs->free_fd_count > NETFS_FD_MAX) { ERR_PRT((L"too many free descriptors %d", nfs->free_fd_count)); }}static INTNnetbuf_alloc(netfs_fd_t *f){ /* we will try to reuse the existing buffer first */ if (f->netbuf != 0) return 0; f->netbuf_pos = 0; f->netbuf = (CHAR8 *)alloc_pages(EFI_SIZE_TO_PAGES(f->netbuf_maxsize), EfiLoaderData, AllocateAnyPages, 0); return f->netbuf == 0 ? -1 : 0;}static EFI_STATUSnetfs_name(netfs_interface_t *this, CHAR16 *name, UINTN maxlen){ if (name == NULL || maxlen < 1) return EFI_INVALID_PARAMETER; StrnCpy(name, FS_NAME, maxlen-1); name[maxlen-1] = CHAR_NULL; return EFI_SUCCESS;}static VOIDnetfs_extract_ip(netfs_priv_state_t *nfs){ EFI_PXE_BASE_CODE *pxe = nfs->pxe; if (pxe->Mode->PxeDiscoverValid) { nfs->using_pxe = TRUE; Memcpy(&nfs->srv_ip, pxe->Mode->PxeReply.Dhcpv4.BootpSiAddr, sizeof(EFI_IP_ADDRESS)); Memcpy(&nfs->hw_addr, pxe->Mode->PxeReply.Dhcpv4.BootpHwAddr, 16*sizeof(UINT8)); } else { Memcpy(&nfs->srv_ip, pxe->Mode->DhcpAck.Dhcpv4.BootpSiAddr, sizeof(EFI_IP_ADDRESS)); Memcpy(&nfs->hw_addr, pxe->Mode->DhcpAck.Dhcpv4.BootpHwAddr, sizeof(nfs->hw_addr)); } Memcpy(&nfs->cln_ip, &pxe->Mode->StationIp, sizeof(EFI_IP_ADDRESS)); Memcpy(&nfs->netmask, &pxe->Mode->SubnetMask, sizeof(EFI_IP_ADDRESS)); /* * the fact that we use index 0, is just a guess */ if (pxe->Mode->RouteTableEntries>0) Memcpy(&nfs->gw_ip, &pxe->Mode->RouteTable[0].GwAddr, sizeof(EFI_IP_ADDRESS)); VERB_PRT(1, Print(L"PXE PxeDiscoverValid: %s\n", pxe->Mode->PxeDiscoverValid? L"Yes (PXE-aware DHCPD)" : L"No (Regular DHCPD)"));#if 0 status = BS->HandleProtocol(dev, &PxeCallbackProtocol, (VOID **)&netfs_callback); status = LibInstallProtocolInterfaces(&dev, &PxeCallbackProtocol, &netfs_callback, NULL); Print(L"PXE Callback support : %r\n", status); if (status == EFI_SUCCESS) { BOOLEAN doit = TRUE; status = pxe->SetParameters(pxe, NULL, NULL, NULL, NULL, &doit); Print(L"PXE Callback SetParameters: %r\n", status); }#endif /* * XXX: TFTPD server not quite right when using PXE, need to extract bootservers... */ VERB_PRT(1, Print(L"Local IP: %d.%d.%d.%d\n", pxe->Mode->StationIp.v4.Addr[0] & 0xff, pxe->Mode->StationIp.v4.Addr[1] & 0xff, pxe->Mode->StationIp.v4.Addr[2] & 0xff, pxe->Mode->StationIp.v4.Addr[3] & 0xff)); VERB_PRT(1, Print(L"SM: %d.%d.%d.%d\n", pxe->Mode->SubnetMask.v4.Addr[0] & 0xff, pxe->Mode->SubnetMask.v4.Addr[1] & 0xff, pxe->Mode->SubnetMask.v4.Addr[2] & 0xff, pxe->Mode->SubnetMask.v4.Addr[3] & 0xff)); VERB_PRT(1, Print(L"TFTPD IP: %d.%d.%d.%d\n", nfs->srv_ip.v4.Addr[0] & 0xff, nfs->srv_ip.v4.Addr[1] & 0xff, nfs->srv_ip.v4.Addr[2] & 0xff, nfs->srv_ip.v4.Addr[3] & 0xff)); VERB_PRT(1, Print(L"Gateway IP: %d.%d.%d.%d\n", nfs->gw_ip.v4.Addr[0] & 0xff, nfs->gw_ip.v4.Addr[1] & 0xff, nfs->gw_ip.v4.Addr[2] & 0xff, nfs->gw_ip.v4.Addr[3] & 0xff));}static EFI_STATUSnetfs_start(EFI_PXE_BASE_CODE *pxe){ EFI_STATUS status; status = pxe->Start(pxe, FALSE); if (EFI_ERROR(status)) return status; return pxe->Dhcp(pxe, FALSE);}static EFI_STATUSnetfs_open(netfs_interface_t *this, CHAR16 *name, UINTN *fd){ netfs_priv_state_t *nfs; netfs_fd_t *f; EFI_STATUS status; CHAR8 ascii_name[FILENAME_MAXLEN]; UINTN blocksize = 0, prev_netbufsize; if (this == NULL || name == NULL || fd == NULL) return EFI_INVALID_PARAMETER; nfs = FS_PRIVATE(this); if (nfs->pxe == NULL) return EFI_INVALID_PARAMETER; /* * Try to start protocol if not already active */ if (nfs->pxe->Mode->Started == FALSE) { status = netfs_start(nfs->pxe); if (EFI_ERROR(status)) return status; netfs_extract_ip(nfs); } if ((f=netfs_fd_alloc(nfs, name)) == NULL) return EFI_OUT_OF_RESOURCES; if (f->netbuf_reuse) { f->netbuf_pos = 0; f->is_valid = TRUE; *fd = NETFS_F2FD(nfs, f); return EFI_SUCCESS; } f->netbuf_maxsize = NETFS_DEFAULT_BUFSIZE; if (f->netbuf == NULL && netbuf_alloc(f) == -1) { netfs_fd_free(nfs, f); return EFI_OUT_OF_RESOURCES; } /* well, we need to download ! */ U2ascii(name, ascii_name, FILENAME_MAXLEN); VERB_PRT(2, Print(L"downloading %a from %d.%d.%d.%d...", ascii_name, nfs->srv_ip.v4.Addr[0], nfs->srv_ip.v4.Addr[1], nfs->srv_ip.v4.Addr[2], nfs->srv_ip.v4.Addr[3]));retry: f->netbuf_size = f->netbuf_maxsize; DBG_PRT((L"\nbefore netbuf:0x%lx netbuf_size=%ld\n", f->netbuf, f->netbuf_size)); /* * For EFI versions older than 14.61: * it seems like there is an EFI bug (or undocumented behavior) when the buffer size * is too small AND the blocksize parameter is NULL, i.e., used the largest possible. * In this case, Mtftp() never returns EFI_BUFFER_TOO_SMALL but EFI_TIMEOUT instead. * This is true for 1.02 and also 1.10 it seems. Here we set it to the minimal value (512). * * Also it seems like on a READ_FILE which returns EFI_BUFFER_TOO_SMALL, the buffersize * is NOT updated to reflect the required size for the next attempt. * * For EFI versions 14.61 and higher: * In case the buffer is too small AND the TFTP server reports the file size (see RFC 2349), * the f->netbuf_size will report the exact size for the buffer. */ prev_netbufsize = f->netbuf_size; status = nfs->pxe->Mtftp(nfs->pxe, EFI_PXE_BASE_CODE_TFTP_READ_FILE, f->netbuf, FALSE, &(f->netbuf_size), blocksize > 0 ? &blocksize : NULL, &nfs->srv_ip, ascii_name, NULL, FALSE); DBG_PRT((L"after Mftp=%r netbuf:0x%lx netbuf_size=%ld blocksize=%ld\n", status, f->netbuf, f->netbuf_size, blocksize)); if (status == EFI_TIMEOUT && blocksize == 0) { /* * XXX: if blocksize is not adjusted we could loop forever here */ //blocksize = 512; status = EFI_BUFFER_TOO_SMALL; } /* * check if we need to increase our buffer size */ if (status == EFI_BUFFER_TOO_SMALL) { DBG_PRT((L"buffer too small, need netbuf_size=%d", f->netbuf_size)); /* * if the TFTP server supports TFTP options, then we should * get the required size. So we test to see if the size * we set has changed. If so, we got the required size. * If not, we increase the buffer size and retry. */ if (f->netbuf_size == prev_netbufsize) { f->netbuf_maxsize += NETFS_DEFAULT_BUFSIZE_INC; } else { /* we got an answer from the TFTP server, let's try it */ f->netbuf_maxsize = f->netbuf_size; } free(f->netbuf); f->netbuf = NULL; /* will force reallocation */ if (netbuf_alloc(f) == 0) goto retry; /* fall through in case of error */ } if (status == EFI_SUCCESS) { /* start at the beginning of the file */ f->netbuf_pos = 0; /* cache file name */ StrCpy(f->last_file, name); f->is_valid = 1; *fd = NETFS_F2FD(nfs, f);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?