/** @file

  @copyright
  Copyright 1999 - 2021 Intel Corporation. <BR>

  SPDX-License-Identifier: BSD-2-Clause-Patent
**/

#include <Guid/SocketPciResourceData.h>
#include <Guid/SocketIioVariable.h>
#include "PciHostBridge.h"
#include "PciRootBridge.h"
#include "PciHostResource.h"

#include <CpuAndRevisionDefines.h>
#include <Library/UefiRuntimeServicesTableLib.h>
#include <Protocol/IioUds.h>
#include <Upi/KtiHost.h>

#include <Library/SetupLib.h>

#include "PciRebalance.h"


/******************************************************************************
 * Local definitions.
 ******************************************************************************/
typedef struct {
  PCI_ROOT_BRIDGE_INSTANCE        RootBridge;
  EFI_PCI_ROOT_BRIDGE_DEVICE_PATH RootDevPath;
} PCI_ROOT_BRIDGE_WITH_PATH;


/******************************************************************************
 * Variables.
 ******************************************************************************/
GLOBAL_REMOVE_IF_UNREFERENCED CHAR16 *mAcpiAddressSpaceTypeStr[] = {
  L"Mem", L"I/O", L"Bus"
};
GLOBAL_REMOVE_IF_UNREFERENCED CHAR16 *mPciResourceTypeStr[] = {
  L"I/O", L"Mem32", L"PMem32", L"Mem64", L"PMem64", L"Bus"
};

//
// We can hardcode the following for a Simple IIO -
// Root Bridge Count within the Host bridge
// Root Bridge's device path
// Root Bridge's resource appeture
//

STATIC EFI_PCI_ROOT_BRIDGE_DEVICE_PATH    mEfiPciRootBridgeDevicePathTemplate = {
      {{ACPI_DEVICE_PATH,
      ACPI_DP,
      {(UINT8) (sizeof (ACPI_HID_DEVICE_PATH)),
      (UINT8) ((sizeof (ACPI_HID_DEVICE_PATH)) >> 8)}},
      EISA_PNP_ID(0x0A03),
      0},
      {END_DEVICE_PATH_TYPE,
      END_ENTIRE_DEVICE_PATH_SUBTYPE,
      {END_DEVICE_PATH_LENGTH,
      0}}
    };

STATIC EFI_HANDLE                         mDriverImageHandle;

EDKII_IOMMU_PROTOCOL                     *mIoMmu;
EFI_EVENT                                 mIoMmuEvent;
VOID                                     *mIoMmuRegistration;

EFI_IIO_UDS_PROTOCOL                     *mIioUds;
PCI_HOST_BRIDGE_INSTANCE                 *mHostBridge;

/**
  Event notification that is fired when IOMMU protocol is installed.

  @param  Event                 The Event that is being processed.
  @param  Context               Event Context.

**/
VOID
EFIAPI
IoMmuProtocolCallback (
  IN  EFI_EVENT       Event,
  IN  VOID            *Context
  )
{
  EFI_STATUS   Status;

  Status = gBS->LocateProtocol (&gEdkiiIoMmuProtocolGuid, NULL, (VOID **) &mIoMmu);
  if (!EFI_ERROR(Status)) {
    gBS->CloseEvent (mIoMmuEvent);
  }
}


/**

  Entry point of this driver.

  @param ImageHandle  Image handle of this driver.
  @param SystemTable  Pointer to standard EFI system table.

  @retval EFI_SUCCESS       Succeed.
  @retval EFI_DEVICE_ERROR  Fail to install PCI_ROOT_BRIDGE_IO protocol.

**/
EFI_STATUS
EFIAPI
InitializePciHostBridge (
  IN EFI_HANDLE         ImageHandle,
  IN EFI_SYSTEM_TABLE   *SystemTable
  )
{
  EFI_STATUS                  Status;
  UINTN                       IioIndex;
  EFI_PHYSICAL_ADDRESS        BaseAddress;
  UINTN                       IioStack;
  UINT64                      ResourceLength;
  PCI_ROOT_BRIDGE_RESOURCE_APERTURE  RbAperture;

  PCI_HOST_BRIDGE_INSTANCE          *HostBridge;
  PCI_ROOT_BRIDGE_INSTANCE          *RootBridge;
  EFI_PCI_ROOT_BRIDGE_DEVICE_PATH   *TempDevicePath;
  STACK_RES                         *CurStackRes;

  mDriverImageHandle = ImageHandle;
  //
  // Located the IIO Protocol Interface
  //
  Status = gBS->LocateProtocol (&gEfiIioUdsProtocolGuid, NULL, (VOID **) &mIioUds);
  ASSERT_EFI_ERROR (Status);

  //
  // Most systems in the world including complex servers have only one Host Bridge.
  //
  HostBridge = AllocateZeroPool (sizeof (PCI_HOST_BRIDGE_INSTANCE));
  if (HostBridge == NULL) {
    ASSERT(FALSE);
    return EFI_OUT_OF_RESOURCES;
  }
  HostBridge->Signature         = PCI_HOST_BRIDGE_SIGNATURE;
  HostBridge->RootBridgeCount   = 0;
  HostBridge->ResourceSubmited  = FALSE;
  HostBridge->CanRestarted      = TRUE;
  InitializeListHead (&HostBridge->RootBridges);

  HostBridge->ResAlloc.NotifyPhase          = NotifyPhase;
  HostBridge->ResAlloc.GetNextRootBridge    = GetNextRootBridge;
  HostBridge->ResAlloc.GetAllocAttributes   = GetAttributes;
  HostBridge->ResAlloc.StartBusEnumeration  = StartBusEnumeration;
  HostBridge->ResAlloc.SetBusNumbers        = SetBusNumbers;
  HostBridge->ResAlloc.SubmitResources      = SubmitResources;
  HostBridge->ResAlloc.GetProposedResources = GetProposedResources;
  HostBridge->ResAlloc.PreprocessController = PreprocessController;

  Status = gBS->InstallProtocolInterface (
                    &HostBridge->Handle,
                    &gEfiPciHostBridgeResourceAllocationProtocolGuid,
                    EFI_NATIVE_INTERFACE,
                    &HostBridge->ResAlloc
                    );
  if (EFI_ERROR (Status)) {
    FreePool (HostBridge);
    return EFI_DEVICE_ERROR;
  }

  //
  // Create Root Bridges in this Host Bridge.
  //
  for (IioIndex = 0; IioIndex < mIioUds->IioUdsPtr->PlatformData.numofIIO; IioIndex++) {

    if (!mIioUds->IioUdsPtr->PlatformData.IIO_resource[IioIndex].Valid) {
      continue;
    } // No guarantee that the valid IIOs are sequential starting at 0!

    for (IioStack = 0; IioStack < MAX_IIO_STACK; IioStack++) {

      if (!(mIioUds->IioUdsPtr->PlatformData.CpuQpiInfo[IioIndex].stackPresentBitmap & (1 << IioStack))) {
        continue;
      }
      // Skip stack which is disabled by resource limitation or DEBUG purpose!
      if (mIioUds->IioUdsPtr->PlatformData.IIO_resource[IioIndex].StackRes[IioStack].Personality >= TYPE_DISABLED) {
        continue;
      }
      //
      // Create Root Bridge Instance
      //
      RootBridge = AllocateZeroPool (sizeof(PCI_ROOT_BRIDGE_WITH_PATH));
      if (RootBridge == NULL) {
        ASSERT_EFI_ERROR (EFI_OUT_OF_RESOURCES);
        return EFI_OUT_OF_RESOURCES;
      }
      TempDevicePath = &((PCI_ROOT_BRIDGE_WITH_PATH*)RootBridge)->RootDevPath;

      RootBridge->Signature  = PCI_ROOT_BRIDGE_SIGNATURE;
      RootBridge->DevicePath = (EFI_DEVICE_PATH_PROTOCOL*)TempDevicePath;
      CopyMem (TempDevicePath, &mEfiPciRootBridgeDevicePathTemplate, sizeof(*TempDevicePath));
      TempDevicePath->AcpiDevicePath.UID = (UINT32)(IioIndex * MAX_IIO_STACK + IioStack);

      ZeroMem(&RbAperture, sizeof(RbAperture));
      {
        //
        // Update Root Bridge with UDS resource information
        //
        RbAperture.BusBase = mIioUds->IioUdsPtr->PlatformData.IIO_resource[IioIndex].StackRes[IioStack].BusBase;
        RbAperture.BusLimit = mIioUds->IioUdsPtr->PlatformData.IIO_resource[IioIndex].StackRes[IioStack].BusLimit;
        RbAperture.Mem32Base = mIioUds->IioUdsPtr->PlatformData.IIO_resource[IioIndex].StackRes[IioStack].PciResourceMem32Base;
        RbAperture.Mem32Limit = mIioUds->IioUdsPtr->PlatformData.IIO_resource[IioIndex].StackRes[IioStack].PciResourceMem32Limit;
        RbAperture.BusReserve = 0;
        if ((mIioUds->IioUdsPtr->PlatformData.IIO_resource[IioIndex].StackRes[IioStack].PciResourceMem64Limit >
             mIioUds->IioUdsPtr->PlatformData.IIO_resource[IioIndex].StackRes[IioStack].PciResourceMem64Base) &&
            (mIioUds->IioUdsPtr->PlatformData.Pci64BitResourceAllocation)) {
          RbAperture.Mem64Base = mIioUds->IioUdsPtr->PlatformData.IIO_resource[IioIndex].StackRes[IioStack].PciResourceMem64Base;
          RbAperture.Mem64Limit = mIioUds->IioUdsPtr->PlatformData.IIO_resource[IioIndex].StackRes[IioStack].PciResourceMem64Limit;
        }
        RbAperture.IoBase = mIioUds->IioUdsPtr->PlatformData.IIO_resource[IioIndex].StackRes[IioStack].PciResourceIoBase;
        RbAperture.IoLimit = mIioUds->IioUdsPtr->PlatformData.IIO_resource[IioIndex].StackRes[IioStack].PciResourceIoLimit;

        SimpleIioRootBridgeConstructor (&RootBridge->RootBridgeIo, HostBridge->Handle, &RbAperture,
                                        mIioUds->IioUdsPtr->PlatformData.CpuQpiInfo[IioIndex].PcieSegment,
                                        GetAllocAttributes (HostBridge->RootBridgeCount));
        DEBUG ((DEBUG_INFO, "[PCI] Create Root Bridge for [%d.%d] in PCI segment %d with below resource:\n", IioIndex, IioStack, RootBridge->RootBridgeIo.SegmentNumber));
        DEBUG ((DEBUG_INFO, "      Bus:   Base = 0x%08x, Limit = 0x%08x, BusReserve = 0x%08x\n", RootBridge->Aperture.BusBase, RootBridge->Aperture.BusLimit, RootBridge->Aperture.BusReserve));
        DEBUG ((DEBUG_INFO, "      IO:    Base = 0x%08x, Limit = 0x%08x\n", RootBridge->Aperture.IoBase, RootBridge->Aperture.IoLimit));
        DEBUG ((DEBUG_INFO, "      Mem32: Base = 0x%08x, Limit = 0x%08x\n", RootBridge->Aperture.Mem32Base, RootBridge->Aperture.Mem32Limit));
        DEBUG ((DEBUG_INFO, "      Mem64: Base = 0x%lx, Limit = 0x%lx\n", RootBridge->Aperture.Mem64Base, RootBridge->Aperture.Mem64Limit));
        //
        // Insert Root Bridge Instance
        //
        Status = gBS->InstallMultipleProtocolInterfaces (
                        &RootBridge->Handle,
                        &gEfiDevicePathProtocolGuid,
                        RootBridge->DevicePath,
                        &gEfiPciRootBridgeIoProtocolGuid,
                        &RootBridge->RootBridgeIo,
                        NULL
                        );
        ASSERT_EFI_ERROR (Status);
        InsertTailList (&HostBridge->RootBridges, &RootBridge->Link);
        mPciRootBridgeTable[IioIndex][IioStack] = RootBridge;
        HostBridge->RootBridgeCount++;
      }
    } // for (IioStack...)
  } // for (IioIndex...)
  //
  // Add PCIE base into Runtime memory so that it can be reported in E820 table
  //
  Status = gDS->AddMemorySpace (
                  EfiGcdMemoryTypeMemoryMappedIo,
                  mIioUds->IioUdsPtr->PlatformData.PciExpressBase,
                  mIioUds->IioUdsPtr->PlatformData.PciExpressSize,
                  EFI_MEMORY_RUNTIME | EFI_MEMORY_UC
                  );
  ASSERT_EFI_ERROR(Status);

  BaseAddress = mIioUds->IioUdsPtr->PlatformData.PciExpressBase;

  Status = gDS->AllocateMemorySpace (
                  EfiGcdAllocateAddress,
                  EfiGcdMemoryTypeMemoryMappedIo,
                  0,
                  mIioUds->IioUdsPtr->PlatformData.PciExpressSize,
                  &BaseAddress,
                  ImageHandle,
                  NULL
                  );
  ASSERT_EFI_ERROR(Status);

  Status = gDS->SetMemorySpaceAttributes (
                  mIioUds->IioUdsPtr->PlatformData.PciExpressBase,
                  mIioUds->IioUdsPtr->PlatformData.PciExpressSize,
                  EFI_MEMORY_RUNTIME
                  );
  ASSERT_EFI_ERROR (Status);

  //s4030180
  for (IioIndex = 0; IioIndex < mIioUds->IioUdsPtr->PlatformData.numofIIO; IioIndex++) {

    if (!mIioUds->IioUdsPtr->PlatformData.IIO_resource[IioIndex].Valid) {
      continue;
    } // No guarantee that the valid IIOs are sequential starting at 0!
    for (IioStack = 0; IioStack < MAX_IIO_STACK; IioStack++) {
      if (!(mIioUds->IioUdsPtr->PlatformData.CpuQpiInfo[IioIndex].stackPresentBitmap & (1 << IioStack))) {
        continue;
      }

      if (mIioUds->IioUdsPtr->PlatformData.IIO_resource[IioIndex].StackRes[IioStack].PciResourceIoLimit >
           mIioUds->IioUdsPtr->PlatformData.IIO_resource[IioIndex].StackRes[IioStack].PciResourceIoBase) {
        //
        // At present, we use up the first 4k for fixed ranges like
        // ICH GPIO, ACPI and ISA devices. The first 4k is not
        // tracked through GCD. It should be.
        //
        Status = gDS->AddIoSpace (
                        EfiGcdIoTypeIo,
                        mIioUds->IioUdsPtr->PlatformData.IIO_resource[IioIndex].StackRes[IioStack].PciResourceIoBase ,
                        (mIioUds->IioUdsPtr->PlatformData.IIO_resource[IioIndex].StackRes[IioStack].PciResourceIoLimit -
                         mIioUds->IioUdsPtr->PlatformData.IIO_resource[IioIndex].StackRes[IioStack].PciResourceIoBase + 1)
                        );
        ASSERT_EFI_ERROR (Status);
      }

      CurStackRes = &mIioUds->IioUdsPtr->PlatformData.IIO_resource[IioIndex].StackRes[IioStack];
      if (CurStackRes->PciResourceMem32Limit > CurStackRes->PciResourceMem32Base) {
        //
        // Shouldn't the capabilities be UC?
        //
        ResourceLength = CurStackRes->PciResourceMem32Limit - CurStackRes->PciResourceMem32Base + 1;
        Status = gDS->AddMemorySpace (
          EfiGcdMemoryTypeMemoryMappedIo,
          CurStackRes->PciResourceMem32Base,
          ResourceLength,
          0
          );
        ASSERT_EFI_ERROR (Status);
        //
        // Track VT-d bar in GCD.
        //
        // We now have a region of memory x-z associated with this stack
        // and there is also subregion y-z that is the VT-d space (x <= y <= z)
        BaseAddress = CurStackRes->VtdBarAddress;
        ResourceLength = CurStackRes->PciResourceMem32Limit - BaseAddress + 1;
        Status = gDS->AllocateMemorySpace (
          EfiGcdAllocateAddress,
          EfiGcdMemoryTypeMemoryMappedIo,
          0,
          ResourceLength,
          &BaseAddress,
          ImageHandle,
          NULL
          );
        ASSERT_EFI_ERROR(Status);
      }

      if ((mIioUds->IioUdsPtr->PlatformData.IIO_resource[IioIndex].StackRes[IioStack].PciResourceMem64Limit >
             mIioUds->IioUdsPtr->PlatformData.IIO_resource[IioIndex].StackRes[IioStack].PciResourceMem64Base) &&
             (mIioUds->IioUdsPtr->PlatformData.Pci64BitResourceAllocation)) {

        Status = gDS->AddMemorySpace (
                           EfiGcdMemoryTypeMemoryMappedIo,
                           mIioUds->IioUdsPtr->PlatformData.IIO_resource[IioIndex].StackRes[IioStack].PciResourceMem64Base ,
                           (mIioUds->IioUdsPtr->PlatformData.IIO_resource[IioIndex].StackRes[IioStack].PciResourceMem64Limit -
                            mIioUds->IioUdsPtr->PlatformData.IIO_resource[IioIndex].StackRes[IioStack].PciResourceMem64Base + 1),
                           0
                           );
        ASSERT_EFI_ERROR (Status);
      }
    }
  }

  if (!EFI_ERROR (Status)) {
    mIoMmuEvent = EfiCreateProtocolNotifyEvent (
                    &gEdkiiIoMmuProtocolGuid,
                    TPL_CALLBACK,
                    IoMmuProtocolCallback,
                    NULL,
                    &mIoMmuRegistration
                    );
  }
  mHostBridge = HostBridge;
  return EFI_SUCCESS;
}


/**
  Enter the EfiPciHostBridgeBeginEnumeration phase of the PCI(e)
  numeration process.

  @param This     The EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL
                  instance.

  @retval EFI_SUCCESS
  @retval EFI_NOT_READY
*/
STATIC EFI_STATUS
BeginBridgeEnumeration (
  IN EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL *This
  )
{
  PCI_HOST_BRIDGE_INSTANCE *HostBridge;
  LIST_ENTRY               *List;
  PCI_ROOT_BRIDGE_INSTANCE *RootBridge;
  PCI_RESOURCE_TYPE        Index;

  Index = TypeMax;
  HostBridge = PCI_HOST_BRIDGE_FROM_THIS (This);

  if (HostBridge->CanRestarted) {
    //
    // Reset Root Bridge
    //
    List = HostBridge->RootBridges.ForwardLink;

    while (List != &HostBridge->RootBridges) {
      RootBridge = ROOT_BRIDGE_FROM_LINK (List);
      for (Index = TypeIo; Index < TypeMax; Index++) {
        RootBridge->ResAllocNode[Index].Type    = Index;
        RootBridge->ResAllocNode[Index].Base    = 0;
        RootBridge->ResAllocNode[Index].Length  = 0;
        RootBridge->ResAllocNode[Index].Status  = ResNone;
      } // for

      List = List->ForwardLink;
    } // while

    HostBridge->ResourceSubmited  = FALSE;
    HostBridge->CanRestarted      = TRUE;
  } else {
    //
    // Can not restart
    //
    return EFI_NOT_READY;
  } // if

  return EFI_SUCCESS;
}


/**
  Enter the EfiPciHostBridgeAllocateResources phase of the
  PCI(e) numeration process.

  @param[in] This The EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL instance.

  @retval EFI_SUCCESS
  @retval EFI_NOT_READY
  @retval EFI_OUT_OF_RESOURCES
*/
STATIC EFI_STATUS
BridgeAllocateResources (
  IN EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL *This
  )
{
  PCI_HOST_BRIDGE_INSTANCE              *HostBridge;
  PCI_ROOT_BRIDGE_INSTANCE              *RootBridge;
  PCI_RESOURCE_TYPE                     Index;
  LIST_ENTRY                            *List;
  EFI_PHYSICAL_ADDRESS                  BaseAddress;
  UINT64                                AddrLen;
  UINTN                                 BitsOfAlignment;
  UINT64                                Alignment;
  EFI_STATUS                            Status;
  EFI_STATUS                            ReturnStatus;
  PCI_RESOURCE_TYPE                     Index1;
  PCI_RESOURCE_TYPE                     Index2;
  BOOLEAN                               ResNodeHandled[TypeMax];
  UINT64                                MaxAlignment;
  SOCKET_RESOURCE_ADJUSTMENT_RESULT     RatioAdjustResult;
  UINT8                                 Socket;
  UINT8                                 Stack;

  Index = TypeMax;
  HostBridge = PCI_HOST_BRIDGE_FROM_THIS (This);

  ReturnStatus = EFI_SUCCESS;
  if (HostBridge->ResourceSubmited) {

    List = HostBridge->RootBridges.ForwardLink;
    while (List != &HostBridge->RootBridges) {

      for (Index1 = TypeIo; Index1 < TypeBus; Index1++) {
        ResNodeHandled[Index1] = FALSE;
      }
      RootBridge = ROOT_BRIDGE_FROM_LINK (List);
      Socket = Stack = (UINT8)-1;
      PciRootBridge2SocketStack (RootBridge, &Socket, &Stack);
      for (Index1 = TypeIo; Index1 < TypeBus; Index1++) {

        if (RootBridge->ResAllocNode[Index1].Status == ResNone) {

          ResNodeHandled[Index1] = TRUE;

        } else {
          //
          // Allocate the resource node with max alignment at first
          //
          MaxAlignment = 0;
          Index        = TypeMax;
          for (Index2 = TypeIo; Index2 < TypeBus; Index2++) {

            if (ResNodeHandled[Index2]) {
              continue;
            }
            if (MaxAlignment <= RootBridge->ResAllocNode[Index2].Alignment) {
              MaxAlignment = RootBridge->ResAllocNode[Index2].Alignment;
              Index = Index2;
            }
          } // for

          if (Index < TypeMax) {

            ResNodeHandled[Index] = TRUE;
            Alignment = RootBridge->ResAllocNode[Index].Alignment;
            //
            // Get the number of '1' in Alignment.
            //
            for (BitsOfAlignment = 0; Alignment != 0; BitsOfAlignment++) {
              Alignment = RShiftU64 (Alignment, 1);
            }

            AddrLen   = RootBridge->ResAllocNode[Index].Length;
            Alignment = RootBridge->ResAllocNode[Index].Alignment;

            DEBUG ((DEBUG_INFO, "\n[%d.%d] Resource Type to assign :     %s\n", Socket, Stack, mPciResourceTypeStr[Index]));
            DEBUG ((DEBUG_INFO, "      Length to allocate:           %lx\n", RootBridge->ResAllocNode[Index].Length));
            DEBUG ((DEBUG_INFO, "      Alignment:                    %lx\n", Alignment));
            DEBUG ((DEBUG_INFO, "      Base Address:                 %lx\n", RootBridge->ResAllocNode[Index].Base));

            switch (Index) {
            case TypeIo:
              if (RootBridge->Aperture.IoBase < RootBridge->Aperture.IoLimit) {
                //
                // It is impossible for 0xFFFF Alignment for IO16
                //
                if (BitsOfAlignment >= 16) {
                  Alignment = 0;
                }
                BaseAddress = RootBridge->Aperture.IoBase;
                //
                // Have to make sure Alignment is handled seeing we are doing direct address allocation
                //
                if ((BaseAddress & ~(Alignment)) != BaseAddress) {
                  BaseAddress = ((BaseAddress + Alignment) & ~(Alignment));
                }

                while((BaseAddress + AddrLen) <= RootBridge->Aperture.IoLimit + 1) {
                  Status = gDS->AllocateIoSpace (EfiGcdAllocateAddress, EfiGcdIoTypeIo, BitsOfAlignment,
                    AddrLen, &BaseAddress, mDriverImageHandle, NULL );

                  if (!EFI_ERROR (Status)) {
                    RootBridge->ResAllocNode[Index].Base    = (UINT64) BaseAddress;
                    RootBridge->ResAllocNode[Index].Status  = ResAllocated;
                    goto TypeIoFound;
                  }

                  BaseAddress += (Alignment + 1);
                } // while
              } // if

              TypeIoFound:
              if (RootBridge->ResAllocNode[Index].Status  != ResAllocated) {
                //
                // No Room at the Inn for this resources request
                //
                ReturnStatus = EFI_OUT_OF_RESOURCES;
              } // if
              break;

              case TypeMem32:
                if (RootBridge->Aperture.Mem32Base < RootBridge->Aperture.Mem32Limit) {

                  BaseAddress = RootBridge->Aperture.Mem32Base;
                  //
                  // Have to make sure Alignment is handled seeing we are doing direct address allocation
                  //
                  if ((BaseAddress & ~(Alignment)) != BaseAddress) {
                    BaseAddress = ((BaseAddress + Alignment) & ~(Alignment));
                  }

                  while ((BaseAddress + AddrLen) <= RootBridge->Aperture.Mem32Limit + 1) {
                    DEBUG ((DEBUG_INFO, "      Attempting %s allocation at 0x%llX.....",
                            mPciResourceTypeStr[Index], BaseAddress));
                    Status = gDS->AllocateMemorySpace (EfiGcdAllocateAddress, EfiGcdMemoryTypeMemoryMappedIo,
                                                       BitsOfAlignment, AddrLen, &BaseAddress, mDriverImageHandle, NULL);
                    if (!EFI_ERROR (Status)) {
                      RootBridge->ResAllocNode[Index].Base    = (UINT64) BaseAddress;
                      RootBridge->ResAllocNode[Index].Status  = ResAllocated;
                      DEBUG ((DEBUG_INFO, "Passed\n"));
                      goto TypeMem32Found;
                    } // if
                    DEBUG ((DEBUG_INFO, "Failed\n"));
                    BaseAddress += (Alignment + 1);
                  } // while
                } // if

                TypeMem32Found:
                if (RootBridge->ResAllocNode[Index].Status  != ResAllocated) {
                  //
                  // No Room at the Inn for this resources request
                  //
                  ReturnStatus = EFI_OUT_OF_RESOURCES;
                }
                break;

              case TypePMem32:
                StartTypePMem32:
                if (RootBridge->Aperture.Mem32Base < RootBridge->Aperture.Mem32Limit) {
                  BaseAddress = RootBridge->Aperture.Mem32Limit + 1;
                  BaseAddress -= AddrLen;

                  //
                  // Have to make sure Alignment is handled seeing we are doing direct address allocation
                  //
                  if ((BaseAddress & ~(Alignment)) != BaseAddress) {
                    BaseAddress = ((BaseAddress) & ~(Alignment));
                  }

                  while (RootBridge->Aperture.Mem32Base <= BaseAddress) {

                    DEBUG ((DEBUG_INFO, "      Attempting %s allocation at 0x%llX.....",
                            mPciResourceTypeStr[Index], BaseAddress));
                    Status = gDS->AllocateMemorySpace (EfiGcdAllocateAddress, EfiGcdMemoryTypeMemoryMappedIo,
                                                       BitsOfAlignment, AddrLen, &BaseAddress, mDriverImageHandle, NULL);
                    if (!EFI_ERROR (Status)) {
                      RootBridge->ResAllocNode[Index].Base    = (UINT64) BaseAddress;
                      RootBridge->ResAllocNode[Index].Status  = ResAllocated;
                      DEBUG ((DEBUG_INFO, "Passed\n"));
                      goto TypePMem32Found;
                    }
                    DEBUG ((DEBUG_INFO, "Failed\n"));
                    BaseAddress -= (Alignment + 1);
                  } // while
                } // if

                TypePMem32Found:
                if (RootBridge->ResAllocNode[Index].Status  != ResAllocated) {
                  //
                  // No Room at the Inn for this resources request
                  //
                  ReturnStatus = EFI_OUT_OF_RESOURCES;
                }
                break;

              case TypeMem64:
              case TypePMem64:
                if ((RootBridge->Aperture.Mem64Limit > RootBridge->Aperture.Mem64Base) &&
                    (mIioUds->IioUdsPtr->PlatformData.Pci64BitResourceAllocation)) {

                  if (RootBridge->Aperture.Mem64Limit < AddrLen) {
                    RootBridge->ResAllocNode[Index].Status = ResNone;
                    goto TypeMem64Found;
                  }
                  BaseAddress = RootBridge->Aperture.Mem64Limit + 1;
                  BaseAddress -= AddrLen;

                  //
                  // Have to make sure Alignment is handled seeing we are doing direct address allocation
                  //
                  if ((BaseAddress & ~(Alignment)) != BaseAddress) {
                    BaseAddress = ((BaseAddress) & ~(Alignment));
                  }

                  while (RootBridge->Aperture.Mem64Base <= BaseAddress) {

                    DEBUG ((DEBUG_INFO, "      Attempting %s allocation at 0x%llX.....",
                            mPciResourceTypeStr[Index], BaseAddress));
                    Status = gDS->AllocateMemorySpace (EfiGcdAllocateAddress, EfiGcdMemoryTypeMemoryMappedIo,
                                                       BitsOfAlignment, AddrLen, &BaseAddress, mDriverImageHandle, NULL);
                    if (!EFI_ERROR (Status)) {
                      RootBridge->ResAllocNode[Index].Base    = (UINT64) BaseAddress;
                      RootBridge->ResAllocNode[Index].Status  = ResAllocated;
                      DEBUG ((DEBUG_INFO, "Passed\n"));
                      goto TypeMem64Found;
                    }
                    DEBUG ((DEBUG_INFO, "Failed\n"));
                    BaseAddress -= (Alignment + 1);
                  } // while

                  TypeMem64Found:
                  if (RootBridge->ResAllocNode[Index].Status != ResAllocated) {
                    //
                    // No Room at the Inn for this resources request
                    //
                    ReturnStatus = EFI_OUT_OF_RESOURCES;
                  }
                } else {
                  //
                  // If 64-bit resourcing is not available, and the requested size is not greater than the Mem32Limit, then try as PMem32
                  //
                  if (AddrLen >= RootBridge->Aperture.Mem32Limit + 1) {

                    DEBUG((DEBUG_ERROR, "      64-bit resource length 0x%llX > Mem32Limit (0x%llX). Failed!!\n", AddrLen, RootBridge->Aperture.Mem32Limit + 1));
                    goto TypeMem64Found; // Let it handle out-of-resources case, try MMIO rebalance

                  } else {

                    DEBUG((DEBUG_ERROR, "      64-bit resourcing not available. Try as PMem32\n"));
                    goto StartTypePMem32;
                  }
                }
                break;

              default:
                DEBUG ((EFI_D_ERROR, "[PCI] ERROR: Unhandled resource type (%d)\n", Index));
                break;
            } // End switch (Index)

            DEBUG ((DEBUG_INFO, "Resource Type Assigned:   %s\n", mPciResourceTypeStr[Index]));
            if (RootBridge->ResAllocNode[Index].Status == ResAllocated) {
              DEBUG ((DEBUG_INFO, "  Base Address Assigned: %lx\n", RootBridge->ResAllocNode[Index].Base));
              DEBUG ((DEBUG_INFO, "  Length Assigned:       %lx\n", RootBridge->ResAllocNode[Index].Length));
            } else {
              DEBUG ((DEBUG_ERROR, "  Resource Allocation failed!  There was no room at the inn\n"));
            }
          } else {
            //
            // Index >= TypeMax
            //
            ASSERT (FALSE);
          }
        }
      }
      List = List->ForwardLink;
    }

    if (ReturnStatus == EFI_OUT_OF_RESOURCES) {

      DEBUG ((DEBUG_ERROR, "[PCI] Resource allocation failed, rebalance resource allocation and reboot\n"));
      AdjustResourceAmongRootBridges (HostBridge, &RatioAdjustResult);
      if (RatioAdjustResult == SocketResourceRatioChanged) {
        DEBUG ((DEBUG_WARN, "[PCI] WARNING: Resource allocation failed. Adjusted the resource requests and resetting the system.\n"));
        gRT->ResetSystem (EfiResetWarm, EFI_SUCCESS, 0, NULL);

      } else {

        DEBUG ((DEBUG_WARN, "[PCI] WARNING: Resource allocation failed, rebalance not possible, continue boot\n"));
      }
    }

    //
    // Set resource to zero for nodes where allocation fails
    //
    List = HostBridge->RootBridges.ForwardLink;
    while (List != &HostBridge->RootBridges) {
      RootBridge = ROOT_BRIDGE_FROM_LINK (List);
      for (Index = TypeIo; Index < TypeBus; Index++) {
        if (RootBridge->ResAllocNode[Index].Status != ResAllocated) {
          RootBridge->ResAllocNode[Index].Length = 0;
        }
      }
      List = List->ForwardLink;
    }
    return ReturnStatus;
  } else {
    return EFI_NOT_READY;
  }
  //
  // HostBridge->CanRestarted = FALSE;
  //
}


/**
  Enter the EfiPciHostBridgeFreeResources phase of the
  PCI(e) numeration process.

  @param This     The EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL
                  instance.
  @retval EFI_SUCCESS   Things worked out
  @retval other         Failures as reported by functions leveraged
*/
STATIC EFI_STATUS
BridgeFreeResources (
  IN EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL *This
  )
{
  PCI_HOST_BRIDGE_INSTANCE              *HostBridge;
  PCI_ROOT_BRIDGE_INSTANCE              *RootBridge;
  PCI_RESOURCE_TYPE                     Index;
  LIST_ENTRY                            *List;
  EFI_PHYSICAL_ADDRESS                  BaseAddress;
  UINT64                                AddrLen;
  EFI_STATUS                            Status;
  EFI_STATUS                            ReturnStatus;

  HostBridge = PCI_HOST_BRIDGE_FROM_THIS (This);
  ReturnStatus = EFI_SUCCESS;
  List = HostBridge->RootBridges.ForwardLink;

  while (List != &HostBridge->RootBridges) {
    RootBridge = ROOT_BRIDGE_FROM_LINK (List);
    for (Index = TypeIo; Index < TypeBus; Index++) {
      if (RootBridge->ResAllocNode[Index].Status == ResAllocated) {
        AddrLen     = RootBridge->ResAllocNode[Index].Length;
        BaseAddress = (EFI_PHYSICAL_ADDRESS) RootBridge->ResAllocNode[Index].Base;

        switch (Index) {
        case TypeIo:
          Status = gDS->FreeIoSpace (BaseAddress, AddrLen);
          if (EFI_ERROR (Status)) {
            ReturnStatus = Status;
          }
          break;

        case TypeMem32:
          Status = gDS->FreeMemorySpace (BaseAddress, AddrLen);
          if (EFI_ERROR (Status)) {
            ReturnStatus = Status;
          }
          break;

        case TypePMem32:
          break;

        case TypeMem64:
          break;

        case TypePMem64:
          Status = gDS->FreeMemorySpace (BaseAddress, AddrLen);
          if (EFI_ERROR (Status)) {
            ReturnStatus = Status;
          }
          break;

        default:
          DEBUG ((DEBUG_ERROR, "[PCI] ERROR: Invalid resource type %d\n", Index));
          break;
        }

        RootBridge->ResAllocNode[Index].Type    = Index;
        RootBridge->ResAllocNode[Index].Base    = 0;
        RootBridge->ResAllocNode[Index].Length  = 0;
        RootBridge->ResAllocNode[Index].Status  = ResNone;
      }
    }

    List = List->ForwardLink;
  }

  HostBridge->ResourceSubmited  = FALSE;
  HostBridge->CanRestarted      = TRUE;
  return ReturnStatus;
}


/**
  Enter the EfiPciHostBridgeEndResourceAllocation phase of the
  PCI(e) numeration process.

  @param This     The EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL
                  instance.
  @retval EFI_SUCCESS   Things worked out
*/
STATIC EFI_STATUS
PciHostBridgeEndResourceAllocation (
  IN EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL *This
  )
{
  return EFI_SUCCESS;
}


/**

  Enter a certain phase of the PCI enumeration process.

  @param This   The EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL instance.
  @param Phase  The phase during enumeration.

  @retval EFI_SUCCESS            Succeed.
  @retval EFI_INVALID_PARAMETER  Wrong phase parameter passed in.
  @retval EFI_NOT_READY          Resources have not been submitted yet.

**/
EFI_STATUS
EFIAPI
NotifyPhase (
  IN EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL *This,
  IN EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PHASE    Phase
  )
{
  PCI_HOST_BRIDGE_INSTANCE              *HostBridge;

  HostBridge = PCI_HOST_BRIDGE_FROM_THIS (This);

  switch (Phase) {
  case EfiPciHostBridgeBeginEnumeration:
    return BeginBridgeEnumeration (This);

  case EfiPciHostBridgeEndEnumeration:
    return EFI_SUCCESS;

  case EfiPciHostBridgeBeginBusAllocation:
    //
    // No specific action is required here, can perform any chipset specific programing
    //
    HostBridge->CanRestarted = FALSE;
    break;

  case EfiPciHostBridgeEndBusAllocation:
    //
    // No specific action is required here, can perform any chipset specific programing
    //
    break;

  case EfiPciHostBridgeBeginResourceAllocation:
    //
    // No specific action is required here, can perform any chipset specific programing
    //
    break;

  case EfiPciHostBridgeAllocateResources:
    return BridgeAllocateResources (This);

  case EfiPciHostBridgeSetResources:
    //
    // HostBridgeInstance->CanRestarted = FALSE;
    //
    break;

  case EfiPciHostBridgeFreeResources:
    return BridgeFreeResources (This);

  case EfiPciHostBridgeEndResourceAllocation:
    return PciHostBridgeEndResourceAllocation (This);

  default:
    return EFI_INVALID_PARAMETER;
  }

  return EFI_SUCCESS;
}

/**

  Return the device handle of the next PCI root bridge that is associated with
  this Host Bridge.

  @param This              The EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_ PROTOCOL instance.
  @param RootBridgeHandle  Returns the device handle of the next PCI Root Bridge.
                           On input, it holds the RootBridgeHandle returned by the most
                           recent call to GetNextRootBridge().The handle for the first
                           PCI Root Bridge is returned if RootBridgeHandle is NULL on input.

  @retval EFI_SUCCESS            Succeed.
  @retval EFI_NOT_FOUND          Next PCI root bridge not found.
  @retval EFI_INVALID_PARAMETER  Wrong parameter passed in.

**/
EFI_STATUS
EFIAPI
GetNextRootBridge (
  IN     EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL *This,
  IN OUT EFI_HANDLE                                       *RootBridgeHandle
  )
{
  BOOLEAN                   NoRootBridge;
  LIST_ENTRY                *List;
  PCI_HOST_BRIDGE_INSTANCE  *HostBridge;
  PCI_ROOT_BRIDGE_INSTANCE  *RootBridge;

  NoRootBridge = TRUE;
  HostBridge = PCI_HOST_BRIDGE_FROM_THIS (This);
  List = HostBridge->RootBridges.ForwardLink;

  while (List != &HostBridge->RootBridges) {
    NoRootBridge = FALSE;
    RootBridge = ROOT_BRIDGE_FROM_LINK (List);
    if (*RootBridgeHandle == NULL) {
      //
      // Return the first Root Bridge Handle of the Host Bridge
      //
      *RootBridgeHandle = RootBridge->Handle;
      return EFI_SUCCESS;
    } else {
      if (*RootBridgeHandle == RootBridge->Handle) {
        //
        // Get next if have
        //
        List = List->ForwardLink;
        if (List != &HostBridge->RootBridges) {
          RootBridge = ROOT_BRIDGE_FROM_LINK (List);
          *RootBridgeHandle   = RootBridge->Handle;
          return EFI_SUCCESS;
        } else {
          return EFI_NOT_FOUND;
        }
      }
    }

    List = List->ForwardLink;
    //
    // end while
    //
  }

  if (NoRootBridge) {
    return EFI_NOT_FOUND;
  } else {
    return EFI_INVALID_PARAMETER;
  }
}

/**

  Returns the attributes of a PCI Root Bridge.

  @param This              The EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_ PROTOCOL instance.
  @param RootBridgeHandle  The device handle of the PCI Root Bridge
                           that the caller is interested in.
  @param Attributes        The pointer to attributes of the PCI Root Bridge.

  @retval EFI_SUCCESS            Succeed.
  @retval EFI_INVALID_PARAMETER  Attributes parameter passed in is NULL or
                                 RootBridgeHandle is not an EFI_HANDLE
                                 that was returned on a previous call to
                                 GetNextRootBridge().

**/
EFI_STATUS
EFIAPI
GetAttributes (
  IN  EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL *This,
  IN  EFI_HANDLE                                       RootBridgeHandle,
  OUT UINT64                                           *Attributes
  )
{
  LIST_ENTRY                *Link;
  PCI_HOST_BRIDGE_INSTANCE  *HostBridge;
  PCI_ROOT_BRIDGE_INSTANCE  *RootBridge;

  if (Attributes == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  HostBridge = PCI_HOST_BRIDGE_FROM_THIS (This);
  for (Link = GetFirstNode (&HostBridge->RootBridges)
      ; !IsNull (&HostBridge->RootBridges, Link)
      ; Link = GetNextNode (&HostBridge->RootBridges, Link)
      ) {
    RootBridge = ROOT_BRIDGE_FROM_LINK (Link);
    if (RootBridgeHandle == RootBridge->Handle) {
      *Attributes = RootBridge->AllocationAttributes;
      return EFI_SUCCESS;
    }
  }

  return EFI_INVALID_PARAMETER;
}

/**

  This is the request from the PCI enumerator to set up
  the specified PCI Root Bridge for bus enumeration process.

  @param This              The EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_ PROTOCOL instance.
  @param RootBridgeHandle  The PCI Root Bridge to be set up.
  @param Configuration     Pointer to the pointer to the PCI bus resource descriptor.

  @retval EFI_SUCCESS            Succeed.
  @retval EFI_OUT_OF_RESOURCES   Not enough pool to be allocated.
  @retval EFI_INVALID_PARAMETER  RootBridgeHandle is not a valid handle.

**/
EFI_STATUS
EFIAPI
StartBusEnumeration (
  IN  EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL *This,
  IN  EFI_HANDLE                                       RootBridgeHandle,
  OUT VOID                                             **Configuration
  )
{
  LIST_ENTRY                *List;
  PCI_HOST_BRIDGE_INSTANCE  *HostBridge;
  PCI_ROOT_BRIDGE_INSTANCE  *RootBridge;
  VOID                      *Buffer;
  UINT8                     *Temp;
  EFI_STATUS                Status;
  UINTN                     BusStart;
  UINTN                     BusEnd;
  UINT64                    BusReserve;

  HostBridge = PCI_HOST_BRIDGE_FROM_THIS (This);
  List = HostBridge->RootBridges.ForwardLink;

  while (List != &HostBridge->RootBridges) {

    RootBridge = ROOT_BRIDGE_FROM_LINK (List);
    if (RootBridgeHandle == RootBridge->Handle) {
      //
      // Set up the Root Bridge for Bus Enumeration
      //
      BusStart  = RootBridge->Aperture.BusBase;
      BusEnd    = RootBridge->Aperture.BusLimit;
      BusReserve = RootBridge->Aperture.BusReserve;
      //
      // Program the Hardware(if needed) if error return EFI_DEVICE_ERROR
      //
      Status = gBS->AllocatePool (
                      EfiBootServicesData,
                      sizeof (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR) + sizeof (EFI_ACPI_END_TAG_DESCRIPTOR),
                      &Buffer
                      );
      if (EFI_ERROR (Status)) {
        return EFI_OUT_OF_RESOURCES;
      }

      Temp  = (UINT8 *) Buffer;

      ((EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *) Temp)->Desc                  = ACPI_ADDRESS_SPACE_DESCRIPTOR;
      ((EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *) Temp)->Len                   = 0x2B;
      ((EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *) Temp)->ResType               = ACPI_ADDRESS_SPACE_TYPE_BUS;
      ((EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *) Temp)->GenFlag               = 0;
      ((EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *) Temp)->SpecificFlag          = 0;
      ((EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *) Temp)->AddrSpaceGranularity  = 0;
      ((EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *) Temp)->AddrRangeMin          = BusStart;
      ((EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *) Temp)->AddrRangeMax          = BusReserve;
      ((EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *) Temp)->AddrTranslationOffset = 0;
      ((EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *) Temp)->AddrLen               = BusEnd - BusStart + 1;

      Temp = Temp + sizeof (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR);
      ((EFI_ACPI_END_TAG_DESCRIPTOR *) Temp)->Desc = ACPI_END_TAG_DESCRIPTOR;
      ((EFI_ACPI_END_TAG_DESCRIPTOR *) Temp)->Checksum = 0x0;

      *Configuration = Buffer;
      return EFI_SUCCESS;
    }

    List = List->ForwardLink;
  }

  return EFI_INVALID_PARAMETER;
}

/**

  This function programs the PCI Root Bridge hardware so that
  it decodes the specified PCI bus range.

  @param This              The EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_ PROTOCOL instance.
  @param RootBridgeHandle  The PCI Root Bridge whose bus range is to be programmed.
  @param Configuration     The pointer to the PCI bus resource descriptor.

  @retval EFI_SUCCESS            Succeed.
  @retval EFI_INVALID_PARAMETER  Wrong parameters passed in.

**/
EFI_STATUS
EFIAPI
SetBusNumbers (
  IN EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL *This,
  IN EFI_HANDLE                                       RootBridgeHandle,
  IN VOID                                             *Configuration
  )
{
  LIST_ENTRY                *List;
  PCI_HOST_BRIDGE_INSTANCE  *HostBridge;
  PCI_ROOT_BRIDGE_INSTANCE  *RootBridge;
  UINT8                     *Ptr;
  UINTN                     BusStart;
  UINTN                     BusEnd;
  UINTN                     BusLen;

  if (Configuration == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  Ptr = Configuration;

  //
  // Check the Configuration is valid
  //
  if (*Ptr != ACPI_ADDRESS_SPACE_DESCRIPTOR) {
    return EFI_INVALID_PARAMETER;
  }

  if (((EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *) Ptr)->ResType != ACPI_ADDRESS_SPACE_TYPE_BUS) {
    return EFI_INVALID_PARAMETER;
  }

  Ptr += sizeof (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR);
  if (*Ptr != ACPI_END_TAG_DESCRIPTOR) {
    return EFI_INVALID_PARAMETER;
  }

  HostBridge = PCI_HOST_BRIDGE_FROM_THIS (This);
  List = HostBridge->RootBridges.ForwardLink;
  Ptr = Configuration;

  while (List != &HostBridge->RootBridges) {

    RootBridge = ROOT_BRIDGE_FROM_LINK (List);
    if (RootBridgeHandle == RootBridge->Handle) {
      BusStart  = (UINTN) ((EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *) Ptr)->AddrRangeMin;
      BusLen    = (UINTN) ((EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *) Ptr)->AddrLen;
      BusEnd    = BusStart + BusLen - 1;

      if (BusStart > BusEnd) {
        return EFI_INVALID_PARAMETER;
      }

      if ((BusStart < RootBridge->Aperture.BusBase) || (BusEnd > RootBridge->Aperture.BusLimit)) {
        return EFI_INVALID_PARAMETER;
      }
      //
      // Update the Bus Range
      //
      RootBridge->ResAllocNode[TypeBus].Base    = BusStart;
      RootBridge->ResAllocNode[TypeBus].Length  = BusLen + RootBridge->Aperture.BusReserve;
      RootBridge->ResAllocNode[TypeBus].Status  = ResAllocated;
      RootBridge->BusScanCount++;
      if (RootBridge->BusScanCount > 0) {
        //
        // Only care about the 2nd PCI bus scanning
        //
        RootBridge->BusNumberAssigned = TRUE;
      }

      return EFI_SUCCESS;
    }

    List = List->ForwardLink;
  }

  return EFI_INVALID_PARAMETER;
}


/**

  Submits the I/O and memory resource requirements for the specified PCI Root Bridge.

  @param This              The EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_ PROTOCOL instance.
  @param RootBridgeHandle  The PCI Root Bridge whose I/O and memory resource requirements.
                           are being submitted.
  @param Configuration     The pointer to the PCI I/O and PCI memory resource descriptor.

  @retval EFI_SUCCESS            Succeed.
  @retval EFI_INVALID_PARAMETER  Wrong parameters passed in.
**/
EFI_STATUS
EFIAPI
SubmitResources (
  IN EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL *This,
  IN EFI_HANDLE                                       RootBridgeHandle,
  IN VOID                                             *Configuration
  )
{
  LIST_ENTRY                        *List;
  PCI_HOST_BRIDGE_INSTANCE          *HostBridge;
  PCI_ROOT_BRIDGE_INSTANCE          *RootBridge;
  UINT8                             *Temp;
  EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *ptr;
  UINT64                            AddrLen;
  UINT64                            Alignment;
  UINT64                            Value;

  //
  // Check the input parameter: Configuration
  //
  if (Configuration == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  HostBridge = PCI_HOST_BRIDGE_FROM_THIS (This);
  List = HostBridge->RootBridges.ForwardLink;

  Temp = (UINT8 *) Configuration;
  while (List != &HostBridge->RootBridges) {

    RootBridge = ROOT_BRIDGE_FROM_LINK (List);
    if (RootBridgeHandle == RootBridge->Handle) {
      //
      // Check the resource descriptors.
      // If the Configuration includes one or more invalid resource descriptors, all the resource
      // descriptors are ignored and the function returns EFI_INVALID_PARAMETER.
      //
      for (; *Temp == ACPI_ADDRESS_SPACE_DESCRIPTOR; Temp += sizeof (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR)) {

        ptr = (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *) Temp;
        //DEBUG ((DEBUG_ERROR, " ptr->ResType:%x", ptr->ResType));
        //DEBUG ((DEBUG_ERROR, " ptr->AddrLen:0x%llX AddrRangeMin:0x%llX AddrRangeMax:0x%llX\n",ptr->AddrLen,ptr->AddrRangeMin,ptr->AddrRangeMax));
        //
        // A zero-length resource request indicates the PCI root bridge doesn't require
        // any resources. Skip the check and proceed to the next descriptor.
        //
        if (ptr->AddrLen == 0) {
          continue;
        }

        switch (ptr->ResType) {
          case ACPI_ADDRESS_SPACE_TYPE_MEM:
            if (ptr->AddrSpaceGranularity != 32 && ptr->AddrSpaceGranularity != 64) {

              DEBUG ((DEBUG_ERROR, "[PCI] ERROR: Inavlid granularity %d for resource type %d\n",
                      ptr->AddrSpaceGranularity, ptr->ResType));
              return EFI_INVALID_PARAMETER;
            }
            if (ptr->AddrSpaceGranularity == 32 && ptr->AddrLen > 0xffffffff) {

              DEBUG ((DEBUG_ERROR, "[PCI] ERROR: Inavlid granularity %d for resource type %d of size 0x%llX\n",
                      ptr->AddrSpaceGranularity, ptr->ResType, ptr->AddrLen));
              return EFI_INVALID_PARAMETER;
            }
            //
            // If the PCI root bridge does not support separate windows for nonprefetchable and
            // prefetchable memory, then the PCI bus driver needs to include requests for
            // prefetchable memory in the nonprefetchable memory pool.
            //
            if ((RootBridge->AllocationAttributes & EFI_PCI_HOST_BRIDGE_COMBINE_MEM_PMEM) != 0 &&
                ((ptr->SpecificFlag & (BIT2 | BIT1)) != 0)) {
              return EFI_INVALID_PARAMETER;
            }
          case ACPI_ADDRESS_SPACE_TYPE_IO:
            //
            // Check alignment, it should be of the form 2^n-1
            //
            Value = Power2MaxMemory (ptr->AddrRangeMax + 1);
            if (Value != (ptr->AddrRangeMax + 1)) {
              CpuDeadLoop();
              return EFI_INVALID_PARAMETER;
            }
            break;
          case ACPI_ADDRESS_SPACE_TYPE_BUS:
          default:
            return EFI_INVALID_PARAMETER;
        }
      }
      if (*Temp != ACPI_END_TAG_DESCRIPTOR) {
        return EFI_INVALID_PARAMETER;
      }

      Temp = (UINT8 *) Configuration;
      for (; *Temp == ACPI_ADDRESS_SPACE_DESCRIPTOR; Temp += sizeof (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR)) {
        ptr = (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *) Temp;

        //
        // If a PCI root bridge does not require any resources, a zero-length resource
        // request must be explicitly submitted.
        //
        if (ptr->AddrLen == 0) {
          HostBridge->ResourceSubmited = TRUE;
          continue;
        }

        switch (ptr->ResType) {
        case ACPI_ADDRESS_SPACE_TYPE_MEM:
          AddrLen   = (UINT64) ptr->AddrLen;
          Alignment = (UINT64) ptr->AddrRangeMax;
          if (ptr->AddrSpaceGranularity == 32) {
            if (ptr->SpecificFlag == 0x06) {
              //
              // Apply from GCD
              //
              RootBridge->ResAllocNode[TypePMem32].Status = ResSubmitted;
            } else {
              RootBridge->ResAllocNode[TypeMem32].Length    = AddrLen;
              RootBridge->ResAllocNode[TypeMem32].Alignment = Alignment;
              RootBridge->ResAllocNode[TypeMem32].Status    = ResRequested;
              HostBridge->ResourceSubmited                  = TRUE;
            }
          }

          if (ptr->AddrSpaceGranularity == 64) {
            if (ptr->SpecificFlag == 0x06) {
              RootBridge->ResAllocNode[TypePMem64].Status = ResSubmitted;
            } else {
              RootBridge->ResAllocNode[TypeMem64].Length    = AddrLen;
              RootBridge->ResAllocNode[TypeMem64].Alignment = Alignment;
              RootBridge->ResAllocNode[TypeMem64].Status    = ResSubmitted;
              HostBridge->ResourceSubmited                  = TRUE;
            }
          }
          break;

        case ACPI_ADDRESS_SPACE_TYPE_IO:
          AddrLen   = (UINT64) ptr->AddrLen;
          Alignment = (UINT64) ptr->AddrRangeMax;
          RootBridge->ResAllocNode[TypeIo].Length     = AddrLen;
          RootBridge->ResAllocNode[TypeIo].Alignment  = Alignment;
          RootBridge->ResAllocNode[TypeIo].Status     = ResRequested;
          HostBridge->ResourceSubmited                = TRUE;
          break;

        default:
          break;
        }
      }

      return EFI_SUCCESS;
    }
    List = List->ForwardLink;
  }

  return EFI_INVALID_PARAMETER;
}

/**

  This function returns the proposed resource settings for the specified
  PCI Root Bridge.

  @param This              The EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_ PROTOCOL instance.
  @param RootBridgeHandle  The PCI Root Bridge handle.
  @param Configuration     The pointer to the pointer to the PCI I/O
                           and memory resource descriptor.

  @retval EFI_SUCCESS            Succeed.
  @retval EFI_OUT_OF_RESOURCES   Not enough pool to be allocated.
  @retval EFI_INVALID_PARAMETER  RootBridgeHandle is not a valid handle.

**/
EFI_STATUS
EFIAPI
GetProposedResources (
  IN  EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL *This,
  IN  EFI_HANDLE                                       RootBridgeHandle,
  OUT VOID                                             **Configuration
  )
{
  LIST_ENTRY                        *List;
  PCI_HOST_BRIDGE_INSTANCE          *HostBridge;
  PCI_ROOT_BRIDGE_INSTANCE          *RootBridge;
  UINTN                             Index;
  UINTN                             Number;
  VOID                              *Buffer;
  UINT8                             *Temp;
  EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *ptr;
  EFI_STATUS                        Status;
  UINT64                            ResStatus;

  Buffer  = NULL;
  Number  = 0;
  //
  // Get the Host Bridge Instance from the resource allocation protocol
  //
  HostBridge = PCI_HOST_BRIDGE_FROM_THIS (This);
  List = HostBridge->RootBridges.ForwardLink;

  //
  // Enumerate the root bridges in this Host bridge
  //
  while (List != &HostBridge->RootBridges) {

    RootBridge = ROOT_BRIDGE_FROM_LINK (List);
    if (RootBridgeHandle == RootBridge->Handle) {
      for (Index = 0; Index < TypeBus; Index++) {
        if (RootBridge->ResAllocNode[Index].Status != ResNone) {
          Number++;
        }
      }

      Status = gBS->AllocatePool (
                      EfiBootServicesData,
                      Number * sizeof (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR) + sizeof (EFI_ACPI_END_TAG_DESCRIPTOR),
                      &Buffer
                      );

      if (EFI_ERROR (Status)) {
        return EFI_OUT_OF_RESOURCES;
      }

      ZeroMem (Buffer, sizeof (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR) * Number + sizeof (EFI_ACPI_END_TAG_DESCRIPTOR));

      Temp = Buffer;
      for (Index = 0; Index < TypeBus; Index++) {
        if (RootBridge->ResAllocNode[Index].Status != ResNone) {
          ptr       = (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *) Temp;
          ResStatus = RootBridge->ResAllocNode[Index].Status;

          switch (Index) {

          case TypeIo:
            //
            // Io
            //
            ptr->Desc                   = 0x8A;
            ptr->Len                    = 0x2B;
            ptr->ResType                = 1;
            ptr->GenFlag                = 0;
            ptr->SpecificFlag           = 0;
            ptr->AddrRangeMin           = RootBridge->ResAllocNode[Index].Base;
            ptr->AddrRangeMax           = 0;
            ptr->AddrTranslationOffset  = (ResStatus == ResAllocated) ? EFI_RESOURCE_SATISFIED : EFI_RESOURCE_LESS;
            ptr->AddrLen                = RootBridge->ResAllocNode[Index].Length;
            break;

          case TypeMem32:
            //
            // Memory 32
            //
            ptr->Desc                   = 0x8A;
            ptr->Len                    = 0x2B;
            ptr->ResType                = 0;
            ptr->GenFlag                = 0;
            ptr->SpecificFlag           = 0;
            ptr->AddrSpaceGranularity   = 32;
            ptr->AddrRangeMin           = RootBridge->ResAllocNode[Index].Base;
            ptr->AddrRangeMax           = 0;
            ptr->AddrTranslationOffset  = (ResStatus == ResAllocated) ? EFI_RESOURCE_SATISFIED : EFI_RESOURCE_LESS;
            ptr->AddrLen                = RootBridge->ResAllocNode[Index].Length;
            break;

          case TypePMem32:
            //
            // Prefetch memory 32
            //
            ptr->Desc                   = 0x8A;
            ptr->Len                    = 0x2B;
            ptr->ResType                = 0;
            ptr->GenFlag                = 0;
            ptr->SpecificFlag           = 6;
            ptr->AddrSpaceGranularity   = 32;
            ptr->AddrRangeMin           = 0;
            ptr->AddrRangeMax           = 0;
            ptr->AddrTranslationOffset  = EFI_RESOURCE_NONEXISTENT;
            ptr->AddrLen                = 0;
            break;

          case TypeMem64:
            //
            // Memory 64
            //
            ptr->Desc                   = 0x8A;
            ptr->Len                    = 0x2B;
            ptr->ResType                = 0;
            ptr->GenFlag                = 0;
            ptr->SpecificFlag           = 0;
            ptr->AddrSpaceGranularity   = 64;
            ptr->AddrRangeMin           = RootBridge->ResAllocNode[Index].Base;
            ptr->AddrRangeMax           = 0;
            ptr->AddrTranslationOffset  = (ResStatus == ResAllocated) ? EFI_RESOURCE_SATISFIED : EFI_RESOURCE_LESS;
            ptr->AddrLen                = RootBridge->ResAllocNode[Index].Length;
            break;

          case TypePMem64:
            //
            // Prefetch memory 64
            //
            ptr->Desc                   = 0x8A;
            ptr->Len                    = 0x2B;
            ptr->ResType                = 0;
            ptr->GenFlag                = 0;
            ptr->SpecificFlag           = 6;
            ptr->AddrSpaceGranularity   = 64;
            ptr->AddrRangeMin           = 0;
            ptr->AddrRangeMax           = 0;
            ptr->AddrTranslationOffset  = EFI_RESOURCE_NONEXISTENT;
            ptr->AddrLen                = 0;
            break;
            default:
              DEBUG ((EFI_D_INFO, "default case.\n"));  //Auto added. Please review.
              break;
          }

          Temp += sizeof (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR);
        }
      }

      ((EFI_ACPI_END_TAG_DESCRIPTOR *) Temp)->Desc      = 0x79;
      ((EFI_ACPI_END_TAG_DESCRIPTOR *) Temp)->Checksum  = 0x0;

      *Configuration = Buffer;

      return EFI_SUCCESS;
    }

    List = List->ForwardLink;
  }

  return EFI_INVALID_PARAMETER;
}

/**

  This function is called for all the PCI controllers that the PCI
  bus driver finds. Can be used to Preprogram the controller.

  @param This              The EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_ PROTOCOL instance.
  @param RootBridgeHandle  The PCI Root Bridge handle.
  @param PciAddress        Address of the controller on the PCI bus.
  @param Phase             The Phase during resource allocation.

  @retval EFI_SUCCESS            Succeed.
  @retval EFI_INVALID_PARAMETER  RootBridgeHandle is not a valid handle.

**/
EFI_STATUS
EFIAPI
PreprocessController (
  IN  EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL          *This,
  IN  EFI_HANDLE                                                RootBridgeHandle,
  IN  EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_PCI_ADDRESS               PciAddress,
  IN  EFI_PCI_CONTROLLER_RESOURCE_ALLOCATION_PHASE              Phase
  )
{
  BOOLEAN                   RootBridgeFound;
  LIST_ENTRY                *List;
  PCI_HOST_BRIDGE_INSTANCE  *HostBridge;
  PCI_ROOT_BRIDGE_INSTANCE  *RootBridge;

  if (RootBridgeHandle == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  RootBridgeFound     = FALSE;
  HostBridge = PCI_HOST_BRIDGE_FROM_THIS (This);
  List = HostBridge->RootBridges.ForwardLink;

  while (List != &HostBridge->RootBridges) {

    RootBridge = ROOT_BRIDGE_FROM_LINK (List);
    if (RootBridgeHandle == RootBridge->Handle) {
      RootBridgeFound = TRUE;
      break;
    }
    //
    // Get next if have
    //
    List = List->ForwardLink;
  }

  if (RootBridgeFound == FALSE) {
    return EFI_INVALID_PARAMETER;
  }

  return EFI_SUCCESS;
}

/**

  Calculate maximum memory length that can be fit to a mtrr.

  @param MemoryLength  -  Input memory length.

  @retval Returned Maximum length.

**/
UINT64
Power2MaxMemory (
  IN UINT64                     MemoryLength
  )
{
  UINT64  Result;

  if (RShiftU64 (MemoryLength, 32)) {
    Result = LShiftU64 ((UINT64) GetPowerOfTwo64 ((UINT32) RShiftU64 (MemoryLength, 32)), 32);
  } else {
    Result = (UINT64) GetPowerOfTwo64 ((UINT32) MemoryLength);
  }

  return Result;
}
