+++ /dev/null
-/** @file\r
-*\r
-* Copyright (c) 2011-2015, ARM Limited. All rights reserved.\r
-*\r
-* This program and the accompanying materials\r
-* are licensed and made available under the terms and conditions of the BSD License\r
-* which accompanies this distribution. The full text of the license may be found at\r
-* http://opensource.org/licenses/bsd-license.php\r
-*\r
-* THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
-* WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
-*\r
-**/\r
-\r
-#include <PiDxe.h>\r
-#include <Library/ArmLib.h>\r
-#include <Library/HobLib.h>\r
-\r
-#include <Guid/ArmMpCoreInfo.h>\r
-\r
-#include "LinuxLoader.h"\r
-\r
-#define ALIGN(x, a) (((x) + ((a) - 1)) & ~((a) - 1))\r
-#define PALIGN(p, a) ((void *)(ALIGN ((unsigned long)(p), (a))))\r
-#define GET_CELL(p) (p += 4, *((const UINT32 *)(p-4)))\r
-\r
-STATIC\r
-UINTN\r
-cpu_to_fdtn (UINTN x) {\r
- if (sizeof (UINTN) == sizeof (UINT32)) {\r
- return cpu_to_fdt32 (x);\r
- } else {\r
- return cpu_to_fdt64 (x);\r
- }\r
-}\r
-\r
-typedef struct {\r
- UINTN Base;\r
- UINTN Size;\r
-} FDT_REGION;\r
-\r
-STATIC\r
-BOOLEAN\r
-IsLinuxReservedRegion (\r
- IN EFI_MEMORY_TYPE MemoryType\r
- )\r
-{\r
- switch (MemoryType) {\r
- case EfiRuntimeServicesCode:\r
- case EfiRuntimeServicesData:\r
- case EfiUnusableMemory:\r
- case EfiACPIReclaimMemory:\r
- case EfiACPIMemoryNVS:\r
- case EfiReservedMemoryType:\r
- return TRUE;\r
- default:\r
- return FALSE;\r
- }\r
-}\r
-\r
-/**\r
-** Relocate the FDT blob to a more appropriate location for the Linux kernel.\r
-** This function will allocate memory for the relocated FDT blob.\r
-**\r
-** @retval EFI_SUCCESS on success.\r
-** @retval EFI_OUT_OF_RESOURCES or EFI_INVALID_PARAMETER on failure.\r
-*/\r
-STATIC\r
-EFI_STATUS\r
-RelocateFdt (\r
- EFI_PHYSICAL_ADDRESS SystemMemoryBase,\r
- EFI_PHYSICAL_ADDRESS OriginalFdt,\r
- UINTN OriginalFdtSize,\r
- EFI_PHYSICAL_ADDRESS *RelocatedFdt,\r
- UINTN *RelocatedFdtSize,\r
- EFI_PHYSICAL_ADDRESS *RelocatedFdtAlloc\r
- )\r
-{\r
- EFI_STATUS Status;\r
- INTN Error;\r
- UINT64 FdtAlignment;\r
-\r
- *RelocatedFdtSize = OriginalFdtSize + FDT_ADDITIONAL_ENTRIES_SIZE;\r
-\r
- // If FDT load address needs to be aligned, allocate more space.\r
- FdtAlignment = PcdGet32 (PcdArmLinuxFdtAlignment);\r
- if (FdtAlignment != 0) {\r
- *RelocatedFdtSize += FdtAlignment;\r
- }\r
-\r
- // Try below a watermark address.\r
- Status = EFI_NOT_FOUND;\r
- if (PcdGet32 (PcdArmLinuxFdtMaxOffset) != 0) {\r
- *RelocatedFdt = LINUX_FDT_MAX_OFFSET;\r
- Status = gBS->AllocatePages (AllocateMaxAddress, EfiBootServicesData,\r
- EFI_SIZE_TO_PAGES (*RelocatedFdtSize), RelocatedFdt);\r
- if (EFI_ERROR (Status)) {\r
- 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));\r
- }\r
- }\r
-\r
- // Try anywhere there is available space.\r
- if (EFI_ERROR (Status)) {\r
- Status = gBS->AllocatePages (AllocateAnyPages, EfiBootServicesData,\r
- EFI_SIZE_TO_PAGES (*RelocatedFdtSize), RelocatedFdt);\r
- if (EFI_ERROR (Status)) {\r
- ASSERT_EFI_ERROR (Status);\r
- return EFI_OUT_OF_RESOURCES;\r
- } else {\r
- 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));\r
- }\r
- }\r
-\r
- *RelocatedFdtAlloc = *RelocatedFdt;\r
- if (FdtAlignment != 0) {\r
- *RelocatedFdt = ALIGN (*RelocatedFdt, FdtAlignment);\r
- }\r
-\r
- // Load the Original FDT tree into the new region\r
- Error = fdt_open_into ((VOID*)(UINTN) OriginalFdt,\r
- (VOID*)(UINTN)(*RelocatedFdt), *RelocatedFdtSize);\r
- if (Error) {\r
- DEBUG ((EFI_D_ERROR, "fdt_open_into(): %a\n", fdt_strerror (Error)));\r
- gBS->FreePages (*RelocatedFdtAlloc, EFI_SIZE_TO_PAGES (*RelocatedFdtSize));\r
- return EFI_INVALID_PARAMETER;\r
- }\r
-\r
- return EFI_SUCCESS;\r
-}\r
-\r
-EFI_STATUS\r
-PrepareFdt (\r
- IN EFI_PHYSICAL_ADDRESS SystemMemoryBase,\r
- IN CONST CHAR8* CommandLineArguments,\r
- IN EFI_PHYSICAL_ADDRESS InitrdImage,\r
- IN UINTN InitrdImageSize,\r
- IN OUT EFI_PHYSICAL_ADDRESS *FdtBlobBase,\r
- IN OUT UINTN *FdtBlobSize\r
- )\r
-{\r
- EFI_STATUS Status;\r
- EFI_PHYSICAL_ADDRESS NewFdtBlobBase;\r
- EFI_PHYSICAL_ADDRESS NewFdtBlobAllocation;\r
- UINTN NewFdtBlobSize;\r
- VOID* fdt;\r
- INTN err;\r
- INTN node;\r
- INTN cpu_node;\r
- INT32 lenp;\r
- CONST VOID* BootArg;\r
- CONST VOID* Method;\r
- EFI_PHYSICAL_ADDRESS InitrdImageStart;\r
- EFI_PHYSICAL_ADDRESS InitrdImageEnd;\r
- FDT_REGION Region;\r
- UINTN Index;\r
- CHAR8 Name[10];\r
- LIST_ENTRY ResourceList;\r
- SYSTEM_MEMORY_RESOURCE *Resource;\r
- ARM_PROCESSOR_TABLE *ArmProcessorTable;\r
- ARM_CORE_INFO *ArmCoreInfoTable;\r
- UINT32 MpId;\r
- UINT32 ClusterId;\r
- UINT32 CoreId;\r
- UINT64 CpuReleaseAddr;\r
- UINTN MemoryMapSize;\r
- EFI_MEMORY_DESCRIPTOR *MemoryMap;\r
- EFI_MEMORY_DESCRIPTOR *MemoryMapPtr;\r
- UINTN MapKey;\r
- UINTN DescriptorSize;\r
- UINT32 DescriptorVersion;\r
- UINTN Pages;\r
- UINTN OriginalFdtSize;\r
- BOOLEAN CpusNodeExist;\r
- UINTN CoreMpId;\r
-\r
- NewFdtBlobAllocation = 0;\r
-\r
- //\r
- // Sanity checks on the original FDT blob.\r
- //\r
- err = fdt_check_header ((VOID*)(UINTN)(*FdtBlobBase));\r
- if (err != 0) {\r
- Print (L"ERROR: Device Tree header not valid (err:%d)\n", err);\r
- return EFI_INVALID_PARAMETER;\r
- }\r
-\r
- // The original FDT blob might have been loaded partially.\r
- // Check that it is not the case.\r
- OriginalFdtSize = (UINTN)fdt_totalsize ((VOID*)(UINTN)(*FdtBlobBase));\r
- if (OriginalFdtSize > *FdtBlobSize) {\r
- Print (L"ERROR: Incomplete FDT. Only %d/%d bytes have been loaded.\n",\r
- *FdtBlobSize, OriginalFdtSize);\r
- return EFI_INVALID_PARAMETER;\r
- }\r
-\r
- //\r
- // Relocate the FDT to its final location.\r
- //\r
- Status = RelocateFdt (SystemMemoryBase, *FdtBlobBase, OriginalFdtSize,\r
- &NewFdtBlobBase, &NewFdtBlobSize, &NewFdtBlobAllocation);\r
- if (EFI_ERROR (Status)) {\r
- goto FAIL_RELOCATE_FDT;\r
- }\r
-\r
- fdt = (VOID*)(UINTN)NewFdtBlobBase;\r
-\r
- node = fdt_subnode_offset (fdt, 0, "chosen");\r
- if (node < 0) {\r
- // The 'chosen' node does not exist, create it\r
- node = fdt_add_subnode (fdt, 0, "chosen");\r
- if (node < 0) {\r
- DEBUG ((EFI_D_ERROR, "Error on finding 'chosen' node\n"));\r
- Status = EFI_INVALID_PARAMETER;\r
- goto FAIL_COMPLETE_FDT;\r
- }\r
- }\r
-\r
- DEBUG_CODE_BEGIN ();\r
- BootArg = fdt_getprop (fdt, node, "bootargs", &lenp);\r
- if (BootArg != NULL) {\r
- DEBUG ((EFI_D_ERROR, "BootArg: %a\n", BootArg));\r
- }\r
- DEBUG_CODE_END ();\r
-\r
- //\r
- // Set Linux CmdLine\r
- //\r
- if ((CommandLineArguments != NULL) && (AsciiStrLen (CommandLineArguments) > 0)) {\r
- err = fdt_setprop (fdt, node, "bootargs", CommandLineArguments, AsciiStrSize (CommandLineArguments));\r
- if (err) {\r
- DEBUG ((EFI_D_ERROR, "Fail to set new 'bootarg' (err:%d)\n", err));\r
- }\r
- }\r
-\r
- //\r
- // Set Linux Initrd\r
- //\r
- if (InitrdImageSize != 0) {\r
- InitrdImageStart = cpu_to_fdt64 (InitrdImage);\r
- err = fdt_setprop (fdt, node, "linux,initrd-start", &InitrdImageStart, sizeof (EFI_PHYSICAL_ADDRESS));\r
- if (err) {\r
- DEBUG ((EFI_D_ERROR, "Fail to set new 'linux,initrd-start' (err:%d)\n", err));\r
- }\r
- InitrdImageEnd = cpu_to_fdt64 (InitrdImage + InitrdImageSize);\r
- err = fdt_setprop (fdt, node, "linux,initrd-end", &InitrdImageEnd, sizeof (EFI_PHYSICAL_ADDRESS));\r
- if (err) {\r
- DEBUG ((EFI_D_ERROR, "Fail to set new 'linux,initrd-start' (err:%d)\n", err));\r
- }\r
- }\r
-\r
- //\r
- // Set Physical memory setup if does not exist\r
- //\r
- node = fdt_subnode_offset (fdt, 0, "memory");\r
- if (node < 0) {\r
- // The 'memory' node does not exist, create it\r
- node = fdt_add_subnode (fdt, 0, "memory");\r
- if (node >= 0) {\r
- fdt_setprop_string (fdt, node, "name", "memory");\r
- fdt_setprop_string (fdt, node, "device_type", "memory");\r
-\r
- GetSystemMemoryResources (&ResourceList);\r
- Resource = (SYSTEM_MEMORY_RESOURCE*)ResourceList.ForwardLink;\r
-\r
- Region.Base = cpu_to_fdtn ((UINTN)Resource->PhysicalStart);\r
- Region.Size = cpu_to_fdtn ((UINTN)Resource->ResourceLength);\r
-\r
- err = fdt_setprop (fdt, node, "reg", &Region, sizeof (Region));\r
- if (err) {\r
- DEBUG ((EFI_D_ERROR, "Fail to set new 'memory region' (err:%d)\n", err));\r
- }\r
- }\r
- }\r
-\r
- //\r
- // Add the memory regions reserved by the UEFI Firmware\r
- //\r
-\r
- // Retrieve the UEFI Memory Map\r
- MemoryMap = NULL;\r
- MemoryMapSize = 0;\r
- Status = gBS->GetMemoryMap (&MemoryMapSize, MemoryMap, &MapKey, &DescriptorSize, &DescriptorVersion);\r
- if (Status == EFI_BUFFER_TOO_SMALL) {\r
- // The UEFI specification advises to allocate more memory for the MemoryMap buffer between successive\r
- // calls to GetMemoryMap(), since allocation of the new buffer may potentially increase memory map size.\r
- Pages = EFI_SIZE_TO_PAGES (MemoryMapSize) + 1;\r
- MemoryMap = AllocatePages (Pages);\r
- if (MemoryMap == NULL) {\r
- Status = EFI_OUT_OF_RESOURCES;\r
- goto FAIL_COMPLETE_FDT;\r
- }\r
- Status = gBS->GetMemoryMap (&MemoryMapSize, MemoryMap, &MapKey, &DescriptorSize, &DescriptorVersion);\r
- }\r
-\r
- // Go through the list and add the reserved region to the Device Tree\r
- if (!EFI_ERROR (Status)) {\r
- MemoryMapPtr = MemoryMap;\r
- for (Index = 0; Index < (MemoryMapSize / DescriptorSize); Index++) {\r
- if (IsLinuxReservedRegion ((EFI_MEMORY_TYPE)MemoryMapPtr->Type)) {\r
- DEBUG ((DEBUG_VERBOSE, "Reserved region of type %d [0x%lX, 0x%lX]\n",\r
- MemoryMapPtr->Type,\r
- (UINTN)MemoryMapPtr->PhysicalStart,\r
- (UINTN)(MemoryMapPtr->PhysicalStart + MemoryMapPtr->NumberOfPages * EFI_PAGE_SIZE)));\r
- err = fdt_add_mem_rsv (fdt, MemoryMapPtr->PhysicalStart, MemoryMapPtr->NumberOfPages * EFI_PAGE_SIZE);\r
- if (err != 0) {\r
- Print (L"Warning: Fail to add 'memreserve' (err:%d)\n", err);\r
- }\r
- }\r
- MemoryMapPtr = (EFI_MEMORY_DESCRIPTOR*)((UINTN)MemoryMapPtr + DescriptorSize);\r
- }\r
- }\r
-\r
- //\r
- // Setup Arm Mpcore Info if it is a multi-core or multi-cluster platforms.\r
- //\r
- // For 'cpus' and 'cpu' device tree nodes bindings, refer to this file\r
- // in the kernel documentation:\r
- // Documentation/devicetree/bindings/arm/cpus.txt\r
- //\r
- for (Index = 0; Index < gST->NumberOfTableEntries; Index++) {\r
- // Check for correct GUID type\r
- if (CompareGuid (&gArmMpCoreInfoGuid, &(gST->ConfigurationTable[Index].VendorGuid))) {\r
- MpId = ArmReadMpidr ();\r
- ClusterId = GET_CLUSTER_ID (MpId);\r
- CoreId = GET_CORE_ID (MpId);\r
-\r
- node = fdt_subnode_offset (fdt, 0, "cpus");\r
- if (node < 0) {\r
- // Create the /cpus node\r
- node = fdt_add_subnode (fdt, 0, "cpus");\r
- fdt_setprop_string (fdt, node, "name", "cpus");\r
- fdt_setprop_cell (fdt, node, "#address-cells", sizeof (UINTN) / 4);\r
- fdt_setprop_cell (fdt, node, "#size-cells", 0);\r
- CpusNodeExist = FALSE;\r
- } else {\r
- CpusNodeExist = TRUE;\r
- }\r
-\r
- // Get pointer to ARM processor table\r
- ArmProcessorTable = (ARM_PROCESSOR_TABLE *)gST->ConfigurationTable[Index].VendorTable;\r
- ArmCoreInfoTable = ArmProcessorTable->ArmCpus;\r
-\r
- for (Index = 0; Index < ArmProcessorTable->NumberOfEntries; Index++) {\r
- CoreMpId = (UINTN) GET_MPID (ArmCoreInfoTable[Index].ClusterId,\r
- ArmCoreInfoTable[Index].CoreId);\r
- AsciiSPrint (Name, 10, "cpu@%x", CoreMpId);\r
-\r
- // If the 'cpus' node did not exist then create all the 'cpu' nodes.\r
- // In case 'cpus' node is provided in the original FDT then we do not add\r
- // any 'cpu' node.\r
- if (!CpusNodeExist) {\r
- cpu_node = fdt_add_subnode (fdt, node, Name);\r
- if (cpu_node < 0) {\r
- DEBUG ((EFI_D_ERROR, "Error on creating '%s' node\n", Name));\r
- Status = EFI_INVALID_PARAMETER;\r
- goto FAIL_COMPLETE_FDT;\r
- }\r
-\r
- fdt_setprop_string (fdt, cpu_node, "device_type", "cpu");\r
-\r
- CoreMpId = cpu_to_fdtn (CoreMpId);\r
- fdt_setprop (fdt, cpu_node, "reg", &CoreMpId, sizeof (CoreMpId));\r
- } else {\r
- cpu_node = fdt_subnode_offset (fdt, node, Name);\r
- }\r
-\r
- if (cpu_node >= 0) {\r
- Method = fdt_getprop (fdt, cpu_node, "enable-method", &lenp);\r
- // We only care when 'enable-method' == 'spin-table'. If the enable-method is not defined\r
- // or defined as 'psci' then we ignore its properties.\r
- if ((Method != NULL) && (AsciiStrCmp ((CHAR8 *)Method, "spin-table") == 0)) {\r
- // There are two cases;\r
- // - UEFI firmware parked the secondary cores and/or UEFI firmware is aware of the CPU\r
- // release addresses (PcdArmLinuxSpinTable == TRUE)\r
- // - the parking of the secondary cores has been managed before starting UEFI and/or UEFI\r
- // does not anything about the CPU release addresses - in this case we do nothing\r
- if (FeaturePcdGet (PcdArmLinuxSpinTable)) {\r
- CpuReleaseAddr = cpu_to_fdt64 (ArmCoreInfoTable[Index].MailboxSetAddress);\r
- fdt_setprop (fdt, cpu_node, "cpu-release-addr", &CpuReleaseAddr, sizeof (CpuReleaseAddr));\r
-\r
- // If it is not the primary core than the cpu should be disabled\r
- if (((ArmCoreInfoTable[Index].ClusterId != ClusterId) || (ArmCoreInfoTable[Index].CoreId != CoreId))) {\r
- fdt_setprop_string (fdt, cpu_node, "status", "disabled");\r
- }\r
- }\r
- }\r
- }\r
- }\r
- break;\r
- }\r
- }\r
-\r
- // If we succeeded to generate the new Device Tree then free the old Device Tree\r
- gBS->FreePages (*FdtBlobBase, EFI_SIZE_TO_PAGES (*FdtBlobSize));\r
-\r
- // Update the real size of the Device Tree\r
- fdt_pack ((VOID*)(UINTN)(NewFdtBlobBase));\r
-\r
- *FdtBlobBase = NewFdtBlobBase;\r
- *FdtBlobSize = (UINTN)fdt_totalsize ((VOID*)(UINTN)(NewFdtBlobBase));\r
- return EFI_SUCCESS;\r
-\r
-FAIL_COMPLETE_FDT:\r
- gBS->FreePages (NewFdtBlobAllocation, EFI_SIZE_TO_PAGES (NewFdtBlobSize));\r
-\r
-FAIL_RELOCATE_FDT:\r
- *FdtBlobSize = (UINTN)fdt_totalsize ((VOID*)(UINTN)(*FdtBlobBase));\r
- // Return success even if we failed to update the FDT blob.\r
- // The original one is still valid.\r
- return EFI_SUCCESS;\r
-}\r