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
This commit is contained in:
Ronald Cron 2014-12-12 19:14:22 +00:00 committed by oliviermartin
parent b4c222655c
commit 061568e2d5
6 changed files with 643 additions and 186 deletions

View File

@ -55,6 +55,10 @@
PeCoffLib|MdePkg/Library/BasePeCoffLib/BasePeCoffLib.inf PeCoffLib|MdePkg/Library/BasePeCoffLib/BasePeCoffLib.inf
PeCoffGetEntryPointLib|MdePkg/Library/BasePeCoffGetEntryPointLib/BasePeCoffGetEntryPointLib.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 SemihostLib|ArmPkg/Library/SemihostLib/SemihostLib.inf
UncachedMemoryAllocationLib|ArmPkg/Library/UncachedMemoryAllocationLib/UncachedMemoryAllocationLib.inf UncachedMemoryAllocationLib|ArmPkg/Library/UncachedMemoryAllocationLib/UncachedMemoryAllocationLib.inf
DxeServicesTableLib|MdePkg/Library/DxeServicesTableLib/DxeServicesTableLib.inf DxeServicesTableLib|MdePkg/Library/DxeServicesTableLib/DxeServicesTableLib.inf

View File

@ -14,13 +14,41 @@
#include "BdsInternal.h" #include "BdsInternal.h"
#include <Library/NetLib.h>
#include <Protocol/Bds.h>
#include <Protocol/UsbIo.h> #include <Protocol/UsbIo.h>
#include <Protocol/DiskIo.h> #include <Protocol/DiskIo.h>
#include <Protocol/LoadedImage.h> #include <Protocol/LoadedImage.h>
#include <Protocol/SimpleNetwork.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))) #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 // Extract the FilePath from the Device Path
CHAR16* CHAR16*
BdsExtractFilePathFromDevicePath ( BdsExtractFilePathFromDevicePath (
@ -730,7 +758,7 @@ BdsTftpSupport (
{ {
EFI_STATUS Status; EFI_STATUS Status;
EFI_DEVICE_PATH *NextDevicePath; EFI_DEVICE_PATH *NextDevicePath;
EFI_PXE_BASE_CODE_PROTOCOL *PxeBcProtocol; VOID *Interface;
// Validate the Remaining Device Path // Validate the Remaining Device Path
if (IsDevicePathEnd (RemainingDevicePath)) { if (IsDevicePathEnd (RemainingDevicePath)) {
@ -748,18 +776,222 @@ BdsTftpSupport (
return FALSE; return FALSE;
} }
Status = gBS->HandleProtocol (Handle, &gEfiPxeBaseCodeProtocolGuid, (VOID **)&PxeBcProtocol); Status = gBS->HandleProtocol (
Handle, &gEfiDevicePathProtocolGuid,
&Interface
);
if (EFI_ERROR (Status)) { if (EFI_ERROR (Status)) {
return FALSE; return FALSE;
} else { }
//
// 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; 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 EFI_STATUS
BdsTftpLoadImage ( BdsTftpLoadImage (
IN EFI_DEVICE_PATH* DevicePath, IN EFI_DEVICE_PATH* DevicePath,
IN EFI_HANDLE Handle, IN EFI_HANDLE ControllerHandle,
IN EFI_DEVICE_PATH* RemainingDevicePath, IN EFI_DEVICE_PATH* RemainingDevicePath,
IN EFI_ALLOCATE_TYPE Type, IN EFI_ALLOCATE_TYPE Type,
IN OUT EFI_PHYSICAL_ADDRESS *Image, IN OUT EFI_PHYSICAL_ADDRESS *Image,
@ -767,99 +999,189 @@ BdsTftpLoadImage (
) )
{ {
EFI_STATUS Status; EFI_STATUS Status;
EFI_PXE_BASE_CODE_PROTOCOL *Pxe; EFI_HANDLE Dhcp4ChildHandle;
UINT64 TftpBufferSize; EFI_DHCP4_PROTOCOL *Dhcp4;
UINT64 TftpTransferSize; BOOLEAN Dhcp4ToStop;
EFI_IP_ADDRESS ServerIp; 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; IPv4_DEVICE_PATH *IPv4DevicePathNode;
FILEPATH_DEVICE_PATH* FilePathDevicePath; FILEPATH_DEVICE_PATH *FilePathDevicePathNode;
EFI_IP_ADDRESS LocalIp; CHAR8 *AsciiFilePath;
CHAR8* AsciiPathName; EFI_MTFTP4_TOKEN Mtftp4Token;
EFI_SIMPLE_NETWORK_PROTOCOL *Snp; UINT64 FileSize;
UINT64 TftpBufferSize;
BDS_TFTP_CONTEXT *TftpContext;
ASSERT(IS_DEVICE_PATH_NODE (RemainingDevicePath, MESSAGING_DEVICE_PATH, MSG_IPv4_DP)); ASSERT(IS_DEVICE_PATH_NODE (RemainingDevicePath, MESSAGING_DEVICE_PATH, MSG_IPv4_DP));
IPv4DevicePathNode = (IPv4_DEVICE_PATH*)RemainingDevicePath; IPv4DevicePathNode = (IPv4_DEVICE_PATH*)RemainingDevicePath;
FilePathDevicePath = (FILEPATH_DEVICE_PATH*)(IPv4DevicePathNode + 1);
Status = gBS->LocateProtocol (&gEfiPxeBaseCodeProtocolGuid, NULL, (VOID **)&Pxe); Dhcp4ChildHandle = NULL;
if (EFI_ERROR (Status)) { Dhcp4 = NULL;
return Status; Dhcp4ToStop = FALSE;
} Mtftp4ChildHandle = NULL;
Mtftp4 = NULL;
AsciiFilePath = NULL;
TftpContext = NULL;
Status = Pxe->Start (Pxe, FALSE);
if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {
return Status;
}
do {
if (!IPv4DevicePathNode->StaticIpAddress) { if (!IPv4DevicePathNode->StaticIpAddress) {
Status = Pxe->Dhcp (Pxe, TRUE); //
} else { // Using the DHCP4 Service Binding Protocol, create a child handle of the DHCP4 service and
CopyMem (&LocalIp.v4, &IPv4DevicePathNode->LocalIpAddress, sizeof (EFI_IPv4_ADDRESS)); // install the DHCP4 protocol on it. Then, open the DHCP protocol.
Status = Pxe->SetStationIp (Pxe, &LocalIp, NULL); //
} Status = NetLibCreateServiceChild (
ControllerHandle,
// If an IP Address has already been set and a different static IP address is requested then restart gImageHandle,
// the Network service. &gEfiDhcp4ServiceBindingProtocolGuid,
if (Status == EFI_ALREADY_STARTED) { &Dhcp4ChildHandle
Status = gBS->LocateProtocol (&gEfiSimpleNetworkProtocolGuid, NULL, (VOID **)&Snp); );
if (!EFI_ERROR (Status) && IPv4DevicePathNode->StaticIpAddress && if (!EFI_ERROR (Status)) {
(CompareMem (&Snp->Mode->CurrentAddress, &IPv4DevicePathNode->LocalIpAddress, sizeof(EFI_MAC_ADDRESS)) != 0)) Status = gBS->OpenProtocol (
{ Dhcp4ChildHandle,
Pxe->Stop (Pxe); &gEfiDhcp4ProtocolGuid,
Status = Pxe->Start (Pxe, FALSE); (VOID **) &Dhcp4,
if (EFI_ERROR(Status)) { gImageHandle,
break; ControllerHandle,
} EFI_OPEN_PROTOCOL_BY_DRIVER
// After restarting the PXE protocol, we want to try again with our new IP Address
Status = EFI_ALREADY_STARTED;
}
}
} while (Status == EFI_ALREADY_STARTED);
if (EFI_ERROR(Status)) {
return Status;
}
CopyMem (&ServerIp.v4, &IPv4DevicePathNode->RemoteIpAddress, sizeof (EFI_IPv4_ADDRESS));
// Convert the Unicode PathName to Ascii
AsciiPathName = AllocatePool ((StrLen (FilePathDevicePath->PathName) + 1) * sizeof (CHAR8));
if (AsciiPathName == NULL) {
return EFI_OUT_OF_RESOURCES;
}
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; if (EFI_ERROR (Status)) {
Print (L"Unable to open DHCP4 protocol\n");
goto Error;
}
} }
// //
// Two cases: // Using the MTFTP4 Service Binding Protocol, create a child handle of the MTFTP4 service and
// 1) the file size is unknown (tsize extension not supported) // install the MTFTP4 protocol on it. Then, open the MTFTP4 protocol.
// 2) tsize returned the file size //
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;
}
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;
}
}
//
// 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;
}
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;
}
}
//
// Configure the TFTP4 protocol
//
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));
}
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;
}
//
// Convert the Unicode path of the file to Ascii
//
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))) {
// //
if (Status == EFI_PROTOCOL_ERROR) {
for (TftpBufferSize = SIZE_8MB; TftpBufferSize <= FixedPcdGet32 (PcdMaxTftpFileSize); TftpBufferSize += SIZE_8MB) {
// Allocate a buffer to hold the whole file. // Allocate a buffer to hold the whole file.
//
Status = gBS->AllocatePages ( Status = gBS->AllocatePages (
Type, Type,
EfiBootServicesCode, EfiBootServicesCode,
@ -867,64 +1189,85 @@ BdsTftpLoadImage (
Image Image
); );
if (EFI_ERROR (Status)) { if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "Failed to allocate space for image: %r\n", Status)); Print (L"Failed to allocate space for image\n");
goto EXIT; goto Error;
} }
TftpTransferSize = TftpBufferSize; TftpContext->DownloadedNbOfBytes = 0;
Status = Pxe->Mtftp ( TftpContext->LastReportedNbOfBytes = 0;
Pxe,
EFI_PXE_BASE_CODE_TFTP_READ_FILE, ZeroMem (&Mtftp4Token, sizeof (EFI_MTFTP4_TOKEN));
(VOID *)(UINTN)*Image, Mtftp4Token.Filename = (UINT8*)AsciiFilePath;
FALSE, Mtftp4Token.BufferSize = TftpBufferSize;
&TftpTransferSize, Mtftp4Token.Buffer = (VOID *)(UINTN)*Image;
NULL, Mtftp4Token.CheckPacket = Mtftp4CheckPacket;
&ServerIp, Mtftp4Token.Context = (VOID*)TftpContext;
(UINT8*)AsciiPathName,
NULL, Print (L"Downloading the file <%s> from the TFTP server\n", FilePathDevicePathNode->PathName);
FALSE Status = Mtftp4->ReadFile (Mtftp4, &Mtftp4Token);
); Print (L"\n");
if (EFI_ERROR (Status)) { if (EFI_ERROR (Status)) {
if (Status == EFI_BUFFER_TOO_SMALL) {
Print (L"Downloading failed, file larger than expected.\n");
gBS->FreePages (*Image, EFI_SIZE_TO_PAGES (TftpBufferSize)); gBS->FreePages (*Image, EFI_SIZE_TO_PAGES (TftpBufferSize));
continue;
} else { } else {
*ImageSize = (UINTN)TftpBufferSize; goto Error;
}
}
*ImageSize = Mtftp4Token.BufferSize;
break; break;
} }
Error:
if (Dhcp4ChildHandle != NULL) {
if (Dhcp4 != NULL) {
if (Dhcp4ToStop) {
Dhcp4->Stop (Dhcp4);
} }
} else { gBS->CloseProtocol (
// Allocate a buffer to hold the whole file. Dhcp4ChildHandle,
Status = gBS->AllocatePages ( &gEfiDhcp4ProtocolGuid,
Type, gImageHandle,
EfiBootServicesCode, ControllerHandle
EFI_SIZE_TO_PAGES (TftpBufferSize), );
Image }
NetLibDestroyServiceChild (
ControllerHandle,
gImageHandle,
&gEfiDhcp4ServiceBindingProtocolGuid,
Dhcp4ChildHandle
); );
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "Failed to allocate space for kernel image: %r\n", Status));
goto EXIT;
} }
Status = Pxe->Mtftp ( if (Mtftp4ChildHandle != NULL) {
Pxe, if (Mtftp4 != NULL) {
EFI_PXE_BASE_CODE_TFTP_READ_FILE, if (AsciiFilePath != NULL) {
(VOID *)(UINTN)*Image, FreePool (AsciiFilePath);
FALSE,
&TftpBufferSize,
NULL,
&ServerIp,
(UINT8*)AsciiPathName,
NULL,
FALSE
);
if (EFI_ERROR (Status)) {
gBS->FreePages (*Image, EFI_SIZE_TO_PAGES (TftpBufferSize));
} else {
*ImageSize = (UINTN)TftpBufferSize;
} }
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; return Status;
} }

View File

@ -71,6 +71,11 @@ typedef struct _BDS_SYSTEM_MEMORY_RESOURCE {
UINT64 ResourceLength; UINT64 ResourceLength;
} BDS_SYSTEM_MEMORY_RESOURCE; } BDS_SYSTEM_MEMORY_RESOURCE;
typedef struct {
UINT64 FileSize;
UINT64 DownloadedNbOfBytes;
UINT64 LastReportedNbOfBytes;
} BDS_TFTP_CONTEXT;
// BdsHelper.c // BdsHelper.c
EFI_STATUS EFI_STATUS

View File

@ -37,6 +37,7 @@
[Packages] [Packages]
MdePkg/MdePkg.dec MdePkg/MdePkg.dec
MdeModulePkg/MdeModulePkg.dec
EmbeddedPkg/EmbeddedPkg.dec EmbeddedPkg/EmbeddedPkg.dec
ArmPkg/ArmPkg.dec ArmPkg/ArmPkg.dec
ArmPlatformPkg/ArmPlatformPkg.dec ArmPlatformPkg/ArmPlatformPkg.dec
@ -53,6 +54,7 @@
SerialPortLib SerialPortLib
FdtLib FdtLib
TimerLib TimerLib
NetLib
[LibraryClasses.AARCH64] [LibraryClasses.AARCH64]
ArmGicLib ArmGicLib
@ -74,6 +76,10 @@
gEfiUsbIoProtocolGuid gEfiUsbIoProtocolGuid
gEfiLoadedImageProtocolGuid gEfiLoadedImageProtocolGuid
gEfiSimpleNetworkProtocolGuid gEfiSimpleNetworkProtocolGuid
gEfiDhcp4ServiceBindingProtocolGuid
gEfiDhcp4ProtocolGuid
gEfiMtftp4ServiceBindingProtocolGuid
gEfiMtftp4ProtocolGuid
[FeaturePcd] [FeaturePcd]
gArmTokenSpaceGuid.PcdArmLinuxSpinTable gArmTokenSpaceGuid.PcdArmLinuxSpinTable

View File

@ -63,6 +63,8 @@
gEfiDevicePathToTextProtocolGuid gEfiDevicePathToTextProtocolGuid
gEfiFirmwareVolumeBlockProtocolGuid gEfiFirmwareVolumeBlockProtocolGuid
gEfiFirmwareVolumeBlock2ProtocolGuid gEfiFirmwareVolumeBlock2ProtocolGuid
gEfiDhcp4ServiceBindingProtocolGuid
gEfiMtftp4ServiceBindingProtocolGuid
[Pcd] [Pcd]
gArmPlatformTokenSpaceGuid.PcdFirmwareVendor gArmPlatformTokenSpaceGuid.PcdFirmwareVendor

View File

@ -22,6 +22,8 @@
#include <Protocol/PxeBaseCode.h> #include <Protocol/PxeBaseCode.h>
#include <Protocol/SimpleFileSystem.h> #include <Protocol/SimpleFileSystem.h>
#include <Protocol/SimpleNetwork.h> #include <Protocol/SimpleNetwork.h>
#include <Protocol/Dhcp4.h>
#include <Protocol/Mtftp4.h>
#include <Guid/FileSystemInfo.h> #include <Guid/FileSystemInfo.h>
@ -866,6 +868,14 @@ 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 EFI_STATUS
BdsLoadOptionTftpList ( BdsLoadOptionTftpList (
IN OUT LIST_ENTRY* BdsLoadOptionList IN OUT LIST_ENTRY* BdsLoadOptionList
@ -874,42 +884,81 @@ BdsLoadOptionTftpList (
EFI_STATUS Status; EFI_STATUS Status;
UINTN HandleCount; UINTN HandleCount;
EFI_HANDLE *HandleBuffer; EFI_HANDLE *HandleBuffer;
EFI_HANDLE Handle;
UINTN Index; UINTN Index;
BDS_SUPPORTED_DEVICE *SupportedDevice;
EFI_DEVICE_PATH_PROTOCOL *DevicePathProtocol; EFI_DEVICE_PATH_PROTOCOL *DevicePathProtocol;
EFI_SIMPLE_NETWORK_PROTOCOL* SimpleNet; VOID *Interface;
CHAR16 DeviceDescription[BOOT_DEVICE_DESCRIPTION_MAX]; EFI_SIMPLE_NETWORK_PROTOCOL *SimpleNetworkProtocol;
BDS_SUPPORTED_DEVICE *SupportedDevice;
EFI_MAC_ADDRESS *Mac; 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)) { if (EFI_ERROR (Status)) {
return Status; return Status;
} }
for (Index = 0; Index < HandleCount; Index++) { 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) Handle = HandleBuffer[Index];
Status = gBS->HandleProtocol (HandleBuffer[Index], &gEfiDevicePathProtocolGuid, (VOID **)&DevicePathProtocol); //
if (!EFI_ERROR(Status)) { // 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->HandleProtocol (
Handle,
&gEfiMtftp4ServiceBindingProtocolGuid,
&Interface
);
if (EFI_ERROR (Status)) {
continue;
}
Status = gBS->HandleProtocol (
Handle,
&gEfiSimpleNetworkProtocolGuid,
(VOID **)&SimpleNetworkProtocol
);
if (EFI_ERROR (Status)) {
continue;
}
// Allocate BDS Supported Device structure // Allocate BDS Supported Device structure
SupportedDevice = (BDS_SUPPORTED_DEVICE*)AllocatePool (sizeof (BDS_SUPPORTED_DEVICE)); SupportedDevice = (BDS_SUPPORTED_DEVICE*)AllocatePool (sizeof (BDS_SUPPORTED_DEVICE));
if (SupportedDevice == NULL) {
Status = gBS->LocateProtocol (&gEfiSimpleNetworkProtocolGuid, NULL, (VOID **)&SimpleNet); continue;
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);
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->DevicePathProtocol = DevicePathProtocol;
SupportedDevice->Support = &BdsLoadOptionSupportList[BDS_DEVICE_TFTP]; SupportedDevice->Support = &BdsLoadOptionSupportList[BDS_DEVICE_TFTP];
InsertTailList (BdsLoadOptionList, &SupportedDevice->Link); InsertTailList (BdsLoadOptionList, &SupportedDevice->Link);
} }
}
return EFI_SUCCESS; return EFI_SUCCESS;
} }
@ -923,6 +972,8 @@ BdsLoadOptionTftpCreateDevicePath (
EFI_STATUS Status; EFI_STATUS Status;
BOOLEAN IsDHCP; BOOLEAN IsDHCP;
EFI_IP_ADDRESS LocalIp; EFI_IP_ADDRESS LocalIp;
EFI_IP_ADDRESS SubnetMask;
EFI_IP_ADDRESS GatewayIp;
EFI_IP_ADDRESS RemoteIp; EFI_IP_ADDRESS RemoteIp;
IPv4_DEVICE_PATH *IPv4DevicePathNode; IPv4_DEVICE_PATH *IPv4DevicePathNode;
FILEPATH_DEVICE_PATH *FilePathDevicePath; FILEPATH_DEVICE_PATH *FilePathDevicePath;
@ -936,11 +987,21 @@ BdsLoadOptionTftpCreateDevicePath (
} }
if (!IsDHCP) { if (!IsDHCP) {
Print(L"Get the static IP address: "); Print (L"Local static IP address: ");
Status = GetHIInputIP (&LocalIp); Status = GetHIInputIP (&LocalIp);
if (EFI_ERROR (Status)) { if (EFI_ERROR (Status)) {
return EFI_ABORTED; 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: ");
@ -967,7 +1028,13 @@ BdsLoadOptionTftpCreateDevicePath (
IPv4DevicePathNode->Header.Type = MESSAGING_DEVICE_PATH; IPv4DevicePathNode->Header.Type = MESSAGING_DEVICE_PATH;
IPv4DevicePathNode->Header.SubType = MSG_IPv4_DP; IPv4DevicePathNode->Header.SubType = MSG_IPv4_DP;
SetDevicePathNodeLength (&IPv4DevicePathNode->Header, sizeof(IPv4_DEVICE_PATH)); SetDevicePathNodeLength (&IPv4DevicePathNode->Header, sizeof(IPv4_DEVICE_PATH));
if (!IsDHCP) {
CopyMem (&IPv4DevicePathNode->LocalIpAddress, &LocalIp.v4, sizeof (EFI_IPv4_ADDRESS)); 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)); CopyMem (&IPv4DevicePathNode->RemoteIpAddress, &RemoteIp.v4, sizeof (EFI_IPv4_ADDRESS));
IPv4DevicePathNode->LocalPort = 0; IPv4DevicePathNode->LocalPort = 0;
IPv4DevicePathNode->RemotePort = 0; IPv4DevicePathNode->RemotePort = 0;
@ -1021,7 +1088,11 @@ BdsLoadOptionTftpUpdateDevicePath (
IPv4_DEVICE_PATH Ipv4Node; IPv4_DEVICE_PATH Ipv4Node;
BOOLEAN IsDHCP; BOOLEAN IsDHCP;
EFI_IP_ADDRESS OldIp; EFI_IP_ADDRESS OldIp;
EFI_IP_ADDRESS OldSubnetMask;
EFI_IP_ADDRESS OldGatewayIp;
EFI_IP_ADDRESS LocalIp; EFI_IP_ADDRESS LocalIp;
EFI_IP_ADDRESS SubnetMask;
EFI_IP_ADDRESS GatewayIp;
EFI_IP_ADDRESS RemoteIp; EFI_IP_ADDRESS RemoteIp;
UINT8 *FileNodePtr; UINT8 *FileNodePtr;
CHAR16 BootFilePath[BOOT_DEVICE_FILEPATH_MAX]; CHAR16 BootFilePath[BOOT_DEVICE_FILEPATH_MAX];
@ -1074,9 +1145,7 @@ BdsLoadOptionTftpUpdateDevicePath (
if (!IsDHCP) { if (!IsDHCP) {
Print (L"Local static IP address: "); Print (L"Local static IP address: ");
if (Ipv4Node.StaticIpAddress) { if (Ipv4Node.StaticIpAddress) {
// Copy local IPv4 address into IPv4 or IPv6 union
CopyMem (&OldIp.v4, &Ipv4Node.LocalIpAddress, sizeof (EFI_IPv4_ADDRESS)); CopyMem (&OldIp.v4, &Ipv4Node.LocalIpAddress, sizeof (EFI_IPv4_ADDRESS));
Status = EditHIInputIP (&OldIp, &LocalIp); Status = EditHIInputIP (&OldIp, &LocalIp);
} else { } else {
Status = GetHIInputIP (&LocalIp); Status = GetHIInputIP (&LocalIp);
@ -1084,6 +1153,28 @@ BdsLoadOptionTftpUpdateDevicePath (
if (EFI_ERROR (Status)) { if (EFI_ERROR (Status)) {
goto ErrorExit; 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: "); Print (L"TFTP server IP address: ");
@ -1126,12 +1217,18 @@ BdsLoadOptionTftpUpdateDevicePath (
// //
// Update the IPv4 node. IPv6 case not handled yet. // Update the IPv4 node. IPv6 case not handled yet.
// //
if (IsDHCP == TRUE) { if (IsDHCP) {
Ipv4Node.StaticIpAddress = FALSE; 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 { } else {
Ipv4Node.StaticIpAddress = TRUE; Ipv4Node.StaticIpAddress = TRUE;
}
CopyMem (&Ipv4Node.LocalIpAddress, &LocalIp.v4, sizeof (EFI_IPv4_ADDRESS)); 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.RemoteIpAddress, &RemoteIp.v4, sizeof (EFI_IPv4_ADDRESS)); CopyMem (&Ipv4Node.RemoteIpAddress, &RemoteIp.v4, sizeof (EFI_IPv4_ADDRESS));
CopyMem (Ipv4NodePtr, &Ipv4Node, sizeof (IPv4_DEVICE_PATH)); CopyMem (Ipv4NodePtr, &Ipv4Node, sizeof (IPv4_DEVICE_PATH));