X-Git-Url: https://git.gag.com/?a=blobdiff_plain;f=fs%2Fnetfs.c;h=39f4d950c803e56c7528c42a6f5818d9922a9482;hb=90dc485b21e0c6b3417e22e86f637f45e62748f2;hp=abef2abc33190940f5d0ea8cd623177bb7032a13;hpb=3384f0373c8758e5ddd08f489ad7110898dd5406;p=debian%2Felilo diff --git a/fs/netfs.c b/fs/netfs.c index abef2ab..39f4d95 100644 --- a/fs/netfs.c +++ b/fs/netfs.c @@ -1,6 +1,7 @@ /* - * Copyright (C) 2001-2003 Hewlett-Packard Co. + * Copyright (C) 2001-2009 Hewlett-Packard Co. * Contributed by Stephane Eranian + * Contributed by Jason Fleischli * Copyright (C) 2006-2009 Intel Corporation * Contributed by Fenghua Yu * Contributed by Bibo Mao @@ -38,6 +39,13 @@ #define NETFS_DEFAULT_BUFSIZE 16*MB #define NETFS_DEFAULT_BUFSIZE_INC 8*MB +#define NETFS_DEFAULT_BLOCKSIZE 1024 /* setting to zero is supposed to default the underlying */ + /* pxe implementation to largest blocksize supported,... */ + /* in reality on original older efi implementations its */ + /* never set causing the pxe transfer to timeout. */ + /* the spec defines the minimum supported blocksize default */ + /* to be 512 bytes... a bit extreme, 1024 should work for */ + /* everything */ #define NETFS_DEFAULT_SERVER_TYPE EFI_PXE_BASE_CODE_BOOT_TYPE_BOOTSTRAP #define NETFS_FD_MAX 2 @@ -221,7 +229,7 @@ netfs_extract_ip(netfs_priv_state_t *nfs) 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)")); + VERB_PRT(1, Print(L"PXE PxeDiscoverValid: %s\n", pxe->Mode->PxeDiscoverValid? L"Yes (PXE-aware DHCPD)\n" : L"No (Regular DHCPD)\n")); #if 0 status = BS->HandleProtocol(dev, &PxeCallbackProtocol, (VOID **)&netfs_callback); status = LibInstallProtocolInterfaces(&dev, &PxeCallbackProtocol, &netfs_callback, NULL); @@ -279,7 +287,9 @@ netfs_open(netfs_interface_t *this, CHAR16 *name, UINTN *fd) netfs_fd_t *f; EFI_STATUS status; CHAR8 ascii_name[FILENAME_MAXLEN]; - UINTN blocksize = 0, prev_netbufsize; + UINTN blocksize = NETFS_DEFAULT_BLOCKSIZE; + UINTN prev_netbufsize, retries = 0; + BOOLEAN server_provided_filesize = FALSE; if (this == NULL || name == NULL || fd == NULL) return EFI_INVALID_PARAMETER; @@ -305,6 +315,7 @@ netfs_open(netfs_interface_t *this, CHAR16 *name, UINTN *fd) return EFI_SUCCESS; } f->netbuf_maxsize = NETFS_DEFAULT_BUFSIZE; + f->netbuf_size = 0; if (f->netbuf == NULL && netbuf_alloc(f) == -1) { netfs_fd_free(nfs, f); @@ -315,58 +326,108 @@ netfs_open(netfs_interface_t *this, CHAR16 *name, UINTN *fd) U2ascii(name, ascii_name, FILENAME_MAXLEN); - VERB_PRT(2, Print(L"downloading %a from %d.%d.%d.%d...", ascii_name, + VERB_PRT(2, Print(L"downloading %a from %d.%d.%d.%d...\n", 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:" PTR_FMT " netbuf_size=%d\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. + if (retries == 2) { + netfs_fd_free(nfs, f); + VERB_PRT(2, Print(L"Failed: %r\n", status)); + return status; + } + +/* + * netboot bugfix SF tracker 2874380 + * EFI 1.10 spec + * For read operations, the return data will be placed in the buffer specified by BufferPtr. If + * BufferSize is too small to contain the entire downloaded file, then + * EFI_BUFFER_TOO_SMALL will be returned and BufferSize will be set to zero or the size of + * the requested file (the size of the requested file is only returned if the TFTP server supports TFTP + * options). If BufferSize is large enough for the read operation, then BufferSize will be set to + * the size of the downloaded file, and EFI_SUCCESS will be returned. Applications using the + * PxeBc.Mtftp() services should use the get-file-size operations to determine the size of the + * downloaded file prior to using the read-file operations—especially when downloading large + * (greater than 64 MB) files—instead of making two calls to the read-file operation. Following this + * recommendation will save time if the file is larger than expected and the TFTP server does not + * support TFTP option extensions. Without TFTP option extension support, the client has to + * download the entire file, counting and discarding the received packets, to determine the file size. + * ... + * For TFTP “get file size” operations, the size of the requested file or directory is returned in + * BufferSize, and EFI_SUCCESS will be returned. If the TFTP server does not support options, + * the file will be downloaded into a bit bucket and the length of the downloaded file will be returned. + */ + status = uefi_call_wrapper(nfs->pxe->Mtftp, 10, + nfs->pxe, + EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE, + f->netbuf, + FALSE, + &(f->netbuf_size), // PXE writes size of file from server here + &blocksize, + &nfs->srv_ip, + ascii_name, + NULL, + FALSE); + /* + * If options are not supported by this tftp server, according to the spec the file will be + * downloaded into a bit bucket, the size calculated by efi fw and returned in the status + * field of this call. YUK!!... in this case we will default to currently allocated max + * if thats still not big enough it will be caught and increased following the read file attempt + * then retried. + * XXX need to research how this is handled or changed in the latest UEFI spec. */ - prev_netbufsize = f->netbuf_size; + if (status != EFI_SUCCESS) { + f->netbuf_size = f->netbuf_maxsize; + VERB_PRT(2, Print(L"setting default buffer size of %d for %a, no filesize recd from tftp server\n", + f->netbuf_size, ascii_name)); + } - status = uefi_call_wrapper(nfs->pxe->Mtftp, 10, 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); + if (status == EFI_SUCCESS) { + server_provided_filesize = TRUE; + VERB_PRT(2, Print(L"received file size of %d for %a from tftp server.\n", + f->netbuf_size, ascii_name)); + } - DBG_PRT((L"after Mftp=%r netbuf:" PTR_FMT " netbuf_size=%d blocksize=%d\n", + if (f->netbuf_size > f->netbuf_maxsize) { // we need a bigger buffer + VERB_PRT(2, Print(L"allocated buffer too small, attempting to increase\n")); + f->netbuf_maxsize += f->netbuf_size; + free(f->netbuf); + f->netbuf = NULL; + if (netbuf_alloc(f) == -1) return EFI_OUT_OF_RESOURCES; + } + + /* paranoid catch any corner case missed */ + if (f->netbuf_size == 0) f->netbuf_size = f->netbuf_maxsize; + + DBG_PRT((L"\nbefore read: netbuf:" PTR_FMT " netbuf_size=%d blocksize=%d\n", + f->netbuf, + f->netbuf_size, + blocksize)); + + prev_netbufsize = f->netbuf_size; + + /* now try and download this file from the tftp server */ + status = uefi_call_wrapper(nfs->pxe->Mtftp, 10, + nfs->pxe, + EFI_PXE_BASE_CODE_TFTP_READ_FILE, + f->netbuf, + FALSE, + &(f->netbuf_size), + &blocksize, + &nfs->srv_ip, + ascii_name, + NULL, + FALSE); + + DBG_PRT((L"after: status=%r netbuf:" PTR_FMT " netbuf_size=%d blocksize=%d\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 ((status == EFI_TIMEOUT || status == EFI_BUFFER_TOO_SMALL) && !server_provided_filesize) { + Print(L"buffer too small, need netbuf_size=%d\n", 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 @@ -378,16 +439,22 @@ retry: } else { /* we got an answer from the TFTP server, let's try it */ f->netbuf_maxsize = f->netbuf_size; + server_provided_filesize = TRUE; } free(f->netbuf); f->netbuf = NULL; /* will force reallocation */ - if (netbuf_alloc(f) == 0) goto retry; + if (netbuf_alloc(f) == 0) { + retries++; + goto retry; + } - /* fall through in case of error */ + } else if (status == EFI_TIMEOUT) { //if just a simple timeout, buffers are good just retry + VERB_PRT(2, Print(L"TFTP returned EFI_TIMEOUT ERROR... %d retries left.\n", (2 - retries))); + retries++; + goto retry; } - if (status == EFI_SUCCESS) { /* start at the beginning of the file */ f->netbuf_pos = 0;