From 061568e2d5f21aeafa942891b15768c57fa0ffac Mon Sep 17 00:00:00 2001 From: Ronald Cron Date: Fri, 12 Dec 2014 19:14:22 +0000 Subject: [PATCH] ArmPkg/BdsLib: Rework TFTP boot Rework the downloading of an image from a TFTP server to do not depend on any "PXE specific" setting of the DHCP server. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Ronald Cron Reviewed-by: Olivier Martin git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@16516 6f19259b-4bc3-4df7-8a09-765794883524 --- ArmPkg/ArmPkg.dsc | 4 + ArmPkg/Library/BdsLib/BdsFilePath.c | 605 +++++++++++++++++++------ ArmPkg/Library/BdsLib/BdsInternal.h | 5 + ArmPkg/Library/BdsLib/BdsLib.inf | 6 + ArmPlatformPkg/Bds/Bds.inf | 2 + ArmPlatformPkg/Bds/BootOptionSupport.c | 195 ++++++-- 6 files changed, 637 insertions(+), 180 deletions(-) diff --git a/ArmPkg/ArmPkg.dsc b/ArmPkg/ArmPkg.dsc index 5d9eb177e0..be5126ccef 100644 --- a/ArmPkg/ArmPkg.dsc +++ b/ArmPkg/ArmPkg.dsc @@ -55,6 +55,10 @@ PeCoffLib|MdePkg/Library/BasePeCoffLib/BasePeCoffLib.inf PeCoffGetEntryPointLib|MdePkg/Library/BasePeCoffGetEntryPointLib/BasePeCoffGetEntryPointLib.inf + NetLib|MdeModulePkg/Library/DxeNetLib/DxeNetLib.inf + UefiHiiServicesLib|MdeModulePkg/Library/UefiHiiServicesLib/UefiHiiServicesLib.inf + HiiLib|MdeModulePkg/Library/UefiHiiLib/UefiHiiLib.inf + SemihostLib|ArmPkg/Library/SemihostLib/SemihostLib.inf UncachedMemoryAllocationLib|ArmPkg/Library/UncachedMemoryAllocationLib/UncachedMemoryAllocationLib.inf DxeServicesTableLib|MdePkg/Library/DxeServicesTableLib/DxeServicesTableLib.inf diff --git a/ArmPkg/Library/BdsLib/BdsFilePath.c b/ArmPkg/Library/BdsLib/BdsFilePath.c index f26ba39ddb..0057d94126 100644 --- a/ArmPkg/Library/BdsLib/BdsFilePath.c +++ b/ArmPkg/Library/BdsLib/BdsFilePath.c @@ -14,13 +14,41 @@ #include "BdsInternal.h" +#include + +#include #include #include #include #include +#include +#include #define IS_DEVICE_PATH_NODE(node,type,subtype) (((node)->Type == (type)) && ((node)->SubType == (subtype))) +/* + Constant strings and define related to the message indicating the amount of + progress in the dowloading of a TFTP file. +*/ + +// Frame for the progression slider +STATIC CONST CHAR16 mTftpProgressFrame[] = L"[ ]"; + +// Number of steps in the progression slider +#define TFTP_PROGRESS_SLIDER_STEPS ((sizeof (mTftpProgressFrame) / sizeof (CHAR16)) - 3) + +// Size in number of characters plus one (final zero) of the message to +// indicate the progress of a tftp download. The format is "[(progress slider: +// 40 characters)] (nb of KBytes downloaded so far: 7 characters) Kb". There +// are thus the number of characters in mTftpProgressFrame[] plus 11 characters +// (2 // spaces, "Kb" and seven characters for the number of KBytes). +#define TFTP_PROGRESS_MESSAGE_SIZE ((sizeof (mTftpProgressFrame) / sizeof (CHAR16)) + 12) + +// String to delete the tftp progress message to be able to update it : +// (TFTP_PROGRESS_MESSAGE_SIZE-1) '\b' +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"; + + // Extract the FilePath from the Device Path CHAR16* BdsExtractFilePathFromDevicePath ( @@ -723,14 +751,14 @@ BdsPxeLoadImage ( BOOLEAN BdsTftpSupport ( - IN EFI_DEVICE_PATH* DevicePath, - IN EFI_HANDLE Handle, - IN EFI_DEVICE_PATH* RemainingDevicePath + IN EFI_DEVICE_PATH *DevicePath, + IN EFI_HANDLE Handle, + IN EFI_DEVICE_PATH *RemainingDevicePath ) { - EFI_STATUS Status; + EFI_STATUS Status; EFI_DEVICE_PATH *NextDevicePath; - EFI_PXE_BASE_CODE_PROTOCOL *PxeBcProtocol; + VOID *Interface; // Validate the Remaining Device Path if (IsDevicePathEnd (RemainingDevicePath)) { @@ -748,151 +776,412 @@ BdsTftpSupport ( return FALSE; } - Status = gBS->HandleProtocol (Handle, &gEfiPxeBaseCodeProtocolGuid, (VOID **)&PxeBcProtocol); + Status = gBS->HandleProtocol ( + Handle, &gEfiDevicePathProtocolGuid, + &Interface + ); if (EFI_ERROR (Status)) { return FALSE; - } else { - return TRUE; } + + // + // Check that the controller (identified by its handle "Handle") supports the + // MTFTPv4 Service Binding Protocol. If it does, it means that it supports the + // EFI MTFTPv4 Protocol needed to download the image through TFTP. + // + Status = gBS->HandleProtocol ( + Handle, &gEfiMtftp4ServiceBindingProtocolGuid, + &Interface + ); + if (EFI_ERROR (Status)) { + return FALSE; + } + + return TRUE; +} + +/** + Worker function that get the size in numbers of bytes of a file from a TFTP + server before to download the file. + + @param[in] Mtftp4 MTFTP4 protocol interface + @param[in] FilePath Path of the file, Ascii encoded + @param[out] FileSize Address where to store the file size in number of + bytes. + + @retval EFI_SUCCESS The size of the file was returned. + @retval !EFI_SUCCESS The size of the file was not returned. + +**/ +STATIC +EFI_STATUS +Mtftp4GetFileSize ( + IN EFI_MTFTP4_PROTOCOL *Mtftp4, + IN CHAR8 *FilePath, + OUT UINT64 *FileSize + ) +{ + EFI_STATUS Status; + EFI_MTFTP4_OPTION ReqOpt[1]; + EFI_MTFTP4_PACKET *Packet; + UINT32 PktLen; + EFI_MTFTP4_OPTION *TableOfOptions; + EFI_MTFTP4_OPTION *Option; + UINT32 OptCnt; + UINT8 OptBuf[128]; + + ReqOpt[0].OptionStr = (UINT8*)"tsize"; + OptBuf[0] = '0'; + OptBuf[1] = 0; + ReqOpt[0].ValueStr = OptBuf; + + Status = Mtftp4->GetInfo ( + Mtftp4, + NULL, + (UINT8*)FilePath, + NULL, + 1, + ReqOpt, + &PktLen, + &Packet + ); + + if (EFI_ERROR (Status)) { + goto Error; + } + + Status = Mtftp4->ParseOptions ( + Mtftp4, + PktLen, + Packet, + (UINT32 *) &OptCnt, + &TableOfOptions + ); + if (EFI_ERROR (Status)) { + goto Error; + } + + Option = TableOfOptions; + while (OptCnt != 0) { + if (AsciiStrnCmp ((CHAR8 *)Option->OptionStr, "tsize", 5) == 0) { + *FileSize = AsciiStrDecimalToUint64 ((CHAR8 *)Option->ValueStr); + break; + } + OptCnt--; + Option++; + } + FreePool (TableOfOptions); + + if (OptCnt == 0) { + Status = EFI_UNSUPPORTED; + } + +Error : + + return Status; } +/** + Update the progress of a file download + This procedure is called each time a new TFTP packet is received. + + @param[in] This MTFTP4 protocol interface + @param[in] Token Parameters for the download of the file + @param[in] PacketLen Length of the packet + @param[in] Packet Address of the packet + + @retval EFI_SUCCESS All packets are accepted. + +**/ +STATIC +EFI_STATUS +Mtftp4CheckPacket ( + IN EFI_MTFTP4_PROTOCOL *This, + IN EFI_MTFTP4_TOKEN *Token, + IN UINT16 PacketLen, + IN EFI_MTFTP4_PACKET *Packet + ) +{ + BDS_TFTP_CONTEXT *Context; + CHAR16 Progress[TFTP_PROGRESS_MESSAGE_SIZE]; + UINT64 NbOfKb; + UINTN Index; + UINTN LastStep; + UINTN Step; + UINT64 LastNbOf50Kb; + UINT64 NbOf50Kb; + + if ((NTOHS (Packet->OpCode)) == EFI_MTFTP4_OPCODE_DATA) { + Context = (BDS_TFTP_CONTEXT*)Token->Context; + + if (Context->DownloadedNbOfBytes == 0) { + if (Context->FileSize > 0) { + Print (L"%s 0 Kb", mTftpProgressFrame); + } else { + Print (L" 0 Kb"); + } + } + + // + // The data is the packet are prepended with two UINT16 : + // . OpCode = EFI_MTFTP4_OPCODE_DATA + // . Block = the number of this block of data + // + Context->DownloadedNbOfBytes += PacketLen - sizeof (Packet->OpCode) - sizeof (Packet->Data.Block); + NbOfKb = Context->DownloadedNbOfBytes / 1024; + + Progress[0] = L'\0'; + if (Context->FileSize > 0) { + LastStep = (Context->LastReportedNbOfBytes * TFTP_PROGRESS_SLIDER_STEPS) / Context->FileSize; + Step = (Context->DownloadedNbOfBytes * TFTP_PROGRESS_SLIDER_STEPS) / Context->FileSize; + if (Step > LastStep) { + Print (mTftpProgressDelete); + StrCpy (Progress, mTftpProgressFrame); + for (Index = 1; Index < Step; Index++) { + Progress[Index] = L'='; + } + Progress[Step] = L'>'; + + UnicodeSPrint ( + Progress + (sizeof (mTftpProgressFrame) / sizeof (CHAR16)) - 1, + sizeof (Progress) - sizeof (mTftpProgressFrame), + L" %7d Kb", + NbOfKb + ); + Context->LastReportedNbOfBytes = Context->DownloadedNbOfBytes; + } + } else { + // + // Case when we do not know the size of the final file. + // We print the updated size every 50KB of downloaded data + // + LastNbOf50Kb = Context->LastReportedNbOfBytes / (50*1024); + NbOf50Kb = Context->DownloadedNbOfBytes / (50*1024); + if (NbOf50Kb > LastNbOf50Kb) { + Print (L"\b\b\b\b\b\b\b\b\b\b"); + UnicodeSPrint (Progress, sizeof (Progress), L"%7d Kb", NbOfKb); + Context->LastReportedNbOfBytes = Context->DownloadedNbOfBytes; + } + } + if (Progress[0] != L'\0') { + Print (L"%s", Progress); + } + } + + return EFI_SUCCESS; +} + +/** + Download an image from a TFTP server + + @param[in] DevicePath Device path of the TFTP boot option + @param[in] ControllerHandle Handle of the network controller + @param[in] RemainingDevicePath Device path of the TFTP boot option but + the first node that identifies the network controller + @param[in] Type Type to allocate memory pages + @param[out] Image Address of the bufer where the image is stored in + case of success + @param[out] ImageSize Size in number of bytes of the i;age in case of + success + + @retval EFI_SUCCESS The image was returned. + @retval !EFI_SUCCESS Something went wrong. + +**/ EFI_STATUS BdsTftpLoadImage ( IN EFI_DEVICE_PATH* DevicePath, - IN EFI_HANDLE Handle, + IN EFI_HANDLE ControllerHandle, IN EFI_DEVICE_PATH* RemainingDevicePath, IN EFI_ALLOCATE_TYPE Type, IN OUT EFI_PHYSICAL_ADDRESS *Image, OUT UINTN *ImageSize ) { - EFI_STATUS Status; - EFI_PXE_BASE_CODE_PROTOCOL *Pxe; - UINT64 TftpBufferSize; - UINT64 TftpTransferSize; - EFI_IP_ADDRESS ServerIp; - IPv4_DEVICE_PATH* IPv4DevicePathNode; - FILEPATH_DEVICE_PATH* FilePathDevicePath; - EFI_IP_ADDRESS LocalIp; - CHAR8* AsciiPathName; - EFI_SIMPLE_NETWORK_PROTOCOL *Snp; + EFI_STATUS Status; + EFI_HANDLE Dhcp4ChildHandle; + EFI_DHCP4_PROTOCOL *Dhcp4; + BOOLEAN Dhcp4ToStop; + EFI_HANDLE Mtftp4ChildHandle; + EFI_MTFTP4_PROTOCOL *Mtftp4; + EFI_DHCP4_CONFIG_DATA Dhcp4CfgData; + EFI_DHCP4_MODE_DATA Dhcp4Mode; + EFI_MTFTP4_CONFIG_DATA Mtftp4CfgData; + IPv4_DEVICE_PATH *IPv4DevicePathNode; + FILEPATH_DEVICE_PATH *FilePathDevicePathNode; + CHAR8 *AsciiFilePath; + EFI_MTFTP4_TOKEN Mtftp4Token; + UINT64 FileSize; + UINT64 TftpBufferSize; + BDS_TFTP_CONTEXT *TftpContext; ASSERT(IS_DEVICE_PATH_NODE (RemainingDevicePath, MESSAGING_DEVICE_PATH, MSG_IPv4_DP)); - IPv4DevicePathNode = (IPv4_DEVICE_PATH*)RemainingDevicePath; - FilePathDevicePath = (FILEPATH_DEVICE_PATH*)(IPv4DevicePathNode + 1); - Status = gBS->LocateProtocol (&gEfiPxeBaseCodeProtocolGuid, NULL, (VOID **)&Pxe); - if (EFI_ERROR (Status)) { - return Status; + Dhcp4ChildHandle = NULL; + Dhcp4 = NULL; + Dhcp4ToStop = FALSE; + Mtftp4ChildHandle = NULL; + Mtftp4 = NULL; + AsciiFilePath = NULL; + TftpContext = NULL; + + if (!IPv4DevicePathNode->StaticIpAddress) { + // + // Using the DHCP4 Service Binding Protocol, create a child handle of the DHCP4 service and + // install the DHCP4 protocol on it. Then, open the DHCP protocol. + // + Status = NetLibCreateServiceChild ( + ControllerHandle, + gImageHandle, + &gEfiDhcp4ServiceBindingProtocolGuid, + &Dhcp4ChildHandle + ); + if (!EFI_ERROR (Status)) { + Status = gBS->OpenProtocol ( + Dhcp4ChildHandle, + &gEfiDhcp4ProtocolGuid, + (VOID **) &Dhcp4, + gImageHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + } + if (EFI_ERROR (Status)) { + Print (L"Unable to open DHCP4 protocol\n"); + goto Error; + } } - Status = Pxe->Start (Pxe, FALSE); - if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) { - return Status; + // + // Using the MTFTP4 Service Binding Protocol, create a child handle of the MTFTP4 service and + // install the MTFTP4 protocol on it. Then, open the MTFTP4 protocol. + // + Status = NetLibCreateServiceChild ( + ControllerHandle, + gImageHandle, + &gEfiMtftp4ServiceBindingProtocolGuid, + &Mtftp4ChildHandle + ); + if (!EFI_ERROR (Status)) { + Status = gBS->OpenProtocol ( + Mtftp4ChildHandle, + &gEfiMtftp4ProtocolGuid, + (VOID **) &Mtftp4, + gImageHandle, + ControllerHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + } + if (EFI_ERROR (Status)) { + Print (L"Unable to open MTFTP4 protocol\n"); + goto Error; } - do { - if (!IPv4DevicePathNode->StaticIpAddress) { - Status = Pxe->Dhcp (Pxe, TRUE); - } else { - CopyMem (&LocalIp.v4, &IPv4DevicePathNode->LocalIpAddress, sizeof (EFI_IPv4_ADDRESS)); - Status = Pxe->SetStationIp (Pxe, &LocalIp, NULL); + if (!IPv4DevicePathNode->StaticIpAddress) { + // + // Configure the DHCP4, all default settings. It is acceptable for the configuration to + // fail if the return code is equal to EFI_ACCESS_DENIED which means that the configuration + // has been done by another instance of the DHCP4 protocol or that the DHCP configuration + // process has been started but is not completed yet. + // + ZeroMem (&Dhcp4CfgData, sizeof (EFI_DHCP4_CONFIG_DATA)); + Status = Dhcp4->Configure (Dhcp4, &Dhcp4CfgData); + if (EFI_ERROR (Status)) { + if (Status != EFI_ACCESS_DENIED) { + Print (L"Error while configuring the DHCP4 protocol\n"); + goto Error; + } } - // If an IP Address has already been set and a different static IP address is requested then restart - // the Network service. - if (Status == EFI_ALREADY_STARTED) { - Status = gBS->LocateProtocol (&gEfiSimpleNetworkProtocolGuid, NULL, (VOID **)&Snp); - if (!EFI_ERROR (Status) && IPv4DevicePathNode->StaticIpAddress && - (CompareMem (&Snp->Mode->CurrentAddress, &IPv4DevicePathNode->LocalIpAddress, sizeof(EFI_MAC_ADDRESS)) != 0)) - { - Pxe->Stop (Pxe); - Status = Pxe->Start (Pxe, FALSE); - if (EFI_ERROR(Status)) { - break; - } - // After restarting the PXE protocol, we want to try again with our new IP Address - Status = EFI_ALREADY_STARTED; + // + // Start the DHCP configuration. This may have already been done thus do not leave in error + // if the return code is EFI_ALREADY_STARTED. + // + Status = Dhcp4->Start (Dhcp4, NULL); + if (EFI_ERROR (Status)) { + if (Status != EFI_ALREADY_STARTED) { + Print (L"DHCP configuration failed\n"); + goto Error; } + } else { + Dhcp4ToStop = TRUE; } - } while (Status == EFI_ALREADY_STARTED); - if (EFI_ERROR(Status)) { - return Status; + Status = Dhcp4->GetModeData (Dhcp4, &Dhcp4Mode); + if (EFI_ERROR (Status)) { + goto Error; + } + + if (Dhcp4Mode.State != Dhcp4Bound) { + Status = EFI_TIMEOUT; + Print (L"DHCP configuration failed\n"); + goto Error; + } } - CopyMem (&ServerIp.v4, &IPv4DevicePathNode->RemoteIpAddress, sizeof (EFI_IPv4_ADDRESS)); + // + // Configure the TFTP4 protocol + // - // Convert the Unicode PathName to Ascii - AsciiPathName = AllocatePool ((StrLen (FilePathDevicePath->PathName) + 1) * sizeof (CHAR8)); - if (AsciiPathName == NULL) { - return EFI_OUT_OF_RESOURCES; + ZeroMem (&Mtftp4CfgData, sizeof (EFI_MTFTP4_CONFIG_DATA)); + Mtftp4CfgData.UseDefaultSetting = FALSE; + Mtftp4CfgData.TimeoutValue = 4; + Mtftp4CfgData.TryCount = 6; + + if (IPv4DevicePathNode->StaticIpAddress) { + CopyMem (&Mtftp4CfgData.StationIp , &IPv4DevicePathNode->LocalIpAddress, sizeof (EFI_IPv4_ADDRESS)); + CopyMem (&Mtftp4CfgData.SubnetMask, &IPv4DevicePathNode->SubnetMask, sizeof (EFI_IPv4_ADDRESS)); + CopyMem (&Mtftp4CfgData.GatewayIp , &IPv4DevicePathNode->GatewayIpAddress, sizeof (EFI_IPv4_ADDRESS)); + } else { + CopyMem (&Mtftp4CfgData.StationIp , &Dhcp4Mode.ClientAddress, sizeof (EFI_IPv4_ADDRESS)); + CopyMem (&Mtftp4CfgData.SubnetMask, &Dhcp4Mode.SubnetMask , sizeof (EFI_IPv4_ADDRESS)); + CopyMem (&Mtftp4CfgData.GatewayIp , &Dhcp4Mode.RouterAddress, sizeof (EFI_IPv4_ADDRESS)); } - UnicodeStrToAsciiStr (FilePathDevicePath->PathName, AsciiPathName); - - // Try to get the size (required the TFTP server to have "tsize" extension) - Status = Pxe->Mtftp ( - Pxe, - EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE, - NULL, - FALSE, - &TftpBufferSize, - NULL, - &ServerIp, - (UINT8*)AsciiPathName, - NULL, - FALSE - ); - // Pxe.Mtftp replies EFI_PROTOCOL_ERROR if tsize is not supported by the TFTP server - if (EFI_ERROR (Status) && (Status != EFI_PROTOCOL_ERROR)) { - if (Status == EFI_TFTP_ERROR) { - DEBUG((EFI_D_ERROR, "TFTP Error: Fail to get the size of the file\n")); - } - goto EXIT; + + CopyMem (&Mtftp4CfgData.ServerIp , &IPv4DevicePathNode->RemoteIpAddress, sizeof (EFI_IPv4_ADDRESS)); + + Status = Mtftp4->Configure (Mtftp4, &Mtftp4CfgData); + if (EFI_ERROR (Status)) { + Print (L"Error while configuring the MTFTP4 protocol\n"); + goto Error; } // - // Two cases: - // 1) the file size is unknown (tsize extension not supported) - // 2) tsize returned the file size + // Convert the Unicode path of the file to Ascii // - if (Status == EFI_PROTOCOL_ERROR) { - for (TftpBufferSize = SIZE_8MB; TftpBufferSize <= FixedPcdGet32 (PcdMaxTftpFileSize); TftpBufferSize += SIZE_8MB) { - // Allocate a buffer to hold the whole file. - Status = gBS->AllocatePages ( - Type, - EfiBootServicesCode, - EFI_SIZE_TO_PAGES (TftpBufferSize), - Image - ); - if (EFI_ERROR (Status)) { - DEBUG ((EFI_D_ERROR, "Failed to allocate space for image: %r\n", Status)); - goto EXIT; - } - TftpTransferSize = TftpBufferSize; - Status = Pxe->Mtftp ( - Pxe, - EFI_PXE_BASE_CODE_TFTP_READ_FILE, - (VOID *)(UINTN)*Image, - FALSE, - &TftpTransferSize, - NULL, - &ServerIp, - (UINT8*)AsciiPathName, - NULL, - FALSE - ); - if (EFI_ERROR (Status)) { - gBS->FreePages (*Image, EFI_SIZE_TO_PAGES (TftpBufferSize)); - } else { - *ImageSize = (UINTN)TftpBufferSize; - break; - } - } + FilePathDevicePathNode = (FILEPATH_DEVICE_PATH*)(IPv4DevicePathNode + 1); + AsciiFilePath = AllocatePool ((StrLen (FilePathDevicePathNode->PathName) + 1) * sizeof (CHAR8)); + if (AsciiFilePath == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + UnicodeStrToAsciiStr (FilePathDevicePathNode->PathName, AsciiFilePath); + + // + // Try to get the size of the file in bytes from the server. If it fails, + // start with a 8MB buffer to download the file. + // + FileSize = 0; + if (Mtftp4GetFileSize (Mtftp4, AsciiFilePath, &FileSize) == EFI_SUCCESS) { + TftpBufferSize = FileSize; } else { + TftpBufferSize = SIZE_8MB; + } + + TftpContext = AllocatePool (sizeof (BDS_TFTP_CONTEXT)); + if (TftpContext == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + TftpContext->FileSize = FileSize; + + for (; TftpBufferSize <= FixedPcdGet32 (PcdMaxTftpFileSize); + TftpBufferSize = (TftpBufferSize + SIZE_8MB) & (~(SIZE_8MB-1))) { + // // Allocate a buffer to hold the whole file. + // Status = gBS->AllocatePages ( Type, EfiBootServicesCode, @@ -900,31 +1189,85 @@ BdsTftpLoadImage ( Image ); if (EFI_ERROR (Status)) { - DEBUG ((EFI_D_ERROR, "Failed to allocate space for kernel image: %r\n", Status)); - goto EXIT; + Print (L"Failed to allocate space for image\n"); + goto Error; } - Status = Pxe->Mtftp ( - Pxe, - EFI_PXE_BASE_CODE_TFTP_READ_FILE, - (VOID *)(UINTN)*Image, - FALSE, - &TftpBufferSize, - NULL, - &ServerIp, - (UINT8*)AsciiPathName, - NULL, - FALSE - ); + TftpContext->DownloadedNbOfBytes = 0; + TftpContext->LastReportedNbOfBytes = 0; + + ZeroMem (&Mtftp4Token, sizeof (EFI_MTFTP4_TOKEN)); + Mtftp4Token.Filename = (UINT8*)AsciiFilePath; + Mtftp4Token.BufferSize = TftpBufferSize; + Mtftp4Token.Buffer = (VOID *)(UINTN)*Image; + Mtftp4Token.CheckPacket = Mtftp4CheckPacket; + Mtftp4Token.Context = (VOID*)TftpContext; + + Print (L"Downloading the file <%s> from the TFTP server\n", FilePathDevicePathNode->PathName); + Status = Mtftp4->ReadFile (Mtftp4, &Mtftp4Token); + Print (L"\n"); if (EFI_ERROR (Status)) { - gBS->FreePages (*Image, EFI_SIZE_TO_PAGES (TftpBufferSize)); - } else { - *ImageSize = (UINTN)TftpBufferSize; + if (Status == EFI_BUFFER_TOO_SMALL) { + Print (L"Downloading failed, file larger than expected.\n"); + gBS->FreePages (*Image, EFI_SIZE_TO_PAGES (TftpBufferSize)); + continue; + } else { + goto Error; + } } + + *ImageSize = Mtftp4Token.BufferSize; + break; + } + +Error: + if (Dhcp4ChildHandle != NULL) { + if (Dhcp4 != NULL) { + if (Dhcp4ToStop) { + Dhcp4->Stop (Dhcp4); + } + gBS->CloseProtocol ( + Dhcp4ChildHandle, + &gEfiDhcp4ProtocolGuid, + gImageHandle, + ControllerHandle + ); + } + NetLibDestroyServiceChild ( + ControllerHandle, + gImageHandle, + &gEfiDhcp4ServiceBindingProtocolGuid, + Dhcp4ChildHandle + ); + } + + if (Mtftp4ChildHandle != NULL) { + if (Mtftp4 != NULL) { + if (AsciiFilePath != NULL) { + FreePool (AsciiFilePath); + } + if (TftpContext != NULL) { + FreePool (TftpContext); + } + gBS->CloseProtocol ( + Mtftp4ChildHandle, + &gEfiMtftp4ProtocolGuid, + gImageHandle, + ControllerHandle + ); + } + NetLibDestroyServiceChild ( + ControllerHandle, + gImageHandle, + &gEfiMtftp4ServiceBindingProtocolGuid, + Mtftp4ChildHandle + ); + } + + if (EFI_ERROR (Status)) { + Print (L"Failed to download the file - Error=%r\n", Status); } -EXIT: - FreePool (AsciiPathName); return Status; } diff --git a/ArmPkg/Library/BdsLib/BdsInternal.h b/ArmPkg/Library/BdsLib/BdsInternal.h index 9b5d3a4ff6..602fd8dcf3 100644 --- a/ArmPkg/Library/BdsLib/BdsInternal.h +++ b/ArmPkg/Library/BdsLib/BdsInternal.h @@ -71,6 +71,11 @@ typedef struct _BDS_SYSTEM_MEMORY_RESOURCE { UINT64 ResourceLength; } BDS_SYSTEM_MEMORY_RESOURCE; +typedef struct { + UINT64 FileSize; + UINT64 DownloadedNbOfBytes; + UINT64 LastReportedNbOfBytes; +} BDS_TFTP_CONTEXT; // BdsHelper.c EFI_STATUS diff --git a/ArmPkg/Library/BdsLib/BdsLib.inf b/ArmPkg/Library/BdsLib/BdsLib.inf index 02409e1392..6d6a2dfa8a 100644 --- a/ArmPkg/Library/BdsLib/BdsLib.inf +++ b/ArmPkg/Library/BdsLib/BdsLib.inf @@ -37,6 +37,7 @@ [Packages] MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec EmbeddedPkg/EmbeddedPkg.dec ArmPkg/ArmPkg.dec ArmPlatformPkg/ArmPlatformPkg.dec @@ -53,6 +54,7 @@ SerialPortLib FdtLib TimerLib + NetLib [LibraryClasses.AARCH64] ArmGicLib @@ -74,6 +76,10 @@ gEfiUsbIoProtocolGuid gEfiLoadedImageProtocolGuid gEfiSimpleNetworkProtocolGuid + gEfiDhcp4ServiceBindingProtocolGuid + gEfiDhcp4ProtocolGuid + gEfiMtftp4ServiceBindingProtocolGuid + gEfiMtftp4ProtocolGuid [FeaturePcd] gArmTokenSpaceGuid.PcdArmLinuxSpinTable diff --git a/ArmPlatformPkg/Bds/Bds.inf b/ArmPlatformPkg/Bds/Bds.inf index 5a2f86bf8c..c3de53c2bd 100644 --- a/ArmPlatformPkg/Bds/Bds.inf +++ b/ArmPlatformPkg/Bds/Bds.inf @@ -63,6 +63,8 @@ gEfiDevicePathToTextProtocolGuid gEfiFirmwareVolumeBlockProtocolGuid gEfiFirmwareVolumeBlock2ProtocolGuid + gEfiDhcp4ServiceBindingProtocolGuid + gEfiMtftp4ServiceBindingProtocolGuid [Pcd] gArmPlatformTokenSpaceGuid.PcdFirmwareVendor diff --git a/ArmPlatformPkg/Bds/BootOptionSupport.c b/ArmPlatformPkg/Bds/BootOptionSupport.c index ee4281855e..974f220553 100644 --- a/ArmPlatformPkg/Bds/BootOptionSupport.c +++ b/ArmPlatformPkg/Bds/BootOptionSupport.c @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include @@ -866,49 +868,96 @@ BdsLoadOptionPxeIsSupported ( } } +/** + Add to the list of boot devices the devices allowing a TFTP boot + + @param[in] BdsLoadOptionList List of devices to boot from + + @retval EFI_SUCCESS Update completed + @retval EFI_OUT_OF_RESOURCES Fail to perform the update due to lack of resource +**/ EFI_STATUS BdsLoadOptionTftpList ( IN OUT LIST_ENTRY* BdsLoadOptionList ) { - EFI_STATUS Status; - UINTN HandleCount; - EFI_HANDLE *HandleBuffer; - UINTN Index; - BDS_SUPPORTED_DEVICE *SupportedDevice; - EFI_DEVICE_PATH_PROTOCOL* DevicePathProtocol; - EFI_SIMPLE_NETWORK_PROTOCOL* SimpleNet; - CHAR16 DeviceDescription[BOOT_DEVICE_DESCRIPTION_MAX]; - EFI_MAC_ADDRESS *Mac; + EFI_STATUS Status; + UINTN HandleCount; + EFI_HANDLE *HandleBuffer; + EFI_HANDLE Handle; + UINTN Index; + EFI_DEVICE_PATH_PROTOCOL *DevicePathProtocol; + VOID *Interface; + EFI_SIMPLE_NETWORK_PROTOCOL *SimpleNetworkProtocol; + BDS_SUPPORTED_DEVICE *SupportedDevice; + EFI_MAC_ADDRESS *Mac; - // List all the PXE Protocols - Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiPxeBaseCodeProtocolGuid, NULL, &HandleCount, &HandleBuffer); + // + // List all the handles on which the Simple Network Protocol is installed. + // + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiSimpleNetworkProtocolGuid, + NULL, + &HandleCount, + &HandleBuffer + ); if (EFI_ERROR (Status)) { return Status; } for (Index = 0; Index < HandleCount; Index++) { - // 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) - Status = gBS->HandleProtocol (HandleBuffer[Index], &gEfiDevicePathProtocolGuid, (VOID **)&DevicePathProtocol); - if (!EFI_ERROR(Status)) { - // Allocate BDS Supported Device structure - SupportedDevice = (BDS_SUPPORTED_DEVICE*)AllocatePool(sizeof(BDS_SUPPORTED_DEVICE)); + Handle = HandleBuffer[Index]; + // + // We select the handles that support : + // . the Device Path Protocol + // . the MTFTP4 Protocol + // + Status = gBS->HandleProtocol ( + Handle, + &gEfiDevicePathProtocolGuid, + (VOID **)&DevicePathProtocol + ); + if (EFI_ERROR (Status)) { + continue; + } - Status = gBS->LocateProtocol (&gEfiSimpleNetworkProtocolGuid, NULL, (VOID **)&SimpleNet); - if (!EFI_ERROR(Status)) { - Mac = &SimpleNet->Mode->CurrentAddress; - 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]); - } else { - Status = GenerateDeviceDescriptionName (HandleBuffer[Index], DeviceDescription); - ASSERT_EFI_ERROR (Status); - } - UnicodeSPrint (SupportedDevice->Description,BOOT_DEVICE_DESCRIPTION_MAX,L"TFTP on %s",DeviceDescription); + Status = gBS->HandleProtocol ( + Handle, + &gEfiMtftp4ServiceBindingProtocolGuid, + &Interface + ); + if (EFI_ERROR (Status)) { + continue; + } - SupportedDevice->DevicePathProtocol = DevicePathProtocol; - SupportedDevice->Support = &BdsLoadOptionSupportList[BDS_DEVICE_TFTP]; + Status = gBS->HandleProtocol ( + Handle, + &gEfiSimpleNetworkProtocolGuid, + (VOID **)&SimpleNetworkProtocol + ); + if (EFI_ERROR (Status)) { + continue; + } - InsertTailList (BdsLoadOptionList,&SupportedDevice->Link); + // Allocate BDS Supported Device structure + SupportedDevice = (BDS_SUPPORTED_DEVICE*)AllocatePool (sizeof (BDS_SUPPORTED_DEVICE)); + if (SupportedDevice == NULL) { + continue; } + + Mac = &SimpleNetworkProtocol->Mode->CurrentAddress; + UnicodeSPrint ( + SupportedDevice->Description, + BOOT_DEVICE_DESCRIPTION_MAX, + L"TFTP on 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] + ); + + SupportedDevice->DevicePathProtocol = DevicePathProtocol; + SupportedDevice->Support = &BdsLoadOptionSupportList[BDS_DEVICE_TFTP]; + + InsertTailList (BdsLoadOptionList, &SupportedDevice->Link); } return EFI_SUCCESS; @@ -920,38 +969,50 @@ BdsLoadOptionTftpCreateDevicePath ( OUT EFI_DEVICE_PATH_PROTOCOL **DevicePathNodes ) { - EFI_STATUS Status; - BOOLEAN IsDHCP; - EFI_IP_ADDRESS LocalIp; - EFI_IP_ADDRESS RemoteIp; - IPv4_DEVICE_PATH* IPv4DevicePathNode; - FILEPATH_DEVICE_PATH* FilePathDevicePath; - CHAR16 BootFilePath[BOOT_DEVICE_FILEPATH_MAX]; - UINTN BootFilePathSize; + EFI_STATUS Status; + BOOLEAN IsDHCP; + EFI_IP_ADDRESS LocalIp; + EFI_IP_ADDRESS SubnetMask; + EFI_IP_ADDRESS GatewayIp; + EFI_IP_ADDRESS RemoteIp; + IPv4_DEVICE_PATH *IPv4DevicePathNode; + FILEPATH_DEVICE_PATH *FilePathDevicePath; + CHAR16 BootFilePath[BOOT_DEVICE_FILEPATH_MAX]; + UINTN BootFilePathSize; - Print(L"Get the IP address from DHCP: "); + Print (L"Get the IP address from DHCP: "); Status = GetHIInputBoolean (&IsDHCP); - if (EFI_ERROR(Status)) { + if (EFI_ERROR (Status)) { return EFI_ABORTED; } if (!IsDHCP) { - Print(L"Get the static IP address: "); + Print (L"Local static IP address: "); Status = GetHIInputIP (&LocalIp); - if (EFI_ERROR(Status)) { + if (EFI_ERROR (Status)) { + return EFI_ABORTED; + } + Print (L"Get the network mask: "); + Status = GetHIInputIP (&SubnetMask); + if (EFI_ERROR (Status)) { + return EFI_ABORTED; + } + Print (L"Get the gateway IP address: "); + Status = GetHIInputIP (&GatewayIp); + if (EFI_ERROR (Status)) { return EFI_ABORTED; } } - Print(L"Get the TFTP server IP address: "); + Print (L"Get the TFTP server IP address: "); Status = GetHIInputIP (&RemoteIp); - if (EFI_ERROR(Status)) { + if (EFI_ERROR (Status)) { return EFI_ABORTED; } - Print(L"File path of the %s : ", FileName); + Print (L"File path of the %s : ", FileName); Status = GetHIInputStr (BootFilePath, BOOT_DEVICE_FILEPATH_MAX); - if (EFI_ERROR(Status)) { + if (EFI_ERROR (Status)) { return EFI_ABORTED; } @@ -967,7 +1028,13 @@ BdsLoadOptionTftpCreateDevicePath ( IPv4DevicePathNode->Header.Type = MESSAGING_DEVICE_PATH; IPv4DevicePathNode->Header.SubType = MSG_IPv4_DP; SetDevicePathNodeLength (&IPv4DevicePathNode->Header, sizeof(IPv4_DEVICE_PATH)); - CopyMem (&IPv4DevicePathNode->LocalIpAddress, &LocalIp.v4, sizeof (EFI_IPv4_ADDRESS)); + + if (!IsDHCP) { + CopyMem (&IPv4DevicePathNode->LocalIpAddress, &LocalIp.v4, sizeof (EFI_IPv4_ADDRESS)); + CopyMem (&IPv4DevicePathNode->SubnetMask, &SubnetMask.v4, sizeof (EFI_IPv4_ADDRESS)); + CopyMem (&IPv4DevicePathNode->GatewayIpAddress, &GatewayIp.v4, sizeof (EFI_IPv4_ADDRESS)); + } + CopyMem (&IPv4DevicePathNode->RemoteIpAddress, &RemoteIp.v4, sizeof (EFI_IPv4_ADDRESS)); IPv4DevicePathNode->LocalPort = 0; IPv4DevicePathNode->RemotePort = 0; @@ -1021,7 +1088,11 @@ BdsLoadOptionTftpUpdateDevicePath ( IPv4_DEVICE_PATH Ipv4Node; BOOLEAN IsDHCP; EFI_IP_ADDRESS OldIp; + EFI_IP_ADDRESS OldSubnetMask; + EFI_IP_ADDRESS OldGatewayIp; EFI_IP_ADDRESS LocalIp; + EFI_IP_ADDRESS SubnetMask; + EFI_IP_ADDRESS GatewayIp; EFI_IP_ADDRESS RemoteIp; UINT8 *FileNodePtr; CHAR16 BootFilePath[BOOT_DEVICE_FILEPATH_MAX]; @@ -1074,9 +1145,7 @@ BdsLoadOptionTftpUpdateDevicePath ( if (!IsDHCP) { Print (L"Local static IP address: "); if (Ipv4Node.StaticIpAddress) { - // Copy local IPv4 address into IPv4 or IPv6 union CopyMem (&OldIp.v4, &Ipv4Node.LocalIpAddress, sizeof (EFI_IPv4_ADDRESS)); - Status = EditHIInputIP (&OldIp, &LocalIp); } else { Status = GetHIInputIP (&LocalIp); @@ -1084,6 +1153,28 @@ BdsLoadOptionTftpUpdateDevicePath ( if (EFI_ERROR (Status)) { goto ErrorExit; } + + Print (L"Get the network mask: "); + if (Ipv4Node.StaticIpAddress) { + CopyMem (&OldSubnetMask.v4, &Ipv4Node.SubnetMask, sizeof (EFI_IPv4_ADDRESS)); + Status = EditHIInputIP (&OldSubnetMask, &SubnetMask); + } else { + Status = GetHIInputIP (&SubnetMask); + } + if (EFI_ERROR (Status)) { + goto ErrorExit; + } + + Print (L"Get the gateway IP address: "); + if (Ipv4Node.StaticIpAddress) { + CopyMem (&OldGatewayIp.v4, &Ipv4Node.GatewayIpAddress, sizeof (EFI_IPv4_ADDRESS)); + Status = EditHIInputIP (&OldGatewayIp, &GatewayIp); + } else { + Status = GetHIInputIP (&GatewayIp); + } + if (EFI_ERROR (Status)) { + goto ErrorExit; + } } Print (L"TFTP server IP address: "); @@ -1126,12 +1217,18 @@ BdsLoadOptionTftpUpdateDevicePath ( // // Update the IPv4 node. IPv6 case not handled yet. // - if (IsDHCP == TRUE) { + if (IsDHCP) { Ipv4Node.StaticIpAddress = FALSE; + ZeroMem (&Ipv4Node.LocalIpAddress, sizeof (EFI_IPv4_ADDRESS)); + ZeroMem (&Ipv4Node.SubnetMask, sizeof (EFI_IPv4_ADDRESS)); + ZeroMem (&Ipv4Node.GatewayIpAddress, sizeof (EFI_IPv4_ADDRESS)); } else { Ipv4Node.StaticIpAddress = TRUE; + CopyMem (&Ipv4Node.LocalIpAddress, &LocalIp.v4, sizeof (EFI_IPv4_ADDRESS)); + CopyMem (&Ipv4Node.SubnetMask, &SubnetMask.v4, sizeof (EFI_IPv4_ADDRESS)); + CopyMem (&Ipv4Node.GatewayIpAddress, &GatewayIp.v4, sizeof (EFI_IPv4_ADDRESS)); } - CopyMem (&Ipv4Node.LocalIpAddress, &LocalIp.v4, sizeof (EFI_IPv4_ADDRESS)); + CopyMem (&Ipv4Node.RemoteIpAddress, &RemoteIp.v4, sizeof (EFI_IPv4_ADDRESS)); CopyMem (Ipv4NodePtr, &Ipv4Node, sizeof (IPv4_DEVICE_PATH)); -- 2.39.2