\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
#define IS_DEVICE_PATH_NODE(node,type,subtype) (((node)->Type == (type)) && ((node)->SubType == (subtype)))\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
+\r
// Extract the FilePath from the Device Path\r
CHAR16*\r
BdsExtractFilePathFromDevicePath (\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
+ StrCpy (Progress, 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_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
- UINT64 TftpTransferSize;\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
+ EFI_DHCP4_CONFIG_DATA Dhcp4CfgData;\r
+ EFI_DHCP4_MODE_DATA Dhcp4Mode;\r
+ EFI_MTFTP4_CONFIG_DATA Mtftp4CfgData;\r
+ IPv4_DEVICE_PATH *IPv4DevicePathNode;\r
+ FILEPATH_DEVICE_PATH *FilePathDevicePathNode;\r
+ CHAR8 *AsciiFilePath;\r
+ EFI_MTFTP4_TOKEN Mtftp4Token;\r
+ UINT64 FileSize;\r
+ UINT64 TftpBufferSize;\r
+ BDS_TFTP_CONTEXT *TftpContext;\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
+ 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
+ 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
\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
+ //\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
- } while (Status == EFI_ALREADY_STARTED);\r
\r
- if (EFI_ERROR(Status)) {\r
- return Status;\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
- // Try to get the size (required the TFTP server to have "tsize" extension)\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
- // Pxe.Mtftp replies EFI_PROTOCOL_ERROR if tsize is not supported by the TFTP server\r
- if (EFI_ERROR (Status) && (Status != EFI_PROTOCOL_ERROR)) {\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
+\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
//\r
- // Two cases:\r
- // 1) the file size is unknown (tsize extension not supported)\r
- // 2) tsize returned the file size\r
+ // Convert the Unicode path of the file to Ascii\r
//\r
- if (Status == EFI_PROTOCOL_ERROR) {\r
- for (TftpBufferSize = SIZE_8MB; TftpBufferSize <= FixedPcdGet32 (PcdMaxTftpFileSize); TftpBufferSize += SIZE_8MB) {\r
- // Allocate a buffer to hold the whole file.\r
- Status = gBS->AllocatePages (\r
- Type,\r
- EfiBootServicesCode,\r
- EFI_SIZE_TO_PAGES (TftpBufferSize),\r
- Image\r
- );\r
- if (EFI_ERROR (Status)) {\r
- DEBUG ((EFI_D_ERROR, "Failed to allocate space for image: %r\n", Status));\r
- goto EXIT;\r
- }\r
\r
- TftpTransferSize = TftpBufferSize;\r
- Status = Pxe->Mtftp (\r
- Pxe,\r
- EFI_PXE_BASE_CODE_TFTP_READ_FILE,\r
- (VOID *)(UINTN)*Image,\r
- FALSE,\r
- &TftpTransferSize,\r
- NULL,\r
- &ServerIp,\r
- (UINT8*)AsciiPathName,\r
- NULL,\r
- FALSE\r
- );\r
- if (EFI_ERROR (Status)) {\r
- gBS->FreePages (*Image, EFI_SIZE_TO_PAGES (TftpBufferSize));\r
- } else {\r
- *ImageSize = (UINTN)TftpBufferSize;\r
- break;\r
- }\r
- }\r
+ FilePathDevicePathNode = (FILEPATH_DEVICE_PATH*)(IPv4DevicePathNode + 1);\r
+ AsciiFilePath = AllocatePool ((StrLen (FilePathDevicePathNode->PathName) + 1) * sizeof (CHAR8));\r
+ if (AsciiFilePath == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto Error;\r
+ }\r
+ UnicodeStrToAsciiStr (FilePathDevicePathNode->PathName, AsciiFilePath);\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_8MB;\r
+ }\r
+\r
+ TftpContext = AllocatePool (sizeof (BDS_TFTP_CONTEXT));\r
+ if (TftpContext == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto Error;\r
+ }\r
+ TftpContext->FileSize = FileSize;\r
+\r
+ for (; TftpBufferSize <= FixedPcdGet32 (PcdMaxTftpFileSize);\r
+ TftpBufferSize = (TftpBufferSize + SIZE_8MB) & (~(SIZE_8MB-1))) {\r
+ //\r
// Allocate a buffer to hold the whole file.\r
+ //\r
Status = gBS->AllocatePages (\r
Type,\r
EfiBootServicesCode,\r
Image\r
);\r
if (EFI_ERROR (Status)) {\r
- DEBUG ((EFI_D_ERROR, "Failed to allocate space for kernel image: %r\n", Status));\r
- goto EXIT;\r
+ Print (L"Failed to allocate space for image\n");\r
+ goto Error;\r
}\r
\r
- Status = Pxe->Mtftp (\r
- Pxe,\r
- EFI_PXE_BASE_CODE_TFTP_READ_FILE,\r
- (VOID *)(UINTN)*Image,\r
- FALSE,\r
- &TftpBufferSize,\r
- NULL,\r
- &ServerIp,\r
- (UINT8*)AsciiPathName,\r
- NULL,\r
- FALSE\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 <%s> from the TFTP server\n", FilePathDevicePathNode->PathName);\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
- } else {\r
- *ImageSize = (UINTN)TftpBufferSize;\r
+ if (Status == EFI_BUFFER_TOO_SMALL) {\r
+ Print (L"Downloading failed, file larger than expected.\n");\r
+ gBS->FreePages (*Image, EFI_SIZE_TO_PAGES (TftpBufferSize));\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
+ Print (L"Failed to download the file - Error=%r\n", Status);\r
}\r
\r
-EXIT:\r
- FreePool (AsciiPathName);\r
return Status;\r
}\r
\r
#include <Protocol/PxeBaseCode.h>\r
#include <Protocol/SimpleFileSystem.h>\r
#include <Protocol/SimpleNetwork.h>\r
+#include <Protocol/Dhcp4.h>\r
+#include <Protocol/Mtftp4.h>\r
\r
#include <Guid/FileSystemInfo.h>\r
\r
}\r
}\r
\r
+/**\r
+ Add to the list of boot devices the devices allowing a TFTP boot\r
+\r
+ @param[in] BdsLoadOptionList List of devices to boot from\r
+\r
+ @retval EFI_SUCCESS Update completed\r
+ @retval EFI_OUT_OF_RESOURCES Fail to perform the update due to lack of resource\r
+**/\r
EFI_STATUS\r
BdsLoadOptionTftpList (\r
IN OUT LIST_ENTRY* BdsLoadOptionList\r
)\r
{\r
- EFI_STATUS Status;\r
- UINTN HandleCount;\r
- EFI_HANDLE *HandleBuffer;\r
- UINTN Index;\r
- BDS_SUPPORTED_DEVICE *SupportedDevice;\r
- EFI_DEVICE_PATH_PROTOCOL* DevicePathProtocol;\r
- EFI_SIMPLE_NETWORK_PROTOCOL* SimpleNet;\r
- CHAR16 DeviceDescription[BOOT_DEVICE_DESCRIPTION_MAX];\r
- EFI_MAC_ADDRESS *Mac;\r
+ EFI_STATUS Status;\r
+ UINTN HandleCount;\r
+ EFI_HANDLE *HandleBuffer;\r
+ EFI_HANDLE Handle;\r
+ UINTN Index;\r
+ EFI_DEVICE_PATH_PROTOCOL *DevicePathProtocol;\r
+ VOID *Interface;\r
+ EFI_SIMPLE_NETWORK_PROTOCOL *SimpleNetworkProtocol;\r
+ BDS_SUPPORTED_DEVICE *SupportedDevice;\r
+ EFI_MAC_ADDRESS *Mac;\r
\r
- // List all the PXE Protocols\r
- Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiPxeBaseCodeProtocolGuid, NULL, &HandleCount, &HandleBuffer);\r
+ //\r
+ // List all the handles on which the Simple Network Protocol is installed.\r
+ //\r
+ Status = gBS->LocateHandleBuffer (\r
+ ByProtocol,\r
+ &gEfiSimpleNetworkProtocolGuid,\r
+ NULL,\r
+ &HandleCount,\r
+ &HandleBuffer\r
+ );\r
if (EFI_ERROR (Status)) {\r
return Status;\r
}\r
\r
for (Index = 0; Index < HandleCount; Index++) {\r
- // We only select the handle WITH a Device Path AND the PXE Protocol AND the TFTP Protocol (the TFTP protocol is required to start PXE)\r
- Status = gBS->HandleProtocol (HandleBuffer[Index], &gEfiDevicePathProtocolGuid, (VOID **)&DevicePathProtocol);\r
- if (!EFI_ERROR(Status)) {\r
- // Allocate BDS Supported Device structure\r
- SupportedDevice = (BDS_SUPPORTED_DEVICE*)AllocatePool(sizeof(BDS_SUPPORTED_DEVICE));\r
+ Handle = HandleBuffer[Index];\r
+ //\r
+ // We select the handles that support :\r
+ // . the Device Path Protocol\r
+ // . the MTFTP4 Protocol\r
+ //\r
+ Status = gBS->HandleProtocol (\r
+ Handle,\r
+ &gEfiDevicePathProtocolGuid,\r
+ (VOID **)&DevicePathProtocol\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ continue;\r
+ }\r
\r
- Status = gBS->LocateProtocol (&gEfiSimpleNetworkProtocolGuid, NULL, (VOID **)&SimpleNet);\r
- if (!EFI_ERROR(Status)) {\r
- Mac = &SimpleNet->Mode->CurrentAddress;\r
- UnicodeSPrint (DeviceDescription,BOOT_DEVICE_DESCRIPTION_MAX,L"MAC Address: %02x:%02x:%02x:%02x:%02x:%02x", Mac->Addr[0], Mac->Addr[1], Mac->Addr[2], Mac->Addr[3], Mac->Addr[4], Mac->Addr[5]);\r
- } else {\r
- Status = GenerateDeviceDescriptionName (HandleBuffer[Index], DeviceDescription);\r
- ASSERT_EFI_ERROR (Status);\r
- }\r
- UnicodeSPrint (SupportedDevice->Description,BOOT_DEVICE_DESCRIPTION_MAX,L"TFTP on %s",DeviceDescription);\r
+ Status = gBS->HandleProtocol (\r
+ Handle,\r
+ &gEfiMtftp4ServiceBindingProtocolGuid,\r
+ &Interface\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ continue;\r
+ }\r
\r
- SupportedDevice->DevicePathProtocol = DevicePathProtocol;\r
- SupportedDevice->Support = &BdsLoadOptionSupportList[BDS_DEVICE_TFTP];\r
+ Status = gBS->HandleProtocol (\r
+ Handle,\r
+ &gEfiSimpleNetworkProtocolGuid,\r
+ (VOID **)&SimpleNetworkProtocol\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ continue;\r
+ }\r
\r
- InsertTailList (BdsLoadOptionList,&SupportedDevice->Link);\r
+ // Allocate BDS Supported Device structure\r
+ SupportedDevice = (BDS_SUPPORTED_DEVICE*)AllocatePool (sizeof (BDS_SUPPORTED_DEVICE));\r
+ if (SupportedDevice == NULL) {\r
+ continue;\r
}\r
+\r
+ Mac = &SimpleNetworkProtocol->Mode->CurrentAddress;\r
+ UnicodeSPrint (\r
+ SupportedDevice->Description,\r
+ BOOT_DEVICE_DESCRIPTION_MAX,\r
+ L"TFTP on MAC Address: %02x:%02x:%02x:%02x:%02x:%02x",\r
+ Mac->Addr[0], Mac->Addr[1], Mac->Addr[2], Mac->Addr[3], Mac->Addr[4], Mac->Addr[5]\r
+ );\r
+\r
+ SupportedDevice->DevicePathProtocol = DevicePathProtocol;\r
+ SupportedDevice->Support = &BdsLoadOptionSupportList[BDS_DEVICE_TFTP];\r
+\r
+ InsertTailList (BdsLoadOptionList, &SupportedDevice->Link);\r
}\r
\r
return EFI_SUCCESS;\r
OUT EFI_DEVICE_PATH_PROTOCOL **DevicePathNodes\r
)\r
{\r
- EFI_STATUS Status;\r
- BOOLEAN IsDHCP;\r
- EFI_IP_ADDRESS LocalIp;\r
- EFI_IP_ADDRESS RemoteIp;\r
- IPv4_DEVICE_PATH* IPv4DevicePathNode;\r
- FILEPATH_DEVICE_PATH* FilePathDevicePath;\r
- CHAR16 BootFilePath[BOOT_DEVICE_FILEPATH_MAX];\r
- UINTN BootFilePathSize;\r
+ EFI_STATUS Status;\r
+ BOOLEAN IsDHCP;\r
+ EFI_IP_ADDRESS LocalIp;\r
+ EFI_IP_ADDRESS SubnetMask;\r
+ EFI_IP_ADDRESS GatewayIp;\r
+ EFI_IP_ADDRESS RemoteIp;\r
+ IPv4_DEVICE_PATH *IPv4DevicePathNode;\r
+ FILEPATH_DEVICE_PATH *FilePathDevicePath;\r
+ CHAR16 BootFilePath[BOOT_DEVICE_FILEPATH_MAX];\r
+ UINTN BootFilePathSize;\r
\r
- Print(L"Get the IP address from DHCP: ");\r
+ Print (L"Get the IP address from DHCP: ");\r
Status = GetHIInputBoolean (&IsDHCP);\r
- if (EFI_ERROR(Status)) {\r
+ if (EFI_ERROR (Status)) {\r
return EFI_ABORTED;\r
}\r
\r
if (!IsDHCP) {\r
- Print(L"Get the static IP address: ");\r
+ Print (L"Local static IP address: ");\r
Status = GetHIInputIP (&LocalIp);\r
- if (EFI_ERROR(Status)) {\r
+ if (EFI_ERROR (Status)) {\r
+ return EFI_ABORTED;\r
+ }\r
+ Print (L"Get the network mask: ");\r
+ Status = GetHIInputIP (&SubnetMask);\r
+ if (EFI_ERROR (Status)) {\r
+ return EFI_ABORTED;\r
+ }\r
+ Print (L"Get the gateway IP address: ");\r
+ Status = GetHIInputIP (&GatewayIp);\r
+ if (EFI_ERROR (Status)) {\r
return EFI_ABORTED;\r
}\r
}\r
\r
- Print(L"Get the TFTP server IP address: ");\r
+ Print (L"Get the TFTP server IP address: ");\r
Status = GetHIInputIP (&RemoteIp);\r
- if (EFI_ERROR(Status)) {\r
+ if (EFI_ERROR (Status)) {\r
return EFI_ABORTED;\r
}\r
\r
- Print(L"File path of the %s : ", FileName);\r
+ Print (L"File path of the %s : ", FileName);\r
Status = GetHIInputStr (BootFilePath, BOOT_DEVICE_FILEPATH_MAX);\r
- if (EFI_ERROR(Status)) {\r
+ if (EFI_ERROR (Status)) {\r
return EFI_ABORTED;\r
}\r
\r
IPv4DevicePathNode->Header.Type = MESSAGING_DEVICE_PATH;\r
IPv4DevicePathNode->Header.SubType = MSG_IPv4_DP;\r
SetDevicePathNodeLength (&IPv4DevicePathNode->Header, sizeof(IPv4_DEVICE_PATH));\r
- CopyMem (&IPv4DevicePathNode->LocalIpAddress, &LocalIp.v4, sizeof (EFI_IPv4_ADDRESS));\r
+\r
+ if (!IsDHCP) {\r
+ CopyMem (&IPv4DevicePathNode->LocalIpAddress, &LocalIp.v4, sizeof (EFI_IPv4_ADDRESS));\r
+ CopyMem (&IPv4DevicePathNode->SubnetMask, &SubnetMask.v4, sizeof (EFI_IPv4_ADDRESS));\r
+ CopyMem (&IPv4DevicePathNode->GatewayIpAddress, &GatewayIp.v4, sizeof (EFI_IPv4_ADDRESS));\r
+ }\r
+\r
CopyMem (&IPv4DevicePathNode->RemoteIpAddress, &RemoteIp.v4, sizeof (EFI_IPv4_ADDRESS));\r
IPv4DevicePathNode->LocalPort = 0;\r
IPv4DevicePathNode->RemotePort = 0;\r
IPv4_DEVICE_PATH Ipv4Node;\r
BOOLEAN IsDHCP;\r
EFI_IP_ADDRESS OldIp;\r
+ EFI_IP_ADDRESS OldSubnetMask;\r
+ EFI_IP_ADDRESS OldGatewayIp;\r
EFI_IP_ADDRESS LocalIp;\r
+ EFI_IP_ADDRESS SubnetMask;\r
+ EFI_IP_ADDRESS GatewayIp;\r
EFI_IP_ADDRESS RemoteIp;\r
UINT8 *FileNodePtr;\r
CHAR16 BootFilePath[BOOT_DEVICE_FILEPATH_MAX];\r
if (!IsDHCP) {\r
Print (L"Local static IP address: ");\r
if (Ipv4Node.StaticIpAddress) {\r
- // Copy local IPv4 address into IPv4 or IPv6 union\r
CopyMem (&OldIp.v4, &Ipv4Node.LocalIpAddress, sizeof (EFI_IPv4_ADDRESS));\r
-\r
Status = EditHIInputIP (&OldIp, &LocalIp);\r
} else {\r
Status = GetHIInputIP (&LocalIp);\r
if (EFI_ERROR (Status)) {\r
goto ErrorExit;\r
}\r
+\r
+ Print (L"Get the network mask: ");\r
+ if (Ipv4Node.StaticIpAddress) {\r
+ CopyMem (&OldSubnetMask.v4, &Ipv4Node.SubnetMask, sizeof (EFI_IPv4_ADDRESS));\r
+ Status = EditHIInputIP (&OldSubnetMask, &SubnetMask);\r
+ } else {\r
+ Status = GetHIInputIP (&SubnetMask);\r
+ }\r
+ if (EFI_ERROR (Status)) {\r
+ goto ErrorExit;\r
+ }\r
+\r
+ Print (L"Get the gateway IP address: ");\r
+ if (Ipv4Node.StaticIpAddress) {\r
+ CopyMem (&OldGatewayIp.v4, &Ipv4Node.GatewayIpAddress, sizeof (EFI_IPv4_ADDRESS));\r
+ Status = EditHIInputIP (&OldGatewayIp, &GatewayIp);\r
+ } else {\r
+ Status = GetHIInputIP (&GatewayIp);\r
+ }\r
+ if (EFI_ERROR (Status)) {\r
+ goto ErrorExit;\r
+ }\r
}\r
\r
Print (L"TFTP server IP address: ");\r
//\r
// Update the IPv4 node. IPv6 case not handled yet.\r
//\r
- if (IsDHCP == TRUE) {\r
+ if (IsDHCP) {\r
Ipv4Node.StaticIpAddress = FALSE;\r
+ ZeroMem (&Ipv4Node.LocalIpAddress, sizeof (EFI_IPv4_ADDRESS));\r
+ ZeroMem (&Ipv4Node.SubnetMask, sizeof (EFI_IPv4_ADDRESS));\r
+ ZeroMem (&Ipv4Node.GatewayIpAddress, sizeof (EFI_IPv4_ADDRESS));\r
} else {\r
Ipv4Node.StaticIpAddress = TRUE;\r
+ CopyMem (&Ipv4Node.LocalIpAddress, &LocalIp.v4, sizeof (EFI_IPv4_ADDRESS));\r
+ CopyMem (&Ipv4Node.SubnetMask, &SubnetMask.v4, sizeof (EFI_IPv4_ADDRESS));\r
+ CopyMem (&Ipv4Node.GatewayIpAddress, &GatewayIp.v4, sizeof (EFI_IPv4_ADDRESS));\r
}\r
- CopyMem (&Ipv4Node.LocalIpAddress, &LocalIp.v4, sizeof (EFI_IPv4_ADDRESS));\r
+\r
CopyMem (&Ipv4Node.RemoteIpAddress, &RemoteIp.v4, sizeof (EFI_IPv4_ADDRESS));\r
CopyMem (Ipv4NodePtr, &Ipv4Node, sizeof (IPv4_DEVICE_PATH));\r
\r