X-Git-Url: https://git.proxmox.com/?p=mirror_edk2.git;a=blobdiff_plain;f=ArmPkg%2FLibrary%2FBdsLib%2FBdsLinuxLoader.c;h=e3249e130775d7f0fe4282651e2a14b5b3d514e5;hp=901781f2f852bc158e6ef64e9c40edefed00b85e;hb=1e57a46299244793beb27e74be171d1540606999;hpb=5767f22fca7c337cdc113e14b411c1fd0ea7bd53 diff --git a/ArmPkg/Library/BdsLib/BdsLinuxLoader.c b/ArmPkg/Library/BdsLib/BdsLinuxLoader.c index 901781f2f8..e3249e1307 100644 --- a/ArmPkg/Library/BdsLib/BdsLinuxLoader.c +++ b/ArmPkg/Library/BdsLib/BdsLinuxLoader.c @@ -1,274 +1,274 @@ -/** @file -* -* Copyright (c) 2011-2012, ARM Limited. All rights reserved. -* -* This program and the accompanying materials -* are licensed and made available under the terms and conditions of the BSD License -* which accompanies this distribution. The full text of the license may be found at -* http://opensource.org/licenses/bsd-license.php -* -* THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, -* WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. -* -**/ - -#include "BdsInternal.h" -#include "BdsLinuxLoader.h" - -#define ALIGN32_BELOW(addr) ALIGN_POINTER(addr - 32,32) - -STATIC -EFI_STATUS -PreparePlatformHardware ( - VOID - ) -{ - //Note: Interrupts will be disabled by the GIC driver when ExitBootServices() will be called. - - // Clean, invalidate, disable data cache - ArmDisableDataCache(); - ArmCleanInvalidateDataCache(); - - // Invalidate and disable the Instruction cache - ArmDisableInstructionCache (); - ArmInvalidateInstructionCache (); - - // Turn off MMU - ArmDisableMmu(); - - return EFI_SUCCESS; -} - -STATIC -EFI_STATUS -StartLinux ( - IN EFI_PHYSICAL_ADDRESS LinuxImage, - IN UINTN LinuxImageSize, - IN EFI_PHYSICAL_ADDRESS KernelParamsAddress, - IN UINTN KernelParamsSize, - IN UINT32 MachineType - ) -{ - EFI_STATUS Status; - LINUX_KERNEL LinuxKernel; - - // Shut down UEFI boot services. ExitBootServices() will notify every driver that created an event on - // ExitBootServices event. Example the Interrupt DXE driver will disable the interrupts on this event. - Status = ShutdownUefiBootServices (); - if(EFI_ERROR(Status)) { - DEBUG((EFI_D_ERROR,"ERROR: Can not shutdown UEFI boot services. Status=0x%X\n", Status)); - goto Exit; - } - - // Move the kernel parameters to any address inside the first 1MB. - // This is necessary because the ARM Linux kernel requires - // the FTD / ATAG List to reside entirely inside the first 1MB of - // physical memory. - //Note: There is no requirement on the alignment - if (MachineType != ARM_FDT_MACHINE_TYPE) { - if (((UINTN)KernelParamsAddress > LINUX_ATAG_MAX_OFFSET) && (KernelParamsSize < PcdGet32(PcdArmLinuxAtagMaxOffset))) { - KernelParamsAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)CopyMem (ALIGN32_BELOW(LINUX_ATAG_MAX_OFFSET - KernelParamsSize), (VOID*)(UINTN)KernelParamsAddress, KernelParamsSize); - } - } else { - if (((UINTN)KernelParamsAddress > LINUX_FDT_MAX_OFFSET) && (KernelParamsSize < PcdGet32(PcdArmLinuxFdtMaxOffset))) { - KernelParamsAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)CopyMem (ALIGN32_BELOW(LINUX_FDT_MAX_OFFSET - KernelParamsSize), (VOID*)(UINTN)KernelParamsAddress, KernelParamsSize); - } - } - - if ((UINTN)LinuxImage > LINUX_KERNEL_MAX_OFFSET) { - //Note: There is no requirement on the alignment - LinuxKernel = (LINUX_KERNEL)CopyMem (ALIGN32_BELOW(LINUX_KERNEL_MAX_OFFSET - LinuxImageSize), (VOID*)(UINTN)LinuxImage, LinuxImageSize); - } else { - LinuxKernel = (LINUX_KERNEL)(UINTN)LinuxImage; - } - - // Check if the Linux Image is a uImage - if (*(UINT32*)LinuxKernel == LINUX_UIMAGE_SIGNATURE) { - // Assume the Image Entry Point is just after the uImage header (64-byte size) - LinuxKernel = (LINUX_KERNEL)((UINTN)LinuxKernel + 64); - LinuxImageSize -= 64; - } - - //TODO: Check there is no overlapping between kernel and Atag - - // - // Switch off interrupts, caches, mmu, etc - // - Status = PreparePlatformHardware (); - ASSERT_EFI_ERROR(Status); - - // Register and print out performance information - PERF_END (NULL, "BDS", NULL, 0); - if (PerformanceMeasurementEnabled ()) { - PrintPerformance (); - } - - // - // Start the Linux Kernel - // - - // Outside BootServices, so can't use Print(); - DEBUG((EFI_D_ERROR, "\nStarting the kernel:\n\n")); - - // Jump to kernel with register set - LinuxKernel ((UINTN)0, MachineType, (UINTN)KernelParamsAddress); - - // Kernel should never exit - // After Life services are not provided - ASSERT(FALSE); - -Exit: - // Only be here if we fail to start Linux - Print (L"ERROR : Can not start the kernel. Status=0x%X\n", Status); - - // Free Runtimee Memory (kernel and FDT) - return Status; -} - -/** - Start a Linux kernel from a Device Path - - @param LinuxKernel Device Path to the Linux Kernel - @param Parameters Linux kernel arguments - @param Fdt Device Path to the Flat Device Tree - - @retval EFI_SUCCESS All drivers have been connected - @retval EFI_NOT_FOUND The Linux kernel Device Path has not been found - @retval EFI_OUT_OF_RESOURCES There is not enough resource memory to store the matching results. - -**/ -EFI_STATUS -BdsBootLinuxAtag ( - IN EFI_DEVICE_PATH_PROTOCOL* LinuxKernelDevicePath, - IN EFI_DEVICE_PATH_PROTOCOL* InitrdDevicePath, - IN CONST CHAR8* CommandLineArguments - ) -{ - EFI_STATUS Status; - UINT32 LinuxImageSize; - UINT32 InitrdImageSize = 0; - UINT32 AtagSize; - EFI_PHYSICAL_ADDRESS AtagBase; - EFI_PHYSICAL_ADDRESS LinuxImage; - EFI_PHYSICAL_ADDRESS InitrdImage; - - PERF_START (NULL, "BDS", NULL, 0); - - // Load the Linux kernel from a device path - 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; - } - - if (InitrdDevicePath) { - // Load the initrd near to the Linux kernel - InitrdImage = LINUX_KERNEL_MAX_OFFSET; - Status = BdsLoadImage (InitrdDevicePath, AllocateMaxAddress, &InitrdImage, &InitrdImageSize); - if (Status == EFI_OUT_OF_RESOURCES) { - Status = BdsLoadImage (InitrdDevicePath, AllocateAnyPages, &InitrdImage, &InitrdImageSize); - } - if (EFI_ERROR(Status)) { - Print (L"ERROR: Did not find initrd image.\n"); - return Status; - } - - // Check if the initrd is a uInitrd - if (*(UINT32*)((UINTN)InitrdImage) == LINUX_UIMAGE_SIGNATURE) { - // Skip the 64-byte image header - InitrdImage = (EFI_PHYSICAL_ADDRESS)((UINTN)InitrdImage + 64); - InitrdImageSize -= 64; - } - } - - // - // Setup the Linux Kernel Parameters - // - - // By setting address=0 we leave the memory allocation to the function - Status = PrepareAtagList (CommandLineArguments, InitrdImage, InitrdImageSize, &AtagBase, &AtagSize); - if (EFI_ERROR(Status)) { - Print(L"ERROR: Can not prepare ATAG list. Status=0x%X\n", Status); - return Status; - } - - return StartLinux (LinuxImage, LinuxImageSize, AtagBase, AtagSize, PcdGet32(PcdArmMachineType)); -} - -/** - Start a Linux kernel from a Device Path - - @param LinuxKernel Device Path to the Linux Kernel - @param Parameters Linux kernel arguments - @param Fdt Device Path to the Flat Device Tree - - @retval EFI_SUCCESS All drivers have been connected - @retval EFI_NOT_FOUND The Linux kernel Device Path has not been found - @retval EFI_OUT_OF_RESOURCES There is not enough resource memory to store the matching results. - -**/ -EFI_STATUS -BdsBootLinuxFdt ( - IN EFI_DEVICE_PATH_PROTOCOL* LinuxKernelDevicePath, - IN EFI_DEVICE_PATH_PROTOCOL* InitrdDevicePath, - IN CONST CHAR8* CommandLineArguments, - IN EFI_DEVICE_PATH_PROTOCOL* FdtDevicePath - ) -{ - EFI_STATUS Status; - UINT32 LinuxImageSize; - UINT32 InitrdImageSize = 0; - UINT32 FdtBlobSize; - EFI_PHYSICAL_ADDRESS FdtBlobBase; - EFI_PHYSICAL_ADDRESS LinuxImage; - EFI_PHYSICAL_ADDRESS InitrdImage; - - PERF_START (NULL, "BDS", NULL, 0); - - // Load the Linux kernel from a device path - 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; - } - - if (InitrdDevicePath) { - InitrdImage = LINUX_KERNEL_MAX_OFFSET; - Status = BdsLoadImage (InitrdDevicePath, AllocateMaxAddress, &InitrdImage, &InitrdImageSize); - if (Status == EFI_OUT_OF_RESOURCES) { - Status = BdsLoadImage (InitrdDevicePath, AllocateAnyPages, &InitrdImage, &InitrdImageSize); - } - if (EFI_ERROR(Status)) { - Print (L"ERROR: Did not find initrd image.\n"); - return Status; - } - - // Check if the initrd is a uInitrd - if (*(UINT32*)((UINTN)InitrdImage) == LINUX_UIMAGE_SIGNATURE) { - // Skip the 64-byte image header - InitrdImage = (EFI_PHYSICAL_ADDRESS)((UINTN)InitrdImage + 64); - InitrdImageSize -= 64; - } - } - - // Load the FDT binary from a device path. The FDT will be reloaded later to a more appropriate location for the Linux kernel. - FdtBlobBase = 0; - Status = BdsLoadImage (FdtDevicePath, AllocateAnyPages, &FdtBlobBase, &FdtBlobSize); - if (EFI_ERROR(Status)) { - Print (L"ERROR: Did not find Device Tree blob.\n"); - return Status; - } - - // Update the Fdt with the Initrd information. The FDT will increase in size. - // By setting address=0 we leave the memory allocation to the function - Status = PrepareFdt (CommandLineArguments, InitrdImage, InitrdImageSize, &FdtBlobBase, &FdtBlobSize); - if (EFI_ERROR(Status)) { - Print(L"ERROR: Can not load kernel with FDT. Status=%r\n", Status); - return Status; - } - - return StartLinux (LinuxImage, LinuxImageSize, FdtBlobBase, FdtBlobSize, ARM_FDT_MACHINE_TYPE); -} - +/** @file +* +* Copyright (c) 2011-2012, ARM Limited. All rights reserved. +* +* This program and the accompanying materials +* are licensed and made available under the terms and conditions of the BSD License +* which accompanies this distribution. The full text of the license may be found at +* http://opensource.org/licenses/bsd-license.php +* +* THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +* WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +* +**/ + +#include "BdsInternal.h" +#include "BdsLinuxLoader.h" + +#define ALIGN32_BELOW(addr) ALIGN_POINTER(addr - 32,32) + +STATIC +EFI_STATUS +PreparePlatformHardware ( + VOID + ) +{ + //Note: Interrupts will be disabled by the GIC driver when ExitBootServices() will be called. + + // Clean, invalidate, disable data cache + ArmDisableDataCache(); + ArmCleanInvalidateDataCache(); + + // Invalidate and disable the Instruction cache + ArmDisableInstructionCache (); + ArmInvalidateInstructionCache (); + + // Turn off MMU + ArmDisableMmu(); + + return EFI_SUCCESS; +} + +STATIC +EFI_STATUS +StartLinux ( + IN EFI_PHYSICAL_ADDRESS LinuxImage, + IN UINTN LinuxImageSize, + IN EFI_PHYSICAL_ADDRESS KernelParamsAddress, + IN UINTN KernelParamsSize, + IN UINT32 MachineType + ) +{ + EFI_STATUS Status; + LINUX_KERNEL LinuxKernel; + + // Shut down UEFI boot services. ExitBootServices() will notify every driver that created an event on + // ExitBootServices event. Example the Interrupt DXE driver will disable the interrupts on this event. + Status = ShutdownUefiBootServices (); + if(EFI_ERROR(Status)) { + DEBUG((EFI_D_ERROR,"ERROR: Can not shutdown UEFI boot services. Status=0x%X\n", Status)); + goto Exit; + } + + // Move the kernel parameters to any address inside the first 1MB. + // This is necessary because the ARM Linux kernel requires + // the FTD / ATAG List to reside entirely inside the first 1MB of + // physical memory. + //Note: There is no requirement on the alignment + if (MachineType != ARM_FDT_MACHINE_TYPE) { + if (((UINTN)KernelParamsAddress > LINUX_ATAG_MAX_OFFSET) && (KernelParamsSize < PcdGet32(PcdArmLinuxAtagMaxOffset))) { + KernelParamsAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)CopyMem (ALIGN32_BELOW(LINUX_ATAG_MAX_OFFSET - KernelParamsSize), (VOID*)(UINTN)KernelParamsAddress, KernelParamsSize); + } + } else { + if (((UINTN)KernelParamsAddress > LINUX_FDT_MAX_OFFSET) && (KernelParamsSize < PcdGet32(PcdArmLinuxFdtMaxOffset))) { + KernelParamsAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)CopyMem (ALIGN32_BELOW(LINUX_FDT_MAX_OFFSET - KernelParamsSize), (VOID*)(UINTN)KernelParamsAddress, KernelParamsSize); + } + } + + if ((UINTN)LinuxImage > LINUX_KERNEL_MAX_OFFSET) { + //Note: There is no requirement on the alignment + LinuxKernel = (LINUX_KERNEL)CopyMem (ALIGN32_BELOW(LINUX_KERNEL_MAX_OFFSET - LinuxImageSize), (VOID*)(UINTN)LinuxImage, LinuxImageSize); + } else { + LinuxKernel = (LINUX_KERNEL)(UINTN)LinuxImage; + } + + // Check if the Linux Image is a uImage + if (*(UINT32*)LinuxKernel == LINUX_UIMAGE_SIGNATURE) { + // Assume the Image Entry Point is just after the uImage header (64-byte size) + LinuxKernel = (LINUX_KERNEL)((UINTN)LinuxKernel + 64); + LinuxImageSize -= 64; + } + + //TODO: Check there is no overlapping between kernel and Atag + + // + // Switch off interrupts, caches, mmu, etc + // + Status = PreparePlatformHardware (); + ASSERT_EFI_ERROR(Status); + + // Register and print out performance information + PERF_END (NULL, "BDS", NULL, 0); + if (PerformanceMeasurementEnabled ()) { + PrintPerformance (); + } + + // + // Start the Linux Kernel + // + + // Outside BootServices, so can't use Print(); + DEBUG((EFI_D_ERROR, "\nStarting the kernel:\n\n")); + + // Jump to kernel with register set + LinuxKernel ((UINTN)0, MachineType, (UINTN)KernelParamsAddress); + + // Kernel should never exit + // After Life services are not provided + ASSERT(FALSE); + +Exit: + // Only be here if we fail to start Linux + Print (L"ERROR : Can not start the kernel. Status=0x%X\n", Status); + + // Free Runtimee Memory (kernel and FDT) + return Status; +} + +/** + Start a Linux kernel from a Device Path + + @param LinuxKernel Device Path to the Linux Kernel + @param Parameters Linux kernel arguments + @param Fdt Device Path to the Flat Device Tree + + @retval EFI_SUCCESS All drivers have been connected + @retval EFI_NOT_FOUND The Linux kernel Device Path has not been found + @retval EFI_OUT_OF_RESOURCES There is not enough resource memory to store the matching results. + +**/ +EFI_STATUS +BdsBootLinuxAtag ( + IN EFI_DEVICE_PATH_PROTOCOL* LinuxKernelDevicePath, + IN EFI_DEVICE_PATH_PROTOCOL* InitrdDevicePath, + IN CONST CHAR8* CommandLineArguments + ) +{ + EFI_STATUS Status; + UINT32 LinuxImageSize; + UINT32 InitrdImageSize = 0; + UINT32 AtagSize; + EFI_PHYSICAL_ADDRESS AtagBase; + EFI_PHYSICAL_ADDRESS LinuxImage; + EFI_PHYSICAL_ADDRESS InitrdImage; + + PERF_START (NULL, "BDS", NULL, 0); + + // Load the Linux kernel from a device path + 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; + } + + if (InitrdDevicePath) { + // Load the initrd near to the Linux kernel + InitrdImage = LINUX_KERNEL_MAX_OFFSET; + Status = BdsLoadImage (InitrdDevicePath, AllocateMaxAddress, &InitrdImage, &InitrdImageSize); + if (Status == EFI_OUT_OF_RESOURCES) { + Status = BdsLoadImage (InitrdDevicePath, AllocateAnyPages, &InitrdImage, &InitrdImageSize); + } + if (EFI_ERROR(Status)) { + Print (L"ERROR: Did not find initrd image.\n"); + return Status; + } + + // Check if the initrd is a uInitrd + if (*(UINT32*)((UINTN)InitrdImage) == LINUX_UIMAGE_SIGNATURE) { + // Skip the 64-byte image header + InitrdImage = (EFI_PHYSICAL_ADDRESS)((UINTN)InitrdImage + 64); + InitrdImageSize -= 64; + } + } + + // + // Setup the Linux Kernel Parameters + // + + // By setting address=0 we leave the memory allocation to the function + Status = PrepareAtagList (CommandLineArguments, InitrdImage, InitrdImageSize, &AtagBase, &AtagSize); + if (EFI_ERROR(Status)) { + Print(L"ERROR: Can not prepare ATAG list. Status=0x%X\n", Status); + return Status; + } + + return StartLinux (LinuxImage, LinuxImageSize, AtagBase, AtagSize, PcdGet32(PcdArmMachineType)); +} + +/** + Start a Linux kernel from a Device Path + + @param LinuxKernel Device Path to the Linux Kernel + @param Parameters Linux kernel arguments + @param Fdt Device Path to the Flat Device Tree + + @retval EFI_SUCCESS All drivers have been connected + @retval EFI_NOT_FOUND The Linux kernel Device Path has not been found + @retval EFI_OUT_OF_RESOURCES There is not enough resource memory to store the matching results. + +**/ +EFI_STATUS +BdsBootLinuxFdt ( + IN EFI_DEVICE_PATH_PROTOCOL* LinuxKernelDevicePath, + IN EFI_DEVICE_PATH_PROTOCOL* InitrdDevicePath, + IN CONST CHAR8* CommandLineArguments, + IN EFI_DEVICE_PATH_PROTOCOL* FdtDevicePath + ) +{ + EFI_STATUS Status; + UINT32 LinuxImageSize; + UINT32 InitrdImageSize = 0; + UINT32 FdtBlobSize; + EFI_PHYSICAL_ADDRESS FdtBlobBase; + EFI_PHYSICAL_ADDRESS LinuxImage; + EFI_PHYSICAL_ADDRESS InitrdImage; + + PERF_START (NULL, "BDS", NULL, 0); + + // Load the Linux kernel from a device path + 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; + } + + if (InitrdDevicePath) { + InitrdImage = LINUX_KERNEL_MAX_OFFSET; + Status = BdsLoadImage (InitrdDevicePath, AllocateMaxAddress, &InitrdImage, &InitrdImageSize); + if (Status == EFI_OUT_OF_RESOURCES) { + Status = BdsLoadImage (InitrdDevicePath, AllocateAnyPages, &InitrdImage, &InitrdImageSize); + } + if (EFI_ERROR(Status)) { + Print (L"ERROR: Did not find initrd image.\n"); + return Status; + } + + // Check if the initrd is a uInitrd + if (*(UINT32*)((UINTN)InitrdImage) == LINUX_UIMAGE_SIGNATURE) { + // Skip the 64-byte image header + InitrdImage = (EFI_PHYSICAL_ADDRESS)((UINTN)InitrdImage + 64); + InitrdImageSize -= 64; + } + } + + // Load the FDT binary from a device path. The FDT will be reloaded later to a more appropriate location for the Linux kernel. + FdtBlobBase = 0; + Status = BdsLoadImage (FdtDevicePath, AllocateAnyPages, &FdtBlobBase, &FdtBlobSize); + if (EFI_ERROR(Status)) { + Print (L"ERROR: Did not find Device Tree blob.\n"); + return Status; + } + + // Update the Fdt with the Initrd information. The FDT will increase in size. + // By setting address=0 we leave the memory allocation to the function + Status = PrepareFdt (CommandLineArguments, InitrdImage, InitrdImageSize, &FdtBlobBase, &FdtBlobSize); + if (EFI_ERROR(Status)) { + Print(L"ERROR: Can not load kernel with FDT. Status=%r\n", Status); + return Status; + } + + return StartLinux (LinuxImage, LinuxImageSize, FdtBlobBase, FdtBlobSize, ARM_FDT_MACHINE_TYPE); +} +