Home | History | Annotate | Download | only in FdtDxe
      1 /** @file
      2 *
      3 *  Copyright (c) 2011-2014, ARM Limited. All rights reserved.<BR>
      4 *  Copyright (c) 2014 - 2016, AMD Inc. All rights reserved.<BR>
      5 *
      6 *  This program and the accompanying materials
      7 *  are licensed and made available under the terms and conditions of the BSD License
      8 *  which accompanies this distribution.  The full text of the license may be found at
      9 *  http://opensource.org/licenses/bsd-license.php
     10 *
     11 *  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     12 *  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     13 *
     14 **/
     15 /**
     16 
     17   Derived from:
     18    ArmPkg/Library/BdsLib/BdsLinuxFdt.c
     19 
     20 **/
     21 
     22 #include <Library/PcdLib.h>
     23 #include <libfdt.h>
     24 
     25 #include <Library/BdsLib/BdsInternal.h>
     26 
     27 #include <Guid/ArmMpCoreInfo.h>
     28 #include <Protocol/AmdMpCoreInfo.h>
     29 
     30 #define LINUX_FDT_MAX_OFFSET      (PcdGet64 (PcdSystemMemoryBase) + PcdGet32(PcdArmLinuxFdtMaxOffset))
     31 
     32 
     33 // Additional size that could be used for FDT entries added by the UEFI OS Loader
     34 // Estimation based on: EDID (300bytes) + bootargs (200bytes) + initrd region (20bytes)
     35 //                      + system memory region (20bytes) + mp_core entries (200 bytes)
     36 #define FDT_ADDITIONAL_ENTRIES_SIZE     0x300
     37 
     38 
     39 EFI_STATUS
     40 GetSystemMemoryResources (
     41   IN  LIST_ENTRY *ResourceList
     42   );
     43 
     44 VOID
     45 DebugDumpFdt (
     46   IN VOID*                FdtBlob
     47   );
     48 
     49 #define ALIGN(x, a)     (((x) + ((a) - 1)) & ~((a) - 1))
     50 #define PALIGN(p, a)    ((void *)(ALIGN((unsigned long)(p), (a))))
     51 #define GET_CELL(p)     (p += 4, *((const UINT32 *)(p-4)))
     52 
     53 //
     54 // PMU interrupts per core
     55 //
     56 #pragma pack(push, 1)
     57 typedef struct {
     58   UINT32 Flag;          // 0 == SPI
     59   UINT32 IntId;         // GSIV == IntId+32
     60   UINT32 Type;          // 4 == Level-Sensitive, Active-High
     61 } PMU_INTERRUPT;
     62 #pragma pack(pop)
     63 
     64 #define PMU_INT_FLAG_SPI        0
     65 #define PMU_INT_TYPE_HIGH_LEVEL 4
     66 
     67 
     68 typedef struct {
     69   UINTN   Base;
     70   UINTN   Size;
     71 } FdtRegion;
     72 
     73 
     74 STATIC
     75 UINTN
     76 cpu_to_fdtn (UINTN x) {
     77   if (sizeof (UINTN) == sizeof (UINT32)) {
     78     return cpu_to_fdt32 (x);
     79   } else {
     80     return cpu_to_fdt64 (x);
     81   }
     82 }
     83 
     84 
     85 STATIC
     86 BOOLEAN
     87 ClusterInRange(
     88   IN ARM_CORE_INFO *ArmCoreInfoTable,
     89   IN UINTN         ClusterId,
     90   IN UINTN         LowIndex,
     91   IN UINTN         HighIndex
     92   )
     93 {
     94   do {
     95     if (ClusterId == ArmCoreInfoTable[LowIndex].ClusterId)
     96       return TRUE;
     97   } while (++LowIndex <= HighIndex);
     98 
     99   return FALSE;
    100 }
    101 
    102 
    103 STATIC
    104 UINTN
    105 NumberOfCoresInCluster(
    106   IN ARM_CORE_INFO *ArmCoreInfoTable,
    107   IN UINTN         NumberOfEntries,
    108   IN UINTN         ClusterId
    109   )
    110 {
    111   UINTN Index, Cores;
    112 
    113   Cores = 0;
    114   for (Index = 0; Index < NumberOfEntries; ++Index) {
    115     if (ClusterId == ArmCoreInfoTable[Index].ClusterId)
    116       ++Cores;
    117   }
    118 
    119   return Cores;
    120 }
    121 
    122 
    123 STATIC
    124 UINTN
    125 NumberOfClustersInTable(
    126   IN ARM_CORE_INFO *ArmCoreInfoTable,
    127   IN UINTN         NumberOfEntries
    128   )
    129 {
    130   UINTN Index, Cores, Clusters, ClusterId;
    131 
    132   Index = 0;
    133   Clusters = 0;
    134   Cores = NumberOfEntries;
    135   while (Cores) {
    136      ++Clusters;
    137      ClusterId = ArmCoreInfoTable[Index].ClusterId;
    138      Cores -= NumberOfCoresInCluster (ArmCoreInfoTable,
    139                                       NumberOfEntries,
    140                                       ClusterId);
    141      if (Cores) {
    142        do {
    143          ++Index;
    144        } while (ClusterInRange (ArmCoreInfoTable,
    145                                 ArmCoreInfoTable[Index].ClusterId,
    146                                 0, Index-1));
    147      }
    148   }
    149 
    150   return Clusters;
    151 }
    152 
    153 
    154 STATIC
    155 int
    156 fdt_alloc_phandle(
    157   IN VOID *blob
    158   )
    159 {
    160 
    161   int offset, phandle = 0;
    162 
    163   for (offset = fdt_next_node(blob, -1, NULL); offset >= 0;
    164        offset = fdt_next_node(blob, offset, NULL)) {
    165        phandle = MAX(phandle, fdt_get_phandle(blob, offset));
    166   }
    167 
    168   return phandle + 1;
    169 }
    170 
    171 
    172 STATIC
    173 BOOLEAN
    174 IsLinuxReservedRegion (
    175   IN EFI_MEMORY_TYPE MemoryType
    176   )
    177 {
    178   switch(MemoryType) {
    179   case EfiRuntimeServicesCode:
    180   case EfiRuntimeServicesData:
    181   case EfiUnusableMemory:
    182   case EfiACPIReclaimMemory:
    183   case EfiACPIMemoryNVS:
    184   case EfiReservedMemoryType:
    185     return TRUE;
    186   default:
    187     return FALSE;
    188   }
    189 }
    190 
    191 STATIC
    192 VOID
    193 SetDeviceStatus (
    194   IN VOID *fdt,
    195   IN CHAR8 *device,
    196   IN BOOLEAN enable
    197   )
    198 {
    199   int node, subnode, rc;
    200 
    201   node = fdt_subnode_offset (fdt, 0, "smb");
    202   if (node >= 0) {
    203     subnode = fdt_subnode_offset (fdt, node, device);
    204     if (subnode >= 0) {
    205       rc = fdt_setprop_string(fdt, subnode, "status", enable ? "ok" : "disabled");
    206       if (rc) {
    207         DEBUG((EFI_D_ERROR,"%a: Could not set 'status' property for '%a' node\n",
    208             __FUNCTION__, device));
    209       }
    210     }
    211   }
    212 }
    213 
    214 #if DO_XGBE
    215 STATIC
    216 VOID
    217 SetMacAddress (
    218   IN VOID *fdt,
    219   IN CHAR8 *device,
    220   IN UINT64 mac_addr
    221   )
    222 {
    223   int node, subnode, rc;
    224 
    225   node = fdt_subnode_offset (fdt, 0, "smb");
    226   if (node >= 0) {
    227     subnode = fdt_subnode_offset (fdt, node, device);
    228     if (subnode >= 0) {
    229       rc = fdt_setprop(fdt, subnode, "mac-address", (void *)&mac_addr, 6);
    230       if (rc) {
    231         DEBUG((EFI_D_ERROR,"%a: Could not set 'mac-address' property for '%a' node\n",
    232             __FUNCTION__, device));
    233       }
    234     }
    235   }
    236 }
    237 #endif
    238 
    239 VOID
    240 SetSocIdStatus (
    241   IN VOID *fdt
    242   )
    243 {
    244   UINT32                SocId;
    245   BOOLEAN               IsRevB1;
    246 
    247   SocId = PcdGet32 (PcdSocCpuId);
    248   IsRevB1 = (SocId & 0xFF0) && (SocId & 0x00F);
    249 
    250   SetDeviceStatus (fdt, "sata@e0d00000",
    251     IsRevB1 && FixedPcdGet8(PcdSata1PortCount) > 0);
    252   SetDeviceStatus (fdt, "gpio@e0020000", IsRevB1);
    253   SetDeviceStatus (fdt, "gpio@e0030000", IsRevB1);
    254   SetDeviceStatus (fdt, "gwdt@e0bb0000", IsRevB1);
    255 #if DO_KCS
    256   SetDeviceStatus (fdt, "kcs@e0010000", IsRevB1);
    257 #else
    258   SetDeviceStatus (fdt, "kcs@e0010000", FALSE);
    259 #endif
    260 }
    261 
    262 VOID
    263 SetXgbeStatus (
    264   IN VOID *fdt
    265   )
    266 {
    267 #if DO_XGBE
    268   SetDeviceStatus (fdt, "xgmac@e0700000", TRUE);
    269   SetDeviceStatus (fdt, "phy@e1240800", TRUE);
    270   SetDeviceStatus (fdt, "xgmac@e0900000", TRUE);
    271   SetDeviceStatus (fdt, "phy@e1240c00", TRUE);
    272 
    273   SetMacAddress (fdt, "xgmac@e0700000", PcdGet64 (PcdEthMacA));
    274   SetMacAddress (fdt, "xgmac@e0900000", PcdGet64 (PcdEthMacB));
    275 #else
    276   SetDeviceStatus (fdt, "xgmac@e0700000", FALSE);
    277   SetDeviceStatus (fdt, "phy@e1240800", FALSE);
    278   SetDeviceStatus (fdt, "xgmac@e0900000", FALSE);
    279   SetDeviceStatus (fdt, "phy@e1240c00", FALSE);
    280 #endif
    281 }
    282 
    283 
    284 /**
    285 ** Relocate the FDT blob to a more appropriate location for the Linux kernel.
    286 ** This function will allocate memory for the relocated FDT blob.
    287 **
    288 ** @retval EFI_SUCCESS on success.
    289 ** @retval EFI_OUT_OF_RESOURCES or EFI_INVALID_PARAMETER on failure.
    290 */
    291 STATIC
    292 EFI_STATUS
    293 RelocateFdt (
    294   EFI_PHYSICAL_ADDRESS   OriginalFdt,
    295   UINTN                  OriginalFdtSize,
    296   EFI_PHYSICAL_ADDRESS   *RelocatedFdt,
    297   UINTN                  *RelocatedFdtSize,
    298   EFI_PHYSICAL_ADDRESS   *RelocatedFdtAlloc
    299   )
    300 {
    301   EFI_STATUS            Status;
    302   INTN                  Error;
    303   UINT64                FdtAlignment;
    304 
    305   *RelocatedFdtSize = OriginalFdtSize + FDT_ADDITIONAL_ENTRIES_SIZE;
    306 
    307   // If FDT load address needs to be aligned, allocate more space.
    308   FdtAlignment = PcdGet32 (PcdArmLinuxFdtAlignment);
    309   if (FdtAlignment != 0) {
    310     *RelocatedFdtSize += FdtAlignment;
    311   }
    312 
    313   // Try below a watermark address.
    314   Status = EFI_NOT_FOUND;
    315   if (PcdGet32 (PcdArmLinuxFdtMaxOffset) != 0) {
    316     *RelocatedFdt = LINUX_FDT_MAX_OFFSET;
    317     Status = gBS->AllocatePages (AllocateMaxAddress, EfiBootServicesData,
    318                     EFI_SIZE_TO_PAGES (*RelocatedFdtSize), RelocatedFdt);
    319     if (EFI_ERROR (Status)) {
    320       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));
    321     }
    322   }
    323 
    324   // Try anywhere there is available space.
    325   if (EFI_ERROR (Status)) {
    326     Status = gBS->AllocatePages (AllocateAnyPages, EfiBootServicesData,
    327                     EFI_SIZE_TO_PAGES (*RelocatedFdtSize), RelocatedFdt);
    328     if (EFI_ERROR (Status)) {
    329       ASSERT_EFI_ERROR (Status);
    330       return EFI_OUT_OF_RESOURCES;
    331     } else {
    332       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));
    333     }
    334   }
    335 
    336   *RelocatedFdtAlloc = *RelocatedFdt;
    337   if (FdtAlignment != 0) {
    338     *RelocatedFdt = ALIGN (*RelocatedFdt, FdtAlignment);
    339   }
    340 
    341   // Load the Original FDT tree into the new region
    342   Error = fdt_open_into ((VOID*)(UINTN) OriginalFdt,
    343             (VOID*)(UINTN)(*RelocatedFdt), *RelocatedFdtSize);
    344   if (Error) {
    345     DEBUG ((EFI_D_ERROR, "fdt_open_into(): %a\n", fdt_strerror (Error)));
    346     gBS->FreePages (*RelocatedFdtAlloc, EFI_SIZE_TO_PAGES (*RelocatedFdtSize));
    347     return EFI_INVALID_PARAMETER;
    348   }
    349 
    350   DEBUG_CODE_BEGIN();
    351     // DebugDumpFdt ((VOID*)(UINTN)(*RelocatedFdt));
    352   DEBUG_CODE_END();
    353 
    354   return EFI_SUCCESS;
    355 }
    356 
    357 
    358 EFI_STATUS
    359 AmdStyxPrepareFdt (
    360   IN     CONST CHAR8*         CommandLineArguments,
    361   IN     EFI_PHYSICAL_ADDRESS InitrdImage,
    362   IN     UINTN                InitrdImageSize,
    363   IN OUT EFI_PHYSICAL_ADDRESS *FdtBlobBase,
    364   IN OUT UINTN                *FdtBlobSize
    365   )
    366 {
    367   EFI_STATUS            Status;
    368   EFI_PHYSICAL_ADDRESS  NewFdtBlobBase;
    369   EFI_PHYSICAL_ADDRESS  NewFdtBlobAllocation;
    370   UINTN                 NewFdtBlobSize;
    371   VOID                 *fdt;
    372   int                   err;
    373   int                   node;
    374   int                   cpu_node;
    375   int                   lenp;
    376   CONST VOID           *BootArg;
    377   EFI_PHYSICAL_ADDRESS  InitrdImageStart;
    378   EFI_PHYSICAL_ADDRESS  InitrdImageEnd;
    379   FdtRegion             Region;
    380   UINTN                 Index;
    381   CHAR8                 Name[10];
    382   LIST_ENTRY            ResourceList;
    383   BDS_SYSTEM_MEMORY_RESOURCE  *Resource;
    384   ARM_CORE_INFO         *ArmCoreInfoTable;
    385   UINTN                 ArmCoreCount;
    386   UINT32                PrimaryClusterId;
    387   UINT32                PrimaryCoreId;
    388   UINTN                 MemoryMapSize;
    389   EFI_MEMORY_DESCRIPTOR *MemoryMap;
    390   EFI_MEMORY_DESCRIPTOR *MemoryMapPtr;
    391   UINTN                 MapKey;
    392   UINTN                 DescriptorSize;
    393   UINT32                DescriptorVersion;
    394   UINTN                 Pages;
    395   UINTN                 OriginalFdtSize;
    396   int                   map_node;
    397   int                   cluster_node;
    398   int                   pmu_node;
    399   PMU_INTERRUPT         PmuInt;
    400   int                   phandle[NUM_CORES];
    401   UINT32                ClusterIndex, CoreIndex;
    402   UINT32                ClusterCount, CoresInCluster;
    403   UINT32                ClusterId;
    404   UINTN                 MpId, MbAddr;
    405   AMD_MP_CORE_INFO_PROTOCOL *AmdMpCoreInfoProtocol;
    406 
    407   //
    408   // Sanity checks on the original FDT blob.
    409   //
    410   err = fdt_check_header ((VOID*)(UINTN)(*FdtBlobBase));
    411   if (err != 0) {
    412     Print (L"ERROR: Device Tree header not valid (err:%d)\n", err);
    413     return EFI_INVALID_PARAMETER;
    414   }
    415 
    416   // The original FDT blob might have been loaded partially.
    417   // Check that it is not the case.
    418   OriginalFdtSize = (UINTN)fdt_totalsize ((VOID*)(UINTN)(*FdtBlobBase));
    419   if (OriginalFdtSize > *FdtBlobSize) {
    420     Print (L"ERROR: Incomplete FDT. Only %d/%d bytes have been loaded.\n",
    421            *FdtBlobSize, OriginalFdtSize);
    422     return EFI_INVALID_PARAMETER;
    423   }
    424 
    425   //
    426   // Relocate the FDT to its final location.
    427   //
    428   NewFdtBlobAllocation = 0;
    429   Status = RelocateFdt (*FdtBlobBase, OriginalFdtSize,
    430              &NewFdtBlobBase, &NewFdtBlobSize, &NewFdtBlobAllocation);
    431   if (EFI_ERROR (Status)) {
    432     goto FAIL_RELOCATE_FDT;
    433   }
    434   fdt = (VOID*)(UINTN)NewFdtBlobBase;
    435 
    436   node = fdt_subnode_offset (fdt, 0, "chosen");
    437   if (node < 0) {
    438     // The 'chosen' node does not exist, create it
    439     node = fdt_add_subnode(fdt, 0, "chosen");
    440     if (node < 0) {
    441       DEBUG((EFI_D_ERROR,"Error on finding 'chosen' node\n"));
    442       Status = EFI_INVALID_PARAMETER;
    443       goto FAIL_COMPLETE_FDT;
    444     }
    445   }
    446 
    447   DEBUG_CODE_BEGIN();
    448     BootArg = fdt_getprop(fdt, node, "bootargs", &lenp);
    449     if (BootArg != NULL) {
    450       DEBUG((EFI_D_ERROR,"BootArg: %a\n",BootArg));
    451     }
    452   DEBUG_CODE_END();
    453 
    454   //
    455   // Set Linux CmdLine
    456   //
    457   if ((CommandLineArguments != NULL) && (AsciiStrLen (CommandLineArguments) > 0)) {
    458     err = fdt_setprop(fdt, node, "bootargs", CommandLineArguments, AsciiStrSize(CommandLineArguments));
    459     if (err) {
    460       DEBUG((EFI_D_ERROR,"Fail to set new 'bootarg' (err:%d)\n",err));
    461     }
    462   }
    463 
    464   //
    465   // Set Linux Initrd
    466   //
    467   if (InitrdImageSize != 0) {
    468     InitrdImageStart = cpu_to_fdt64 (InitrdImage);
    469     err = fdt_setprop(fdt, node, "linux,initrd-start", &InitrdImageStart, sizeof(EFI_PHYSICAL_ADDRESS));
    470     if (err) {
    471       DEBUG((EFI_D_ERROR,"Fail to set new 'linux,initrd-start' (err:%d)\n",err));
    472     }
    473     InitrdImageEnd = cpu_to_fdt64 (InitrdImage + InitrdImageSize);
    474     err = fdt_setprop(fdt, node, "linux,initrd-end", &InitrdImageEnd, sizeof(EFI_PHYSICAL_ADDRESS));
    475     if (err) {
    476       DEBUG((EFI_D_ERROR,"Fail to set new 'linux,initrd-start' (err:%d)\n",err));
    477     }
    478   }
    479 
    480   //
    481   // Set Physical memory setup if does not exist
    482   //
    483   node = fdt_subnode_offset(fdt, 0, "memory");
    484   if (node < 0) {
    485     // The 'memory' node does not exist, create it
    486     node = fdt_add_subnode(fdt, 0, "memory");
    487     if (node >= 0) {
    488       fdt_setprop_string(fdt, node, "name", "memory");
    489       fdt_setprop_string(fdt, node, "device_type", "memory");
    490 
    491       GetSystemMemoryResources (&ResourceList);
    492       Resource = (BDS_SYSTEM_MEMORY_RESOURCE*)ResourceList.ForwardLink;
    493 
    494       Region.Base = cpu_to_fdtn ((UINTN)Resource->PhysicalStart);
    495       Region.Size = cpu_to_fdtn ((UINTN)Resource->ResourceLength);
    496 
    497       err = fdt_setprop(fdt, node, "reg", &Region, sizeof(Region));
    498       if (err) {
    499         DEBUG((EFI_D_ERROR,"Fail to set new 'memory region' (err:%d)\n",err));
    500       }
    501     }
    502   }
    503 
    504   //
    505   // Add the memory regions reserved by the UEFI Firmware
    506   //
    507 
    508   // Retrieve the UEFI Memory Map
    509   MemoryMap = NULL;
    510   MemoryMapSize = 0;
    511   Status = gBS->GetMemoryMap (&MemoryMapSize, MemoryMap, &MapKey, &DescriptorSize, &DescriptorVersion);
    512   if (Status == EFI_BUFFER_TOO_SMALL) {
    513     // The UEFI specification advises to allocate more memory for the MemoryMap buffer between successive
    514     // calls to GetMemoryMap(), since allocation of the new buffer may potentially increase memory map size.
    515     Pages = EFI_SIZE_TO_PAGES (MemoryMapSize) + 1;
    516     MemoryMap = AllocatePages (Pages);
    517     if (MemoryMap == NULL) {
    518       Status = EFI_OUT_OF_RESOURCES;
    519       goto FAIL_COMPLETE_FDT;
    520     }
    521     Status = gBS->GetMemoryMap (&MemoryMapSize, MemoryMap, &MapKey, &DescriptorSize, &DescriptorVersion);
    522   }
    523 
    524   // Go through the list and add the reserved region to the Device Tree
    525   if (!EFI_ERROR(Status)) {
    526     MemoryMapPtr = MemoryMap;
    527     for (Index = 0; Index < (MemoryMapSize / DescriptorSize); Index++) {
    528       if (IsLinuxReservedRegion ((EFI_MEMORY_TYPE)MemoryMapPtr->Type)) {
    529         DEBUG((DEBUG_VERBOSE, "Reserved region of type %d [0x%lX, 0x%lX]\n",
    530             MemoryMapPtr->Type,
    531             (UINTN)MemoryMapPtr->PhysicalStart,
    532             (UINTN)(MemoryMapPtr->PhysicalStart + MemoryMapPtr->NumberOfPages * EFI_PAGE_SIZE)));
    533         err = fdt_add_mem_rsv(fdt, MemoryMapPtr->PhysicalStart, MemoryMapPtr->NumberOfPages * EFI_PAGE_SIZE);
    534         if (err != 0) {
    535           Print(L"Warning: Fail to add 'memreserve' (err:%d)\n", err);
    536         }
    537       }
    538       MemoryMapPtr = (EFI_MEMORY_DESCRIPTOR*)((UINTN)MemoryMapPtr + DescriptorSize);
    539     }
    540   }
    541 
    542   //
    543   // Setup Arm Mpcore Info if it is a multi-core or multi-cluster platforms.
    544   //
    545   // For 'cpus' and 'cpu' device tree nodes bindings, refer to this file
    546   // in the kernel documentation:
    547   // Documentation/devicetree/bindings/arm/cpus.txt
    548   //
    549   Status = gBS->LocateProtocol (
    550                &gAmdMpCoreInfoProtocolGuid,
    551                NULL,
    552                (VOID **)&AmdMpCoreInfoProtocol
    553                );
    554   ASSERT_EFI_ERROR (Status);
    555 
    556   // Get pointer to ARM core info table
    557   ArmCoreInfoTable = AmdMpCoreInfoProtocol->GetArmCoreInfoTable (&ArmCoreCount);
    558   ASSERT (ArmCoreInfoTable != NULL);
    559   ASSERT (ArmCoreCount <= NUM_CORES);
    560 
    561   // Get Id from primary CPU
    562   MpId = (UINTN) ArmReadMpidr ();
    563   PrimaryClusterId = GET_CLUSTER_ID((UINT32) MpId);
    564   PrimaryCoreId    = GET_CORE_ID((UINT32) MpId);
    565 
    566   // Remove existing 'pmu' node and create a new one
    567   pmu_node = fdt_subnode_offset (fdt, 0, "pmu");
    568   if (pmu_node >= 0) {
    569     fdt_del_node (fdt, pmu_node);
    570   }
    571   pmu_node = fdt_add_subnode(fdt, 0, "pmu");
    572   if (pmu_node >= 0) {
    573     // append PMU interrupts
    574     for (Index = 0; Index < ArmCoreCount; Index++) {
    575       MpId = (UINTN) GET_MPID (ArmCoreInfoTable[Index].ClusterId,
    576                                ArmCoreInfoTable[Index].CoreId);
    577 
    578       Status = AmdMpCoreInfoProtocol->GetPmuSpiFromMpId (MpId, &PmuInt.IntId);
    579       if (EFI_ERROR (Status)) {
    580         DEBUG ((EFI_D_ERROR, "FDT: Error getting PMU interrupt for MpId '0x%x'\n", MpId));
    581         goto FAIL_COMPLETE_FDT;
    582       }
    583 
    584       PmuInt.Flag = cpu_to_fdt32(PMU_INT_FLAG_SPI);
    585       PmuInt.IntId = cpu_to_fdt32(PmuInt.IntId);
    586       PmuInt.Type = cpu_to_fdt32(PMU_INT_TYPE_HIGH_LEVEL);
    587       fdt_appendprop(fdt, pmu_node, "interrupts", &PmuInt, sizeof(PmuInt));
    588     }
    589     fdt_setprop_string(fdt, pmu_node, "compatible", "arm,armv8-pmuv3");
    590   } else {
    591     DEBUG((EFI_D_ERROR,"FDT: Error creating 'pmu' node\n"));
    592     Status = EFI_INVALID_PARAMETER;
    593     goto FAIL_COMPLETE_FDT;
    594   }
    595 
    596   // Remove existing 'psci' node if feature not supported
    597   node = fdt_subnode_offset (fdt, 0, "psci");
    598   if (node >= 0) {
    599     if (!FixedPcdGetBool (PcdPsciOsSupport)) {
    600       fdt_del_node (fdt, node);
    601     }
    602   } else if (FixedPcdGetBool (PcdPsciOsSupport) &&
    603       FixedPcdGetBool (PcdTrustedFWSupport)) {
    604     // Add 'psci' node if not present
    605     node = fdt_add_subnode(fdt, 0, "psci");
    606     if (node >= 0) {
    607       fdt_setprop_string(fdt, node, "compatible", "arm,psci-0.2");
    608       fdt_appendprop_string(fdt, node, "compatible", "arm,psci");
    609       fdt_setprop_string(fdt, node, "method", "smc");
    610     } else {
    611       DEBUG((EFI_D_ERROR,"FDT: Error creating 'psci' node\n"));
    612       Status = EFI_INVALID_PARAMETER;
    613       goto FAIL_COMPLETE_FDT;
    614     }
    615   }
    616 
    617   // Remove existing 'cpus' node and create a new one
    618   node = fdt_subnode_offset (fdt, 0, "cpus");
    619   if (node >= 0) {
    620     fdt_del_node (fdt, node);
    621   }
    622   node = fdt_add_subnode(fdt, 0, "cpus");
    623   if (node >= 0) {
    624     // Configure the 'cpus' node
    625     fdt_setprop_string(fdt, node, "name", "cpus");
    626     fdt_setprop_cell (fdt, node, "#address-cells", sizeof (UINTN) / 4);
    627     fdt_setprop_cell(fdt, node, "#size-cells", 0);
    628   } else {
    629     DEBUG((EFI_D_ERROR,"FDT: Error creating 'cpus' node\n"));
    630     Status = EFI_INVALID_PARAMETER;
    631     goto FAIL_COMPLETE_FDT;
    632   }
    633 
    634   //
    635   // Walk the processor table in reverse order for proper listing in FDT
    636   //
    637   Index = ArmCoreCount;
    638   while (Index--) {
    639     // Create 'cpu' node
    640     AsciiSPrint (Name, sizeof(Name), "CPU%d", Index);
    641     cpu_node = fdt_add_subnode (fdt, node, Name);
    642     if (cpu_node < 0) {
    643       DEBUG ((EFI_D_ERROR, "FDT: Error on creating '%a' node\n", Name));
    644       Status = EFI_INVALID_PARAMETER;
    645       goto FAIL_COMPLETE_FDT;
    646     }
    647     phandle[Index] = fdt_alloc_phandle(fdt);
    648     fdt_setprop_cell (fdt, cpu_node, "phandle", phandle[Index]);
    649     fdt_setprop_cell (fdt, cpu_node, "linux,phandle", phandle[Index]);
    650 
    651     if (FixedPcdGetBool (PcdPsciOsSupport) &&
    652       FixedPcdGetBool (PcdTrustedFWSupport)) {
    653       fdt_setprop_string(fdt, cpu_node, "enable-method", "psci");
    654     } else {
    655       fdt_setprop_string(fdt, cpu_node, "enable-method", "spin-table");
    656       MbAddr = ArmCoreInfoTable[Index].MailboxSetAddress;
    657       MbAddr = cpu_to_fdtn (MbAddr);
    658       fdt_setprop (fdt, cpu_node, "cpu-release-addr", &MbAddr, sizeof (MbAddr));
    659     }
    660     MpId = (UINTN) GET_MPID (ArmCoreInfoTable[Index].ClusterId,
    661                                  ArmCoreInfoTable[Index].CoreId);
    662     MpId = cpu_to_fdtn (MpId);
    663     fdt_setprop (fdt, cpu_node, "reg", &MpId, sizeof (MpId));
    664     fdt_setprop_string(fdt, cpu_node, "compatible", "arm,armv8");
    665     fdt_setprop_string (fdt, cpu_node, "device_type", "cpu");
    666 
    667     // If it is not the primary core than the cpu should be disabled
    668     if (((ArmCoreInfoTable[Index].ClusterId != PrimaryClusterId) ||
    669          (ArmCoreInfoTable[Index].CoreId != PrimaryCoreId))) {
    670       fdt_setprop_string(fdt, cpu_node, "status", "disabled");
    671     }
    672   }
    673 
    674   // Remove existing 'cpu-map' node and create a new one
    675   map_node = fdt_subnode_offset (fdt, node, "cpu-map");
    676   if (map_node >= 0) {
    677     fdt_del_node (fdt, map_node);
    678   }
    679   map_node = fdt_add_subnode(fdt, node, "cpu-map");
    680   if (map_node >= 0) {
    681     ClusterIndex = ArmCoreCount - 1;
    682     ClusterCount = NumberOfClustersInTable (ArmCoreInfoTable,
    683                                             ArmCoreCount);
    684     while (ClusterCount--) {
    685       // Create 'cluster' node
    686       AsciiSPrint (Name, sizeof(Name), "cluster%d", ClusterCount);
    687       cluster_node = fdt_add_subnode (fdt, map_node, Name);
    688       if (cluster_node < 0) {
    689         DEBUG ((EFI_D_ERROR, "FDT: Error creating '%a' node\n", Name));
    690         Status = EFI_INVALID_PARAMETER;
    691         goto FAIL_COMPLETE_FDT;
    692       }
    693 
    694       ClusterId = ArmCoreInfoTable[ClusterIndex].ClusterId;
    695       CoreIndex = ClusterIndex;
    696       CoresInCluster = NumberOfCoresInCluster (ArmCoreInfoTable,
    697                                                ArmCoreCount,
    698                                                ClusterId);
    699       while (CoresInCluster--) {
    700         // Create 'core' node
    701         AsciiSPrint (Name, sizeof(Name), "core%d", CoresInCluster);
    702         cpu_node = fdt_add_subnode (fdt, cluster_node, Name);
    703         if (cpu_node < 0) {
    704           DEBUG ((EFI_D_ERROR, "FDT: Error creating '%a' node\n", Name));
    705           Status = EFI_INVALID_PARAMETER;
    706           goto FAIL_COMPLETE_FDT;
    707         }
    708         fdt_setprop_cell (fdt, cpu_node, "cpu", phandle[CoreIndex]);
    709 
    710         // iterate to next core in cluster
    711         if (CoresInCluster) {
    712           do {
    713              --CoreIndex;
    714           } while (ClusterId != ArmCoreInfoTable[CoreIndex].ClusterId);
    715         }
    716       }
    717 
    718       // iterate to next cluster
    719       if (ClusterCount) {
    720         do {
    721            --ClusterIndex;
    722         } while (ClusterInRange (ArmCoreInfoTable,
    723                                  ArmCoreInfoTable[ClusterIndex].ClusterId,
    724                                  ClusterIndex + 1,
    725                                  ArmCoreCount - 1));
    726       }
    727     }
    728   } else {
    729     DEBUG((EFI_D_ERROR,"FDT: Error creating 'cpu-map' node\n"));
    730     Status = EFI_INVALID_PARAMETER;
    731     goto FAIL_COMPLETE_FDT;
    732   }
    733 
    734   SetSocIdStatus (fdt);
    735   SetXgbeStatus (fdt);
    736 
    737   DEBUG_CODE_BEGIN();
    738     // DebugDumpFdt (fdt);
    739   DEBUG_CODE_END();
    740 
    741   // If we succeeded to generate the new Device Tree then free the old Device Tree
    742   gBS->FreePages (*FdtBlobBase, EFI_SIZE_TO_PAGES (*FdtBlobSize));
    743 
    744   // Update the real size of the Device Tree
    745   fdt_pack ((VOID*)(UINTN)(NewFdtBlobBase));
    746 
    747   *FdtBlobBase = NewFdtBlobBase;
    748   *FdtBlobSize = (UINTN)fdt_totalsize ((VOID*)(UINTN)(NewFdtBlobBase));
    749   return EFI_SUCCESS;
    750 
    751 FAIL_COMPLETE_FDT:
    752   gBS->FreePages (NewFdtBlobAllocation, EFI_SIZE_TO_PAGES (NewFdtBlobSize));
    753 
    754 FAIL_RELOCATE_FDT:
    755   *FdtBlobSize = (UINTN)fdt_totalsize ((VOID*)(UINTN)(*FdtBlobBase));
    756   // Return success even if we failed to update the FDT blob.
    757   // The original one is still valid.
    758   return EFI_SUCCESS;
    759 }
    760 
    761