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