3 * Copyright (c) 2011-2014, ARM Limited. All rights reserved.
5 * This program and the accompanying materials
6 * are licensed and made available under the terms and conditions of the BSD License
7 * which accompanies this distribution. The full text of the license may be found at
8 * http://opensource.org/licenses/bsd-license.php
10 * THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
15 #include <Library/ArmSmcLib.h>
16 #include <Library/PcdLib.h>
19 #include "BdsInternal.h"
20 #include "BdsLinuxLoader.h"
22 #define ALIGN(x, a) (((x) + ((a) - 1)) & ~((a) - 1))
23 #define PALIGN(p, a) ((void *)(ALIGN((unsigned long)(p), (a))))
24 #define GET_CELL(p) (p += 4, *((const UINT32 *)(p-4)))
28 cpu_to_fdtn (UINTN x
) {
29 if (sizeof (UINTN
) == sizeof (UINT32
)) {
30 return cpu_to_fdt32 (x
);
32 return cpu_to_fdt64 (x
);
49 CONST CHAR8
*s
= data
;
57 // Must terminate with zero
58 if (s
[len
- 1] != '\0') {
63 while (*s
/* && isprint(*s)*/) {
67 // Not zero, or not done yet
68 if (*s
!= '\0' || (s
+ 1 - ss
) < len
) {
83 CONST CHAR8
*p
= data
;
85 // No data, don't print
89 if (IsPrintableString (data
, len
)) {
90 Print(L
" = \"%a\"", (const char *)data
);
91 } else if ((len
% 4) == 0) {
93 for (i
= 0; i
< len
; i
+= 4) {
94 Print(L
"0x%08x%a", fdt32_to_cpu(GET_CELL(p
)),i
< (len
- 4) ? " " : "");
99 for (i
= 0; i
< len
; i
++)
100 Print(L
"%02x%a", *p
++, i
< len
- 1 ? " " : "");
110 struct fdt_header
*bph
;
113 CONST CHAR8
* p_struct
;
114 CONST CHAR8
* p_strings
;
125 // Can 'memreserve' be printed by below code?
126 INTN num
= fdt_num_mem_rsv(FdtBlob
);
128 UINT64 addr
= 0,size
= 0;
130 for (i
= 0; i
< num
; i
++) {
131 err
= fdt_get_mem_rsv(FdtBlob
, i
, &addr
, &size
);
133 DEBUG((EFI_D_ERROR
, "Error (%d) : Cannot get memreserve section (%d)\n", err
, i
));
136 Print(L
"/memreserve/ \t0x%lx \t0x%lx;\n",addr
,size
);
145 off_dt
= fdt32_to_cpu(bph
->off_dt_struct
);
146 off_str
= fdt32_to_cpu(bph
->off_dt_strings
);
147 p_struct
= (CONST CHAR8
*)FdtBlob
+ off_dt
;
148 p_strings
= (CONST CHAR8
*)FdtBlob
+ off_str
;
149 version
= fdt32_to_cpu(bph
->version
);
152 while ((tag
= fdt32_to_cpu(GET_CELL(p
))) != FDT_END
) {
153 if (tag
== FDT_BEGIN_NODE
) {
155 p
= PALIGN(p
+ AsciiStrLen (s
) + 1, 4);
160 Print(L
"%*s%a {\n", depth
* shift
, L
" ", s
);
166 if (tag
== FDT_END_NODE
) {
169 Print(L
"%*s};\n", depth
* shift
, L
" ");
173 if (tag
== FDT_NOP
) {
174 Print(L
"%*s// [NOP]\n", depth
* shift
, L
" ");
178 if (tag
!= FDT_PROP
) {
179 Print(L
"%*s ** Unknown tag 0x%08x\n", depth
* shift
, L
" ", tag
);
182 sz
= fdt32_to_cpu(GET_CELL(p
));
183 s
= p_strings
+ fdt32_to_cpu(GET_CELL(p
));
184 if (version
< 16 && sz
>= 8)
188 p
= PALIGN(p
+ sz
, 4);
190 Print(L
"%*s%a", depth
* shift
, L
" ", s
);
198 IsLinuxReservedRegion (
199 IN EFI_MEMORY_TYPE MemoryType
203 case EfiRuntimeServicesCode
:
204 case EfiRuntimeServicesData
:
205 case EfiUnusableMemory
:
206 case EfiACPIReclaimMemory
:
207 case EfiACPIMemoryNVS
:
208 case EfiReservedMemoryType
:
216 ** Relocate the FDT blob to a more appropriate location for the Linux kernel.
217 ** This function will allocate memory for the relocated FDT blob.
219 ** @retval EFI_SUCCESS on success.
220 ** @retval EFI_OUT_OF_RESOURCES or EFI_INVALID_PARAMETER on failure.
225 EFI_PHYSICAL_ADDRESS OriginalFdt
,
226 UINTN OriginalFdtSize
,
227 EFI_PHYSICAL_ADDRESS
*RelocatedFdt
,
228 UINTN
*RelocatedFdtSize
,
229 EFI_PHYSICAL_ADDRESS
*RelocatedFdtAlloc
236 *RelocatedFdtSize
= OriginalFdtSize
+ FDT_ADDITIONAL_ENTRIES_SIZE
;
238 // If FDT load address needs to be aligned, allocate more space.
239 FdtAlignment
= PcdGet32 (PcdArmLinuxFdtAlignment
);
240 if (FdtAlignment
!= 0) {
241 *RelocatedFdtSize
+= FdtAlignment
;
244 // Try below a watermark address.
245 Status
= EFI_NOT_FOUND
;
246 if (PcdGet32 (PcdArmLinuxFdtMaxOffset
) != 0) {
247 *RelocatedFdt
= LINUX_FDT_MAX_OFFSET
;
248 Status
= gBS
->AllocatePages (AllocateMaxAddress
, EfiBootServicesData
,
249 EFI_SIZE_TO_PAGES (*RelocatedFdtSize
), RelocatedFdt
);
250 if (EFI_ERROR (Status
)) {
251 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
));
255 // Try anywhere there is available space.
256 if (EFI_ERROR (Status
)) {
257 Status
= gBS
->AllocatePages (AllocateAnyPages
, EfiBootServicesData
,
258 EFI_SIZE_TO_PAGES (*RelocatedFdtSize
), RelocatedFdt
);
259 if (EFI_ERROR (Status
)) {
260 ASSERT_EFI_ERROR (Status
);
261 return EFI_OUT_OF_RESOURCES
;
263 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
));
267 *RelocatedFdtAlloc
= *RelocatedFdt
;
268 if (FdtAlignment
!= 0) {
269 *RelocatedFdt
= ALIGN (*RelocatedFdt
, FdtAlignment
);
272 // Load the Original FDT tree into the new region
273 Error
= fdt_open_into ((VOID
*)(UINTN
) OriginalFdt
,
274 (VOID
*)(UINTN
)(*RelocatedFdt
), *RelocatedFdtSize
);
276 DEBUG ((EFI_D_ERROR
, "fdt_open_into(): %a\n", fdt_strerror (Error
)));
277 gBS
->FreePages (*RelocatedFdtAlloc
, EFI_SIZE_TO_PAGES (*RelocatedFdtSize
));
278 return EFI_INVALID_PARAMETER
;
282 //DebugDumpFdt (fdt);
291 IN CONST CHAR8
* CommandLineArguments
,
292 IN EFI_PHYSICAL_ADDRESS InitrdImage
,
293 IN UINTN InitrdImageSize
,
294 IN OUT EFI_PHYSICAL_ADDRESS
*FdtBlobBase
,
295 IN OUT UINTN
*FdtBlobSize
299 EFI_PHYSICAL_ADDRESS NewFdtBlobBase
;
300 EFI_PHYSICAL_ADDRESS NewFdtBlobAllocation
;
301 UINTN NewFdtBlobSize
;
309 EFI_PHYSICAL_ADDRESS InitrdImageStart
;
310 EFI_PHYSICAL_ADDRESS InitrdImageEnd
;
314 LIST_ENTRY ResourceList
;
315 BDS_SYSTEM_MEMORY_RESOURCE
*Resource
;
316 ARM_PROCESSOR_TABLE
*ArmProcessorTable
;
317 ARM_CORE_INFO
*ArmCoreInfoTable
;
321 UINT64 CpuReleaseAddr
;
323 EFI_MEMORY_DESCRIPTOR
*MemoryMap
;
324 EFI_MEMORY_DESCRIPTOR
*MemoryMapPtr
;
326 UINTN DescriptorSize
;
327 UINT32 DescriptorVersion
;
329 UINTN OriginalFdtSize
;
330 BOOLEAN CpusNodeExist
;
333 NewFdtBlobAllocation
= 0;
336 // Sanity checks on the original FDT blob.
338 err
= fdt_check_header ((VOID
*)(UINTN
)(*FdtBlobBase
));
340 Print (L
"ERROR: Device Tree header not valid (err:%d)\n", err
);
341 return EFI_INVALID_PARAMETER
;
344 // The original FDT blob might have been loaded partially.
345 // Check that it is not the case.
346 OriginalFdtSize
= (UINTN
)fdt_totalsize ((VOID
*)(UINTN
)(*FdtBlobBase
));
347 if (OriginalFdtSize
> *FdtBlobSize
) {
348 Print (L
"ERROR: Incomplete FDT. Only %d/%d bytes have been loaded.\n",
349 *FdtBlobSize
, OriginalFdtSize
);
350 return EFI_INVALID_PARAMETER
;
354 // Relocate the FDT to its final location.
356 Status
= RelocateFdt (*FdtBlobBase
, OriginalFdtSize
,
357 &NewFdtBlobBase
, &NewFdtBlobSize
, &NewFdtBlobAllocation
);
358 if (EFI_ERROR (Status
)) {
359 goto FAIL_RELOCATE_FDT
;
362 fdt
= (VOID
*)(UINTN
)NewFdtBlobBase
;
364 node
= fdt_subnode_offset (fdt
, 0, "chosen");
366 // The 'chosen' node does not exist, create it
367 node
= fdt_add_subnode(fdt
, 0, "chosen");
369 DEBUG((EFI_D_ERROR
,"Error on finding 'chosen' node\n"));
370 Status
= EFI_INVALID_PARAMETER
;
371 goto FAIL_COMPLETE_FDT
;
376 BootArg
= fdt_getprop(fdt
, node
, "bootargs", &lenp
);
377 if (BootArg
!= NULL
) {
378 DEBUG((EFI_D_ERROR
,"BootArg: %a\n",BootArg
));
385 if ((CommandLineArguments
!= NULL
) && (AsciiStrLen (CommandLineArguments
) > 0)) {
386 err
= fdt_setprop(fdt
, node
, "bootargs", CommandLineArguments
, AsciiStrSize(CommandLineArguments
));
388 DEBUG((EFI_D_ERROR
,"Fail to set new 'bootarg' (err:%d)\n",err
));
395 if (InitrdImageSize
!= 0) {
396 InitrdImageStart
= cpu_to_fdt64 (InitrdImage
);
397 err
= fdt_setprop(fdt
, node
, "linux,initrd-start", &InitrdImageStart
, sizeof(EFI_PHYSICAL_ADDRESS
));
399 DEBUG((EFI_D_ERROR
,"Fail to set new 'linux,initrd-start' (err:%d)\n",err
));
401 InitrdImageEnd
= cpu_to_fdt64 (InitrdImage
+ InitrdImageSize
);
402 err
= fdt_setprop(fdt
, node
, "linux,initrd-end", &InitrdImageEnd
, sizeof(EFI_PHYSICAL_ADDRESS
));
404 DEBUG((EFI_D_ERROR
,"Fail to set new 'linux,initrd-start' (err:%d)\n",err
));
409 // Set Physical memory setup if does not exist
411 node
= fdt_subnode_offset(fdt
, 0, "memory");
413 // The 'memory' node does not exist, create it
414 node
= fdt_add_subnode(fdt
, 0, "memory");
416 fdt_setprop_string(fdt
, node
, "name", "memory");
417 fdt_setprop_string(fdt
, node
, "device_type", "memory");
419 GetSystemMemoryResources (&ResourceList
);
420 Resource
= (BDS_SYSTEM_MEMORY_RESOURCE
*)ResourceList
.ForwardLink
;
422 Region
.Base
= cpu_to_fdtn ((UINTN
)Resource
->PhysicalStart
);
423 Region
.Size
= cpu_to_fdtn ((UINTN
)Resource
->ResourceLength
);
425 err
= fdt_setprop(fdt
, node
, "reg", &Region
, sizeof(Region
));
427 DEBUG((EFI_D_ERROR
,"Fail to set new 'memory region' (err:%d)\n",err
));
433 // Add the memory regions reserved by the UEFI Firmware
436 // Retrieve the UEFI Memory Map
439 Status
= gBS
->GetMemoryMap (&MemoryMapSize
, MemoryMap
, &MapKey
, &DescriptorSize
, &DescriptorVersion
);
440 if (Status
== EFI_BUFFER_TOO_SMALL
) {
441 // The UEFI specification advises to allocate more memory for the MemoryMap buffer between successive
442 // calls to GetMemoryMap(), since allocation of the new buffer may potentially increase memory map size.
443 Pages
= EFI_SIZE_TO_PAGES (MemoryMapSize
) + 1;
444 MemoryMap
= AllocatePages (Pages
);
445 if (MemoryMap
== NULL
) {
446 Status
= EFI_OUT_OF_RESOURCES
;
447 goto FAIL_COMPLETE_FDT
;
449 Status
= gBS
->GetMemoryMap (&MemoryMapSize
, MemoryMap
, &MapKey
, &DescriptorSize
, &DescriptorVersion
);
452 // Go through the list and add the reserved region to the Device Tree
453 if (!EFI_ERROR(Status
)) {
454 MemoryMapPtr
= MemoryMap
;
455 for (Index
= 0; Index
< (MemoryMapSize
/ DescriptorSize
); Index
++) {
456 if (IsLinuxReservedRegion ((EFI_MEMORY_TYPE
)MemoryMapPtr
->Type
)) {
457 DEBUG((DEBUG_VERBOSE
, "Reserved region of type %d [0x%lX, 0x%lX]\n",
459 (UINTN
)MemoryMapPtr
->PhysicalStart
,
460 (UINTN
)(MemoryMapPtr
->PhysicalStart
+ MemoryMapPtr
->NumberOfPages
* EFI_PAGE_SIZE
)));
461 err
= fdt_add_mem_rsv(fdt
, MemoryMapPtr
->PhysicalStart
, MemoryMapPtr
->NumberOfPages
* EFI_PAGE_SIZE
);
463 Print(L
"Warning: Fail to add 'memreserve' (err:%d)\n", err
);
466 MemoryMapPtr
= (EFI_MEMORY_DESCRIPTOR
*)((UINTN
)MemoryMapPtr
+ DescriptorSize
);
471 // Setup Arm Mpcore Info if it is a multi-core or multi-cluster platforms.
473 // For 'cpus' and 'cpu' device tree nodes bindings, refer to this file
474 // in the kernel documentation:
475 // Documentation/devicetree/bindings/arm/cpus.txt
477 for (Index
=0; Index
< gST
->NumberOfTableEntries
; Index
++) {
478 // Check for correct GUID type
479 if (CompareGuid (&gArmMpCoreInfoGuid
, &(gST
->ConfigurationTable
[Index
].VendorGuid
))) {
480 MpId
= ArmReadMpidr ();
481 ClusterId
= GET_CLUSTER_ID(MpId
);
482 CoreId
= GET_CORE_ID(MpId
);
484 node
= fdt_subnode_offset(fdt
, 0, "cpus");
486 // Create the /cpus node
487 node
= fdt_add_subnode(fdt
, 0, "cpus");
488 fdt_setprop_string(fdt
, node
, "name", "cpus");
489 fdt_setprop_cell (fdt
, node
, "#address-cells", sizeof (UINTN
) / 4);
490 fdt_setprop_cell(fdt
, node
, "#size-cells", 0);
491 CpusNodeExist
= FALSE
;
493 CpusNodeExist
= TRUE
;
496 // Get pointer to ARM processor table
497 ArmProcessorTable
= (ARM_PROCESSOR_TABLE
*)gST
->ConfigurationTable
[Index
].VendorTable
;
498 ArmCoreInfoTable
= ArmProcessorTable
->ArmCpus
;
500 for (Index
= 0; Index
< ArmProcessorTable
->NumberOfEntries
; Index
++) {
501 CoreMpId
= (UINTN
) GET_MPID (ArmCoreInfoTable
[Index
].ClusterId
,
502 ArmCoreInfoTable
[Index
].CoreId
);
503 AsciiSPrint (Name
, 10, "cpu@%x", CoreMpId
);
505 // If the 'cpus' node did not exist then create all the 'cpu' nodes.
506 // In case 'cpus' node is provided in the original FDT then we do not add
508 if (!CpusNodeExist
) {
509 cpu_node
= fdt_add_subnode (fdt
, node
, Name
);
511 DEBUG ((EFI_D_ERROR
, "Error on creating '%s' node\n", Name
));
512 Status
= EFI_INVALID_PARAMETER
;
513 goto FAIL_COMPLETE_FDT
;
516 fdt_setprop_string (fdt
, cpu_node
, "device_type", "cpu");
518 CoreMpId
= cpu_to_fdtn (CoreMpId
);
519 fdt_setprop (fdt
, cpu_node
, "reg", &CoreMpId
, sizeof (CoreMpId
));
521 cpu_node
= fdt_subnode_offset(fdt
, node
, Name
);
525 Method
= fdt_getprop (fdt
, cpu_node
, "enable-method", &lenp
);
526 // We only care when 'enable-method' == 'spin-table'. If the enable-method is not defined
527 // or defined as 'psci' then we ignore its properties.
528 if ((Method
!= NULL
) && (AsciiStrCmp ((CHAR8
*)Method
, "spin-table") == 0)) {
529 // There are two cases;
530 // - UEFI firmware parked the secondary cores and/or UEFI firmware is aware of the CPU
531 // release addresses (PcdArmLinuxSpinTable == TRUE)
532 // - the parking of the secondary cores has been managed before starting UEFI and/or UEFI
533 // does not anything about the CPU release addresses - in this case we do nothing
534 if (FeaturePcdGet (PcdArmLinuxSpinTable
)) {
535 CpuReleaseAddr
= cpu_to_fdt64 (ArmCoreInfoTable
[Index
].MailboxSetAddress
);
536 fdt_setprop (fdt
, cpu_node
, "cpu-release-addr", &CpuReleaseAddr
, sizeof(CpuReleaseAddr
));
538 // If it is not the primary core than the cpu should be disabled
539 if (((ArmCoreInfoTable
[Index
].ClusterId
!= ClusterId
) || (ArmCoreInfoTable
[Index
].CoreId
!= CoreId
))) {
540 fdt_setprop_string(fdt
, cpu_node
, "status", "disabled");
551 //DebugDumpFdt (fdt);
554 // If we succeeded to generate the new Device Tree then free the old Device Tree
555 gBS
->FreePages (*FdtBlobBase
, EFI_SIZE_TO_PAGES (*FdtBlobSize
));
557 // Update the real size of the Device Tree
558 fdt_pack ((VOID
*)(UINTN
)(NewFdtBlobBase
));
560 *FdtBlobBase
= NewFdtBlobBase
;
561 *FdtBlobSize
= (UINTN
)fdt_totalsize ((VOID
*)(UINTN
)(NewFdtBlobBase
));
565 gBS
->FreePages (NewFdtBlobAllocation
, EFI_SIZE_TO_PAGES (NewFdtBlobSize
));
568 *FdtBlobSize
= (UINTN
)fdt_totalsize ((VOID
*)(UINTN
)(*FdtBlobBase
));
569 // Return success even if we failed to update the FDT blob.
570 // The original one is still valid.