ArmPkg/Library/BdsLib: Fix Linux kernel load when 1st call to BdsLoadImage fails.

Code tried to load the Linux kernel and the processor holding pen (PSCI disabled)
as close to the start of System Memory as possible while keeping the pen on a 2MB
boundary, and the kernel on a 2MB boundary + 0x80000.  An assumption was made that 
if the kernel could be loaded at 2MB Boundary + 0x80000, then the memory region 
at 2MB Boundary would be free as well.  Also, loaded kernel could be moved to 
a 2MB boundary + 0x80000 without allocating the proper memory.  
Fix:
Allocate holding pen (PSCI disabled) in the same code section that loads the 
kernel. Once BdsLoadImage fails, kernel size is known, try to allocate/free 
memory instead of calling BdsLoadImage which will read the media to retrieve 
the image size every time.
Plus: 
Support ignoring EfiReservedMemoryType when updating the FDT.

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Garrett Kirkendall <garrett.kirkendall@amd.com>

Index: ArmPkg/Library/BdsLib/AArch64/BdsLinuxLoader.c
===================================================================
--- ArmPkg/Library/BdsLib/AArch64/BdsLinuxLoader.c	(revision 15244)
+++ ArmPkg/Library/BdsLib/AArch64/BdsLinuxLoader.c	(working copy)
@@ -189,7 +189,6 @@
   )
 {
   EFI_STATUS            Status;
-  EFI_STATUS            PenBaseStatus;
   UINTN                 LinuxImageSize;
   UINTN                 InitrdImageSize;
   UINTN                 InitrdImageBaseSize;
@@ -205,8 +204,9 @@
   UINTN                 PenSize;
   UINTN                 MailBoxBase;
 
-  PenBaseStatus = EFI_UNSUPPORTED;
+  Status = EFI_SUCCESS;
   PenSize = 0;
+  PenBase = ~0;   // Set to all ones so it won't be page aligned if we don't use it
   InitrdImage = 0;
   InitrdImageSize = 0;
   InitrdImageBase = 0;
@@ -218,23 +218,55 @@
   // Load the Linux kernel from a device path
   //
 
-  // Try to put the kernel at the start of RAM so as to give it access to all memory.
+  // Try to put the kernel close to the start of RAM so as to give it access to all memory.
   // If that fails fall back to try loading it within LINUX_KERNEL_MAX_OFFSET of memory start.
-  LinuxImage = PcdGet64 (PcdSystemMemoryBase) + 0x80000;
-  Status = BdsLoadImage (LinuxKernelDevicePath, AllocateAddress, &LinuxImage, &LinuxImageSize);
-  if (EFI_ERROR(Status)) {
-    // Try again but give the loader more freedom of where to put the image.
-    LinuxImage = LINUX_KERNEL_MAX_OFFSET;
-    Status = BdsLoadImage (LinuxKernelDevicePath, AllocateMaxAddress, &LinuxImage, &LinuxImageSize);
-    if (EFI_ERROR(Status)) {
-      Print (L"ERROR: Did not find Linux kernel.\n");
-      return Status;
+  // Kernel must reside at a 2MB boundary + 0x80000.
+  // The processor pen will be at 2MB boundary.  Allocate memory until enough
+  // Free memory is found.
+  LinuxImageSize = 0;
+  for (LinuxImage = PcdGet64 (PcdSystemMemoryBase) + 0x80000;
+        LinuxImage < LINUX_KERNEL_MAX_OFFSET;
+        LinuxImage += SIZE_2MB) {
+    Status = EFI_SUCCESS;
+    if (FeaturePcdGet (PcdArmPsciSupport) == FALSE) {
+      // Place Pen at the start of Linux memory. We can then tell Linux to not use this bit of memory
+      PenBase  = LinuxImage - 0x80000;
+      PenSize  = (UINTN) & SecondariesPenEnd - (UINTN) & SecondariesPenStart;
+
+      // Reserve the memory as RuntimeServices
+      Status = gBS->AllocatePages (AllocateAddress, EfiRuntimeServicesCode, EFI_SIZE_TO_PAGES (PenSize), &PenBase);
     }
+    if (!EFI_ERROR (Status)) {
+      // First time try to load the image so we can at least get the LinuxImageSize
+      if (LinuxImageSize == 0) {
+        Status = BdsLoadImage (LinuxKernelDevicePath, AllocateAddress, &LinuxImage, &LinuxImageSize);
+      } else {
+        // So we don't have to suffer the penalty of storage access, try to allocate only
+        Status = gBS->AllocatePages (AllocateAddress, EfiBootServicesCode, EFI_SIZE_TO_PAGES(LinuxImageSize), &LinuxImage);
+        if (!EFI_ERROR (Status)) {
+          gBS->FreePages (LinuxImage, EFI_SIZE_TO_PAGES (LinuxImageSize));
+          Status = BdsLoadImage (LinuxKernelDevicePath, AllocateAddress, &LinuxImage, &LinuxImageSize);
+        }
+      }
+      if (EFI_ERROR(Status)){
+        // Free up the Pen.
+        if (PenBase != ~0) {
+          gBS->FreePages (PenBase, EFI_SIZE_TO_PAGES (PenSize));
+          PenBase = ~0;
+        }
+        if (Status == EFI_NOT_FOUND && LinuxImageSize == 0) {
+          Print (L"ERROR: Did not find Linux kernel.\n");
+          return (Status);
+        }
+      }
+    }
+    if (!EFI_ERROR(Status)) {
+      break;
+    }
   }
-  // Adjust the kernel location slightly if required. The kernel needs to be placed at start
-  //  of memory (2MB aligned) + 0x80000.
-  if ((LinuxImage & LINUX_ALIGN_MASK) != LINUX_ALIGN_VAL) {
-    LinuxImage = (EFI_PHYSICAL_ADDRESS)CopyMem (ALIGN_2MB(LinuxImage) + 0x80000, (VOID*)(UINTN)LinuxImage, LinuxImageSize);
+  if (EFI_ERROR (Status)) {
+    Print (L"ERROR: Linux kernel found, but load failed.\n");
+    return (Status);
   }
 
   if (InitrdDevicePath) {
@@ -272,17 +304,7 @@
   // Install secondary core pens if the Power State Coordination Interface is not supported
   //
   if (FeaturePcdGet (PcdArmPsciSupport) == FALSE) {
-    // Place Pen at the start of Linux memory. We can then tell Linux to not use this bit of memory
-    PenBase  = LinuxImage - 0x80000;
-    PenSize  = (UINTN)&SecondariesPenEnd - (UINTN)&SecondariesPenStart;
 
-    // Reserve the memory as RuntimeServices
-    PenBaseStatus = gBS->AllocatePages (AllocateAddress, EfiRuntimeServicesCode, EFI_SIZE_TO_PAGES (PenSize), &PenBase);
-    if (EFI_ERROR (PenBaseStatus)) {
-      Print (L"Warning: Failed to reserve the memory required for the secondary cores at 0x%lX, Status = %r\n", PenBase, PenBaseStatus);
-      // Even if there is a risk of memory corruption we carry on
-    }
-
     // Put mailboxes below the pen code so we know where they are relative to code.
     MailBoxBase = (UINTN)PenBase + ((UINTN)&SecondariesPenEnd - (UINTN)&SecondariesPenStart);
     // Make sure this is 8 byte aligned.
@@ -331,8 +353,9 @@
   return StartLinux (LinuxImage, LinuxImageSize, FdtBlobBase, FdtBlobSize);
 
 EXIT_FREE_FDT:
-  if (!EFI_ERROR (PenBaseStatus)) {
+  if (PenBase != ~0) {
     gBS->FreePages (PenBase, EFI_SIZE_TO_PAGES (PenSize));
+    PenBase = ~0;
   }
 
   gBS->FreePages (FdtBlobBase, EFI_SIZE_TO_PAGES (FdtBlobSize));
Index: ArmPkg/Library/BdsLib/BdsLinuxFdt.c
===================================================================
--- ArmPkg/Library/BdsLib/BdsLinuxFdt.c	(revision 15244)
+++ ArmPkg/Library/BdsLib/BdsLinuxFdt.c	(working copy)
@@ -207,6 +207,7 @@
   case EfiUnusableMemory:
   case EfiACPIReclaimMemory:
   case EfiACPIMemoryNVS:
+  case EfiReservedMemoryType:
     return TRUE;
   default:
     return FALSE;
@@ -500,7 +501,7 @@
     MemoryMapPtr = MemoryMap;
     for (Index = 0; Index < (MemoryMapSize / DescriptorSize); Index++) {
       if (IsLinuxReservedRegion ((EFI_MEMORY_TYPE)MemoryMapPtr->Type)) {
-        DEBUG((DEBUG_VERBOSE, "Reserved region of type %d [0x%X, 0x%X]\n",
+        DEBUG((DEBUG_VERBOSE, "Reserved region of type %d [0x%lX, 0x%lX]\n",
             MemoryMapPtr->Type,
             (UINTN)MemoryMapPtr->PhysicalStart,
             (UINTN)(MemoryMapPtr->PhysicalStart + MemoryMapPtr->NumberOfPages * EFI_PAGE_SIZE)));
