/** @file
  Copyright (c) 2024, Intel Corporation. All rights reserved.<BR>
  SPDX-License-Identifier: BSD-2-Clause-Patent
**/

#include <PiPei.h>
#include <Library/BaseLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/DebugLib.h>
#include <Library/HobLib.h>
#include <Library/PcdLib.h>
#include <Library/IoLib.h>
#include <Guid/MemoryAllocationHob.h>
#include <Guid/DebugPrintErrorLevel.h>
#include <Guid/SerialPortInfoGuid.h>
#include <Guid/MemoryMapInfoGuid.h>
#include <Guid/AcpiBoardInfoGuid.h>
#include <Guid/GraphicsInfoHob.h>
#include <Guid/UniversalPayloadBase.h>
#include <UniversalPayload/SmbiosTable.h>
#include <UniversalPayload/AcpiTable.h>
#include <UniversalPayload/UniversalPayload.h>
#include <UniversalPayload/ExtraData.h>
#include <UniversalPayload/SerialPortInfo.h>
#include <UniversalPayload/DeviceTree.h>
#include <UniversalPayload/PciRootBridges.h>
#include <IndustryStandard/SmBios.h>
#include <Library/PrintLib.h>
#include <Library/FdtLib.h>
#include <Protocol/PciHostBridgeResourceAllocation.h>
#include <Protocol/PciIo.h>
#include <Guid/PciSegmentInfoGuid.h>

typedef enum {
  ReservedMemory = 1,
  Memory,
  FrameBuffer,
  PciRootBridge,
  Options,
  SerialPort,
  DoNothing
} FDT_NODE_TYPE;

#define MEMORY_ATTRIBUTE_DEFAULT  (EFI_RESOURCE_ATTRIBUTE_PRESENT | \
                                   EFI_RESOURCE_ATTRIBUTE_INITIALIZED | \
                                   EFI_RESOURCE_ATTRIBUTE_TESTED | \
                                   EFI_RESOURCE_ATTRIBUTE_UNCACHEABLE | \
                                   EFI_RESOURCE_ATTRIBUTE_WRITE_COMBINEABLE | \
                                   EFI_RESOURCE_ATTRIBUTE_WRITE_THROUGH_CACHEABLE | \
                                   EFI_RESOURCE_ATTRIBUTE_WRITE_BACK_CACHEABLE)

#define ROOT_BRIDGE_SUPPORTS_DEFAULT  (EFI_PCI_IO_ATTRIBUTE_VGA_IO_16 | \
                                       EFI_PCI_IO_ATTRIBUTE_VGA_PALETTE_IO_16 | \
                                       EFI_PCI_IO_ATTRIBUTE_ISA_IO_16 | \
                                       EFI_PCI_IO_ATTRIBUTE_IDE_PRIMARY_IO | \
                                       EFI_PCI_IO_ATTRIBUTE_VGA_IO | \
                                       EFI_PCI_IO_ATTRIBUTE_VGA_MEMORY | \
                                       EFI_PCI_IO_ATTRIBUTE_VGA_PALETTE_IO | \
                                       EFI_PCI_IO_ATTRIBUTE_ISA_IO | \
                                       EFI_PCI_IO_ATTRIBUTE_ISA_MOTHERBOARD_IO)

#define UPL_ALIGN_DOWN(Addr)  ((UINT64)(Addr) & ~(UINT64)(EFI_PAGE_SIZE - 1))

extern VOID                         *mHobList;
UNIVERSAL_PAYLOAD_PCI_ROOT_BRIDGES  *mPciRootBridgeInfo = NULL;
INT32                               mNode[0x500]        = { 0 };
UINT32                              mNodeIndex          = 0;
UPL_PCI_SEGMENT_INFO_HOB            *mUplPciSegmentInfoHob;

/**
  Build a Handoff Information Table HOB

  This function initializes a HOB region from EfiMemoryBegin to
  EfiMemoryTop. And EfiFreeMemoryBottom and EfiFreeMemoryTop should
  be inside the HOB region.

  @param[in] EfiMemoryBottom       Total memory start address
  @param[in] EfiMemoryTop          Total memory end address.
  @param[in] EfiFreeMemoryBottom   Free memory start address
  @param[in] EfiFreeMemoryTop      Free memory end address.

  @return   The pointer to the handoff HOB table.

**/
EFI_HOB_HANDOFF_INFO_TABLE *
EFIAPI
HobConstructor (
  IN VOID  *EfiMemoryBottom,
  IN VOID  *EfiMemoryTop,
  IN VOID  *EfiFreeMemoryBottom,
  IN VOID  *EfiFreeMemoryTop
  );

/**
  It will record the memory node initialized.

  @param[in]  Node           memory node is going to parsing.
**/
VOID
RecordMemoryNode (
  INT32  Node
  )
{
  DEBUG ((DEBUG_INFO, "\n RecordMemoryNode  %x , mNodeIndex :%x  \n", Node, mNodeIndex));
  mNode[mNodeIndex] = Node;
  mNodeIndex++;
}

/**
  Check the memory node if initialized.

  @param[in]  Node           memory node is going to parsing.

  @return TRUE               memory node was initialized. don't parse it again.
  @return FALSE              memory node wasn't initialized, go to parse it.
**/
BOOLEAN
CheckMemoryNodeIfInit (
  INT32  Node
  )
{
  UINT32  i;

  for (i = 0; i < mNodeIndex; i++) {
    if (mNode[i] == Node) {
      return TRUE;
    }
  }

  return FALSE;
}

/**
  It will check device node from FDT.

  @param[in]  NodeString        Device node name string.
  @param[in]  Depth             Check layer of Device node, only parse the 1st layer

  @return FDT_NODE_TYPE         what type of the device node.
**/
FDT_NODE_TYPE
CheckNodeType (
  CHAR8  *NodeString,
  INT32  Depth
  )
{
  DEBUG ((DEBUG_INFO, "\n CheckNodeType  %a   \n", NodeString));
  if (AsciiStrnCmp (NodeString, "serial@", AsciiStrLen ("serial@")) == 0) {
    return SerialPort;
  } else if (AsciiStrnCmp (NodeString, "reserved-memory", AsciiStrLen ("reserved-memory")) == 0) {
    return ReservedMemory;
  } else if (AsciiStrnCmp (NodeString, "memory@", AsciiStrLen ("memory@")) == 0) {
    return Memory;
  } else if (AsciiStrnCmp (NodeString, "framebuffer@", AsciiStrLen ("framebuffer@")) == 0) {
    return FrameBuffer;
  } else if (AsciiStrnCmp (NodeString, "pci-rb", AsciiStrLen ("pci-rb")) == 0) {
    return PciRootBridge;
  } else if (AsciiStrCmp (NodeString, "options") == 0) {
    return Options;
  } else {
    return DoNothing;
  }
}

/**
  It will ParseMemory node from FDT.

  @param[in]  Fdt               Address of the Fdt data.
  @param[in]  SubNode           first node of the PCI root bridge node.
**/
VOID
ParseMemory (
  IN VOID   *Fdt,
  IN INT32  Node
  )
{
  UINT32              Attribute;
  UINT8               ECCAttribute;
  UINT32              ECCData, ECCData2;
  INT32               Property;
  CONST FDT_PROPERTY  *PropertyPtr;
  INT32               TempLen;
  CONST CHAR8         *TempStr;
  UINT64              *Data64;
  UINT32              *Data32;
  UINT64              StartAddress;
  UINT64              NumberOfBytes;

  Attribute    = MEMORY_ATTRIBUTE_DEFAULT;
  ECCAttribute = 0;
  ECCData      = ECCData2 = 0;
  for (Property = FdtFirstPropertyOffset (Fdt, Node); Property >= 0; Property = FdtNextPropertyOffset (Fdt, Property)) {
    PropertyPtr = FdtGetPropertyByOffset (Fdt, Property, &TempLen);
    TempStr     = FdtGetString (Fdt, Fdt32ToCpu (PropertyPtr->NameOffset), NULL);
    if (AsciiStrCmp (TempStr, "reg") == 0) {
      Data64        = (UINT64 *)(PropertyPtr->Data);
      StartAddress  = Fdt64ToCpu (ReadUnaligned64 (Data64));
      NumberOfBytes = Fdt64ToCpu (ReadUnaligned64 (Data64 + 1));
    } else if (AsciiStrCmp (TempStr, "ecc-detection-bits") == 0) {
      Data32  = (UINT32 *)(PropertyPtr->Data);
      ECCData = Fdt32ToCpu (*Data32);
    } else if (AsciiStrCmp (TempStr, "ecc-correction-bits") == 0) {
      Data32   = (UINT32 *)(PropertyPtr->Data);
      ECCData2 = Fdt32ToCpu (*Data32);
    }
  }

  if (ECCData == ECCData2) {
    if (ECCData == 1) {
      ECCAttribute = EFI_RESOURCE_ATTRIBUTE_SINGLE_BIT_ECC;
    } else if (ECCData == 2) {
      ECCAttribute = EFI_RESOURCE_ATTRIBUTE_MULTIPLE_BIT_ECC;
    }
  }

  if (ECCAttribute != 0) {
    Attribute |= ECCAttribute;
  }

  BuildResourceDescriptorHob (EFI_RESOURCE_SYSTEM_MEMORY, Attribute, StartAddress, NumberOfBytes);
}

/**
  It will ParseReservedMemory node from FDT.

  @param[in]  Fdt               Address of the Fdt data.
  @param[in]  SubNode           first node of the PCI root bridge node.
**/
VOID
ParseReservedMemory (
  IN VOID   *Fdt,
  IN INT32  Node
  )
{
  INT32                           SubNode;
  INT32                           TempLen;
  CONST CHAR8                     *TempStr;
  CONST FDT_PROPERTY              *PropertyPtr;
  UINT64                          *Data64;
  UINT64                          StartAddress;
  UINT64                          NumberOfBytes;
  UNIVERSAL_PAYLOAD_ACPI_TABLE    *PlatformAcpiTable;
  UNIVERSAL_PAYLOAD_SMBIOS_TABLE  *SmbiosTable;
  FDT_NODE_HEADER                 *NodePtr;
  UINT32                          Attribute;

  PlatformAcpiTable = NULL;

  for (SubNode = FdtFirstSubnode (Fdt, Node); SubNode >= 0; SubNode = FdtNextSubnode (Fdt, SubNode)) {
    NodePtr = (FDT_NODE_HEADER *)((CONST CHAR8 *)Fdt + SubNode + Fdt32ToCpu (((FDT_HEADER *)Fdt)->OffsetDtStruct));
    DEBUG ((DEBUG_INFO, "\n      SubNode(%08X)  %a", SubNode, NodePtr->Name));
    PropertyPtr = FdtGetProperty (Fdt, SubNode, "reg", &TempLen);
    ASSERT (TempLen > 0);
    TempStr = (CHAR8 *)(PropertyPtr->Data);
    if (TempLen > 0) {
      Data64        = (UINT64 *)(PropertyPtr->Data);
      StartAddress  = Fdt64ToCpu (ReadUnaligned64 (Data64));
      NumberOfBytes = Fdt64ToCpu (ReadUnaligned64 (Data64 + 1));
      DEBUG ((DEBUG_INFO, "\n         Property  %a", TempStr));
      DEBUG ((DEBUG_INFO, "  %016lX  %016lX\n", StartAddress, NumberOfBytes));
    }

    RecordMemoryNode (SubNode);

    if (AsciiStrnCmp (NodePtr->Name, "mmio@", AsciiStrLen ("mmio@")) == 0) {
      DEBUG ((DEBUG_INFO, "  MemoryMappedIO"));
      BuildMemoryAllocationHob (StartAddress, NumberOfBytes, EfiMemoryMappedIO);
    } else {
      PropertyPtr = FdtGetProperty (Fdt, SubNode, "compatible", &TempLen);
      TempStr     = (CHAR8 *)(PropertyPtr->Data);
      DEBUG ((DEBUG_INFO, "compatible:  %a\n", TempStr));
      if (AsciiStrnCmp (TempStr, "boot-code", AsciiStrLen ("boot-code")) == 0) {
        DEBUG ((DEBUG_INFO, "  boot-code\n"));
        BuildMemoryAllocationHob (StartAddress, NumberOfBytes, EfiBootServicesCode);
      } else if (AsciiStrnCmp (TempStr, "boot-data", AsciiStrLen ("boot-data")) == 0) {
        DEBUG ((DEBUG_INFO, "  boot-data\n"));
        BuildMemoryAllocationHob (StartAddress, NumberOfBytes, EfiBootServicesData);
      } else if (AsciiStrnCmp (TempStr, "runtime-code", AsciiStrLen ("runtime-code")) == 0) {
        DEBUG ((DEBUG_INFO, "  runtime-code\n"));
        BuildMemoryAllocationHob (StartAddress, NumberOfBytes, EfiRuntimeServicesCode);
      } else if (AsciiStrnCmp (TempStr, "runtime-data", AsciiStrLen ("runtime-data")) == 0) {
        DEBUG ((DEBUG_INFO, "  runtime-data\n"));
        BuildMemoryAllocationHob (StartAddress, NumberOfBytes, EfiRuntimeServicesData);
      } else if (AsciiStrnCmp (TempStr, "special-purpose", AsciiStrLen ("special-purpose")) == 0) {
        Attribute = MEMORY_ATTRIBUTE_DEFAULT | EFI_RESOURCE_ATTRIBUTE_SPECIAL_PURPOSE;
        DEBUG ((DEBUG_INFO, "  special-purpose memory\n"));
        BuildResourceDescriptorHob (EFI_RESOURCE_SYSTEM_MEMORY, Attribute, StartAddress, NumberOfBytes);
      } else if (AsciiStrnCmp (TempStr, "acpi-nvs", AsciiStrLen ("acpi-nvs")) == 0) {
        DEBUG ((DEBUG_INFO, "\n ********* acpi-nvs ********\n"));
        BuildMemoryAllocationHob (StartAddress, NumberOfBytes, EfiACPIMemoryNVS);
      } else if (AsciiStrnCmp (TempStr, "acpi", AsciiStrLen ("acpi")) == 0) {
        DEBUG ((DEBUG_INFO, "  acpi, StartAddress:%x, NumberOfBytes:%x\n", StartAddress, NumberOfBytes));

        BuildMemoryAllocationHob (
          UPL_ALIGN_DOWN (StartAddress),
          ALIGN_VALUE (NumberOfBytes, EFI_PAGE_SIZE),
          EfiBootServicesData
          );
        PlatformAcpiTable = BuildGuidHob (&gUniversalPayloadAcpiTableGuid, sizeof (UNIVERSAL_PAYLOAD_ACPI_TABLE));
        if (PlatformAcpiTable != NULL) {
          DEBUG ((DEBUG_INFO, " build gUniversalPayloadAcpiTableGuid , NumberOfBytes:%x\n", NumberOfBytes));
          PlatformAcpiTable->Rsdp            = (EFI_PHYSICAL_ADDRESS)(UINTN)StartAddress;
          PlatformAcpiTable->Header.Revision = UNIVERSAL_PAYLOAD_ACPI_TABLE_REVISION;
          PlatformAcpiTable->Header.Length   = sizeof (UNIVERSAL_PAYLOAD_ACPI_TABLE);
        }
      } else if (AsciiStrnCmp (TempStr, "smbios", AsciiStrLen ("smbios")) == 0) {
        DEBUG ((DEBUG_INFO, " build smbios, NumberOfBytes:%x\n", NumberOfBytes));
        BuildMemoryAllocationHob (StartAddress, NumberOfBytes, EfiBootServicesData);
        SmbiosTable = BuildGuidHob (&gUniversalPayloadSmbios3TableGuid, sizeof (UNIVERSAL_PAYLOAD_SMBIOS_TABLE));
        if (SmbiosTable != NULL) {
          SmbiosTable->Header.Revision  = UNIVERSAL_PAYLOAD_SMBIOS_TABLE_REVISION;
          SmbiosTable->Header.Length    = sizeof (UNIVERSAL_PAYLOAD_SMBIOS_TABLE);
          SmbiosTable->SmBiosEntryPoint = (EFI_PHYSICAL_ADDRESS)(UINTN)(StartAddress);
        }
      } else {
        BuildMemoryAllocationHob (StartAddress, NumberOfBytes, EfiReservedMemoryType);
      }
    }
  }
}

/**
  It will ParseFrameBuffer node from FDT.

  @param[in]  Fdt               Address of the Fdt data.
  @param[in]  SubNode           first Sub node of the PCI root bridge node.

  @return     GmaStr            Graphic device node name string.
**/
CHAR8 *
ParseFrameBuffer (
  IN VOID   *Fdt,
  IN INT32  Node
  )
{
  INT32                      Property;
  INT32                      TempLen;
  CONST FDT_PROPERTY         *PropertyPtr;
  CONST CHAR8                *TempStr;
  UINT32                     *Data32;
  UINT64                     FrameBufferBase;
  UINT32                     FrameBufferSize;
  EFI_PEI_GRAPHICS_INFO_HOB  *GraphicsInfo;
  CHAR8                      *GmaStr;

  GmaStr = "Gma";
  //
  // Create GraphicInfo HOB.
  //
  GraphicsInfo = BuildGuidHob (&gEfiGraphicsInfoHobGuid, sizeof (EFI_PEI_GRAPHICS_INFO_HOB));
  ASSERT (GraphicsInfo != NULL);
  if (GraphicsInfo == NULL) {
    return GmaStr;
  }

  ZeroMem (GraphicsInfo, sizeof (EFI_PEI_GRAPHICS_INFO_HOB));

  for (Property = FdtFirstPropertyOffset (Fdt, Node); Property >= 0; Property = FdtNextPropertyOffset (Fdt, Property)) {
    PropertyPtr = FdtGetPropertyByOffset (Fdt, Property, &TempLen);
    TempStr     = FdtGetString (Fdt, Fdt32ToCpu (PropertyPtr->NameOffset), NULL);
    if (AsciiStrCmp (TempStr, "reg") == 0) {
      Data32                        = (UINT32 *)(PropertyPtr->Data);
      FrameBufferBase               = Fdt32ToCpu (*(Data32 + 0));
      FrameBufferSize               = Fdt32ToCpu (*(Data32 + 1));
      GraphicsInfo->FrameBufferBase = FrameBufferBase;
      GraphicsInfo->FrameBufferSize = (UINT32)FrameBufferSize;
    } else if (AsciiStrCmp (TempStr, "width") == 0) {
      Data32                                          = (UINT32 *)(PropertyPtr->Data);
      GraphicsInfo->GraphicsMode.HorizontalResolution = Fdt32ToCpu (*Data32);
    } else if (AsciiStrCmp (TempStr, "height") == 0) {
      Data32                                        = (UINT32 *)(PropertyPtr->Data);
      GraphicsInfo->GraphicsMode.VerticalResolution = Fdt32ToCpu (*Data32);
    } else if (AsciiStrCmp (TempStr, "format") == 0) {
      TempStr = (CHAR8 *)(PropertyPtr->Data);
      if (AsciiStrCmp (TempStr, "a8r8g8b8") == 0) {
        GraphicsInfo->GraphicsMode.PixelFormat = PixelRedGreenBlueReserved8BitPerColor;
      } else if (AsciiStrCmp (TempStr, "a8b8g8r8") == 0) {
        GraphicsInfo->GraphicsMode.PixelFormat = PixelBlueGreenRedReserved8BitPerColor;
      } else {
        GraphicsInfo->GraphicsMode.PixelFormat = PixelFormatMax;
      }
    } else if (AsciiStrCmp (TempStr, "display") == 0) {
      GmaStr = (CHAR8 *)(PropertyPtr->Data);
      GmaStr++;
      DEBUG ((DEBUG_INFO, "  display (%s)", GmaStr));
    }

    // In most case, PixelsPerScanLine is identical to HorizontalResolution
    GraphicsInfo->GraphicsMode.PixelsPerScanLine = GraphicsInfo->GraphicsMode.HorizontalResolution;
  }

  return GmaStr;
}

/**
  It will ParseOptions node from FDT.

  @param[in]  Fdt               Address of the Fdt data.
  @param[in]  SubNode           first Sub node of the PCI root bridge node.
  @param[out] PciEnumDone       Init ParsePciRootBridge node for ParsePciRootBridge.
  @param[out] BootMode          Init the system boot mode
**/
VOID
ParseOptions (
  IN VOID            *Fdt,
  IN INT32           Node,
  OUT UINT8          *PciEnumDone,
  OUT EFI_BOOT_MODE  *BootMode
  )
{
  INT32                   SubNode;
  FDT_NODE_HEADER         *NodePtr;
  UNIVERSAL_PAYLOAD_BASE  *PayloadBase;
  CONST FDT_PROPERTY      *PropertyPtr;
  CONST CHAR8             *TempStr;
  INT32                   TempLen;
  UINT32                  *Data32;
  UINT64                  *Data64;
  UINT64                  StartAddress;
  UINT8                   SizeOfMemorySpace;

  for (SubNode = FdtFirstSubnode (Fdt, Node); SubNode >= 0; SubNode = FdtNextSubnode (Fdt, SubNode)) {
    NodePtr = (FDT_NODE_HEADER *)((CONST CHAR8 *)Fdt + SubNode + Fdt32ToCpu (((FDT_HEADER *)Fdt)->OffsetDtStruct));
    DEBUG ((DEBUG_INFO, "\n      SubNode(%08X)  %a", SubNode, NodePtr->Name));

    if (AsciiStrnCmp (NodePtr->Name, "upl-images@", AsciiStrLen ("upl-images@")) == 0) {
      DEBUG ((DEBUG_INFO, "  Found image@ node \n"));
      //
      // Build PayloadBase HOB .
      //
      PayloadBase = BuildGuidHob (&gUniversalPayloadBaseGuid, sizeof (UNIVERSAL_PAYLOAD_BASE));
      ASSERT (PayloadBase != NULL);
      if (PayloadBase == NULL) {
        return;
      }

      PayloadBase->Header.Revision = UNIVERSAL_PAYLOAD_BASE_REVISION;
      PayloadBase->Header.Length   = sizeof (UNIVERSAL_PAYLOAD_BASE);

      PropertyPtr = FdtGetProperty (Fdt, SubNode, "addr", &TempLen);

      ASSERT (TempLen > 0);
      if (TempLen > 0) {
        Data64       = (UINT64 *)(PropertyPtr->Data);
        StartAddress = Fdt64ToCpu (ReadUnaligned64 (Data64));
        DEBUG ((DEBUG_INFO, "\n         Property(00000000)  entry"));
        DEBUG ((DEBUG_INFO, "  %016lX\n", StartAddress));

        PayloadBase->Entry = (EFI_PHYSICAL_ADDRESS)StartAddress;
      }
    }

    if (AsciiStrnCmp (NodePtr->Name, "upl-params", AsciiStrLen ("upl-params")) == 0) {
      PropertyPtr = FdtGetProperty (Fdt, SubNode, "addr-width", &TempLen);
      if (TempLen > 0) {
        Data32 = (UINT32 *)(PropertyPtr->Data);
        DEBUG ((DEBUG_INFO, "\n         Property(00000000)  address_width"));
        DEBUG ((DEBUG_INFO, "  %X", Fdt32ToCpu (*Data32)));
        SizeOfMemorySpace = (UINT8)Fdt32ToCpu (*Data32);
        BuildCpuHob (SizeOfMemorySpace, PcdGet8 (SizeOfIoSpace));
      }

      PropertyPtr = FdtGetProperty (Fdt, SubNode, "pci-enum-done", &TempLen);
      if (TempLen > 0) {
        *PciEnumDone = 1;
        DEBUG ((DEBUG_INFO, "  Found PciEnumDone (%08X)\n", *PciEnumDone));
      } else {
        *PciEnumDone = 0;
        DEBUG ((DEBUG_INFO, "  Not Found PciEnumDone \n"));
      }

      PropertyPtr = FdtGetProperty (Fdt, SubNode, "boot-mode", &TempLen);
      if (TempLen > 0) {
        TempStr = (CHAR8 *)(PropertyPtr->Data);
        if (AsciiStrCmp (TempStr, "normal") == 0) {
          *BootMode = BOOT_WITH_FULL_CONFIGURATION;
        } else if (AsciiStrCmp (TempStr, "fast") == 0) {
          *BootMode = BOOT_WITH_MINIMAL_CONFIGURATION;
        } else if (AsciiStrCmp (TempStr, "full") == 0) {
          *BootMode = BOOT_WITH_FULL_CONFIGURATION_PLUS_DIAGNOSTICS;
        } else if (AsciiStrCmp (TempStr, "default") == 0) {
          *BootMode = BOOT_WITH_DEFAULT_SETTINGS;
        } else if (AsciiStrCmp (TempStr, "s4") == 0) {
          *BootMode = BOOT_ON_S4_RESUME;
        } else if (AsciiStrCmp (TempStr, "s3") == 0) {
          *BootMode = BOOT_ON_S3_RESUME;
        }
      }
    }
  }
}

/**
  It will Parsegraphic node from FDT.

  @param[in]  Fdt               Address of the Fdt data.
  @param[in]  SubNode           first Sub node of the PCI root bridge node.
**/
VOID
ParsegraphicNode (
  IN VOID   *Fdt,
  IN INT32  SubNode
  )
{
  EFI_PEI_GRAPHICS_DEVICE_INFO_HOB  *GraphicsDev;
  CONST FDT_PROPERTY                *PropertyPtr;
  UINT16                            GmaID;
  UINT32                            *Data32;
  INT32                             TempLen;

  DEBUG ((DEBUG_INFO, "  Found gma@ node \n"));
  GraphicsDev = NULL;
  //
  // Build Graphic info HOB .
  //
  GraphicsDev = BuildGuidHob (&gEfiGraphicsDeviceInfoHobGuid, sizeof (EFI_PEI_GRAPHICS_DEVICE_INFO_HOB));
  ASSERT (GraphicsDev != NULL);
  if (GraphicsDev == NULL) {
    return;
  }

  SetMem (GraphicsDev, sizeof (EFI_PEI_GRAPHICS_DEVICE_INFO_HOB), 0xFF);
  PropertyPtr = FdtGetProperty (Fdt, SubNode, "vendor-id", &TempLen);
  ASSERT (TempLen > 0);
  if (TempLen > 0) {
    Data32 = (UINT32 *)(PropertyPtr->Data);
    GmaID  = (UINT16)Fdt32ToCpu (*Data32);
    DEBUG ((DEBUG_INFO, "\n   vendor-id"));
    DEBUG ((DEBUG_INFO, "  %016lX\n", GmaID));
    GraphicsDev->VendorId = GmaID;
  }

  PropertyPtr = FdtGetProperty (Fdt, SubNode, "device-id", &TempLen);
  ASSERT (TempLen > 0);
  if (TempLen > 0) {
    Data32 = (UINT32 *)(PropertyPtr->Data);
    GmaID  = (UINT16)Fdt32ToCpu (*Data32);
    DEBUG ((DEBUG_INFO, "\n   device-id"));
    DEBUG ((DEBUG_INFO, "  %016lX\n", GmaID));
    GraphicsDev->DeviceId = GmaID;
  }

  PropertyPtr = FdtGetProperty (Fdt, SubNode, "revision-id", &TempLen);
  ASSERT (TempLen > 0);
  if (TempLen > 0) {
    Data32 = (UINT32 *)(PropertyPtr->Data);
    GmaID  = (UINT16)Fdt32ToCpu (*Data32);
    DEBUG ((DEBUG_INFO, "\n   revision-id"));
    DEBUG ((DEBUG_INFO, "  %016lX\n", GmaID));
    GraphicsDev->RevisionId = (UINT8)GmaID;
  }

  PropertyPtr = FdtGetProperty (Fdt, SubNode, "subsystem-vendor-id", &TempLen);
  ASSERT (TempLen > 0);
  if (TempLen > 0) {
    Data32 = (UINT32 *)(PropertyPtr->Data);
    GmaID  = (UINT16)Fdt32ToCpu (*Data32);
    DEBUG ((DEBUG_INFO, "\n   subsystem-vendor-id"));
    DEBUG ((DEBUG_INFO, "  %016lX\n", GmaID));
    GraphicsDev->SubsystemVendorId = GmaID;
  }

  PropertyPtr = FdtGetProperty (Fdt, SubNode, "subsystem-id", &TempLen);
  ASSERT (TempLen > 0);
  if (TempLen > 0) {
    Data32 = (UINT32 *)(PropertyPtr->Data);
    GmaID  = (UINT16)Fdt32ToCpu (*Data32);
    DEBUG ((DEBUG_INFO, "\n   subsystem-id"));
    DEBUG ((DEBUG_INFO, "  %016lX\n", GmaID));
    GraphicsDev->SubsystemId = GmaID;
  }
}

/**
  It will ParseSerialPort node from FDT.

  @param[in]  Fdt               Address of the Fdt data.
  @param[in]  SubNode           first Sub node of the PCI root bridge node.
  @param[in]  AddressCells      #address-cells for serial port node 'reg' property.
**/
VOID
ParseSerialPort (
  IN VOID    *Fdt,
  IN INT32   SubNode,
  IN UINT32  AddressCells
  )
{
  UNIVERSAL_PAYLOAD_SERIAL_PORT_INFO  *Serial;
  CONST FDT_PROPERTY                  *PropertyPtr;
  INT32                               TempLen;
  UINT32                              *Data32;
  UINT32                              Value32;

  //
  // Create SerialPortInfo HOB.
  //
  Serial = BuildGuidHob (&gUniversalPayloadSerialPortInfoGuid, sizeof (UNIVERSAL_PAYLOAD_SERIAL_PORT_INFO));
  ASSERT (Serial != NULL);
  if (Serial == NULL) {
    return;
  }

  Serial->Header.Revision = UNIVERSAL_PAYLOAD_SERIAL_PORT_INFO_REVISION;
  Serial->Header.Length   = sizeof (UNIVERSAL_PAYLOAD_SERIAL_PORT_INFO);
  Serial->RegisterStride  = 1;
  Serial->UseMmio         = TRUE;

  PropertyPtr = FdtGetProperty (Fdt, SubNode, "current-speed", &TempLen);
  ASSERT (TempLen > 0);
  if (TempLen > 0) {
    Data32           = (UINT32 *)(PropertyPtr->Data);
    Serial->BaudRate = Fdt32ToCpu (*Data32);
  }

  PropertyPtr = FdtGetProperty (Fdt, SubNode, "reg-shift", &TempLen);
  if (TempLen > 0) {
    Data32                 = (UINT32 *)(PropertyPtr->Data);
    Serial->RegisterStride = (UINT8)(1 << Fdt32ToCpu (*Data32));
  }

  PropertyPtr = FdtGetProperty (Fdt, SubNode, "reg", &TempLen);
  ASSERT (TempLen > 0);
  if (TempLen > 0) {
    Data32  = (UINT32 *)(PropertyPtr->Data);
    Value32 = Fdt32ToCpu (Data32[0]);
    switch (AddressCells) {
      case 1:
        Serial->RegisterBase = Value32;
        if (Value32 < SIZE_64KB) {
          Serial->UseMmio = FALSE;
        }

        break;
      case 2:
        Serial->RegisterBase = Fdt32ToCpu (Data32[1]);
        if (Value32 == 1) {
          // IO type for Legacy serial
          Serial->UseMmio = FALSE;
        } else {
          Serial->RegisterBase |= LShiftU64 (Value32, 32);
        }

        break;
      case 3:
        // First U32 format: npt000ss bbbbbbbb dddddfff rrrrrrrr
        if ((Value32 & 0x03000000) == 0x01000000) {
          Serial->UseMmio = FALSE;
        }

        Serial->RegisterBase = LShiftU64 ((UINT64)Fdt32ToCpu (Data32[1]), 32) | Fdt32ToCpu (Data32[2]);
        break;
      default:
        DEBUG ((DEBUG_INFO, "ERROR: not supported address cells %d\n", AddressCells));
        break;
    }
  }

  DEBUG ((DEBUG_INFO, "Serial->UseMmio        = %x\n", Serial->UseMmio));
  DEBUG ((DEBUG_INFO, "Serial->RegisterBase   = 0x%x\n", Serial->RegisterBase));
  DEBUG ((DEBUG_INFO, "Serial->BaudRate       = %d\n", Serial->BaudRate));
  DEBUG ((DEBUG_INFO, "Serial->RegisterStride = %x\n", Serial->RegisterStride));
}

/**
  It will ParsePciRootBridge node from FDT.

  @param[in]  Fdt               Address of the Fdt data.
  @param[in]  Node              first node of the Fdt data.
  @param[in]  PciEnumDone       To use ParsePciRootBridge node.
  @param[in]  RootBridgeCount   Number of pci RootBridge.
  @param[in]  GmaStr            Graphic device node name string.
  @param[in]  index             Index of ParsePciRootBridge node.
**/
VOID
ParsePciRootBridge (
  IN VOID   *Fdt,
  IN INT32  Node,
  IN UINT8  RootBridgeCount,
  IN CHAR8  *GmaStr,
  IN UINT8  *index
  )
{
  INT32               SubNode;
  INT32               Property;
  INT32               SSubNode;
  FDT_NODE_HEADER     *NodePtr;
  CONST FDT_PROPERTY  *PropertyPtr;
  INT32               TempLen;
  UINT32              *Data32;
  UINT32              MemType;
  CONST CHAR8         *TempStr;
  UINT8               RbIndex;
  UINTN               HobDataSize;
  UINT8               Base;
  UINT32              AddressCells;

  if (RootBridgeCount == 0) {
    return;
  }

  RbIndex     = *index;
  HobDataSize = sizeof (UNIVERSAL_PAYLOAD_PCI_ROOT_BRIDGES) + (RootBridgeCount * sizeof (UNIVERSAL_PAYLOAD_PCI_ROOT_BRIDGE));
  //
  // Create PCI Root Bridge Info Hob.
  //
  if (mPciRootBridgeInfo == NULL) {
    mPciRootBridgeInfo = BuildGuidHob (&gUniversalPayloadPciRootBridgeInfoGuid, HobDataSize);
    ASSERT (mPciRootBridgeInfo != NULL);
    if (mPciRootBridgeInfo == NULL) {
      return;
    }

    ZeroMem (mPciRootBridgeInfo, HobDataSize);
    mPciRootBridgeInfo->Header.Length    = (UINT16)HobDataSize;
    mPciRootBridgeInfo->Header.Revision  = UNIVERSAL_PAYLOAD_PCI_ROOT_BRIDGES_REVISION;
    mPciRootBridgeInfo->Count            = RootBridgeCount;
    mPciRootBridgeInfo->ResourceAssigned = FALSE;
  }

  if (mUplPciSegmentInfoHob == NULL) {
    HobDataSize           = sizeof (UPL_PCI_SEGMENT_INFO_HOB) + ((RootBridgeCount) * sizeof (UPL_SEGMENT_INFO));
    mUplPciSegmentInfoHob = BuildGuidHob (&gUplPciSegmentInfoHobGuid, HobDataSize);
    if (mUplPciSegmentInfoHob != NULL) {
      ZeroMem (mUplPciSegmentInfoHob, HobDataSize);
      mUplPciSegmentInfoHob->Header.Revision = UNIVERSAL_PAYLOAD_PCI_SEGMENT_INFO_REVISION;
      mUplPciSegmentInfoHob->Header.Length   = (UINT16)HobDataSize;
      mUplPciSegmentInfoHob->Count           = RootBridgeCount;
    }
  }

  AddressCells = 3;
  PropertyPtr  = FdtGetProperty (Fdt, Node, "#address-cells", &TempLen);
  if ((PropertyPtr != NULL) && (TempLen > 0)) {
    AddressCells = Fdt32ToCpu (*(UINT32 *)PropertyPtr->Data);
  }

  for (SubNode = FdtFirstSubnode (Fdt, Node); SubNode >= 0; SubNode = FdtNextSubnode (Fdt, SubNode)) {
    NodePtr = (FDT_NODE_HEADER *)((CONST CHAR8 *)Fdt + SubNode + Fdt32ToCpu (((FDT_HEADER *)Fdt)->OffsetDtStruct));
    DEBUG ((DEBUG_INFO, "\n      SubNode(%08X)  %a", SubNode, NodePtr->Name));

    if (AsciiStrnCmp (NodePtr->Name, GmaStr, AsciiStrLen (GmaStr)) == 0) {
      DEBUG ((DEBUG_INFO, "  Found gma@ node \n"));
      ParsegraphicNode (Fdt, SubNode);
    }

    if (AsciiStrnCmp (NodePtr->Name, "isa", AsciiStrLen ("isa")) == 0) {
      PropertyPtr = FdtGetProperty (Fdt, SubNode, "#address-cells", &TempLen);
      if ((PropertyPtr != NULL) && (TempLen > 0)) {
        AddressCells = Fdt32ToCpu (*(UINT32 *)PropertyPtr->Data);
      }

      SSubNode = FdtFirstSubnode (Fdt, SubNode);
      ParseSerialPort (Fdt, SSubNode, AddressCells);
    }

    if (AsciiStrnCmp (NodePtr->Name, "serial@", AsciiStrLen ("serial@")) == 0) {
      ParseSerialPort (Fdt, SubNode, AddressCells);
    }
  }

  for (Property = FdtFirstPropertyOffset (Fdt, Node); Property >= 0; Property = FdtNextPropertyOffset (Fdt, Property)) {
    PropertyPtr = FdtGetPropertyByOffset (Fdt, Property, &TempLen);
    TempStr     = FdtGetString (Fdt, Fdt32ToCpu (PropertyPtr->NameOffset), NULL);

    if (AsciiStrCmp (TempStr, "ranges") == 0) {
      DEBUG ((DEBUG_INFO, "  Found ranges Property TempLen (%08X), limit %x\n", TempLen, TempLen / sizeof (UINT32)));

      mPciRootBridgeInfo->RootBridge[RbIndex].AllocationAttributes = EFI_PCI_HOST_BRIDGE_COMBINE_MEM_PMEM | EFI_PCI_HOST_BRIDGE_MEM64_DECODE;
      mPciRootBridgeInfo->RootBridge[RbIndex].Supports             = ROOT_BRIDGE_SUPPORTS_DEFAULT;
      mPciRootBridgeInfo->RootBridge[RbIndex].PMemAbove4G.Base     = PcdGet64 (PcdPciReservedPMemAbove4GBBase);
      mPciRootBridgeInfo->RootBridge[RbIndex].PMemAbove4G.Limit    = PcdGet64 (PcdPciReservedPMemAbove4GBLimit);
      mPciRootBridgeInfo->RootBridge[RbIndex].PMem.Base            = PcdGet32 (PcdPciReservedPMemBase);
      mPciRootBridgeInfo->RootBridge[RbIndex].PMem.Limit           = PcdGet32 (PcdPciReservedPMemLimit);
      mPciRootBridgeInfo->RootBridge[RbIndex].UID                  = RbIndex;
      mPciRootBridgeInfo->RootBridge[RbIndex].HID                  = EISA_PNP_ID (0x0A03);

      Data32 = (UINT32 *)(PropertyPtr->Data);
      for (Base = 0; Base < TempLen / sizeof (UINT32); Base = Base + DWORDS_TO_NEXT_ADDR_TYPE) {
        DEBUG ((DEBUG_INFO, "  Base :%x \n", Base));
        MemType = Fdt32ToCpu (*(Data32 + Base));
        if (((MemType) & (SS_64BIT_MEMORY_SPACE)) == SS_64BIT_MEMORY_SPACE) {
          mPciRootBridgeInfo->RootBridge[RbIndex].MemAbove4G.Base  = Fdt32ToCpu (*(Data32 + Base + 2)) + LShiftU64 (Fdt32ToCpu (*(Data32 + Base + 1)), 32);
          mPciRootBridgeInfo->RootBridge[RbIndex].MemAbove4G.Limit = mPciRootBridgeInfo->RootBridge[RbIndex].MemAbove4G.Base + LShiftU64 (Fdt32ToCpu (*(Data32 + Base + 5)), 32) + Fdt32ToCpu (*(Data32 + Base + 6)) - 1;
        } else if (((MemType) & (SS_32BIT_MEMORY_SPACE)) == SS_32BIT_MEMORY_SPACE) {
          mPciRootBridgeInfo->RootBridge[RbIndex].Mem.Base  = Fdt32ToCpu (*(Data32 + Base + 2));
          mPciRootBridgeInfo->RootBridge[RbIndex].Mem.Limit = mPciRootBridgeInfo->RootBridge[RbIndex].Mem.Base + Fdt32ToCpu (*(Data32 + Base + 6)) - 1;
        } else if (((MemType) & (SS_IO_SPACE)) == SS_IO_SPACE) {
          mPciRootBridgeInfo->RootBridge[RbIndex].Io.Base  = Fdt32ToCpu (*(Data32 + Base + 2));
          mPciRootBridgeInfo->RootBridge[RbIndex].Io.Limit = mPciRootBridgeInfo->RootBridge[RbIndex].Io.Base + Fdt32ToCpu (*(Data32 + Base + 6)) - 1;
        }
      }

      DEBUG ((DEBUG_INFO, "RootBridgeCount %x, index :%x\n", RootBridgeCount, RbIndex));

      DEBUG ((DEBUG_INFO, "PciRootBridge->Mem.Base %x, \n", mPciRootBridgeInfo->RootBridge[RbIndex].Mem.Base));
      DEBUG ((DEBUG_INFO, "PciRootBridge->Mem.limit %x, \n", mPciRootBridgeInfo->RootBridge[RbIndex].Mem.Limit));

      DEBUG ((DEBUG_INFO, "PciRootBridge->MemAbove4G.Base %llx, \n", mPciRootBridgeInfo->RootBridge[RbIndex].MemAbove4G.Base));
      DEBUG ((DEBUG_INFO, "PciRootBridge->MemAbove4G.limit %llx, \n", mPciRootBridgeInfo->RootBridge[RbIndex].MemAbove4G.Limit));

      DEBUG ((DEBUG_INFO, "PciRootBridge->Io.Base %llx, \n", mPciRootBridgeInfo->RootBridge[RbIndex].Io.Base));
      DEBUG ((DEBUG_INFO, "PciRootBridge->Io.limit %llx, \n", mPciRootBridgeInfo->RootBridge[RbIndex].Io.Limit));
    }

    if (AsciiStrCmp (TempStr, "reg") == 0) {
      UINT64  *Data64 = (UINT64 *)(PropertyPtr->Data);
      mUplPciSegmentInfoHob->SegmentInfo[RbIndex].BaseAddress = Fdt64ToCpu (ReadUnaligned64 (Data64));
      DEBUG ((DEBUG_INFO, "PciRootBridge->Ecam.Base %llx, \n", mUplPciSegmentInfoHob->SegmentInfo[RbIndex].BaseAddress));
    }

    if (AsciiStrCmp (TempStr, "bus-range") == 0) {
      Data32                                                  = (UINT32 *)(PropertyPtr->Data);
      mPciRootBridgeInfo->RootBridge[RbIndex].Bus.Base        = Fdt32ToCpu (*Data32) & 0xFF;
      mPciRootBridgeInfo->RootBridge[RbIndex].Bus.Limit       = Fdt32ToCpu (*(Data32 + 1)) & 0xFF;
      mPciRootBridgeInfo->RootBridge[RbIndex].Bus.Translation = 0;

      DEBUG ((DEBUG_INFO, "PciRootBridge->Bus.Base %x, index %x\n", mPciRootBridgeInfo->RootBridge[RbIndex].Bus.Base, RbIndex));
      DEBUG ((DEBUG_INFO, "PciRootBridge->Bus.limit %x, index %x\n", mPciRootBridgeInfo->RootBridge[RbIndex].Bus.Limit, RbIndex));
    }
  }

  if (RbIndex > 0) {
    RbIndex--;
  }

  *index = RbIndex;
}

/**
  It will parse FDT based on DTB from bootloaders.

  @param[in]  FdtBase               Address of the Fdt data.

  @return   The address to the new hob list
**/
UINTN
EFIAPI
ParseDtb (
  IN VOID  *FdtBase
  )
{
  VOID                  *Fdt;
  INT32                 Node;
  INT32                 Property;
  INT32                 Depth;
  FDT_NODE_HEADER       *NodePtr;
  CONST FDT_PROPERTY    *PropertyPtr;
  CONST CHAR8           *TempStr;
  INT32                 TempLen;
  UINT64                *Data64;
  UINT64                StartAddress;
  UINT64                NumberOfBytes;
  UINTN                 MinimalNeededSize;
  EFI_PHYSICAL_ADDRESS  FreeMemoryBottom;
  EFI_PHYSICAL_ADDRESS  FreeMemoryTop;
  EFI_PHYSICAL_ADDRESS  MemoryBottom;
  EFI_PHYSICAL_ADDRESS  MemoryTop;
  BOOLEAN               IsHobConstructed;
  UINTN                 NewHobList;
  UINT8                 RootBridgeCount;
  UINT8                 index;
  UINT8                 PciEnumDone;
  UINT8                 NodeType;
  EFI_BOOT_MODE         BootMode;
  CHAR8                 *GmaStr;
  INTN                  NumRsv;
  EFI_PHYSICAL_ADDRESS  Addr;
  UINT64                Size;
  UINT16                SegmentNumber;
  UINT64                CurrentPciBaseAddress;
  UINT64                NextPciBaseAddress;
  UINT8                 *RbSegNumAlreadyAssigned;
  UINT8                 NumberOfRbSegNumAlreadyAssigned;
  UINT32                RootAddressCells;

  Fdt               = FdtBase;
  Depth             = 0;
  MinimalNeededSize = FixedPcdGet32 (PcdSystemMemoryUefiRegionSize);
  IsHobConstructed  = FALSE;
  NewHobList        = 0;
  RootBridgeCount   = 0;
  index             = 0;
  // TODO: This value comes from FDT. Currently there is a bug in implementation
  // which assumes node ordering. Which requires a fix.
  PciEnumDone      = 1;
  BootMode         = 0;
  NodeType         = 0;
  RootAddressCells = 2;

  DEBUG ((DEBUG_INFO, "FDT = 0x%x  %x\n", Fdt, Fdt32ToCpu (*((UINT32 *)Fdt))));
  DEBUG ((DEBUG_INFO, "Start parsing DTB data\n"));
  DEBUG ((DEBUG_INFO, "MinimalNeededSize :%x\n", MinimalNeededSize));

  PropertyPtr = FdtGetProperty (Fdt, 0, "#address-cells", &TempLen);
  if ((PropertyPtr != NULL) && (TempLen > 0)) {
    RootAddressCells = Fdt32ToCpu (*(UINT32 *)PropertyPtr->Data);
    DEBUG ((DEBUG_INFO, " root #address-cells = 0x%x\n", RootAddressCells));
  }

  for (Node = FdtNextNode (Fdt, 0, &Depth); Node >= 0; Node = FdtNextNode (Fdt, Node, &Depth)) {
    NodePtr = (FDT_NODE_HEADER *)((CONST CHAR8 *)Fdt + Node + Fdt32ToCpu (((FDT_HEADER *)Fdt)->OffsetDtStruct));
    DEBUG ((DEBUG_INFO, "\n   Node(%08x)  %a   Depth %x\n", Node, NodePtr->Name, Depth));
    // memory node
    if (AsciiStrnCmp (NodePtr->Name, "memory@", AsciiStrLen ("memory@")) == 0) {
      for (Property = FdtFirstPropertyOffset (Fdt, Node); Property >= 0; Property = FdtNextPropertyOffset (Fdt, Property)) {
        PropertyPtr = FdtGetPropertyByOffset (Fdt, Property, &TempLen);
        TempStr     = FdtGetString (Fdt, Fdt32ToCpu (PropertyPtr->NameOffset), NULL);
        if (AsciiStrCmp (TempStr, "reg") == 0) {
          Data64        = (UINT64 *)(PropertyPtr->Data);
          StartAddress  = Fdt64ToCpu (ReadUnaligned64 (Data64));
          NumberOfBytes = Fdt64ToCpu (ReadUnaligned64 (Data64 + 1));
          DEBUG ((DEBUG_INFO, "\n         Property(%08X)  %a", Property, TempStr));
          DEBUG ((DEBUG_INFO, "  %016lX  %016lX", StartAddress, NumberOfBytes));
          if (!IsHobConstructed) {
            if ((NumberOfBytes > MinimalNeededSize) && (StartAddress < BASE_4GB)) {
              MemoryBottom     = StartAddress + NumberOfBytes - MinimalNeededSize;
              FreeMemoryBottom = MemoryBottom;
              FreeMemoryTop    = StartAddress + NumberOfBytes;
              MemoryTop        = FreeMemoryTop;

              DEBUG ((DEBUG_INFO, "MemoryBottom :0x%llx\n", MemoryBottom));
              DEBUG ((DEBUG_INFO, "FreeMemoryBottom :0x%llx\n", FreeMemoryBottom));
              DEBUG ((DEBUG_INFO, "FreeMemoryTop :0x%llx\n", FreeMemoryTop));
              DEBUG ((DEBUG_INFO, "MemoryTop :0x%llx\n", MemoryTop));
              mHobList         = HobConstructor ((VOID *)(UINTN)MemoryBottom, (VOID *)(UINTN)MemoryTop, (VOID *)(UINTN)FreeMemoryBottom, (VOID *)(UINTN)FreeMemoryTop);
              IsHobConstructed = TRUE;
              NewHobList       = (UINTN)mHobList;
              break;
            }
          }
        }
      }
    } else {
      PropertyPtr = FdtGetProperty (Fdt, Node, "compatible", &TempLen);
      if (PropertyPtr == NULL) {
        continue;
      }

      TempStr = (CHAR8 *)(PropertyPtr->Data);
      if (AsciiStrnCmp (TempStr, "pci-rb", AsciiStrLen ("pci-rb")) == 0) {
        RootBridgeCount++;
      }
    }
  }

  NumRsv = FdtGetNumberOfReserveMapEntries (Fdt);
  /* Look for an existing entry and add it to the efi mem map. */
  for (index = 0; index < NumRsv; index++) {
    if (FdtGetReserveMapEntry (Fdt, index, &Addr, &Size) != 0) {
      continue;
    }

    BuildMemoryAllocationHob (Addr, Size, EfiReservedMemoryType);
  }

  index = RootBridgeCount - 1;
  Depth = 0;
  for (Node = FdtNextNode (Fdt, 0, &Depth); Node >= 0; Node = FdtNextNode (Fdt, Node, &Depth)) {
    NodePtr = (FDT_NODE_HEADER *)((CONST CHAR8 *)Fdt + Node + Fdt32ToCpu (((FDT_HEADER *)Fdt)->OffsetDtStruct));
    DEBUG ((DEBUG_INFO, "\n   Node(%08x)  %a   Depth %x", Node, NodePtr->Name, Depth));

    NodeType = CheckNodeType (NodePtr->Name, Depth);
    DEBUG ((DEBUG_INFO, "NodeType :0x%x\n", NodeType));
    switch (NodeType) {
      case SerialPort:
        ParseSerialPort (Fdt, Node, RootAddressCells);
        break;
      case ReservedMemory:
        DEBUG ((DEBUG_INFO, "ParseReservedMemory\n"));
        ParseReservedMemory (Fdt, Node);
        break;
      case Memory:
        DEBUG ((DEBUG_INFO, "ParseMemory\n"));
        if (!CheckMemoryNodeIfInit (Node)) {
          ParseMemory (Fdt, Node);
        } else {
          DEBUG ((DEBUG_INFO, "Memory has initialized\n"));
        }

        break;
      case FrameBuffer:
        DEBUG ((DEBUG_INFO, "ParseFrameBuffer\n"));
        GmaStr = ParseFrameBuffer (Fdt, Node);
        break;
      case PciRootBridge:
        DEBUG ((DEBUG_INFO, "ParsePciRootBridge, index :%x \n", index));
        ParsePciRootBridge (Fdt, Node, RootBridgeCount, GmaStr, &index);
        DEBUG ((DEBUG_INFO, "After ParsePciRootBridge, index :%x\n", index));
        break;
      case Options:
        // FIXME: Need to ensure this node gets parsed first so that it gets
        // correct options to feed into other init like PciEnumDone etc.
        DEBUG ((DEBUG_INFO, "ParseOptions\n"));
        ParseOptions (Fdt, Node, &PciEnumDone, &BootMode);
        break;
      default:
        DEBUG ((DEBUG_INFO, "ParseNothing\n"));
        break;
    }
  }

  // Post processing: TODO: Need to look into it. Such cross dependency on DT nodes
  // may not be good idea. Instead have this prop part of RB
  mPciRootBridgeInfo->ResourceAssigned = (BOOLEAN)PciEnumDone;

  //
  // Assign PCI Segment number after all root bridge info ready
  //
  SegmentNumber                   = 0;
  RbSegNumAlreadyAssigned         = AllocateZeroPool (sizeof (UINT8) * RootBridgeCount);
  NextPciBaseAddress              = 0;
  NumberOfRbSegNumAlreadyAssigned = 0;

  //
  // Always assign first root bridge segment number as 0
  //
  CurrentPciBaseAddress                               = mUplPciSegmentInfoHob->SegmentInfo[0].BaseAddress & ~0xFFFFFFF;
  NextPciBaseAddress                                  = CurrentPciBaseAddress;
  mUplPciSegmentInfoHob->SegmentInfo[0].SegmentNumber = SegmentNumber;
  mPciRootBridgeInfo->RootBridge[0].Segment           = SegmentNumber;
  RbSegNumAlreadyAssigned[0]                          = 1;
  NumberOfRbSegNumAlreadyAssigned++;

  while (NumberOfRbSegNumAlreadyAssigned < RootBridgeCount) {
    for (index = 1; index < RootBridgeCount; index++) {
      if (RbSegNumAlreadyAssigned[index] == 1) {
        continue;
      }

      if (CurrentPciBaseAddress == (mUplPciSegmentInfoHob->SegmentInfo[index].BaseAddress & ~0xFFFFFFF)) {
        mUplPciSegmentInfoHob->SegmentInfo[index].SegmentNumber = SegmentNumber;
        mPciRootBridgeInfo->RootBridge[index].Segment           = SegmentNumber;
        RbSegNumAlreadyAssigned[index]                          = 1;
        NumberOfRbSegNumAlreadyAssigned++;
      } else if (CurrentPciBaseAddress == NextPciBaseAddress) {
        NextPciBaseAddress = mUplPciSegmentInfoHob->SegmentInfo[index].BaseAddress & ~0xFFFFFFF;
      }
    }

    SegmentNumber++;
    CurrentPciBaseAddress = NextPciBaseAddress;
  }

  ((EFI_HOB_HANDOFF_INFO_TABLE *)(mHobList))->BootMode = BootMode;
  DEBUG ((DEBUG_INFO, "\n"));

  return NewHobList;
}

/**
  It will Parse FDT -node based on information from bootloaders.

  @param[in]  FdtBase   The starting memory address of FdtBase

  @retval HobList   The base address of Hoblist.
**/
UINTN
EFIAPI
FdtNodeParser (
  IN VOID  *FdtBase
  )
{
  return ParseDtb (FdtBase);
}

/**
  It will initialize HOBs for UPL.

  @param[in]  FdtBase        Address of the Fdt data.

  @retval EFI_SUCCESS        If it completed successfully.
  @retval Others             If it failed to initialize HOBs.
**/
UINTN
EFIAPI
UplInitHob (
  IN VOID  *FdtBase
  )
{
  UINTN  NHobAddress;

  NHobAddress = 0;
  //
  // Check parameter type
  //
  if (FdtCheckHeader (FdtBase) == 0) {
    DEBUG ((DEBUG_INFO, "%a() FDT blob\n", __func__));
    NHobAddress = FdtNodeParser ((VOID *)FdtBase);
  } else {
    DEBUG ((DEBUG_INFO, "%a() HOb list\n", __func__));
    mHobList = FdtBase;

    return (UINTN)(mHobList);
  }

  return NHobAddress;
}
