mirror of https://github.com/acidanthera/audk.git
1415 lines
47 KiB
C
1415 lines
47 KiB
C
/** @file
|
|
*
|
|
* Copyright (c) 2011-2014, ARM Limited. All rights reserved.
|
|
*
|
|
* This program and the accompanying materials
|
|
* are licensed and made available under the terms and conditions of the BSD License
|
|
* which accompanies this distribution. The full text of the license may be found at
|
|
* http://opensource.org/licenses/bsd-license.php
|
|
*
|
|
* THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
|
*
|
|
**/
|
|
|
|
#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)))
|
|
|
|
/* Type and defines to set up the DHCP4 options */
|
|
|
|
typedef struct {
|
|
EFI_DHCP4_PACKET_OPTION Head;
|
|
UINT8 Route;
|
|
} DHCP4_OPTION;
|
|
|
|
#define DHCP_TAG_PARA_LIST 55
|
|
#define DHCP_TAG_NETMASK 1
|
|
#define DHCP_TAG_ROUTER 3
|
|
|
|
/*
|
|
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 (
|
|
IN CONST CHAR16 *StrDevicePath,
|
|
IN UINTN NumberDevicePathNode
|
|
)
|
|
{
|
|
UINTN Node;
|
|
CHAR16 *Str;
|
|
|
|
Str = (CHAR16*)StrDevicePath;
|
|
Node = 0;
|
|
while ((Str != NULL) && (*Str != L'\0') && (Node < NumberDevicePathNode)) {
|
|
if ((*Str == L'/') || (*Str == L'\\')) {
|
|
Node++;
|
|
}
|
|
Str++;
|
|
}
|
|
|
|
if (*Str == L'\0') {
|
|
return NULL;
|
|
} else {
|
|
return Str;
|
|
}
|
|
}
|
|
|
|
BOOLEAN
|
|
BdsIsRemovableUsb (
|
|
IN EFI_DEVICE_PATH* DevicePath
|
|
)
|
|
{
|
|
return ((DevicePathType (DevicePath) == MESSAGING_DEVICE_PATH) &&
|
|
((DevicePathSubType (DevicePath) == MSG_USB_CLASS_DP) ||
|
|
(DevicePathSubType (DevicePath) == MSG_USB_WWID_DP)));
|
|
}
|
|
|
|
EFI_STATUS
|
|
BdsGetDeviceUsb (
|
|
IN EFI_DEVICE_PATH* RemovableDevicePath,
|
|
OUT EFI_HANDLE* DeviceHandle,
|
|
OUT EFI_DEVICE_PATH** NewDevicePath
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN Index;
|
|
UINTN UsbIoHandleCount;
|
|
EFI_HANDLE *UsbIoBuffer;
|
|
EFI_DEVICE_PATH* UsbIoDevicePath;
|
|
EFI_DEVICE_PATH* TmpDevicePath;
|
|
USB_WWID_DEVICE_PATH* WwidDevicePath1;
|
|
USB_WWID_DEVICE_PATH* WwidDevicePath2;
|
|
USB_CLASS_DEVICE_PATH* UsbClassDevicePath1;
|
|
USB_CLASS_DEVICE_PATH* UsbClassDevicePath2;
|
|
|
|
// Get all the UsbIo handles
|
|
UsbIoHandleCount = 0;
|
|
Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiUsbIoProtocolGuid, NULL, &UsbIoHandleCount, &UsbIoBuffer);
|
|
if (EFI_ERROR (Status) || (UsbIoHandleCount == 0)) {
|
|
return Status;
|
|
}
|
|
|
|
// Check if one of the handles matches the USB description
|
|
for (Index = 0; Index < UsbIoHandleCount; Index++) {
|
|
Status = gBS->HandleProtocol (UsbIoBuffer[Index], &gEfiDevicePathProtocolGuid, (VOID **) &UsbIoDevicePath);
|
|
if (!EFI_ERROR (Status)) {
|
|
TmpDevicePath = UsbIoDevicePath;
|
|
while (!IsDevicePathEnd (TmpDevicePath)) {
|
|
// Check if the Device Path node is a USB Removable device Path node
|
|
if (BdsIsRemovableUsb (TmpDevicePath)) {
|
|
if (TmpDevicePath->SubType == MSG_USB_WWID_DP) {
|
|
WwidDevicePath1 = (USB_WWID_DEVICE_PATH*)RemovableDevicePath;
|
|
WwidDevicePath2 = (USB_WWID_DEVICE_PATH*)TmpDevicePath;
|
|
if ((WwidDevicePath1->VendorId == WwidDevicePath2->VendorId) &&
|
|
(WwidDevicePath1->ProductId == WwidDevicePath2->ProductId) &&
|
|
(CompareMem (WwidDevicePath1+1, WwidDevicePath2+1, DevicePathNodeLength(WwidDevicePath1)-sizeof (USB_WWID_DEVICE_PATH)) == 0))
|
|
{
|
|
*DeviceHandle = UsbIoBuffer[Index];
|
|
// Add the additional original Device Path Nodes (eg: FilePath Device Path Node) to the new Device Path
|
|
*NewDevicePath = AppendDevicePath (UsbIoDevicePath, NextDevicePathNode (RemovableDevicePath));
|
|
return EFI_SUCCESS;
|
|
}
|
|
} else {
|
|
UsbClassDevicePath1 = (USB_CLASS_DEVICE_PATH*)RemovableDevicePath;
|
|
UsbClassDevicePath2 = (USB_CLASS_DEVICE_PATH*)TmpDevicePath;
|
|
if ((UsbClassDevicePath1->VendorId != 0xFFFF) && (UsbClassDevicePath1->VendorId == UsbClassDevicePath2->VendorId) &&
|
|
(UsbClassDevicePath1->ProductId != 0xFFFF) && (UsbClassDevicePath1->ProductId == UsbClassDevicePath2->ProductId) &&
|
|
(UsbClassDevicePath1->DeviceClass != 0xFF) && (UsbClassDevicePath1->DeviceClass == UsbClassDevicePath2->DeviceClass) &&
|
|
(UsbClassDevicePath1->DeviceSubClass != 0xFF) && (UsbClassDevicePath1->DeviceSubClass == UsbClassDevicePath2->DeviceSubClass) &&
|
|
(UsbClassDevicePath1->DeviceProtocol != 0xFF) && (UsbClassDevicePath1->DeviceProtocol == UsbClassDevicePath2->DeviceProtocol))
|
|
{
|
|
*DeviceHandle = UsbIoBuffer[Index];
|
|
// Add the additional original Device Path Nodes (eg: FilePath Device Path Node) to the new Device Path
|
|
*NewDevicePath = AppendDevicePath (UsbIoDevicePath, NextDevicePathNode (RemovableDevicePath));
|
|
return EFI_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
TmpDevicePath = NextDevicePathNode (TmpDevicePath);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
BOOLEAN
|
|
BdsIsRemovableHd (
|
|
IN EFI_DEVICE_PATH* DevicePath
|
|
)
|
|
{
|
|
return IS_DEVICE_PATH_NODE (DevicePath, MEDIA_DEVICE_PATH, MEDIA_HARDDRIVE_DP);
|
|
}
|
|
|
|
EFI_STATUS
|
|
BdsGetDeviceHd (
|
|
IN EFI_DEVICE_PATH* RemovableDevicePath,
|
|
OUT EFI_HANDLE* DeviceHandle,
|
|
OUT EFI_DEVICE_PATH** NewDevicePath
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN Index;
|
|
UINTN PartitionHandleCount;
|
|
EFI_HANDLE *PartitionBuffer;
|
|
EFI_DEVICE_PATH* PartitionDevicePath;
|
|
EFI_DEVICE_PATH* TmpDevicePath;
|
|
HARDDRIVE_DEVICE_PATH* HardDriveDevicePath1;
|
|
HARDDRIVE_DEVICE_PATH* HardDriveDevicePath2;
|
|
|
|
// Get all the DiskIo handles
|
|
PartitionHandleCount = 0;
|
|
Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiDiskIoProtocolGuid, NULL, &PartitionHandleCount, &PartitionBuffer);
|
|
if (EFI_ERROR (Status) || (PartitionHandleCount == 0)) {
|
|
return Status;
|
|
}
|
|
|
|
// Check if one of the handles matches the Hard Disk Description
|
|
for (Index = 0; Index < PartitionHandleCount; Index++) {
|
|
Status = gBS->HandleProtocol (PartitionBuffer[Index], &gEfiDevicePathProtocolGuid, (VOID **) &PartitionDevicePath);
|
|
if (!EFI_ERROR (Status)) {
|
|
TmpDevicePath = PartitionDevicePath;
|
|
while (!IsDevicePathEnd (TmpDevicePath)) {
|
|
// Check if the Device Path node is a HD Removable device Path node
|
|
if (BdsIsRemovableHd (TmpDevicePath)) {
|
|
HardDriveDevicePath1 = (HARDDRIVE_DEVICE_PATH*)RemovableDevicePath;
|
|
HardDriveDevicePath2 = (HARDDRIVE_DEVICE_PATH*)TmpDevicePath;
|
|
if ((HardDriveDevicePath1->SignatureType == HardDriveDevicePath2->SignatureType) &&
|
|
(CompareGuid ((EFI_GUID *)HardDriveDevicePath1->Signature, (EFI_GUID *)HardDriveDevicePath2->Signature) == TRUE) &&
|
|
(HardDriveDevicePath1->PartitionNumber == HardDriveDevicePath2->PartitionNumber))
|
|
{
|
|
*DeviceHandle = PartitionBuffer[Index];
|
|
// Add the additional original Device Path Nodes (eg: FilePath Device Path Node) to the new Device Path
|
|
*NewDevicePath = AppendDevicePath (PartitionDevicePath, NextDevicePathNode (RemovableDevicePath));
|
|
return EFI_SUCCESS;
|
|
}
|
|
}
|
|
TmpDevicePath = NextDevicePathNode (TmpDevicePath);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
/*BOOLEAN
|
|
BdsIsRemovableCdrom (
|
|
IN EFI_DEVICE_PATH* DevicePath
|
|
)
|
|
{
|
|
return IS_DEVICE_PATH_NODE (DevicePath, MEDIA_DEVICE_PATH, MEDIA_CDROM_DP);
|
|
}
|
|
|
|
EFI_STATUS
|
|
BdsGetDeviceCdrom (
|
|
IN EFI_DEVICE_PATH* RemovableDevicePath,
|
|
OUT EFI_HANDLE* DeviceHandle,
|
|
OUT EFI_DEVICE_PATH** DevicePath
|
|
)
|
|
{
|
|
ASSERT(0);
|
|
return EFI_UNSUPPORTED;
|
|
}*/
|
|
|
|
typedef BOOLEAN
|
|
(*BDS_IS_REMOVABLE) (
|
|
IN EFI_DEVICE_PATH* DevicePath
|
|
);
|
|
|
|
typedef EFI_STATUS
|
|
(*BDS_GET_DEVICE) (
|
|
IN EFI_DEVICE_PATH* RemovableDevicePath,
|
|
OUT EFI_HANDLE* DeviceHandle,
|
|
OUT EFI_DEVICE_PATH** DevicePath
|
|
);
|
|
|
|
typedef struct {
|
|
BDS_IS_REMOVABLE IsRemovable;
|
|
BDS_GET_DEVICE GetDevice;
|
|
} BDS_REMOVABLE_DEVICE_SUPPORT;
|
|
|
|
BDS_REMOVABLE_DEVICE_SUPPORT RemovableDeviceSupport[] = {
|
|
{ BdsIsRemovableUsb, BdsGetDeviceUsb },
|
|
{ BdsIsRemovableHd, BdsGetDeviceHd },
|
|
//{ BdsIsRemovableCdrom, BdsGetDeviceCdrom }
|
|
};
|
|
|
|
STATIC
|
|
BOOLEAN
|
|
IsRemovableDevice (
|
|
IN EFI_DEVICE_PATH* DevicePath
|
|
)
|
|
{
|
|
UINTN Index;
|
|
EFI_DEVICE_PATH* TmpDevicePath;
|
|
|
|
TmpDevicePath = DevicePath;
|
|
while (!IsDevicePathEnd (TmpDevicePath)) {
|
|
for (Index = 0; Index < sizeof (RemovableDeviceSupport) / sizeof (BDS_REMOVABLE_DEVICE_SUPPORT); Index++) {
|
|
if (RemovableDeviceSupport[Index].IsRemovable (TmpDevicePath)) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
TmpDevicePath = NextDevicePathNode (TmpDevicePath);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
STATIC
|
|
EFI_STATUS
|
|
TryRemovableDevice (
|
|
IN EFI_DEVICE_PATH* DevicePath,
|
|
OUT EFI_HANDLE* DeviceHandle,
|
|
OUT EFI_DEVICE_PATH** NewDevicePath
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN Index;
|
|
EFI_DEVICE_PATH* TmpDevicePath;
|
|
BDS_REMOVABLE_DEVICE_SUPPORT* RemovableDevice;
|
|
EFI_DEVICE_PATH* RemovableDevicePath;
|
|
BOOLEAN RemovableFound;
|
|
|
|
RemovableDevice = NULL;
|
|
RemovableDevicePath = NULL;
|
|
RemovableFound = FALSE;
|
|
TmpDevicePath = DevicePath;
|
|
|
|
while (!IsDevicePathEnd (TmpDevicePath) && !RemovableFound) {
|
|
for (Index = 0; Index < sizeof (RemovableDeviceSupport) / sizeof (BDS_REMOVABLE_DEVICE_SUPPORT); Index++) {
|
|
RemovableDevice = &RemovableDeviceSupport[Index];
|
|
if (RemovableDevice->IsRemovable (TmpDevicePath)) {
|
|
RemovableDevicePath = TmpDevicePath;
|
|
RemovableFound = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
TmpDevicePath = NextDevicePathNode (TmpDevicePath);
|
|
}
|
|
|
|
if (!RemovableFound) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
// Search into the current started drivers
|
|
Status = RemovableDevice->GetDevice (RemovableDevicePath, DeviceHandle, NewDevicePath);
|
|
if (Status == EFI_NOT_FOUND) {
|
|
// Connect all the drivers
|
|
BdsConnectAllDrivers ();
|
|
|
|
// Search again into all the drivers
|
|
Status = RemovableDevice->GetDevice (RemovableDevicePath, DeviceHandle, NewDevicePath);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
STATIC
|
|
EFI_STATUS
|
|
BdsConnectAndUpdateDevicePath (
|
|
IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath,
|
|
OUT EFI_HANDLE *Handle,
|
|
OUT EFI_DEVICE_PATH_PROTOCOL **RemainingDevicePath
|
|
)
|
|
{
|
|
EFI_DEVICE_PATH* Remaining;
|
|
EFI_DEVICE_PATH* NewDevicePath;
|
|
EFI_STATUS Status;
|
|
EFI_HANDLE PreviousHandle;
|
|
|
|
if ((DevicePath == NULL) || (*DevicePath == NULL) || (Handle == NULL)) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
PreviousHandle = NULL;
|
|
do {
|
|
Remaining = *DevicePath;
|
|
|
|
// The LocateDevicePath() function locates all devices on DevicePath that support Protocol and returns
|
|
// the handle to the device that is closest to DevicePath. On output, the device path pointer is modified
|
|
// to point to the remaining part of the device path
|
|
Status = gBS->LocateDevicePath (&gEfiDevicePathProtocolGuid, &Remaining, Handle);
|
|
|
|
if (!EFI_ERROR (Status)) {
|
|
if (*Handle == PreviousHandle) {
|
|
//
|
|
// If no forward progress is made try invoking the Dispatcher.
|
|
// A new FV may have been added to the system and new drivers
|
|
// may now be found.
|
|
// Status == EFI_SUCCESS means a driver was dispatched
|
|
// Status == EFI_NOT_FOUND means no new drivers were dispatched
|
|
//
|
|
Status = gDS->Dispatch ();
|
|
}
|
|
|
|
if (!EFI_ERROR (Status)) {
|
|
PreviousHandle = *Handle;
|
|
|
|
// Recursive = FALSE: We do not want to start the whole device tree
|
|
Status = gBS->ConnectController (*Handle, NULL, Remaining, FALSE);
|
|
}
|
|
}
|
|
} while (!EFI_ERROR (Status) && !IsDevicePathEnd (Remaining));
|
|
|
|
if (!EFI_ERROR (Status)) {
|
|
// Now, we have got the whole Device Path connected, call again ConnectController to ensure all the supported Driver
|
|
// Binding Protocol are connected (such as DiskIo and SimpleFileSystem)
|
|
Remaining = *DevicePath;
|
|
Status = gBS->LocateDevicePath (&gEfiDevicePathProtocolGuid, &Remaining, Handle);
|
|
if (!EFI_ERROR (Status)) {
|
|
Status = gBS->ConnectController (*Handle, NULL, Remaining, FALSE);
|
|
if (EFI_ERROR (Status)) {
|
|
// If the last node is a Memory Map Device Path just return EFI_SUCCESS.
|
|
if ((Remaining->Type == HARDWARE_DEVICE_PATH) && (Remaining->SubType == HW_MEMMAP_DP)) {
|
|
Status = EFI_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
} else if (!IsDevicePathEnd (Remaining) && !IsRemovableDevice (Remaining)) {
|
|
|
|
/*// If the remaining Device Path is a FilePath or MemoryMap then we consider the Device Path has been loaded correctly
|
|
if ((Remaining->Type == MEDIA_DEVICE_PATH) && (Remaining->SubType == MEDIA_FILEPATH_DP)) {
|
|
Status = EFI_SUCCESS;
|
|
} else if ((Remaining->Type == HARDWARE_DEVICE_PATH) && (Remaining->SubType == HW_MEMMAP_DP)) {
|
|
Status = EFI_SUCCESS;
|
|
}*/
|
|
|
|
//TODO: Should we just return success and leave the caller decide if it is the expected RemainingPath
|
|
Status = EFI_SUCCESS;
|
|
} else {
|
|
Status = TryRemovableDevice (*DevicePath, Handle, &NewDevicePath);
|
|
if (!EFI_ERROR (Status)) {
|
|
Status = BdsConnectAndUpdateDevicePath (&NewDevicePath, Handle, RemainingDevicePath);
|
|
*DevicePath = NewDevicePath;
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
if (RemainingDevicePath) {
|
|
*RemainingDevicePath = Remaining;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Connect a Device Path and return the handle of the driver that support this DevicePath
|
|
|
|
@param DevicePath Device Path of the File to connect
|
|
@param Handle Handle of the driver that support this DevicePath
|
|
@param RemainingDevicePath Remaining DevicePath nodes that do not match the driver DevicePath
|
|
|
|
@retval EFI_SUCCESS A driver that matches the Device Path has been found
|
|
@retval EFI_NOT_FOUND No handles match the search.
|
|
@retval EFI_INVALID_PARAMETER DevicePath or Handle is NULL
|
|
|
|
**/
|
|
EFI_STATUS
|
|
BdsConnectDevicePath (
|
|
IN EFI_DEVICE_PATH_PROTOCOL* DevicePath,
|
|
OUT EFI_HANDLE *Handle,
|
|
OUT EFI_DEVICE_PATH_PROTOCOL **RemainingDevicePath
|
|
)
|
|
{
|
|
return BdsConnectAndUpdateDevicePath (&DevicePath, Handle, RemainingDevicePath);
|
|
}
|
|
|
|
BOOLEAN
|
|
BdsFileSystemSupport (
|
|
IN EFI_DEVICE_PATH *DevicePath,
|
|
IN EFI_HANDLE Handle,
|
|
IN EFI_DEVICE_PATH *RemainingDevicePath
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *FsProtocol;
|
|
|
|
Status = gBS->HandleProtocol (Handle, &gEfiSimpleFileSystemProtocolGuid, (VOID **)&FsProtocol);
|
|
|
|
return (!EFI_ERROR (Status) && IS_DEVICE_PATH_NODE (RemainingDevicePath, MEDIA_DEVICE_PATH, MEDIA_FILEPATH_DP));
|
|
}
|
|
|
|
EFI_STATUS
|
|
BdsFileSystemLoadImage (
|
|
IN OUT EFI_DEVICE_PATH **DevicePath,
|
|
IN EFI_HANDLE Handle,
|
|
IN EFI_DEVICE_PATH *RemainingDevicePath,
|
|
IN EFI_ALLOCATE_TYPE Type,
|
|
IN OUT EFI_PHYSICAL_ADDRESS *Image,
|
|
OUT UINTN *ImageSize
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
FILEPATH_DEVICE_PATH *FilePathDevicePath;
|
|
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *FsProtocol;
|
|
EFI_FILE_PROTOCOL *Fs;
|
|
EFI_FILE_INFO *FileInfo;
|
|
EFI_FILE_PROTOCOL *File;
|
|
UINTN Size;
|
|
|
|
ASSERT (IS_DEVICE_PATH_NODE (RemainingDevicePath, MEDIA_DEVICE_PATH, MEDIA_FILEPATH_DP));
|
|
|
|
FilePathDevicePath = (FILEPATH_DEVICE_PATH*)RemainingDevicePath;
|
|
|
|
Status = gBS->OpenProtocol (
|
|
Handle,
|
|
&gEfiSimpleFileSystemProtocolGuid,
|
|
(VOID**)&FsProtocol,
|
|
gImageHandle,
|
|
Handle,
|
|
EFI_OPEN_PROTOCOL_BY_DRIVER
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
// Try to Open the volume and get root directory
|
|
Status = FsProtocol->OpenVolume (FsProtocol, &Fs);
|
|
if (EFI_ERROR (Status)) {
|
|
goto CLOSE_PROTOCOL;
|
|
}
|
|
|
|
Status = Fs->Open (Fs, &File, FilePathDevicePath->PathName, EFI_FILE_MODE_READ, 0);
|
|
if (EFI_ERROR (Status)) {
|
|
goto CLOSE_PROTOCOL;
|
|
}
|
|
|
|
Size = 0;
|
|
File->GetInfo (File, &gEfiFileInfoGuid, &Size, NULL);
|
|
FileInfo = AllocatePool (Size);
|
|
Status = File->GetInfo (File, &gEfiFileInfoGuid, &Size, FileInfo);
|
|
if (EFI_ERROR (Status)) {
|
|
goto CLOSE_FILE;
|
|
}
|
|
|
|
// Get the file size
|
|
Size = FileInfo->FileSize;
|
|
if (ImageSize) {
|
|
*ImageSize = Size;
|
|
}
|
|
FreePool (FileInfo);
|
|
|
|
Status = gBS->AllocatePages (Type, EfiBootServicesCode, EFI_SIZE_TO_PAGES(Size), Image);
|
|
// Try to allocate in any pages if failed to allocate memory at the defined location
|
|
if ((Status == EFI_OUT_OF_RESOURCES) && (Type != AllocateAnyPages)) {
|
|
Status = gBS->AllocatePages (AllocateAnyPages, EfiBootServicesCode, EFI_SIZE_TO_PAGES(Size), Image);
|
|
}
|
|
if (!EFI_ERROR (Status)) {
|
|
Status = File->Read (File, &Size, (VOID*)(UINTN)(*Image));
|
|
}
|
|
|
|
CLOSE_FILE:
|
|
File->Close (File);
|
|
|
|
CLOSE_PROTOCOL:
|
|
gBS->CloseProtocol (
|
|
Handle,
|
|
&gEfiSimpleFileSystemProtocolGuid,
|
|
gImageHandle,
|
|
Handle);
|
|
|
|
return Status;
|
|
}
|
|
|
|
BOOLEAN
|
|
BdsMemoryMapSupport (
|
|
IN EFI_DEVICE_PATH *DevicePath,
|
|
IN EFI_HANDLE Handle,
|
|
IN EFI_DEVICE_PATH *RemainingDevicePath
|
|
)
|
|
{
|
|
return IS_DEVICE_PATH_NODE (DevicePath, HARDWARE_DEVICE_PATH, HW_MEMMAP_DP) ||
|
|
IS_DEVICE_PATH_NODE (RemainingDevicePath, HARDWARE_DEVICE_PATH, HW_MEMMAP_DP);
|
|
}
|
|
|
|
EFI_STATUS
|
|
BdsMemoryMapLoadImage (
|
|
IN OUT EFI_DEVICE_PATH **DevicePath,
|
|
IN EFI_HANDLE Handle,
|
|
IN EFI_DEVICE_PATH *RemainingDevicePath,
|
|
IN EFI_ALLOCATE_TYPE Type,
|
|
IN OUT EFI_PHYSICAL_ADDRESS* Image,
|
|
OUT UINTN *ImageSize
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
MEMMAP_DEVICE_PATH* MemMapPathDevicePath;
|
|
UINTN Size;
|
|
|
|
if (IS_DEVICE_PATH_NODE (RemainingDevicePath, HARDWARE_DEVICE_PATH, HW_MEMMAP_DP)) {
|
|
MemMapPathDevicePath = (MEMMAP_DEVICE_PATH*)RemainingDevicePath;
|
|
} else {
|
|
ASSERT (IS_DEVICE_PATH_NODE (*DevicePath, HARDWARE_DEVICE_PATH, HW_MEMMAP_DP));
|
|
MemMapPathDevicePath = (MEMMAP_DEVICE_PATH*)*DevicePath;
|
|
}
|
|
|
|
Size = MemMapPathDevicePath->EndingAddress - MemMapPathDevicePath->StartingAddress;
|
|
if (Size == 0) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
Status = gBS->AllocatePages (Type, EfiBootServicesCode, EFI_SIZE_TO_PAGES(Size), Image);
|
|
// Try to allocate in any pages if failed to allocate memory at the defined location
|
|
if ((Status == EFI_OUT_OF_RESOURCES) && (Type != AllocateAnyPages)) {
|
|
Status = gBS->AllocatePages (AllocateAnyPages, EfiBootServicesCode, EFI_SIZE_TO_PAGES(Size), Image);
|
|
}
|
|
if (!EFI_ERROR (Status)) {
|
|
CopyMem ((VOID*)(UINTN)(*Image), (CONST VOID*)(UINTN)MemMapPathDevicePath->StartingAddress, Size);
|
|
|
|
if (ImageSize != NULL) {
|
|
*ImageSize = Size;
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
BOOLEAN
|
|
BdsFirmwareVolumeSupport (
|
|
IN EFI_DEVICE_PATH *DevicePath,
|
|
IN EFI_HANDLE Handle,
|
|
IN EFI_DEVICE_PATH *RemainingDevicePath
|
|
)
|
|
{
|
|
return IS_DEVICE_PATH_NODE (RemainingDevicePath, MEDIA_DEVICE_PATH, MEDIA_PIWG_FW_FILE_DP);
|
|
}
|
|
|
|
EFI_STATUS
|
|
BdsFirmwareVolumeLoadImage (
|
|
IN OUT EFI_DEVICE_PATH **DevicePath,
|
|
IN EFI_HANDLE Handle,
|
|
IN EFI_DEVICE_PATH *RemainingDevicePath,
|
|
IN EFI_ALLOCATE_TYPE Type,
|
|
IN OUT EFI_PHYSICAL_ADDRESS* Image,
|
|
OUT UINTN *ImageSize
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_FIRMWARE_VOLUME2_PROTOCOL *FwVol;
|
|
EFI_GUID *FvNameGuid;
|
|
EFI_SECTION_TYPE SectionType;
|
|
EFI_FV_FILETYPE FvType;
|
|
EFI_FV_FILE_ATTRIBUTES Attrib;
|
|
UINT32 AuthenticationStatus;
|
|
VOID* ImageBuffer;
|
|
|
|
ASSERT (IS_DEVICE_PATH_NODE (RemainingDevicePath, MEDIA_DEVICE_PATH, MEDIA_PIWG_FW_FILE_DP));
|
|
|
|
Status = gBS->HandleProtocol (Handle, &gEfiFirmwareVolume2ProtocolGuid, (VOID **)&FwVol);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
FvNameGuid = EfiGetNameGuidFromFwVolDevicePathNode ((CONST MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *)RemainingDevicePath);
|
|
if (FvNameGuid == NULL) {
|
|
Status = EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
SectionType = EFI_SECTION_PE32;
|
|
AuthenticationStatus = 0;
|
|
//Note: ReadSection at the opposite of ReadFile does not allow to pass ImageBuffer == NULL to get the size of the file.
|
|
ImageBuffer = NULL;
|
|
Status = FwVol->ReadSection (
|
|
FwVol,
|
|
FvNameGuid,
|
|
SectionType,
|
|
0,
|
|
&ImageBuffer,
|
|
ImageSize,
|
|
&AuthenticationStatus
|
|
);
|
|
if (!EFI_ERROR (Status)) {
|
|
#if 0
|
|
// In case the buffer has some address requirements, we must copy the buffer to a buffer following the requirements
|
|
if (Type != AllocateAnyPages) {
|
|
Status = gBS->AllocatePages (Type, EfiBootServicesCode, EFI_SIZE_TO_PAGES(*ImageSize),Image);
|
|
if (!EFI_ERROR (Status)) {
|
|
CopyMem ((VOID*)(UINTN)(*Image), ImageBuffer, *ImageSize);
|
|
FreePool (ImageBuffer);
|
|
}
|
|
}
|
|
#else
|
|
// We must copy the buffer into a page allocations. Otherwise, the caller could call gBS->FreePages() on the pool allocation
|
|
Status = gBS->AllocatePages (Type, EfiBootServicesCode, EFI_SIZE_TO_PAGES(*ImageSize), Image);
|
|
// Try to allocate in any pages if failed to allocate memory at the defined location
|
|
if ((Status == EFI_OUT_OF_RESOURCES) && (Type != AllocateAnyPages)) {
|
|
Status = gBS->AllocatePages (AllocateAnyPages, EfiBootServicesCode, EFI_SIZE_TO_PAGES(*ImageSize), Image);
|
|
}
|
|
if (!EFI_ERROR (Status)) {
|
|
CopyMem ((VOID*)(UINTN)(*Image), ImageBuffer, *ImageSize);
|
|
FreePool (ImageBuffer);
|
|
}
|
|
#endif
|
|
} else {
|
|
// Try a raw file, since a PE32 SECTION does not exist
|
|
Status = FwVol->ReadFile (
|
|
FwVol,
|
|
FvNameGuid,
|
|
NULL,
|
|
ImageSize,
|
|
&FvType,
|
|
&Attrib,
|
|
&AuthenticationStatus
|
|
);
|
|
if (!EFI_ERROR (Status)) {
|
|
Status = gBS->AllocatePages (Type, EfiBootServicesCode, EFI_SIZE_TO_PAGES(*ImageSize), Image);
|
|
// Try to allocate in any pages if failed to allocate memory at the defined location
|
|
if ((Status == EFI_OUT_OF_RESOURCES) && (Type != AllocateAnyPages)) {
|
|
Status = gBS->AllocatePages (AllocateAnyPages, EfiBootServicesCode, EFI_SIZE_TO_PAGES(*ImageSize), Image);
|
|
}
|
|
if (!EFI_ERROR (Status)) {
|
|
Status = FwVol->ReadFile (
|
|
FwVol,
|
|
FvNameGuid,
|
|
(VOID**)Image,
|
|
ImageSize,
|
|
&FvType,
|
|
&Attrib,
|
|
&AuthenticationStatus
|
|
);
|
|
}
|
|
}
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
BOOLEAN
|
|
BdsPxeSupport (
|
|
IN EFI_DEVICE_PATH* DevicePath,
|
|
IN EFI_HANDLE Handle,
|
|
IN EFI_DEVICE_PATH* RemainingDevicePath
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_PXE_BASE_CODE_PROTOCOL* PxeBcProtocol;
|
|
|
|
if (!IsDevicePathEnd (RemainingDevicePath)) {
|
|
return FALSE;
|
|
}
|
|
|
|
Status = gBS->HandleProtocol (Handle, &gEfiPxeBaseCodeProtocolGuid, (VOID **)&PxeBcProtocol);
|
|
if (EFI_ERROR (Status)) {
|
|
return FALSE;
|
|
} else {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
EFI_STATUS
|
|
BdsPxeLoadImage (
|
|
IN OUT EFI_DEVICE_PATH **DevicePath,
|
|
IN EFI_HANDLE Handle,
|
|
IN EFI_DEVICE_PATH *RemainingDevicePath,
|
|
IN EFI_ALLOCATE_TYPE Type,
|
|
IN OUT EFI_PHYSICAL_ADDRESS* Image,
|
|
OUT UINTN *ImageSize
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_LOAD_FILE_PROTOCOL *LoadFileProtocol;
|
|
UINTN BufferSize;
|
|
EFI_PXE_BASE_CODE_PROTOCOL *Pxe;
|
|
|
|
// Get Load File Protocol attached to the PXE protocol
|
|
Status = gBS->HandleProtocol (Handle, &gEfiLoadFileProtocolGuid, (VOID **)&LoadFileProtocol);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Status = LoadFileProtocol->LoadFile (LoadFileProtocol, RemainingDevicePath, TRUE, &BufferSize, NULL);
|
|
if (Status == EFI_BUFFER_TOO_SMALL) {
|
|
Status = gBS->AllocatePages (Type, EfiBootServicesCode, EFI_SIZE_TO_PAGES(BufferSize), Image);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Status = LoadFileProtocol->LoadFile (LoadFileProtocol, RemainingDevicePath, TRUE, &BufferSize, (VOID*)(UINTN)(*Image));
|
|
if (!EFI_ERROR (Status) && (ImageSize != NULL)) {
|
|
*ImageSize = BufferSize;
|
|
}
|
|
}
|
|
|
|
if (Status == EFI_ALREADY_STARTED) {
|
|
Status = gBS->LocateProtocol (&gEfiPxeBaseCodeProtocolGuid, NULL, (VOID **)&Pxe);
|
|
if (!EFI_ERROR(Status)) {
|
|
// If PXE is already started, we stop it
|
|
Pxe->Stop (Pxe);
|
|
// And we try again
|
|
return BdsPxeLoadImage (DevicePath, Handle, RemainingDevicePath, Type, Image, ImageSize);
|
|
}
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
BOOLEAN
|
|
BdsTftpSupport (
|
|
IN EFI_DEVICE_PATH *DevicePath,
|
|
IN EFI_HANDLE Handle,
|
|
IN EFI_DEVICE_PATH *RemainingDevicePath
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_DEVICE_PATH *NextDevicePath;
|
|
VOID *Interface;
|
|
|
|
// Validate the Remaining Device Path
|
|
if (IsDevicePathEnd (RemainingDevicePath)) {
|
|
return FALSE;
|
|
}
|
|
if (!IS_DEVICE_PATH_NODE (RemainingDevicePath, MESSAGING_DEVICE_PATH, MSG_IPv4_DP) &&
|
|
!IS_DEVICE_PATH_NODE (RemainingDevicePath, MESSAGING_DEVICE_PATH, MSG_IPv6_DP)) {
|
|
return FALSE;
|
|
}
|
|
NextDevicePath = NextDevicePathNode (RemainingDevicePath);
|
|
if (IsDevicePathEnd (NextDevicePath)) {
|
|
return FALSE;
|
|
}
|
|
if (!IS_DEVICE_PATH_NODE (NextDevicePath, MEDIA_DEVICE_PATH, MEDIA_FILEPATH_DP)) {
|
|
return FALSE;
|
|
}
|
|
|
|
Status = gBS->HandleProtocol (
|
|
Handle, &gEfiDevicePathProtocolGuid,
|
|
&Interface
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// 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);
|
|
CopyMem (Progress, mTftpProgressFrame, sizeof 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 OUT EFI_DEVICE_PATH **DevicePath,
|
|
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_HANDLE Dhcp4ChildHandle;
|
|
EFI_DHCP4_PROTOCOL *Dhcp4;
|
|
BOOLEAN Dhcp4ToStop;
|
|
EFI_HANDLE Mtftp4ChildHandle;
|
|
EFI_MTFTP4_PROTOCOL *Mtftp4;
|
|
DHCP4_OPTION ParaList;
|
|
EFI_DHCP4_PACKET_OPTION *OptionList[2];
|
|
EFI_DHCP4_CONFIG_DATA Dhcp4CfgData;
|
|
EFI_DHCP4_MODE_DATA Dhcp4Mode;
|
|
EFI_MTFTP4_CONFIG_DATA Mtftp4CfgData;
|
|
IPv4_DEVICE_PATH *IPv4DevicePathNode;
|
|
CHAR16 *PathName;
|
|
CHAR8 *AsciiFilePath;
|
|
EFI_MTFTP4_TOKEN Mtftp4Token;
|
|
UINT64 FileSize;
|
|
UINT64 TftpBufferSize;
|
|
BDS_TFTP_CONTEXT *TftpContext;
|
|
UINTN PathNameLen;
|
|
|
|
ASSERT(IS_DEVICE_PATH_NODE (RemainingDevicePath, MESSAGING_DEVICE_PATH, MSG_IPv4_DP));
|
|
IPv4DevicePathNode = (IPv4_DEVICE_PATH*)RemainingDevicePath;
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
//
|
|
// 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;
|
|
}
|
|
|
|
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));
|
|
ParaList.Head.OpCode = DHCP_TAG_PARA_LIST;
|
|
ParaList.Head.Length = 2;
|
|
ParaList.Head.Data[0] = DHCP_TAG_NETMASK;
|
|
ParaList.Route = DHCP_TAG_ROUTER;
|
|
OptionList[0] = &ParaList.Head;
|
|
Dhcp4CfgData.OptionCount = 1;
|
|
Dhcp4CfgData.OptionList = OptionList;
|
|
|
|
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;
|
|
}
|
|
|
|
// The Device Path might contain multiple FilePath nodes
|
|
PathName = ConvertDevicePathToText ((EFI_DEVICE_PATH_PROTOCOL*)(IPv4DevicePathNode + 1), FALSE, FALSE);
|
|
PathNameLen = StrLen (PathName) + 1;
|
|
AsciiFilePath = AllocatePool (PathNameLen);
|
|
UnicodeStrToAsciiStrS (PathName, AsciiFilePath, PathNameLen);
|
|
|
|
//
|
|
// 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_16MB;
|
|
}
|
|
|
|
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_16MB) & (~(SIZE_16MB-1))) {
|
|
//
|
|
// Allocate a buffer to hold the whole file.
|
|
//
|
|
Status = gBS->AllocatePages (
|
|
Type,
|
|
EfiBootServicesCode,
|
|
EFI_SIZE_TO_PAGES (TftpBufferSize),
|
|
Image
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
Print (L"Failed to allocate space for image\n");
|
|
goto Error;
|
|
}
|
|
|
|
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 <%a> from the TFTP server\n", AsciiFilePath);
|
|
Status = Mtftp4->ReadFile (Mtftp4, &Mtftp4Token);
|
|
Print (L"\n");
|
|
if (EFI_ERROR (Status)) {
|
|
gBS->FreePages (*Image, EFI_SIZE_TO_PAGES (TftpBufferSize));
|
|
if (Status == EFI_BUFFER_TOO_SMALL) {
|
|
Print (L"Downloading failed, file larger than expected.\n");
|
|
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)) {
|
|
*Image = 0;
|
|
Print (L"Failed to download the file - Error=%r\n", Status);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
BDS_FILE_LOADER FileLoaders[] = {
|
|
{ BdsFileSystemSupport, BdsFileSystemLoadImage },
|
|
{ BdsFirmwareVolumeSupport, BdsFirmwareVolumeLoadImage },
|
|
//{ BdsLoadFileSupport, BdsLoadFileLoadImage },
|
|
{ BdsMemoryMapSupport, BdsMemoryMapLoadImage },
|
|
{ BdsPxeSupport, BdsPxeLoadImage },
|
|
{ BdsTftpSupport, BdsTftpLoadImage },
|
|
{ NULL, NULL }
|
|
};
|
|
|
|
EFI_STATUS
|
|
BdsLoadImageAndUpdateDevicePath (
|
|
IN OUT EFI_DEVICE_PATH **DevicePath,
|
|
IN EFI_ALLOCATE_TYPE Type,
|
|
IN OUT EFI_PHYSICAL_ADDRESS* Image,
|
|
OUT UINTN *FileSize
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_HANDLE Handle;
|
|
EFI_DEVICE_PATH *RemainingDevicePath;
|
|
BDS_FILE_LOADER* FileLoader;
|
|
|
|
Status = BdsConnectAndUpdateDevicePath (DevicePath, &Handle, &RemainingDevicePath);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
FileLoader = FileLoaders;
|
|
while (FileLoader->Support != NULL) {
|
|
if (FileLoader->Support (*DevicePath, Handle, RemainingDevicePath)) {
|
|
return FileLoader->LoadImage (DevicePath, Handle, RemainingDevicePath, Type, Image, FileSize);
|
|
}
|
|
FileLoader++;
|
|
}
|
|
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
EFI_STATUS
|
|
BdsLoadImage (
|
|
IN EFI_DEVICE_PATH *DevicePath,
|
|
IN EFI_ALLOCATE_TYPE Type,
|
|
IN OUT EFI_PHYSICAL_ADDRESS* Image,
|
|
OUT UINTN *FileSize
|
|
)
|
|
{
|
|
return BdsLoadImageAndUpdateDevicePath (&DevicePath, Type, Image, FileSize);
|
|
}
|
|
|
|
/**
|
|
Start an EFI Application from a Device Path
|
|
|
|
@param ParentImageHandle Handle of the calling image
|
|
@param DevicePath Location of the EFI Application
|
|
|
|
@retval EFI_SUCCESS All drivers have been connected
|
|
@retval EFI_NOT_FOUND The Linux kernel Device Path has not been found
|
|
@retval EFI_OUT_OF_RESOURCES There is not enough resource memory to store the matching results.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
BdsStartEfiApplication (
|
|
IN EFI_HANDLE ParentImageHandle,
|
|
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
|
|
IN UINTN LoadOptionsSize,
|
|
IN VOID* LoadOptions
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_HANDLE ImageHandle;
|
|
EFI_PHYSICAL_ADDRESS BinaryBuffer;
|
|
UINTN BinarySize;
|
|
EFI_LOADED_IMAGE_PROTOCOL* LoadedImage;
|
|
|
|
// Find the nearest supported file loader
|
|
Status = BdsLoadImageAndUpdateDevicePath (&DevicePath, AllocateAnyPages, &BinaryBuffer, &BinarySize);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
// Load the image from the Buffer with Boot Services function
|
|
Status = gBS->LoadImage (TRUE, ParentImageHandle, DevicePath, (VOID*)(UINTN)BinaryBuffer, BinarySize, &ImageHandle);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
// Passed LoadOptions to the EFI Application
|
|
if (LoadOptionsSize != 0) {
|
|
Status = gBS->HandleProtocol (ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **) &LoadedImage);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
LoadedImage->LoadOptionsSize = LoadOptionsSize;
|
|
LoadedImage->LoadOptions = LoadOptions;
|
|
}
|
|
|
|
// Before calling the image, enable the Watchdog Timer for the 5 Minute period
|
|
gBS->SetWatchdogTimer (5 * 60, 0x0000, 0x00, NULL);
|
|
// Start the image
|
|
Status = gBS->StartImage (ImageHandle, NULL, NULL);
|
|
// Clear the Watchdog Timer after the image returns
|
|
gBS->SetWatchdogTimer (0x0000, 0x0000, 0x0000, NULL);
|
|
|
|
return Status;
|
|
}
|