/** @file\r
*\r
-* Copyright (c) 2011-2013, ARM Limited. All rights reserved.\r
-* \r
-* This program and the accompanying materials \r
-* are licensed and made available under the terms and conditions of the BSD License \r
-* which accompanies this distribution. The full text of the license may be found at \r
-* http://opensource.org/licenses/bsd-license.php \r
+* Copyright (c) 2011-2014, ARM Limited. All rights reserved.\r
*\r
-* THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, \r
-* WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. \r
+* This program and the accompanying materials\r
+* are licensed and made available under the terms and conditions of the BSD License\r
+* which accompanies this distribution. The full text of the license may be found at\r
+* http://opensource.org/licenses/bsd-license.php\r
+*\r
+* THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+* WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
*\r
**/\r
\r
#include "BdsInternal.h"\r
\r
+#include <Library/NetLib.h>\r
+\r
+#include <Protocol/Bds.h>\r
#include <Protocol/UsbIo.h>\r
#include <Protocol/DiskIo.h>\r
#include <Protocol/LoadedImage.h>\r
#include <Protocol/SimpleNetwork.h>\r
+#include <Protocol/Dhcp4.h>\r
+#include <Protocol/Mtftp4.h>\r
+\r
+/* Type and defines to set up the DHCP4 options */\r
+\r
+typedef struct {\r
+ EFI_DHCP4_PACKET_OPTION Head;\r
+ UINT8 Route;\r
+} DHCP4_OPTION;\r
+\r
+#define DHCP_TAG_PARA_LIST 55\r
+#define DHCP_TAG_NETMASK 1\r
+#define DHCP_TAG_ROUTER 3\r
+\r
+/*\r
+ Constant strings and define related to the message indicating the amount of\r
+ progress in the dowloading of a TFTP file.\r
+*/\r
+\r
+// Frame for the progression slider\r
+STATIC CONST CHAR16 mTftpProgressFrame[] = L"[ ]";\r
+\r
+// Number of steps in the progression slider\r
+#define TFTP_PROGRESS_SLIDER_STEPS ((sizeof (mTftpProgressFrame) / sizeof (CHAR16)) - 3)\r
+\r
+// Size in number of characters plus one (final zero) of the message to\r
+// indicate the progress of a tftp download. The format is "[(progress slider:\r
+// 40 characters)] (nb of KBytes downloaded so far: 7 characters) Kb". There\r
+// are thus the number of characters in mTftpProgressFrame[] plus 11 characters\r
+// (2 // spaces, "Kb" and seven characters for the number of KBytes).\r
+#define TFTP_PROGRESS_MESSAGE_SIZE ((sizeof (mTftpProgressFrame) / sizeof (CHAR16)) + 12)\r
+\r
+// String to delete the tftp progress message to be able to update it :\r
+// (TFTP_PROGRESS_MESSAGE_SIZE-1) '\b'\r
+STATIC CONST CHAR16 mTftpProgressDelete[] = L"\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b";\r
\r
-#define IS_DEVICE_PATH_NODE(node,type,subtype) (((node)->Type == (type)) && ((node)->SubType == (subtype)))\r
\r
// Extract the FilePath from the Device Path\r
CHAR16*\r
return Status;\r
}\r
\r
-/**\r
- Connect a Device Path and return the handle of the driver that support this DevicePath\r
-\r
- @param DevicePath Device Path of the File to connect\r
- @param Handle Handle of the driver that support this DevicePath\r
- @param RemainingDevicePath Remaining DevicePath nodes that do not match the driver DevicePath\r
-\r
- @retval EFI_SUCCESS A driver that matches the Device Path has been found\r
- @retval EFI_NOT_FOUND No handles match the search.\r
- @retval EFI_INVALID_PARAMETER DevicePath or Handle is NULL\r
-\r
-**/\r
+STATIC\r
EFI_STATUS\r
-BdsConnectDevicePath (\r
- IN EFI_DEVICE_PATH_PROTOCOL* DevicePath,\r
- OUT EFI_HANDLE *Handle,\r
- OUT EFI_DEVICE_PATH_PROTOCOL **RemainingDevicePath\r
+BdsConnectAndUpdateDevicePath (\r
+ IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath,\r
+ OUT EFI_HANDLE *Handle,\r
+ OUT EFI_DEVICE_PATH_PROTOCOL **RemainingDevicePath\r
)\r
{\r
EFI_DEVICE_PATH* Remaining;\r
EFI_DEVICE_PATH* NewDevicePath;\r
EFI_STATUS Status;\r
+ EFI_HANDLE PreviousHandle;\r
\r
- if ((DevicePath == NULL) || (Handle == NULL)) {\r
+ if ((DevicePath == NULL) || (*DevicePath == NULL) || (Handle == NULL)) {\r
return EFI_INVALID_PARAMETER;\r
}\r
\r
+ PreviousHandle = NULL;\r
do {\r
- Remaining = DevicePath;\r
+ Remaining = *DevicePath;\r
+\r
// The LocateDevicePath() function locates all devices on DevicePath that support Protocol and returns\r
// the handle to the device that is closest to DevicePath. On output, the device path pointer is modified\r
// to point to the remaining part of the device path\r
Status = gBS->LocateDevicePath (&gEfiDevicePathProtocolGuid, &Remaining, Handle);\r
+\r
if (!EFI_ERROR (Status)) {\r
- // Recursive = FALSE: We do not want to start all the device tree\r
- Status = gBS->ConnectController (*Handle, NULL, Remaining, FALSE);\r
- }\r
+ if (*Handle == PreviousHandle) {\r
+ //\r
+ // If no forward progress is made try invoking the Dispatcher.\r
+ // A new FV may have been added to the system and new drivers\r
+ // may now be found.\r
+ // Status == EFI_SUCCESS means a driver was dispatched\r
+ // Status == EFI_NOT_FOUND means no new drivers were dispatched\r
+ //\r
+ Status = gDS->Dispatch ();\r
+ }\r
\r
- /*// We need to check if RemainingDevicePath does not point on the last node. Otherwise, calling\r
- // NextDevicePathNode () will return an undetermined Device Path Node\r
- if (!IsDevicePathEnd (RemainingDevicePath)) {\r
- RemainingDevicePath = NextDevicePathNode (RemainingDevicePath);\r
- }*/\r
+ if (!EFI_ERROR (Status)) {\r
+ PreviousHandle = *Handle;\r
+\r
+ // Recursive = FALSE: We do not want to start the whole device tree\r
+ Status = gBS->ConnectController (*Handle, NULL, Remaining, FALSE);\r
+ }\r
+ }\r
} while (!EFI_ERROR (Status) && !IsDevicePathEnd (Remaining));\r
\r
if (!EFI_ERROR (Status)) {\r
// Now, we have got the whole Device Path connected, call again ConnectController to ensure all the supported Driver\r
// Binding Protocol are connected (such as DiskIo and SimpleFileSystem)\r
- Remaining = DevicePath;\r
+ Remaining = *DevicePath;\r
Status = gBS->LocateDevicePath (&gEfiDevicePathProtocolGuid, &Remaining, Handle);\r
if (!EFI_ERROR (Status)) {\r
Status = gBS->ConnectController (*Handle, NULL, Remaining, FALSE);\r
//TODO: Should we just return success and leave the caller decide if it is the expected RemainingPath\r
Status = EFI_SUCCESS;\r
} else {\r
- Status = TryRemovableDevice (DevicePath, Handle, &NewDevicePath);\r
+ Status = TryRemovableDevice (*DevicePath, Handle, &NewDevicePath);\r
if (!EFI_ERROR (Status)) {\r
- return BdsConnectDevicePath (NewDevicePath, Handle, RemainingDevicePath);\r
+ Status = BdsConnectAndUpdateDevicePath (&NewDevicePath, Handle, RemainingDevicePath);\r
+ *DevicePath = NewDevicePath;\r
+ return Status;\r
}\r
}\r
\r
return Status;\r
}\r
\r
+/**\r
+ Connect a Device Path and return the handle of the driver that support this DevicePath\r
+\r
+ @param DevicePath Device Path of the File to connect\r
+ @param Handle Handle of the driver that support this DevicePath\r
+ @param RemainingDevicePath Remaining DevicePath nodes that do not match the driver DevicePath\r
+\r
+ @retval EFI_SUCCESS A driver that matches the Device Path has been found\r
+ @retval EFI_NOT_FOUND No handles match the search.\r
+ @retval EFI_INVALID_PARAMETER DevicePath or Handle is NULL\r
+\r
+**/\r
+EFI_STATUS\r
+BdsConnectDevicePath (\r
+ IN EFI_DEVICE_PATH_PROTOCOL* DevicePath,\r
+ OUT EFI_HANDLE *Handle,\r
+ OUT EFI_DEVICE_PATH_PROTOCOL **RemainingDevicePath\r
+ )\r
+{\r
+ return BdsConnectAndUpdateDevicePath (&DevicePath, Handle, RemainingDevicePath);\r
+}\r
+\r
BOOLEAN\r
BdsFileSystemSupport (\r
IN EFI_DEVICE_PATH *DevicePath,\r
\r
EFI_STATUS\r
BdsFileSystemLoadImage (\r
- IN EFI_DEVICE_PATH *DevicePath,\r
- IN EFI_HANDLE Handle,\r
- IN EFI_DEVICE_PATH *RemainingDevicePath,\r
+ IN OUT EFI_DEVICE_PATH **DevicePath,\r
+ IN EFI_HANDLE Handle,\r
+ IN EFI_DEVICE_PATH *RemainingDevicePath,\r
IN EFI_ALLOCATE_TYPE Type,\r
- IN OUT EFI_PHYSICAL_ADDRESS* Image,\r
+ IN OUT EFI_PHYSICAL_ADDRESS *Image,\r
OUT UINTN *ImageSize\r
)\r
{\r
- FILEPATH_DEVICE_PATH* FilePathDevicePath;\r
- EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *FsProtocol;\r
- EFI_FILE_PROTOCOL *Fs;\r
- EFI_STATUS Status;\r
- EFI_FILE_INFO *FileInfo;\r
- EFI_FILE_PROTOCOL *File;\r
- UINTN Size;\r
+ EFI_STATUS Status;\r
+ FILEPATH_DEVICE_PATH *FilePathDevicePath;\r
+ EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *FsProtocol;\r
+ EFI_FILE_PROTOCOL *Fs;\r
+ EFI_FILE_INFO *FileInfo;\r
+ EFI_FILE_PROTOCOL *File;\r
+ UINTN Size;\r
\r
ASSERT (IS_DEVICE_PATH_NODE (RemainingDevicePath, MEDIA_DEVICE_PATH, MEDIA_FILEPATH_DP));\r
\r
FilePathDevicePath = (FILEPATH_DEVICE_PATH*)RemainingDevicePath;\r
\r
- Status = gBS->HandleProtocol (Handle, &gEfiSimpleFileSystemProtocolGuid, (VOID **)&FsProtocol);\r
+ Status = gBS->OpenProtocol (\r
+ Handle,\r
+ &gEfiSimpleFileSystemProtocolGuid,\r
+ (VOID**)&FsProtocol,\r
+ gImageHandle,\r
+ Handle,\r
+ EFI_OPEN_PROTOCOL_BY_DRIVER\r
+ );\r
if (EFI_ERROR (Status)) {\r
return Status;\r
}\r
// Try to Open the volume and get root directory\r
Status = FsProtocol->OpenVolume (FsProtocol, &Fs);\r
if (EFI_ERROR (Status)) {\r
- return Status;\r
+ goto CLOSE_PROTOCOL;\r
}\r
\r
- File = NULL;\r
Status = Fs->Open (Fs, &File, FilePathDevicePath->PathName, EFI_FILE_MODE_READ, 0);\r
if (EFI_ERROR (Status)) {\r
- return Status;\r
+ goto CLOSE_PROTOCOL;\r
}\r
\r
Size = 0;\r
FileInfo = AllocatePool (Size);\r
Status = File->GetInfo (File, &gEfiFileInfoGuid, &Size, FileInfo);\r
if (EFI_ERROR (Status)) {\r
- return Status;\r
+ goto CLOSE_FILE;\r
}\r
\r
// Get the file size\r
Status = File->Read (File, &Size, (VOID*)(UINTN)(*Image));\r
}\r
\r
+CLOSE_FILE:\r
+ File->Close (File);\r
+\r
+CLOSE_PROTOCOL:\r
+ gBS->CloseProtocol (\r
+ Handle,\r
+ &gEfiSimpleFileSystemProtocolGuid,\r
+ gImageHandle,\r
+ Handle);\r
+\r
return Status;\r
}\r
\r
\r
EFI_STATUS\r
BdsMemoryMapLoadImage (\r
- IN EFI_DEVICE_PATH *DevicePath,\r
- IN EFI_HANDLE Handle,\r
- IN EFI_DEVICE_PATH *RemainingDevicePath,\r
+ IN OUT EFI_DEVICE_PATH **DevicePath,\r
+ IN EFI_HANDLE Handle,\r
+ IN EFI_DEVICE_PATH *RemainingDevicePath,\r
IN EFI_ALLOCATE_TYPE Type,\r
IN OUT EFI_PHYSICAL_ADDRESS* Image,\r
OUT UINTN *ImageSize\r
if (IS_DEVICE_PATH_NODE (RemainingDevicePath, HARDWARE_DEVICE_PATH, HW_MEMMAP_DP)) {\r
MemMapPathDevicePath = (MEMMAP_DEVICE_PATH*)RemainingDevicePath;\r
} else {\r
- ASSERT (IS_DEVICE_PATH_NODE (DevicePath, HARDWARE_DEVICE_PATH, HW_MEMMAP_DP));\r
- MemMapPathDevicePath = (MEMMAP_DEVICE_PATH*)DevicePath;\r
+ ASSERT (IS_DEVICE_PATH_NODE (*DevicePath, HARDWARE_DEVICE_PATH, HW_MEMMAP_DP));\r
+ MemMapPathDevicePath = (MEMMAP_DEVICE_PATH*)*DevicePath;\r
}\r
\r
Size = MemMapPathDevicePath->EndingAddress - MemMapPathDevicePath->StartingAddress;\r
\r
EFI_STATUS\r
BdsFirmwareVolumeLoadImage (\r
- IN EFI_DEVICE_PATH *DevicePath,\r
- IN EFI_HANDLE Handle,\r
- IN EFI_DEVICE_PATH *RemainingDevicePath,\r
+ IN OUT EFI_DEVICE_PATH **DevicePath,\r
+ IN EFI_HANDLE Handle,\r
+ IN EFI_DEVICE_PATH *RemainingDevicePath,\r
IN EFI_ALLOCATE_TYPE Type,\r
IN OUT EFI_PHYSICAL_ADDRESS* Image,\r
OUT UINTN *ImageSize\r
Status = FwVol->ReadFile (\r
FwVol,\r
FvNameGuid,\r
- (VOID*)(UINTN)(*Image),\r
+ (VOID**)Image,\r
ImageSize,\r
&FvType,\r
&Attrib,\r
\r
EFI_STATUS\r
BdsPxeLoadImage (\r
- IN EFI_DEVICE_PATH* DevicePath,\r
- IN EFI_HANDLE Handle,\r
- IN EFI_DEVICE_PATH* RemainingDevicePath,\r
- IN EFI_ALLOCATE_TYPE Type,\r
- IN OUT EFI_PHYSICAL_ADDRESS *Image,\r
- OUT UINTN *ImageSize\r
+ IN OUT EFI_DEVICE_PATH **DevicePath,\r
+ IN EFI_HANDLE Handle,\r
+ IN EFI_DEVICE_PATH *RemainingDevicePath,\r
+ IN EFI_ALLOCATE_TYPE Type,\r
+ IN OUT EFI_PHYSICAL_ADDRESS* Image,\r
+ OUT UINTN *ImageSize\r
)\r
{\r
EFI_STATUS Status;\r
return Status;\r
}\r
\r
- Status = LoadFileProtocol->LoadFile (LoadFileProtocol, DevicePath, TRUE, &BufferSize, NULL);\r
+ Status = LoadFileProtocol->LoadFile (LoadFileProtocol, RemainingDevicePath, TRUE, &BufferSize, NULL);\r
if (Status == EFI_BUFFER_TOO_SMALL) {\r
Status = gBS->AllocatePages (Type, EfiBootServicesCode, EFI_SIZE_TO_PAGES(BufferSize), Image);\r
if (EFI_ERROR (Status)) {\r
return Status;\r
}\r
\r
- Status = LoadFileProtocol->LoadFile (LoadFileProtocol, DevicePath, TRUE, &BufferSize, (VOID*)(UINTN)(*Image));\r
+ Status = LoadFileProtocol->LoadFile (LoadFileProtocol, RemainingDevicePath, TRUE, &BufferSize, (VOID*)(UINTN)(*Image));\r
if (!EFI_ERROR (Status) && (ImageSize != NULL)) {\r
*ImageSize = BufferSize;\r
}\r
\r
BOOLEAN\r
BdsTftpSupport (\r
- IN EFI_DEVICE_PATH* DevicePath,\r
- IN EFI_HANDLE Handle,\r
- IN EFI_DEVICE_PATH* RemainingDevicePath\r
+ IN EFI_DEVICE_PATH *DevicePath,\r
+ IN EFI_HANDLE Handle,\r
+ IN EFI_DEVICE_PATH *RemainingDevicePath\r
)\r
{\r
- EFI_STATUS Status;\r
+ EFI_STATUS Status;\r
EFI_DEVICE_PATH *NextDevicePath;\r
- EFI_PXE_BASE_CODE_PROTOCOL *PxeBcProtocol;\r
+ VOID *Interface;\r
\r
// Validate the Remaining Device Path\r
if (IsDevicePathEnd (RemainingDevicePath)) {\r
return FALSE;\r
}\r
\r
- Status = gBS->HandleProtocol (Handle, &gEfiPxeBaseCodeProtocolGuid, (VOID **)&PxeBcProtocol);\r
+ Status = gBS->HandleProtocol (\r
+ Handle, &gEfiDevicePathProtocolGuid,\r
+ &Interface\r
+ );\r
if (EFI_ERROR (Status)) {\r
return FALSE;\r
- } else {\r
- return TRUE;\r
}\r
+\r
+ //\r
+ // Check that the controller (identified by its handle "Handle") supports the\r
+ // MTFTPv4 Service Binding Protocol. If it does, it means that it supports the\r
+ // EFI MTFTPv4 Protocol needed to download the image through TFTP.\r
+ //\r
+ Status = gBS->HandleProtocol (\r
+ Handle, &gEfiMtftp4ServiceBindingProtocolGuid,\r
+ &Interface\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return FALSE;\r
+ }\r
+\r
+ return TRUE;\r
+}\r
+\r
+/**\r
+ Worker function that get the size in numbers of bytes of a file from a TFTP\r
+ server before to download the file.\r
+\r
+ @param[in] Mtftp4 MTFTP4 protocol interface\r
+ @param[in] FilePath Path of the file, Ascii encoded\r
+ @param[out] FileSize Address where to store the file size in number of\r
+ bytes.\r
+\r
+ @retval EFI_SUCCESS The size of the file was returned.\r
+ @retval !EFI_SUCCESS The size of the file was not returned.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+Mtftp4GetFileSize (\r
+ IN EFI_MTFTP4_PROTOCOL *Mtftp4,\r
+ IN CHAR8 *FilePath,\r
+ OUT UINT64 *FileSize\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_MTFTP4_OPTION ReqOpt[1];\r
+ EFI_MTFTP4_PACKET *Packet;\r
+ UINT32 PktLen;\r
+ EFI_MTFTP4_OPTION *TableOfOptions;\r
+ EFI_MTFTP4_OPTION *Option;\r
+ UINT32 OptCnt;\r
+ UINT8 OptBuf[128];\r
+\r
+ ReqOpt[0].OptionStr = (UINT8*)"tsize";\r
+ OptBuf[0] = '0';\r
+ OptBuf[1] = 0;\r
+ ReqOpt[0].ValueStr = OptBuf;\r
+\r
+ Status = Mtftp4->GetInfo (\r
+ Mtftp4,\r
+ NULL,\r
+ (UINT8*)FilePath,\r
+ NULL,\r
+ 1,\r
+ ReqOpt,\r
+ &PktLen,\r
+ &Packet\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error;\r
+ }\r
+\r
+ Status = Mtftp4->ParseOptions (\r
+ Mtftp4,\r
+ PktLen,\r
+ Packet,\r
+ (UINT32 *) &OptCnt,\r
+ &TableOfOptions\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error;\r
+ }\r
+\r
+ Option = TableOfOptions;\r
+ while (OptCnt != 0) {\r
+ if (AsciiStrnCmp ((CHAR8 *)Option->OptionStr, "tsize", 5) == 0) {\r
+ *FileSize = AsciiStrDecimalToUint64 ((CHAR8 *)Option->ValueStr);\r
+ break;\r
+ }\r
+ OptCnt--;\r
+ Option++;\r
+ }\r
+ FreePool (TableOfOptions);\r
+\r
+ if (OptCnt == 0) {\r
+ Status = EFI_UNSUPPORTED;\r
+ }\r
+\r
+Error :\r
+\r
+ return Status;\r
}\r
\r
+/**\r
+ Update the progress of a file download\r
+ This procedure is called each time a new TFTP packet is received.\r
+\r
+ @param[in] This MTFTP4 protocol interface\r
+ @param[in] Token Parameters for the download of the file\r
+ @param[in] PacketLen Length of the packet\r
+ @param[in] Packet Address of the packet\r
+\r
+ @retval EFI_SUCCESS All packets are accepted.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+Mtftp4CheckPacket (\r
+ IN EFI_MTFTP4_PROTOCOL *This,\r
+ IN EFI_MTFTP4_TOKEN *Token,\r
+ IN UINT16 PacketLen,\r
+ IN EFI_MTFTP4_PACKET *Packet\r
+ )\r
+{\r
+ BDS_TFTP_CONTEXT *Context;\r
+ CHAR16 Progress[TFTP_PROGRESS_MESSAGE_SIZE];\r
+ UINT64 NbOfKb;\r
+ UINTN Index;\r
+ UINTN LastStep;\r
+ UINTN Step;\r
+ UINT64 LastNbOf50Kb;\r
+ UINT64 NbOf50Kb;\r
+\r
+ if ((NTOHS (Packet->OpCode)) == EFI_MTFTP4_OPCODE_DATA) {\r
+ Context = (BDS_TFTP_CONTEXT*)Token->Context;\r
+\r
+ if (Context->DownloadedNbOfBytes == 0) {\r
+ if (Context->FileSize > 0) {\r
+ Print (L"%s 0 Kb", mTftpProgressFrame);\r
+ } else {\r
+ Print (L" 0 Kb");\r
+ }\r
+ }\r
+\r
+ //\r
+ // The data is the packet are prepended with two UINT16 :\r
+ // . OpCode = EFI_MTFTP4_OPCODE_DATA\r
+ // . Block = the number of this block of data\r
+ //\r
+ Context->DownloadedNbOfBytes += PacketLen - sizeof (Packet->OpCode) - sizeof (Packet->Data.Block);\r
+ NbOfKb = Context->DownloadedNbOfBytes / 1024;\r
+\r
+ Progress[0] = L'\0';\r
+ if (Context->FileSize > 0) {\r
+ LastStep = (Context->LastReportedNbOfBytes * TFTP_PROGRESS_SLIDER_STEPS) / Context->FileSize;\r
+ Step = (Context->DownloadedNbOfBytes * TFTP_PROGRESS_SLIDER_STEPS) / Context->FileSize;\r
+ if (Step > LastStep) {\r
+ Print (mTftpProgressDelete);\r
+ CopyMem (Progress, mTftpProgressFrame, sizeof mTftpProgressFrame);\r
+ for (Index = 1; Index < Step; Index++) {\r
+ Progress[Index] = L'=';\r
+ }\r
+ Progress[Step] = L'>';\r
+\r
+ UnicodeSPrint (\r
+ Progress + (sizeof (mTftpProgressFrame) / sizeof (CHAR16)) - 1,\r
+ sizeof (Progress) - sizeof (mTftpProgressFrame),\r
+ L" %7d Kb",\r
+ NbOfKb\r
+ );\r
+ Context->LastReportedNbOfBytes = Context->DownloadedNbOfBytes;\r
+ }\r
+ } else {\r
+ //\r
+ // Case when we do not know the size of the final file.\r
+ // We print the updated size every 50KB of downloaded data\r
+ //\r
+ LastNbOf50Kb = Context->LastReportedNbOfBytes / (50*1024);\r
+ NbOf50Kb = Context->DownloadedNbOfBytes / (50*1024);\r
+ if (NbOf50Kb > LastNbOf50Kb) {\r
+ Print (L"\b\b\b\b\b\b\b\b\b\b");\r
+ UnicodeSPrint (Progress, sizeof (Progress), L"%7d Kb", NbOfKb);\r
+ Context->LastReportedNbOfBytes = Context->DownloadedNbOfBytes;\r
+ }\r
+ }\r
+ if (Progress[0] != L'\0') {\r
+ Print (L"%s", Progress);\r
+ }\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Download an image from a TFTP server\r
+\r
+ @param[in] DevicePath Device path of the TFTP boot option\r
+ @param[in] ControllerHandle Handle of the network controller\r
+ @param[in] RemainingDevicePath Device path of the TFTP boot option but\r
+ the first node that identifies the network controller\r
+ @param[in] Type Type to allocate memory pages\r
+ @param[out] Image Address of the bufer where the image is stored in\r
+ case of success\r
+ @param[out] ImageSize Size in number of bytes of the i;age in case of\r
+ success\r
+\r
+ @retval EFI_SUCCESS The image was returned.\r
+ @retval !EFI_SUCCESS Something went wrong.\r
+\r
+**/\r
EFI_STATUS\r
BdsTftpLoadImage (\r
- IN EFI_DEVICE_PATH* DevicePath,\r
- IN EFI_HANDLE Handle,\r
- IN EFI_DEVICE_PATH* RemainingDevicePath,\r
- IN EFI_ALLOCATE_TYPE Type,\r
- IN OUT EFI_PHYSICAL_ADDRESS *Image,\r
- OUT UINTN *ImageSize\r
+ IN OUT EFI_DEVICE_PATH **DevicePath,\r
+ IN EFI_HANDLE ControllerHandle,\r
+ IN EFI_DEVICE_PATH *RemainingDevicePath,\r
+ IN EFI_ALLOCATE_TYPE Type,\r
+ IN OUT EFI_PHYSICAL_ADDRESS *Image,\r
+ OUT UINTN *ImageSize\r
)\r
{\r
- EFI_STATUS Status;\r
- EFI_PXE_BASE_CODE_PROTOCOL *Pxe;\r
- UINT64 TftpBufferSize;\r
- VOID* TftpBuffer;\r
- EFI_IP_ADDRESS ServerIp;\r
- IPv4_DEVICE_PATH* IPv4DevicePathNode;\r
- FILEPATH_DEVICE_PATH* FilePathDevicePath;\r
- EFI_IP_ADDRESS LocalIp;\r
- CHAR8* AsciiPathName;\r
- EFI_SIMPLE_NETWORK_PROTOCOL *Snp;\r
+ EFI_STATUS Status;\r
+ EFI_HANDLE Dhcp4ChildHandle;\r
+ EFI_DHCP4_PROTOCOL *Dhcp4;\r
+ BOOLEAN Dhcp4ToStop;\r
+ EFI_HANDLE Mtftp4ChildHandle;\r
+ EFI_MTFTP4_PROTOCOL *Mtftp4;\r
+ DHCP4_OPTION ParaList;\r
+ EFI_DHCP4_PACKET_OPTION *OptionList[2];\r
+ EFI_DHCP4_CONFIG_DATA Dhcp4CfgData;\r
+ EFI_DHCP4_MODE_DATA Dhcp4Mode;\r
+ EFI_MTFTP4_CONFIG_DATA Mtftp4CfgData;\r
+ IPv4_DEVICE_PATH *IPv4DevicePathNode;\r
+ CHAR16 *PathName;\r
+ CHAR8 *AsciiFilePath;\r
+ EFI_MTFTP4_TOKEN Mtftp4Token;\r
+ UINT64 FileSize;\r
+ UINT64 TftpBufferSize;\r
+ BDS_TFTP_CONTEXT *TftpContext;\r
+ UINTN PathNameLen;\r
\r
ASSERT(IS_DEVICE_PATH_NODE (RemainingDevicePath, MESSAGING_DEVICE_PATH, MSG_IPv4_DP));\r
-\r
IPv4DevicePathNode = (IPv4_DEVICE_PATH*)RemainingDevicePath;\r
- FilePathDevicePath = (FILEPATH_DEVICE_PATH*)(IPv4DevicePathNode + 1);\r
\r
- Status = gBS->LocateProtocol (&gEfiPxeBaseCodeProtocolGuid, NULL, (VOID **)&Pxe);\r
- if (EFI_ERROR (Status)) {\r
- return Status;\r
+ Dhcp4ChildHandle = NULL;\r
+ Dhcp4 = NULL;\r
+ Dhcp4ToStop = FALSE;\r
+ Mtftp4ChildHandle = NULL;\r
+ Mtftp4 = NULL;\r
+ AsciiFilePath = NULL;\r
+ TftpContext = NULL;\r
+\r
+ if (!IPv4DevicePathNode->StaticIpAddress) {\r
+ //\r
+ // Using the DHCP4 Service Binding Protocol, create a child handle of the DHCP4 service and\r
+ // install the DHCP4 protocol on it. Then, open the DHCP protocol.\r
+ //\r
+ Status = NetLibCreateServiceChild (\r
+ ControllerHandle,\r
+ gImageHandle,\r
+ &gEfiDhcp4ServiceBindingProtocolGuid,\r
+ &Dhcp4ChildHandle\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ Status = gBS->OpenProtocol (\r
+ Dhcp4ChildHandle,\r
+ &gEfiDhcp4ProtocolGuid,\r
+ (VOID **) &Dhcp4,\r
+ gImageHandle,\r
+ ControllerHandle,\r
+ EFI_OPEN_PROTOCOL_BY_DRIVER\r
+ );\r
+ }\r
+ if (EFI_ERROR (Status)) {\r
+ Print (L"Unable to open DHCP4 protocol\n");\r
+ goto Error;\r
+ }\r
}\r
\r
- Status = Pxe->Start (Pxe, FALSE);\r
- if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {\r
- return Status;\r
+ //\r
+ // Using the MTFTP4 Service Binding Protocol, create a child handle of the MTFTP4 service and\r
+ // install the MTFTP4 protocol on it. Then, open the MTFTP4 protocol.\r
+ //\r
+ Status = NetLibCreateServiceChild (\r
+ ControllerHandle,\r
+ gImageHandle,\r
+ &gEfiMtftp4ServiceBindingProtocolGuid,\r
+ &Mtftp4ChildHandle\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ Status = gBS->OpenProtocol (\r
+ Mtftp4ChildHandle,\r
+ &gEfiMtftp4ProtocolGuid,\r
+ (VOID **) &Mtftp4,\r
+ gImageHandle,\r
+ ControllerHandle,\r
+ EFI_OPEN_PROTOCOL_BY_DRIVER\r
+ );\r
+ }\r
+ if (EFI_ERROR (Status)) {\r
+ Print (L"Unable to open MTFTP4 protocol\n");\r
+ goto Error;\r
}\r
\r
- do {\r
- if (!IPv4DevicePathNode->StaticIpAddress) {\r
- Status = Pxe->Dhcp (Pxe, TRUE);\r
- } else {\r
- CopyMem (&LocalIp.v4, &IPv4DevicePathNode->LocalIpAddress, sizeof (EFI_IPv4_ADDRESS));\r
- Status = Pxe->SetStationIp (Pxe, &LocalIp, NULL);\r
- }\r
-\r
- // If an IP Address has already been set and a different static IP address is requested then restart\r
- // the Network service.\r
- if (Status == EFI_ALREADY_STARTED) {\r
- Status = gBS->LocateProtocol (&gEfiSimpleNetworkProtocolGuid, NULL, (VOID **)&Snp);\r
- if (!EFI_ERROR (Status) && IPv4DevicePathNode->StaticIpAddress &&\r
- (CompareMem (&Snp->Mode->CurrentAddress, &IPv4DevicePathNode->LocalIpAddress, sizeof(EFI_MAC_ADDRESS)) != 0))\r
- {\r
- Pxe->Stop (Pxe);\r
- Status = Pxe->Start (Pxe, FALSE);\r
- if (EFI_ERROR(Status)) {\r
- break;\r
- }\r
- // After restarting the PXE protocol, we want to try again with our new IP Address\r
- Status = EFI_ALREADY_STARTED;\r
+ if (!IPv4DevicePathNode->StaticIpAddress) {\r
+ //\r
+ // Configure the DHCP4, all default settings. It is acceptable for the configuration to\r
+ // fail if the return code is equal to EFI_ACCESS_DENIED which means that the configuration\r
+ // has been done by another instance of the DHCP4 protocol or that the DHCP configuration\r
+ // process has been started but is not completed yet.\r
+ //\r
+ ZeroMem (&Dhcp4CfgData, sizeof (EFI_DHCP4_CONFIG_DATA));\r
+ ParaList.Head.OpCode = DHCP_TAG_PARA_LIST;\r
+ ParaList.Head.Length = 2;\r
+ ParaList.Head.Data[0] = DHCP_TAG_NETMASK;\r
+ ParaList.Route = DHCP_TAG_ROUTER;\r
+ OptionList[0] = &ParaList.Head;\r
+ Dhcp4CfgData.OptionCount = 1;\r
+ Dhcp4CfgData.OptionList = OptionList;\r
+\r
+ Status = Dhcp4->Configure (Dhcp4, &Dhcp4CfgData);\r
+ if (EFI_ERROR (Status)) {\r
+ if (Status != EFI_ACCESS_DENIED) {\r
+ Print (L"Error while configuring the DHCP4 protocol\n");\r
+ goto Error;\r
}\r
}\r
- } while (Status == EFI_ALREADY_STARTED);\r
\r
- if (EFI_ERROR(Status)) {\r
- return Status;\r
+ //\r
+ // Start the DHCP configuration. This may have already been done thus do not leave in error\r
+ // if the return code is EFI_ALREADY_STARTED.\r
+ //\r
+ Status = Dhcp4->Start (Dhcp4, NULL);\r
+ if (EFI_ERROR (Status)) {\r
+ if (Status != EFI_ALREADY_STARTED) {\r
+ Print (L"DHCP configuration failed\n");\r
+ goto Error;\r
+ }\r
+ } else {\r
+ Dhcp4ToStop = TRUE;\r
+ }\r
+\r
+ Status = Dhcp4->GetModeData (Dhcp4, &Dhcp4Mode);\r
+ if (EFI_ERROR (Status)) {\r
+ goto Error;\r
+ }\r
+\r
+ if (Dhcp4Mode.State != Dhcp4Bound) {\r
+ Status = EFI_TIMEOUT;\r
+ Print (L"DHCP configuration failed\n");\r
+ goto Error;\r
+ }\r
}\r
\r
- CopyMem (&ServerIp.v4, &IPv4DevicePathNode->RemoteIpAddress, sizeof (EFI_IPv4_ADDRESS));\r
+ //\r
+ // Configure the TFTP4 protocol\r
+ //\r
\r
- // Convert the Unicode PathName to Ascii\r
- AsciiPathName = AllocatePool ((StrLen (FilePathDevicePath->PathName) + 1) * sizeof (CHAR8));\r
- if (AsciiPathName == NULL) {\r
- return EFI_OUT_OF_RESOURCES;\r
+ ZeroMem (&Mtftp4CfgData, sizeof (EFI_MTFTP4_CONFIG_DATA));\r
+ Mtftp4CfgData.UseDefaultSetting = FALSE;\r
+ Mtftp4CfgData.TimeoutValue = 4;\r
+ Mtftp4CfgData.TryCount = 6;\r
+\r
+ if (IPv4DevicePathNode->StaticIpAddress) {\r
+ CopyMem (&Mtftp4CfgData.StationIp , &IPv4DevicePathNode->LocalIpAddress, sizeof (EFI_IPv4_ADDRESS));\r
+ CopyMem (&Mtftp4CfgData.SubnetMask, &IPv4DevicePathNode->SubnetMask, sizeof (EFI_IPv4_ADDRESS));\r
+ CopyMem (&Mtftp4CfgData.GatewayIp , &IPv4DevicePathNode->GatewayIpAddress, sizeof (EFI_IPv4_ADDRESS));\r
+ } else {\r
+ CopyMem (&Mtftp4CfgData.StationIp , &Dhcp4Mode.ClientAddress, sizeof (EFI_IPv4_ADDRESS));\r
+ CopyMem (&Mtftp4CfgData.SubnetMask, &Dhcp4Mode.SubnetMask , sizeof (EFI_IPv4_ADDRESS));\r
+ CopyMem (&Mtftp4CfgData.GatewayIp , &Dhcp4Mode.RouterAddress, sizeof (EFI_IPv4_ADDRESS));\r
}\r
- UnicodeStrToAsciiStr (FilePathDevicePath->PathName, AsciiPathName);\r
\r
- Status = Pxe->Mtftp (\r
- Pxe,\r
- EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE,\r
- NULL,\r
- FALSE,\r
- &TftpBufferSize,\r
- NULL,\r
- &ServerIp,\r
- (UINT8*)AsciiPathName,\r
- NULL,\r
- FALSE\r
- );\r
- if (EFI_ERROR(Status)) {\r
- if (Status == EFI_TFTP_ERROR) {\r
- DEBUG((EFI_D_ERROR, "TFTP Error: Fail to get the size of the file\n"));\r
- }\r
- goto EXIT;\r
+ CopyMem (&Mtftp4CfgData.ServerIp , &IPv4DevicePathNode->RemoteIpAddress, sizeof (EFI_IPv4_ADDRESS));\r
+\r
+ Status = Mtftp4->Configure (Mtftp4, &Mtftp4CfgData);\r
+ if (EFI_ERROR (Status)) {\r
+ Print (L"Error while configuring the MTFTP4 protocol\n");\r
+ goto Error;\r
+ }\r
+\r
+ // The Device Path might contain multiple FilePath nodes\r
+ PathName = ConvertDevicePathToText ((EFI_DEVICE_PATH_PROTOCOL*)(IPv4DevicePathNode + 1), FALSE, FALSE);\r
+ PathNameLen = StrLen (PathName) + 1;\r
+ AsciiFilePath = AllocatePool (PathNameLen);\r
+ UnicodeStrToAsciiStrS (PathName, AsciiFilePath, PathNameLen);\r
+\r
+ //\r
+ // Try to get the size of the file in bytes from the server. If it fails,\r
+ // start with a 8MB buffer to download the file.\r
+ //\r
+ FileSize = 0;\r
+ if (Mtftp4GetFileSize (Mtftp4, AsciiFilePath, &FileSize) == EFI_SUCCESS) {\r
+ TftpBufferSize = FileSize;\r
+ } else {\r
+ TftpBufferSize = SIZE_16MB;\r
}\r
\r
- // Allocate a buffer to hold the whole file.\r
- TftpBuffer = AllocatePool (TftpBufferSize);\r
- if (TftpBuffer == NULL) {\r
+ TftpContext = AllocatePool (sizeof (BDS_TFTP_CONTEXT));\r
+ if (TftpContext == NULL) {\r
Status = EFI_OUT_OF_RESOURCES;\r
- goto EXIT;\r
- }\r
-\r
- Status = Pxe->Mtftp (\r
- Pxe,\r
- EFI_PXE_BASE_CODE_TFTP_READ_FILE,\r
- TftpBuffer,\r
- FALSE,\r
- &TftpBufferSize,\r
- NULL,\r
- &ServerIp,\r
- (UINT8*)AsciiPathName,\r
- NULL,\r
- FALSE\r
- );\r
+ goto Error;\r
+ }\r
+ TftpContext->FileSize = FileSize;\r
+\r
+ for (; TftpBufferSize <= FixedPcdGet32 (PcdMaxTftpFileSize);\r
+ TftpBufferSize = (TftpBufferSize + SIZE_16MB) & (~(SIZE_16MB-1))) {\r
+ //\r
+ // Allocate a buffer to hold the whole file.\r
+ //\r
+ Status = gBS->AllocatePages (\r
+ Type,\r
+ EfiBootServicesCode,\r
+ EFI_SIZE_TO_PAGES (TftpBufferSize),\r
+ Image\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ Print (L"Failed to allocate space for image\n");\r
+ goto Error;\r
+ }\r
+\r
+ TftpContext->DownloadedNbOfBytes = 0;\r
+ TftpContext->LastReportedNbOfBytes = 0;\r
+\r
+ ZeroMem (&Mtftp4Token, sizeof (EFI_MTFTP4_TOKEN));\r
+ Mtftp4Token.Filename = (UINT8*)AsciiFilePath;\r
+ Mtftp4Token.BufferSize = TftpBufferSize;\r
+ Mtftp4Token.Buffer = (VOID *)(UINTN)*Image;\r
+ Mtftp4Token.CheckPacket = Mtftp4CheckPacket;\r
+ Mtftp4Token.Context = (VOID*)TftpContext;\r
+\r
+ Print (L"Downloading the file <%a> from the TFTP server\n", AsciiFilePath);\r
+ Status = Mtftp4->ReadFile (Mtftp4, &Mtftp4Token);\r
+ Print (L"\n");\r
+ if (EFI_ERROR (Status)) {\r
+ gBS->FreePages (*Image, EFI_SIZE_TO_PAGES (TftpBufferSize));\r
+ if (Status == EFI_BUFFER_TOO_SMALL) {\r
+ Print (L"Downloading failed, file larger than expected.\n");\r
+ continue;\r
+ } else {\r
+ goto Error;\r
+ }\r
+ }\r
+\r
+ *ImageSize = Mtftp4Token.BufferSize;\r
+ break;\r
+ }\r
+\r
+Error:\r
+ if (Dhcp4ChildHandle != NULL) {\r
+ if (Dhcp4 != NULL) {\r
+ if (Dhcp4ToStop) {\r
+ Dhcp4->Stop (Dhcp4);\r
+ }\r
+ gBS->CloseProtocol (\r
+ Dhcp4ChildHandle,\r
+ &gEfiDhcp4ProtocolGuid,\r
+ gImageHandle,\r
+ ControllerHandle\r
+ );\r
+ }\r
+ NetLibDestroyServiceChild (\r
+ ControllerHandle,\r
+ gImageHandle,\r
+ &gEfiDhcp4ServiceBindingProtocolGuid,\r
+ Dhcp4ChildHandle\r
+ );\r
+ }\r
+\r
+ if (Mtftp4ChildHandle != NULL) {\r
+ if (Mtftp4 != NULL) {\r
+ if (AsciiFilePath != NULL) {\r
+ FreePool (AsciiFilePath);\r
+ }\r
+ if (TftpContext != NULL) {\r
+ FreePool (TftpContext);\r
+ }\r
+ gBS->CloseProtocol (\r
+ Mtftp4ChildHandle,\r
+ &gEfiMtftp4ProtocolGuid,\r
+ gImageHandle,\r
+ ControllerHandle\r
+ );\r
+ }\r
+ NetLibDestroyServiceChild (\r
+ ControllerHandle,\r
+ gImageHandle,\r
+ &gEfiMtftp4ServiceBindingProtocolGuid,\r
+ Mtftp4ChildHandle\r
+ );\r
+ }\r
+\r
if (EFI_ERROR (Status)) {\r
- FreePool (TftpBuffer);\r
- } else if (ImageSize != NULL) {\r
- *Image = (UINTN)TftpBuffer;\r
- *ImageSize = (UINTN)TftpBufferSize;\r
+ *Image = 0;\r
+ Print (L"Failed to download the file - Error=%r\n", Status);\r
}\r
\r
-EXIT:\r
- FreePool (AsciiPathName);\r
return Status;\r
}\r
\r
};\r
\r
EFI_STATUS\r
-BdsLoadImage (\r
- IN EFI_DEVICE_PATH *DevicePath,\r
+BdsLoadImageAndUpdateDevicePath (\r
+ IN OUT EFI_DEVICE_PATH **DevicePath,\r
IN EFI_ALLOCATE_TYPE Type,\r
IN OUT EFI_PHYSICAL_ADDRESS* Image,\r
OUT UINTN *FileSize\r
EFI_DEVICE_PATH *RemainingDevicePath;\r
BDS_FILE_LOADER* FileLoader;\r
\r
- Status = BdsConnectDevicePath (DevicePath, &Handle, &RemainingDevicePath);\r
+ Status = BdsConnectAndUpdateDevicePath (DevicePath, &Handle, &RemainingDevicePath);\r
if (EFI_ERROR (Status)) {\r
return Status;\r
}\r
\r
FileLoader = FileLoaders;\r
while (FileLoader->Support != NULL) {\r
- if (FileLoader->Support (DevicePath, Handle, RemainingDevicePath)) {\r
+ if (FileLoader->Support (*DevicePath, Handle, RemainingDevicePath)) {\r
return FileLoader->LoadImage (DevicePath, Handle, RemainingDevicePath, Type, Image, FileSize);\r
}\r
FileLoader++;\r
return EFI_UNSUPPORTED;\r
}\r
\r
+EFI_STATUS\r
+BdsLoadImage (\r
+ IN EFI_DEVICE_PATH *DevicePath,\r
+ IN EFI_ALLOCATE_TYPE Type,\r
+ IN OUT EFI_PHYSICAL_ADDRESS* Image,\r
+ OUT UINTN *FileSize\r
+ )\r
+{\r
+ return BdsLoadImageAndUpdateDevicePath (&DevicePath, Type, Image, FileSize);\r
+}\r
+\r
/**\r
Start an EFI Application from a Device Path\r
\r
EFI_LOADED_IMAGE_PROTOCOL* LoadedImage;\r
\r
// Find the nearest supported file loader\r
- Status = BdsLoadImage (DevicePath, AllocateAnyPages, &BinaryBuffer, &BinarySize);\r
+ Status = BdsLoadImageAndUpdateDevicePath (&DevicePath, AllocateAnyPages, &BinaryBuffer, &BinarySize);\r
if (EFI_ERROR (Status)) {\r
return Status;\r
}\r