| /** @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; | |
| } |