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 + -
显示快捷键?