From 9c16a23fba659cdf0ce798aa085a4fb8c3bd47d1 Mon Sep 17 00:00:00 2001 From: Olivier Martin Date: Thu, 16 Feb 2012 15:50:59 +0000 Subject: [PATCH 2/3] ArmPkg/BdsLib: Added support for modifying the passed FDT blob - Add Linux CmdLine if not defined - Add initrd if not defined - Add CPU parking address if not defined - Add System Memory info if not defined --- ArmPkg/ArmPkg.dsc | 1 + ArmPkg/Library/BdsLib/BdsInternal.h | 9 + ArmPkg/Library/BdsLib/BdsLib.inf | 3 + ArmPkg/Library/BdsLib/BdsLinuxFdt.c | 353 ++++++++++++++++++++++++++++++++ ArmPkg/Library/BdsLib/BdsLinuxLoader.c | 8 + ArmPkg/Library/BdsLib/BdsLinuxLoader.h | 10 +- 6 files changed, 383 insertions(+), 1 deletions(-) mode change 100644 => 100755 ArmPkg/ArmPkg.dsc mode change 100644 => 100755 ArmPkg/Library/BdsLib/BdsInternal.h mode change 100644 => 100755 ArmPkg/Library/BdsLib/BdsLib.inf create mode 100755 ArmPkg/Library/BdsLib/BdsLinuxFdt.c diff --git a/ArmPkg/ArmPkg.dsc b/ArmPkg/ArmPkg.dsc old mode 100644 new mode 100755 index f4989a6..07c825d --- a/ArmPkg/ArmPkg.dsc +++ b/ArmPkg/ArmPkg.dsc @@ -72,6 +72,7 @@ SerialPortLib|MdePkg/Library/BaseSerialPortLibNull/BaseSerialPortLibNull.inf BdsLib|ArmPkg/Library/BdsLib/BdsLib.inf + FdtLib|EmbeddedPkg/Library/FdtLib/FdtLib.inf IoLib|MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsic.inf diff --git a/ArmPkg/Library/BdsLib/BdsInternal.h b/ArmPkg/Library/BdsLib/BdsInternal.h old mode 100644 new mode 100755 index 880d780..80d21b2 --- a/ArmPkg/Library/BdsLib/BdsInternal.h +++ b/ArmPkg/Library/BdsLib/BdsInternal.h @@ -103,4 +103,13 @@ PrepareAtagList ( OUT UINT32 *AtagSize ); +EFI_STATUS +PrepareFdt ( + IN CONST CHAR8* CommandLineString, + IN EFI_PHYSICAL_ADDRESS InitrdImage, + IN UINTN InitrdImageSize, + IN OUT EFI_PHYSICAL_ADDRESS *FdtBlobBase, + IN OUT UINT32 *FdtBlobSize + ); + #endif diff --git a/ArmPkg/Library/BdsLib/BdsLib.inf b/ArmPkg/Library/BdsLib/BdsLib.inf old mode 100644 new mode 100755 index 20644f1..b3cab21 --- a/ArmPkg/Library/BdsLib/BdsLib.inf +++ b/ArmPkg/Library/BdsLib/BdsLib.inf @@ -27,6 +27,7 @@ BdsLinuxLoader.c BdsLinuxAtag.c + BdsLinuxFdt.c [Packages] MdePkg/MdePkg.dec @@ -41,9 +42,11 @@ HobLib PerformanceLib SerialPortLib + FdtLib [Guids] gEfiFileInfoGuid + gArmMpCoreInfoGuid [Protocols] gEfiBdsArchProtocolGuid diff --git a/ArmPkg/Library/BdsLib/BdsLinuxFdt.c b/ArmPkg/Library/BdsLib/BdsLinuxFdt.c new file mode 100755 index 0000000..5c14b65 --- /dev/null +++ b/ArmPkg/Library/BdsLib/BdsLinuxFdt.c @@ -0,0 +1,353 @@ +/** @file +* +* Copyright (c) 2011-2012, ARM Limited. All rights reserved. +* +* This program and the accompanying materials +* are licensed and made available under the terms and conditions of the BSD License +* which accompanies this distribution. The full text of the license may be found at +* http://opensource.org/licenses/bsd-license.php +* +* THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +* WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +* +**/ + +#include +#include + +#include "BdsInternal.h" + +#define LINUX_FDT_MAX_OFFSET (PcdGet32(PcdSystemMemoryBase) + PcdGet32(PcdArmLinuxAtagMaxOffset)) + +#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 +IsPrintableString ( + IN CONST VOID* data, + IN UINTN len + ) +{ + CONST CHAR8 *s = data; + CONST CHAR8 *ss; + + // zero length is not + if (len == 0) { + return 0; + } + + // must terminate with zero + if (s[len - 1] != '\0') { + return 0; + } + + ss = s; + while (*s/* && isprint(*s)*/) { + s++; + } + + // not zero, or not done yet + if (*s != '\0' || (s + 1 - ss) < len) { + return 0; + } + + return 1; +} + +STATIC +VOID +PrintData ( + IN CONST CHAR8* data, + IN UINTN len + ) +{ + UINTN i; + CONST CHAR8 *p = data; + + // no data, don't print + if (len == 0) + return; + + if (IsPrintableString (data, len)) { + Print(L" = \"%a\"", (const char *)data); + } else if ((len % 4) == 0) { + Print(L" = <"); + for (i = 0; i < len; i += 4) { + Print(L"0x%08x%a", fdt32_to_cpu(GET_CELL(p)),i < (len - 4) ? " " : ""); + } + Print(L">"); + } else { + Print(L" = ["); + for (i = 0; i < len; i++) + Print(L"%02x%a", *p++, i < len - 1 ? " " : ""); + Print(L"]"); + } +} + +VOID +DebugDumpFdt ( + IN VOID* FdtBlob + ) +{ + struct fdt_header *bph; + UINT32 off_dt; + UINT32 off_str; + CONST CHAR8* p_struct; + CONST CHAR8* p_strings; + CONST CHAR8* p; + CONST CHAR8* s; + CONST CHAR8* t; + UINT32 tag; + UINTN sz; + UINTN depth; + UINTN shift; + UINT32 version; + + depth = 0; + shift = 4; + + bph = FdtBlob; + off_dt = fdt32_to_cpu(bph->off_dt_struct); + off_str = fdt32_to_cpu(bph->off_dt_strings); + p_struct = (CONST CHAR8*)FdtBlob + off_dt; + p_strings = (CONST CHAR8*)FdtBlob + off_str; + version = fdt32_to_cpu(bph->version); + + p = p_struct; + while ((tag = fdt32_to_cpu(GET_CELL(p))) != FDT_END) { + + //printf("tag: 0x%08x (%d)\n", tag, p - p_struct); + + if (tag == FDT_BEGIN_NODE) { + s = p; + p = PALIGN(p + AsciiStrLen (s) + 1, 4); + + if (*s == '\0') + s = "/"; + + Print(L"%*s%a {\n", depth * shift, L" ", s); + + depth++; + continue; + } + + if (tag == FDT_END_NODE) { + depth--; + + Print(L"%*s};\n", depth * shift, L" "); + continue; + } + + if (tag == FDT_NOP) { + Print(L"%*s// [NOP]\n", depth * shift, L" "); + continue; + } + + if (tag != FDT_PROP) { + Print(L"%*s ** Unknown tag 0x%08x\n", depth * shift, L" ", tag); + break; + } + sz = fdt32_to_cpu(GET_CELL(p)); + s = p_strings + fdt32_to_cpu(GET_CELL(p)); + if (version < 16 && sz >= 8) + p = PALIGN(p, 8); + t = p; + + p = PALIGN(p + sz, 4); + + Print(L"%*s%a", depth * shift, L" ", s); + PrintData(t, sz); + Print(L";\n"); + } +} + +typedef struct { + UINTN Base; + UINTN Size; +} FdtRegion; + +EFI_STATUS +PrepareFdt ( + IN CONST CHAR8* CommandLineString, + IN EFI_PHYSICAL_ADDRESS InitrdImage, + IN UINTN InitrdImageSize, + IN OUT EFI_PHYSICAL_ADDRESS *FdtBlobBase, + IN OUT UINT32 *FdtBlobSize + ) +{ + EFI_STATUS Status; + EFI_PHYSICAL_ADDRESS NewFdtBlobBase; + UINTN NewFdtBlobSize; + VOID* fdt; + INTN err; + INTN node; + INTN cpu_node; + INTN lenp; + CONST VOID* BootArg; + EFI_PHYSICAL_ADDRESS InitrdImageStart; + EFI_PHYSICAL_ADDRESS InitrdImageEnd; + FdtRegion Region; + UINTN Index; + CHAR8 Name[10]; + LIST_ENTRY ResourceList; + BDS_SYSTEM_MEMORY_RESOURCE *Resource; + ARM_PROCESSOR_TABLE *ArmProcessorTable; + ARM_CORE_INFO *ArmCoreInfoTable; + UINT32 MpId; + UINT32 ClusterId; + UINT32 CoreId; + UINT64 CpuReleaseAddr; + + 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; + } + + // Allocate memory for the new FDT + NewFdtBlobBase = LINUX_FDT_MAX_OFFSET; + NewFdtBlobSize = *FdtBlobSize + FDT_ADDITIONAL_ENTRIES_SIZE; + Status = gBS->AllocatePages (AllocateMaxAddress, EfiBootServicesData, EFI_SIZE_TO_PAGES(NewFdtBlobSize), &NewFdtBlobBase); + if (EFI_ERROR(Status)) { + DEBUG ((EFI_D_WARN, "Warning: Failed to allocate Fdt below 0x%lX (%r). The Fdt will be allocated somewhere else in System Memory.\n",NewFdtBlobBase,Status)); + Status = gBS->AllocatePages (AllocateAnyPages, EfiBootServicesData, EFI_SIZE_TO_PAGES(NewFdtBlobSize), &NewFdtBlobBase); + ASSERT_EFI_ERROR(Status); + goto FAIL_NEW_FDT; + } + + // Load the Original FDT tree into the new region + fdt = (VOID*)(UINTN)NewFdtBlobBase; + err = fdt_open_into((VOID*)(UINTN)(*FdtBlobBase), fdt, NewFdtBlobSize); + if (err) { + DEBUG((EFI_D_ERROR, "fdt_open_into(): %a\n", fdt_strerror(err))); + Status = EFI_INVALID_PARAMETER; + goto FAIL_NEW_FDT; + } + + DEBUG_CODE_BEGIN(); + DebugDumpFdt (fdt); + DEBUG_CODE_END(); + + 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_NEW_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 ((CommandLineString != NULL) && (AsciiStrLen (CommandLineString) > 0)) { + err = fdt_setprop(fdt, node, "bootargs", CommandLineString, AsciiStrSize(CommandLineString)); + 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 'chosen' 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 = (BDS_SYSTEM_MEMORY_RESOURCE*)ResourceList.ForwardLink; + + if (sizeof(UINTN) == sizeof(UINT32)) { + Region.Base = cpu_to_fdt32((UINTN)Resource->PhysicalStart); + Region.Size = cpu_to_fdt32((UINTN)Resource->ResourceLength); + } else { + Region.Base = cpu_to_fdt64((UINTN)Resource->PhysicalStart); + Region.Size = cpu_to_fdt64((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)); + } + } + } + + // Setup Arm Mpcore Info if it is a multi-core or multi-cluster platforms + 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", 1); + fdt_setprop_cell(fdt, node, "#size-cells", 0); + } + + // Get pointer to ARM processor table + ArmProcessorTable = (ARM_PROCESSOR_TABLE *)gST->ConfigurationTable[Index].VendorTable; + ArmCoreInfoTable = ArmProcessorTable->ArmCpus; + + for (Index = 0; Index < ArmProcessorTable->NumberOfEntries; Index++) { + if (((ArmCoreInfoTable[Index].ClusterId != ClusterId) || (ArmCoreInfoTable[Index].CoreId != CoreId))) { + AsciiSPrint (Name, 10, "cpu@%d", Index); + cpu_node = fdt_subnode_offset(fdt, node, Name); + if (cpu_node < 0) { + cpu_node = fdt_add_subnode(fdt, node, Name); + } + fdt_setprop_string(fdt, cpu_node, "device-type", "cpu"); + fdt_setprop_string(fdt, cpu_node, "enable-method", "spin-table"); + fdt_setprop_string(fdt, cpu_node, "status", "disabled"); + CpuReleaseAddr = cpu_to_fdt64(ArmCoreInfoTable[Index].MailboxSetAddress); + fdt_setprop(fdt, cpu_node, "cpu-release-addr", &CpuReleaseAddr, sizeof(CpuReleaseAddr)); + } + } + break; + } + } + + DEBUG_CODE_BEGIN(); + DebugDumpFdt (fdt); + DEBUG_CODE_END(); + + *FdtBlobBase = NewFdtBlobBase; + *FdtBlobSize = (UINTN)fdt_totalsize ((VOID*)(UINTN)(NewFdtBlobBase));; + return EFI_SUCCESS; + +FAIL_NEW_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/Library/BdsLib/BdsLinuxLoader.c b/ArmPkg/Library/BdsLib/BdsLinuxLoader.c index 12a8862..82fa811 100755 --- a/ArmPkg/Library/BdsLib/BdsLinuxLoader.c +++ b/ArmPkg/Library/BdsLib/BdsLinuxLoader.c @@ -241,6 +241,14 @@ BdsBootLinuxFdt ( Print (L"ERROR: Did not find Device Tree blob.\n"); return Status; } + + // By setting address=0 we leave the memory allocation to the function + Status = PrepareFdt (Arguments, InitrdImage, InitrdImageSize, &KernelParamsAddress, &KernelParamsSize); + if (EFI_ERROR(Status)) { + Print(L"ERROR: Can not load Linux kernel with Device Tree. Status=0x%X\n", Status); + return Status; + } + return StartLinux (LinuxImage, LinuxImageSize, KernelParamsAddress, KernelParamsSize, FdtMachineType); } diff --git a/ArmPkg/Library/BdsLib/BdsLinuxLoader.h b/ArmPkg/Library/BdsLib/BdsLinuxLoader.h index 8d58ce1..9e45e03 100755 --- a/ArmPkg/Library/BdsLib/BdsLinuxLoader.h +++ b/ArmPkg/Library/BdsLib/BdsLinuxLoader.h @@ -15,12 +15,20 @@ #ifndef __BDSLINUXLOADER_H #define __BDSLINUXLOADER_H +#include + #define LINUX_UIMAGE_SIGNATURE 0x56190527 #define LINUX_ATAG_MAX_OFFSET (PcdGet32(PcdSystemMemoryBase) + PcdGet32(PcdArmLinuxAtagMaxOffset)) #define LINUX_KERNEL_MAX_OFFSET (PcdGet32(PcdSystemMemoryBase) + PcdGet32(PcdArmLinuxKernelMaxOffset)) -#define ATAG_MAX_SIZE 0x3000 +// Size allocated for the Atag list +#define ATAG_MAX_SIZE 0x3000 + +// 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 /* ATAG : list of possible tags */ #define ATAG_NONE 0x00000000 -- 1.7.0.4