2012-09-28 11:58:42 +02:00
/** @file
*
2013-03-12 02:01:55 +01:00
* Copyright ( c ) 2011 - 2013 , ARM Limited . All rights reserved .
2012-09-28 11:58:42 +02:00
*
2013-03-12 02:01:55 +01:00
* 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 .
2012-09-28 11:58:42 +02:00
*
* */
2013-03-12 01:56:37 +01:00
# include <Library/ArmSmcLib.h>
2012-09-28 11:58:42 +02:00
# include <Library/PcdLib.h>
# include <libfdt.h>
2013-03-12 01:56:37 +01:00
# include <IndustryStandard/ArmSmc.h>
2012-09-28 11:58:42 +02:00
# include "BdsInternal.h"
# include "BdsLinuxLoader.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)))
2013-12-10 17:39:54 +01:00
STATIC
2013-05-13 01:55:22 +02:00
UINTN
cpu_to_fdtn ( UINTN x ) {
if ( sizeof ( UINTN ) = = sizeof ( UINT32 ) ) {
return cpu_to_fdt32 ( x ) ;
} else {
return cpu_to_fdt64 ( x ) ;
}
}
2013-05-13 01:54:16 +02:00
typedef struct {
UINTN Base ;
UINTN Size ;
} FdtRegion ;
2012-09-28 11:58:42 +02:00
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 ;
2013-03-12 01:54:58 +01:00
{
// Can 'memreserve' be printed by below code?
INTN num = fdt_num_mem_rsv ( FdtBlob ) ;
INTN i , err ;
UINT64 addr = 0 , size = 0 ;
for ( i = 0 ; i < num ; i + + ) {
err = fdt_get_mem_rsv ( FdtBlob , i , & addr , & size ) ;
if ( err ) {
DEBUG ( ( EFI_D_ERROR , " Error (%d) : Cannot get memreserve section (%d) \n " , err , i ) ) ;
}
else {
Print ( L " /memreserve/ \t 0x%lx \t 0x%lx; \n " , addr , size ) ;
}
}
}
2012-09-28 11:58:42 +02:00
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 ) {
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 " ) ;
}
}
2013-03-12 01:54:58 +01:00
STATIC
BOOLEAN
IsLinuxReservedRegion (
IN EFI_MEMORY_TYPE MemoryType
)
{
switch ( MemoryType ) {
case EfiRuntimeServicesCode :
case EfiRuntimeServicesData :
case EfiUnusableMemory :
case EfiACPIReclaimMemory :
case EfiACPIMemoryNVS :
2014-02-24 17:27:48 +01:00
case EfiReservedMemoryType :
2013-03-12 01:54:58 +01:00
return TRUE ;
default :
return FALSE ;
}
}
2013-05-13 01:54:16 +02:00
STATIC
BOOLEAN
IsPsciSmcSupported (
VOID
)
{
BOOLEAN PsciSmcSupported ;
UINTN Rx ;
PsciSmcSupported = FALSE ;
// Check the SMC response to the Presence SMC
Rx = ARM_SMC_ID_PRESENCE ;
ArmCallSmc ( & Rx ) ;
if ( Rx = = 1 ) {
// Check the SMC UID
Rx = ARM_SMC_ID_UID ;
ArmCallSmc ( & Rx ) ;
if ( Rx = = ARM_TRUSTZONE_UID_4LETTERID ) {
Rx = ARM_SMC_ID_UID + 1 ;
ArmCallSmc ( & Rx ) ;
if ( Rx = = ARM_TRUSTZONE_ARM_UID ) {
PsciSmcSupported = TRUE ;
}
}
}
return PsciSmcSupported ;
}
/**
* * 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 OriginalFdt ,
UINTN OriginalFdtSize ,
EFI_PHYSICAL_ADDRESS * RelocatedFdt ,
UINTN * RelocatedFdtSize ,
EFI_PHYSICAL_ADDRESS * RelocatedFdtAlloc
)
{
EFI_STATUS Status ;
INTN Error ;
2013-11-28 22:40:48 +01:00
UINT64 FdtAlignment ;
2013-05-13 01:54:16 +02:00
* 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. \n WARNING: 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 ;
}
DEBUG_CODE_BEGIN ( ) ;
//DebugDumpFdt (fdt);
DEBUG_CODE_END ( ) ;
return EFI_SUCCESS ;
}
2012-09-28 11:58:42 +02:00
EFI_STATUS
PrepareFdt (
IN CONST CHAR8 * CommandLineArguments ,
IN EFI_PHYSICAL_ADDRESS InitrdImage ,
IN UINTN InitrdImageSize ,
IN OUT EFI_PHYSICAL_ADDRESS * FdtBlobBase ,
2013-03-12 01:50:46 +01:00
IN OUT UINTN * FdtBlobSize
2012-09-28 11:58:42 +02:00
)
{
EFI_STATUS Status ;
EFI_PHYSICAL_ADDRESS NewFdtBlobBase ;
2013-04-14 11:36:41 +02:00
EFI_PHYSICAL_ADDRESS NewFdtBlobAllocation ;
2012-09-28 11:58:42 +02:00
UINTN NewFdtBlobSize ;
VOID * fdt ;
INTN err ;
INTN node ;
INTN cpu_node ;
2013-07-18 20:07:46 +02:00
INT32 lenp ;
2012-09-28 11:58:42 +02:00
CONST VOID * BootArg ;
2013-03-12 01:56:37 +01:00
CONST VOID * Method ;
2012-09-28 11:58:42 +02:00
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 ;
2013-03-12 01:54:58 +01:00
UINTN MemoryMapSize ;
EFI_MEMORY_DESCRIPTOR * MemoryMap ;
2013-06-27 20:18:24 +02:00
EFI_MEMORY_DESCRIPTOR * MemoryMapPtr ;
2013-03-12 01:54:58 +01:00
UINTN MapKey ;
UINTN DescriptorSize ;
UINT32 DescriptorVersion ;
UINTN Pages ;
2013-03-12 01:56:37 +01:00
BOOLEAN PsciSmcSupported ;
2013-03-12 02:01:55 +01:00
UINTN OriginalFdtSize ;
2013-04-14 11:33:35 +02:00
BOOLEAN CpusNodeExist ;
2013-05-13 01:56:35 +02:00
UINTN CoreMpId ;
UINTN Smc ;
2013-03-12 01:56:37 +01:00
2013-05-13 01:54:16 +02:00
NewFdtBlobAllocation = 0 ;
2012-09-28 11:58:42 +02:00
2013-03-12 02:01:55 +01:00
//
// Sanity checks on the original FDT blob.
//
2012-09-28 11:58:42 +02:00
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 ;
}
2013-03-12 02:01:55 +01:00
// 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 ;
}
2012-09-28 11:58:42 +02:00
//
2013-05-13 01:54:16 +02:00
// Relocate the FDT to its final location.
2012-09-28 11:58:42 +02:00
//
2013-05-13 01:54:16 +02:00
Status = RelocateFdt ( * FdtBlobBase , OriginalFdtSize ,
& NewFdtBlobBase , & NewFdtBlobSize , & NewFdtBlobAllocation ) ;
if ( EFI_ERROR ( Status ) ) {
goto FAIL_RELOCATE_FDT ;
2013-04-14 11:36:41 +02:00
}
2013-05-13 01:54:16 +02:00
//
// Ensure the Power State Coordination Interface (PSCI) SMCs are there if supported
//
PsciSmcSupported = FALSE ;
if ( FeaturePcdGet ( PcdArmPsciSupport ) = = TRUE ) {
PsciSmcSupported = IsPsciSmcSupported ( ) ;
if ( PsciSmcSupported = = FALSE ) {
DEBUG ( ( EFI_D_ERROR , " Warning: The Power State Coordination Interface (PSCI) is not supported by your platform Trusted Firmware. \n " ) ) ;
2012-09-28 11:58:42 +02:00
}
}
fdt = ( VOID * ) ( UINTN ) NewFdtBlobBase ;
2013-05-13 01:54:16 +02:00
node = fdt_subnode_offset ( fdt , 0 , " chosen " ) ;
2012-09-28 11:58:42 +02:00
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 ;
2013-05-13 01:54:16 +02:00
goto FAIL_COMPLETE_FDT ;
2012-09-28 11:58:42 +02:00
}
}
DEBUG_CODE_BEGIN ( ) ;
BootArg = fdt_getprop ( fdt , node , " bootargs " , & lenp ) ;
if ( BootArg ! = NULL ) {
DEBUG ( ( EFI_D_ERROR , " BootArg: %a \n " , BootArg ) ) ;
}
DEBUG_CODE_END ( ) ;
2013-03-12 01:54:58 +01:00
//
2012-09-28 11:58:42 +02:00
// Set Linux CmdLine
2013-03-12 01:54:58 +01:00
//
2012-09-28 11:58:42 +02:00
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 ) ) ;
}
}
2013-03-12 01:54:58 +01:00
//
2012-09-28 11:58:42 +02:00
// Set Linux Initrd
2013-03-12 01:54:58 +01:00
//
2012-09-28 11:58:42 +02:00
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 ) ) ;
}
}
2013-03-12 01:54:58 +01:00
//
2012-09-28 11:58:42 +02:00
// Set Physical memory setup if does not exist
2013-03-12 01:54:58 +01:00
//
2012-09-28 11:58:42 +02:00
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 " ) ;
2013-03-12 02:01:55 +01:00
2012-09-28 11:58:42 +02:00
GetSystemMemoryResources ( & ResourceList ) ;
Resource = ( BDS_SYSTEM_MEMORY_RESOURCE * ) ResourceList . ForwardLink ;
2013-03-12 02:01:55 +01:00
2013-05-13 01:55:22 +02:00
Region . Base = cpu_to_fdtn ( ( UINTN ) Resource - > PhysicalStart ) ;
Region . Size = cpu_to_fdtn ( ( UINTN ) Resource - > ResourceLength ) ;
2012-09-28 11:58:42 +02:00
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 ) ) ;
}
}
}
2013-03-12 01:54:58 +01:00
//
// 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 ) {
2013-06-27 20:18:24 +02:00
// 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.
2013-03-12 01:54:58 +01:00
Pages = EFI_SIZE_TO_PAGES ( MemoryMapSize ) + 1 ;
MemoryMap = AllocatePages ( Pages ) ;
2013-06-27 20:18:24 +02:00
if ( MemoryMap = = NULL ) {
Status = EFI_OUT_OF_RESOURCES ;
goto FAIL_COMPLETE_FDT ;
}
2013-03-12 01:54:58 +01:00
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 ) ) {
2013-06-27 20:18:24 +02:00
MemoryMapPtr = MemoryMap ;
for ( Index = 0 ; Index < ( MemoryMapSize / DescriptorSize ) ; Index + + ) {
if ( IsLinuxReservedRegion ( ( EFI_MEMORY_TYPE ) MemoryMapPtr - > Type ) ) {
2014-02-24 17:27:48 +01:00
DEBUG ( ( DEBUG_VERBOSE , " Reserved region of type %d [0x%lX, 0x%lX] \n " ,
2013-06-27 20:18:24 +02:00
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 ) ;
2013-03-12 01:54:58 +01:00
if ( err ! = 0 ) {
Print ( L " Warning: Fail to add 'memreserve' (err:%d) \n " , err ) ;
}
}
2013-06-27 20:18:24 +02:00
MemoryMapPtr = ( EFI_MEMORY_DESCRIPTOR * ) ( ( UINTN ) MemoryMapPtr + DescriptorSize ) ;
2013-03-12 01:54:58 +01:00
}
}
//
2013-05-13 01:56:35 +02:00
// 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
2013-03-12 01:54:58 +01:00
//
2012-09-28 11:58:42 +02:00
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 " ) ;
2013-05-13 01:56:35 +02:00
fdt_setprop_cell ( fdt , node , " #address-cells " , sizeof ( UINTN ) / 4 ) ;
2012-09-28 11:58:42 +02:00
fdt_setprop_cell ( fdt , node , " #size-cells " , 0 ) ;
2013-04-14 11:33:35 +02:00
CpusNodeExist = FALSE ;
} else {
CpusNodeExist = TRUE ;
2012-09-28 11:58:42 +02:00
}
// Get pointer to ARM processor table
ArmProcessorTable = ( ARM_PROCESSOR_TABLE * ) gST - > ConfigurationTable [ Index ] . VendorTable ;
ArmCoreInfoTable = ArmProcessorTable - > ArmCpus ;
for ( Index = 0 ; Index < ArmProcessorTable - > NumberOfEntries ; Index + + ) {
2013-06-19 20:09:19 +02:00
CoreMpId = ( UINTN ) GET_MPID ( ArmCoreInfoTable [ Index ] . ClusterId ,
ArmCoreInfoTable [ Index ] . CoreId ) ;
AsciiSPrint ( Name , 10 , " cpu@%x " , CoreMpId ) ;
2013-04-14 11:33:35 +02:00
2013-05-13 01:56:35 +02:00
// 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.
2013-04-14 11:33:35 +02:00
if ( ! CpusNodeExist ) {
2013-05-13 01:56:35 +02:00
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 " ) ;
2013-06-19 20:09:19 +02:00
2013-05-13 01:56:35 +02:00
CoreMpId = cpu_to_fdtn ( CoreMpId ) ;
fdt_setprop ( fdt , cpu_node , " reg " , & CoreMpId , sizeof ( CoreMpId ) ) ;
if ( PsciSmcSupported ) {
fdt_setprop_string ( fdt , cpu_node , " enable-method " , " psci " ) ;
}
2013-04-14 11:33:35 +02:00
} else {
cpu_node = fdt_subnode_offset ( fdt , node , Name ) ;
2012-09-28 11:58:42 +02:00
}
2013-03-12 01:56:37 +01:00
// If Power State Coordination Interface (PSCI) is not supported then it is expected the secondary
2013-03-12 02:01:11 +01:00
// cores are spinning waiting for the Operating System to release them
2013-04-14 11:33:35 +02:00
if ( ( PsciSmcSupported = = FALSE ) & & ( cpu_node > = 0 ) ) {
2013-03-12 02:01:11 +01:00
// We as the bootloader are responsible for either creating or updating
// these entries. Do not trust the entries in the DT. We only know about
// 'spin-table' type. Do not try to update other types if defined.
2013-03-12 01:56:37 +01:00
Method = fdt_getprop ( fdt , cpu_node , " enable-method " , & lenp ) ;
2013-03-12 02:01:11 +01:00
if ( ( Method = = NULL ) | | ( ! AsciiStrCmp ( ( CHAR8 * ) Method , " spin-table " ) ) ) {
2013-03-12 01:56:37 +01:00
fdt_setprop_string ( fdt , cpu_node , " enable-method " , " spin-table " ) ;
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 " ) ;
}
2013-03-12 02:01:11 +01:00
} else {
Print ( L " Warning: Unsupported enable-method type for CPU[%d] : %a \n " , Index , ( CHAR8 * ) Method ) ;
2013-03-12 01:56:37 +01:00
}
2012-09-28 11:58:42 +02:00
}
}
break ;
}
}
2013-03-12 01:56:37 +01:00
// If the Power State Coordination Interface is supported then we signal it in the Device Tree
if ( PsciSmcSupported = = TRUE ) {
// Before to create it we check if the node is not already defined in the Device Tree
node = fdt_subnode_offset ( fdt , 0 , " psci " ) ;
if ( node < 0 ) {
// The 'psci' node does not exist, create it
node = fdt_add_subnode ( fdt , 0 , " psci " ) ;
if ( node < 0 ) {
DEBUG ( ( EFI_D_ERROR , " Error on creating 'psci' node \n " ) ) ;
Status = EFI_INVALID_PARAMETER ;
2013-05-13 01:54:16 +02:00
goto FAIL_COMPLETE_FDT ;
2013-03-12 01:56:37 +01:00
} else {
2013-05-13 01:56:35 +02:00
fdt_setprop_string ( fdt , node , " compatible " , " arm,psci " ) ;
fdt_setprop_string ( fdt , node , " method " , " smc " ) ;
Smc = cpu_to_fdtn ( ARM_SMC_ARM_CPU_SUSPEND ) ;
fdt_setprop ( fdt , node , " cpu_suspend " , & Smc , sizeof ( Smc ) ) ;
Smc = cpu_to_fdtn ( ARM_SMC_ARM_CPU_OFF ) ;
fdt_setprop ( fdt , node , " cpu_off " , & Smc , sizeof ( Smc ) ) ;
Smc = cpu_to_fdtn ( ARM_SMC_ARM_CPU_ON ) ;
fdt_setprop ( fdt , node , " cpu_on " , & Smc , sizeof ( Smc ) ) ;
Smc = cpu_to_fdtn ( ARM_SMC_ARM_MIGRATE ) ;
fdt_setprop ( fdt , node , " migrate " , & Smc , sizeof ( Smc ) ) ;
2013-03-12 01:56:37 +01:00
}
}
}
2012-09-28 11:58:42 +02:00
DEBUG_CODE_BEGIN ( ) ;
//DebugDumpFdt (fdt);
DEBUG_CODE_END ( ) ;
2013-03-12 02:02:44 +01:00
// If we succeeded to generate the new Device Tree then free the old Device Tree
gBS - > FreePages ( * FdtBlobBase , EFI_SIZE_TO_PAGES ( * FdtBlobSize ) ) ;
2012-09-28 11:58:42 +02:00
* FdtBlobBase = NewFdtBlobBase ;
* FdtBlobSize = ( UINTN ) fdt_totalsize ( ( VOID * ) ( UINTN ) ( NewFdtBlobBase ) ) ;
return EFI_SUCCESS ;
2013-05-13 01:54:16 +02:00
FAIL_COMPLETE_FDT :
2013-04-14 11:36:41 +02:00
gBS - > FreePages ( NewFdtBlobAllocation , EFI_SIZE_TO_PAGES ( NewFdtBlobSize ) ) ;
2013-03-12 02:02:44 +01:00
2013-05-13 01:54:16 +02:00
FAIL_RELOCATE_FDT :
2013-03-12 02:02:44 +01:00
* FdtBlobSize = ( UINTN ) fdt_totalsize ( ( VOID * ) ( UINTN ) ( * FdtBlobBase ) ) ;
2013-05-13 01:54:16 +02:00
// Return success even if we failed to update the FDT blob.
// The original one is still valid.
2012-09-28 11:58:42 +02:00
return EFI_SUCCESS ;
}