]> git.proxmox.com Git - mirror_edk2.git/blobdiff - ArmPkg/Library/BdsLib/BdsFilePath.c
ArmPkg: Move IS_DEVICE_PATH_NODE for sharing
[mirror_edk2.git] / ArmPkg / Library / BdsLib / BdsFilePath.c
index b1460b9c9db1d44e85afd6d378043e19c9d0b49a..41557bb91816e7df488298e72e44994090b2af6b 100644 (file)
-/** @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"
-
-// Count the number of DevicePath Node
-static UINTN NumberNodeFromDevicePath(
-    IN  EFI_DEVICE_PATH_PROTOCOL* DevicePath
-) {
-    UINTN NumberDevicePathNode = 0;
-
-    while (!IsDevicePathEnd (DevicePath)) {
-        NumberDevicePathNode++;
-        DevicePath = NextDevicePathNode(DevicePath);
-    }
-    return NumberDevicePathNode;
-}
-
-// 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;
-    }
-}
-
-EFI_STATUS
-BdsLoadDevicePath(
-    IN  EFI_DEVICE_PATH_PROTOCOL* DevicePath,
-    OUT EFI_HANDLE                *Handle
-) {
-    EFI_DEVICE_PATH_PROTOCOL    *RemainingDevicePath;
-    EFI_STATUS                  Status;
-
-    if ((DevicePath == NULL) || (Handle == NULL)) {
-        return EFI_INVALID_PARAMETER;
-    }
-
-    do {
-        RemainingDevicePath = 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,&RemainingDevicePath,Handle);
-        if (!EFI_ERROR (Status)) {
-            // Recursive = FALSE: We do not want to start all the device tree
-            Status = gBS->ConnectController (*Handle, NULL, RemainingDevicePath, 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 (RemainingDevicePath));
-
-    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)
-        RemainingDevicePath = DevicePath;
-        Status = gBS->LocateDevicePath(&gEfiDevicePathProtocolGuid,&RemainingDevicePath,Handle);
-        if (!EFI_ERROR (Status)) {
-            Status = gBS->ConnectController (*Handle, NULL, RemainingDevicePath, FALSE);
-            if (EFI_ERROR (Status)) {
-                // If the last node is a Memory Map Device Path just return EFI_SUCCESS.
-                if ((RemainingDevicePath->Type == HARDWARE_DEVICE_PATH) && (RemainingDevicePath->SubType == HW_MEMMAP_DP)) {
-                    Status = EFI_SUCCESS;
-                }
-            }
-        }
-    } else if (IsDevicePathEnd (RemainingDevicePath)) {
-        // Case when the DevicePath contains a MemoryMap Device Path Node and all drivers are connected.
-        // Ensure the Device Path exists
-        RemainingDevicePath = DevicePath;
-        Status = gBS->LocateDevicePath(&gEfiDevicePathProtocolGuid,&RemainingDevicePath,Handle);
-    }
-
-    return Status;
-}
-
-
-EFI_STATUS
-BdsLoadFilePath (
-    IN  CONST CHAR16        *DeviceFilePath,
-    OUT BDS_FILE            *File
-) {
-    EFI_DEVICE_PATH_PROTOCOL            *DevicePath;
-    EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL  *EfiDevicePathFromTextProtocol;
-    EFI_STATUS Status;
-    EFI_HANDLE Handle;
-    UINTN                               NumberDevicePathNode;
-    CHAR16                              *FilePath;
-
-    //Do a sanity check on the Device file path
-    if (DeviceFilePath == NULL) {
-        return EFI_INVALID_PARAMETER;
-    }
-
-    //  Convert the Device Path String into Device Path Protocol
-    Status = gBS->LocateProtocol(&gEfiDevicePathFromTextProtocolGuid, NULL, (VOID **)&EfiDevicePathFromTextProtocol);
-    ASSERT_EFI_ERROR(Status);
-    DevicePath = EfiDevicePathFromTextProtocol->ConvertTextToDevicePath(DeviceFilePath);
-
-    //Do a sanity check on the Device Path
-    if (DevicePath == NULL) {
-        return EFI_INVALID_PARAMETER;
-    }
-
-    // Count the number of DevicePath Node
-    NumberDevicePathNode = NumberNodeFromDevicePath(DevicePath);
-    // Extract the FilePath from the Device Path
-    FilePath = BdsExtractFilePathFromDevicePath(DeviceFilePath,NumberDevicePathNode);
-
-    Status = BdsLoadDevicePath(DevicePath,&Handle);
-    if (EFI_ERROR (Status)) {
-        return Status;
-    }
-
-    //If FilePath == NULL then let consider if a MemoryMap Device Path
-    if (FilePath == NULL) {
-        // Check if the Node is a MemoryMap Device Path
-        Status = BdsLoadFileFromMemMap(Handle,DevicePath,File);
-    } else {
-        Status = BdsLoadFileFromSimpleFileSystem(Handle,FilePath,File);
-        if (EFI_ERROR (Status)) {
-            Status = BdsLoadFileFromFirmwareVolume(Handle,FilePath,EFI_FV_FILETYPE_ALL,File);
-        }
-    }
-
-    if (!EFI_ERROR (Status)) {
-        File->DevicePath = DevicePath;
-    }
-
-    return Status;
-}
-
-EFI_STATUS BdsCopyRawFileToRuntimeMemory(
-    IN  BDS_FILE            *File,
-    OUT VOID                **FileImage,
-    OUT UINTN               *FileSize
-) {
-    if (File == NULL) {
-        return EFI_INVALID_PARAMETER;
-    }
-
-    if (File->Type == BDS_FILETYPE_FS) {
-        return BdsCopyRawFileToRuntimeMemoryFS(File->File.Fs.Handle,FileImage,FileSize);
-    } else if (File->Type == BDS_FILETYPE_FV) {
-        return BdsCopyRawFileToRuntimeMemoryFV(&(File->File.Fv),FileImage,FileSize);
-    } else if (File->Type == BDS_FILETYPE_MEM) {
-        return BdsCopyRawFileToRuntimeMemoryMemMap(&(File->File.Mem),FileImage,FileSize);
-    } else {
-        return EFI_INVALID_PARAMETER;
-    }
-}
+/** @file\r
+*\r
+*  Copyright (c) 2011-2014, ARM Limited. All rights reserved.\r
+*\r
+*  This program and the accompanying materials\r
+*  are licensed and made available under the terms and conditions of the BSD License\r
+*  which accompanies this distribution.  The full text of the license may be found at\r
+*  http://opensource.org/licenses/bsd-license.php\r
+*\r
+*  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+*  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+*\r
+**/\r
+\r
+#include "BdsInternal.h"\r
+\r
+#include <Library/NetLib.h>\r
+\r
+#include <Protocol/Bds.h>\r
+#include <Protocol/UsbIo.h>\r
+#include <Protocol/DiskIo.h>\r
+#include <Protocol/LoadedImage.h>\r
+#include <Protocol/SimpleNetwork.h>\r
+#include <Protocol/Dhcp4.h>\r
+#include <Protocol/Mtftp4.h>\r
+\r
+/* Type and defines to set up the DHCP4 options */\r
+\r
+typedef struct {\r
+  EFI_DHCP4_PACKET_OPTION Head;\r
+  UINT8                   Route;\r
+} DHCP4_OPTION;\r
+\r
+#define DHCP_TAG_PARA_LIST  55\r
+#define DHCP_TAG_NETMASK     1\r
+#define DHCP_TAG_ROUTER      3\r
+\r
+/*\r
+   Constant strings and define related to the message indicating the amount of\r
+   progress in the dowloading of a TFTP file.\r
+*/\r
+\r
+// Frame for the progression slider\r
+STATIC CONST CHAR16 mTftpProgressFrame[] = L"[                                        ]";\r
+\r
+// Number of steps in the progression slider\r
+#define TFTP_PROGRESS_SLIDER_STEPS  ((sizeof (mTftpProgressFrame) / sizeof (CHAR16)) - 3)\r
+\r
+// Size in number of characters plus one (final zero) of the message to\r
+// indicate the progress of a tftp download. The format is "[(progress slider:\r
+// 40 characters)] (nb of KBytes downloaded so far: 7 characters) Kb". There\r
+// are thus the number of characters in mTftpProgressFrame[] plus 11 characters\r
+// (2 // spaces, "Kb" and seven characters for the number of KBytes).\r
+#define TFTP_PROGRESS_MESSAGE_SIZE  ((sizeof (mTftpProgressFrame) / sizeof (CHAR16)) + 12)\r
+\r
+// String to delete the tftp progress message to be able to update it :\r
+// (TFTP_PROGRESS_MESSAGE_SIZE-1) '\b'\r
+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";\r
+\r
+\r
+// Extract the FilePath from the Device Path\r
+CHAR16*\r
+BdsExtractFilePathFromDevicePath (\r
+  IN  CONST CHAR16    *StrDevicePath,\r
+  IN  UINTN           NumberDevicePathNode\r
+  )\r
+{\r
+  UINTN       Node;\r
+  CHAR16      *Str;\r
+\r
+  Str = (CHAR16*)StrDevicePath;\r
+  Node = 0;\r
+  while ((Str != NULL) && (*Str != L'\0') && (Node < NumberDevicePathNode)) {\r
+    if ((*Str == L'/') || (*Str == L'\\')) {\r
+        Node++;\r
+    }\r
+    Str++;\r
+  }\r
+\r
+  if (*Str == L'\0') {\r
+    return NULL;\r
+  } else {\r
+    return Str;\r
+  }\r
+}\r
+\r
+BOOLEAN\r
+BdsIsRemovableUsb (\r
+  IN  EFI_DEVICE_PATH*  DevicePath\r
+  )\r
+{\r
+  return ((DevicePathType (DevicePath) == MESSAGING_DEVICE_PATH) &&\r
+          ((DevicePathSubType (DevicePath) == MSG_USB_CLASS_DP) ||\r
+           (DevicePathSubType (DevicePath) == MSG_USB_WWID_DP)));\r
+}\r
+\r
+EFI_STATUS\r
+BdsGetDeviceUsb (\r
+  IN  EFI_DEVICE_PATH*  RemovableDevicePath,\r
+  OUT EFI_HANDLE*       DeviceHandle,\r
+  OUT EFI_DEVICE_PATH** NewDevicePath\r
+  )\r
+{\r
+  EFI_STATUS                    Status;\r
+  UINTN                         Index;\r
+  UINTN                         UsbIoHandleCount;\r
+  EFI_HANDLE                    *UsbIoBuffer;\r
+  EFI_DEVICE_PATH*              UsbIoDevicePath;\r
+  EFI_DEVICE_PATH*              TmpDevicePath;\r
+  USB_WWID_DEVICE_PATH*         WwidDevicePath1;\r
+  USB_WWID_DEVICE_PATH*         WwidDevicePath2;\r
+  USB_CLASS_DEVICE_PATH*        UsbClassDevicePath1;\r
+  USB_CLASS_DEVICE_PATH*        UsbClassDevicePath2;\r
+\r
+  // Get all the UsbIo handles\r
+  UsbIoHandleCount = 0;\r
+  Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiUsbIoProtocolGuid, NULL, &UsbIoHandleCount, &UsbIoBuffer);\r
+  if (EFI_ERROR (Status) || (UsbIoHandleCount == 0)) {\r
+    return Status;\r
+  }\r
+\r
+  // Check if one of the handles matches the USB description\r
+  for (Index = 0; Index < UsbIoHandleCount; Index++) {\r
+    Status = gBS->HandleProtocol (UsbIoBuffer[Index], &gEfiDevicePathProtocolGuid, (VOID **) &UsbIoDevicePath);\r
+    if (!EFI_ERROR (Status)) {\r
+      TmpDevicePath = UsbIoDevicePath;\r
+      while (!IsDevicePathEnd (TmpDevicePath)) {\r
+        // Check if the Device Path node is a USB Removable device Path node\r
+        if (BdsIsRemovableUsb (TmpDevicePath)) {\r
+          if (TmpDevicePath->SubType == MSG_USB_WWID_DP) {\r
+            WwidDevicePath1 = (USB_WWID_DEVICE_PATH*)RemovableDevicePath;\r
+            WwidDevicePath2 = (USB_WWID_DEVICE_PATH*)TmpDevicePath;\r
+            if ((WwidDevicePath1->VendorId == WwidDevicePath2->VendorId) &&\r
+                (WwidDevicePath1->ProductId == WwidDevicePath2->ProductId) &&\r
+                (CompareMem (WwidDevicePath1+1, WwidDevicePath2+1, DevicePathNodeLength(WwidDevicePath1)-sizeof (USB_WWID_DEVICE_PATH)) == 0))\r
+            {\r
+              *DeviceHandle = UsbIoBuffer[Index];\r
+              // Add the additional original Device Path Nodes (eg: FilePath Device Path Node) to the new Device Path\r
+              *NewDevicePath = AppendDevicePath (UsbIoDevicePath, NextDevicePathNode (RemovableDevicePath));\r
+              return EFI_SUCCESS;\r
+            }\r
+          } else {\r
+            UsbClassDevicePath1 = (USB_CLASS_DEVICE_PATH*)RemovableDevicePath;\r
+            UsbClassDevicePath2 = (USB_CLASS_DEVICE_PATH*)TmpDevicePath;\r
+            if ((UsbClassDevicePath1->VendorId != 0xFFFF) && (UsbClassDevicePath1->VendorId == UsbClassDevicePath2->VendorId) &&\r
+                (UsbClassDevicePath1->ProductId != 0xFFFF) && (UsbClassDevicePath1->ProductId == UsbClassDevicePath2->ProductId) &&\r
+                (UsbClassDevicePath1->DeviceClass != 0xFF) && (UsbClassDevicePath1->DeviceClass == UsbClassDevicePath2->DeviceClass) &&\r
+                (UsbClassDevicePath1->DeviceSubClass != 0xFF) && (UsbClassDevicePath1->DeviceSubClass == UsbClassDevicePath2->DeviceSubClass) &&\r
+                (UsbClassDevicePath1->DeviceProtocol != 0xFF) && (UsbClassDevicePath1->DeviceProtocol == UsbClassDevicePath2->DeviceProtocol))\r
+            {\r
+              *DeviceHandle = UsbIoBuffer[Index];\r
+              // Add the additional original Device Path Nodes (eg: FilePath Device Path Node) to the new Device Path\r
+              *NewDevicePath = AppendDevicePath (UsbIoDevicePath, NextDevicePathNode (RemovableDevicePath));\r
+              return EFI_SUCCESS;\r
+            }\r
+          }\r
+        }\r
+        TmpDevicePath = NextDevicePathNode (TmpDevicePath);\r
+      }\r
+\r
+    }\r
+  }\r
+\r
+  return EFI_NOT_FOUND;\r
+}\r
+\r
+BOOLEAN\r
+BdsIsRemovableHd (\r
+  IN  EFI_DEVICE_PATH*  DevicePath\r
+  )\r
+{\r
+  return IS_DEVICE_PATH_NODE (DevicePath, MEDIA_DEVICE_PATH, MEDIA_HARDDRIVE_DP);\r
+}\r
+\r
+EFI_STATUS\r
+BdsGetDeviceHd (\r
+  IN  EFI_DEVICE_PATH*  RemovableDevicePath,\r
+  OUT EFI_HANDLE*       DeviceHandle,\r
+  OUT EFI_DEVICE_PATH** NewDevicePath\r
+  )\r
+{\r
+  EFI_STATUS                    Status;\r
+  UINTN                         Index;\r
+  UINTN                         PartitionHandleCount;\r
+  EFI_HANDLE                    *PartitionBuffer;\r
+  EFI_DEVICE_PATH*              PartitionDevicePath;\r
+  EFI_DEVICE_PATH*              TmpDevicePath;\r
+  HARDDRIVE_DEVICE_PATH*        HardDriveDevicePath1;\r
+  HARDDRIVE_DEVICE_PATH*        HardDriveDevicePath2;\r
+\r
+  // Get all the DiskIo handles\r
+  PartitionHandleCount = 0;\r
+  Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiDiskIoProtocolGuid, NULL, &PartitionHandleCount, &PartitionBuffer);\r
+  if (EFI_ERROR (Status) || (PartitionHandleCount == 0)) {\r
+    return Status;\r
+  }\r
+\r
+  // Check if one of the handles matches the Hard Disk Description\r
+  for (Index = 0; Index < PartitionHandleCount; Index++) {\r
+    Status = gBS->HandleProtocol (PartitionBuffer[Index], &gEfiDevicePathProtocolGuid, (VOID **) &PartitionDevicePath);\r
+    if (!EFI_ERROR (Status)) {\r
+      TmpDevicePath = PartitionDevicePath;\r
+      while (!IsDevicePathEnd (TmpDevicePath)) {\r
+        // Check if the Device Path node is a HD Removable device Path node\r
+        if (BdsIsRemovableHd (TmpDevicePath)) {\r
+          HardDriveDevicePath1 = (HARDDRIVE_DEVICE_PATH*)RemovableDevicePath;\r
+          HardDriveDevicePath2 = (HARDDRIVE_DEVICE_PATH*)TmpDevicePath;\r
+          if ((HardDriveDevicePath1->SignatureType == HardDriveDevicePath2->SignatureType) &&\r
+              (CompareGuid ((EFI_GUID *)HardDriveDevicePath1->Signature, (EFI_GUID *)HardDriveDevicePath2->Signature) == TRUE) &&\r
+              (HardDriveDevicePath1->PartitionNumber == HardDriveDevicePath2->PartitionNumber))\r
+          {\r
+            *DeviceHandle = PartitionBuffer[Index];\r
+            // Add the additional original Device Path Nodes (eg: FilePath Device Path Node) to the new Device Path\r
+            *NewDevicePath = AppendDevicePath (PartitionDevicePath, NextDevicePathNode (RemovableDevicePath));\r
+            return EFI_SUCCESS;\r
+          }\r
+        }\r
+        TmpDevicePath = NextDevicePathNode (TmpDevicePath);\r
+      }\r
+\r
+    }\r
+  }\r
+\r
+  return EFI_NOT_FOUND;\r
+}\r
+\r
+/*BOOLEAN\r
+BdsIsRemovableCdrom (\r
+  IN  EFI_DEVICE_PATH*  DevicePath\r
+  )\r
+{\r
+  return IS_DEVICE_PATH_NODE (DevicePath, MEDIA_DEVICE_PATH, MEDIA_CDROM_DP);\r
+}\r
+\r
+EFI_STATUS\r
+BdsGetDeviceCdrom (\r
+  IN  EFI_DEVICE_PATH*  RemovableDevicePath,\r
+  OUT EFI_HANDLE*       DeviceHandle,\r
+  OUT EFI_DEVICE_PATH** DevicePath\r
+  )\r
+{\r
+  ASSERT(0);\r
+  return EFI_UNSUPPORTED;\r
+}*/\r
+\r
+typedef BOOLEAN\r
+(*BDS_IS_REMOVABLE) (\r
+  IN  EFI_DEVICE_PATH*  DevicePath\r
+  );\r
+\r
+typedef EFI_STATUS\r
+(*BDS_GET_DEVICE) (\r
+  IN  EFI_DEVICE_PATH*  RemovableDevicePath,\r
+  OUT EFI_HANDLE*       DeviceHandle,\r
+  OUT EFI_DEVICE_PATH** DevicePath\r
+  );\r
+\r
+typedef struct {\r
+  BDS_IS_REMOVABLE    IsRemovable;\r
+  BDS_GET_DEVICE      GetDevice;\r
+} BDS_REMOVABLE_DEVICE_SUPPORT;\r
+\r
+BDS_REMOVABLE_DEVICE_SUPPORT  RemovableDeviceSupport[] = {\r
+  { BdsIsRemovableUsb, BdsGetDeviceUsb },\r
+  { BdsIsRemovableHd, BdsGetDeviceHd },\r
+  //{ BdsIsRemovableCdrom, BdsGetDeviceCdrom }\r
+};\r
+\r
+STATIC\r
+BOOLEAN\r
+IsRemovableDevice (\r
+  IN  EFI_DEVICE_PATH*  DevicePath\r
+  )\r
+{\r
+  UINTN             Index;\r
+  EFI_DEVICE_PATH*  TmpDevicePath;\r
+\r
+  TmpDevicePath = DevicePath;\r
+  while (!IsDevicePathEnd (TmpDevicePath)) {\r
+    for (Index = 0; Index < sizeof (RemovableDeviceSupport) / sizeof (BDS_REMOVABLE_DEVICE_SUPPORT); Index++) {\r
+      if (RemovableDeviceSupport[Index].IsRemovable (TmpDevicePath)) {\r
+        return TRUE;\r
+      }\r
+    }\r
+    TmpDevicePath = NextDevicePathNode (TmpDevicePath);\r
+  }\r
+\r
+  return FALSE;\r
+}\r
+\r
+STATIC\r
+EFI_STATUS\r
+TryRemovableDevice (\r
+  IN  EFI_DEVICE_PATH*  DevicePath,\r
+  OUT EFI_HANDLE*       DeviceHandle,\r
+  OUT EFI_DEVICE_PATH** NewDevicePath\r
+  )\r
+{\r
+  EFI_STATUS        Status;\r
+  UINTN             Index;\r
+  EFI_DEVICE_PATH*  TmpDevicePath;\r
+  BDS_REMOVABLE_DEVICE_SUPPORT* RemovableDevice;\r
+  EFI_DEVICE_PATH* RemovableDevicePath;\r
+  BOOLEAN         RemovableFound;\r
+\r
+  RemovableDevice     = NULL;\r
+  RemovableDevicePath = NULL;\r
+  RemovableFound      = FALSE;\r
+  TmpDevicePath       = DevicePath;\r
+\r
+  while (!IsDevicePathEnd (TmpDevicePath) && !RemovableFound) {\r
+    for (Index = 0; Index < sizeof (RemovableDeviceSupport) / sizeof (BDS_REMOVABLE_DEVICE_SUPPORT); Index++) {\r
+      RemovableDevice = &RemovableDeviceSupport[Index];\r
+      if (RemovableDevice->IsRemovable (TmpDevicePath)) {\r
+        RemovableDevicePath = TmpDevicePath;\r
+        RemovableFound = TRUE;\r
+        break;\r
+      }\r
+    }\r
+    TmpDevicePath = NextDevicePathNode (TmpDevicePath);\r
+  }\r
+\r
+  if (!RemovableFound) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  // Search into the current started drivers\r
+  Status = RemovableDevice->GetDevice (RemovableDevicePath, DeviceHandle, NewDevicePath);\r
+  if (Status == EFI_NOT_FOUND) {\r
+    // Connect all the drivers\r
+    BdsConnectAllDrivers ();\r
+\r
+    // Search again into all the drivers\r
+    Status = RemovableDevice->GetDevice (RemovableDevicePath, DeviceHandle, NewDevicePath);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+STATIC\r
+EFI_STATUS\r
+BdsConnectAndUpdateDevicePath (\r
+  IN OUT EFI_DEVICE_PATH_PROTOCOL  **DevicePath,\r
+  OUT    EFI_HANDLE                *Handle,\r
+  OUT    EFI_DEVICE_PATH_PROTOCOL  **RemainingDevicePath\r
+  )\r
+{\r
+  EFI_DEVICE_PATH*            Remaining;\r
+  EFI_DEVICE_PATH*            NewDevicePath;\r
+  EFI_STATUS                  Status;\r
+  EFI_HANDLE                  PreviousHandle;\r
+\r
+  if ((DevicePath == NULL) || (*DevicePath == NULL) || (Handle == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  PreviousHandle = NULL;\r
+  do {\r
+    Remaining = *DevicePath;\r
+\r
+    // The LocateDevicePath() function locates all devices on DevicePath that support Protocol and returns\r
+    // the handle to the device that is closest to DevicePath. On output, the device path pointer is modified\r
+    // to point to the remaining part of the device path\r
+    Status = gBS->LocateDevicePath (&gEfiDevicePathProtocolGuid, &Remaining, Handle);\r
+\r
+    if (!EFI_ERROR (Status)) {\r
+      if (*Handle == PreviousHandle) {\r
+        //\r
+        // If no forward progress is made try invoking the Dispatcher.\r
+        // A new FV may have been added to the system and new drivers\r
+        // may now be found.\r
+        // Status == EFI_SUCCESS means a driver was dispatched\r
+        // Status == EFI_NOT_FOUND means no new drivers were dispatched\r
+        //\r
+        Status = gDS->Dispatch ();\r
+      }\r
+\r
+      if (!EFI_ERROR (Status)) {\r
+        PreviousHandle = *Handle;\r
+\r
+        // Recursive = FALSE: We do not want to start the whole device tree\r
+        Status = gBS->ConnectController (*Handle, NULL, Remaining, FALSE);\r
+      }\r
+    }\r
+  } while (!EFI_ERROR (Status) && !IsDevicePathEnd (Remaining));\r
+\r
+  if (!EFI_ERROR (Status)) {\r
+    // Now, we have got the whole Device Path connected, call again ConnectController to ensure all the supported Driver\r
+    // Binding Protocol are connected (such as DiskIo and SimpleFileSystem)\r
+    Remaining = *DevicePath;\r
+    Status = gBS->LocateDevicePath (&gEfiDevicePathProtocolGuid, &Remaining, Handle);\r
+    if (!EFI_ERROR (Status)) {\r
+      Status = gBS->ConnectController (*Handle, NULL, Remaining, FALSE);\r
+      if (EFI_ERROR (Status)) {\r
+        // If the last node is a Memory Map Device Path just return EFI_SUCCESS.\r
+        if ((Remaining->Type == HARDWARE_DEVICE_PATH) && (Remaining->SubType == HW_MEMMAP_DP)) {\r
+            Status = EFI_SUCCESS;\r
+        }\r
+      }\r
+    }\r
+  } else if (!IsDevicePathEnd (Remaining) && !IsRemovableDevice (Remaining)) {\r
+\r
+    /*// If the remaining Device Path is a FilePath or MemoryMap then we consider the Device Path has been loaded correctly\r
+    if ((Remaining->Type == MEDIA_DEVICE_PATH) && (Remaining->SubType == MEDIA_FILEPATH_DP)) {\r
+      Status = EFI_SUCCESS;\r
+    } else if ((Remaining->Type == HARDWARE_DEVICE_PATH) && (Remaining->SubType == HW_MEMMAP_DP)) {\r
+      Status = EFI_SUCCESS;\r
+    }*/\r
+\r
+    //TODO: Should we just return success and leave the caller decide if it is the expected RemainingPath\r
+    Status = EFI_SUCCESS;\r
+  } else {\r
+    Status = TryRemovableDevice (*DevicePath, Handle, &NewDevicePath);\r
+    if (!EFI_ERROR (Status)) {\r
+      Status = BdsConnectAndUpdateDevicePath (&NewDevicePath, Handle, RemainingDevicePath);\r
+      *DevicePath = NewDevicePath;\r
+      return Status;\r
+    }\r
+  }\r
+\r
+  if (RemainingDevicePath) {\r
+    *RemainingDevicePath = Remaining;\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Connect a Device Path and return the handle of the driver that support this DevicePath\r
+\r
+  @param  DevicePath            Device Path of the File to connect\r
+  @param  Handle                Handle of the driver that support this DevicePath\r
+  @param  RemainingDevicePath   Remaining DevicePath nodes that do not match the driver DevicePath\r
+\r
+  @retval EFI_SUCCESS           A driver that matches the Device Path has been found\r
+  @retval EFI_NOT_FOUND         No handles match the search.\r
+  @retval EFI_INVALID_PARAMETER DevicePath or Handle is NULL\r
+\r
+**/\r
+EFI_STATUS\r
+BdsConnectDevicePath (\r
+  IN  EFI_DEVICE_PATH_PROTOCOL* DevicePath,\r
+  OUT EFI_HANDLE                *Handle,\r
+  OUT EFI_DEVICE_PATH_PROTOCOL  **RemainingDevicePath\r
+  )\r
+{\r
+  return BdsConnectAndUpdateDevicePath (&DevicePath, Handle, RemainingDevicePath);\r
+}\r
+\r
+BOOLEAN\r
+BdsFileSystemSupport (\r
+  IN EFI_DEVICE_PATH *DevicePath,\r
+  IN EFI_HANDLE Handle,\r
+  IN EFI_DEVICE_PATH *RemainingDevicePath\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+  EFI_SIMPLE_FILE_SYSTEM_PROTOCOL     *FsProtocol;\r
+\r
+  Status = gBS->HandleProtocol (Handle, &gEfiSimpleFileSystemProtocolGuid, (VOID **)&FsProtocol);\r
+\r
+  return (!EFI_ERROR (Status) && IS_DEVICE_PATH_NODE (RemainingDevicePath, MEDIA_DEVICE_PATH, MEDIA_FILEPATH_DP));\r
+}\r
+\r
+EFI_STATUS\r
+BdsFileSystemLoadImage (\r
+  IN OUT EFI_DEVICE_PATH       **DevicePath,\r
+  IN     EFI_HANDLE            Handle,\r
+  IN     EFI_DEVICE_PATH       *RemainingDevicePath,\r
+  IN     EFI_ALLOCATE_TYPE     Type,\r
+  IN OUT EFI_PHYSICAL_ADDRESS  *Image,\r
+  OUT    UINTN                 *ImageSize\r
+  )\r
+{\r
+  EFI_STATUS                       Status;\r
+  FILEPATH_DEVICE_PATH             *FilePathDevicePath;\r
+  EFI_SIMPLE_FILE_SYSTEM_PROTOCOL  *FsProtocol;\r
+  EFI_FILE_PROTOCOL                *Fs;\r
+  EFI_FILE_INFO                    *FileInfo;\r
+  EFI_FILE_PROTOCOL                *File;\r
+  UINTN                            Size;\r
+\r
+  ASSERT (IS_DEVICE_PATH_NODE (RemainingDevicePath, MEDIA_DEVICE_PATH, MEDIA_FILEPATH_DP));\r
+\r
+  FilePathDevicePath = (FILEPATH_DEVICE_PATH*)RemainingDevicePath;\r
+\r
+  Status = gBS->OpenProtocol (\r
+                  Handle,\r
+                  &gEfiSimpleFileSystemProtocolGuid,\r
+                  (VOID**)&FsProtocol,\r
+                  gImageHandle,\r
+                  Handle,\r
+                  EFI_OPEN_PROTOCOL_BY_DRIVER\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  // Try to Open the volume and get root directory\r
+  Status = FsProtocol->OpenVolume (FsProtocol, &Fs);\r
+  if (EFI_ERROR (Status)) {\r
+    goto CLOSE_PROTOCOL;\r
+  }\r
+\r
+  Status = Fs->Open (Fs, &File, FilePathDevicePath->PathName, EFI_FILE_MODE_READ, 0);\r
+  if (EFI_ERROR (Status)) {\r
+    goto CLOSE_PROTOCOL;\r
+  }\r
+\r
+  Size = 0;\r
+  File->GetInfo (File, &gEfiFileInfoGuid, &Size, NULL);\r
+  FileInfo = AllocatePool (Size);\r
+  Status = File->GetInfo (File, &gEfiFileInfoGuid, &Size, FileInfo);\r
+  if (EFI_ERROR (Status)) {\r
+    goto CLOSE_FILE;\r
+  }\r
+\r
+  // Get the file size\r
+  Size = FileInfo->FileSize;\r
+  if (ImageSize) {\r
+    *ImageSize = Size;\r
+  }\r
+  FreePool (FileInfo);\r
+\r
+  Status = gBS->AllocatePages (Type, EfiBootServicesCode, EFI_SIZE_TO_PAGES(Size), Image);\r
+  // Try to allocate in any pages if failed to allocate memory at the defined location\r
+  if ((Status == EFI_OUT_OF_RESOURCES) && (Type != AllocateAnyPages)) {\r
+    Status = gBS->AllocatePages (AllocateAnyPages, EfiBootServicesCode, EFI_SIZE_TO_PAGES(Size), Image);\r
+  }\r
+  if (!EFI_ERROR (Status)) {\r
+    Status = File->Read (File, &Size, (VOID*)(UINTN)(*Image));\r
+  }\r
+\r
+CLOSE_FILE:\r
+  File->Close (File);\r
+\r
+CLOSE_PROTOCOL:\r
+  gBS->CloseProtocol (\r
+         Handle,\r
+         &gEfiSimpleFileSystemProtocolGuid,\r
+         gImageHandle,\r
+         Handle);\r
+\r
+  return Status;\r
+}\r
+\r
+BOOLEAN\r
+BdsMemoryMapSupport (\r
+  IN EFI_DEVICE_PATH *DevicePath,\r
+  IN EFI_HANDLE Handle,\r
+  IN EFI_DEVICE_PATH *RemainingDevicePath\r
+  )\r
+{\r
+  return IS_DEVICE_PATH_NODE (DevicePath, HARDWARE_DEVICE_PATH, HW_MEMMAP_DP) ||\r
+         IS_DEVICE_PATH_NODE (RemainingDevicePath, HARDWARE_DEVICE_PATH, HW_MEMMAP_DP);\r
+}\r
+\r
+EFI_STATUS\r
+BdsMemoryMapLoadImage (\r
+  IN OUT EFI_DEVICE_PATH       **DevicePath,\r
+  IN     EFI_HANDLE            Handle,\r
+  IN     EFI_DEVICE_PATH       *RemainingDevicePath,\r
+  IN     EFI_ALLOCATE_TYPE     Type,\r
+  IN OUT EFI_PHYSICAL_ADDRESS* Image,\r
+  OUT    UINTN                 *ImageSize\r
+  )\r
+{\r
+  EFI_STATUS            Status;\r
+  MEMMAP_DEVICE_PATH*   MemMapPathDevicePath;\r
+  UINTN                 Size;\r
+\r
+  if (IS_DEVICE_PATH_NODE (RemainingDevicePath, HARDWARE_DEVICE_PATH, HW_MEMMAP_DP)) {\r
+    MemMapPathDevicePath = (MEMMAP_DEVICE_PATH*)RemainingDevicePath;\r
+  } else {\r
+    ASSERT (IS_DEVICE_PATH_NODE (*DevicePath, HARDWARE_DEVICE_PATH, HW_MEMMAP_DP));\r
+    MemMapPathDevicePath = (MEMMAP_DEVICE_PATH*)*DevicePath;\r
+  }\r
+\r
+  Size = MemMapPathDevicePath->EndingAddress - MemMapPathDevicePath->StartingAddress;\r
+  if (Size == 0) {\r
+      return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Status = gBS->AllocatePages (Type, EfiBootServicesCode, EFI_SIZE_TO_PAGES(Size), Image);\r
+  // Try to allocate in any pages if failed to allocate memory at the defined location\r
+  if ((Status == EFI_OUT_OF_RESOURCES) && (Type != AllocateAnyPages)) {\r
+    Status = gBS->AllocatePages (AllocateAnyPages, EfiBootServicesCode, EFI_SIZE_TO_PAGES(Size), Image);\r
+  }\r
+  if (!EFI_ERROR (Status)) {\r
+    CopyMem ((VOID*)(UINTN)(*Image), (CONST VOID*)(UINTN)MemMapPathDevicePath->StartingAddress, Size);\r
+\r
+    if (ImageSize != NULL) {\r
+        *ImageSize = Size;\r
+    }\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+BOOLEAN\r
+BdsFirmwareVolumeSupport (\r
+  IN EFI_DEVICE_PATH *DevicePath,\r
+  IN EFI_HANDLE Handle,\r
+  IN EFI_DEVICE_PATH *RemainingDevicePath\r
+  )\r
+{\r
+  return IS_DEVICE_PATH_NODE (RemainingDevicePath, MEDIA_DEVICE_PATH, MEDIA_PIWG_FW_FILE_DP);\r
+}\r
+\r
+EFI_STATUS\r
+BdsFirmwareVolumeLoadImage (\r
+  IN OUT EFI_DEVICE_PATH       **DevicePath,\r
+  IN     EFI_HANDLE            Handle,\r
+  IN     EFI_DEVICE_PATH       *RemainingDevicePath,\r
+  IN     EFI_ALLOCATE_TYPE     Type,\r
+  IN OUT EFI_PHYSICAL_ADDRESS* Image,\r
+  OUT    UINTN                 *ImageSize\r
+  )\r
+{\r
+  EFI_STATUS            Status;\r
+  EFI_FIRMWARE_VOLUME2_PROTOCOL     *FwVol;\r
+  EFI_GUID                          *FvNameGuid;\r
+  EFI_SECTION_TYPE                  SectionType;\r
+  EFI_FV_FILETYPE                   FvType;\r
+  EFI_FV_FILE_ATTRIBUTES            Attrib;\r
+  UINT32                            AuthenticationStatus;\r
+  VOID* ImageBuffer;\r
+\r
+  ASSERT (IS_DEVICE_PATH_NODE (RemainingDevicePath, MEDIA_DEVICE_PATH, MEDIA_PIWG_FW_FILE_DP));\r
+\r
+  Status = gBS->HandleProtocol (Handle, &gEfiFirmwareVolume2ProtocolGuid, (VOID **)&FwVol);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  FvNameGuid = EfiGetNameGuidFromFwVolDevicePathNode ((CONST MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *)RemainingDevicePath);\r
+  if (FvNameGuid == NULL) {\r
+    Status = EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  SectionType = EFI_SECTION_PE32;\r
+  AuthenticationStatus = 0;\r
+  //Note: ReadSection at the opposite of ReadFile does not allow to pass ImageBuffer == NULL to get the size of the file.\r
+  ImageBuffer = NULL;\r
+  Status = FwVol->ReadSection (\r
+                    FwVol,\r
+                    FvNameGuid,\r
+                    SectionType,\r
+                    0,\r
+                    &ImageBuffer,\r
+                    ImageSize,\r
+                    &AuthenticationStatus\r
+                    );\r
+  if (!EFI_ERROR (Status)) {\r
+#if 0\r
+    // In case the buffer has some address requirements, we must copy the buffer to a buffer following the requirements\r
+    if (Type != AllocateAnyPages) {\r
+      Status = gBS->AllocatePages (Type, EfiBootServicesCode, EFI_SIZE_TO_PAGES(*ImageSize),Image);\r
+      if (!EFI_ERROR (Status)) {\r
+        CopyMem ((VOID*)(UINTN)(*Image), ImageBuffer, *ImageSize);\r
+        FreePool (ImageBuffer);\r
+      }\r
+    }\r
+#else\r
+    // We must copy the buffer into a page allocations. Otherwise, the caller could call gBS->FreePages() on the pool allocation\r
+    Status = gBS->AllocatePages (Type, EfiBootServicesCode, EFI_SIZE_TO_PAGES(*ImageSize), Image);\r
+    // Try to allocate in any pages if failed to allocate memory at the defined location\r
+    if ((Status == EFI_OUT_OF_RESOURCES) && (Type != AllocateAnyPages)) {\r
+      Status = gBS->AllocatePages (AllocateAnyPages, EfiBootServicesCode, EFI_SIZE_TO_PAGES(*ImageSize), Image);\r
+    }\r
+    if (!EFI_ERROR (Status)) {\r
+      CopyMem ((VOID*)(UINTN)(*Image), ImageBuffer, *ImageSize);\r
+      FreePool (ImageBuffer);\r
+    }\r
+#endif\r
+  } else {\r
+    // Try a raw file, since a PE32 SECTION does not exist\r
+    Status = FwVol->ReadFile (\r
+                        FwVol,\r
+                        FvNameGuid,\r
+                        NULL,\r
+                        ImageSize,\r
+                        &FvType,\r
+                        &Attrib,\r
+                        &AuthenticationStatus\r
+                        );\r
+    if (!EFI_ERROR (Status)) {\r
+      Status = gBS->AllocatePages (Type, EfiBootServicesCode, EFI_SIZE_TO_PAGES(*ImageSize), Image);\r
+      // Try to allocate in any pages if failed to allocate memory at the defined location\r
+      if ((Status == EFI_OUT_OF_RESOURCES) && (Type != AllocateAnyPages)) {\r
+        Status = gBS->AllocatePages (AllocateAnyPages, EfiBootServicesCode, EFI_SIZE_TO_PAGES(*ImageSize), Image);\r
+      }\r
+      if (!EFI_ERROR (Status)) {\r
+        Status = FwVol->ReadFile (\r
+                                FwVol,\r
+                                FvNameGuid,\r
+                                (VOID**)Image,\r
+                                ImageSize,\r
+                                &FvType,\r
+                                &Attrib,\r
+                                &AuthenticationStatus\r
+                                );\r
+      }\r
+    }\r
+  }\r
+  return Status;\r
+}\r
+\r
+BOOLEAN\r
+BdsPxeSupport (\r
+  IN EFI_DEVICE_PATH*           DevicePath,\r
+  IN EFI_HANDLE                 Handle,\r
+  IN EFI_DEVICE_PATH*           RemainingDevicePath\r
+  )\r
+{\r
+  EFI_STATUS                  Status;\r
+  EFI_PXE_BASE_CODE_PROTOCOL* PxeBcProtocol;\r
+\r
+  if (!IsDevicePathEnd (RemainingDevicePath)) {\r
+    return FALSE;\r
+  }\r
+\r
+  Status = gBS->HandleProtocol (Handle, &gEfiPxeBaseCodeProtocolGuid, (VOID **)&PxeBcProtocol);\r
+  if (EFI_ERROR (Status)) {\r
+    return FALSE;\r
+  } else {\r
+    return TRUE;\r
+  }\r
+}\r
+\r
+EFI_STATUS\r
+BdsPxeLoadImage (\r
+  IN OUT EFI_DEVICE_PATH       **DevicePath,\r
+  IN     EFI_HANDLE            Handle,\r
+  IN     EFI_DEVICE_PATH       *RemainingDevicePath,\r
+  IN     EFI_ALLOCATE_TYPE     Type,\r
+  IN OUT EFI_PHYSICAL_ADDRESS* Image,\r
+  OUT    UINTN                 *ImageSize\r
+  )\r
+{\r
+  EFI_STATUS              Status;\r
+  EFI_LOAD_FILE_PROTOCOL  *LoadFileProtocol;\r
+  UINTN                   BufferSize;\r
+  EFI_PXE_BASE_CODE_PROTOCOL *Pxe;\r
+\r
+  // Get Load File Protocol attached to the PXE protocol\r
+  Status = gBS->HandleProtocol (Handle, &gEfiLoadFileProtocolGuid, (VOID **)&LoadFileProtocol);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  Status = LoadFileProtocol->LoadFile (LoadFileProtocol, RemainingDevicePath, TRUE, &BufferSize, NULL);\r
+  if (Status == EFI_BUFFER_TOO_SMALL) {\r
+    Status = gBS->AllocatePages (Type, EfiBootServicesCode, EFI_SIZE_TO_PAGES(BufferSize), Image);\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+\r
+    Status = LoadFileProtocol->LoadFile (LoadFileProtocol, RemainingDevicePath, TRUE, &BufferSize, (VOID*)(UINTN)(*Image));\r
+    if (!EFI_ERROR (Status) && (ImageSize != NULL)) {\r
+      *ImageSize = BufferSize;\r
+    }\r
+  }\r
+\r
+  if (Status == EFI_ALREADY_STARTED) {\r
+    Status = gBS->LocateProtocol (&gEfiPxeBaseCodeProtocolGuid, NULL, (VOID **)&Pxe);\r
+    if (!EFI_ERROR(Status)) {\r
+      // If PXE is already started, we stop it\r
+      Pxe->Stop (Pxe);\r
+      // And we try again\r
+      return BdsPxeLoadImage (DevicePath, Handle, RemainingDevicePath, Type, Image, ImageSize);\r
+    }\r
+  }\r
+  return Status;\r
+}\r
+\r
+BOOLEAN\r
+BdsTftpSupport (\r
+  IN EFI_DEVICE_PATH  *DevicePath,\r
+  IN EFI_HANDLE       Handle,\r
+  IN EFI_DEVICE_PATH  *RemainingDevicePath\r
+  )\r
+{\r
+  EFI_STATUS       Status;\r
+  EFI_DEVICE_PATH  *NextDevicePath;\r
+  VOID             *Interface;\r
+\r
+  // Validate the Remaining Device Path\r
+  if (IsDevicePathEnd (RemainingDevicePath)) {\r
+    return FALSE;\r
+  }\r
+  if (!IS_DEVICE_PATH_NODE (RemainingDevicePath, MESSAGING_DEVICE_PATH, MSG_IPv4_DP) &&\r
+      !IS_DEVICE_PATH_NODE (RemainingDevicePath, MESSAGING_DEVICE_PATH, MSG_IPv6_DP)) {\r
+    return FALSE;\r
+  }\r
+  NextDevicePath = NextDevicePathNode (RemainingDevicePath);\r
+  if (IsDevicePathEnd (NextDevicePath)) {\r
+    return FALSE;\r
+  }\r
+  if (!IS_DEVICE_PATH_NODE (NextDevicePath, MEDIA_DEVICE_PATH, MEDIA_FILEPATH_DP)) {\r
+    return FALSE;\r
+  }\r
+\r
+  Status = gBS->HandleProtocol (\r
+                  Handle, &gEfiDevicePathProtocolGuid,\r
+                  &Interface\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    return FALSE;\r
+  }\r
+\r
+  //\r
+  // Check that the controller (identified by its handle "Handle") supports the\r
+  // MTFTPv4 Service Binding Protocol. If it does, it means that it supports the\r
+  // EFI MTFTPv4 Protocol needed to download the image through TFTP.\r
+  //\r
+  Status = gBS->HandleProtocol (\r
+                  Handle, &gEfiMtftp4ServiceBindingProtocolGuid,\r
+                  &Interface\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    return FALSE;\r
+  }\r
+\r
+  return TRUE;\r
+}\r
+\r
+/**\r
+  Worker function that get the size in numbers of bytes of a file from a TFTP\r
+  server before to download the file.\r
+\r
+  @param[in]   Mtftp4    MTFTP4 protocol interface\r
+  @param[in]   FilePath  Path of the file, Ascii encoded\r
+  @param[out]  FileSize  Address where to store the file size in number of\r
+                         bytes.\r
+\r
+  @retval  EFI_SUCCESS   The size of the file was returned.\r
+  @retval  !EFI_SUCCESS  The size of the file was not returned.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+Mtftp4GetFileSize (\r
+  IN  EFI_MTFTP4_PROTOCOL  *Mtftp4,\r
+  IN  CHAR8                *FilePath,\r
+  OUT UINT64               *FileSize\r
+  )\r
+{\r
+  EFI_STATUS         Status;\r
+  EFI_MTFTP4_OPTION  ReqOpt[1];\r
+  EFI_MTFTP4_PACKET  *Packet;\r
+  UINT32             PktLen;\r
+  EFI_MTFTP4_OPTION  *TableOfOptions;\r
+  EFI_MTFTP4_OPTION  *Option;\r
+  UINT32             OptCnt;\r
+  UINT8              OptBuf[128];\r
+\r
+  ReqOpt[0].OptionStr = (UINT8*)"tsize";\r
+  OptBuf[0] = '0';\r
+  OptBuf[1] = 0;\r
+  ReqOpt[0].ValueStr = OptBuf;\r
+\r
+  Status = Mtftp4->GetInfo (\r
+             Mtftp4,\r
+             NULL,\r
+             (UINT8*)FilePath,\r
+             NULL,\r
+             1,\r
+             ReqOpt,\r
+             &PktLen,\r
+             &Packet\r
+             );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto Error;\r
+  }\r
+\r
+  Status = Mtftp4->ParseOptions (\r
+                     Mtftp4,\r
+                     PktLen,\r
+                     Packet,\r
+                     (UINT32 *) &OptCnt,\r
+                     &TableOfOptions\r
+                     );\r
+  if (EFI_ERROR (Status)) {\r
+    goto Error;\r
+  }\r
+\r
+  Option = TableOfOptions;\r
+  while (OptCnt != 0) {\r
+    if (AsciiStrnCmp ((CHAR8 *)Option->OptionStr, "tsize", 5) == 0) {\r
+      *FileSize = AsciiStrDecimalToUint64 ((CHAR8 *)Option->ValueStr);\r
+      break;\r
+    }\r
+    OptCnt--;\r
+    Option++;\r
+  }\r
+  FreePool (TableOfOptions);\r
+\r
+  if (OptCnt == 0) {\r
+    Status = EFI_UNSUPPORTED;\r
+  }\r
+\r
+Error :\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Update the progress of a file download\r
+  This procedure is called each time a new TFTP packet is received.\r
+\r
+  @param[in]  This       MTFTP4 protocol interface\r
+  @param[in]  Token      Parameters for the download of the file\r
+  @param[in]  PacketLen  Length of the packet\r
+  @param[in]  Packet     Address of the packet\r
+\r
+  @retval  EFI_SUCCESS  All packets are accepted.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+Mtftp4CheckPacket (\r
+  IN EFI_MTFTP4_PROTOCOL  *This,\r
+  IN EFI_MTFTP4_TOKEN     *Token,\r
+  IN UINT16               PacketLen,\r
+  IN EFI_MTFTP4_PACKET    *Packet\r
+  )\r
+{\r
+  BDS_TFTP_CONTEXT  *Context;\r
+  CHAR16            Progress[TFTP_PROGRESS_MESSAGE_SIZE];\r
+  UINT64            NbOfKb;\r
+  UINTN             Index;\r
+  UINTN             LastStep;\r
+  UINTN             Step;\r
+  UINT64            LastNbOf50Kb;\r
+  UINT64            NbOf50Kb;\r
+\r
+  if ((NTOHS (Packet->OpCode)) == EFI_MTFTP4_OPCODE_DATA) {\r
+    Context = (BDS_TFTP_CONTEXT*)Token->Context;\r
+\r
+    if (Context->DownloadedNbOfBytes == 0) {\r
+      if (Context->FileSize > 0) {\r
+        Print (L"%s       0 Kb", mTftpProgressFrame);\r
+      } else {\r
+        Print (L"    0 Kb");\r
+      }\r
+    }\r
+\r
+    //\r
+    // The data is the packet are prepended with two UINT16 :\r
+    // . OpCode = EFI_MTFTP4_OPCODE_DATA\r
+    // . Block  = the number of this block of data\r
+    //\r
+    Context->DownloadedNbOfBytes += PacketLen - sizeof (Packet->OpCode) - sizeof (Packet->Data.Block);\r
+    NbOfKb = Context->DownloadedNbOfBytes / 1024;\r
+\r
+    Progress[0] = L'\0';\r
+    if (Context->FileSize > 0) {\r
+      LastStep  = (Context->LastReportedNbOfBytes * TFTP_PROGRESS_SLIDER_STEPS) / Context->FileSize;\r
+      Step      = (Context->DownloadedNbOfBytes   * TFTP_PROGRESS_SLIDER_STEPS) / Context->FileSize;\r
+      if (Step > LastStep) {\r
+        Print (mTftpProgressDelete);\r
+        CopyMem (Progress, mTftpProgressFrame, sizeof mTftpProgressFrame);\r
+        for (Index = 1; Index < Step; Index++) {\r
+          Progress[Index] = L'=';\r
+        }\r
+        Progress[Step] = L'>';\r
+\r
+        UnicodeSPrint (\r
+          Progress + (sizeof (mTftpProgressFrame) / sizeof (CHAR16)) - 1,\r
+          sizeof (Progress) - sizeof (mTftpProgressFrame),\r
+          L" %7d Kb",\r
+          NbOfKb\r
+          );\r
+        Context->LastReportedNbOfBytes = Context->DownloadedNbOfBytes;\r
+      }\r
+    } else {\r
+      //\r
+      // Case when we do not know the size of the final file.\r
+      // We print the updated size every 50KB of downloaded data\r
+      //\r
+      LastNbOf50Kb = Context->LastReportedNbOfBytes / (50*1024);\r
+      NbOf50Kb     = Context->DownloadedNbOfBytes   / (50*1024);\r
+      if (NbOf50Kb > LastNbOf50Kb) {\r
+        Print (L"\b\b\b\b\b\b\b\b\b\b");\r
+        UnicodeSPrint (Progress, sizeof (Progress), L"%7d Kb", NbOfKb);\r
+        Context->LastReportedNbOfBytes = Context->DownloadedNbOfBytes;\r
+      }\r
+    }\r
+    if (Progress[0] != L'\0') {\r
+      Print (L"%s", Progress);\r
+    }\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Download an image from a TFTP server\r
+\r
+  @param[in]   DevicePath           Device path of the TFTP boot option\r
+  @param[in]   ControllerHandle     Handle of the network controller\r
+  @param[in]   RemainingDevicePath  Device path of the TFTP boot option but\r
+                                    the first node that identifies the network controller\r
+  @param[in]   Type                 Type to allocate memory pages\r
+  @param[out]  Image                Address of the bufer where the image is stored in\r
+                                    case of success\r
+  @param[out]  ImageSize            Size in number of bytes of the i;age in case of\r
+                                    success\r
+\r
+  @retval  EFI_SUCCESS   The image was returned.\r
+  @retval  !EFI_SUCCESS  Something went wrong.\r
+\r
+**/\r
+EFI_STATUS\r
+BdsTftpLoadImage (\r
+  IN OUT EFI_DEVICE_PATH       **DevicePath,\r
+  IN     EFI_HANDLE            ControllerHandle,\r
+  IN     EFI_DEVICE_PATH       *RemainingDevicePath,\r
+  IN     EFI_ALLOCATE_TYPE     Type,\r
+  IN OUT EFI_PHYSICAL_ADDRESS  *Image,\r
+  OUT    UINTN                 *ImageSize\r
+  )\r
+{\r
+  EFI_STATUS               Status;\r
+  EFI_HANDLE               Dhcp4ChildHandle;\r
+  EFI_DHCP4_PROTOCOL       *Dhcp4;\r
+  BOOLEAN                  Dhcp4ToStop;\r
+  EFI_HANDLE               Mtftp4ChildHandle;\r
+  EFI_MTFTP4_PROTOCOL      *Mtftp4;\r
+  DHCP4_OPTION             ParaList;\r
+  EFI_DHCP4_PACKET_OPTION  *OptionList[2];\r
+  EFI_DHCP4_CONFIG_DATA    Dhcp4CfgData;\r
+  EFI_DHCP4_MODE_DATA      Dhcp4Mode;\r
+  EFI_MTFTP4_CONFIG_DATA   Mtftp4CfgData;\r
+  IPv4_DEVICE_PATH         *IPv4DevicePathNode;\r
+  CHAR16                   *PathName;\r
+  CHAR8                    *AsciiFilePath;\r
+  EFI_MTFTP4_TOKEN         Mtftp4Token;\r
+  UINT64                   FileSize;\r
+  UINT64                   TftpBufferSize;\r
+  BDS_TFTP_CONTEXT         *TftpContext;\r
+  UINTN                    PathNameLen;\r
+\r
+  ASSERT(IS_DEVICE_PATH_NODE (RemainingDevicePath, MESSAGING_DEVICE_PATH, MSG_IPv4_DP));\r
+  IPv4DevicePathNode = (IPv4_DEVICE_PATH*)RemainingDevicePath;\r
+\r
+  Dhcp4ChildHandle  = NULL;\r
+  Dhcp4             = NULL;\r
+  Dhcp4ToStop       = FALSE;\r
+  Mtftp4ChildHandle = NULL;\r
+  Mtftp4            = NULL;\r
+  AsciiFilePath     = NULL;\r
+  TftpContext       = NULL;\r
+\r
+  if (!IPv4DevicePathNode->StaticIpAddress) {\r
+    //\r
+    // Using the DHCP4 Service Binding Protocol, create a child handle of the DHCP4 service and\r
+    // install the DHCP4 protocol on it. Then, open the DHCP protocol.\r
+    //\r
+    Status = NetLibCreateServiceChild (\r
+               ControllerHandle,\r
+               gImageHandle,\r
+               &gEfiDhcp4ServiceBindingProtocolGuid,\r
+               &Dhcp4ChildHandle\r
+               );\r
+    if (!EFI_ERROR (Status)) {\r
+      Status = gBS->OpenProtocol (\r
+                      Dhcp4ChildHandle,\r
+                      &gEfiDhcp4ProtocolGuid,\r
+                      (VOID **) &Dhcp4,\r
+                      gImageHandle,\r
+                      ControllerHandle,\r
+                      EFI_OPEN_PROTOCOL_BY_DRIVER\r
+                      );\r
+    }\r
+    if (EFI_ERROR (Status)) {\r
+      Print (L"Unable to open DHCP4 protocol\n");\r
+      goto Error;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Using the MTFTP4 Service Binding Protocol, create a child handle of the MTFTP4 service and\r
+  // install the MTFTP4 protocol on it. Then, open the MTFTP4 protocol.\r
+  //\r
+  Status = NetLibCreateServiceChild (\r
+             ControllerHandle,\r
+             gImageHandle,\r
+             &gEfiMtftp4ServiceBindingProtocolGuid,\r
+             &Mtftp4ChildHandle\r
+             );\r
+  if (!EFI_ERROR (Status)) {\r
+    Status = gBS->OpenProtocol (\r
+                    Mtftp4ChildHandle,\r
+                    &gEfiMtftp4ProtocolGuid,\r
+                    (VOID **) &Mtftp4,\r
+                    gImageHandle,\r
+                    ControllerHandle,\r
+                    EFI_OPEN_PROTOCOL_BY_DRIVER\r
+                    );\r
+  }\r
+  if (EFI_ERROR (Status)) {\r
+    Print (L"Unable to open MTFTP4 protocol\n");\r
+    goto Error;\r
+  }\r
+\r
+  if (!IPv4DevicePathNode->StaticIpAddress) {\r
+    //\r
+    // Configure the DHCP4, all default settings. It is acceptable for the configuration to\r
+    // fail if the return code is equal to EFI_ACCESS_DENIED which means that the configuration\r
+    // has been done by another instance of the DHCP4 protocol or that the DHCP configuration\r
+    // process has been started but is not completed yet.\r
+    //\r
+    ZeroMem (&Dhcp4CfgData, sizeof (EFI_DHCP4_CONFIG_DATA));\r
+    ParaList.Head.OpCode     = DHCP_TAG_PARA_LIST;\r
+    ParaList.Head.Length     = 2;\r
+    ParaList.Head.Data[0]    = DHCP_TAG_NETMASK;\r
+    ParaList.Route           = DHCP_TAG_ROUTER;\r
+    OptionList[0]            = &ParaList.Head;\r
+    Dhcp4CfgData.OptionCount = 1;\r
+    Dhcp4CfgData.OptionList  = OptionList;\r
+\r
+    Status = Dhcp4->Configure (Dhcp4, &Dhcp4CfgData);\r
+    if (EFI_ERROR (Status)) {\r
+      if (Status != EFI_ACCESS_DENIED) {\r
+        Print (L"Error while configuring the DHCP4 protocol\n");\r
+        goto Error;\r
+      }\r
+    }\r
+\r
+    //\r
+    // Start the DHCP configuration. This may have already been done thus do not leave in error\r
+    // if the return code is EFI_ALREADY_STARTED.\r
+    //\r
+    Status = Dhcp4->Start (Dhcp4, NULL);\r
+    if (EFI_ERROR (Status)) {\r
+      if (Status != EFI_ALREADY_STARTED) {\r
+        Print (L"DHCP configuration failed\n");\r
+        goto Error;\r
+      }\r
+    } else {\r
+      Dhcp4ToStop = TRUE;\r
+    }\r
+\r
+    Status = Dhcp4->GetModeData (Dhcp4, &Dhcp4Mode);\r
+    if (EFI_ERROR (Status)) {\r
+      goto Error;\r
+    }\r
+\r
+    if (Dhcp4Mode.State != Dhcp4Bound) {\r
+      Status = EFI_TIMEOUT;\r
+      Print (L"DHCP configuration failed\n");\r
+      goto Error;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Configure the TFTP4 protocol\r
+  //\r
+\r
+  ZeroMem (&Mtftp4CfgData, sizeof (EFI_MTFTP4_CONFIG_DATA));\r
+  Mtftp4CfgData.UseDefaultSetting = FALSE;\r
+  Mtftp4CfgData.TimeoutValue      = 4;\r
+  Mtftp4CfgData.TryCount          = 6;\r
+\r
+  if (IPv4DevicePathNode->StaticIpAddress) {\r
+    CopyMem (&Mtftp4CfgData.StationIp , &IPv4DevicePathNode->LocalIpAddress, sizeof (EFI_IPv4_ADDRESS));\r
+    CopyMem (&Mtftp4CfgData.SubnetMask, &IPv4DevicePathNode->SubnetMask, sizeof (EFI_IPv4_ADDRESS));\r
+    CopyMem (&Mtftp4CfgData.GatewayIp , &IPv4DevicePathNode->GatewayIpAddress, sizeof (EFI_IPv4_ADDRESS));\r
+  } else {\r
+    CopyMem (&Mtftp4CfgData.StationIp , &Dhcp4Mode.ClientAddress, sizeof (EFI_IPv4_ADDRESS));\r
+    CopyMem (&Mtftp4CfgData.SubnetMask, &Dhcp4Mode.SubnetMask   , sizeof (EFI_IPv4_ADDRESS));\r
+    CopyMem (&Mtftp4CfgData.GatewayIp , &Dhcp4Mode.RouterAddress, sizeof (EFI_IPv4_ADDRESS));\r
+  }\r
+\r
+  CopyMem (&Mtftp4CfgData.ServerIp  , &IPv4DevicePathNode->RemoteIpAddress, sizeof (EFI_IPv4_ADDRESS));\r
+\r
+  Status = Mtftp4->Configure (Mtftp4, &Mtftp4CfgData);\r
+  if (EFI_ERROR (Status)) {\r
+    Print (L"Error while configuring the MTFTP4 protocol\n");\r
+    goto Error;\r
+  }\r
+\r
+  // The Device Path might contain multiple FilePath nodes\r
+  PathName      = ConvertDevicePathToText ((EFI_DEVICE_PATH_PROTOCOL*)(IPv4DevicePathNode + 1), FALSE, FALSE);\r
+  PathNameLen   = StrLen (PathName) + 1;\r
+  AsciiFilePath = AllocatePool (PathNameLen);\r
+  UnicodeStrToAsciiStrS (PathName, AsciiFilePath, PathNameLen);\r
+\r
+  //\r
+  // Try to get the size of the file in bytes from the server. If it fails,\r
+  // start with a 8MB buffer to download the file.\r
+  //\r
+  FileSize = 0;\r
+  if (Mtftp4GetFileSize (Mtftp4, AsciiFilePath, &FileSize) == EFI_SUCCESS) {\r
+    TftpBufferSize = FileSize;\r
+  } else {\r
+    TftpBufferSize = SIZE_16MB;\r
+  }\r
+\r
+  TftpContext = AllocatePool (sizeof (BDS_TFTP_CONTEXT));\r
+  if (TftpContext == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto Error;\r
+  }\r
+  TftpContext->FileSize = FileSize;\r
+\r
+  for (; TftpBufferSize <= FixedPcdGet32 (PcdMaxTftpFileSize);\r
+         TftpBufferSize = (TftpBufferSize + SIZE_16MB) & (~(SIZE_16MB-1))) {\r
+    //\r
+    // Allocate a buffer to hold the whole file.\r
+    //\r
+    Status = gBS->AllocatePages (\r
+                    Type,\r
+                    EfiBootServicesCode,\r
+                    EFI_SIZE_TO_PAGES (TftpBufferSize),\r
+                    Image\r
+                    );\r
+    if (EFI_ERROR (Status)) {\r
+      Print (L"Failed to allocate space for image\n");\r
+      goto Error;\r
+    }\r
+\r
+    TftpContext->DownloadedNbOfBytes   = 0;\r
+    TftpContext->LastReportedNbOfBytes = 0;\r
+\r
+    ZeroMem (&Mtftp4Token, sizeof (EFI_MTFTP4_TOKEN));\r
+    Mtftp4Token.Filename    = (UINT8*)AsciiFilePath;\r
+    Mtftp4Token.BufferSize  = TftpBufferSize;\r
+    Mtftp4Token.Buffer      = (VOID *)(UINTN)*Image;\r
+    Mtftp4Token.CheckPacket = Mtftp4CheckPacket;\r
+    Mtftp4Token.Context     = (VOID*)TftpContext;\r
+\r
+    Print (L"Downloading the file <%a> from the TFTP server\n", AsciiFilePath);\r
+    Status = Mtftp4->ReadFile (Mtftp4, &Mtftp4Token);\r
+    Print (L"\n");\r
+    if (EFI_ERROR (Status)) {\r
+      gBS->FreePages (*Image, EFI_SIZE_TO_PAGES (TftpBufferSize));\r
+      if (Status == EFI_BUFFER_TOO_SMALL) {\r
+        Print (L"Downloading failed, file larger than expected.\n");\r
+        continue;\r
+      } else {\r
+        goto Error;\r
+      }\r
+    }\r
+\r
+    *ImageSize = Mtftp4Token.BufferSize;\r
+    break;\r
+  }\r
+\r
+Error:\r
+  if (Dhcp4ChildHandle != NULL) {\r
+    if (Dhcp4 != NULL) {\r
+      if (Dhcp4ToStop) {\r
+        Dhcp4->Stop (Dhcp4);\r
+      }\r
+      gBS->CloseProtocol (\r
+             Dhcp4ChildHandle,\r
+             &gEfiDhcp4ProtocolGuid,\r
+             gImageHandle,\r
+             ControllerHandle\r
+            );\r
+    }\r
+    NetLibDestroyServiceChild (\r
+      ControllerHandle,\r
+      gImageHandle,\r
+      &gEfiDhcp4ServiceBindingProtocolGuid,\r
+      Dhcp4ChildHandle\r
+      );\r
+  }\r
+\r
+  if (Mtftp4ChildHandle != NULL) {\r
+    if (Mtftp4 != NULL) {\r
+      if (AsciiFilePath != NULL) {\r
+        FreePool (AsciiFilePath);\r
+      }\r
+      if (TftpContext != NULL) {\r
+        FreePool (TftpContext);\r
+      }\r
+      gBS->CloseProtocol (\r
+             Mtftp4ChildHandle,\r
+             &gEfiMtftp4ProtocolGuid,\r
+             gImageHandle,\r
+             ControllerHandle\r
+            );\r
+    }\r
+    NetLibDestroyServiceChild (\r
+      ControllerHandle,\r
+      gImageHandle,\r
+      &gEfiMtftp4ServiceBindingProtocolGuid,\r
+      Mtftp4ChildHandle\r
+      );\r
+  }\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    *Image = 0;\r
+    Print (L"Failed to download the file - Error=%r\n", Status);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+BDS_FILE_LOADER FileLoaders[] = {\r
+    { BdsFileSystemSupport, BdsFileSystemLoadImage },\r
+    { BdsFirmwareVolumeSupport, BdsFirmwareVolumeLoadImage },\r
+    //{ BdsLoadFileSupport, BdsLoadFileLoadImage },\r
+    { BdsMemoryMapSupport, BdsMemoryMapLoadImage },\r
+    { BdsPxeSupport, BdsPxeLoadImage },\r
+    { BdsTftpSupport, BdsTftpLoadImage },\r
+    { NULL, NULL }\r
+};\r
+\r
+EFI_STATUS\r
+BdsLoadImageAndUpdateDevicePath (\r
+  IN OUT EFI_DEVICE_PATH       **DevicePath,\r
+  IN     EFI_ALLOCATE_TYPE     Type,\r
+  IN OUT EFI_PHYSICAL_ADDRESS* Image,\r
+  OUT    UINTN                 *FileSize\r
+  )\r
+{\r
+  EFI_STATUS      Status;\r
+  EFI_HANDLE      Handle;\r
+  EFI_DEVICE_PATH *RemainingDevicePath;\r
+  BDS_FILE_LOADER*  FileLoader;\r
+\r
+  Status = BdsConnectAndUpdateDevicePath (DevicePath, &Handle, &RemainingDevicePath);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  FileLoader = FileLoaders;\r
+  while (FileLoader->Support != NULL) {\r
+    if (FileLoader->Support (*DevicePath, Handle, RemainingDevicePath)) {\r
+      return FileLoader->LoadImage (DevicePath, Handle, RemainingDevicePath, Type, Image, FileSize);\r
+    }\r
+    FileLoader++;\r
+  }\r
+\r
+  return EFI_UNSUPPORTED;\r
+}\r
+\r
+EFI_STATUS\r
+BdsLoadImage (\r
+  IN     EFI_DEVICE_PATH       *DevicePath,\r
+  IN     EFI_ALLOCATE_TYPE     Type,\r
+  IN OUT EFI_PHYSICAL_ADDRESS* Image,\r
+  OUT    UINTN                 *FileSize\r
+  )\r
+{\r
+  return BdsLoadImageAndUpdateDevicePath (&DevicePath, Type, Image, FileSize);\r
+}\r
+\r
+/**\r
+  Start an EFI Application from a Device Path\r
+\r
+  @param  ParentImageHandle     Handle of the calling image\r
+  @param  DevicePath            Location of the EFI Application\r
+\r
+  @retval EFI_SUCCESS           All drivers have been connected\r
+  @retval EFI_NOT_FOUND         The Linux kernel Device Path has not been found\r
+  @retval EFI_OUT_OF_RESOURCES  There is not enough resource memory to store the matching results.\r
+\r
+**/\r
+EFI_STATUS\r
+BdsStartEfiApplication (\r
+  IN EFI_HANDLE                  ParentImageHandle,\r
+  IN EFI_DEVICE_PATH_PROTOCOL    *DevicePath,\r
+  IN UINTN                       LoadOptionsSize,\r
+  IN VOID*                       LoadOptions\r
+  )\r
+{\r
+  EFI_STATUS                   Status;\r
+  EFI_HANDLE                   ImageHandle;\r
+  EFI_PHYSICAL_ADDRESS         BinaryBuffer;\r
+  UINTN                        BinarySize;\r
+  EFI_LOADED_IMAGE_PROTOCOL*   LoadedImage;\r
+\r
+  // Find the nearest supported file loader\r
+  Status = BdsLoadImageAndUpdateDevicePath (&DevicePath, AllocateAnyPages, &BinaryBuffer, &BinarySize);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  // Load the image from the Buffer with Boot Services function\r
+  Status = gBS->LoadImage (TRUE, ParentImageHandle, DevicePath, (VOID*)(UINTN)BinaryBuffer, BinarySize, &ImageHandle);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  // Passed LoadOptions to the EFI Application\r
+  if (LoadOptionsSize != 0) {\r
+    Status = gBS->HandleProtocol (ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **) &LoadedImage);\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+\r
+    LoadedImage->LoadOptionsSize  = LoadOptionsSize;\r
+    LoadedImage->LoadOptions      = LoadOptions;\r
+  }\r
+\r
+  // Before calling the image, enable the Watchdog Timer for  the 5 Minute period\r
+  gBS->SetWatchdogTimer (5 * 60, 0x0000, 0x00, NULL);\r
+  // Start the image\r
+  Status = gBS->StartImage (ImageHandle, NULL, NULL);\r
+  // Clear the Watchdog Timer after the image returns\r
+  gBS->SetWatchdogTimer (0x0000, 0x0000, 0x0000, NULL);\r
+\r
+  return Status;\r
+}\r