123b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron/** @file
223b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron*
323b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron*  Copyright (c) 2011-2015, ARM Limited. All rights reserved.
423b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron*
523b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron*  This program and the accompanying materials
623b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron*  are licensed and made available under the terms and conditions of the BSD License
723b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron*  which accompanies this distribution.  The full text of the license may be found at
823b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron*  http://opensource.org/licenses/bsd-license.php
923b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron*
1023b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron*  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
1123b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron*  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
1223b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron*
1323b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron**/
1423b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron
1523b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron#include <Library/ArmLib.h>
1623b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron#include <Library/PcdLib.h>
1723b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron
1823b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron#include <Guid/Fdt.h>
1923b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron
2023b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron#include "LinuxLoader.h"
2123b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron
2223b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron#define ALIGN32_BELOW(addr)   ALIGN_POINTER(addr - 32,32)
2323b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron
2423b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron#define IS_ADDRESS_IN_REGION(RegionStart, RegionSize, Address) \
2523b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron    (((UINTN)(RegionStart) <= (UINTN)(Address)) && ((UINTN)(Address) <= ((UINTN)(RegionStart) + (UINTN)(RegionSize))))
2623b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron
2723b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald CronEFI_STATUS
2823b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald CronPrepareAtagList (
2923b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  IN  EFI_PHYSICAL_ADDRESS  SystemMemoryBase,
3023b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  IN  CONST CHAR8*          CommandLineString,
3123b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  IN  EFI_PHYSICAL_ADDRESS  InitrdImage,
3223b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  IN  UINTN                 InitrdImageSize,
3323b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  OUT EFI_PHYSICAL_ADDRESS  *AtagBase,
3423b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  OUT UINT32                *AtagSize
3523b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  );
3623b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron
3723b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald CronSTATIC
3823b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald CronVOID
3923b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald CronPreparePlatformHardware (
4023b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  VOID
4123b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  )
4223b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron{
4323b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  //Note: Interrupts will be disabled by the GIC driver when ExitBootServices() will be called.
4423b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron
4523b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  // Clean before Disable else the Stack gets corrupted with old data.
4623b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  ArmCleanDataCache ();
4723b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  ArmDisableDataCache ();
4823b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  // Invalidate all the entries that might have snuck in.
4923b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  ArmInvalidateDataCache ();
5023b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron
5123b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  // Invalidate and disable the Instruction cache
5223b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  ArmDisableInstructionCache ();
5323b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  ArmInvalidateInstructionCache ();
5423b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron
5523b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  // Turn off MMU
5623b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  ArmDisableMmu ();
5723b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron}
5823b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron
5923b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald CronSTATIC
6023b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald CronEFI_STATUS
6123b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald CronStartLinux (
6223b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  IN  EFI_PHYSICAL_ADDRESS  SystemMemoryBase,
6323b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  IN  EFI_PHYSICAL_ADDRESS  LinuxImage,
6423b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  IN  UINTN                 LinuxImageSize,
6523b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  IN  EFI_PHYSICAL_ADDRESS  KernelParamsAddress,
6623b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  IN  UINTN                 KernelParamsSize,
6723b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  IN  UINT32                MachineType
6823b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  )
6923b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron{
7023b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  EFI_STATUS            Status;
7123b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  LINUX_KERNEL          LinuxKernel;
7223b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron
7323b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  // Shut down UEFI boot services. ExitBootServices() will notify every driver that created an event on
7423b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  // ExitBootServices event. Example the Interrupt DXE driver will disable the interrupts on this event.
7523b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  Status = ShutdownUefiBootServices ();
7623b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  if (EFI_ERROR (Status)) {
7723b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron    DEBUG ((EFI_D_ERROR, "ERROR: Can not shutdown UEFI boot services. Status=0x%X\n", Status));
7823b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron    return Status;
7923b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  }
8023b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron
8123b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  // Move the kernel parameters to any address inside the first 1MB.
8223b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  // This is necessary because the ARM Linux kernel requires
8323b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  // the FTD / ATAG List to reside entirely inside the first 1MB of
8423b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  // physical memory.
8523b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  //Note: There is no requirement on the alignment
8623b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  if (MachineType != ARM_FDT_MACHINE_TYPE) {
8723b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron    if (((UINTN)KernelParamsAddress > LINUX_ATAG_MAX_OFFSET) && (KernelParamsSize < PcdGet32 (PcdArmLinuxAtagMaxOffset))) {
8823b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron      KernelParamsAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)CopyMem (ALIGN32_BELOW (LINUX_ATAG_MAX_OFFSET - KernelParamsSize), (VOID*)(UINTN)KernelParamsAddress, KernelParamsSize);
8923b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron    }
9023b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  } else {
9123b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron    if (((UINTN)KernelParamsAddress > LINUX_FDT_MAX_OFFSET) && (KernelParamsSize < PcdGet32 (PcdArmLinuxFdtMaxOffset))) {
9223b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron      KernelParamsAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)CopyMem (ALIGN32_BELOW (LINUX_FDT_MAX_OFFSET - KernelParamsSize), (VOID*)(UINTN)KernelParamsAddress, KernelParamsSize);
9323b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron    }
9423b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  }
9523b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron
9623b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  if ((UINTN)LinuxImage > LINUX_KERNEL_MAX_OFFSET) {
9723b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron    //Note: There is no requirement on the alignment
9823b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron    LinuxKernel = (LINUX_KERNEL)CopyMem (ALIGN32_BELOW (LINUX_KERNEL_MAX_OFFSET - LinuxImageSize), (VOID*)(UINTN)LinuxImage, LinuxImageSize);
9923b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  } else {
10023b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron    LinuxKernel = (LINUX_KERNEL)(UINTN)LinuxImage;
10123b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  }
10223b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron
10323b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  // Check if the Linux Image is a uImage
10423b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  if (*(UINT32*)LinuxKernel == LINUX_UIMAGE_SIGNATURE) {
10523b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron    // Assume the Image Entry Point is just after the uImage header (64-byte size)
10623b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron    LinuxKernel = (LINUX_KERNEL)((UINTN)LinuxKernel + 64);
10723b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron    LinuxImageSize -= 64;
10823b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  }
10923b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron
11023b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  // Check there is no overlapping between kernel and its parameters
11123b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  // We can only assert because it is too late to fallback to UEFI (ExitBootServices has been called).
11223b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  ASSERT (!IS_ADDRESS_IN_REGION (LinuxKernel, LinuxImageSize, KernelParamsAddress) &&
11323b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron          !IS_ADDRESS_IN_REGION (LinuxKernel, LinuxImageSize, KernelParamsAddress + KernelParamsSize));
11423b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron
11523b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  //
11623b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  // Switch off interrupts, caches, mmu, etc
11723b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  //
11823b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  PreparePlatformHardware ();
11923b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron
12023b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  // Register and print out performance information
12123b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  PERF_END (NULL, "BDS", NULL, 0);
12223b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  if (PerformanceMeasurementEnabled ()) {
12323b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron    PrintPerformance ();
12423b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  }
12523b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron
12623b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  //
12723b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  // Start the Linux Kernel
12823b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  //
12923b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron
13023b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  // Outside BootServices, so can't use Print();
13123b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  DEBUG ((EFI_D_ERROR, "\nStarting the kernel:\n\n"));
13223b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron
13323b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  // Jump to kernel with register set
13423b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  LinuxKernel ((UINTN)0, MachineType, (UINTN)KernelParamsAddress);
13523b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron
13623b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  // Kernel should never exit
13723b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  // After Life services are not provided
13823b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  ASSERT (FALSE);
13923b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  // We cannot recover the execution at this stage
14023b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  while (1);
14123b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron}
14223b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron
14323b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron/**
14423b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  Start a Linux kernel from a Device Path
14523b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron
14623b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  @param  SystemMemoryBase      Base of the system memory
14723b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  @param  LinuxKernel           Device Path to the Linux Kernel
14823b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  @param  Parameters            Linux kernel arguments
14923b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  @param  Fdt                   Device Path to the Flat Device Tree
15023b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  @param  MachineType           ARM machine type value
15123b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron
15223b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  @retval EFI_SUCCESS           All drivers have been connected
15323b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  @retval EFI_NOT_FOUND         The Linux kernel Device Path has not been found
15423b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  @retval EFI_OUT_OF_RESOURCES  There is not enough resource memory to store the matching results.
15523b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  @retval RETURN_UNSUPPORTED    ATAG is not support by this architecture
15623b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron
15723b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron**/
15823b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald CronEFI_STATUS
15923b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald CronBootLinuxAtag (
16023b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  IN  EFI_PHYSICAL_ADDRESS      SystemMemoryBase,
16123b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  IN  EFI_DEVICE_PATH_PROTOCOL* LinuxKernelDevicePath,
16223b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  IN  EFI_DEVICE_PATH_PROTOCOL* InitrdDevicePath,
16323b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  IN  CONST CHAR8*              CommandLineArguments,
16423b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  IN  UINTN                     MachineType
16523b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  )
16623b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron{
16723b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  EFI_STATUS            Status;
16823b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  UINT32                LinuxImageSize;
16923b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  UINT32                InitrdImageBaseSize = 0;
17023b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  UINT32                InitrdImageSize = 0;
17123b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  UINT32                AtagSize;
17223b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  EFI_PHYSICAL_ADDRESS  AtagBase;
17323b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  EFI_PHYSICAL_ADDRESS  LinuxImage;
17423b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  EFI_PHYSICAL_ADDRESS  InitrdImageBase = 0;
17523b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  EFI_PHYSICAL_ADDRESS  InitrdImage = 0;
17623b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron
17723b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  PERF_START (NULL, "BDS", NULL, 0);
17823b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron
17923b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  // Load the Linux kernel from a device path
18023b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  LinuxImage = LINUX_KERNEL_MAX_OFFSET;
18123b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  Status = BdsLoadImage (LinuxKernelDevicePath, AllocateMaxAddress, &LinuxImage, &LinuxImageSize);
18223b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  if (EFI_ERROR (Status)) {
18323b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron    Print (L"ERROR: Did not find Linux kernel.\n");
18423b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron    return Status;
18523b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  }
18623b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron
18723b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  if (InitrdDevicePath) {
18823b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron    // Load the initrd near to the Linux kernel
18923b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron    InitrdImageBase = LINUX_KERNEL_MAX_OFFSET;
19023b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron    Status = BdsLoadImage (InitrdDevicePath, AllocateMaxAddress, &InitrdImageBase, &InitrdImageBaseSize);
19123b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron    if (Status == EFI_OUT_OF_RESOURCES) {
19223b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron      Status = BdsLoadImage (InitrdDevicePath, AllocateAnyPages, &InitrdImageBase, &InitrdImageBaseSize);
19323b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron    }
19423b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron    if (EFI_ERROR (Status)) {
19523b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron      Print (L"ERROR: Did not find initrd image.\n");
19623b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron      goto EXIT_FREE_LINUX;
19723b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron    }
19823b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron
19923b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron    // Check if the initrd is a uInitrd
20023b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron    if (*(UINT32*)((UINTN)InitrdImageBase) == LINUX_UIMAGE_SIGNATURE) {
20123b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron      // Skip the 64-byte image header
20223b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron      InitrdImage = (EFI_PHYSICAL_ADDRESS)((UINTN)InitrdImageBase + 64);
20323b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron      InitrdImageSize = InitrdImageBaseSize - 64;
20423b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron    } else {
20523b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron      InitrdImage = InitrdImageBase;
20623b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron      InitrdImageSize = InitrdImageBaseSize;
20723b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron    }
20823b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  }
20923b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron
21023b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  //
21123b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  // Setup the Linux Kernel Parameters
21223b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  //
21323b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron
21423b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  // By setting address=0 we leave the memory allocation to the function
21523b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  Status = PrepareAtagList (SystemMemoryBase, CommandLineArguments, InitrdImage, InitrdImageSize, &AtagBase, &AtagSize);
21623b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  if (EFI_ERROR (Status)) {
21723b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron    Print (L"ERROR: Can not prepare ATAG list. Status=0x%X\n", Status);
21823b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron    goto EXIT_FREE_INITRD;
21923b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  }
22023b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron
22123b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  return StartLinux (SystemMemoryBase, LinuxImage, LinuxImageSize, AtagBase, AtagSize, MachineType);
22223b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron
22323b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald CronEXIT_FREE_INITRD:
22423b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  if (InitrdDevicePath) {
22523b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron    gBS->FreePages (InitrdImageBase, EFI_SIZE_TO_PAGES (InitrdImageBaseSize));
22623b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  }
22723b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron
22823b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald CronEXIT_FREE_LINUX:
22923b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  gBS->FreePages (LinuxImage, EFI_SIZE_TO_PAGES (LinuxImageSize));
23023b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron
23123b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  return Status;
23223b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron}
23323b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron
23423b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron/**
23523b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  Start a Linux kernel from a Device Path
23623b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron
23723b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  @param  LinuxKernelDevicePath  Device Path to the Linux Kernel
23823b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  @param  InitrdDevicePath       Device Path to the Initrd
23923b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  @param  CommandLineArguments   Linux command line
24023b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron
24123b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  @retval EFI_SUCCESS           All drivers have been connected
24223b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  @retval EFI_NOT_FOUND         The Linux kernel Device Path has not been found
24323b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  @retval EFI_OUT_OF_RESOURCES  There is not enough resource memory to store the matching results.
24423b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron
24523b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron**/
24623b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald CronEFI_STATUS
24723b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald CronBootLinuxFdt (
24823b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  IN  EFI_PHYSICAL_ADDRESS      SystemMemoryBase,
24923b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  IN  EFI_DEVICE_PATH_PROTOCOL* LinuxKernelDevicePath,
25023b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  IN  EFI_DEVICE_PATH_PROTOCOL* InitrdDevicePath,
25123b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  IN  EFI_DEVICE_PATH_PROTOCOL* FdtDevicePath,
25223b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  IN  CONST CHAR8*              CommandLineArguments
25323b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  )
25423b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron{
25523b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  EFI_STATUS               Status;
25623b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  UINT32                   LinuxImageSize;
25723b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  UINT32                   InitrdImageBaseSize = 0;
25823b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  UINT32                   InitrdImageSize = 0;
25923b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  VOID                     *InstalledFdtBase;
26023b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  UINT32                   FdtBlobSize;
26123b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  EFI_PHYSICAL_ADDRESS     FdtBlobBase;
26223b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  EFI_PHYSICAL_ADDRESS     LinuxImage;
26323b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  EFI_PHYSICAL_ADDRESS     InitrdImageBase = 0;
26423b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  EFI_PHYSICAL_ADDRESS     InitrdImage = 0;
26523b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron
26623b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  PERF_START (NULL, "BDS", NULL, 0);
26723b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron
26823b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  // Load the Linux kernel from a device path
26923b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  LinuxImage = LINUX_KERNEL_MAX_OFFSET;
27023b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  Status = BdsLoadImage (LinuxKernelDevicePath, AllocateMaxAddress, &LinuxImage, &LinuxImageSize);
27123b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  if (EFI_ERROR (Status)) {
27223b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron    Print (L"ERROR: Did not find Linux kernel.\n");
27323b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron    return Status;
27423b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  }
27523b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron
27623b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  if (InitrdDevicePath) {
27723b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron    InitrdImageBase = LINUX_KERNEL_MAX_OFFSET;
27823b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron    Status = BdsLoadImage (InitrdDevicePath, AllocateMaxAddress, &InitrdImageBase, &InitrdImageBaseSize);
27923b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron    if (Status == EFI_OUT_OF_RESOURCES) {
28023b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron      Status = BdsLoadImage (InitrdDevicePath, AllocateAnyPages, &InitrdImageBase, &InitrdImageBaseSize);
28123b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron    }
28223b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron    if (EFI_ERROR (Status)) {
28323b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron      Print (L"ERROR: Did not find initrd image.\n");
28423b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron      goto EXIT_FREE_LINUX;
28523b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron    }
28623b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron
28723b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron    // Check if the initrd is a uInitrd
28823b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron    if (*(UINT32*)((UINTN)InitrdImageBase) == LINUX_UIMAGE_SIGNATURE) {
28923b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron      // Skip the 64-byte image header
29023b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron      InitrdImage = (EFI_PHYSICAL_ADDRESS)((UINTN)InitrdImageBase + 64);
29123b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron      InitrdImageSize = InitrdImageBaseSize - 64;
29223b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron    } else {
29323b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron      InitrdImage = InitrdImageBase;
29423b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron      InitrdImageSize = InitrdImageBaseSize;
29523b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron    }
29623b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  }
29723b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron
29823b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  if (FdtDevicePath == NULL) {
29923b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron    //
30023b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron    // Get the FDT from the Configuration Table.
30123b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron    // The FDT will be reloaded in PrepareFdt() to a more appropriate
30223b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron    // location for the Linux Kernel.
30323b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron    //
30423b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron    Status = EfiGetSystemConfigurationTable (&gFdtTableGuid, &InstalledFdtBase);
30523b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron    if (EFI_ERROR (Status)) {
30623b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron      Print (L"ERROR: Did not get the Device Tree blob (%r).\n", Status);
30723b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron      goto EXIT_FREE_INITRD;
30823b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron    }
30923b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron    FdtBlobBase = (EFI_PHYSICAL_ADDRESS)(UINTN)InstalledFdtBase;
31023b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron    FdtBlobSize = fdt_totalsize (InstalledFdtBase);
31123b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  } else {
31223b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron    //
31323b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron    // FDT device path explicitly defined. The FDT is relocated later to a
31423b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron    // more appropriate location for the Linux kernel.
31523b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron    //
31623b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron    FdtBlobBase = LINUX_KERNEL_MAX_OFFSET;
31723b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron    Status = BdsLoadImage (FdtDevicePath, AllocateMaxAddress, &FdtBlobBase, &FdtBlobSize);
31823b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron    if (EFI_ERROR (Status)) {
31923b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron      Print (L"ERROR: Did not find Device Tree blob (%r).\n", Status);
32023b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron      goto EXIT_FREE_INITRD;
32123b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron    }
32223b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  }
32323b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron
32423b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  // Update the Fdt with the Initrd information. The FDT will increase in size.
32523b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  // By setting address=0 we leave the memory allocation to the function
32623b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  Status = PrepareFdt (SystemMemoryBase, CommandLineArguments, InitrdImage, InitrdImageSize, &FdtBlobBase, &FdtBlobSize);
32723b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  if (EFI_ERROR (Status)) {
32823b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron    Print (L"ERROR: Can not load kernel with FDT. Status=%r\n", Status);
32923b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron    goto EXIT_FREE_FDT;
33023b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  }
33123b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron
33223b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  return StartLinux (SystemMemoryBase, LinuxImage, LinuxImageSize, FdtBlobBase, FdtBlobSize, ARM_FDT_MACHINE_TYPE);
33323b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron
33423b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald CronEXIT_FREE_FDT:
33523b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  gBS->FreePages (FdtBlobBase, EFI_SIZE_TO_PAGES (FdtBlobSize));
33623b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron
33723b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald CronEXIT_FREE_INITRD:
33823b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  if (InitrdDevicePath) {
33923b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron    gBS->FreePages (InitrdImageBase, EFI_SIZE_TO_PAGES (InitrdImageBaseSize));
34023b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  }
34123b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron
34223b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald CronEXIT_FREE_LINUX:
34323b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  gBS->FreePages (LinuxImage, EFI_SIZE_TO_PAGES (LinuxImageSize));
34423b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron
34523b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron  return Status;
34623b01c83b28a7cb8f7eea7af679aecd39f782f1eRonald Cron}
347