[9/9] ArmPlatformPkg/Bds: add local FDT config

Message ID 1371801922-15142-10-git-send-email-ryan.harkin@linaro.org
State New
Headers show

Commit Message

Ryan Harkin June 21, 2013, 8:05 a.m.
This patch adds a local FDT config option to each boot device.

Bds was using a global FDT config for all boot devices of Loader Type = 2.

I've added a new Loader Type = 3 that stores an device path to an FDT
file this is only used for this boot device.

This change will invalidate any existing configs stored on the board due
to the change in structure sizes to store the boot device configurations.

Signed-off-by: Ryan Harkin <ryan.harkin@linaro.org>
---
 ArmPlatformPkg/ArmPlatformPkg.dec      |    2 +
 ArmPlatformPkg/Bds/Bds.c               |   30 +++-
 ArmPlatformPkg/Bds/Bds.inf             |    1 +
 ArmPlatformPkg/Bds/BdsInternal.h       |   11 +-
 ArmPlatformPkg/Bds/BootMenu.c          |  246 +++++++++++++++++++++++---------
 ArmPlatformPkg/Bds/BootOption.c        |   46 ++++--
 ArmPlatformPkg/Bds/BootOptionSupport.c |   17 ++-
 7 files changed, 254 insertions(+), 99 deletions(-)

Patch

diff --git a/ArmPlatformPkg/ArmPlatformPkg.dec b/ArmPlatformPkg/ArmPlatformPkg.dec
index 3e2900b..e672ce2 100644
--- a/ArmPlatformPkg/ArmPlatformPkg.dec
+++ b/ArmPlatformPkg/ArmPlatformPkg.dec
@@ -136,6 +136,8 @@ 
   gArmPlatformTokenSpaceGuid.PcdDefaultBootType|0|UINT32|0x00000010
   gArmPlatformTokenSpaceGuid.PcdFdtDevicePath|L""|VOID*|0x00000011
   
+  gArmPlatformTokenSpaceGuid.PcdDefaultFdtLocalDevicePath|L""|VOID*|0x0000002E
+
   ## Timeout value for displaying progressing bar in before boot OS.
   #  According to UEFI 2.0 spec, the default TimeOut should be 0xffff.
   gArmPlatformTokenSpaceGuid.PcdPlatformBootTimeOut|0xffff|UINT16|0x0000001A
diff --git a/ArmPlatformPkg/Bds/Bds.c b/ArmPlatformPkg/Bds/Bds.c
index 2801fac..d0fc6a3 100644
--- a/ArmPlatformPkg/Bds/Bds.c
+++ b/ArmPlatformPkg/Bds/Bds.c
@@ -223,8 +223,10 @@  DefineDefaultBootEntries (
   ARM_BDS_LOADER_ARGUMENTS*           BootArguments;
   ARM_BDS_LOADER_TYPE                 BootType;
   EFI_DEVICE_PATH*                    InitrdPath;
+  EFI_DEVICE_PATH*                    FdtLocalPath;
   UINTN                               CmdLineSize;
   UINTN                               InitrdSize;
+  UINTN                               FdtLocalSize;
 
   //
   // If Boot Order does not exist then create a default entry
@@ -262,17 +264,30 @@  DefineDefaultBootEntries (
     if (BootDevicePath != NULL) {
       BootType = (ARM_BDS_LOADER_TYPE)PcdGet32 (PcdDefaultBootType);
 
-      if ((BootType == BDS_LOADER_KERNEL_LINUX_ATAG) || (BootType == BDS_LOADER_KERNEL_LINUX_FDT)) {
+      if ((BootType == BDS_LOADER_KERNEL_LINUX_ATAG) || (BootType == BDS_LOADER_KERNEL_LINUX_GLOBAL_FDT) || (BootType == BDS_LOADER_KERNEL_LINUX_LOCAL_FDT)) {
         CmdLineSize = AsciiStrSize ((CHAR8*)PcdGetPtr(PcdDefaultBootArgument));
         InitrdPath = EfiDevicePathFromTextProtocol->ConvertTextToDevicePath ((CHAR16*)PcdGetPtr(PcdDefaultBootInitrdPath));
         InitrdSize = GetDevicePathSize (InitrdPath);
+        if (BootType == BDS_LOADER_KERNEL_LINUX_LOCAL_FDT) {
+          FdtLocalPath = EfiDevicePathFromTextProtocol->ConvertTextToDevicePath ((CHAR16*)PcdGetPtr(PcdDefaultFdtLocalDevicePath));
+          FdtLocalSize = GetDevicePathSize (FdtLocalPath);
+        } else {
+          FdtLocalPath = NULL;
+          FdtLocalSize = 0;
+        }
 
-        BootArguments = (ARM_BDS_LOADER_ARGUMENTS*)AllocatePool (sizeof(ARM_BDS_LOADER_ARGUMENTS) + CmdLineSize + InitrdSize);
-        BootArguments->LinuxArguments.CmdLineSize = CmdLineSize;
-        BootArguments->LinuxArguments.InitrdSize = InitrdSize;
-
-        CopyMem ((VOID*)(BootArguments + 1), (CHAR8*)PcdGetPtr(PcdDefaultBootArgument), CmdLineSize);
-        CopyMem ((VOID*)((UINTN)(BootArguments + 1) + CmdLineSize), InitrdPath, InitrdSize);
+        BootArguments = (ARM_BDS_LOADER_ARGUMENTS*)AllocatePool (sizeof(ARM_BDS_LOADER_ARGUMENTS) + CmdLineSize + InitrdSize + FdtLocalSize);
+        if ( BootArguments != NULL ) {
+          BootArguments->LinuxArguments.CmdLineSize = CmdLineSize;
+          BootArguments->LinuxArguments.InitrdSize = InitrdSize;
+          BootArguments->LinuxArguments.FdtLocalSize = FdtLocalSize;
+
+          CopyMem ((VOID*)(BootArguments + 1), (CHAR8*)PcdGetPtr(PcdDefaultBootArgument), CmdLineSize);
+          CopyMem ((VOID*)((UINTN)(BootArguments + 1) + CmdLineSize), InitrdPath, InitrdSize);
+          CopyMem ((VOID*)((UINTN)(BootArguments + 1) + CmdLineSize + InitrdSize), FdtLocalPath, FdtLocalSize);
+        }
+        FreePool (FdtLocalPath);
+        FreePool (InitrdPath);
       } else {
         BootArguments = NULL;
       }
@@ -285,6 +300,7 @@  DefineDefaultBootEntries (
         &BdsLoadOption
         );
       FreePool (BdsLoadOption);
+      FreePool (BootDevicePath);
     } else {
       Status = EFI_UNSUPPORTED;
     }
diff --git a/ArmPlatformPkg/Bds/Bds.inf b/ArmPlatformPkg/Bds/Bds.inf
index 5faa4b2..8b3f1d2 100644
--- a/ArmPlatformPkg/Bds/Bds.inf
+++ b/ArmPlatformPkg/Bds/Bds.inf
@@ -68,6 +68,7 @@ 
   gArmPlatformTokenSpaceGuid.PcdDefaultBootArgument
   gArmPlatformTokenSpaceGuid.PcdDefaultBootType
   gArmPlatformTokenSpaceGuid.PcdFdtDevicePath
+  gArmPlatformTokenSpaceGuid.PcdDefaultFdtLocalDevicePath
   gArmPlatformTokenSpaceGuid.PcdPlatformBootTimeOut
   gArmPlatformTokenSpaceGuid.PcdDefaultConInPaths
   gArmPlatformTokenSpaceGuid.PcdDefaultConOutPaths
diff --git a/ArmPlatformPkg/Bds/BdsInternal.h b/ArmPlatformPkg/Bds/BdsInternal.h
index a8d836d..7830066 100644
--- a/ArmPlatformPkg/Bds/BdsInternal.h
+++ b/ArmPlatformPkg/Bds/BdsInternal.h
@@ -38,6 +38,11 @@ 
 #define BOOT_DEVICE_OPTION_MAX        300
 #define BOOT_DEVICE_ADDRESS_MAX       (sizeof(L"0x0000000000000000"))
 
+// Length of the buffer used to hold the user input for the main menu
+// This includes the NULL terminator
+// 1 chars + newline + NULL gives room for up to 9 boot device configs
+#define BOOT_OPTION_LEN   3
+
 #define ARM_BDS_OPTIONAL_DATA_SIGNATURE   SIGNATURE_32('a', 'b', 'o', 'd')
 
 #define IS_ARM_BDS_BOOTENTRY(ptr)  (ReadUnaligned32 ((CONST UINT32*)&((ARM_BDS_LOADER_OPTIONAL_DATA*)((ptr)->OptionalData))->Header.Signature) == ARM_BDS_OPTIONAL_DATA_SIGNATURE)
@@ -48,16 +53,20 @@ 
 typedef enum {
     BDS_LOADER_EFI_APPLICATION = 0,
     BDS_LOADER_KERNEL_LINUX_ATAG,
-    BDS_LOADER_KERNEL_LINUX_FDT,
+    BDS_LOADER_KERNEL_LINUX_GLOBAL_FDT,
+    BDS_LOADER_KERNEL_LINUX_LOCAL_FDT,
 } ARM_BDS_LOADER_TYPE;
 
 typedef struct {
   UINT16                     CmdLineSize;
   UINT16                     InitrdSize;
 
+  UINT16                     FdtLocalSize;
+
   // These following fields have variable length and are packed:
   //CHAR8                      *CmdLine;
   //EFI_DEVICE_PATH_PROTOCOL   *InitrdPathList;
+  //EFI_DEVICE_PATH_PROTOCOL   *FdtLocalPathList;
 } ARM_BDS_LINUX_ARGUMENTS;
 
 typedef union {
diff --git a/ArmPlatformPkg/Bds/BootMenu.c b/ArmPlatformPkg/Bds/BootMenu.c
index 2b32e83..de2117b 100644
--- a/ArmPlatformPkg/Bds/BootMenu.c
+++ b/ArmPlatformPkg/Bds/BootMenu.c
@@ -128,9 +128,12 @@  BootMenuAddBootOption (
   EFI_DEVICE_PATH_PROTOCOL  *DevicePathNodes;
   EFI_DEVICE_PATH_PROTOCOL  *InitrdPathNodes;
   EFI_DEVICE_PATH_PROTOCOL  *InitrdPath;
+  EFI_DEVICE_PATH_PROTOCOL  *FdtLocalPathNode;
+  EFI_DEVICE_PATH_PROTOCOL  *FdtLocalPath;
   UINTN                     CmdLineSize;
   BOOLEAN                   InitrdSupport;
   UINTN                     InitrdSize;
+  UINTN                     FdtLocalSize;
 
   Attributes                = 0;
   SupportedBootDevice = NULL;
@@ -155,12 +158,12 @@  BootMenuAddBootOption (
     goto EXIT;
   }
 
-  if ((BootType == BDS_LOADER_KERNEL_LINUX_ATAG) || (BootType == BDS_LOADER_KERNEL_LINUX_FDT)) {
+  if ((BootType == BDS_LOADER_KERNEL_LINUX_ATAG) || (BootType == BDS_LOADER_KERNEL_LINUX_GLOBAL_FDT) || (BootType == BDS_LOADER_KERNEL_LINUX_LOCAL_FDT)) {
     Print(L"Add an initrd: ");
     Status = GetHIInputBoolean (&InitrdSupport);
     if (EFI_ERROR(Status)) {
       Status = EFI_ABORTED;
-      goto EXIT;
+      goto FREE_DEVICE_PATH;
     }
 
     if (InitrdSupport) {
@@ -168,7 +171,7 @@  BootMenuAddBootOption (
       Status = SupportedBootDevice->Support->CreateDevicePathNode (L"initrd", &InitrdPathNodes, NULL, NULL);
       if (EFI_ERROR(Status) && Status != EFI_NOT_FOUND) { // EFI_NOT_FOUND is returned on empty input string, but we can boot without an initrd
         Status = EFI_ABORTED;
-        goto EXIT;
+        goto FREE_DEVICE_PATH;
       }
 
       if (InitrdPathNodes != NULL) {
@@ -176,7 +179,7 @@  BootMenuAddBootOption (
         InitrdPath = AppendDevicePath (SupportedBootDevice->DevicePathProtocol, (CONST EFI_DEVICE_PATH_PROTOCOL *)InitrdPathNodes);
         if (InitrdPath == NULL) {
           Status = EFI_OUT_OF_RESOURCES;
-          goto EXIT;
+          goto FREE_DEVICE_PATH;
         }
       } else {
         InitrdPath = NULL;
@@ -192,15 +195,37 @@  BootMenuAddBootOption (
       goto FREE_DEVICE_PATH;
     }
 
-    CmdLineSize = AsciiStrSize (CmdLine);
-    InitrdSize = GetDevicePathSize (InitrdPath);
+    if (BootType == BDS_LOADER_KERNEL_LINUX_LOCAL_FDT) {
+      // Create the specific device path node
+      Status = SupportedBootDevice->Support->CreateDevicePathNode (L"local FDT", &FdtLocalPathNode, NULL, NULL);
+      if (EFI_ERROR(Status) || (FdtLocalPathNode == NULL)) {
+        Status = EFI_ABORTED;
+        goto FREE_DEVICE_PATH;
+      }
 
-    BootArguments = (ARM_BDS_LOADER_ARGUMENTS*)AllocatePool (sizeof(ARM_BDS_LOADER_ARGUMENTS) + CmdLineSize + InitrdSize);
+      if (FdtLocalPathNode != NULL) {
+        // Append the Device Path node to the select device path
+        FdtLocalPath = AppendDevicePathNode (SupportedBootDevice->DevicePathProtocol, (CONST EFI_DEVICE_PATH_PROTOCOL *)FdtLocalPathNode);
+      } else {
+        FdtLocalPath = NULL;
+      }
+    } else {
+      FdtLocalPath = NULL;
+    }
 
-    BootArguments->LinuxArguments.CmdLineSize = CmdLineSize;
-    BootArguments->LinuxArguments.InitrdSize = InitrdSize;
-    CopyMem ((VOID*)(&BootArguments->LinuxArguments + 1), CmdLine, CmdLineSize);
-    CopyMem ((VOID*)((UINTN)(&BootArguments->LinuxArguments + 1) + CmdLineSize), InitrdPath, InitrdSize);
+    CmdLineSize = AsciiStrSize (CmdLine);
+    InitrdSize = GetDevicePathSize (InitrdPath);
+    FdtLocalSize = GetDevicePathSize (FdtLocalPath);
+
+    BootArguments = (ARM_BDS_LOADER_ARGUMENTS*)AllocatePool (sizeof(ARM_BDS_LOADER_ARGUMENTS) + CmdLineSize + InitrdSize + FdtLocalSize);
+    if ( BootArguments != NULL ) {
+      BootArguments->LinuxArguments.CmdLineSize = CmdLineSize;
+      BootArguments->LinuxArguments.InitrdSize = InitrdSize;
+      BootArguments->LinuxArguments.FdtLocalSize = FdtLocalSize;
+      CopyMem ((VOID*)(&BootArguments->LinuxArguments + 1), CmdLine, CmdLineSize);
+      CopyMem ((VOID*)((UINTN)(&BootArguments->LinuxArguments + 1) + CmdLineSize), InitrdPath, InitrdSize);
+      CopyMem ((VOID*)((UINTN)(&BootArguments->LinuxArguments + 1) + CmdLineSize + InitrdSize), FdtLocalPath, FdtLocalSize);
+    }
   } else {
     BootArguments = NULL;
   }
@@ -214,6 +239,10 @@  BootMenuAddBootOption (
 
   // Create new entry
   BdsLoadOptionEntry = (BDS_LOAD_OPTION_ENTRY*)AllocatePool (sizeof(BDS_LOAD_OPTION_ENTRY));
+  if ( BdsLoadOptionEntry == NULL ) {
+    Status = EFI_ABORTED;
+    goto FREE_DEVICE_PATH;
+  }
   Status = BootOptionCreate (Attributes, BootDescription, DevicePath, BootType, BootArguments, &BdsLoadOptionEntry->BdsLoadOption);
   if (!EFI_ERROR(Status)) {
     InsertTailList (BootOptionsList, &BdsLoadOptionEntry->Link);
@@ -274,7 +303,7 @@  BootMenuSelectBootOption (
       Print(L"\t- %s\n",DevicePathTxt);
       OptionalData = BdsLoadOption->OptionalData;
       LoaderType = (ARM_BDS_LOADER_TYPE)ReadUnaligned32 ((CONST UINT32*)&OptionalData->Header.LoaderType);
-      if ((LoaderType == BDS_LOADER_KERNEL_LINUX_ATAG) || (LoaderType == BDS_LOADER_KERNEL_LINUX_FDT)) {
+      if ((LoaderType == BDS_LOADER_KERNEL_LINUX_ATAG) || (LoaderType == BDS_LOADER_KERNEL_LINUX_GLOBAL_FDT) || (LoaderType == BDS_LOADER_KERNEL_LINUX_LOCAL_FDT)) {
         Print (L"\t- Arguments: %a\n",&OptionalData->Arguments.LinuxArguments + 1);
       }
 
@@ -366,14 +395,19 @@  BootMenuUpdateBootOption (
   CHAR8                         CmdLine[BOOT_DEVICE_OPTION_MAX];
   EFI_DEVICE_PATH               *DevicePath;
   EFI_DEVICE_PATH               *TempInitrdPath;
+  EFI_DEVICE_PATH               *TempFdtLocalPath;
   ARM_BDS_LOADER_TYPE           BootType;
   ARM_BDS_LOADER_OPTIONAL_DATA* OptionalData;
   ARM_BDS_LINUX_ARGUMENTS*      LinuxArguments;
   EFI_DEVICE_PATH               *InitrdPathNodes;
   EFI_DEVICE_PATH               *InitrdPath;
   UINTN                         InitrdSize;
+  EFI_DEVICE_PATH               *FdtLocalPathNode;
+  EFI_DEVICE_PATH               *FdtLocalPath;
+  UINTN                         FdtLocalSize;
   UINTN                         CmdLineSize;
   BOOLEAN                       InitrdSupport;
+  BOOLEAN                       FdtLocalSupport;
 
   Status = BootMenuSelectBootOption (BootOptionsList, UPDATE_BOOT_ENTRY, TRUE, &BootOptionEntry);
   if (EFI_ERROR(Status)) {
@@ -397,12 +431,65 @@  BootMenuUpdateBootOption (
   OptionalData = BootOption->OptionalData;
   BootType = (ARM_BDS_LOADER_TYPE)ReadUnaligned32 ((UINT32 *)(&OptionalData->Header.LoaderType));
 
-  if ((BootType == BDS_LOADER_KERNEL_LINUX_ATAG) || (BootType == BDS_LOADER_KERNEL_LINUX_FDT)) {
+  if ((BootType == BDS_LOADER_KERNEL_LINUX_ATAG) || (BootType == BDS_LOADER_KERNEL_LINUX_GLOBAL_FDT) || (BootType == BDS_LOADER_KERNEL_LINUX_LOCAL_FDT)) {
     LinuxArguments = &OptionalData->Arguments.LinuxArguments;
 
     CmdLineSize = ReadUnaligned16 ((CONST UINT16*)&LinuxArguments->CmdLineSize);
 
     InitrdSize = ReadUnaligned16 ((CONST UINT16*)&LinuxArguments->InitrdSize);
+    FdtLocalSize = ReadUnaligned16 ((CONST UINT16*)&LinuxArguments->FdtLocalSize);
+
+    if (BootType == BDS_LOADER_KERNEL_LINUX_LOCAL_FDT) {
+      if (FdtLocalSize > 0) {
+        Print(L"Keep the local FDT: ");
+      } else {
+        Print(L"Add a local FDT: ");
+      }
+      Status = GetHIInputBoolean (&FdtLocalSupport);
+      if (EFI_ERROR(Status)) {
+        Status = EFI_ABORTED;
+        goto EXIT;
+      }
+      if (FdtLocalSupport && BootType == BDS_LOADER_KERNEL_LINUX_LOCAL_FDT) {
+        if (FdtLocalSize > 0) {
+          // Case we update the FDT local device path
+          Status = DeviceSupport->UpdateDevicePathNode ((EFI_DEVICE_PATH*)((UINTN)(LinuxArguments + 1) + CmdLineSize + InitrdSize), L"local FDT", &FdtLocalPath, NULL, NULL);
+          if (EFI_ERROR(Status) && Status != EFI_NOT_FOUND) {// EFI_NOT_FOUND is returned on empty input string
+            Status = EFI_ABORTED;
+            goto EXIT;
+          }
+          FdtLocalSize = GetDevicePathSize (FdtLocalPath);
+        } else {
+          // Case we create the FdtLocal device path
+
+          Status = DeviceSupport->CreateDevicePathNode (L"local FDT", &FdtLocalPathNode, NULL, NULL);
+          if (EFI_ERROR(Status) || (FdtLocalPathNode == NULL)) {
+            Status = EFI_ABORTED;
+            goto EXIT;
+          }
+
+          if (FdtLocalPathNode != NULL) {
+            // Duplicate Linux kernel Device Path
+            TempFdtLocalPath = DuplicateDevicePath (BootOption->FilePathList);
+            if ( TempFdtLocalPath != NULL ) {
+              // Replace Linux kernel Node by EndNode
+              SetDevicePathEndNode (GetLastDevicePathNode (TempFdtLocalPath));
+              // Append the Device Path node to the select device path
+              FdtLocalPath = AppendDevicePathNode (TempFdtLocalPath, (CONST EFI_DEVICE_PATH_PROTOCOL *)FdtLocalPathNode);
+              FreePool (TempFdtLocalPath);
+              FdtLocalSize = GetDevicePathSize (FdtLocalPath);
+            }
+          } else {
+            FdtLocalPath = NULL;
+          }
+        }
+      } else {
+        FdtLocalSize = 0;
+      }
+    } else {
+      FdtLocalSupport = FALSE;
+    }
+
     if (InitrdSize > 0) {
       Print(L"Keep the initrd: ");
     } else {
@@ -467,11 +554,15 @@  BootMenuUpdateBootOption (
 
     CmdLineSize = AsciiStrSize (CmdLine);
 
-    BootArguments = (ARM_BDS_LOADER_ARGUMENTS*)AllocatePool(sizeof(ARM_BDS_LOADER_ARGUMENTS) + CmdLineSize + InitrdSize);
-    BootArguments->LinuxArguments.CmdLineSize = CmdLineSize;
-    BootArguments->LinuxArguments.InitrdSize = InitrdSize;
-    CopyMem (&BootArguments->LinuxArguments + 1, CmdLine, CmdLineSize);
-    CopyMem ((VOID*)((UINTN)(&BootArguments->LinuxArguments + 1) + CmdLineSize), InitrdPath, InitrdSize);
+    BootArguments = (ARM_BDS_LOADER_ARGUMENTS*)AllocatePool(sizeof(ARM_BDS_LOADER_ARGUMENTS) + CmdLineSize + InitrdSize + FdtLocalSize);
+    if ( BootArguments != NULL ) {
+      BootArguments->LinuxArguments.CmdLineSize = CmdLineSize;
+      BootArguments->LinuxArguments.InitrdSize = InitrdSize;
+      BootArguments->LinuxArguments.FdtLocalSize = FdtLocalSize;
+      CopyMem (&BootArguments->LinuxArguments + 1, CmdLine, CmdLineSize);
+      CopyMem ((VOID*)((UINTN)(&BootArguments->LinuxArguments + 1) + CmdLineSize), InitrdPath, InitrdSize);
+      CopyMem ((VOID*)((UINTN)(&BootArguments->LinuxArguments + 1) + CmdLineSize + InitrdSize), FdtLocalPath, FdtLocalSize);
+    }
   } else {
     BootArguments = NULL;
   }
@@ -648,19 +739,20 @@  BootMenuMain (
   VOID
   )
 {
-  LIST_ENTRY                    BootOptionsList;
-  UINTN                         OptionCount;
-  UINTN                         HardCodedOptionCount;
-  UINTN                         BootOptionCount;
-  EFI_STATUS                    Status;
-  LIST_ENTRY*                   Entry;
-  BDS_LOAD_OPTION*              BootOption;
-  UINTN                         BootOptionSelected;
-  UINTN                         Index;
-  UINTN                         BootMainEntryCount;
-  CHAR8                         BootOptionSelectedStr[32];
-  EFI_DEVICE_PATH_PROTOCOL*     DefaultFdtDevicePath;
-  UINTN                         FdtDevicePathSize;
+  LIST_ENTRY                        BootOptionsList;
+  UINTN                             OptionCount;
+  UINTN                             BootOptionCount;
+  EFI_STATUS                        Status;
+  LIST_ENTRY*                       Entry;
+  BDS_LOAD_OPTION*                  BootOption;
+  UINTN                             BootOptionSelected;
+  UINTN                             Index;
+  UINTN                             BootMainEntryCount;
+  CHAR8                             BootOptionSelectedStr[BOOT_OPTION_LEN];
+  EFI_DEVICE_PATH_PROTOCOL*         DefaultFdtDevicePath;
+  UINTN                             FdtDevicePathSize;
+  EFI_DEVICE_PATH_TO_TEXT_PROTOCOL* DevicePathToTextProtocol;
+  CHAR16*                           DevicePathTxt;
 
 
   BootOption              = NULL;
@@ -682,11 +774,10 @@  BootMenuMain (
 
       Print(L"[%d] %s\n", OptionCount, BootOption->Description);
 
-      DEBUG_CODE_BEGIN();
-        CHAR16*                           DevicePathTxt;
-        EFI_DEVICE_PATH_TO_TEXT_PROTOCOL* DevicePathToTextProtocol;
+      //DEBUG_CODE_BEGIN();
         ARM_BDS_LOADER_OPTIONAL_DATA*     OptionalData;
         UINTN                             CmdLineSize;
+        UINTN                             InitrdSize;
         ARM_BDS_LOADER_TYPE               LoaderType;
 
         Status = gBS->LocateProtocol (&gEfiDevicePathToTextProtocolGuid, NULL, (VOID **)&DevicePathToTextProtocol);
@@ -703,7 +794,7 @@  BootMenuMain (
         if (IS_ARM_BDS_BOOTENTRY (BootOption)) {
           OptionalData = BootOption->OptionalData;
           LoaderType = (ARM_BDS_LOADER_TYPE)ReadUnaligned32 ((CONST UINT32*)&OptionalData->Header.LoaderType);
-          if ((LoaderType == BDS_LOADER_KERNEL_LINUX_ATAG) || (LoaderType == BDS_LOADER_KERNEL_LINUX_FDT)) {
+          if ((LoaderType == BDS_LOADER_KERNEL_LINUX_ATAG) || (LoaderType == BDS_LOADER_KERNEL_LINUX_GLOBAL_FDT) || (LoaderType == BDS_LOADER_KERNEL_LINUX_LOCAL_FDT)) {
             if (ReadUnaligned16 (&OptionalData->Arguments.LinuxArguments.InitrdSize) > 0) {
               CmdLineSize = ReadUnaligned16 (&OptionalData->Arguments.LinuxArguments.CmdLineSize);
               DevicePathTxt = DevicePathToTextProtocol->ConvertDevicePathToText (
@@ -722,16 +813,28 @@  BootMenuMain (
               Print(L"\t- LoaderType: Linux kernel with ATAG support\n");
               break;
 
-            case BDS_LOADER_KERNEL_LINUX_FDT:
-              Print(L"\t- LoaderType: Linux kernel with FDT support\n");
+            case BDS_LOADER_KERNEL_LINUX_GLOBAL_FDT:
+              Print(L"\t- LoaderType: Linux kernel with global FDT support\n");
+              break;
+            case BDS_LOADER_KERNEL_LINUX_LOCAL_FDT:
+              if (ReadUnaligned16 (&OptionalData->Arguments.LinuxArguments.FdtLocalSize) > 0) {
+                CmdLineSize = ReadUnaligned16 (&OptionalData->Arguments.LinuxArguments.CmdLineSize);
+                InitrdSize  = ReadUnaligned16 (&OptionalData->Arguments.LinuxArguments.InitrdSize);
+                DevicePathTxt = DevicePathToTextProtocol->ConvertDevicePathToText (
+                    GetAlignedDevicePath ((EFI_DEVICE_PATH*)((UINTN)(&OptionalData->Arguments.LinuxArguments + 1) + CmdLineSize + InitrdSize)), TRUE, TRUE);
+                Print(L"\t- FDT: %s\n", DevicePathTxt);
+              } else {
+                Print(L"\t- FDT: error, local FDT not specified, using global FDT\n");
+              }
+              Print(L"\t- LoaderType: Linux kernel with Local FDT\n");
               break;
-
             default:
               Print(L"\t- LoaderType: Not recognized (%d)\n", LoaderType);
+              break;
           }
         }
         FreePool(DevicePathTxt);
-      DEBUG_CODE_END();
+      //DEBUG_CODE_END();
 
       OptionCount++;
     }
@@ -739,55 +842,56 @@  BootMenuMain (
 
     // Display the global FDT config
     Print(L"-----------------------\n");
-      {
-        EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL*   EfiDevicePathFromTextProtocol;
-        EFI_DEVICE_PATH_PROTOCOL*             FdtDevicePath;
-        EFI_DEVICE_PATH_TO_TEXT_PROTOCOL*     DevicePathToTextProtocol;
-        CHAR16*                               DevicePathTxt;
+    {
+      EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL*   EfiDevicePathFromTextProtocol;
+      EFI_DEVICE_PATH_PROTOCOL*             FdtDevicePath;
 
-        // Get the default FDT device path
-        Status = gBS->LocateProtocol (&gEfiDevicePathFromTextProtocolGuid, NULL, (VOID **)&EfiDevicePathFromTextProtocol);
-        ASSERT_EFI_ERROR(Status);
-        DefaultFdtDevicePath = EfiDevicePathFromTextProtocol->ConvertTextToDevicePath ((CHAR16*)PcdGetPtr(PcdFdtDevicePath));
+      // Get the default FDT device path
+      Status = gBS->LocateProtocol (&gEfiDevicePathFromTextProtocolGuid, NULL, (VOID **)&EfiDevicePathFromTextProtocol);
+      ASSERT_EFI_ERROR(Status);
+      DefaultFdtDevicePath = EfiDevicePathFromTextProtocol->ConvertTextToDevicePath ((CHAR16*)PcdGetPtr(PcdFdtDevicePath));
 
-        // Get the FDT device path
-        FdtDevicePathSize = GetDevicePathSize (DefaultFdtDevicePath);
-        Status = GetEnvironmentVariable ((CHAR16 *)L"Fdt", &gArmGlobalVariableGuid, DefaultFdtDevicePath, &FdtDevicePathSize, (VOID **)&FdtDevicePath);
+      // Get the FDT device path
+      FdtDevicePathSize = GetDevicePathSize (DefaultFdtDevicePath);
+      Status = GetEnvironmentVariable ((CHAR16 *)L"Fdt", &gArmGlobalVariableGuid, DefaultFdtDevicePath, &FdtDevicePathSize, (VOID **)&FdtDevicePath);
 
-        // Convert FdtDevicePath to text
-        if (EFI_ERROR(Status)) {
-          DevicePathTxt = L"not configured";
-        } else {
-          Status = gBS->LocateProtocol (&gEfiDevicePathToTextProtocolGuid, NULL, (VOID **)&DevicePathToTextProtocol);
-          DevicePathTxt = DevicePathToTextProtocol->ConvertDevicePathToText ( FdtDevicePath, TRUE, TRUE );
-        }
-        Print(L"Global FDT Config\n\t- %s\n", DevicePathTxt);
+      // Convert FdtDevicePath to text
+      if (EFI_ERROR(Status)) {
+        DevicePathTxt = L"not configured";
+      } else {
+        Status = gBS->LocateProtocol (&gEfiDevicePathToTextProtocolGuid, NULL, (VOID **)&DevicePathToTextProtocol);
+        DevicePathTxt = DevicePathToTextProtocol->ConvertDevicePathToText ( FdtDevicePath, TRUE, TRUE );
       }
+      Print(L"Global FDT Config\n\t- %s\n", DevicePathTxt);
+      FreePool(DevicePathTxt);
+      FreePool(DefaultFdtDevicePath);
+    }
 
     // Display the hardcoded Boot entries
     Print(L"-----------------------\n");
     for (Index = 0; Index < BootMainEntryCount; Index++) {
-      Print(L"[%c] %s\n", ('a'+Index), BootMainEntries[Index]);
+      Print(L"[%c] %s\n", ('a' + Index), BootMainEntries[Index]);
       OptionCount++;
     }
-    HardCodedOptionCount=Index;
 
     // Request the boot entry from the user
     BootOptionSelected = 0;
     while (BootOptionSelected == 0) {
       Print(L"Start: ");
-      Status = GetHIInputAscii (BootOptionSelectedStr,8);
+      Status = GetHIInputAscii (BootOptionSelectedStr, BOOT_OPTION_LEN);
 
-      if (BootOptionSelectedStr[0]-'0' <= OptionCount) {
-        BootOptionSelected = BootOptionSelectedStr[0] - '0';
-      } else if (BootOptionSelectedStr[0]-'a' <= HardCodedOptionCount) {
-        BootOptionSelected = BootOptionCount + 1 + BootOptionSelectedStr[0] - 'a';
-      }
+      if (!EFI_ERROR(Status)) {
+        if ((BootOptionSelectedStr[0] - '0') < OptionCount) {
+          BootOptionSelected = BootOptionSelectedStr[0] - '0';
+        } else if ((BootOptionSelectedStr[0] - 'a') < BootMainEntryCount) {
+          BootOptionSelected = BootOptionCount + 1 + BootOptionSelectedStr[0] - 'a';
+        }
 
-      if (EFI_ERROR(Status) || (BootOptionSelected == 0) || (BootOptionSelected > OptionCount)) {
-        Print(L"Invalid input (max %d)\n",(OptionCount-1));
-        BootOptionSelected = 0;
-      }
+        if ((BootOptionSelected == 0) || (BootOptionSelected > OptionCount)) {
+          Print(L"Invalid input, please choose a menu option from the list above\n");
+          BootOptionSelected = 0;
+        }
+      }
     }
 
     // Start the selected entry
diff --git a/ArmPlatformPkg/Bds/BootOption.c b/ArmPlatformPkg/Bds/BootOption.c
index ee0301e..6b756a3 100644
--- a/ArmPlatformPkg/Bds/BootOption.c
+++ b/ArmPlatformPkg/Bds/BootOption.c
@@ -33,6 +33,7 @@  BootOptionStart (
   UINTN                                 CmdLineSize;
   UINTN                                 InitrdSize;
   EFI_DEVICE_PATH*                      Initrd;
+  UINTN                                 FdtLocalSize;
   UINT16                                LoadOptionIndexSize;
 
   if (IS_ARM_BDS_BOOTENTRY (BootOption)) {
@@ -59,7 +60,7 @@  BootOptionStart (
       Status = BdsBootLinuxAtag (BootOption->FilePathList,
                                  Initrd, // Initrd
                                  (CHAR8*)(LinuxArguments + 1)); // CmdLine
-    } else if (LoaderType == BDS_LOADER_KERNEL_LINUX_FDT) {
+    } else if ((LoaderType == BDS_LOADER_KERNEL_LINUX_GLOBAL_FDT) || (LoaderType == BDS_LOADER_KERNEL_LINUX_LOCAL_FDT)) {
       LinuxArguments = &(OptionalData->Arguments.LinuxArguments);
       CmdLineSize = ReadUnaligned16 ((CONST UINT16*)&LinuxArguments->CmdLineSize);
       InitrdSize = ReadUnaligned16 ((CONST UINT16*)&LinuxArguments->InitrdSize);
@@ -70,17 +71,27 @@  BootOptionStart (
         Initrd = NULL;
       }
 
-      // Get the default FDT device path
-      Status = gBS->LocateProtocol (&gEfiDevicePathFromTextProtocolGuid, NULL, (VOID **)&EfiDevicePathFromTextProtocol);
-      ASSERT_EFI_ERROR(Status);
-      DefaultFdtDevicePath = EfiDevicePathFromTextProtocol->ConvertTextToDevicePath ((CHAR16*)PcdGetPtr(PcdFdtDevicePath));
-
-      // Get the FDT device path
-      FdtDevicePathSize = GetDevicePathSize (DefaultFdtDevicePath);
-      Status = GetEnvironmentVariable ((CHAR16 *)L"Fdt", &gArmGlobalVariableGuid,
-                 DefaultFdtDevicePath, &FdtDevicePathSize, (VOID **)&FdtDevicePath);
-      ASSERT_EFI_ERROR(Status);
+      if (LoaderType == BDS_LOADER_KERNEL_LINUX_LOCAL_FDT) {
+        FdtLocalSize = ReadUnaligned16 ((CONST UINT16*)&LinuxArguments->FdtLocalSize);
 
+        if (FdtLocalSize > 0) {
+          FdtDevicePath = GetAlignedDevicePath ((EFI_DEVICE_PATH*)((UINTN)(LinuxArguments + 1) + CmdLineSize + InitrdSize));
+        } else {
+          FdtDevicePath = NULL;
+        }
+      } else  {
+        // Get the default FDT device path
+        Status = gBS->LocateProtocol (&gEfiDevicePathFromTextProtocolGuid, NULL, (VOID **)&EfiDevicePathFromTextProtocol);
+        ASSERT_EFI_ERROR(Status);
+        DefaultFdtDevicePath = EfiDevicePathFromTextProtocol->ConvertTextToDevicePath ((CHAR16*)PcdGetPtr(PcdFdtDevicePath));
+
+        // Get the FDT device path
+        FdtDevicePathSize = GetDevicePathSize (DefaultFdtDevicePath);
+        Status = GetEnvironmentVariable ((CHAR16 *)L"Fdt", &gArmGlobalVariableGuid,
+                   DefaultFdtDevicePath, &FdtDevicePathSize, (VOID **)&FdtDevicePath);
+        ASSERT_EFI_ERROR(Status);
+        FreePool (DefaultFdtDevicePath);
+      }
       Status = BdsBootLinuxFdt (BootOption->FilePathList,
                                 Initrd, // Initrd
                                 (CHAR8*)(LinuxArguments + 1),
@@ -159,6 +170,7 @@  BootOptionSetFields (
   UINT16                        FilePathListLength;
   UINT8*                        EfiLoadOptionPtr;
   UINT8*                        InitrdPathListPtr;
+  UINT8*                        FdtLocalPathListPtr;
   UINTN                         OptionalDataSize;
   ARM_BDS_LINUX_ARGUMENTS*      DestLinuxArguments;
   ARM_BDS_LINUX_ARGUMENTS*      SrcLinuxArguments;
@@ -170,8 +182,8 @@  BootOptionSetFields (
 
   BootDescriptionSize = StrSize (BootDescription);
   BootOptionalDataSize = sizeof(ARM_BDS_LOADER_OPTIONAL_DATA_HEADER);
-  if ((BootType == BDS_LOADER_KERNEL_LINUX_ATAG) || (BootType == BDS_LOADER_KERNEL_LINUX_FDT)) {
-    BootOptionalDataSize += sizeof(ARM_BDS_LINUX_ARGUMENTS) + BootArguments->LinuxArguments.CmdLineSize + BootArguments->LinuxArguments.InitrdSize;
+  if ((BootType == BDS_LOADER_KERNEL_LINUX_ATAG) || (BootType == BDS_LOADER_KERNEL_LINUX_GLOBAL_FDT) || (BootType == BDS_LOADER_KERNEL_LINUX_LOCAL_FDT)) {
+    BootOptionalDataSize += sizeof(ARM_BDS_LINUX_ARGUMENTS) + BootArguments->LinuxArguments.CmdLineSize + BootArguments->LinuxArguments.InitrdSize + BootArguments->LinuxArguments.FdtLocalSize;
   }
 
   // Compute the size of the FilePath list
@@ -213,12 +225,13 @@  BootOptionSetFields (
 
   OptionalDataSize = sizeof(ARM_BDS_LOADER_OPTIONAL_DATA_HEADER);
 
-  if ((BootType == BDS_LOADER_KERNEL_LINUX_ATAG) || (BootType == BDS_LOADER_KERNEL_LINUX_FDT)) {
+  if ((BootType == BDS_LOADER_KERNEL_LINUX_ATAG) || (BootType == BDS_LOADER_KERNEL_LINUX_GLOBAL_FDT) || (BootType == BDS_LOADER_KERNEL_LINUX_LOCAL_FDT)) {
     SrcLinuxArguments = &(BootArguments->LinuxArguments);
     DestLinuxArguments = &((ARM_BDS_LOADER_OPTIONAL_DATA*)EfiLoadOptionPtr)->Arguments.LinuxArguments;
 
     WriteUnaligned16 ((UINT16 *)&(DestLinuxArguments->CmdLineSize), SrcLinuxArguments->CmdLineSize);
     WriteUnaligned16 ((UINT16 *)&(DestLinuxArguments->InitrdSize), SrcLinuxArguments->InitrdSize);
+    WriteUnaligned16 ((UINT16 *)&(DestLinuxArguments->FdtLocalSize), SrcLinuxArguments->FdtLocalSize);
     OptionalDataSize += sizeof (ARM_BDS_LINUX_ARGUMENTS);
 
     if (SrcLinuxArguments->CmdLineSize > 0) {
@@ -230,6 +243,11 @@  BootOptionSetFields (
       InitrdPathListPtr = (UINT8*)((UINTN)(DestLinuxArguments + 1) + SrcLinuxArguments->CmdLineSize);
       CopyMem (InitrdPathListPtr, (VOID*)((UINTN)(SrcLinuxArguments + 1) + SrcLinuxArguments->CmdLineSize), SrcLinuxArguments->InitrdSize);
     }
+
+    if (SrcLinuxArguments->FdtLocalSize > 0) {
+      FdtLocalPathListPtr = (UINT8*)((UINTN)(DestLinuxArguments + 1) + SrcLinuxArguments->CmdLineSize + SrcLinuxArguments->InitrdSize);
+      CopyMem (FdtLocalPathListPtr, (VOID*)((UINTN)(SrcLinuxArguments + 1) + SrcLinuxArguments->CmdLineSize + SrcLinuxArguments->InitrdSize), SrcLinuxArguments->FdtLocalSize);
+    }
   }
   BootOption->OptionalDataSize = OptionalDataSize;
 
diff --git a/ArmPlatformPkg/Bds/BootOptionSupport.c b/ArmPlatformPkg/Bds/BootOptionSupport.c
index 190169a..c2df7f9 100644
--- a/ArmPlatformPkg/Bds/BootOptionSupport.c
+++ b/ArmPlatformPkg/Bds/BootOptionSupport.c
@@ -25,6 +25,7 @@ 
 #include <Guid/FileSystemInfo.h>
 
 #define IS_DEVICE_PATH_NODE(node,type,subtype) (((node)->Type == (type)) && ((node)->SubType == (subtype)))
+#define LOCAL_FDT_RESPONSE_LEN 2  // 1 character, plus carriage return
 
 EFI_STATUS
 BdsLoadOptionFileSystemList (
@@ -233,7 +234,7 @@  BootDeviceGetType (
   EFI_STATUS Status;
   BOOLEAN IsEfiApp;
   BOOLEAN IsBootLoader;
-  BOOLEAN     HasFDTSupport;
+  CHAR16  FDTType[ LOCAL_FDT_RESPONSE_LEN ];
 
   if (FileName == NULL) {
     Print(L"Is an EFI Application? ");
@@ -258,15 +259,19 @@  BootDeviceGetType (
     }
     *BootType = BDS_LOADER_EFI_APPLICATION;
   } else {
-    Print(L"Has FDT support? ");
-    Status = GetHIInputBoolean (&HasFDTSupport);
+    Print(L"Boot Type: [a] ATAGS, [g] Global FDT or [l] Local FDT? [a/g/l] ");
+    Status = GetHIInputStr (FDTType, LOCAL_FDT_RESPONSE_LEN );
     if (EFI_ERROR(Status)) {
       return EFI_ABORTED;
     }
-    if (HasFDTSupport) {
-      *BootType = BDS_LOADER_KERNEL_LINUX_FDT;
-    } else {
+    if (StrCmp(FDTType, L"g") == 0) {
+      *BootType = BDS_LOADER_KERNEL_LINUX_GLOBAL_FDT;
+    } else if (StrCmp(FDTType, L"l") == 0) {
+      *BootType = BDS_LOADER_KERNEL_LINUX_LOCAL_FDT;
+    } else if (StrCmp(FDTType, L"a") == 0) {
       *BootType = BDS_LOADER_KERNEL_LINUX_ATAG;
+    } else {
+      return EFI_ABORTED;
     }
   }