diff --git a/ArmPkg/Application/LinuxLoader/AArch64/LinuxStarter.c b/ArmPkg/Application/LinuxLoader/AArch64/LinuxStarter.c new file mode 100644 index 0000000000..0d540c920f --- /dev/null +++ b/ArmPkg/Application/LinuxLoader/AArch64/LinuxStarter.c @@ -0,0 +1,370 @@ +/** @file +* +* Copyright (c) 2011-2015, 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 +#include +#include +#include + +#include + +#include + +#include "LinuxLoader.h" + +/* + Linux kernel booting: Look at the doc in the Kernel source : + Documentation/arm64/booting.txt + The kernel image must be placed at the start of the memory to be used by the + kernel (2MB aligned) + 0x80000. + + The Device tree blob is expected to be under 2MB and be within the first 512MB + of kernel memory and be 2MB aligned. + + A Flattened Device Tree (FDT) used to boot linux needs to be updated before + the kernel is started. It needs to indicate how secondary cores are brought up + and where they are waiting before loading Linux. The FDT also needs to hold + the correct kernel command line and filesystem RAM-disk information. + At the moment we do not fully support generating this FDT information at + runtime. A prepared FDT should be provided at boot. FDT is the only supported + method for booting the AArch64 Linux kernel. + + Linux does not use any runtime services at this time, so we can let it + overwrite UEFI. +*/ + + +#define LINUX_ALIGN_VAL (0x080000) // 2MB + 0x80000 mask +#define LINUX_ALIGN_MASK (0x1FFFFF) // Bottom 21bits +#define ALIGN_2MB(addr) ALIGN_POINTER(addr , (2*1024*1024)) + +/* ARM32 and AArch64 kernel handover differ. + * x0 is set to FDT base. + * x1-x3 are reserved for future use and should be set to zero. + */ +typedef VOID (*LINUX_KERNEL64)(UINTN ParametersBase, UINTN Reserved0, + UINTN Reserved1, UINTN Reserved2); + +/* These externs are used to relocate some ASM code into Linux memory. */ +extern VOID *SecondariesPenStart; +extern VOID *SecondariesPenEnd; +extern UINTN *AsmMailboxbase; + + +STATIC +VOID +PreparePlatformHardware ( + VOID + ) +{ + //Note: Interrupts will be disabled by the GIC driver when ExitBootServices() will be called. + + // Clean before Disable else the Stack gets corrupted with old data. + ArmCleanDataCache (); + ArmDisableDataCache (); + // Invalidate all the entries that might have snuck in. + ArmInvalidateDataCache (); + + // Disable and invalidate the instruction cache + ArmDisableInstructionCache (); + ArmInvalidateInstructionCache (); + + // Turn off MMU + ArmDisableMmu (); +} + +STATIC +EFI_STATUS +StartLinux ( + IN EFI_PHYSICAL_ADDRESS LinuxImage, + IN UINTN LinuxImageSize, + IN EFI_PHYSICAL_ADDRESS FdtBlobBase, + IN UINTN FdtBlobSize + ) +{ + EFI_STATUS Status; + LINUX_KERNEL64 LinuxKernel = (LINUX_KERNEL64)LinuxImage; + + // Send msg to secondary cores to go to the kernel pen. + ArmGicSendSgiTo (PcdGet32 (PcdGicDistributorBase), ARM_GIC_ICDSGIR_FILTER_EVERYONEELSE, 0x0E, PcdGet32 (PcdGicSgiIntId)); + + // 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)); + return Status; + } + + // Check if the Linux Image is a uImage + if (*(UINTN*)LinuxKernel == LINUX_UIMAGE_SIGNATURE) { + // Assume the Image Entry Point is just after the uImage header (64-byte size) + LinuxKernel = (LINUX_KERNEL64)((UINTN)LinuxKernel + 64); + LinuxImageSize -= 64; + } + + // + // Switch off interrupts, caches, mmu, etc + // + PreparePlatformHardware (); + + // Register and print out performance information + PERF_END (NULL, "BDS", NULL, 0); + if (PerformanceMeasurementEnabled ()) { + PrintPerformance (); + } + + // + // Start the Linux Kernel + // + + // x1-x3 are reserved (set to zero) for future use. + LinuxKernel ((UINTN)FdtBlobBase, 0, 0, 0); + + // Kernel should never exit + // After Life services are not provided + ASSERT (FALSE); + + // We cannot recover the execution at this stage + while (1); +} + +/** + Start a Linux kernel from a Device Path + + @param SystemMemoryBase Base of the system memory + @param LinuxKernel Device Path to the Linux Kernel + @param Parameters Linux kernel arguments + @param Fdt Device Path to the Flat Device Tree + @param MachineType ARM machine type value + + @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. + @retval RETURN_UNSUPPORTED ATAG is not support by this architecture + +**/ +EFI_STATUS +BootLinuxAtag ( + IN EFI_PHYSICAL_ADDRESS SystemMemoryBase, + IN EFI_DEVICE_PATH_PROTOCOL* LinuxKernelDevicePath, + IN EFI_DEVICE_PATH_PROTOCOL* InitrdDevicePath, + IN CONST CHAR8* CommandLineArguments, + IN UINTN MachineType + ) +{ + // NOTE : AArch64 Linux kernel does not support ATAG, FDT only. + ASSERT (0); + + return EFI_UNSUPPORTED; +} + +/** + Start a Linux kernel from a Device Path + + @param[in] LinuxKernelDevicePath Device Path to the Linux Kernel + @param[in] InitrdDevicePath Device Path to the Initrd + @param[in] Arguments Linux kernel arguments + + @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 +BootLinuxFdt ( + IN EFI_PHYSICAL_ADDRESS SystemMemoryBase, + IN EFI_DEVICE_PATH_PROTOCOL* LinuxKernelDevicePath, + IN EFI_DEVICE_PATH_PROTOCOL* InitrdDevicePath, + IN EFI_DEVICE_PATH_PROTOCOL* FdtDevicePath, + IN CONST CHAR8* Arguments + ) +{ + EFI_STATUS Status; + EFI_STATUS PenBaseStatus; + UINTN LinuxImageSize; + UINTN InitrdImageSize; + UINTN InitrdImageBaseSize; + VOID *InstalledFdtBase; + UINTN FdtBlobSize; + EFI_PHYSICAL_ADDRESS FdtBlobBase; + EFI_PHYSICAL_ADDRESS LinuxImage; + EFI_PHYSICAL_ADDRESS InitrdImage; + EFI_PHYSICAL_ADDRESS InitrdImageBase; + ARM_PROCESSOR_TABLE *ArmProcessorTable; + ARM_CORE_INFO *ArmCoreInfoTable; + UINTN Index; + EFI_PHYSICAL_ADDRESS PenBase; + UINTN PenSize; + UINTN MailBoxBase; + + PenBaseStatus = EFI_UNSUPPORTED; + PenSize = 0; + InitrdImage = 0; + InitrdImageSize = 0; + InitrdImageBase = 0; + InitrdImageBaseSize = 0; + + PERF_START (NULL, "BDS", NULL, 0); + + // + // 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. + // If that fails fall back to try loading it within LINUX_KERNEL_MAX_OFFSET of memory start. + LinuxImage = SystemMemoryBase + 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 (%r).\n", Status); + return Status; + } + } + // 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 (InitrdDevicePath) { + InitrdImageBase = LINUX_KERNEL_MAX_OFFSET; + Status = BdsLoadImage (InitrdDevicePath, AllocateMaxAddress, &InitrdImageBase, &InitrdImageBaseSize); + if (Status == EFI_OUT_OF_RESOURCES) { + Status = BdsLoadImage (InitrdDevicePath, AllocateAnyPages, &InitrdImageBase, &InitrdImageBaseSize); + } + if (EFI_ERROR (Status)) { + Print (L"ERROR: Did not find initrd image (%r).\n", Status); + goto EXIT_FREE_LINUX; + } + + // Check if the initrd is a uInitrd + if (*(UINTN*)((UINTN)InitrdImageBase) == LINUX_UIMAGE_SIGNATURE) { + // Skip the 64-byte image header + InitrdImage = (EFI_PHYSICAL_ADDRESS)((UINTN)InitrdImageBase + 64); + InitrdImageSize = InitrdImageBaseSize - 64; + } else { + InitrdImage = InitrdImageBase; + InitrdImageSize = InitrdImageBaseSize; + } + } + + if (FdtDevicePath == NULL) { + // + // Get the FDT from the Configuration Table. + // The FDT will be reloaded in PrepareFdt() to a more appropriate + // location for the Linux Kernel. + // + Status = EfiGetSystemConfigurationTable (&gFdtTableGuid, &InstalledFdtBase); + if (EFI_ERROR (Status)) { + Print (L"ERROR: Did not get the Device Tree blob (%r).\n", Status); + goto EXIT_FREE_INITRD; + } + FdtBlobBase = (EFI_PHYSICAL_ADDRESS)InstalledFdtBase; + FdtBlobSize = fdt_totalsize (InstalledFdtBase); + } else { + // + // FDT device path explicitly defined. The FDT is relocated later to a + // more appropriate location for the Linux kernel. + // + FdtBlobBase = LINUX_KERNEL_MAX_OFFSET; + Status = BdsLoadImage (FdtDevicePath, AllocateMaxAddress, &FdtBlobBase, &FdtBlobSize); + if (EFI_ERROR (Status)) { + Print (L"ERROR: Did not find Device Tree blob (%r).\n", Status); + goto EXIT_FREE_INITRD; + } + } + + // + // Install secondary core pens if the Power State Coordination Interface is not supported + // + if (FeaturePcdGet (PcdArmLinuxSpinTable)) { + // 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. + if (MailBoxBase % sizeof (MailBoxBase) != 0) { + MailBoxBase += sizeof (MailBoxBase) - MailBoxBase % sizeof (MailBoxBase); + } + + CopyMem ( (VOID*)(PenBase), (VOID*)&SecondariesPenStart, PenSize); + + // Update the MailboxBase variable used in the pen code + *(UINTN*)(PenBase + ((UINTN)&AsmMailboxbase - (UINTN)&SecondariesPenStart)) = MailBoxBase; + + for (Index = 0; Index < gST->NumberOfTableEntries; Index++) { + // Check for correct GUID type + if (CompareGuid (&gArmMpCoreInfoGuid, &(gST->ConfigurationTable[Index].VendorGuid))) { + UINTN i; + + // Get them under our control. Move from depending on 32bit reg(sys_flags) and SWI + // to 64 bit addr and WFE + ArmProcessorTable = (ARM_PROCESSOR_TABLE *)gST->ConfigurationTable[Index].VendorTable; + ArmCoreInfoTable = ArmProcessorTable->ArmCpus; + + for (i = 0; i < ArmProcessorTable->NumberOfEntries; i++ ) { + // This goes into the SYSFLAGS register for the VE platform. We only have one 32bit reg to use + MmioWrite32 (ArmCoreInfoTable[i].MailboxSetAddress, (UINTN)PenBase); + + // So FDT can set the mailboxes correctly with the parser. These are 64bit Memory locations. + ArmCoreInfoTable[i].MailboxSetAddress = (UINTN)MailBoxBase + i*sizeof (MailBoxBase); + + // Clear the mailboxes for the respective cores + *((UINTN*)(ArmCoreInfoTable[i].MailboxSetAddress)) = 0x0; + } + } + } + // Flush caches to make sure our pen gets to mem before we free the cores. + ArmCleanDataCache (); + } + + // By setting address=0 we leave the memory allocation to the function + Status = PrepareFdt (SystemMemoryBase, Arguments, InitrdImage, InitrdImageSize, &FdtBlobBase, &FdtBlobSize); + if (EFI_ERROR (Status)) { + Print (L"ERROR: Can not load Linux kernel with Device Tree. Status=0x%X\n", Status); + goto EXIT_FREE_FDT; + } + + return StartLinux (LinuxImage, LinuxImageSize, FdtBlobBase, FdtBlobSize); + +EXIT_FREE_FDT: + if (!EFI_ERROR (PenBaseStatus)) { + gBS->FreePages (PenBase, EFI_SIZE_TO_PAGES (PenSize)); + } + + gBS->FreePages (FdtBlobBase, EFI_SIZE_TO_PAGES (FdtBlobSize)); + +EXIT_FREE_INITRD: + if (InitrdDevicePath) { + gBS->FreePages (InitrdImageBase, EFI_SIZE_TO_PAGES (InitrdImageBaseSize)); + } + +EXIT_FREE_LINUX: + gBS->FreePages (LinuxImage, EFI_SIZE_TO_PAGES (LinuxImageSize)); + + return Status; +} diff --git a/ArmPkg/Application/LinuxLoader/AArch64/LinuxStarterHelper.S b/ArmPkg/Application/LinuxLoader/AArch64/LinuxStarterHelper.S new file mode 100644 index 0000000000..525c1287ef --- /dev/null +++ b/ArmPkg/Application/LinuxLoader/AArch64/LinuxStarterHelper.S @@ -0,0 +1,58 @@ +// +// Copyright (c) 2011-2013, 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. +// +// + + +/* Secondary core pens for AArch64 Linux booting. + + This code it placed in Linux kernel memory and marked reserved. UEFI ensures + that the secondary cores get to this pen and the kernel can then start the + cores from here. + NOTE: This code must be self-contained. +*/ + +#include + +.text +.align 3 + +GCC_ASM_EXPORT(SecondariesPenStart) +ASM_GLOBAL SecondariesPenEnd + +ASM_PFX(SecondariesPenStart): + // Registers x0-x3 are reserved for future use and should be set to zero. + mov x0, xzr + mov x1, xzr + mov x2, xzr + mov x3, xzr + + // Get core position. Taken from ArmPlatformGetCorePosition(). + // Assumes max 4 cores per cluster. + mrs x4, mpidr_el1 // Get MPCore register. + and x5, x4, #ARM_CORE_MASK // Get core number. + and x4, x4, #ARM_CLUSTER_MASK // Get cluster number. + add x4, x5, x4, LSR #6 // Add scaled cluster number to core number. + lsl x4, x4, 3 // Get mailbox offset for this core. + ldr x5, AsmMailboxbase // Get mailbox addr relative to pc (36 bytes ahead). + add x4, x4, x5 // Add core mailbox offset to base of mailbox. +1: ldr x5, [x4] // Load value from mailbox. + cmp xzr, x5 // Has the mailbox been set? + b.ne 2f // If so break out of loop. + wfe // If not, wait a bit. + b 1b // Wait over, check if mailbox set again. +2: br x5 // Jump to mailbox value. + +.align 3 // Make sure the variable below is 8 byte aligned. + .global AsmMailboxbase +AsmMailboxbase: .xword 0xdeaddeadbeefbeef + +SecondariesPenEnd: diff --git a/ArmPkg/Application/LinuxLoader/Arm/LinuxAtag.c b/ArmPkg/Application/LinuxLoader/Arm/LinuxAtag.c new file mode 100644 index 0000000000..fd7ee9c862 --- /dev/null +++ b/ArmPkg/Application/LinuxLoader/Arm/LinuxAtag.c @@ -0,0 +1,177 @@ +/** @file +* +* Copyright (c) 2011-2015, 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 "LinuxAtag.h" +#include "LinuxLoader.h" + +// Point to the current ATAG +STATIC LINUX_ATAG *mLinuxKernelCurrentAtag; + +STATIC +VOID +SetupCoreTag ( + IN UINT32 PageSize + ) +{ + mLinuxKernelCurrentAtag->header.size = tag_size (LINUX_ATAG_CORE); + mLinuxKernelCurrentAtag->header.type = ATAG_CORE; + + mLinuxKernelCurrentAtag->body.core_tag.flags = 1; /* ensure read-only */ + mLinuxKernelCurrentAtag->body.core_tag.pagesize = PageSize; /* systems PageSize (4k) */ + mLinuxKernelCurrentAtag->body.core_tag.rootdev = 0; /* zero root device (typically overridden from kernel command line )*/ + + // move pointer to next tag + mLinuxKernelCurrentAtag = next_tag_address (mLinuxKernelCurrentAtag); +} + +STATIC +VOID +SetupMemTag ( + IN UINTN StartAddress, + IN UINT32 Size + ) +{ + mLinuxKernelCurrentAtag->header.size = tag_size (LINUX_ATAG_MEM); + mLinuxKernelCurrentAtag->header.type = ATAG_MEM; + + mLinuxKernelCurrentAtag->body.mem_tag.start = StartAddress; /* Start of memory chunk for AtagMem */ + mLinuxKernelCurrentAtag->body.mem_tag.size = Size; /* Size of memory chunk for AtagMem */ + + // move pointer to next tag + mLinuxKernelCurrentAtag = next_tag_address (mLinuxKernelCurrentAtag); +} + +STATIC +VOID +SetupCmdlineTag ( + IN CONST CHAR8 *CmdLine + ) +{ + UINT32 LineLength; + + // Increment the line length by 1 to account for the null string terminator character + LineLength = AsciiStrLen (CmdLine) + 1; + + /* Check for NULL strings. + * Do not insert a tag for an empty CommandLine, don't even modify the tag address pointer. + * Remember, you have at least one null string terminator character. + */ + if (LineLength > 1) { + mLinuxKernelCurrentAtag->header.size = ((UINT32)sizeof (LINUX_ATAG_HEADER) + LineLength + (UINT32)3) >> 2; + mLinuxKernelCurrentAtag->header.type = ATAG_CMDLINE; + + /* place CommandLine into tag */ + AsciiStrCpy (mLinuxKernelCurrentAtag->body.cmdline_tag.cmdline, CmdLine); + + // move pointer to next tag + mLinuxKernelCurrentAtag = next_tag_address (mLinuxKernelCurrentAtag); + } +} + +STATIC +VOID +SetupInitrdTag ( + IN UINT32 InitrdImage, + IN UINT32 InitrdImageSize + ) +{ + mLinuxKernelCurrentAtag->header.size = tag_size (LINUX_ATAG_INITRD2); + mLinuxKernelCurrentAtag->header.type = ATAG_INITRD2; + + mLinuxKernelCurrentAtag->body.initrd2_tag.start = InitrdImage; + mLinuxKernelCurrentAtag->body.initrd2_tag.size = InitrdImageSize; + + // Move pointer to next tag + mLinuxKernelCurrentAtag = next_tag_address (mLinuxKernelCurrentAtag); +} +STATIC +VOID +SetupEndTag ( + VOID + ) +{ + // Empty tag ends list; this has zero length and no body + mLinuxKernelCurrentAtag->header.type = ATAG_NONE; + mLinuxKernelCurrentAtag->header.size = 0; + + /* We can not calculate the next address by using the standard macro: + * Params = next_tag_address (Params); + * because it relies on the header.size, which here it is 0 (zero). + * The easiest way is to add the sizeof (mLinuxKernelCurrentAtag->header). + */ + mLinuxKernelCurrentAtag = (LINUX_ATAG*)((UINT32)mLinuxKernelCurrentAtag + sizeof (mLinuxKernelCurrentAtag->header)); +} + +EFI_STATUS +PrepareAtagList ( + IN EFI_PHYSICAL_ADDRESS SystemMemoryBase, + IN CONST CHAR8* CommandLineString, + IN EFI_PHYSICAL_ADDRESS InitrdImage, + IN UINTN InitrdImageSize, + OUT EFI_PHYSICAL_ADDRESS *AtagBase, + OUT UINT32 *AtagSize + ) +{ + EFI_STATUS Status; + LIST_ENTRY *ResourceLink; + LIST_ENTRY ResourceList; + EFI_PHYSICAL_ADDRESS AtagStartAddress; + SYSTEM_MEMORY_RESOURCE *Resource; + + AtagStartAddress = LINUX_ATAG_MAX_OFFSET; + Status = gBS->AllocatePages (AllocateMaxAddress, EfiBootServicesData, EFI_SIZE_TO_PAGES (ATAG_MAX_SIZE), &AtagStartAddress); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_WARN, "Warning: Failed to allocate Atag at 0x%lX (%r). The Atag will be allocated somewhere else in System Memory.\n", AtagStartAddress, Status)); + Status = gBS->AllocatePages (AllocateAnyPages, EfiBootServicesData, EFI_SIZE_TO_PAGES (ATAG_MAX_SIZE), &AtagStartAddress); + if (EFI_ERROR (Status)) { + return Status; + } + } + + // Ready to setup the atag list + mLinuxKernelCurrentAtag = (LINUX_ATAG*)(UINTN)AtagStartAddress; + + // Standard core tag 4k PageSize + SetupCoreTag ( (UINT32)SIZE_4KB ); + + // Physical memory setup + GetSystemMemoryResources (&ResourceList); + ResourceLink = ResourceList.ForwardLink; + while (ResourceLink != NULL && ResourceLink != &ResourceList) { + Resource = (SYSTEM_MEMORY_RESOURCE*)ResourceLink; + DEBUG ((EFI_D_INFO, "- [0x%08X,0x%08X]\n", + (UINT32)Resource->PhysicalStart, + (UINT32)Resource->PhysicalStart + (UINT32)Resource->ResourceLength)); + SetupMemTag ((UINT32)Resource->PhysicalStart, (UINT32)Resource->ResourceLength ); + ResourceLink = ResourceLink->ForwardLink; + } + + // CommandLine setting root device + if (CommandLineString) { + SetupCmdlineTag (CommandLineString); + } + + if (InitrdImageSize > 0 && InitrdImage != 0) { + SetupInitrdTag ((UINT32)InitrdImage, (UINT32)InitrdImageSize); + } + + // End of tags + SetupEndTag (); + + // Calculate atag list size + *AtagBase = AtagStartAddress; + *AtagSize = (UINT32)mLinuxKernelCurrentAtag - (UINT32)AtagStartAddress + 1; + + return EFI_SUCCESS; +} diff --git a/ArmPkg/Application/LinuxLoader/Arm/LinuxAtag.h b/ArmPkg/Application/LinuxLoader/Arm/LinuxAtag.h new file mode 100644 index 0000000000..b4c7e5986e --- /dev/null +++ b/ArmPkg/Application/LinuxLoader/Arm/LinuxAtag.h @@ -0,0 +1,124 @@ +/** @file +* +* Copyright (c) 2011-2015, 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. +* +**/ + +#ifndef __LINUX_ATAG_H__ +#define __LINUX_ATAG_H__ + +// +// ATAG Definitions +// + +#define ATAG_MAX_SIZE 0x3000 + +/* ATAG : list of possible tags */ +#define ATAG_NONE 0x00000000 +#define ATAG_CORE 0x54410001 +#define ATAG_MEM 0x54410002 +#define ATAG_VIDEOTEXT 0x54410003 +#define ATAG_RAMDISK 0x54410004 +#define ATAG_INITRD2 0x54420005 +#define ATAG_SERIAL 0x54410006 +#define ATAG_REVISION 0x54410007 +#define ATAG_VIDEOLFB 0x54410008 +#define ATAG_CMDLINE 0x54410009 +#define ATAG_ARM_MP_CORE 0x5441000A + +#define next_tag_address(t) ((LINUX_ATAG*)((UINT32)(t) + (((t)->header.size) << 2) )) +#define tag_size(type) ((UINT32)((sizeof(LINUX_ATAG_HEADER) + sizeof(type)) >> 2)) + +typedef struct { + UINT32 size; /* length of tag in words including this header */ + UINT32 type; /* tag type */ +} LINUX_ATAG_HEADER; + +typedef struct { + UINT32 flags; + UINT32 pagesize; + UINT32 rootdev; +} LINUX_ATAG_CORE; + +typedef struct { + UINT32 size; + UINTN start; +} LINUX_ATAG_MEM; + +typedef struct { + UINT8 x; + UINT8 y; + UINT16 video_page; + UINT8 video_mode; + UINT8 video_cols; + UINT16 video_ega_bx; + UINT8 video_lines; + UINT8 video_isvga; + UINT16 video_points; +} LINUX_ATAG_VIDEOTEXT; + +typedef struct { + UINT32 flags; + UINT32 size; + UINTN start; +} LINUX_ATAG_RAMDISK; + +typedef struct { + UINT32 start; + UINT32 size; +} LINUX_ATAG_INITRD2; + +typedef struct { + UINT32 low; + UINT32 high; +} LINUX_ATAG_SERIALNR; + +typedef struct { + UINT32 rev; +} LINUX_ATAG_REVISION; + +typedef struct { + UINT16 lfb_width; + UINT16 lfb_height; + UINT16 lfb_depth; + UINT16 lfb_linelength; + UINT32 lfb_base; + UINT32 lfb_size; + UINT8 red_size; + UINT8 red_pos; + UINT8 green_size; + UINT8 green_pos; + UINT8 blue_size; + UINT8 blue_pos; + UINT8 rsvd_size; + UINT8 rsvd_pos; +} LINUX_ATAG_VIDEOLFB; + +typedef struct { + CHAR8 cmdline[1]; +} LINUX_ATAG_CMDLINE; + +typedef struct { + LINUX_ATAG_HEADER header; + union { + LINUX_ATAG_CORE core_tag; + LINUX_ATAG_MEM mem_tag; + LINUX_ATAG_VIDEOTEXT videotext_tag; + LINUX_ATAG_RAMDISK ramdisk_tag; + LINUX_ATAG_INITRD2 initrd2_tag; + LINUX_ATAG_SERIALNR serialnr_tag; + LINUX_ATAG_REVISION revision_tag; + LINUX_ATAG_VIDEOLFB videolfb_tag; + LINUX_ATAG_CMDLINE cmdline_tag; + } body; +} LINUX_ATAG; + +#endif /* __LINUX_ATAG_H__ */ diff --git a/ArmPkg/Application/LinuxLoader/Arm/LinuxStarter.c b/ArmPkg/Application/LinuxLoader/Arm/LinuxStarter.c new file mode 100644 index 0000000000..960fdd6b3c --- /dev/null +++ b/ArmPkg/Application/LinuxLoader/Arm/LinuxStarter.c @@ -0,0 +1,346 @@ +/** @file +* +* Copyright (c) 2011-2015, 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 +#include + +#include + +#include "LinuxLoader.h" + +#define ALIGN32_BELOW(addr) ALIGN_POINTER(addr - 32,32) + +#define IS_ADDRESS_IN_REGION(RegionStart, RegionSize, Address) \ + (((UINTN)(RegionStart) <= (UINTN)(Address)) && ((UINTN)(Address) <= ((UINTN)(RegionStart) + (UINTN)(RegionSize)))) + +EFI_STATUS +PrepareAtagList ( + IN EFI_PHYSICAL_ADDRESS SystemMemoryBase, + IN CONST CHAR8* CommandLineString, + IN EFI_PHYSICAL_ADDRESS InitrdImage, + IN UINTN InitrdImageSize, + OUT EFI_PHYSICAL_ADDRESS *AtagBase, + OUT UINT32 *AtagSize + ); + +STATIC +VOID +PreparePlatformHardware ( + VOID + ) +{ + //Note: Interrupts will be disabled by the GIC driver when ExitBootServices() will be called. + + // Clean before Disable else the Stack gets corrupted with old data. + ArmCleanDataCache (); + ArmDisableDataCache (); + // Invalidate all the entries that might have snuck in. + ArmInvalidateDataCache (); + + // Invalidate and disable the Instruction cache + ArmDisableInstructionCache (); + ArmInvalidateInstructionCache (); + + // Turn off MMU + ArmDisableMmu (); +} + +STATIC +EFI_STATUS +StartLinux ( + IN EFI_PHYSICAL_ADDRESS SystemMemoryBase, + 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)); + return Status; + } + + // 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; + } + + // Check there is no overlapping between kernel and its parameters + // We can only assert because it is too late to fallback to UEFI (ExitBootServices has been called). + ASSERT (!IS_ADDRESS_IN_REGION (LinuxKernel, LinuxImageSize, KernelParamsAddress) && + !IS_ADDRESS_IN_REGION (LinuxKernel, LinuxImageSize, KernelParamsAddress + KernelParamsSize)); + + // + // Switch off interrupts, caches, mmu, etc + // + PreparePlatformHardware (); + + // 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); + // We cannot recover the execution at this stage + while (1); +} + +/** + Start a Linux kernel from a Device Path + + @param SystemMemoryBase Base of the system memory + @param LinuxKernel Device Path to the Linux Kernel + @param Parameters Linux kernel arguments + @param Fdt Device Path to the Flat Device Tree + @param MachineType ARM machine type value + + @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. + @retval RETURN_UNSUPPORTED ATAG is not support by this architecture + +**/ +EFI_STATUS +BootLinuxAtag ( + IN EFI_PHYSICAL_ADDRESS SystemMemoryBase, + IN EFI_DEVICE_PATH_PROTOCOL* LinuxKernelDevicePath, + IN EFI_DEVICE_PATH_PROTOCOL* InitrdDevicePath, + IN CONST CHAR8* CommandLineArguments, + IN UINTN MachineType + ) +{ + EFI_STATUS Status; + UINT32 LinuxImageSize; + UINT32 InitrdImageBaseSize = 0; + UINT32 InitrdImageSize = 0; + UINT32 AtagSize; + EFI_PHYSICAL_ADDRESS AtagBase; + EFI_PHYSICAL_ADDRESS LinuxImage; + EFI_PHYSICAL_ADDRESS InitrdImageBase = 0; + EFI_PHYSICAL_ADDRESS InitrdImage = 0; + + 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 + InitrdImageBase = LINUX_KERNEL_MAX_OFFSET; + Status = BdsLoadImage (InitrdDevicePath, AllocateMaxAddress, &InitrdImageBase, &InitrdImageBaseSize); + if (Status == EFI_OUT_OF_RESOURCES) { + Status = BdsLoadImage (InitrdDevicePath, AllocateAnyPages, &InitrdImageBase, &InitrdImageBaseSize); + } + if (EFI_ERROR (Status)) { + Print (L"ERROR: Did not find initrd image.\n"); + goto EXIT_FREE_LINUX; + } + + // Check if the initrd is a uInitrd + if (*(UINT32*)((UINTN)InitrdImageBase) == LINUX_UIMAGE_SIGNATURE) { + // Skip the 64-byte image header + InitrdImage = (EFI_PHYSICAL_ADDRESS)((UINTN)InitrdImageBase + 64); + InitrdImageSize = InitrdImageBaseSize - 64; + } else { + InitrdImage = InitrdImageBase; + InitrdImageSize = InitrdImageBaseSize; + } + } + + // + // Setup the Linux Kernel Parameters + // + + // By setting address=0 we leave the memory allocation to the function + Status = PrepareAtagList (SystemMemoryBase, CommandLineArguments, InitrdImage, InitrdImageSize, &AtagBase, &AtagSize); + if (EFI_ERROR (Status)) { + Print (L"ERROR: Can not prepare ATAG list. Status=0x%X\n", Status); + goto EXIT_FREE_INITRD; + } + + return StartLinux (SystemMemoryBase, LinuxImage, LinuxImageSize, AtagBase, AtagSize, MachineType); + +EXIT_FREE_INITRD: + if (InitrdDevicePath) { + gBS->FreePages (InitrdImageBase, EFI_SIZE_TO_PAGES (InitrdImageBaseSize)); + } + +EXIT_FREE_LINUX: + gBS->FreePages (LinuxImage, EFI_SIZE_TO_PAGES (LinuxImageSize)); + + return Status; +} + +/** + Start a Linux kernel from a Device Path + + @param LinuxKernelDevicePath Device Path to the Linux Kernel + @param InitrdDevicePath Device Path to the Initrd + @param CommandLineArguments Linux command line + + @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 +BootLinuxFdt ( + IN EFI_PHYSICAL_ADDRESS SystemMemoryBase, + IN EFI_DEVICE_PATH_PROTOCOL* LinuxKernelDevicePath, + IN EFI_DEVICE_PATH_PROTOCOL* InitrdDevicePath, + IN EFI_DEVICE_PATH_PROTOCOL* FdtDevicePath, + IN CONST CHAR8* CommandLineArguments + ) +{ + EFI_STATUS Status; + UINT32 LinuxImageSize; + UINT32 InitrdImageBaseSize = 0; + UINT32 InitrdImageSize = 0; + VOID *InstalledFdtBase; + UINT32 FdtBlobSize; + EFI_PHYSICAL_ADDRESS FdtBlobBase; + EFI_PHYSICAL_ADDRESS LinuxImage; + EFI_PHYSICAL_ADDRESS InitrdImageBase = 0; + EFI_PHYSICAL_ADDRESS InitrdImage = 0; + + 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) { + InitrdImageBase = LINUX_KERNEL_MAX_OFFSET; + Status = BdsLoadImage (InitrdDevicePath, AllocateMaxAddress, &InitrdImageBase, &InitrdImageBaseSize); + if (Status == EFI_OUT_OF_RESOURCES) { + Status = BdsLoadImage (InitrdDevicePath, AllocateAnyPages, &InitrdImageBase, &InitrdImageBaseSize); + } + if (EFI_ERROR (Status)) { + Print (L"ERROR: Did not find initrd image.\n"); + goto EXIT_FREE_LINUX; + } + + // Check if the initrd is a uInitrd + if (*(UINT32*)((UINTN)InitrdImageBase) == LINUX_UIMAGE_SIGNATURE) { + // Skip the 64-byte image header + InitrdImage = (EFI_PHYSICAL_ADDRESS)((UINTN)InitrdImageBase + 64); + InitrdImageSize = InitrdImageBaseSize - 64; + } else { + InitrdImage = InitrdImageBase; + InitrdImageSize = InitrdImageBaseSize; + } + } + + if (FdtDevicePath == NULL) { + // + // Get the FDT from the Configuration Table. + // The FDT will be reloaded in PrepareFdt() to a more appropriate + // location for the Linux Kernel. + // + Status = EfiGetSystemConfigurationTable (&gFdtTableGuid, &InstalledFdtBase); + if (EFI_ERROR (Status)) { + Print (L"ERROR: Did not get the Device Tree blob (%r).\n", Status); + goto EXIT_FREE_INITRD; + } + FdtBlobBase = (EFI_PHYSICAL_ADDRESS)(UINTN)InstalledFdtBase; + FdtBlobSize = fdt_totalsize (InstalledFdtBase); + } else { + // + // FDT device path explicitly defined. The FDT is relocated later to a + // more appropriate location for the Linux kernel. + // + FdtBlobBase = LINUX_KERNEL_MAX_OFFSET; + Status = BdsLoadImage (FdtDevicePath, AllocateMaxAddress, &FdtBlobBase, &FdtBlobSize); + if (EFI_ERROR (Status)) { + Print (L"ERROR: Did not find Device Tree blob (%r).\n", Status); + goto EXIT_FREE_INITRD; + } + } + + // 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 (SystemMemoryBase, CommandLineArguments, InitrdImage, InitrdImageSize, &FdtBlobBase, &FdtBlobSize); + if (EFI_ERROR (Status)) { + Print (L"ERROR: Can not load kernel with FDT. Status=%r\n", Status); + goto EXIT_FREE_FDT; + } + + return StartLinux (SystemMemoryBase, LinuxImage, LinuxImageSize, FdtBlobBase, FdtBlobSize, ARM_FDT_MACHINE_TYPE); + +EXIT_FREE_FDT: + gBS->FreePages (FdtBlobBase, EFI_SIZE_TO_PAGES (FdtBlobSize)); + +EXIT_FREE_INITRD: + if (InitrdDevicePath) { + gBS->FreePages (InitrdImageBase, EFI_SIZE_TO_PAGES (InitrdImageBaseSize)); + } + +EXIT_FREE_LINUX: + gBS->FreePages (LinuxImage, EFI_SIZE_TO_PAGES (LinuxImageSize)); + + return Status; +} diff --git a/ArmPkg/Application/LinuxLoader/LinuxAtagLoader.c b/ArmPkg/Application/LinuxLoader/LinuxAtagLoader.c deleted file mode 100644 index 50a4b474cf..0000000000 --- a/ArmPkg/Application/LinuxLoader/LinuxAtagLoader.c +++ /dev/null @@ -1,103 +0,0 @@ -/** @file -* -* Copyright (c) 2011, 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 "LinuxInternal.h" - -#include -#include - -/** - The user Entry Point for Application. The user code starts with this function - as the real entry point for the application. - - @param[in] ImageHandle The firmware allocated handle for the EFI image. - @param[in] SystemTable A pointer to the EFI System Table. - - @retval EFI_SUCCESS The entry point is executed successfully. - @retval other Some error occurs when executing this entry point. - -**/ -EFI_STATUS -EFIAPI -UefiMain ( - IN EFI_HANDLE ImageHandle, - IN EFI_SYSTEM_TABLE *SystemTable - ) -{ - EFI_STATUS Status; - EFI_LOADED_IMAGE_PROTOCOL *LoadedImage; - LINUX_LOADER_OPTIONAL_DATA* LinuxOptionalData; - EFI_DEVICE_PATH* DevicePathKernel; - EFI_DEVICE_PATH* InitrdDevicePath; - CHAR16* OptionalDataInitrd; - CHAR8* OptionalDataArguments; - CHAR16* Initrd; - - Status = gBS->HandleProtocol (ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **)&LoadedImage); - ASSERT_EFI_ERROR (Status); - - if (LoadedImage->LoadOptionsSize == 0) { - Status = LinuxLoaderConfig (LoadedImage); - } else { - // Ensure the signature is correct - LinuxOptionalData = (LINUX_LOADER_OPTIONAL_DATA*)LoadedImage->LoadOptions; - if (LinuxOptionalData->Signature != LINUX_LOADER_SIGNATURE) { - return EFI_UNSUPPORTED; - } - - // Generate the File Path Node for the Linux Kernel - DevicePathKernel = FileDevicePath (LoadedImage->DeviceHandle, LINUX_KERNEL_NAME); - - if (LinuxOptionalData->CmdLineLength > 0) { - OptionalDataArguments = (CHAR8*)LinuxOptionalData + sizeof(LINUX_LOADER_OPTIONAL_DATA); - } else { - OptionalDataArguments = NULL; - } - - if (LinuxOptionalData->InitrdPathListLength > 0) { - if (OptionalDataArguments != NULL) { - OptionalDataInitrd = (CHAR16*)(OptionalDataArguments + LinuxOptionalData->CmdLineLength); - } else { - OptionalDataInitrd = (CHAR16*)LinuxOptionalData + sizeof(LINUX_LOADER_OPTIONAL_DATA); - } - - // If pointer not aligned - if ((UINTN)OptionalDataInitrd & 0x1) { - Initrd = (CHAR16*)AllocateCopyPool (LinuxOptionalData->InitrdPathListLength, OptionalDataInitrd); - } else { - Initrd = OptionalDataInitrd; - } - - InitrdDevicePath = FileDevicePath (LoadedImage->DeviceHandle, Initrd); - } else { - OptionalDataInitrd = NULL; - InitrdDevicePath = NULL; - Initrd = NULL; - } - - // Load and Start the Linux Kernel (we should never return) - Status = BdsBootLinuxAtag (DevicePathKernel, InitrdDevicePath, OptionalDataArguments); - - if ((UINTN)OptionalDataInitrd & 0x1) { - FreePool (Initrd); - } - - FreePool (DevicePathKernel); - if (InitrdDevicePath) { - FreePool (InitrdDevicePath); - } - } - - return Status; -} diff --git a/ArmPkg/Application/LinuxLoader/LinuxAtagLoader.inf b/ArmPkg/Application/LinuxLoader/LinuxAtagLoader.inf deleted file mode 100644 index dcc2597c24..0000000000 --- a/ArmPkg/Application/LinuxLoader/LinuxAtagLoader.inf +++ /dev/null @@ -1,39 +0,0 @@ -#/* @file -# Copyright (c) 2011-2013, 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. -# -#*/ - -[Defines] - INF_VERSION = 0x00010006 - BASE_NAME = LinuxAtagLoader - FILE_GUID = a912f198-7f0e-4803-b908-b757b806ec83 - MODULE_TYPE = UEFI_APPLICATION - VERSION_STRING = 0.1 - ENTRY_POINT = UefiMain - -[Sources] - LinuxAtagLoader.c - LinuxConfig.c - -[Packages] - ArmPkg/ArmPkg.dec - MdePkg/MdePkg.dec - -[LibraryClasses] - BdsLib - DxeServicesTableLib - UefiLib - UefiApplicationEntryPoint - -[Protocols] - gEfiLoadedImageProtocolGuid - gEfiDevicePathToTextProtocolGuid - diff --git a/ArmPkg/Application/LinuxLoader/LinuxConfig.c b/ArmPkg/Application/LinuxLoader/LinuxConfig.c deleted file mode 100644 index c88dc65e93..0000000000 --- a/ArmPkg/Application/LinuxLoader/LinuxConfig.c +++ /dev/null @@ -1,354 +0,0 @@ -/** @file -* -* Copyright (c) 2011-2013, 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 "LinuxInternal.h" - -#define DEFAULT_BOOT_ENTRY_DESCRIPTION L"Linux" -#define MAX_STR_INPUT 300 -#define MAX_ASCII_INPUT 300 - -typedef enum { - LINUX_LOADER_NEW = 1, - LINUX_LOADER_UPDATE -} LINUX_LOADER_ACTION; - -STATIC -EFI_STATUS -EditHIInputStr ( - IN OUT CHAR16 *CmdLine, - IN UINTN MaxCmdLine - ) -{ - UINTN CmdLineIndex; - UINTN WaitIndex; - CHAR8 Char; - EFI_INPUT_KEY Key; - EFI_STATUS Status; - - Print (CmdLine); - - for (CmdLineIndex = StrLen (CmdLine); CmdLineIndex < MaxCmdLine; ) { - Status = gBS->WaitForEvent (1, &gST->ConIn->WaitForKey, &WaitIndex); - ASSERT_EFI_ERROR (Status); - - Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key); - ASSERT_EFI_ERROR (Status); - - // Unicode character is valid when Scancode is NUll - if (Key.ScanCode == SCAN_NULL) { - // Scan code is NUll, hence read Unicode character - Char = (CHAR8)Key.UnicodeChar; - } else { - Char = CHAR_NULL; - } - - if ((Char == CHAR_LINEFEED) || (Char == CHAR_CARRIAGE_RETURN) || (Char == 0x7f)) { - CmdLine[CmdLineIndex] = '\0'; - Print (L"\n\r"); - - return EFI_SUCCESS; - } else if ((Key.UnicodeChar == L'\b') || (Key.ScanCode == SCAN_LEFT) || (Key.ScanCode == SCAN_DELETE)){ - if (CmdLineIndex != 0) { - CmdLineIndex--; - Print (L"\b \b"); - } - } else if ((Key.ScanCode == SCAN_ESC) || (Char == 0x1B) || (Char == 0x0)) { - return EFI_INVALID_PARAMETER; - } else { - CmdLine[CmdLineIndex++] = Key.UnicodeChar; - Print (L"%c", Key.UnicodeChar); - } - } - - return EFI_SUCCESS; -} - -STATIC -EFI_STATUS -EditHIInputAscii ( - IN OUT CHAR8 *CmdLine, - IN UINTN MaxCmdLine - ) -{ - CHAR16* Str; - EFI_STATUS Status; - - Str = (CHAR16*)AllocatePool (MaxCmdLine * sizeof(CHAR16)); - AsciiStrToUnicodeStr (CmdLine, Str); - - Status = EditHIInputStr (Str, MaxCmdLine); - - UnicodeStrToAsciiStr (Str, CmdLine); - FreePool (Str); - - return Status; -} - -STATIC -EFI_STATUS -GetHIInputInteger ( - OUT UINTN *Integer - ) -{ - CHAR16 CmdLine[255]; - EFI_STATUS Status; - - CmdLine[0] = '\0'; - Status = EditHIInputStr (CmdLine, 255); - if (!EFI_ERROR(Status)) { - *Integer = StrDecimalToUintn (CmdLine); - } - - return Status; -} - -#if 0 -EFI_STATUS -GenerateDeviceDescriptionName ( - IN EFI_HANDLE Handle, - IN OUT CHAR16* Description - ) -{ - EFI_STATUS Status; - EFI_COMPONENT_NAME_PROTOCOL* ComponentName2Protocol; - EFI_DEVICE_PATH_TO_TEXT_PROTOCOL* DevicePathToTextProtocol; - EFI_DEVICE_PATH_PROTOCOL* DevicePathProtocol; - CHAR16* DriverName; - CHAR16* DevicePathTxt; - EFI_DEVICE_PATH* DevicePathNode; - - ComponentName2Protocol = NULL; - Status = gBS->HandleProtocol (Handle, &gEfiComponentName2ProtocolGuid, (VOID **)&ComponentName2Protocol); - if (!EFI_ERROR(Status)) { - //TODO: Fixme. we must find the best langague - Status = ComponentName2Protocol->GetDriverName (ComponentName2Protocol,"en",&DriverName); - if (!EFI_ERROR(Status)) { - StrnCpy (Description,DriverName,BOOT_DEVICE_DESCRIPTION_MAX); - } - } - - if (EFI_ERROR(Status)) { - // Use the lastest non null entry of the Device path as a description - Status = gBS->HandleProtocol (Handle, &gEfiDevicePathProtocolGuid, (VOID **)&DevicePathProtocol); - if (EFI_ERROR(Status)) { - return Status; - } - - // Convert the last non end-type Device Path Node in text for the description - DevicePathNode = GetLastDevicePathNode (DevicePathProtocol); - Status = gBS->LocateProtocol (&gEfiDevicePathToTextProtocolGuid, NULL, (VOID **)&DevicePathToTextProtocol); - ASSERT_EFI_ERROR(Status); - DevicePathTxt = DevicePathToTextProtocol->ConvertDevicePathToText(DevicePathNode,TRUE,TRUE); - StrnCpy (Description, DevicePathTxt, BOOT_DEVICE_DESCRIPTION_MAX); - FreePool (DevicePathTxt); - } - - return EFI_SUCCESS; -} -#endif - -EFI_STATUS -LinuxLoaderConfig ( - IN EFI_LOADED_IMAGE_PROTOCOL *LoadedImage - ) -{ - EFI_STATUS Status; - LINUX_LOADER_ACTION Choice; - UINTN BootOrderSize; - UINT16* BootOrder; - UINTN BootOrderCount; - UINTN Index; - CHAR16 Description[MAX_ASCII_INPUT]; - CHAR8 CmdLine[MAX_ASCII_INPUT]; - CHAR16 Initrd[MAX_STR_INPUT]; - UINT16 InitrdPathListLength; - UINT16 CmdLineLength; - BDS_LOAD_OPTION* BdsLoadOption; - BDS_LOAD_OPTION** SupportedBdsLoadOptions; - UINTN SupportedBdsLoadOptionCount; - LINUX_LOADER_OPTIONAL_DATA* LinuxOptionalData; - EFI_DEVICE_PATH* DevicePathRoot; - - Choice = (LINUX_LOADER_ACTION)0; - SupportedBdsLoadOptions = NULL; - SupportedBdsLoadOptionCount = 0; - - do { - Print (L"[%d] Create new Linux Boot Entry\n",LINUX_LOADER_NEW); - Print (L"[%d] Update Linux Boot Entry\n",LINUX_LOADER_UPDATE); - - Print (L"Option: "); - Status = GetHIInputInteger ((UINTN*)&Choice); - if (Status == EFI_INVALID_PARAMETER) { - Print (L"\n"); - return Status; - } else if ((Choice != LINUX_LOADER_NEW) && (Choice != LINUX_LOADER_UPDATE)) { - Print (L"Error: the option should be either '%d' or '%d'\n",LINUX_LOADER_NEW,LINUX_LOADER_UPDATE); - Status = EFI_INVALID_PARAMETER; - } - } while (EFI_ERROR(Status)); - - if (Choice == LINUX_LOADER_UPDATE) { - // If no compatible entry then we just create a new entry - Choice = LINUX_LOADER_NEW; - - // Scan the OptionalData of every entry for the correct signature - Status = GetGlobalEnvironmentVariable (L"BootOrder", NULL, &BootOrderSize, (VOID**)&BootOrder); - if (!EFI_ERROR(Status)) { - BootOrderCount = BootOrderSize / sizeof(UINT16); - - // Allocate an array to handle maximum number of supported Boot Entry - SupportedBdsLoadOptions = (BDS_LOAD_OPTION**)AllocatePool(sizeof(BDS_LOAD_OPTION*) * BootOrderCount); - - SupportedBdsLoadOptionCount = 0; - - // Check if the signature is present in the list of the current Boot entries - for (Index = 0; Index < BootOrderCount; Index++) { - Status = BootOptionFromLoadOptionIndex (BootOrder[Index], &BdsLoadOption); - if (!EFI_ERROR(Status)) { - if ((BdsLoadOption->OptionalDataSize >= sizeof(UINT32)) && - (*(UINT32*)BdsLoadOption->OptionalData == LINUX_LOADER_SIGNATURE)) { - SupportedBdsLoadOptions[SupportedBdsLoadOptionCount++] = BdsLoadOption; - Choice = LINUX_LOADER_UPDATE; - } - } - } - } - FreePool (BootOrder); - } - - if (Choice == LINUX_LOADER_NEW) { - Description[0] = '\0'; - CmdLine[0] = '\0'; - Initrd[0] = '\0'; - - BdsLoadOption = (BDS_LOAD_OPTION*)AllocateZeroPool (sizeof(BDS_LOAD_OPTION)); - - DEBUG_CODE_BEGIN(); - CHAR16* DevicePathTxt; - EFI_DEVICE_PATH_TO_TEXT_PROTOCOL* DevicePathToTextProtocol; - - Status = gBS->LocateProtocol (&gEfiDevicePathToTextProtocolGuid, NULL, (VOID **)&DevicePathToTextProtocol); - ASSERT_EFI_ERROR(Status); - DevicePathTxt = DevicePathToTextProtocol->ConvertDevicePathToText (LoadedImage->FilePath, TRUE, TRUE); - - Print(L"EFI OS Loader: %s\n",DevicePathTxt); - - FreePool(DevicePathTxt); - DEBUG_CODE_END(); - - // - // Fill the known fields of BdsLoadOption - // - - BdsLoadOption->Attributes = LOAD_OPTION_ACTIVE | LOAD_OPTION_CATEGORY_BOOT; - - // Get the full Device Path for this file - Status = gBS->HandleProtocol (LoadedImage->DeviceHandle, &gEfiDevicePathProtocolGuid, (VOID **)&DevicePathRoot); - ASSERT_EFI_ERROR(Status); - - BdsLoadOption->FilePathList = AppendDevicePath (DevicePathRoot, LoadedImage->FilePath); - BdsLoadOption->FilePathListLength = GetDevicePathSize (BdsLoadOption->FilePathList); - } else { - if (SupportedBdsLoadOptionCount > 1) { - for (Index = 0; Index < SupportedBdsLoadOptionCount; Index++) { - Print (L"[%d] %s\n",Index + 1,SupportedBdsLoadOptions[Index]->Description); - } - - do { - Print (L"Update Boot Entry: "); - Status = GetHIInputInteger ((UINTN*)&Choice); - if (Status == EFI_INVALID_PARAMETER) { - Print (L"\n"); - return Status; - } else if ((Choice < 1) && (Choice > SupportedBdsLoadOptionCount)) { - Print (L"Choose entry from 1 to %d\n",SupportedBdsLoadOptionCount); - Status = EFI_INVALID_PARAMETER; - } - } while (EFI_ERROR(Status)); - BdsLoadOption = SupportedBdsLoadOptions[Choice-1]; - } - StrnCpy (Description, BdsLoadOption->Description, MAX_STR_INPUT); - - LinuxOptionalData = (LINUX_LOADER_OPTIONAL_DATA*)BdsLoadOption->OptionalData; - if (LinuxOptionalData->CmdLineLength > 0) { - CopyMem (CmdLine, (CHAR8*)LinuxOptionalData + sizeof(LINUX_LOADER_OPTIONAL_DATA), LinuxOptionalData->CmdLineLength); - } else { - CmdLine[0] = '\0'; - } - - if (LinuxOptionalData->InitrdPathListLength > 0) { - CopyMem (Initrd, (CHAR8*)LinuxOptionalData + sizeof(LINUX_LOADER_OPTIONAL_DATA) + LinuxOptionalData->CmdLineLength, LinuxOptionalData->InitrdPathListLength); - } else { - Initrd[0] = L'\0'; - } - DEBUG((EFI_D_ERROR,"L\n")); - } - - // Description - Print (L"Description: "); - Status = EditHIInputStr (Description, MAX_STR_INPUT); - if (EFI_ERROR(Status)) { - return Status; - } - if (StrLen (Description) == 0) { - StrnCpy (Description, DEFAULT_BOOT_ENTRY_DESCRIPTION, MAX_STR_INPUT); - } - BdsLoadOption->Description = Description; - - // CmdLine - Print (L"Command Line: "); - Status = EditHIInputAscii (CmdLine, MAX_ASCII_INPUT); - if (EFI_ERROR(Status)) { - return Status; - } - - // Initrd - Print (L"Initrd name: "); - Status = EditHIInputStr (Initrd, MAX_STR_INPUT); - if (EFI_ERROR(Status)) { - return Status; - } - - CmdLineLength = AsciiStrLen (CmdLine); - if (CmdLineLength > 0) { - CmdLineLength += sizeof(CHAR8); - } - - InitrdPathListLength = StrLen (Initrd) * sizeof(CHAR16); - if (InitrdPathListLength > 0) { - InitrdPathListLength += sizeof(CHAR16); - } - - BdsLoadOption->OptionalDataSize = sizeof(LINUX_LOADER_OPTIONAL_DATA) + CmdLineLength + InitrdPathListLength; - - LinuxOptionalData = (LINUX_LOADER_OPTIONAL_DATA*)AllocatePool (BdsLoadOption->OptionalDataSize); - BdsLoadOption->OptionalData = LinuxOptionalData; - - LinuxOptionalData->Signature = LINUX_LOADER_SIGNATURE; - LinuxOptionalData->CmdLineLength = CmdLineLength; - LinuxOptionalData->InitrdPathListLength = InitrdPathListLength; - - if (CmdLineLength > 0) { - CopyMem (LinuxOptionalData + 1, CmdLine, CmdLineLength); - } - if (InitrdPathListLength > 0) { - CopyMem ((UINT8*)(LinuxOptionalData + 1) + CmdLineLength, Initrd, InitrdPathListLength); - } - - // Create or Update the boot entry - Status = BootOptionToLoadOptionVariable (BdsLoadOption); - - return Status; -} diff --git a/ArmPkg/Application/LinuxLoader/LinuxFdtLoader.c b/ArmPkg/Application/LinuxLoader/LinuxFdtLoader.c deleted file mode 100644 index 8e5d1c6aa0..0000000000 --- a/ArmPkg/Application/LinuxLoader/LinuxFdtLoader.c +++ /dev/null @@ -1,103 +0,0 @@ -/** @file -* -* Copyright (c) 2011, 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 "LinuxInternal.h" - -#include -#include - -/** - The user Entry Point for Application. The user code starts with this function - as the real entry point for the application. - - @param[in] ImageHandle The firmware allocated handle for the EFI image. - @param[in] SystemTable A pointer to the EFI System Table. - - @retval EFI_SUCCESS The entry point is executed successfully. - @retval other Some error occurs when executing this entry point. - -**/ -EFI_STATUS -EFIAPI -UefiMain ( - IN EFI_HANDLE ImageHandle, - IN EFI_SYSTEM_TABLE *SystemTable - ) -{ - EFI_STATUS Status; - EFI_LOADED_IMAGE_PROTOCOL *LoadedImage; - LINUX_LOADER_OPTIONAL_DATA* LinuxOptionalData; - EFI_DEVICE_PATH* DevicePathKernel; - EFI_DEVICE_PATH* InitrdDevicePath; - CHAR16* OptionalDataInitrd; - CHAR8* OptionalDataArguments; - CHAR16* Initrd; - - Status = gBS->HandleProtocol (ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **)&LoadedImage); - ASSERT_EFI_ERROR (Status); - - if (LoadedImage->LoadOptionsSize == 0) { - Status = LinuxLoaderConfig (LoadedImage); - } else { - // Ensure the signature is correct - LinuxOptionalData = (LINUX_LOADER_OPTIONAL_DATA*)LoadedImage->LoadOptions; - if (LinuxOptionalData->Signature != LINUX_LOADER_SIGNATURE) { - return EFI_UNSUPPORTED; - } - - // Generate the File Path Node for the Linux Kernel - DevicePathKernel = FileDevicePath (LoadedImage->DeviceHandle, LINUX_KERNEL_NAME); - - if (LinuxOptionalData->CmdLineLength > 0) { - OptionalDataArguments = (CHAR8*)LinuxOptionalData + sizeof(LINUX_LOADER_OPTIONAL_DATA); - } else { - OptionalDataArguments = NULL; - } - - if (LinuxOptionalData->InitrdPathListLength > 0) { - if (OptionalDataArguments != NULL) { - OptionalDataInitrd = (CHAR16*)(OptionalDataArguments + LinuxOptionalData->CmdLineLength); - } else { - OptionalDataInitrd = (CHAR16*)LinuxOptionalData + sizeof(LINUX_LOADER_OPTIONAL_DATA); - } - - // If pointer not aligned - if ((UINTN)OptionalDataInitrd & 0x1) { - Initrd = (CHAR16*)AllocateCopyPool (LinuxOptionalData->InitrdPathListLength, OptionalDataInitrd); - } else { - Initrd = OptionalDataInitrd; - } - - InitrdDevicePath = FileDevicePath (LoadedImage->DeviceHandle, Initrd); - } else { - OptionalDataInitrd = NULL; - InitrdDevicePath = NULL; - Initrd = NULL; - } - - // Load and Start the Linux Kernel (we should never return) - Status = BdsBootLinuxFdt (DevicePathKernel, InitrdDevicePath, OptionalDataArguments); - - if ((UINTN)OptionalDataInitrd & 0x1) { - FreePool (Initrd); - } - - FreePool (DevicePathKernel); - if (InitrdDevicePath) { - FreePool (InitrdDevicePath); - } - } - - return Status; -} diff --git a/ArmPkg/Application/LinuxLoader/LinuxFdtLoader.inf b/ArmPkg/Application/LinuxLoader/LinuxFdtLoader.inf deleted file mode 100644 index 55a6969bb8..0000000000 --- a/ArmPkg/Application/LinuxLoader/LinuxFdtLoader.inf +++ /dev/null @@ -1,39 +0,0 @@ -#/* @file -# Copyright (c) 2011-2013, 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. -# -#*/ - -[Defines] - INF_VERSION = 0x00010006 - BASE_NAME = LinuxFdtLoader - FILE_GUID = f536d559-459f-48fa-8bbc-43b554ecae8d - MODULE_TYPE = UEFI_APPLICATION - VERSION_STRING = 0.1 - ENTRY_POINT = UefiMain - -[Sources] - LinuxFdtLoader.c - LinuxConfig.c - -[Packages] - ArmPkg/ArmPkg.dec - MdePkg/MdePkg.dec - -[LibraryClasses] - BdsLib - DxeServicesTableLib - UefiLib - UefiApplicationEntryPoint - -[Protocols] - gEfiLoadedImageProtocolGuid - gEfiDevicePathToTextProtocolGuid - diff --git a/ArmPkg/Application/LinuxLoader/LinuxInternal.h b/ArmPkg/Application/LinuxLoader/LinuxInternal.h deleted file mode 100644 index 88d786f693..0000000000 --- a/ArmPkg/Application/LinuxLoader/LinuxInternal.h +++ /dev/null @@ -1,50 +0,0 @@ -/** @file -* -* Copyright (c) 2011-2013, 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. -* -**/ - -#ifndef __LOADER_INTERNAL_H -#define __LOADER_INTERNAL_H - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#define LINUX_KERNEL_NAME L"zImage" -#define FDT_NAME L"platform.dtb" - -#define LINUX_LOADER_SIGNATURE SIGNATURE_32('l', 'i', 'l', 'o') - -typedef struct { - UINT32 Signature; - UINT16 CmdLineLength; - UINT16 InitrdPathListLength; - - // These following fields have variable length: - //CHAR8* CmdLine; - //CHAR16* Initrd; -} LINUX_LOADER_OPTIONAL_DATA; - -EFI_STATUS -LinuxLoaderConfig ( - IN EFI_LOADED_IMAGE_PROTOCOL *LoadedImage - ); - -#endif diff --git a/ArmPkg/Application/LinuxLoader/LinuxLoader.c b/ArmPkg/Application/LinuxLoader/LinuxLoader.c new file mode 100644 index 0000000000..70b960b66f --- /dev/null +++ b/ArmPkg/Application/LinuxLoader/LinuxLoader.c @@ -0,0 +1,246 @@ +/** @file +* +* Copyright (c) 2011-2015, 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 +#include + +#include + +#include "LinuxLoader.h" + +/** + The user Entry Point for Application. The user code starts with this function + as the real entry point for the application. + + @param[in] ImageHandle The firmware allocated handle for the EFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The entry point was executed successfully. + @retval EFI_NOT_FOUND Protocol not found. + @retval EFI_NOT_FOUND Path to the Linux kernel not found. + @retval EFI_ABORTED The initialisation of the Shell Library failed. + @retval EFI_INVALID_PARAMETER At least one parameter is not valid or there is a + conflict between two parameters. + @retval EFI_OUT_OF_RESOURCES A memory allocation failed. + +**/ +EFI_STATUS +EFIAPI +LinuxLoaderEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL *EfiDevicePathFromTextProtocol; + EFI_SHELL_PARAMETERS_PROTOCOL *ShellParameters; + CHAR16 *KernelPath; + CHAR16 *FdtPath; + CHAR16 *InitrdPath; + CHAR16 *KernelTextDevicePath; + CHAR16 *FdtTextDevicePath; + CHAR16 *InitrdTextDevicePath; + CHAR16 *LinuxCommandLine; + UINTN AtagMachineType; + EFI_DEVICE_PATH *KernelDevicePath; + EFI_DEVICE_PATH *FdtDevicePath; + EFI_DEVICE_PATH *InitrdDevicePath; + CHAR8 *AsciiLinuxCommandLine; + LIST_ENTRY ResourceList; + LIST_ENTRY *ResourceLink; + SYSTEM_MEMORY_RESOURCE *Resource; + EFI_PHYSICAL_ADDRESS SystemMemoryBase; + + Status = gBS->LocateProtocol ( + &gEfiDevicePathFromTextProtocolGuid, + NULL, + (VOID **)&EfiDevicePathFromTextProtocol + ); + if (EFI_ERROR (Status)) { + return EFI_NOT_FOUND; + } + + // + // Register the strings for the user interface in the HII Database. + // This shows the way to the multi-language support, even if + // only the English language is actually supported. The strings to register + // are stored in the "LinuxLoaderStrings[]" array. This array is + // built by the building process from the "*.uni" file associated to + // the present application (cf. LinuxLoader.inf). Examine the Build + // folder of the application and you will find the array defined in the + // LinuxLoaderStrDefs.h file. + // + mLinuxLoaderHiiHandle = HiiAddPackages ( + &mLinuxLoaderHiiGuid, + ImageHandle, + LinuxLoaderStrings, + NULL + ); + if (mLinuxLoaderHiiHandle == NULL) { + return EFI_NOT_FOUND; + } + + Status = gBS->HandleProtocol ( + ImageHandle, + &gEfiShellParametersProtocolGuid, + (VOID**)&ShellParameters + ); + + KernelDevicePath = NULL; + FdtDevicePath = NULL; + InitrdDevicePath = NULL; + AsciiLinuxCommandLine = NULL; + + // + // Call the proper function to handle the command line + // depending on whether the application has been called + // from the Shell or not. + // + + if (!EFI_ERROR (Status)) { + KernelTextDevicePath = NULL; + FdtTextDevicePath = NULL; + InitrdTextDevicePath = NULL; + + Status = ProcessShellParameters ( + &KernelPath, &FdtPath, &InitrdPath, &LinuxCommandLine, &AtagMachineType + ); + if (EFI_ERROR (Status)) { + goto Error; + } + + KernelDevicePath = gEfiShellProtocol->GetDevicePathFromFilePath (KernelPath); + if (KernelDevicePath != NULL) { + FreePool (KernelPath); + } else { + KernelTextDevicePath = KernelPath; + } + + if (FdtPath != NULL) { + FdtDevicePath = gEfiShellProtocol->GetDevicePathFromFilePath (FdtPath); + if (FdtDevicePath != NULL) { + FreePool (FdtPath); + } else { + FdtTextDevicePath = FdtPath; + } + } + + if (InitrdPath != NULL) { + InitrdDevicePath = gEfiShellProtocol->GetDevicePathFromFilePath (InitrdPath); + if (InitrdDevicePath != NULL) { + FreePool (InitrdPath); + } else { + InitrdTextDevicePath = InitrdPath; + } + } + + } else { + Status = ProcessAppCommandLine ( + &KernelTextDevicePath, &FdtTextDevicePath, + &InitrdTextDevicePath, &LinuxCommandLine, &AtagMachineType + ); + if (EFI_ERROR (Status)) { + goto Error; + } + } + + Status = EFI_INVALID_PARAMETER; + if (KernelTextDevicePath != NULL) { + KernelDevicePath = EfiDevicePathFromTextProtocol->ConvertTextToDevicePath ( + KernelTextDevicePath + ); + if (KernelDevicePath == NULL) { + goto Error; + } + } + if (FdtTextDevicePath != NULL) { + FdtDevicePath = EfiDevicePathFromTextProtocol->ConvertTextToDevicePath ( + FdtTextDevicePath + ); + if (FdtDevicePath == NULL) { + goto Error; + } + } + if (InitrdTextDevicePath != NULL) { + InitrdDevicePath = EfiDevicePathFromTextProtocol->ConvertTextToDevicePath ( + InitrdTextDevicePath + ); + if (InitrdDevicePath == NULL) { + goto Error; + } + } + + if (LinuxCommandLine != NULL) { + AsciiLinuxCommandLine = AllocatePool ((StrLen (LinuxCommandLine) + 1) * sizeof (CHAR8)); + if (AsciiLinuxCommandLine == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + UnicodeStrToAsciiStr (LinuxCommandLine, AsciiLinuxCommandLine); + } + + // + // Find Base of System Memory - we keep the lowest physical address + // + SystemMemoryBase = ~0; + GetSystemMemoryResources (&ResourceList); + ResourceLink = ResourceList.ForwardLink; + while (ResourceLink != NULL && ResourceLink != &ResourceList) { + Resource = (SYSTEM_MEMORY_RESOURCE*)ResourceLink; + if (Resource->PhysicalStart < SystemMemoryBase) { + SystemMemoryBase = Resource->PhysicalStart; + } + ResourceLink = ResourceLink->ForwardLink; + } + + if (AtagMachineType != ARM_FDT_MACHINE_TYPE) { + Status = BootLinuxAtag (SystemMemoryBase, KernelDevicePath, InitrdDevicePath, AsciiLinuxCommandLine, AtagMachineType); + } else { + Status = BootLinuxFdt (SystemMemoryBase, KernelDevicePath, InitrdDevicePath, FdtDevicePath, AsciiLinuxCommandLine); + } + +Error: + if (KernelTextDevicePath != NULL) { + FreePool (KernelTextDevicePath); + } + if (FdtTextDevicePath != NULL) { + FreePool (FdtTextDevicePath); + } + if (InitrdTextDevicePath != NULL) { + FreePool (InitrdTextDevicePath); + } + if (LinuxCommandLine != NULL) { + FreePool (LinuxCommandLine); + } + if (KernelDevicePath != NULL) { + FreePool (KernelDevicePath); + } + if (FdtDevicePath != NULL) { + FreePool (FdtDevicePath); + } + if (InitrdDevicePath != NULL) { + FreePool (InitrdDevicePath); + } + if (AsciiLinuxCommandLine != NULL) { + FreePool (AsciiLinuxCommandLine); + } + + if (EFI_ERROR (Status)) { + PrintHii (NULL, STRING_TOKEN (STR_ERROR), Status); + } + + HiiRemovePackages (mLinuxLoaderHiiHandle); + + return Status; +} diff --git a/ArmPkg/Application/LinuxLoader/LinuxLoader.h b/ArmPkg/Application/LinuxLoader/LinuxLoader.h new file mode 100644 index 0000000000..8a23d7f14b --- /dev/null +++ b/ArmPkg/Application/LinuxLoader/LinuxLoader.h @@ -0,0 +1,166 @@ +/** @file +* +* Copyright (c) 2011-2015, 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. +* +**/ + +#ifndef __LINUX_LOADER_H__ +#define __LINUX_LOADER_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +// +// Definitions +// + +#define MAX_MSG_LEN 80 + +#define LINUX_UIMAGE_SIGNATURE 0x56190527 +#define LINUX_KERNEL_MAX_OFFSET (SystemMemoryBase + PcdGet32(PcdArmLinuxKernelMaxOffset)) +#define LINUX_ATAG_MAX_OFFSET (SystemMemoryBase + PcdGet32(PcdArmLinuxAtagMaxOffset)) +#define LINUX_FDT_MAX_OFFSET (SystemMemoryBase + PcdGet32(PcdArmLinuxFdtMaxOffset)) + +#define ARM_FDT_MACHINE_TYPE 0xFFFFFFFF + +// Additional size that could be used for FDT entries added by the UEFI OS Loader +// Estimation based on: EDID (300bytes) + bootargs (200bytes) + initrd region (20bytes) +// + system memory region (20bytes) + mp_core entries (200 bytes) +#define FDT_ADDITIONAL_ENTRIES_SIZE 0x300 + +// +// Global variables +// +extern CONST EFI_GUID mLinuxLoaderHiiGuid; +extern EFI_HANDLE mLinuxLoaderHiiHandle; + +// +// Local Types +// +typedef struct _SYSTEM_MEMORY_RESOURCE { + LIST_ENTRY Link; // This attribute must be the first entry of this structure (to avoid pointer computation) + EFI_PHYSICAL_ADDRESS PhysicalStart; + UINT64 ResourceLength; +} SYSTEM_MEMORY_RESOURCE; + +typedef VOID (*LINUX_KERNEL)(UINT32 Zero, UINT32 Arch, UINTN ParametersBase); + +// +// Functions +// +EFI_STATUS +PrintHii ( + IN CONST CHAR8 *Language OPTIONAL, + IN CONST EFI_STRING_ID HiiFormatStringId, + ... + ); + +VOID +PrintHelp ( + IN CONST CHAR8 *Language OPTIONAL + ); + +EFI_STATUS +ProcessShellParameters ( + OUT CHAR16 **KernelPath, + OUT CHAR16 **FdtPath, + OUT CHAR16 **InitrdPath, + OUT CHAR16 **LinuxCommandLine, + OUT UINTN *AtagMachineType + ); + +EFI_STATUS +ProcessAppCommandLine ( + OUT CHAR16 **KernelTextDevicePath, + OUT CHAR16 **FdtTextDevicePath, + OUT CHAR16 **InitrdTextDevicePath, + OUT CHAR16 **LinuxCommandLine, + OUT UINTN *AtagMachineType + ); + +VOID +PrintPerformance ( + VOID + ); + +EFI_STATUS +GetSystemMemoryResources ( + IN LIST_ENTRY *ResourceList + ); + +EFI_STATUS +PrepareFdt ( + IN EFI_PHYSICAL_ADDRESS SystemMemoryBase, + IN CONST CHAR8* CommandLineArguments, + IN EFI_PHYSICAL_ADDRESS InitrdImage, + IN UINTN InitrdImageSize, + IN OUT EFI_PHYSICAL_ADDRESS *FdtBlobBase, + IN OUT UINTN *FdtBlobSize + ); + +/** + Start a Linux kernel from a Device Path + + @param SystemMemoryBase Base of the system memory + @param LinuxKernel Device Path to the Linux Kernel + @param Parameters Linux kernel arguments + @param Fdt Device Path to the Flat Device Tree + @param MachineType ARM machine type value + + @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. + @retval RETURN_UNSUPPORTED ATAG is not support by this architecture + +**/ +EFI_STATUS +BootLinuxAtag ( + IN EFI_PHYSICAL_ADDRESS SystemMemoryBase, + IN EFI_DEVICE_PATH_PROTOCOL* LinuxKernelDevicePath, + IN EFI_DEVICE_PATH_PROTOCOL* InitrdDevicePath, + IN CONST CHAR8* CommandLineArguments, + IN UINTN MachineType + ); + +/** + Start a Linux kernel from a Device Path + + @param[in] LinuxKernelDevicePath Device Path to the Linux Kernel + @param[in] InitrdDevicePath Device Path to the Initrd + @param[in] Arguments Linux kernel arguments + + @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 +BootLinuxFdt ( + IN EFI_PHYSICAL_ADDRESS SystemMemoryBase, + IN EFI_DEVICE_PATH_PROTOCOL* LinuxKernelDevicePath, + IN EFI_DEVICE_PATH_PROTOCOL* InitrdDevicePath, + IN EFI_DEVICE_PATH_PROTOCOL* FdtDevicePath, + IN CONST CHAR8* Arguments + ); + +#endif /* __LINUX_LOADER_H__ */ diff --git a/ArmPkg/Application/LinuxLoader/LinuxLoader.inf b/ArmPkg/Application/LinuxLoader/LinuxLoader.inf new file mode 100644 index 0000000000..59ab99c968 --- /dev/null +++ b/ArmPkg/Application/LinuxLoader/LinuxLoader.inf @@ -0,0 +1,91 @@ +#/* @file +# Copyright (c) 2015, 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. +# +#*/ + +[Defines] + INF_VERSION = 0x00010006 + BASE_NAME = LinuxLoader + MODULE_UNI_FILE = LinuxLoader.uni + FILE_GUID = 701f54f2-0d70-4b89-bc0a-d9ca25379059 + MODULE_TYPE = UEFI_APPLICATION + VERSION_STRING = 0.1 + ENTRY_POINT = LinuxLoaderEntryPoint + +[Sources] + LinuxLoader.c + LinuxLoader.h + LinuxLoader.uni + LinuxLoaderFdt.c + LinuxLoaderHelper.c + LinuxLoaderEfiApp.c + LinuxLoaderShellApp.c + +[Sources.AARCH64] + AArch64/LinuxStarter.c + AArch64/LinuxStarterHelper.S + +[Sources.ARM] + Arm/LinuxAtag.h + Arm/LinuxAtag.c + Arm/LinuxStarter.c + +[Packages] + ArmPkg/ArmPkg.dec + EmbeddedPkg/EmbeddedPkg.dec + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + ShellPkg/ShellPkg.dec + +[Guids] + gFdtTableGuid + +[Guids] + gArmMpCoreInfoGuid + +[LibraryClasses] + ArmLib + BdsLib + DebugLib + DxeServicesTableLib + FdtLib + HiiLib + HobLib + PerformanceLib + ShellLib + SerialPortLib + TimerLib + UefiApplicationEntryPoint + UefiLib + +[LibraryClasses.AARCH64] + ArmGicLib + PcdLib + +[Protocols] + gEfiLoadedImageProtocolGuid + gEfiDevicePathToTextProtocolGuid + gEfiShellParametersProtocolGuid + +[FeaturePcd] + gArmTokenSpaceGuid.PcdArmLinuxSpinTable + +[FixedPcd] + gArmTokenSpaceGuid.PcdArmLinuxFdtMaxOffset + gArmTokenSpaceGuid.PcdArmLinuxFdtAlignment + gArmTokenSpaceGuid.PcdArmLinuxKernelMaxOffset + +[FixedPcd.ARM] + gArmTokenSpaceGuid.PcdArmLinuxAtagMaxOffset + +[Pcd.AARCH64] + gArmTokenSpaceGuid.PcdGicDistributorBase + gArmTokenSpaceGuid.PcdGicSgiIntId diff --git a/ArmPkg/Application/LinuxLoader/LinuxLoader.uni b/ArmPkg/Application/LinuxLoader/LinuxLoader.uni new file mode 100644 index 0000000000..861c2b4a99 Binary files /dev/null and b/ArmPkg/Application/LinuxLoader/LinuxLoader.uni differ diff --git a/ArmPkg/Application/LinuxLoader/LinuxLoaderEfiApp.c b/ArmPkg/Application/LinuxLoader/LinuxLoaderEfiApp.c new file mode 100644 index 0000000000..57a9cd38cf --- /dev/null +++ b/ArmPkg/Application/LinuxLoader/LinuxLoaderEfiApp.c @@ -0,0 +1,303 @@ +/** @file +* +* Copyright (c) 2011-2015, 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 "LinuxLoader.h" + +/** + Extract the next item from the command line. + + The items are separated by spaces. Quotation marks (") are used for argument + grouping and the escaping character is "^" as for the EFI Shell command lines. + + @param[in out] CommandLine Command line pointer. + @param[out] Item Pointer to the allocated buffer where the + item is stored. + + @retval EFI_SUCCESS The token was found and extracted. + @retval EFI_NOT_FOUND No item found. + @retval EFI_OUT_OF_RESOURCES The memory allocation failed. + +**/ +STATIC +EFI_STATUS +ExtractNextItem ( + IN OUT CONST CHAR16 **CommandLine, + OUT CHAR16 **Item + ) +{ + CONST CHAR16 *Walker; + VOID *Buffer; + CHAR16 *WritePtr; + BOOLEAN InQuotedString; + BOOLEAN Interpret; + + for (Walker = *CommandLine; *Walker == L' '; Walker++) { + ; + } + + Buffer = AllocatePool (StrSize (Walker)); + if (Buffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + for (WritePtr = Buffer, Interpret = TRUE, InQuotedString = FALSE; + ((*Walker != L' ') || InQuotedString) && (*Walker != L'\0'); + Walker++ + ) { + if (Interpret) { + if (*Walker == L'^') { + Interpret = FALSE; + continue; + } + if (*Walker == L'"') { + InQuotedString = !InQuotedString; + continue; + } + } else { + Interpret = TRUE; + } + *(WritePtr++) = *Walker; + } + + if (WritePtr == Buffer) { + FreePool (Buffer); + return EFI_NOT_FOUND; + } + + *WritePtr = L'\0'; + *CommandLine = Walker; + *Item = Buffer; + + return EFI_SUCCESS; +} + +/** + Check if an item of the command line is a flag or not. + + @param[in] Item Command line item. + + @retval TRUE The item is a flag. + @retval FALSE The item is not a flag. + +**/ +STATIC +BOOLEAN +IsFlag ( + IN CONST CHAR16 *Item + ) +{ + return ((Item[0] == L'-') && (Item[2] == L'\0')); +} + +/** + Process the application command line. + + @param[out] KernelTextDevicePath A pointer to the buffer where the device + path to the Linux kernel is stored. The + address of the buffer is NULL in case of + an error. Otherwise, the returned address + is the address of a buffer allocated with + a call to AllocatePool() that has to be + freed by the caller. + @param[out] FdtTextDevicePath A pointer to the buffer where the device + path to the FDT is stored. The address of + the buffer is NULL in case of an error or + if the device path to the FDT is not + defined. Otherwise, the returned address + is the address of a buffer allocated with + a call to AllocatePool() that has to be + freed by the caller. + @param[out] InitrdTextDevicePath A pointer to the buffer where the device + path to the RAM root file system is stored. + The address of the buffer is NULL in case + of an error or if the device path to the + RAM root file system is not defined. + Otherwise, the returned address is the + address of a buffer allocated with a call + to AllocatePool() that has to be freed by + the caller. + @param[out] LinuxCommandLine A pointer to the buffer where the Linux + kernel command line is stored. The address + of the buffer is NULL in case of an error + or if the Linux command line is not + defined. Otherwise, the returned address + is the address of a buffer allocated with + a call to AllocatePool() that has to be + freed by the caller. + + @param[out] AtagMachineType Value of the ARM Machine Type + + @retval EFI_SUCCESS The processing was successfull. + @retval EFI_NOT_FOUND EFI_LOADED_IMAGE_PROTOCOL not found. + @retval EFI_NOT_FOUND Path to the Linux kernel not found. + @retval EFI_INVALID_PARAMETER At least one parameter is not valid or there is a + conflict between two parameters. + @retval EFI_OUT_OF_RESOURCES A memory allocation failed. + +**/ +EFI_STATUS +ProcessAppCommandLine ( + OUT CHAR16 **KernelTextDevicePath, + OUT CHAR16 **FdtTextDevicePath, + OUT CHAR16 **InitrdTextDevicePath, + OUT CHAR16 **LinuxCommandLine, + OUT UINTN *AtagMachineType + ) +{ + EFI_STATUS Status; + EFI_STATUS Status2; + EFI_LOADED_IMAGE_PROTOCOL *LoadedImage; + CONST CHAR16 *Walker; + CHAR16 *Item; + CHAR16 Flag; + BOOLEAN HasAtagSupport; + BOOLEAN HasFdtSupport; + + *KernelTextDevicePath = NULL; + *FdtTextDevicePath = NULL; + *InitrdTextDevicePath = NULL; + *LinuxCommandLine = NULL; + *AtagMachineType = ARM_FDT_MACHINE_TYPE; + + HasAtagSupport = FALSE; + HasFdtSupport = FALSE; + + Status = gBS->HandleProtocol ( + gImageHandle, + &gEfiLoadedImageProtocolGuid, + (VOID**)&LoadedImage + ); + if (EFI_ERROR (Status)) { + ASSERT_EFI_ERROR (Status); + return Status; + } + + Walker = (CHAR16*)LoadedImage->LoadOptions; + if (Walker == NULL) { + PrintHelp (NULL); + return EFI_INVALID_PARAMETER; + } + + // + // Get the device path to the Linux kernel. + // + + Status = ExtractNextItem (&Walker, &Item); + if (!EFI_ERROR (Status)) { + if (!IsFlag (Item)) { + *KernelTextDevicePath = Item; + } else { + PrintHii (NULL, STRING_TOKEN (STR_MISSING_KERNEL_PATH)); + FreePool (Item); + return EFI_NOT_FOUND; + } + } else { + if (Status != EFI_NOT_FOUND) { + return Status; + } + PrintHelp (NULL); + return EFI_INVALID_PARAMETER; + } + + Status = EFI_INVALID_PARAMETER; + while (*Walker != L'\0') { + Status2 = ExtractNextItem (&Walker, &Item); + if (Status2 == EFI_NOT_FOUND) { + break; + } + if (EFI_ERROR (Status2)) { + Status = Status2; + goto Error; + } + + if (!IsFlag (Item)) { + PrintHii (NULL, STRING_TOKEN (STR_INVALID_FLAG), Item[0], Item[1]); + FreePool (Item); + goto Error; + } + Flag = Item[1]; + FreePool (Item); + + Status2 = ExtractNextItem (&Walker, &Item); + if (Status2 == EFI_NOT_FOUND) { + PrintHii (NULL, STRING_TOKEN (STR_MISSING_VALUE), Flag); + goto Error; + } + if (EFI_ERROR (Status2)) { + Status = Status2; + goto Error; + } + if (IsFlag (Item)) { + PrintHii (NULL, STRING_TOKEN (STR_MISSING_VALUE), Flag); + FreePool (Item); + goto Error; + } + + switch (Flag) { + case L'a': + if (HasFdtSupport) { + PrintHii (NULL, STRING_TOKEN (STR_ATAG_FDT_CONFLICT)); + goto Error; + } + *AtagMachineType = StrDecimalToUintn (Item); + HasAtagSupport = TRUE; + break; + case L'd': + *FdtTextDevicePath = Item; + if (HasAtagSupport) { + PrintHii (NULL, STRING_TOKEN (STR_ATAG_FDT_CONFLICT)); + goto Error; + } + HasFdtSupport = TRUE; + break; + + case L'c': + *LinuxCommandLine = Item; + break; + + case L'f': + *InitrdTextDevicePath = Item; + break; + + default: + PrintHii (NULL, STRING_TOKEN (STR_INVALID_FLAG), L'-', Flag); + FreePool (Item); + goto Error; + } + } + + Status = EFI_SUCCESS; + +Error: + if (EFI_ERROR (Status)) { + if (*KernelTextDevicePath != NULL) { + FreePool (*KernelTextDevicePath); + *KernelTextDevicePath = NULL; + } + if (*FdtTextDevicePath != NULL) { + FreePool (*FdtTextDevicePath); + *FdtTextDevicePath = NULL; + } + if (*InitrdTextDevicePath != NULL) { + FreePool (*InitrdTextDevicePath); + *InitrdTextDevicePath = NULL; + } + if (*LinuxCommandLine != NULL) { + FreePool (*LinuxCommandLine); + *LinuxCommandLine = NULL; + } + } + + return Status; +} diff --git a/ArmPkg/Application/LinuxLoader/LinuxLoaderFdt.c b/ArmPkg/Application/LinuxLoader/LinuxLoaderFdt.c new file mode 100644 index 0000000000..0f5378403f --- /dev/null +++ b/ArmPkg/Application/LinuxLoader/LinuxLoaderFdt.c @@ -0,0 +1,411 @@ +/** @file +* +* Copyright (c) 2011-2015, 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 +#include +#include + +#include + +#include "LinuxLoader.h" + +#define ALIGN(x, a) (((x) + ((a) - 1)) & ~((a) - 1)) +#define PALIGN(p, a) ((void *)(ALIGN ((unsigned long)(p), (a)))) +#define GET_CELL(p) (p += 4, *((const UINT32 *)(p-4))) + +STATIC +UINTN +cpu_to_fdtn (UINTN x) { + if (sizeof (UINTN) == sizeof (UINT32)) { + return cpu_to_fdt32 (x); + } else { + return cpu_to_fdt64 (x); + } +} + +typedef struct { + UINTN Base; + UINTN Size; +} FDT_REGION; + +STATIC +BOOLEAN +IsLinuxReservedRegion ( + IN EFI_MEMORY_TYPE MemoryType + ) +{ + switch (MemoryType) { + case EfiRuntimeServicesCode: + case EfiRuntimeServicesData: + case EfiUnusableMemory: + case EfiACPIReclaimMemory: + case EfiACPIMemoryNVS: + case EfiReservedMemoryType: + return TRUE; + default: + return FALSE; + } +} + +/** +** Relocate the FDT blob to a more appropriate location for the Linux kernel. +** This function will allocate memory for the relocated FDT blob. +** +** @retval EFI_SUCCESS on success. +** @retval EFI_OUT_OF_RESOURCES or EFI_INVALID_PARAMETER on failure. +*/ +STATIC +EFI_STATUS +RelocateFdt ( + EFI_PHYSICAL_ADDRESS SystemMemoryBase, + EFI_PHYSICAL_ADDRESS OriginalFdt, + UINTN OriginalFdtSize, + EFI_PHYSICAL_ADDRESS *RelocatedFdt, + UINTN *RelocatedFdtSize, + EFI_PHYSICAL_ADDRESS *RelocatedFdtAlloc + ) +{ + EFI_STATUS Status; + INTN Error; + UINT64 FdtAlignment; + + *RelocatedFdtSize = OriginalFdtSize + FDT_ADDITIONAL_ENTRIES_SIZE; + + // If FDT load address needs to be aligned, allocate more space. + FdtAlignment = PcdGet32 (PcdArmLinuxFdtAlignment); + if (FdtAlignment != 0) { + *RelocatedFdtSize += FdtAlignment; + } + + // Try below a watermark address. + Status = EFI_NOT_FOUND; + if (PcdGet32 (PcdArmLinuxFdtMaxOffset) != 0) { + *RelocatedFdt = LINUX_FDT_MAX_OFFSET; + Status = gBS->AllocatePages (AllocateMaxAddress, EfiBootServicesData, + EFI_SIZE_TO_PAGES (*RelocatedFdtSize), RelocatedFdt); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_WARN, "Warning: Failed to load FDT below address 0x%lX (%r). Will try again at a random address anywhere.\n", *RelocatedFdt, Status)); + } + } + + // Try anywhere there is available space. + if (EFI_ERROR (Status)) { + Status = gBS->AllocatePages (AllocateAnyPages, EfiBootServicesData, + EFI_SIZE_TO_PAGES (*RelocatedFdtSize), RelocatedFdt); + if (EFI_ERROR (Status)) { + ASSERT_EFI_ERROR (Status); + return EFI_OUT_OF_RESOURCES; + } else { + DEBUG ((EFI_D_WARN, "WARNING: Loaded FDT at random address 0x%lX.\nWARNING: There is a risk of accidental overwriting by other code/data.\n", *RelocatedFdt)); + } + } + + *RelocatedFdtAlloc = *RelocatedFdt; + if (FdtAlignment != 0) { + *RelocatedFdt = ALIGN (*RelocatedFdt, FdtAlignment); + } + + // Load the Original FDT tree into the new region + Error = fdt_open_into ((VOID*)(UINTN) OriginalFdt, + (VOID*)(UINTN)(*RelocatedFdt), *RelocatedFdtSize); + if (Error) { + DEBUG ((EFI_D_ERROR, "fdt_open_into(): %a\n", fdt_strerror (Error))); + gBS->FreePages (*RelocatedFdtAlloc, EFI_SIZE_TO_PAGES (*RelocatedFdtSize)); + return EFI_INVALID_PARAMETER; + } + + return EFI_SUCCESS; +} + +EFI_STATUS +PrepareFdt ( + IN EFI_PHYSICAL_ADDRESS SystemMemoryBase, + IN CONST CHAR8* CommandLineArguments, + IN EFI_PHYSICAL_ADDRESS InitrdImage, + IN UINTN InitrdImageSize, + IN OUT EFI_PHYSICAL_ADDRESS *FdtBlobBase, + IN OUT UINTN *FdtBlobSize + ) +{ + EFI_STATUS Status; + EFI_PHYSICAL_ADDRESS NewFdtBlobBase; + EFI_PHYSICAL_ADDRESS NewFdtBlobAllocation; + UINTN NewFdtBlobSize; + VOID* fdt; + INTN err; + INTN node; + INTN cpu_node; + INT32 lenp; + CONST VOID* BootArg; + CONST VOID* Method; + EFI_PHYSICAL_ADDRESS InitrdImageStart; + EFI_PHYSICAL_ADDRESS InitrdImageEnd; + FDT_REGION Region; + UINTN Index; + CHAR8 Name[10]; + LIST_ENTRY ResourceList; + SYSTEM_MEMORY_RESOURCE *Resource; + ARM_PROCESSOR_TABLE *ArmProcessorTable; + ARM_CORE_INFO *ArmCoreInfoTable; + UINT32 MpId; + UINT32 ClusterId; + UINT32 CoreId; + UINT64 CpuReleaseAddr; + UINTN MemoryMapSize; + EFI_MEMORY_DESCRIPTOR *MemoryMap; + EFI_MEMORY_DESCRIPTOR *MemoryMapPtr; + UINTN MapKey; + UINTN DescriptorSize; + UINT32 DescriptorVersion; + UINTN Pages; + UINTN OriginalFdtSize; + BOOLEAN CpusNodeExist; + UINTN CoreMpId; + + NewFdtBlobAllocation = 0; + + // + // Sanity checks on the original FDT blob. + // + err = fdt_check_header ((VOID*)(UINTN)(*FdtBlobBase)); + if (err != 0) { + Print (L"ERROR: Device Tree header not valid (err:%d)\n", err); + return EFI_INVALID_PARAMETER; + } + + // The original FDT blob might have been loaded partially. + // Check that it is not the case. + OriginalFdtSize = (UINTN)fdt_totalsize ((VOID*)(UINTN)(*FdtBlobBase)); + if (OriginalFdtSize > *FdtBlobSize) { + Print (L"ERROR: Incomplete FDT. Only %d/%d bytes have been loaded.\n", + *FdtBlobSize, OriginalFdtSize); + return EFI_INVALID_PARAMETER; + } + + // + // Relocate the FDT to its final location. + // + Status = RelocateFdt (SystemMemoryBase, *FdtBlobBase, OriginalFdtSize, + &NewFdtBlobBase, &NewFdtBlobSize, &NewFdtBlobAllocation); + if (EFI_ERROR (Status)) { + goto FAIL_RELOCATE_FDT; + } + + fdt = (VOID*)(UINTN)NewFdtBlobBase; + + node = fdt_subnode_offset (fdt, 0, "chosen"); + if (node < 0) { + // The 'chosen' node does not exist, create it + node = fdt_add_subnode (fdt, 0, "chosen"); + if (node < 0) { + DEBUG ((EFI_D_ERROR, "Error on finding 'chosen' node\n")); + Status = EFI_INVALID_PARAMETER; + goto FAIL_COMPLETE_FDT; + } + } + + DEBUG_CODE_BEGIN (); + BootArg = fdt_getprop (fdt, node, "bootargs", &lenp); + if (BootArg != NULL) { + DEBUG ((EFI_D_ERROR, "BootArg: %a\n", BootArg)); + } + DEBUG_CODE_END (); + + // + // Set Linux CmdLine + // + if ((CommandLineArguments != NULL) && (AsciiStrLen (CommandLineArguments) > 0)) { + err = fdt_setprop (fdt, node, "bootargs", CommandLineArguments, AsciiStrSize (CommandLineArguments)); + if (err) { + DEBUG ((EFI_D_ERROR, "Fail to set new 'bootarg' (err:%d)\n", err)); + } + } + + // + // Set Linux Initrd + // + if (InitrdImageSize != 0) { + InitrdImageStart = cpu_to_fdt64 (InitrdImage); + err = fdt_setprop (fdt, node, "linux,initrd-start", &InitrdImageStart, sizeof (EFI_PHYSICAL_ADDRESS)); + if (err) { + DEBUG ((EFI_D_ERROR, "Fail to set new 'linux,initrd-start' (err:%d)\n", err)); + } + InitrdImageEnd = cpu_to_fdt64 (InitrdImage + InitrdImageSize); + err = fdt_setprop (fdt, node, "linux,initrd-end", &InitrdImageEnd, sizeof (EFI_PHYSICAL_ADDRESS)); + if (err) { + DEBUG ((EFI_D_ERROR, "Fail to set new 'linux,initrd-start' (err:%d)\n", err)); + } + } + + // + // Set Physical memory setup if does not exist + // + node = fdt_subnode_offset (fdt, 0, "memory"); + if (node < 0) { + // The 'memory' node does not exist, create it + node = fdt_add_subnode (fdt, 0, "memory"); + if (node >= 0) { + fdt_setprop_string (fdt, node, "name", "memory"); + fdt_setprop_string (fdt, node, "device_type", "memory"); + + GetSystemMemoryResources (&ResourceList); + Resource = (SYSTEM_MEMORY_RESOURCE*)ResourceList.ForwardLink; + + Region.Base = cpu_to_fdtn ((UINTN)Resource->PhysicalStart); + Region.Size = cpu_to_fdtn ((UINTN)Resource->ResourceLength); + + err = fdt_setprop (fdt, node, "reg", &Region, sizeof (Region)); + if (err) { + DEBUG ((EFI_D_ERROR, "Fail to set new 'memory region' (err:%d)\n", err)); + } + } + } + + // + // Add the memory regions reserved by the UEFI Firmware + // + + // Retrieve the UEFI Memory Map + MemoryMap = NULL; + MemoryMapSize = 0; + Status = gBS->GetMemoryMap (&MemoryMapSize, MemoryMap, &MapKey, &DescriptorSize, &DescriptorVersion); + if (Status == EFI_BUFFER_TOO_SMALL) { + // The UEFI specification advises to allocate more memory for the MemoryMap buffer between successive + // calls to GetMemoryMap(), since allocation of the new buffer may potentially increase memory map size. + Pages = EFI_SIZE_TO_PAGES (MemoryMapSize) + 1; + MemoryMap = AllocatePages (Pages); + if (MemoryMap == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto FAIL_COMPLETE_FDT; + } + Status = gBS->GetMemoryMap (&MemoryMapSize, MemoryMap, &MapKey, &DescriptorSize, &DescriptorVersion); + } + + // Go through the list and add the reserved region to the Device Tree + if (!EFI_ERROR (Status)) { + 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%lX, 0x%lX]\n", + MemoryMapPtr->Type, + (UINTN)MemoryMapPtr->PhysicalStart, + (UINTN)(MemoryMapPtr->PhysicalStart + MemoryMapPtr->NumberOfPages * EFI_PAGE_SIZE))); + err = fdt_add_mem_rsv (fdt, MemoryMapPtr->PhysicalStart, MemoryMapPtr->NumberOfPages * EFI_PAGE_SIZE); + if (err != 0) { + Print (L"Warning: Fail to add 'memreserve' (err:%d)\n", err); + } + } + MemoryMapPtr = (EFI_MEMORY_DESCRIPTOR*)((UINTN)MemoryMapPtr + DescriptorSize); + } + } + + // + // Setup Arm Mpcore Info if it is a multi-core or multi-cluster platforms. + // + // For 'cpus' and 'cpu' device tree nodes bindings, refer to this file + // in the kernel documentation: + // Documentation/devicetree/bindings/arm/cpus.txt + // + for (Index = 0; Index < gST->NumberOfTableEntries; Index++) { + // Check for correct GUID type + if (CompareGuid (&gArmMpCoreInfoGuid, &(gST->ConfigurationTable[Index].VendorGuid))) { + MpId = ArmReadMpidr (); + ClusterId = GET_CLUSTER_ID (MpId); + CoreId = GET_CORE_ID (MpId); + + node = fdt_subnode_offset (fdt, 0, "cpus"); + if (node < 0) { + // Create the /cpus node + node = fdt_add_subnode (fdt, 0, "cpus"); + fdt_setprop_string (fdt, node, "name", "cpus"); + fdt_setprop_cell (fdt, node, "#address-cells", sizeof (UINTN) / 4); + fdt_setprop_cell (fdt, node, "#size-cells", 0); + CpusNodeExist = FALSE; + } else { + CpusNodeExist = TRUE; + } + + // Get pointer to ARM processor table + ArmProcessorTable = (ARM_PROCESSOR_TABLE *)gST->ConfigurationTable[Index].VendorTable; + ArmCoreInfoTable = ArmProcessorTable->ArmCpus; + + for (Index = 0; Index < ArmProcessorTable->NumberOfEntries; Index++) { + CoreMpId = (UINTN) GET_MPID (ArmCoreInfoTable[Index].ClusterId, + ArmCoreInfoTable[Index].CoreId); + AsciiSPrint (Name, 10, "cpu@%x", CoreMpId); + + // If the 'cpus' node did not exist then create all the 'cpu' nodes. + // In case 'cpus' node is provided in the original FDT then we do not add + // any 'cpu' node. + if (!CpusNodeExist) { + cpu_node = fdt_add_subnode (fdt, node, Name); + if (cpu_node < 0) { + DEBUG ((EFI_D_ERROR, "Error on creating '%s' node\n", Name)); + Status = EFI_INVALID_PARAMETER; + goto FAIL_COMPLETE_FDT; + } + + fdt_setprop_string (fdt, cpu_node, "device_type", "cpu"); + + CoreMpId = cpu_to_fdtn (CoreMpId); + fdt_setprop (fdt, cpu_node, "reg", &CoreMpId, sizeof (CoreMpId)); + } else { + cpu_node = fdt_subnode_offset (fdt, node, Name); + } + + if (cpu_node >= 0) { + Method = fdt_getprop (fdt, cpu_node, "enable-method", &lenp); + // We only care when 'enable-method' == 'spin-table'. If the enable-method is not defined + // or defined as 'psci' then we ignore its properties. + if ((Method != NULL) && (AsciiStrCmp ((CHAR8 *)Method, "spin-table") == 0)) { + // There are two cases; + // - UEFI firmware parked the secondary cores and/or UEFI firmware is aware of the CPU + // release addresses (PcdArmLinuxSpinTable == TRUE) + // - the parking of the secondary cores has been managed before starting UEFI and/or UEFI + // does not anything about the CPU release addresses - in this case we do nothing + if (FeaturePcdGet (PcdArmLinuxSpinTable)) { + CpuReleaseAddr = cpu_to_fdt64 (ArmCoreInfoTable[Index].MailboxSetAddress); + fdt_setprop (fdt, cpu_node, "cpu-release-addr", &CpuReleaseAddr, sizeof (CpuReleaseAddr)); + + // If it is not the primary core than the cpu should be disabled + if (((ArmCoreInfoTable[Index].ClusterId != ClusterId) || (ArmCoreInfoTable[Index].CoreId != CoreId))) { + fdt_setprop_string (fdt, cpu_node, "status", "disabled"); + } + } + } + } + } + break; + } + } + + // If we succeeded to generate the new Device Tree then free the old Device Tree + gBS->FreePages (*FdtBlobBase, EFI_SIZE_TO_PAGES (*FdtBlobSize)); + + // Update the real size of the Device Tree + fdt_pack ((VOID*)(UINTN)(NewFdtBlobBase)); + + *FdtBlobBase = NewFdtBlobBase; + *FdtBlobSize = (UINTN)fdt_totalsize ((VOID*)(UINTN)(NewFdtBlobBase)); + return EFI_SUCCESS; + +FAIL_COMPLETE_FDT: + gBS->FreePages (NewFdtBlobAllocation, EFI_SIZE_TO_PAGES (NewFdtBlobSize)); + +FAIL_RELOCATE_FDT: + *FdtBlobSize = (UINTN)fdt_totalsize ((VOID*)(UINTN)(*FdtBlobBase)); + // Return success even if we failed to update the FDT blob. + // The original one is still valid. + return EFI_SUCCESS; +} diff --git a/ArmPkg/Application/LinuxLoader/LinuxLoaderHelper.c b/ArmPkg/Application/LinuxLoader/LinuxLoaderHelper.c new file mode 100644 index 0000000000..4d7a844584 --- /dev/null +++ b/ArmPkg/Application/LinuxLoader/LinuxLoaderHelper.c @@ -0,0 +1,192 @@ +/** @file +* +* Copyright (c) 2011-2015, 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 +#include +#include +#include + +#include "LinuxLoader.h" + +STATIC CONST CHAR8 *mTokenList[] = { + /*"SEC",*/ + "PEI", + "DXE", + "BDS", + NULL +}; + +VOID +PrintPerformance ( + VOID + ) +{ + UINTN Key; + CONST VOID *Handle; + CONST CHAR8 *Token, *Module; + UINT64 Start, Stop, TimeStamp; + UINT64 Delta, TicksPerSecond, Milliseconds; + UINTN Index; + CHAR8 Buffer[100]; + UINTN CharCount; + BOOLEAN CountUp; + + TicksPerSecond = GetPerformanceCounterProperties (&Start, &Stop); + if (Start < Stop) { + CountUp = TRUE; + } else { + CountUp = FALSE; + } + + TimeStamp = 0; + Key = 0; + do { + Key = GetPerformanceMeasurement (Key, (CONST VOID **)&Handle, &Token, &Module, &Start, &Stop); + if (Key != 0) { + for (Index = 0; mTokenList[Index] != NULL; Index++) { + if (AsciiStriCmp (mTokenList[Index], Token) == 0) { + Delta = CountUp ? (Stop - Start) : (Start - Stop); + TimeStamp += Delta; + Milliseconds = DivU64x64Remainder (MultU64x32 (Delta, 1000), TicksPerSecond, NULL); + CharCount = AsciiSPrint (Buffer, sizeof (Buffer), "%6a %6ld ms\n", Token, Milliseconds); + SerialPortWrite ((UINT8 *) Buffer, CharCount); + break; + } + } + } + } while (Key != 0); + + CharCount = AsciiSPrint (Buffer, sizeof (Buffer), "Total Time = %ld ms\n\n", + DivU64x64Remainder (MultU64x32 (TimeStamp, 1000), TicksPerSecond, NULL)); + SerialPortWrite ((UINT8 *) Buffer, CharCount); +} + +STATIC +EFI_STATUS +InsertSystemMemoryResources ( + LIST_ENTRY *ResourceList, + EFI_HOB_RESOURCE_DESCRIPTOR *ResHob + ) +{ + SYSTEM_MEMORY_RESOURCE *NewResource; + LIST_ENTRY *Link; + LIST_ENTRY *NextLink; + LIST_ENTRY AttachedResources; + SYSTEM_MEMORY_RESOURCE *Resource; + EFI_PHYSICAL_ADDRESS NewResourceEnd; + + if (IsListEmpty (ResourceList)) { + NewResource = AllocateZeroPool (sizeof (SYSTEM_MEMORY_RESOURCE)); + NewResource->PhysicalStart = ResHob->PhysicalStart; + NewResource->ResourceLength = ResHob->ResourceLength; + InsertTailList (ResourceList, &NewResource->Link); + return EFI_SUCCESS; + } + + InitializeListHead (&AttachedResources); + + Link = ResourceList->ForwardLink; + ASSERT (Link != NULL); + while (Link != ResourceList) { + Resource = (SYSTEM_MEMORY_RESOURCE*)Link; + + // Sanity Check. The resources should not overlapped. + ASSERT (!((ResHob->PhysicalStart >= Resource->PhysicalStart) && (ResHob->PhysicalStart < (Resource->PhysicalStart + Resource->ResourceLength)))); + ASSERT (!((ResHob->PhysicalStart + ResHob->ResourceLength - 1 >= Resource->PhysicalStart) && + ((ResHob->PhysicalStart + ResHob->ResourceLength - 1) < (Resource->PhysicalStart + Resource->ResourceLength)))); + + // The new resource is attached after this resource descriptor + if (ResHob->PhysicalStart == Resource->PhysicalStart + Resource->ResourceLength) { + Resource->ResourceLength = Resource->ResourceLength + ResHob->ResourceLength; + + NextLink = RemoveEntryList (&Resource->Link); + InsertTailList (&AttachedResources, &Resource->Link); + Link = NextLink; + } + // The new resource is attached before this resource descriptor + else if (ResHob->PhysicalStart + ResHob->ResourceLength == Resource->PhysicalStart) { + Resource->PhysicalStart = ResHob->PhysicalStart; + Resource->ResourceLength = Resource->ResourceLength + ResHob->ResourceLength; + + NextLink = RemoveEntryList (&Resource->Link); + InsertTailList (&AttachedResources, &Resource->Link); + Link = NextLink; + } else { + Link = Link->ForwardLink; + } + } + + if (!IsListEmpty (&AttachedResources)) { + // See if we can merge the attached resource with other resources + + NewResource = (SYSTEM_MEMORY_RESOURCE*)GetFirstNode (&AttachedResources); + Link = RemoveEntryList (&NewResource->Link); + while (!IsListEmpty (&AttachedResources)) { + // Merge resources + Resource = (SYSTEM_MEMORY_RESOURCE*)Link; + + // Ensure they overlap each other + ASSERT ( + ((NewResource->PhysicalStart >= Resource->PhysicalStart) && (NewResource->PhysicalStart < (Resource->PhysicalStart + Resource->ResourceLength))) || + (((NewResource->PhysicalStart + NewResource->ResourceLength) >= Resource->PhysicalStart) && ((NewResource->PhysicalStart + NewResource->ResourceLength) < (Resource->PhysicalStart + Resource->ResourceLength))) + ); + + NewResourceEnd = MAX (NewResource->PhysicalStart + NewResource->ResourceLength, Resource->PhysicalStart + Resource->ResourceLength); + NewResource->PhysicalStart = MIN (NewResource->PhysicalStart, Resource->PhysicalStart); + NewResource->ResourceLength = NewResourceEnd - NewResource->PhysicalStart; + + Link = RemoveEntryList (Link); + } + } else { + // None of the Resource of the list is attached to this ResHob. Create a new entry for it + NewResource = AllocateZeroPool (sizeof (SYSTEM_MEMORY_RESOURCE)); + NewResource->PhysicalStart = ResHob->PhysicalStart; + NewResource->ResourceLength = ResHob->ResourceLength; + } + InsertTailList (ResourceList, &NewResource->Link); + return EFI_SUCCESS; +} + +EFI_STATUS +GetSystemMemoryResources ( + IN LIST_ENTRY *ResourceList + ) +{ + EFI_HOB_RESOURCE_DESCRIPTOR *ResHob; + + InitializeListHead (ResourceList); + + // Find the first System Memory Resource Descriptor + ResHob = (EFI_HOB_RESOURCE_DESCRIPTOR *)GetFirstHob (EFI_HOB_TYPE_RESOURCE_DESCRIPTOR); + while ((ResHob != NULL) && (ResHob->ResourceType != EFI_RESOURCE_SYSTEM_MEMORY)) { + ResHob = (EFI_HOB_RESOURCE_DESCRIPTOR *)GetNextHob (EFI_HOB_TYPE_RESOURCE_DESCRIPTOR, (VOID *)((UINTN)ResHob + ResHob->Header.HobLength)); + } + + // Did not find any + if (ResHob == NULL) { + return EFI_NOT_FOUND; + } else { + InsertSystemMemoryResources (ResourceList, ResHob); + } + + ResHob = (EFI_HOB_RESOURCE_DESCRIPTOR *)GetNextHob (EFI_HOB_TYPE_RESOURCE_DESCRIPTOR, (VOID *)((UINTN)ResHob + ResHob->Header.HobLength)); + while (ResHob != NULL) { + if (ResHob->ResourceType == EFI_RESOURCE_SYSTEM_MEMORY) { + InsertSystemMemoryResources (ResourceList, ResHob); + } + ResHob = (EFI_HOB_RESOURCE_DESCRIPTOR *)GetNextHob (EFI_HOB_TYPE_RESOURCE_DESCRIPTOR, (VOID *)((UINTN)ResHob + ResHob->Header.HobLength)); + } + + return EFI_SUCCESS; +} diff --git a/ArmPkg/Application/LinuxLoader/LinuxLoaderShellApp.c b/ArmPkg/Application/LinuxLoader/LinuxLoaderShellApp.c new file mode 100644 index 0000000000..2245185394 --- /dev/null +++ b/ArmPkg/Application/LinuxLoader/LinuxLoaderShellApp.c @@ -0,0 +1,298 @@ +/** @file +* +* Copyright (c) 2011-2015, 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 "LinuxLoader.h" + +// +// Internal variables +// + +CONST EFI_GUID mLinuxLoaderHiiGuid = { + 0xd5d16edc, 0x35c5, 0x4866, + {0xbd, 0xe5, 0x2b, 0x64, 0xa2, 0x26, 0x55, 0x6e} + }; +EFI_HANDLE mLinuxLoaderHiiHandle; + +STATIC CONST SHELL_PARAM_ITEM ParamList[] = { + {L"-f", TypeValue}, + {L"-d", TypeValue}, + {L"-c", TypeValue}, + {L"-a", TypeValue}, + {NULL , TypeMax } + }; + +/** + Print a string given the "HII Id" of the format string and a list of + arguments. + + @param[in] Language The language of the string to retrieve. If + this parameter is NULL, then the current + platform language is used. + @param[in] HiiFormatStringId The format string Id for getting from Hii. + @param[in] ... The variable argument list. + + @retval EFI_SUCCESS The printing was successful. + @retval EFI_OUT_OF_RESOURCES A memory allocation failed. + +**/ +EFI_STATUS +PrintHii ( + IN CONST CHAR8 *Language OPTIONAL, + IN CONST EFI_STRING_ID HiiFormatStringId, + ... + ) +{ + VA_LIST Marker; + CHAR16 *HiiFormatString; + CHAR16 Buffer[MAX_MSG_LEN]; + + VA_START (Marker, HiiFormatStringId); + + HiiFormatString = HiiGetString (mLinuxLoaderHiiHandle, HiiFormatStringId, Language); + if (HiiFormatString != NULL) { + UnicodeVSPrint (Buffer, sizeof (Buffer), HiiFormatString, Marker); + Print (L"%s", Buffer); + FreePool (HiiFormatString); + } else { + ASSERT (FALSE); + return EFI_OUT_OF_RESOURCES; + } + + VA_END (Marker); + + return EFI_SUCCESS; +} + +/** + Print the help. + + @param[in] Language The language of the string to retrieve. If this + parameter is NULL, then the current platform + language is used. +**/ +VOID +PrintHelp ( + IN CONST CHAR8 *Language OPTIONAL + ) +{ + CHAR16 *Help; + CHAR16 *Walker; + CHAR16 *LineEnd; + + // + // Print the help line by line as it is too big to be printed at once. + // + + Help = HiiGetString (mLinuxLoaderHiiHandle, STRING_TOKEN (STR_HELP), Language); + if (Help != NULL) { + Walker = Help; + while (*Walker != L'\0') { + LineEnd = StrStr (Walker, L"\r\n"); + if (LineEnd != NULL) { + *LineEnd = L'\0'; + } + Print (L"%s\r\n", Walker); + if (LineEnd == NULL) { + break; + } + Walker = LineEnd + 2; + } + FreePool (Help); + } + +} + +/** + Process the Shell parameters in the case the application has been called + from the EFI Shell. + + @param[out] KernelPath A pointer to the buffer where the path + to the Linux kernel (EFI Shell file path + or device path is stored. The address of + the buffer is NULL in case of an error. + Otherwise, the returned address is the + address of a buffer allocated with + a call to AllocatePool() that has to be + freed by the caller. + @param[out] FdtPath A pointer to the buffer where the path + to the FDT (EFI Shell file path or + device path) is stored. The address of + the buffer is NULL in case of an error or + if the path to the FDT is not defined. + Otherwise, the returned address is the + address of a buffer allocated with a call + to AllocatePool() that has to be freed by + the caller. + @param[out] InitrdPath A pointer to the buffer where the path + (EFI Shell file path or device path) + to the RAM root file system is stored. + The address of the buffer is NULL in case + of an error or if the path to the RAM root + file system is not defined. Otherwise, the + returned address is the address of a + buffer allocated with a call to + AllocatePool() that has to be freed by + the caller. + @param[out] LinuxCommandLine A pointer to the buffer where the Linux + kernel command line is stored. The address + of the buffer is NULL in case of an error + or if the Linux command line is not + defined. Otherwise, the returned address + is the address of a buffer allocated with + a call to AllocatePool() that has to be + freed by the caller. + @param[out] AtagMachineType Value of the ARM Machine Type + + @retval EFI_SUCCESS The processing was successfull. + @retval EFI_ABORTED The initialisation of the Shell Library failed. + @retval EFI_NOT_FOUND Path to the Linux kernel not found. + @retval EFI_INVALID_PARAMETER At least one parameter is not valid or there is a + conflict between two parameters. + @retval EFI_OUT_OF_RESOURCES A memory allocation failed. + +**/ +EFI_STATUS +ProcessShellParameters ( + OUT CHAR16 **KernelPath, + OUT CHAR16 **FdtPath, + OUT CHAR16 **InitrdPath, + OUT CHAR16 **LinuxCommandLine, + OUT UINTN *AtagMachineType + ) +{ + EFI_STATUS Status; + LIST_ENTRY *CheckPackage; + CHAR16 *ProblemParam; + CONST CHAR16 *FlagValue; + CONST CHAR16 *ParameterValue; + UINTN LinuxCommandLineLen; + + + *KernelPath = NULL; + *FdtPath = NULL; + *InitrdPath = NULL; + *LinuxCommandLine = NULL; + *AtagMachineType = ARM_FDT_MACHINE_TYPE; + + // + // Initialise the Shell Library as we are going to use it. + // Assert that the return code is EFI_SUCCESS as it should. + // To anticipate any change is the codes returned by + // ShellInitialize(), leave in case of error. + // + Status = ShellInitialize (); + if (EFI_ERROR (Status)) { + ASSERT_EFI_ERROR (Status); + return EFI_ABORTED; + } + + Status = ShellCommandLineParse (ParamList, &CheckPackage, &ProblemParam, TRUE); + if (EFI_ERROR (Status)) { + if ((Status == EFI_VOLUME_CORRUPTED) && + (ProblemParam != NULL) ) { + PrintHii ( + NULL, STRING_TOKEN (STR_SHELL_INVALID_PARAMETER), ProblemParam + ); + FreePool (ProblemParam); + } else { + ASSERT (FALSE); + } + goto Error; + } + + Status = EFI_INVALID_PARAMETER; + if (ShellCommandLineGetCount (CheckPackage) != 2) { + PrintHelp (NULL); + goto Error; + } + + Status = EFI_OUT_OF_RESOURCES; + + FlagValue = ShellCommandLineGetValue (CheckPackage, L"-a"); + if (FlagValue != NULL) { + if (ShellCommandLineGetFlag (CheckPackage, L"-d")) { + PrintHii (NULL, STRING_TOKEN (STR_ATAG_FDT_CONFLICT)); + goto Error; + } + *AtagMachineType = StrDecimalToUintn (FlagValue); + } + + ParameterValue = ShellCommandLineGetRawValue (CheckPackage, 1); + *KernelPath = AllocateCopyPool (StrSize (ParameterValue), ParameterValue); + if (*KernelPath == NULL) { + goto Error; + } + + FlagValue = ShellCommandLineGetValue (CheckPackage, L"-d"); + if (FlagValue != NULL) { + *FdtPath = AllocateCopyPool (StrSize (FlagValue), FlagValue); + if (*FdtPath == NULL) { + goto Error; + } + } + + FlagValue = ShellCommandLineGetValue (CheckPackage, L"-f"); + if (FlagValue != NULL) { + *InitrdPath = AllocateCopyPool (StrSize (FlagValue), FlagValue); + if (*InitrdPath == NULL) { + goto Error; + } + } + + FlagValue = ShellCommandLineGetValue (CheckPackage, L"-c"); + if (FlagValue != NULL) { + LinuxCommandLineLen = StrLen (FlagValue); + if ((LinuxCommandLineLen != 0) && + (FlagValue[0] == L'"' ) && + (FlagValue[LinuxCommandLineLen - 1] == L'"')) { + FlagValue++; + LinuxCommandLineLen -= 2; + } + + *LinuxCommandLine = AllocateCopyPool ( + (LinuxCommandLineLen + 1) * sizeof (CHAR16), + FlagValue + ); + if (*LinuxCommandLine == NULL) { + goto Error; + } + (*LinuxCommandLine)[LinuxCommandLineLen] = L'\0'; + } + + Status = EFI_SUCCESS; + +Error: + ShellCommandLineFreeVarList (CheckPackage); + + if (EFI_ERROR (Status)) { + if (*KernelPath != NULL) { + FreePool (*KernelPath); + *KernelPath = NULL; + } + if (*FdtPath != NULL) { + FreePool (*FdtPath); + *FdtPath = NULL; + } + if (*InitrdPath != NULL) { + FreePool (*InitrdPath); + *InitrdPath = NULL; + } + if (*LinuxCommandLine != NULL) { + FreePool (*LinuxCommandLine); + *LinuxCommandLine = NULL; + } + } + + return Status; +} diff --git a/ArmPkg/ArmPkg.dsc b/ArmPkg/ArmPkg.dsc index 6959fde22c..1f69799dc2 100644 --- a/ArmPkg/ArmPkg.dsc +++ b/ArmPkg/ArmPkg.dsc @@ -78,6 +78,10 @@ BdsLib|ArmPkg/Library/BdsLib/BdsLib.inf FdtLib|EmbeddedPkg/Library/FdtLib/FdtLib.inf + ShellLib|ShellPkg/Library/UefiShellLib/UefiShellLib.inf + FileHandleLib|MdePkg/Library/UefiFileHandleLib/UefiFileHandleLib.inf + SortLib|MdeModulePkg/Library/UefiSortLib/UefiSortLib.inf + IoLib|MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsic.inf [LibraryClasses.ARM] @@ -138,7 +142,7 @@ ArmPkg/Filesystem/SemihostFs/SemihostFs.inf - ArmPkg/Application/LinuxLoader/LinuxFdtLoader.inf + ArmPkg/Application/LinuxLoader/LinuxLoader.inf [Components.ARM] ArmPkg/Library/BaseMemoryLibVstm/BaseMemoryLibVstm.inf @@ -154,8 +158,6 @@ ArmPkg/Library/ArmLib/ArmV7/ArmV7LibSec.inf ArmPkg/Library/ArmLib/ArmV7/ArmV7LibPrePi.inf - ArmPkg/Application/LinuxLoader/LinuxAtagLoader.inf - [Components.AARCH64] ArmPkg/Drivers/ArmCpuLib/ArmCortexAEMv8Lib/ArmCortexAEMv8Lib.inf ArmPkg/Drivers/ArmCpuLib/ArmCortexA5xLib/ArmCortexA5xLib.inf