diff mbox series

[edk2,edk2-platforms,1/4] Platform/ARM: import BdsLib from ArmPkg

Message ID 20171120113714.21856-2-ard.biesheuvel@linaro.org
State New
Headers show
Series Platform/ARM: import BdsLib and FdtPlatformDxe | expand

Commit Message

Ard Biesheuvel Nov. 20, 2017, 11:37 a.m. UTC
We are about to migrate the only remaining user of the deprecated ARM
BdsLib, i.e., FdtPlatformDxe, into Platform/ARM. So create our own
copy of BdsLib, allowing us to finally remove it from upstream EDK2.

Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>

---
 Platform/ARM/Library/BdsLib/BdsAppLoader.c  |  253 ++++
 Platform/ARM/Library/BdsLib/BdsFilePath.c   | 1413 ++++++++++++++++++++
 Platform/ARM/Library/BdsLib/BdsHelper.c     |  183 +++
 Platform/ARM/Library/BdsLib/BdsInternal.h   |  111 ++
 Platform/ARM/Library/BdsLib/BdsLib.inf      |   62 +
 Platform/ARM/Library/BdsLib/BdsLoadOption.c |  272 ++++
 6 files changed, 2294 insertions(+)

-- 
2.11.0

_______________________________________________
edk2-devel mailing list
edk2-devel@lists.01.org
https://lists.01.org/mailman/listinfo/edk2-devel
diff mbox series

Patch

diff --git a/Platform/ARM/Library/BdsLib/BdsAppLoader.c b/Platform/ARM/Library/BdsLib/BdsAppLoader.c
new file mode 100644
index 000000000000..1f208f8dd796
--- /dev/null
+++ b/Platform/ARM/Library/BdsLib/BdsAppLoader.c
@@ -0,0 +1,253 @@ 
+/** @file
+*
+*  Copyright (c) 2011-2015, 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"
+
+/**
+  Locate an EFI application in a the Firmware Volumes by its Name
+
+  @param  EfiAppGuid            Guid of the EFI Application into the Firmware Volume
+  @param  DevicePath            EFI Device Path of the EFI application
+
+  @return EFI_SUCCESS           The function completed successfully.
+  @return EFI_NOT_FOUND         The protocol could not be located.
+  @return EFI_OUT_OF_RESOURCES  There are not enough resources to find the protocol.
+
+**/
+EFI_STATUS
+LocateEfiApplicationInFvByName (
+  IN  CONST CHAR16*             EfiAppName,
+  OUT EFI_DEVICE_PATH           **DevicePath
+  )
+{
+  VOID                          *Key;
+  EFI_STATUS                    Status, FileStatus;
+  EFI_GUID                      NameGuid;
+  EFI_FV_FILETYPE               FileType;
+  EFI_FV_FILE_ATTRIBUTES        Attributes;
+  UINTN                         Size;
+  UINTN                         UiStringLen;
+  CHAR16                        *UiSection;
+  UINT32                        Authentication;
+  EFI_DEVICE_PATH               *FvDevicePath;
+  MEDIA_FW_VOL_FILEPATH_DEVICE_PATH    FileDevicePath;
+  EFI_HANDLE                    *HandleBuffer;
+  UINTN                         NumberOfHandles;
+  UINTN                         Index;
+  EFI_FIRMWARE_VOLUME2_PROTOCOL *FvInstance;
+
+  ASSERT (DevicePath != NULL);
+
+  // Length of FilePath
+  UiStringLen = StrLen (EfiAppName);
+
+  // Locate all the Firmware Volume protocols.
+  Status = gBS->LocateHandleBuffer (
+                   ByProtocol,
+                   &gEfiFirmwareVolume2ProtocolGuid,
+                   NULL,
+                   &NumberOfHandles,
+                   &HandleBuffer
+                   );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  *DevicePath = NULL;
+
+  // Looking for FV with ACPI storage file
+  for (Index = 0; Index < NumberOfHandles; Index++) {
+    //
+    // Get the protocol on this handle
+    // This should not fail because of LocateHandleBuffer
+    //
+    Status = gBS->HandleProtocol (
+                     HandleBuffer[Index],
+                     &gEfiFirmwareVolume2ProtocolGuid,
+                     (VOID**) &FvInstance
+                     );
+    if (EFI_ERROR (Status)) {
+      goto FREE_HANDLE_BUFFER;
+    }
+
+    // Allocate Key
+    Key = AllocatePool (FvInstance->KeySize);
+    ASSERT (Key != NULL);
+    ZeroMem (Key, FvInstance->KeySize);
+
+    do {
+      // Search in all files
+      FileType = EFI_FV_FILETYPE_ALL;
+
+      Status = FvInstance->GetNextFile (FvInstance, Key, &FileType, &NameGuid, &Attributes, &Size);
+      if (!EFI_ERROR (Status)) {
+        UiSection = NULL;
+        FileStatus = FvInstance->ReadSection (
+                      FvInstance,
+                      &NameGuid,
+                      EFI_SECTION_USER_INTERFACE,
+                      0,
+                      (VOID **)&UiSection,
+                      &Size,
+                      &Authentication
+                      );
+        if (!EFI_ERROR (FileStatus)) {
+          if (StrnCmp (EfiAppName, UiSection, UiStringLen) == 0) {
+            //
+            // We found a UiString match.
+            //
+            Status = gBS->HandleProtocol (HandleBuffer[Index], &gEfiDevicePathProtocolGuid, (VOID **)&FvDevicePath);
+
+            // Generate the Device Path for the file
+            EfiInitializeFwVolDevicepathNode (&FileDevicePath, &NameGuid);
+            *DevicePath = AppendDevicePathNode (FvDevicePath, (EFI_DEVICE_PATH_PROTOCOL *)&FileDevicePath);
+            ASSERT (*DevicePath != NULL);
+
+            FreePool (Key);
+            FreePool (UiSection);
+            FreePool (HandleBuffer);
+            return FileStatus;
+          }
+          FreePool (UiSection);
+        }
+      }
+    } while (!EFI_ERROR (Status));
+
+    FreePool (Key);
+  }
+
+FREE_HANDLE_BUFFER:
+  FreePool (HandleBuffer);
+  return EFI_NOT_FOUND;
+}
+
+/**
+  Locate an EFI application in a the Firmware Volumes by its GUID
+
+  @param  EfiAppGuid            Guid of the EFI Application into the Firmware Volume
+  @param  DevicePath            EFI Device Path of the EFI application
+
+  @return EFI_SUCCESS           The function completed successfully.
+  @return EFI_NOT_FOUND         The protocol could not be located.
+  @return EFI_OUT_OF_RESOURCES  There are not enough resources to find the protocol.
+
+**/
+EFI_STATUS
+LocateEfiApplicationInFvByGuid (
+  IN  CONST EFI_GUID            *EfiAppGuid,
+  OUT EFI_DEVICE_PATH           **DevicePath
+  )
+{
+  EFI_STATUS                    Status;
+  EFI_DEVICE_PATH               *FvDevicePath;
+  EFI_HANDLE                    *HandleBuffer;
+  UINTN                         NumberOfHandles;
+  UINTN                         Index;
+  EFI_FIRMWARE_VOLUME2_PROTOCOL *FvInstance;
+  EFI_FV_FILE_ATTRIBUTES        Attributes;
+  UINT32                        AuthenticationStatus;
+  EFI_FV_FILETYPE               Type;
+  UINTN                         Size;
+  CHAR16                        *UiSection;
+  MEDIA_FW_VOL_FILEPATH_DEVICE_PATH FvFileDevicePath;
+
+  ASSERT (DevicePath != NULL);
+
+  // Locate all the Firmware Volume protocols.
+  Status = gBS->LocateHandleBuffer (
+                   ByProtocol,
+                   &gEfiFirmwareVolume2ProtocolGuid,
+                   NULL,
+                   &NumberOfHandles,
+                   &HandleBuffer
+                   );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  *DevicePath = NULL;
+
+  // Looking for FV with ACPI storage file
+  for (Index = 0; Index < NumberOfHandles; Index++) {
+    //
+    // Get the protocol on this handle
+    // This should not fail because of LocateHandleBuffer
+    //
+    Status = gBS->HandleProtocol (
+                     HandleBuffer[Index],
+                     &gEfiFirmwareVolume2ProtocolGuid,
+                     (VOID**) &FvInstance
+                     );
+    if (EFI_ERROR (Status)) {
+      goto FREE_HANDLE_BUFFER;
+    }
+
+    Status = FvInstance->ReadFile (
+                  FvInstance,
+                  EfiAppGuid,
+                  NULL,
+                  &Size,
+                  &Type,
+                  &Attributes,
+                  &AuthenticationStatus
+                  );
+    if (EFI_ERROR (Status)) {
+      //
+      // Skip if no EFI application file in the FV
+      //
+      continue;
+    } else {
+      UiSection = NULL;
+      Status = FvInstance->ReadSection (
+                    FvInstance,
+                    EfiAppGuid,
+                    EFI_SECTION_USER_INTERFACE,
+                    0,
+                    (VOID **)&UiSection,
+                    &Size,
+                    &AuthenticationStatus
+                    );
+      if (!EFI_ERROR (Status)) {
+        //
+        // Create the EFI Device Path for the application using the Filename of the application
+        //
+        *DevicePath = FileDevicePath (HandleBuffer[Index], UiSection);
+      } else {
+        Status = gBS->HandleProtocol (HandleBuffer[Index], &gEfiDevicePathProtocolGuid, (VOID**)&FvDevicePath);
+        ASSERT_EFI_ERROR (Status);
+
+        //
+        // Create the EFI Device Path for the application using the EFI GUID of the application
+        //
+        EfiInitializeFwVolDevicepathNode (&FvFileDevicePath, EfiAppGuid);
+
+        *DevicePath = AppendDevicePathNode (FvDevicePath, (EFI_DEVICE_PATH_PROTOCOL *)&FvFileDevicePath);
+        ASSERT (*DevicePath != NULL);
+      }
+      break;
+    }
+  }
+
+FREE_HANDLE_BUFFER:
+  //
+  // Free any allocated buffers
+  //
+  FreePool (HandleBuffer);
+
+  if (*DevicePath == NULL) {
+    return EFI_NOT_FOUND;
+  } else {
+    return EFI_SUCCESS;
+  }
+}
diff --git a/Platform/ARM/Library/BdsLib/BdsFilePath.c b/Platform/ARM/Library/BdsLib/BdsFilePath.c
new file mode 100644
index 000000000000..7a4a5052a786
--- /dev/null
+++ b/Platform/ARM/Library/BdsLib/BdsFilePath.c
@@ -0,0 +1,1413 @@ 
+/** @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 MAX_TFTP_FILE_SIZE    0x01000000
+
+/* 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 <= MAX_TFTP_FILE_SIZE;
+         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;
+}
diff --git a/Platform/ARM/Library/BdsLib/BdsHelper.c b/Platform/ARM/Library/BdsLib/BdsHelper.c
new file mode 100644
index 000000000000..b10fe2074d53
--- /dev/null
+++ b/Platform/ARM/Library/BdsLib/BdsHelper.c
@@ -0,0 +1,183 @@ 
+/** @file
+*
+*  Copyright (c) 2011-2015, 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"
+
+EFI_STATUS
+ShutdownUefiBootServices (
+  VOID
+  )
+{
+  EFI_STATUS              Status;
+  UINTN                   MemoryMapSize;
+  EFI_MEMORY_DESCRIPTOR   *MemoryMap;
+  UINTN                   MapKey;
+  UINTN                   DescriptorSize;
+  UINT32                  DescriptorVersion;
+  UINTN                   Pages;
+
+  MemoryMap = NULL;
+  MemoryMapSize = 0;
+  Pages = 0;
+
+  do {
+    Status = gBS->GetMemoryMap (
+                    &MemoryMapSize,
+                    MemoryMap,
+                    &MapKey,
+                    &DescriptorSize,
+                    &DescriptorVersion
+                    );
+    if (Status == EFI_BUFFER_TOO_SMALL) {
+
+      Pages = EFI_SIZE_TO_PAGES (MemoryMapSize) + 1;
+      MemoryMap = AllocatePages (Pages);
+
+      //
+      // Get System MemoryMap
+      //
+      Status = gBS->GetMemoryMap (
+                      &MemoryMapSize,
+                      MemoryMap,
+                      &MapKey,
+                      &DescriptorSize,
+                      &DescriptorVersion
+                      );
+    }
+
+    // Don't do anything between the GetMemoryMap() and ExitBootServices()
+    if (!EFI_ERROR(Status)) {
+      Status = gBS->ExitBootServices (gImageHandle, MapKey);
+      if (EFI_ERROR(Status)) {
+        FreePages (MemoryMap, Pages);
+        MemoryMap = NULL;
+        MemoryMapSize = 0;
+      }
+    }
+  } while (EFI_ERROR(Status));
+
+  return Status;
+}
+
+/**
+  Connect all DXE drivers
+
+  @retval EFI_SUCCESS           All drivers have been connected
+  @retval EFI_NOT_FOUND         No handles match the search.
+  @retval EFI_OUT_OF_RESOURCES  There is not resource pool memory to store the matching results.
+
+**/
+EFI_STATUS
+BdsConnectAllDrivers (
+  VOID
+  )
+{
+  UINTN                     HandleCount, Index;
+  EFI_HANDLE                *HandleBuffer;
+  EFI_STATUS                Status;
+
+  do {
+    // Locate all the driver handles
+    Status = gBS->LocateHandleBuffer (
+                AllHandles,
+                NULL,
+                NULL,
+                &HandleCount,
+                &HandleBuffer
+                );
+    if (EFI_ERROR (Status)) {
+      break;
+    }
+
+    // Connect every handles
+    for (Index = 0; Index < HandleCount; Index++) {
+      gBS->ConnectController (HandleBuffer[Index], NULL, NULL, TRUE);
+    }
+
+    if (HandleBuffer != NULL) {
+      FreePool (HandleBuffer);
+    }
+
+    // Check if new handles have been created after the start of the previous handles
+    Status = gDS->Dispatch ();
+  } while (!EFI_ERROR(Status));
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+GetGlobalEnvironmentVariable (
+  IN     CONST CHAR16*   VariableName,
+  IN     VOID*           DefaultValue,
+  IN OUT UINTN*          Size,
+  OUT    VOID**          Value
+  )
+{
+  return GetEnvironmentVariable (VariableName, &gEfiGlobalVariableGuid,
+           DefaultValue, Size, Value);
+}
+
+EFI_STATUS
+GetEnvironmentVariable (
+  IN     CONST CHAR16*   VariableName,
+  IN     EFI_GUID*       VendorGuid,
+  IN     VOID*           DefaultValue,
+  IN OUT UINTN*          Size,
+  OUT    VOID**          Value
+  )
+{
+  EFI_STATUS  Status;
+  UINTN       VariableSize;
+
+  // Try to get the variable size.
+  *Value = NULL;
+  VariableSize = 0;
+  Status = gRT->GetVariable ((CHAR16 *) VariableName, VendorGuid, NULL, &VariableSize, *Value);
+  if (Status == EFI_NOT_FOUND) {
+    if ((DefaultValue != NULL) && (Size != NULL) && (*Size != 0)) {
+      // If the environment variable does not exist yet then set it with the default value
+      Status = gRT->SetVariable (
+                    (CHAR16*)VariableName,
+                    VendorGuid,
+                    EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
+                    *Size,
+                    DefaultValue
+                    );
+      *Value = AllocateCopyPool (*Size, DefaultValue);
+    } else {
+      return EFI_NOT_FOUND;
+    }
+  } else if (Status == EFI_BUFFER_TOO_SMALL) {
+    // Get the environment variable value
+    *Value = AllocatePool (VariableSize);
+    if (*Value == NULL) {
+      return EFI_OUT_OF_RESOURCES;
+    }
+
+    Status = gRT->GetVariable ((CHAR16 *)VariableName, VendorGuid, NULL, &VariableSize, *Value);
+    if (EFI_ERROR (Status)) {
+      FreePool(*Value);
+      return EFI_INVALID_PARAMETER;
+    }
+
+    if (Size) {
+      *Size = VariableSize;
+    }
+  } else {
+    *Value = AllocateCopyPool (*Size, DefaultValue);
+    return Status;
+  }
+
+  return EFI_SUCCESS;
+}
diff --git a/Platform/ARM/Library/BdsLib/BdsInternal.h b/Platform/ARM/Library/BdsLib/BdsInternal.h
new file mode 100644
index 000000000000..f70aae603d69
--- /dev/null
+++ b/Platform/ARM/Library/BdsLib/BdsInternal.h
@@ -0,0 +1,111 @@ 
+/** @file
+*
+*  Copyright (c) 2011-2015, 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.
+*
+**/
+
+#ifndef __BDS_INTERNAL_H__
+#define __BDS_INTERNAL_H__
+
+#include <PiDxe.h>
+#include <Library/ArmLib.h>
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DxeServicesTableLib.h>
+#include <Library/HobLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/DebugLib.h>
+#include <Library/BdsLib.h>
+#include <Library/PcdLib.h>
+#include <Library/PrintLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+
+#include <Guid/GlobalVariable.h>
+#include <Guid/FileInfo.h>
+
+#include <Protocol/DevicePath.h>
+#include <Protocol/DevicePathFromText.h>
+#include <Protocol/SimpleFileSystem.h>
+#include <Protocol/FirmwareVolume2.h>
+#include <Protocol/LoadFile.h>
+#include <Protocol/PxeBaseCode.h>
+
+#include <Uefi.h>
+
+/**
+ * Check if the file loader can support this device path.
+ *
+ * @param DevicePath    EFI Device Path of the image to load.
+ *                      This device path generally comes from the boot entry (ie: Boot####).
+ * @param Handle        Handle of the driver supporting the device path
+ * @param RemainingDevicePath   Part of the EFI Device Path that has not been resolved during
+ *                      the Device Path discovery
+ */
+typedef BOOLEAN (*BDS_FILE_LOADER_SUPPORT) (
+  IN EFI_DEVICE_PATH            *DevicePath,
+  IN EFI_HANDLE                 Handle,
+  IN EFI_DEVICE_PATH            *RemainingDevicePath
+  );
+
+/**
+ * Function to load an image from a given Device Path for a
+ * specific support (FileSystem, TFTP, PXE, ...)
+ *
+ * @param DevicePath    EFI Device Path of the image to load.
+ *                      This device path generally comes from the boot entry (ie: Boot####).
+ *                      This path is also defined as 'OUT' as there are some device paths that
+ *                      might not be completed such as EFI path for removable device. In these
+ *                      cases, it is expected the loader to add \EFI\BOOT\BOOT(ARM|AA64).EFI
+ * @param Handle        Handle of the driver supporting the device path
+ * @param RemainingDevicePath   Part of the EFI Device Path that has not been resolved during
+ *                      the Device Path discovery
+ * @param Type          Define where the image should be loaded (see EFI_ALLOCATE_TYPE definition)
+ * @param Image         Base Address of the image has been loaded
+ * @param ImageSize     Size of the image that has been loaded
+ */
+typedef EFI_STATUS (*BDS_FILE_LOADER_LOAD_IMAGE) (
+  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
+  );
+
+typedef struct {
+  BDS_FILE_LOADER_SUPPORT     Support;
+  BDS_FILE_LOADER_LOAD_IMAGE  LoadImage;
+} BDS_FILE_LOADER;
+
+typedef struct _BDS_SYSTEM_MEMORY_RESOURCE {
+  LIST_ENTRY                  Link; // This attribute must be the first entry of this structure (to avoid pointer computation)
+  EFI_PHYSICAL_ADDRESS        PhysicalStart;
+  UINT64                      ResourceLength;
+} BDS_SYSTEM_MEMORY_RESOURCE;
+
+typedef struct {
+  UINT64  FileSize;
+  UINT64  DownloadedNbOfBytes;
+  UINT64  LastReportedNbOfBytes;
+} BDS_TFTP_CONTEXT;
+
+EFI_STATUS
+BdsLoadImage (
+  IN     EFI_DEVICE_PATH       *DevicePath,
+  IN     EFI_ALLOCATE_TYPE     Type,
+  IN OUT EFI_PHYSICAL_ADDRESS* Image,
+  OUT    UINTN                 *FileSize
+  );
+
+#endif
diff --git a/Platform/ARM/Library/BdsLib/BdsLib.inf b/Platform/ARM/Library/BdsLib/BdsLib.inf
new file mode 100644
index 000000000000..96c1d6e7e200
--- /dev/null
+++ b/Platform/ARM/Library/BdsLib/BdsLib.inf
@@ -0,0 +1,62 @@ 
+#/* @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.
+#
+#*/
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = BdsLib
+  FILE_GUID                      = ddbf73a0-bb25-11df-8e4e-0002a5d5c51b
+  MODULE_TYPE                    = DXE_DRIVER
+  VERSION_STRING                 = 1.0
+  LIBRARY_CLASS                  = BdsLib
+
+[Sources.common]
+  BdsFilePath.c
+  BdsAppLoader.c
+  BdsHelper.c
+  BdsLoadOption.c
+
+[Packages]
+  ArmPkg/ArmPkg.dec
+  EmbeddedPkg/EmbeddedPkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+  MdePkg/MdePkg.dec
+
+[LibraryClasses]
+  ArmLib
+  BaseLib
+  DebugLib
+  DevicePathLib
+  HobLib
+  PcdLib
+  NetLib
+
+[Guids]
+  gEfiFileInfoGuid
+
+[Protocols]
+  gEfiBdsArchProtocolGuid
+  gEfiDevicePathProtocolGuid
+  gEfiDevicePathFromTextProtocolGuid
+  gEfiSimpleFileSystemProtocolGuid
+  gEfiFirmwareVolume2ProtocolGuid
+  gEfiLoadFileProtocolGuid
+  gEfiPxeBaseCodeProtocolGuid
+  gEfiDiskIoProtocolGuid
+  gEfiUsbIoProtocolGuid
+  gEfiLoadedImageProtocolGuid
+  gEfiSimpleNetworkProtocolGuid
+  gEfiDhcp4ServiceBindingProtocolGuid
+  gEfiDhcp4ProtocolGuid
+  gEfiMtftp4ServiceBindingProtocolGuid
+  gEfiMtftp4ProtocolGuid
diff --git a/Platform/ARM/Library/BdsLib/BdsLoadOption.c b/Platform/ARM/Library/BdsLib/BdsLoadOption.c
new file mode 100644
index 000000000000..766a9890fc09
--- /dev/null
+++ b/Platform/ARM/Library/BdsLib/BdsLoadOption.c
@@ -0,0 +1,272 @@ 
+/** @file
+*
+*  Copyright (c) 2011-2013, 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"
+
+EFI_STATUS
+BootOptionParseLoadOption (
+  IN     EFI_LOAD_OPTION *EfiLoadOption,
+  IN     UINTN           EfiLoadOptionSize,
+  IN OUT BDS_LOAD_OPTION **BdsLoadOption
+  )
+{
+  BDS_LOAD_OPTION *LoadOption;
+  UINTN           DescriptionLength;
+  UINTN           EfiLoadOptionPtr;
+
+  if (EfiLoadOption == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (EfiLoadOptionSize < sizeof(UINT32) + sizeof(UINT16) + sizeof(CHAR16) + sizeof(EFI_DEVICE_PATH_PROTOCOL)) {
+    return EFI_BAD_BUFFER_SIZE;
+  }
+
+  if (*BdsLoadOption == NULL) {
+    LoadOption = (BDS_LOAD_OPTION*)AllocateZeroPool (sizeof(BDS_LOAD_OPTION));
+    if (LoadOption == NULL) {
+      return EFI_OUT_OF_RESOURCES;
+    }
+  } else {
+    LoadOption = *BdsLoadOption;
+  }
+
+  EfiLoadOptionPtr           = (UINTN)EfiLoadOption;
+  LoadOption->LoadOption     = EfiLoadOption;
+  LoadOption->LoadOptionSize = EfiLoadOptionSize;
+
+  LoadOption->Attributes         = *(UINT32*)EfiLoadOptionPtr;
+  LoadOption->FilePathListLength = *(UINT16*)(EfiLoadOptionPtr + sizeof(UINT32));
+  LoadOption->Description        = (CHAR16*)(EfiLoadOptionPtr + sizeof(UINT32) + sizeof(UINT16));
+  DescriptionLength              = StrSize (LoadOption->Description);
+  LoadOption->FilePathList       = (EFI_DEVICE_PATH_PROTOCOL*)(EfiLoadOptionPtr + sizeof(UINT32) + sizeof(UINT16) + DescriptionLength);
+
+  // If ((End of EfiLoadOptiony - Start of EfiLoadOption) == EfiLoadOptionSize) then No Optional Data
+  if ((UINTN)((UINTN)LoadOption->FilePathList + LoadOption->FilePathListLength - EfiLoadOptionPtr) == EfiLoadOptionSize) {
+    LoadOption->OptionalData     = NULL;
+    LoadOption->OptionalDataSize = 0;
+  } else {
+    LoadOption->OptionalData     = (VOID*)((UINTN)(LoadOption->FilePathList) + LoadOption->FilePathListLength);
+    LoadOption->OptionalDataSize = EfiLoadOptionSize - ((UINTN)LoadOption->OptionalData - EfiLoadOptionPtr);
+  }
+
+  if (*BdsLoadOption == NULL) {
+    *BdsLoadOption = LoadOption;
+  }
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+BootOptionFromLoadOptionVariable (
+  IN  CHAR16*           BootVariableName,
+  OUT BDS_LOAD_OPTION** BdsLoadOption
+  )
+{
+  EFI_STATUS            Status;
+  EFI_LOAD_OPTION       *EfiLoadOption;
+  UINTN                 EfiLoadOptionSize;
+
+  Status = GetGlobalEnvironmentVariable (BootVariableName, NULL, &EfiLoadOptionSize, (VOID**)&EfiLoadOption);
+  if (!EFI_ERROR(Status)) {
+    *BdsLoadOption = NULL;
+    Status = BootOptionParseLoadOption (EfiLoadOption, EfiLoadOptionSize, BdsLoadOption);
+  }
+
+  return Status;
+}
+
+EFI_STATUS
+BootOptionFromLoadOptionIndex (
+  IN  UINT16            LoadOptionIndex,
+  OUT BDS_LOAD_OPTION **BdsLoadOption
+  )
+{
+  CHAR16        BootVariableName[9];
+  EFI_STATUS    Status;
+
+  UnicodeSPrint (BootVariableName, 9 * sizeof(CHAR16), L"Boot%04X", LoadOptionIndex);
+
+  Status = BootOptionFromLoadOptionVariable (BootVariableName, BdsLoadOption);
+  if (!EFI_ERROR(Status)) {
+    (*BdsLoadOption)->LoadOptionIndex = LoadOptionIndex;
+  }
+
+  return Status;
+}
+
+EFI_STATUS
+BootOptionToLoadOptionVariable (
+  IN BDS_LOAD_OPTION*   BdsLoadOption
+  )
+{
+  EFI_STATUS                    Status;
+  UINTN                         DescriptionSize;
+  //UINT16                        FilePathListLength;
+  EFI_DEVICE_PATH_PROTOCOL*     DevicePathNode;
+  UINTN                         NodeLength;
+  UINT8*                        EfiLoadOptionPtr;
+  VOID*                         OldLoadOption;
+  CHAR16                        BootVariableName[9];
+  UINTN                         BootOrderSize;
+  UINT16*                       BootOrder;
+
+  // If we are overwriting an existent Boot Option then we have to free previously allocated memory
+  if (BdsLoadOption->LoadOptionSize > 0) {
+    OldLoadOption = BdsLoadOption->LoadOption;
+  } else {
+    OldLoadOption = NULL;
+
+    // If this function is called at the creation of the Boot Device entry (not at the update) the
+    // BootOption->LoadOptionSize must be zero then we get a new BootIndex for this entry
+    BdsLoadOption->LoadOptionIndex = BootOptionAllocateBootIndex ();
+
+    //TODO: Add to the the Boot Entry List
+  }
+
+  DescriptionSize = StrSize(BdsLoadOption->Description);
+
+  // Ensure the FilePathListLength information is correct
+  ASSERT (GetDevicePathSize (BdsLoadOption->FilePathList) == BdsLoadOption->FilePathListLength);
+
+  // Allocate the memory for the EFI Load Option
+  BdsLoadOption->LoadOptionSize = sizeof(UINT32) + sizeof(UINT16) + DescriptionSize + BdsLoadOption->FilePathListLength + BdsLoadOption->OptionalDataSize;
+
+  BdsLoadOption->LoadOption = (EFI_LOAD_OPTION *)AllocateZeroPool (BdsLoadOption->LoadOptionSize);
+  if (BdsLoadOption->LoadOption == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  EfiLoadOptionPtr = (UINT8 *) BdsLoadOption->LoadOption;
+
+  //
+  // Populate the EFI Load Option and BDS Boot Option structures
+  //
+
+  // Attributes fields
+  *(UINT32*)EfiLoadOptionPtr = BdsLoadOption->Attributes;
+  EfiLoadOptionPtr += sizeof(UINT32);
+
+  // FilePath List fields
+  *(UINT16*)EfiLoadOptionPtr = BdsLoadOption->FilePathListLength;
+  EfiLoadOptionPtr += sizeof(UINT16);
+
+  // Boot description fields
+  CopyMem (EfiLoadOptionPtr, BdsLoadOption->Description, DescriptionSize);
+  EfiLoadOptionPtr += DescriptionSize;
+
+  // File path fields
+  DevicePathNode = BdsLoadOption->FilePathList;
+  while (!IsDevicePathEndType (DevicePathNode)) {
+    NodeLength = DevicePathNodeLength(DevicePathNode);
+    CopyMem (EfiLoadOptionPtr, DevicePathNode, NodeLength);
+    EfiLoadOptionPtr += NodeLength;
+    DevicePathNode = NextDevicePathNode (DevicePathNode);
+  }
+
+  // Set the End Device Path Type
+  SetDevicePathEndNode (EfiLoadOptionPtr);
+  EfiLoadOptionPtr += sizeof(EFI_DEVICE_PATH);
+
+  // Fill the Optional Data
+  if (BdsLoadOption->OptionalDataSize > 0) {
+    CopyMem (EfiLoadOptionPtr, BdsLoadOption->OptionalData, BdsLoadOption->OptionalDataSize);
+  }
+
+  // Case where the fields have been updated
+  if (OldLoadOption) {
+    // Now, the old data has been copied to the new allocated packed structure, we need to update the pointers of BdsLoadOption
+    BootOptionParseLoadOption (BdsLoadOption->LoadOption, BdsLoadOption->LoadOptionSize, &BdsLoadOption);
+    // Free the old packed structure
+    FreePool (OldLoadOption);
+  }
+
+  // Create/Update Boot#### environment variable
+  UnicodeSPrint (BootVariableName, 9 * sizeof(CHAR16), L"Boot%04X", BdsLoadOption->LoadOptionIndex);
+  Status = gRT->SetVariable (
+      BootVariableName,
+      &gEfiGlobalVariableGuid,
+      EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
+      BdsLoadOption->LoadOptionSize,
+      BdsLoadOption->LoadOption
+      );
+
+  // When it is a new entry we must add the entry to the BootOrder
+  if (OldLoadOption == NULL) {
+    // Add the new Boot Index to the list
+    Status = GetGlobalEnvironmentVariable (L"BootOrder", NULL, &BootOrderSize, (VOID**)&BootOrder);
+    if (!EFI_ERROR(Status)) {
+      BootOrder = ReallocatePool (BootOrderSize, BootOrderSize + sizeof(UINT16), BootOrder);
+      // Add the new index at the end
+      BootOrder[BootOrderSize / sizeof(UINT16)] = BdsLoadOption->LoadOptionIndex;
+      BootOrderSize += sizeof(UINT16);
+    } else {
+      // BootOrder does not exist. Create it
+      BootOrderSize = sizeof(UINT16);
+      BootOrder = &(BdsLoadOption->LoadOptionIndex);
+    }
+
+    // Update (or Create) the BootOrder environment variable
+    gRT->SetVariable (
+        L"BootOrder",
+        &gEfiGlobalVariableGuid,
+        EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
+        BootOrderSize,
+        BootOrder
+        );
+    DEBUG((EFI_D_ERROR,"Create %s\n",BootVariableName));
+
+    // Free memory allocated by GetGlobalEnvironmentVariable
+    if (!EFI_ERROR(Status)) {
+      FreePool (BootOrder);
+    }
+  } else {
+    DEBUG((EFI_D_ERROR,"Update %s\n",BootVariableName));
+  }
+
+  return EFI_SUCCESS;
+}
+
+UINT16
+BootOptionAllocateBootIndex (
+  VOID
+  )
+{
+  EFI_STATUS        Status;
+  UINTN             Index;
+  UINT32            BootIndex;
+  UINT16            *BootOrder;
+  UINTN             BootOrderSize;
+  BOOLEAN           Found;
+
+  // Get the Boot Option Order from the environment variable
+  Status = GetGlobalEnvironmentVariable (L"BootOrder", NULL, &BootOrderSize, (VOID**)&BootOrder);
+  if (!EFI_ERROR(Status)) {
+    for (BootIndex = 0; BootIndex <= 0xFFFF; BootIndex++) {
+      Found = FALSE;
+      for (Index = 0; Index < BootOrderSize / sizeof (UINT16); Index++) {
+        if (BootOrder[Index] == BootIndex) {
+          Found = TRUE;
+          break;
+        }
+      }
+      if (!Found) {
+        return BootIndex;
+      }
+    }
+    FreePool (BootOrder);
+  }
+  // Return the first index
+  return 0;
+}