summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRonald Cron <Ronald.Cron@arm.com>2014-12-12 19:14:22 +0000
committerLeif Lindholm <leif.lindholm@linaro.org>2014-12-15 16:23:05 +0000
commit5559e2931dd83c64332291944a4c0f65c4725925 (patch)
tree0012d057733c230319b9b1ff73334ddacdefcb06
parenta53b88f1603cc45e6aeb9c171649a6eb8148c8df (diff)
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 <Ronald.Cron@arm.com> Reviewed-by: Olivier Martin <olivier.martin@arm.com> git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@16516 6f19259b-4bc3-4df7-8a09-765794883524
-rw-r--r--ArmPkg/ArmPkg.dsc4
-rw-r--r--ArmPkg/Library/BdsLib/BdsFilePath.c605
-rw-r--r--ArmPkg/Library/BdsLib/BdsInternal.h5
-rw-r--r--ArmPkg/Library/BdsLib/BdsLib.inf6
-rw-r--r--ArmPlatformPkg/Bds/Bds.inf2
-rw-r--r--ArmPlatformPkg/Bds/BootOptionSupport.c195
6 files changed, 637 insertions, 180 deletions
diff --git a/ArmPkg/ArmPkg.dsc b/ArmPkg/ArmPkg.dsc
index 3af9b3b0b..dcf95ba72 100644
--- a/ArmPkg/ArmPkg.dsc
+++ b/ArmPkg/ArmPkg.dsc
@@ -58,6 +58,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 f26ba39dd..0057d9412 100644
--- a/ArmPkg/Library/BdsLib/BdsFilePath.c
+++ b/ArmPkg/Library/BdsLib/BdsFilePath.c
@@ -14,13 +14,41 @@
#include "BdsInternal.h"
+#include <Library/NetLib.h>
+
+#include <Protocol/Bds.h>
#include <Protocol/UsbIo.h>
#include <Protocol/DiskIo.h>
#include <Protocol/LoadedImage.h>
#include <Protocol/SimpleNetwork.h>
+#include <Protocol/Dhcp4.h>
+#include <Protocol/Mtftp4.h>
#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 9b5d3a4ff..602fd8dcf 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 02409e139..6d6a2dfa8 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 5a2f86bf8..c3de53c2b 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 ee4281855..974f22055 100644
--- a/ArmPlatformPkg/Bds/BootOptionSupport.c
+++ b/ArmPlatformPkg/Bds/BootOptionSupport.c
@@ -22,6 +22,8 @@
#include <Protocol/PxeBaseCode.h>
#include <Protocol/SimpleFileSystem.h>
#include <Protocol/SimpleNetwork.h>
+#include <Protocol/Dhcp4.h>
+#include <Protocol/Mtftp4.h>
#include <Guid/FileSystemInfo.h>
@@ -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));