diff options
author | oliviermartin <oliviermartin@6f19259b-4bc3-4df7-8a09-765794883524> | 2011-06-11 11:56:30 +0000 |
---|---|---|
committer | oliviermartin <oliviermartin@6f19259b-4bc3-4df7-8a09-765794883524> | 2011-06-11 11:56:30 +0000 |
commit | a355a3654f0af22db9f68d988dbb4c72b835f414 (patch) | |
tree | ed34e67320d2f483270a79887138ea02a196c474 /ArmPkg/Library/BdsLib/BdsLinuxLoader.c | |
parent | e6b3b50834110bc796a3706d6de80de113f439d2 (diff) | |
download | edk2-platforms-a355a3654f0af22db9f68d988dbb4c72b835f414.tar.xz |
ArmPkg/BdsLib: Upgrade the library to use natively the Device Path
The previous version was using the string representation of the Device Path.
This new version takes as paramater the binary representation of the Device Path
It also tries to detect which kind of device support it refers by using the remaining
part of the Device Path after it has been loaded by gBS->ConnectController()
Lots of bug have been fixed as well in this new version.
git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@11799 6f19259b-4bc3-4df7-8a09-765794883524
Diffstat (limited to 'ArmPkg/Library/BdsLib/BdsLinuxLoader.c')
-rw-r--r-- | ArmPkg/Library/BdsLib/BdsLinuxLoader.c | 375 |
1 files changed, 171 insertions, 204 deletions
diff --git a/ArmPkg/Library/BdsLib/BdsLinuxLoader.c b/ArmPkg/Library/BdsLib/BdsLinuxLoader.c index 69435936b7..ca5e547043 100644 --- a/ArmPkg/Library/BdsLib/BdsLinuxLoader.c +++ b/ArmPkg/Library/BdsLib/BdsLinuxLoader.c @@ -19,58 +19,53 @@ #include <Library/ArmLib.h> #include <Library/HobLib.h> -STATIC -EFI_STATUS -GetARMLinuxMachineType ( - IN BOOLEAN FdtSupported, - OUT UINT32 *MachineType -) { - if (FdtSupported) - { - // FDT requires that the machine type is set to the maximum 32-bit number. - *MachineType = 0xFFFFFFFF; - } - else - { - // Non-FDT requires a specific machine type. - // This OS Boot loader supports just one machine type, - // but that could change in the future. - *MachineType = PcdGet32(PcdArmMachineType); - } +#define ALIGN32_BELOW(addr) ALIGN_POINTER(addr - 32,32) - return EFI_SUCCESS; -} +#define LINUX_ATAG_MAX_OFFSET (PcdGet32(PcdSystemMemoryBase) + PcdGet32(PcdArmLinuxAtagMaxOffset)) +#define LINUX_KERNEL_MAX_OFFSET (PcdGet32(PcdSystemMemoryBase) + PcdGet32(PcdArmLinuxKernelMaxOffset)) + +// Point to the current ATAG +STATIC LINUX_ATAG *mLinuxKernelCurrentAtag; STATIC VOID -SetupCoreTag( IN UINT32 PageSize ) +SetupCoreTag ( + IN UINT32 PageSize + ) { - Params->header.size = tag_size(atag_core); - Params->header.type = ATAG_CORE; + mLinuxKernelCurrentAtag->header.size = tag_size(LINUX_ATAG_CORE); + mLinuxKernelCurrentAtag->header.type = ATAG_CORE; - Params->body.core_tag.flags = 1; /* ensure read-only */ - Params->body.core_tag.pagesize = PageSize; /* systems PageSize (4k) */ - Params->body.core_tag.rootdev = 0; /* zero root device (typically overridden from kernel command line )*/ + 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 )*/ - Params = next_tag_address(Params); /* move pointer to next tag */ + // move pointer to next tag + mLinuxKernelCurrentAtag = next_tag_address(mLinuxKernelCurrentAtag); } STATIC VOID -SetupMemTag( IN UINTN StartAddress, IN UINT32 Size ) +SetupMemTag ( + IN UINTN StartAddress, + IN UINT32 Size + ) { - Params->header.size = tag_size(atag_mem); - Params->header.type = ATAG_MEM; + mLinuxKernelCurrentAtag->header.size = tag_size(LINUX_ATAG_MEM); + mLinuxKernelCurrentAtag->header.type = ATAG_MEM; - Params->body.mem_tag.start = StartAddress; /* Start of memory chunk for AtagMem */ - Params->body.mem_tag.size = Size; /* Size of memory chunk for AtagMem */ + mLinuxKernelCurrentAtag->body.mem_tag.start = StartAddress; /* Start of memory chunk for AtagMem */ + mLinuxKernelCurrentAtag->body.mem_tag.size = Size; /* Size of memory chunk for AtagMem */ - Params = next_tag_address(Params); /* move pointer to next tag */ + // move pointer to next tag + mLinuxKernelCurrentAtag = next_tag_address(mLinuxKernelCurrentAtag); } STATIC VOID -SetupCmdlineTag( IN CONST CHAR8 *CmdLine ) +SetupCmdlineTag ( + IN CONST CHAR8 *CmdLine + ) { UINT32 LineLength; @@ -81,98 +76,92 @@ SetupCmdlineTag( IN CONST CHAR8 *CmdLine ) * 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 ) - { - Params->header.size = ((UINT32)sizeof(struct atag_header) + LineLength + (UINT32)3) >> 2; - Params->header.type = ATAG_CMDLINE; + 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(Params->body.cmdline_tag.cmdline, CmdLine); + AsciiStrCpy(mLinuxKernelCurrentAtag->body.cmdline_tag.cmdline, CmdLine); - Params = next_tag_address(Params); /* move pointer to next tag */ + // move pointer to next tag + mLinuxKernelCurrentAtag = next_tag_address(mLinuxKernelCurrentAtag); } } STATIC VOID -SetupEndTag( VOID ) +SetupEndTag ( + VOID + ) { // Empty tag ends list; this has zero length and no body - Params->header.type = ATAG_NONE; - Params->header.size = 0; + 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(Params->header). + * The easiest way is to add the sizeof(mLinuxKernelCurrentAtag->header). */ - Params = (struct atag *)((UINT32)Params + sizeof(Params->header)); + mLinuxKernelCurrentAtag = (LINUX_ATAG*)((UINT32)mLinuxKernelCurrentAtag + sizeof(mLinuxKernelCurrentAtag->header)); } STATIC EFI_STATUS -PrepareAtagList( - IN OUT struct atag **AtagStartAddress, - IN CONST CHAR8* CommandLineString, - OUT UINT32 *AtagSize -) { - LIST_ENTRY *ResourceLink; - LIST_ENTRY ResourceList; +PrepareAtagList ( + IN CONST CHAR8* CommandLineString, + OUT LINUX_ATAG **AtagBase, + OUT UINT32 *AtagSize + ) +{ + EFI_STATUS Status; + LIST_ENTRY *ResourceLink; + LIST_ENTRY ResourceList; + EFI_PHYSICAL_ADDRESS AtagStartAddress; BDS_SYSTEM_MEMORY_RESOURCE *Resource; - // If no address supplied then this function will decide where to put it - if( *AtagStartAddress == 0 ) - { - /* WARNING: At the time of writing (2010-July-30) the linux kernel expects - * the atag list it in the first 1MB of memory and preferably at address 0x100. - * This has a very high risk of overwriting UEFI code, but as - * the linux kernel does not expect any runtime services from uefi - * and there is no afterlife section following the linux kernel termination, - * it does not matter if we stamp over that memory area. - * - * The proposed workaround is to create the atag list somewhere in boot services memory - * and then transfer it to address 0x100 (or to runtime services memory) immediately - * before starting the kernel. - * An additional benefit of this is that when we copy the ATAG list to it's final place, - * we can trim down the memory allocation size. Before we create the list we don't know - * how much space it is going to take, so we are over-allocating space. - */ - *AtagStartAddress = (struct atag *) AllocatePool(ATAG_MAX_SIZE); + 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_ERROR,"Failed to allocate Atag at 0x%lX (%r)\n",AtagStartAddress,Status)); + Status = gBS->AllocatePages (AllocateAnyPages, EfiBootServicesData, EFI_SIZE_TO_PAGES(ATAG_MAX_SIZE), &AtagStartAddress); + ASSERT_EFI_ERROR(Status); } - // Ensure the pointer is not NULL. - ASSERT( *AtagStartAddress != (struct atag *)NULL ); - // Ready to setup the atag list - Params = *AtagStartAddress; + mLinuxKernelCurrentAtag = (LINUX_ATAG*)(UINTN)AtagStartAddress; // Standard core tag 4k PageSize SetupCoreTag( (UINT32)SIZE_4KB ); // Physical memory setup - GetSystemMemoryResources(&ResourceList); + GetSystemMemoryResources (&ResourceList); ResourceLink = ResourceList.ForwardLink; while (ResourceLink != NULL && ResourceLink != &ResourceList) { - Resource = (BDS_SYSTEM_MEMORY_RESOURCE*)ResourceList.ForwardLink; + Resource = (BDS_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 - SetupCmdlineTag( CommandLineString ); + SetupCmdlineTag (CommandLineString); // end of tags SetupEndTag(); // Calculate atag list size - *AtagSize = (UINT32)Params - (UINT32)*AtagStartAddress + 1; + *AtagBase = (LINUX_ATAG*)(UINTN)AtagStartAddress; + *AtagSize = (UINT32)mLinuxKernelCurrentAtag - (UINT32)AtagStartAddress + 1; return EFI_SUCCESS; } STATIC EFI_STATUS -PreparePlatformHardware( VOID ) +PreparePlatformHardware ( + VOID + ) { //Note: Interrupts will be disabled by the GIC driver when ExitBootServices() will be called. @@ -185,153 +174,131 @@ PreparePlatformHardware( VOID ) ArmDisableInstructionCache (); // turn off MMU - ArmInvalidateTlb(); ArmDisableMmu(); return EFI_SUCCESS; } -/************************************************* - * R0, R1, R2 correspond to registers R0, R1, R2 - *************************************************/ -//STATIC -EFI_STATUS -StartLinuxKernel( IN VOID* KernelAddress, IN UINTN R0, IN UINTN R1, IN UINTN R2 ) -{ - VOID (*Kernel)(UINT32 Zero, UINT32 Arch, UINTN AtagListParams); +/** + Start a Linux kernel from a Device Path - // set the kernel address - Kernel = (VOID (*)(UINT32, UINT32, UINTN)) KernelAddress; + @param LinuxKernel Device Path to the Linux Kernel + @param Parameters Linux kernel agruments + @param Fdt Device Path to the Flat Device Tree - // Outside BootServices, so can't use Print(); - DEBUG((EFI_D_ERROR, "\nStarting the kernel:\n\n")); + @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. - // jump to kernel with register set - Kernel( R0, R1, R2 ); +**/ +EFI_STATUS +BdsBootLinux ( + IN EFI_DEVICE_PATH_PROTOCOL* LinuxKernelDevicePath, + IN CONST CHAR8* Arguments, + IN EFI_DEVICE_PATH_PROTOCOL* FdtDevicePath + ) +{ + EFI_STATUS Status; + UINT32 LinuxImageSize; + UINT32 KernelParamsSize; + VOID* KernelParamsAddress = NULL; + UINT32 MachineType; + BOOLEAN FdtSupported = FALSE; + LINUX_KERNEL LinuxKernel; + EFI_PHYSICAL_ADDRESS LinuxImage;; + + + 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)) { + DEBUG ((EFI_D_ERROR, "ERROR: Do not find Linux kernel.\n")); + return Status; + } + LinuxKernel = (LINUX_KERNEL)(UINTN)LinuxImage; - // Kernel should never exit - // After Life services are not provided - ASSERT( FALSE ); + // Load the FDT binary from a device path + KernelParamsAddress = (VOID*)LINUX_ATAG_MAX_OFFSET; + Status = BdsLoadImage (FdtDevicePath, AllocateMaxAddress, (EFI_PHYSICAL_ADDRESS*)&KernelParamsAddress, &KernelParamsSize); + if (!EFI_ERROR(Status)) { + FdtSupported = TRUE; + } - return EFI_SUCCESS; -} + // + // Setup the Linux Kernel Parameters + // + if (!FdtSupported) { + // Non-FDT requires a specific machine type. + // This OS Boot loader supports just one machine type, + // but that could change in the future. + MachineType = PcdGet32(PcdArmMachineType); -EFI_STATUS BdsBootLinux( - IN CONST CHAR16* LinuxKernel, - IN CONST CHAR8* ATag, - IN CONST CHAR16* Fdt -) { - BDS_FILE LinuxKernelFile; - BDS_FILE FdtFile; - EFI_STATUS Status; - VOID* LinuxImage; - - UINT32 KernelParamsSize; - VOID* KernelParamsAddress = NULL; - UINTN KernelParamsNewAddress; - UINTN *AtagAddress; - UINT32 MachineType; - BOOLEAN FdtSupported = FALSE; - EFI_HOB_RESOURCE_DESCRIPTOR *ResHob; - - // Load the Linux kernel from a device path - Status = BdsLoadFilePath(LinuxKernel, &LinuxKernelFile); - if (EFI_ERROR(Status)) { - DEBUG ((EFI_D_ERROR, "ERROR: Do not find Linux kernel %s\n",LinuxKernel)); - return Status; + // By setting address=0 we leave the memory allocation to the function + Status = PrepareAtagList (Arguments, (LINUX_ATAG**)&KernelParamsAddress, &KernelParamsSize); + if(EFI_ERROR(Status)) { + Print(L"ERROR: Can not prepare ATAG list. Status=0x%X\n", Status); + goto Exit; } + } else { + MachineType = 0xFFFFFFFF; + } - // Copy the Linux Kernel from the raw file to Runtime memory - Status = BdsCopyRawFileToRuntimeMemory(&LinuxKernelFile,&LinuxImage,NULL); - if (EFI_ERROR(Status)) { - goto Exit; - } + // Shut down UEFI boot services. ExitBootServices() will notify every driver that created an event on + // ExitBootServices event. Example the Interrupt DXE driver will disable the interrupts on this event. + Status = ShutdownUefiBootServices (); + if(EFI_ERROR(Status)) { + DEBUG((EFI_D_ERROR,"ERROR: Can not shutdown UEFI boot services. Status=0x%X\n", Status)); + goto Exit; + } - // Load the FDT binary from a device path - Status = BdsLoadFilePath(Fdt, &FdtFile); - if (!EFI_ERROR(Status)) { - // Copy the FDT binary from the raw file to Runtime memory - Status = BdsCopyRawFileToRuntimeMemory(&FdtFile,&KernelParamsAddress,&KernelParamsSize); - if (EFI_ERROR(Status)) { - goto Exit; - } else { - FdtSupported = TRUE; - } - } + // 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. + if ((UINTN)KernelParamsAddress > LINUX_ATAG_MAX_OFFSET) { + //Note: There is no requirement on the alignment + KernelParamsAddress = CopyMem (ALIGN32_BELOW(LINUX_ATAG_MAX_OFFSET - KernelParamsSize), KernelParamsAddress, KernelParamsSize); + } - /********************************************************** - * Setup the platform type - **********************************************************/ - Status = GetARMLinuxMachineType(FdtSupported, &MachineType); - if(EFI_ERROR(Status)) - { - Print(L"ERROR : Can not prepare ARM Linux machine type. Status=0x%X\n", Status); - goto Exit; - } + 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); + } - if (!FdtSupported) { - /********************************************************** - * Setup the ATAG list - **********************************************************/ - // By setting address=0 we leave the memory allocation to the function - AtagAddress = 0; - Status = PrepareAtagList( (struct atag **)&AtagAddress, ATag, &KernelParamsSize ); - KernelParamsAddress = (VOID*)AtagAddress; - if(EFI_ERROR(Status)) - { - Print(L"ERROR : Can not prepare ATAG list. Status=0x%X\n", Status); - goto Exit; - } - } + //TODO: Check there is no overlapping between kernel and Atag - /********************************************************** - * Switch off interrupts, caches, mmu, etc - **********************************************************/ - Status = PreparePlatformHardware(); - if(EFI_ERROR(Status)) - { - Print(L"ERROR : Can not prepare platform hardware. Status=0x%X\n", Status); - goto Exit; - } + // + // Switch off interrupts, caches, mmu, etc + // + Status = PreparePlatformHardware (); + ASSERT_EFI_ERROR(Status); - // Initialize the ATag destination - KernelParamsNewAddress = 0x100; - - // Update the ATag destination by finding the start address of the first System Memory Resource Descriptor Hob - ResHob = (EFI_HOB_RESOURCE_DESCRIPTOR *)GetFirstHob (EFI_HOB_TYPE_RESOURCE_DESCRIPTOR); - while (ResHob != NULL) { - if (ResHob->ResourceType == EFI_RESOURCE_SYSTEM_MEMORY) { - KernelParamsNewAddress = (UINTN)ResHob->PhysicalStart + 0x100; - break; - } - ResHob = (EFI_HOB_RESOURCE_DESCRIPTOR *)GetNextHob (EFI_HOB_TYPE_RESOURCE_DESCRIPTOR, (VOID *)((UINTN)ResHob + ResHob->Header.HobLength)); - } + // Register and print out performance information + PERF_END (NULL, "BDS", NULL, 0); + if (PerformanceMeasurementEnabled ()) { + PrintPerformance (); + } - // 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)) - { - Print(L"ERROR : Can not shutdown UEFI boot services. Status=0x%X\n", Status); - goto Exit; - } + // + // Start the Linux Kernel + // - // 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. - CopyMem((VOID*)KernelParamsNewAddress, KernelParamsAddress, KernelParamsSize); + // Outside BootServices, so can't use Print(); + DEBUG((EFI_D_ERROR, "\nStarting the kernel:\n\n")); - //********************************************************** - // * Start the Linux Kernel - // ********************************************************** - // Lift off ... - Status = StartLinuxKernel(LinuxImage, (UINTN)0, (UINTN)MachineType, KernelParamsNewAddress ); + // jump to kernel with register set + LinuxKernel ((UINTN)0, (UINTN)MachineType, (UINTN)KernelParamsAddress); - // Only be here if we fail to start Linux - DEBUG((EFI_D_ERROR, "ERROR : Can not start the kernel. Status=0x%X\n", Status)); + // Kernel should never exit + // After Life services are not provided + ASSERT(FALSE); Exit: - // Free Runtimee Memory (kernel and FDT) - return Status; + // Only be here if we fail to start Linux + Print (L"ERROR : Can not start the kernel. Status=0x%X\n", Status); + + // Free Runtimee Memory (kernel and FDT) + return Status; } |