X-Git-Url: https://git.proxmox.com/?p=mirror_edk2.git;a=blobdiff_plain;f=ArmPkg%2FLibrary%2FBdsLib%2FBdsFilePath.c;h=41557bb91816e7df488298e72e44994090b2af6b;hp=88596c7c6a30bab1b2c05c137b0d822bded083ea;hb=edc65fc4d83a7083ed139c00b8669c959a7fae8f;hpb=7598f36265e156f516fe93e0416d4d5b2cd70101 diff --git a/ArmPkg/Library/BdsLib/BdsFilePath.c b/ArmPkg/Library/BdsLib/BdsFilePath.c index 88596c7c6a..41557bb918 100644 --- a/ArmPkg/Library/BdsLib/BdsFilePath.c +++ b/ArmPkg/Library/BdsLib/BdsFilePath.c @@ -1,906 +1,1411 @@ -/** @file -* -* Copyright (c) 2011, 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 -#include -#include - -#define IS_DEVICE_PATH_NODE(node,type,subtype) (((node)->Type == (type)) && ((node)->SubType == (subtype))) - -// 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; -} - -/** - 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 - ) -{ - EFI_DEVICE_PATH* Remaining; - EFI_DEVICE_PATH* NewDevicePath; - EFI_STATUS Status; - - if ((DevicePath == NULL) || (Handle == NULL)) { - return EFI_INVALID_PARAMETER; - } - - 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)) { - // Recursive = FALSE: We do not want to start all the device tree - Status = gBS->ConnectController (*Handle, NULL, Remaining, FALSE); - } - - /*// We need to check if RemainingDevicePath does not point on the last node. Otherwise, calling - // NextDevicePathNode() will return an undetermined Device Path Node - if (!IsDevicePathEnd (RemainingDevicePath)) { - RemainingDevicePath = NextDevicePathNode (RemainingDevicePath); - }*/ - } 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)) { - return BdsConnectDevicePath (NewDevicePath, Handle, RemainingDevicePath); - } - } - - if (RemainingDevicePath) { - *RemainingDevicePath = Remaining; - } - - return Status; -} - -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 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 - ) -{ - FILEPATH_DEVICE_PATH* FilePathDevicePath; - EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *FsProtocol; - EFI_FILE_PROTOCOL *Fs; - EFI_STATUS Status; - 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->HandleProtocol(Handle,&gEfiSimpleFileSystemProtocolGuid, (VOID **)&FsProtocol); - if (EFI_ERROR(Status)) { - return Status; - } - - // Try to Open the volume and get root directory - Status = FsProtocol->OpenVolume (FsProtocol, &Fs); - if (EFI_ERROR(Status)) { - return Status; - } - - File = NULL; - Status = Fs->Open(Fs, &File, FilePathDevicePath->PathName, EFI_FILE_MODE_READ, 0); - if (EFI_ERROR(Status)) { - return Status; - } - - Size = 0; - File->GetInfo(File, &gEfiFileInfoGuid, &Size, NULL); - FileInfo = AllocatePool (Size); - Status = File->GetInfo(File, &gEfiFileInfoGuid, &Size, FileInfo); - if (EFI_ERROR(Status)) { - return Status; - } - - // 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)); - } - - 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 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 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*)(UINTN)(*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 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; - - // 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, DevicePath, 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, DevicePath, TRUE, &BufferSize, (VOID*)(UINTN)(*Image)); - if (!EFI_ERROR(Status) && (ImageSize != NULL)) { - *ImageSize = BufferSize; - } - } - - 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; - EFI_PXE_BASE_CODE_PROTOCOL *PxeBcProtocol; - - // 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, &gEfiPxeBaseCodeProtocolGuid, (VOID **)&PxeBcProtocol); - if (EFI_ERROR (Status)) { - return FALSE; - } else { - return TRUE; - } -} - -EFI_STATUS -BdsTftpLoadImage ( - IN 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_PXE_BASE_CODE_PROTOCOL *Pxe; - UINT64 TftpBufferSize; - VOID* TftpBuffer; - EFI_IP_ADDRESS ServerIp; - IPv4_DEVICE_PATH* IPv4DevicePathNode; - FILEPATH_DEVICE_PATH* FilePathDevicePath; - EFI_IP_ADDRESS LocalIp; - - 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; - } - - Status = Pxe->Start (Pxe, FALSE); - if (EFI_ERROR(Status) && (Status != EFI_ALREADY_STARTED)) { - return Status; - } - - 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 (EFI_ERROR(Status)) { - return Status; - } - - CopyMem (&ServerIp.v4, &IPv4DevicePathNode->RemoteIpAddress, sizeof (EFI_IPv4_ADDRESS)); - - Status = Pxe->Mtftp ( - Pxe, - EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE, - NULL, - FALSE, - &TftpBufferSize, - NULL, - &ServerIp, - (UINT8 *)FilePathDevicePath->PathName, - NULL, - TRUE - ); - if (EFI_ERROR(Status)) { - return Status; - } - - // Allocate a buffer to hold the whole file. - TftpBuffer = AllocatePool(TftpBufferSize); - if (TftpBuffer == NULL) { - return EFI_OUT_OF_RESOURCES; - } - - Status = Pxe->Mtftp ( - Pxe, - EFI_PXE_BASE_CODE_TFTP_READ_FILE, - TftpBuffer, - FALSE, - &TftpBufferSize, - NULL, - &ServerIp, - (UINT8 *)FilePathDevicePath->PathName, - NULL, - FALSE - ); - if (EFI_ERROR(Status)) { - FreePool(TftpBuffer); - } else if (ImageSize != NULL) { - *ImageSize = (UINTN)TftpBufferSize; - } - - return Status; -} - -BDS_FILE_LOADER FileLoaders[] = { - { BdsFileSystemSupport, BdsFileSystemLoadImage }, - { BdsFirmwareVolumeSupport, BdsFirmwareVolumeLoadImage }, - //{ BdsLoadFileSupport, BdsLoadFileLoadImage }, - { BdsMemoryMapSupport, BdsMemoryMapLoadImage }, - { BdsPxeSupport, BdsPxeLoadImage }, - { BdsTftpSupport, BdsTftpLoadImage }, - { NULL, NULL } -}; - -EFI_STATUS -BdsLoadImage ( - IN 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 = BdsConnectDevicePath (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; -} - -/** - 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 = BdsLoadImage (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; -} +/** @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 + +#include +#include +#include +#include +#include +#include +#include + +/* 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; +}