/** @file

  Provides the basic interfaces to abstract a PCI Host Bridge Resource Allocation.

Copyright (c) 1999 - 2016, Intel Corporation. All rights reserved.<BR>
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution.  The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php

THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

**/

#include "PciHostBridge.h"
#include "PciRootBridge.h"
#include "PciHostResource.h"


EFI_METRONOME_ARCH_PROTOCOL *mMetronome;
EFI_CPU_IO2_PROTOCOL        *mCpuIo;

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

/**
  Ensure the compatibility of an IO space descriptor with the IO aperture.

  The IO space descriptor can come from the GCD IO space map, or it can
  represent a gap between two neighboring IO space descriptors. In the latter
  case, the GcdIoType field is expected to be EfiGcdIoTypeNonExistent.

  If the IO space descriptor already has type EfiGcdIoTypeIo, then no action is
  taken -- it is by definition compatible with the aperture.

  Otherwise, the intersection of the IO space descriptor is calculated with the
  aperture. If the intersection is the empty set (no overlap), no action is
  taken; the IO space descriptor is compatible with the aperture.

  Otherwise, the type of the descriptor is investigated again. If the type is
  EfiGcdIoTypeNonExistent (representing a gap, or a genuine descriptor with
  such a type), then an attempt is made to add the intersection as IO space to
  the GCD IO space map. This ensures continuity for the aperture, and the
  descriptor is deemed compatible with the aperture.

  Otherwise, the IO space descriptor is incompatible with the IO aperture.

  @param[in] Base        Base address of the aperture.
  @param[in] Length      Length of the aperture.
  @param[in] Descriptor  The descriptor to ensure compatibility with the
                         aperture for.

  @retval EFI_SUCCESS            The descriptor is compatible. The GCD IO space
                                 map may have been updated, for continuity
                                 within the aperture.
  @retval EFI_INVALID_PARAMETER  The descriptor is incompatible.
  @return                        Error codes from gDS->AddIoSpace().
**/
EFI_STATUS
IntersectIoDescriptor (
  IN  UINT64                            Base,
  IN  UINT64                            Length,
  IN  CONST EFI_GCD_IO_SPACE_DESCRIPTOR *Descriptor
  )
{
  UINT64                                IntersectionBase;
  UINT64                                IntersectionEnd;
  EFI_STATUS                            Status;

  if (Descriptor->GcdIoType == EfiGcdIoTypeIo) {
    return EFI_SUCCESS;
  }

  IntersectionBase = MAX (Base, Descriptor->BaseAddress);
  IntersectionEnd = MIN (Base + Length,
                      Descriptor->BaseAddress + Descriptor->Length);
  if (IntersectionBase >= IntersectionEnd) {
    //
    // The descriptor and the aperture don't overlap.
    //
    return EFI_SUCCESS;
  }

  if (Descriptor->GcdIoType == EfiGcdIoTypeNonExistent) {
    Status = gDS->AddIoSpace (EfiGcdIoTypeIo, IntersectionBase,
                    IntersectionEnd - IntersectionBase);

    DEBUG ((EFI_ERROR (Status) ? EFI_D_ERROR : EFI_D_VERBOSE,
      "%a: %a: add [%Lx, %Lx): %r\n", gEfiCallerBaseName, __FUNCTION__,
      IntersectionBase, IntersectionEnd, Status));
    return Status;
  }

  DEBUG ((EFI_D_ERROR, "%a: %a: desc [%Lx, %Lx) type %u conflicts with "
    "aperture [%Lx, %Lx)\n", gEfiCallerBaseName, __FUNCTION__,
    Descriptor->BaseAddress, Descriptor->BaseAddress + Descriptor->Length,
    (UINT32)Descriptor->GcdIoType, Base, Base + Length));
  return EFI_INVALID_PARAMETER;
}

/**
  Add IO space to GCD.
  The routine checks the GCD database and only adds those which are
  not added in the specified range to GCD.

  @param Base   Base address of the IO space.
  @param Length Length of the IO space.

  @retval EFI_SUCCES The IO space was added successfully.
**/
EFI_STATUS
AddIoSpace (
  IN  UINT64                        Base,
  IN  UINT64                        Length
  )
{
  EFI_STATUS                        Status;
  UINTN                             Index;
  UINTN                             NumberOfDescriptors;
  EFI_GCD_IO_SPACE_DESCRIPTOR       *IoSpaceMap;

  Status = gDS->GetIoSpaceMap (&NumberOfDescriptors, &IoSpaceMap);
  if (EFI_ERROR (Status)) {
    DEBUG ((EFI_D_ERROR, "%a: %a: GetIoSpaceMap(): %r\n",
      gEfiCallerBaseName, __FUNCTION__, Status));
    return Status;
  }

  for (Index = 0; Index < NumberOfDescriptors; Index++) {
    Status = IntersectIoDescriptor (Base, Length, &IoSpaceMap[Index]);
    if (EFI_ERROR (Status)) {
      goto FreeIoSpaceMap;
    }
  }

  DEBUG_CODE (
    //
    // Make sure there are adjacent descriptors covering [Base, Base + Length).
    // It is possible that they have not been merged; merging can be prevented
    // by allocation.
    //
    UINT64                      CheckBase;
    EFI_STATUS                  CheckStatus;
    EFI_GCD_IO_SPACE_DESCRIPTOR Descriptor;

    for (CheckBase = Base;
         CheckBase < Base + Length;
         CheckBase = Descriptor.BaseAddress + Descriptor.Length) {
      CheckStatus = gDS->GetIoSpaceDescriptor (CheckBase, &Descriptor);
      ASSERT_EFI_ERROR (CheckStatus);
      ASSERT (Descriptor.GcdIoType == EfiGcdIoTypeIo);
    }
    );

FreeIoSpaceMap:
  FreePool (IoSpaceMap);

  return Status;
}

/**
  Ensure the compatibility of a memory space descriptor with the MMIO aperture.

  The memory space descriptor can come from the GCD memory space map, or it can
  represent a gap between two neighboring memory space descriptors. In the
  latter case, the GcdMemoryType field is expected to be
  EfiGcdMemoryTypeNonExistent.

  If the memory space descriptor already has type
  EfiGcdMemoryTypeMemoryMappedIo, and its capabilities are a superset of the
  required capabilities, then no action is taken -- it is by definition
  compatible with the aperture.

  Otherwise, the intersection of the memory space descriptor is calculated with
  the aperture. If the intersection is the empty set (no overlap), no action is
  taken; the memory space descriptor is compatible with the aperture.

  Otherwise, the type of the descriptor is investigated again. If the type is
  EfiGcdMemoryTypeNonExistent (representing a gap, or a genuine descriptor with
  such a type), then an attempt is made to add the intersection as MMIO space
  to the GCD memory space map, with the specified capabilities. This ensures
  continuity for the aperture, and the descriptor is deemed compatible with the
  aperture.

  Otherwise, the memory space descriptor is incompatible with the MMIO
  aperture.

  @param[in] Base         Base address of the aperture.
  @param[in] Length       Length of the aperture.
  @param[in] Capabilities Capabilities required by the aperture.
  @param[in] Descriptor   The descriptor to ensure compatibility with the
                          aperture for.

  @retval EFI_SUCCESS            The descriptor is compatible. The GCD memory
                                 space map may have been updated, for
                                 continuity within the aperture.
  @retval EFI_INVALID_PARAMETER  The descriptor is incompatible.
  @return                        Error codes from gDS->AddMemorySpace().
**/
EFI_STATUS
IntersectMemoryDescriptor (
  IN  UINT64                                Base,
  IN  UINT64                                Length,
  IN  UINT64                                Capabilities,
  IN  CONST EFI_GCD_MEMORY_SPACE_DESCRIPTOR *Descriptor
  )
{
  UINT64                                    IntersectionBase;
  UINT64                                    IntersectionEnd;
  EFI_STATUS                                Status;

  if (Descriptor->GcdMemoryType == EfiGcdMemoryTypeMemoryMappedIo &&
      (Descriptor->Capabilities & Capabilities) == Capabilities) {
    return EFI_SUCCESS;
  }

  IntersectionBase = MAX (Base, Descriptor->BaseAddress);
  IntersectionEnd = MIN (Base + Length,
                      Descriptor->BaseAddress + Descriptor->Length);
  if (IntersectionBase >= IntersectionEnd) {
    //
    // The descriptor and the aperture don't overlap.
    //
    return EFI_SUCCESS;
  }

  if (Descriptor->GcdMemoryType == EfiGcdMemoryTypeNonExistent) {
    Status = gDS->AddMemorySpace (EfiGcdMemoryTypeMemoryMappedIo,
                    IntersectionBase, IntersectionEnd - IntersectionBase,
                    Capabilities);

    DEBUG ((EFI_ERROR (Status) ? EFI_D_ERROR : EFI_D_VERBOSE,
      "%a: %a: add [%Lx, %Lx): %r\n", gEfiCallerBaseName, __FUNCTION__,
      IntersectionBase, IntersectionEnd, Status));
    return Status;
  }

  DEBUG ((EFI_D_ERROR, "%a: %a: desc [%Lx, %Lx) type %u cap %Lx conflicts "
    "with aperture [%Lx, %Lx) cap %Lx\n", gEfiCallerBaseName, __FUNCTION__,
    Descriptor->BaseAddress, Descriptor->BaseAddress + Descriptor->Length,
    (UINT32)Descriptor->GcdMemoryType, Descriptor->Capabilities,
    Base, Base + Length, Capabilities));
  return EFI_INVALID_PARAMETER;
}

/**
  Add MMIO space to GCD.
  The routine checks the GCD database and only adds those which are
  not added in the specified range to GCD.

  @param Base         Base address of the MMIO space.
  @param Length       Length of the MMIO space.
  @param Capabilities Capabilities of the MMIO space.

  @retval EFI_SUCCES The MMIO space was added successfully.
**/
EFI_STATUS
AddMemoryMappedIoSpace (
  IN  UINT64                            Base,
  IN  UINT64                            Length,
  IN  UINT64                            Capabilities
  )
{
  EFI_STATUS                            Status;
  UINTN                                 Index;
  UINTN                                 NumberOfDescriptors;
  EFI_GCD_MEMORY_SPACE_DESCRIPTOR       *MemorySpaceMap;

  Status = gDS->GetMemorySpaceMap (&NumberOfDescriptors, &MemorySpaceMap);
  if (EFI_ERROR (Status)) {
    DEBUG ((EFI_D_ERROR, "%a: %a: GetMemorySpaceMap(): %r\n",
      gEfiCallerBaseName, __FUNCTION__, Status));
    return Status;
  }

  for (Index = 0; Index < NumberOfDescriptors; Index++) {
    Status = IntersectMemoryDescriptor (Base, Length, Capabilities,
               &MemorySpaceMap[Index]);
    if (EFI_ERROR (Status)) {
      goto FreeMemorySpaceMap;
    }
  }

  DEBUG_CODE (
    //
    // Make sure there are adjacent descriptors covering [Base, Base + Length).
    // It is possible that they have not been merged; merging can be prevented
    // by allocation and different capabilities.
    //
    UINT64                          CheckBase;
    EFI_STATUS                      CheckStatus;
    EFI_GCD_MEMORY_SPACE_DESCRIPTOR Descriptor;

    for (CheckBase = Base;
         CheckBase < Base + Length;
         CheckBase = Descriptor.BaseAddress + Descriptor.Length) {
      CheckStatus = gDS->GetMemorySpaceDescriptor (CheckBase, &Descriptor);
      ASSERT_EFI_ERROR (CheckStatus);
      ASSERT (Descriptor.GcdMemoryType == EfiGcdMemoryTypeMemoryMappedIo);
      ASSERT ((Descriptor.Capabilities & Capabilities) == Capabilities);
    }
    );

FreeMemorySpaceMap:
  FreePool (MemorySpaceMap);

  return Status;
}

/**

  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;
  PCI_HOST_BRIDGE_INSTANCE    *HostBridge;
  PCI_ROOT_BRIDGE_INSTANCE    *RootBridge;
  PCI_ROOT_BRIDGE             *RootBridges;
  UINTN                       RootBridgeCount;
  UINTN                       Index;
  PCI_ROOT_BRIDGE_APERTURE    *MemApertures[4];
  UINTN                       MemApertureIndex;
  BOOLEAN                     ResourceAssigned;
  LIST_ENTRY                  *Link;

  RootBridges = PciHostBridgeGetRootBridges (&RootBridgeCount);
  if ((RootBridges == NULL) || (RootBridgeCount == 0)) {
    return EFI_UNSUPPORTED;
  }

  Status = gBS->LocateProtocol (&gEfiMetronomeArchProtocolGuid, NULL, (VOID **) &mMetronome);
  ASSERT_EFI_ERROR (Status);
  Status = gBS->LocateProtocol (&gEfiCpuIo2ProtocolGuid, NULL, (VOID **) &mCpuIo);
  ASSERT_EFI_ERROR (Status);

  //
  // Most systems in the world including complex servers have only one Host Bridge.
  //
  HostBridge = AllocateZeroPool (sizeof (PCI_HOST_BRIDGE_INSTANCE));
  ASSERT (HostBridge != NULL);

  HostBridge->Signature        = PCI_HOST_BRIDGE_SIGNATURE;
  HostBridge->CanRestarted     = TRUE;
  InitializeListHead (&HostBridge->RootBridges);
  ResourceAssigned             = FALSE;

  //
  // Create Root Bridge Device Handle in this Host Bridge
  //
  for (Index = 0; Index < RootBridgeCount; Index++) {
    //
    // Create Root Bridge Handle Instance
    //
    RootBridge = CreateRootBridge (&RootBridges[Index]);
    ASSERT (RootBridge != NULL);
    if (RootBridge == NULL) {
      continue;
    }

    //
    // Make sure all root bridges share the same ResourceAssigned value.
    //
    if (Index == 0) {
      ResourceAssigned = RootBridges[Index].ResourceAssigned;
    } else {
      ASSERT (ResourceAssigned == RootBridges[Index].ResourceAssigned);
    }

    if (RootBridges[Index].Io.Base <= RootBridges[Index].Io.Limit) {
      Status = AddIoSpace (
                 RootBridges[Index].Io.Base,
                 RootBridges[Index].Io.Limit - RootBridges[Index].Io.Base + 1
                 );
      ASSERT_EFI_ERROR (Status);
      if (ResourceAssigned) {
        Status = gDS->AllocateIoSpace (
                        EfiGcdAllocateAddress,
                        EfiGcdIoTypeIo,
                        0,
                        RootBridges[Index].Io.Limit - RootBridges[Index].Io.Base + 1,
                        &RootBridges[Index].Io.Base,
                        gImageHandle,
                        NULL
                        );
        ASSERT_EFI_ERROR (Status);
      }
    }

    //
    // Add all the Mem/PMem aperture to GCD
    // Mem/PMem shouldn't overlap with each other
    // Root bridge which needs to combine MEM and PMEM should only report
    // the MEM aperture in Mem
    //
    MemApertures[0] = &RootBridges[Index].Mem;
    MemApertures[1] = &RootBridges[Index].MemAbove4G;
    MemApertures[2] = &RootBridges[Index].PMem;
    MemApertures[3] = &RootBridges[Index].PMemAbove4G;

    for (MemApertureIndex = 0; MemApertureIndex < ARRAY_SIZE (MemApertures); MemApertureIndex++) {
      if (MemApertures[MemApertureIndex]->Base <= MemApertures[MemApertureIndex]->Limit) {
        Status = AddMemoryMappedIoSpace (
                   MemApertures[MemApertureIndex]->Base,
                   MemApertures[MemApertureIndex]->Limit - MemApertures[MemApertureIndex]->Base + 1,
                   EFI_MEMORY_UC
                   );
        ASSERT_EFI_ERROR (Status);
        Status = gDS->SetMemorySpaceAttributes (
                        MemApertures[MemApertureIndex]->Base,
                        MemApertures[MemApertureIndex]->Limit - MemApertures[MemApertureIndex]->Base + 1,
                        EFI_MEMORY_UC
                        );
        if (EFI_ERROR (Status)) {
          DEBUG ((DEBUG_WARN, "PciHostBridge driver failed to set EFI_MEMORY_UC to MMIO aperture - %r.\n", Status));
        }
        if (ResourceAssigned) {
          Status = gDS->AllocateMemorySpace (
                          EfiGcdAllocateAddress,
                          EfiGcdMemoryTypeMemoryMappedIo,
                          0,
                          MemApertures[MemApertureIndex]->Limit - MemApertures[MemApertureIndex]->Base + 1,
                          &MemApertures[MemApertureIndex]->Base,
                          gImageHandle,
                          NULL
                          );
          ASSERT_EFI_ERROR (Status);
        }
      }
    }
    //
    // Insert Root Bridge Handle Instance
    //
    InsertTailList (&HostBridge->RootBridges, &RootBridge->Link);
  }

  //
  // When resources were assigned, it's not needed to expose
  // PciHostBridgeResourceAllocation protocol.
  //
  if (!ResourceAssigned) {
    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->InstallMultipleProtocolInterfaces (
                    &HostBridge->Handle,
                    &gEfiPciHostBridgeResourceAllocationProtocolGuid, &HostBridge->ResAlloc,
                    NULL
                    );
    ASSERT_EFI_ERROR (Status);
  }

  for (Link = GetFirstNode (&HostBridge->RootBridges)
       ; !IsNull (&HostBridge->RootBridges, Link)
       ; Link = GetNextNode (&HostBridge->RootBridges, Link)
       ) {
    RootBridge = ROOT_BRIDGE_FROM_LINK (Link);
    RootBridge->RootBridgeIo.ParentHandle = HostBridge->Handle;

    Status = gBS->InstallMultipleProtocolInterfaces (
                    &RootBridge->Handle,
                    &gEfiDevicePathProtocolGuid, RootBridge->DevicePath,
                    &gEfiPciRootBridgeIoProtocolGuid, &RootBridge->RootBridgeIo,
                    NULL
                    );
    ASSERT_EFI_ERROR (Status);
  }
  PciHostBridgeFreeRootBridges (RootBridges, RootBridgeCount);
  return Status;
}

/**
  This routine constructs the resource descriptors for all root bridges and call PciHostBridgeResourceConflict().

  @param HostBridge The Host Bridge Instance where the resource adjustment happens.
**/
VOID
ResourceConflict (
  IN  PCI_HOST_BRIDGE_INSTANCE *HostBridge
  )
{
  EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Resources;
  EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Descriptor;
  EFI_ACPI_END_TAG_DESCRIPTOR       *End;
  PCI_ROOT_BRIDGE_INSTANCE          *RootBridge;
  LIST_ENTRY                        *Link;
  UINTN                             RootBridgeCount;
  PCI_RESOURCE_TYPE                 Index;
  PCI_RES_NODE                      *ResAllocNode;

  RootBridgeCount = 0;
  for (Link = GetFirstNode (&HostBridge->RootBridges)
       ; !IsNull (&HostBridge->RootBridges, Link)
       ; Link = GetNextNode (&HostBridge->RootBridges, Link)
       ) {
    RootBridgeCount++;
  }

  Resources = AllocatePool (
                RootBridgeCount * (TypeMax * sizeof (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR) + sizeof (EFI_ACPI_END_TAG_DESCRIPTOR)) +
                sizeof (EFI_ACPI_END_TAG_DESCRIPTOR)
                );
  ASSERT (Resources != NULL);

  for (Link = GetFirstNode (&HostBridge->RootBridges), Descriptor = Resources
       ; !IsNull (&HostBridge->RootBridges, Link)
       ; Link = GetNextNode (&HostBridge->RootBridges, Link)
       ) {
    RootBridge = ROOT_BRIDGE_FROM_LINK (Link);
    for (Index = TypeIo; Index < TypeMax; Index++) {
      ResAllocNode = &RootBridge->ResAllocNode[Index];

      Descriptor->Desc = ACPI_ADDRESS_SPACE_DESCRIPTOR;
      Descriptor->Len = sizeof (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR) - 3;
      Descriptor->AddrRangeMin = ResAllocNode->Base;
      Descriptor->AddrRangeMax = ResAllocNode->Alignment;
      Descriptor->AddrLen = ResAllocNode->Length;
      switch (ResAllocNode->Type) {

      case TypeIo:
        Descriptor->ResType = ACPI_ADDRESS_SPACE_TYPE_IO;
        break;

      case TypePMem32:
        Descriptor->SpecificFlag = EFI_ACPI_MEMORY_RESOURCE_SPECIFIC_FLAG_CACHEABLE_PREFETCHABLE;
      case TypeMem32:
        Descriptor->ResType = ACPI_ADDRESS_SPACE_TYPE_MEM;
        Descriptor->AddrSpaceGranularity = 32;
        break;

      case TypePMem64:
        Descriptor->SpecificFlag = EFI_ACPI_MEMORY_RESOURCE_SPECIFIC_FLAG_CACHEABLE_PREFETCHABLE;
      case TypeMem64:
        Descriptor->ResType = ACPI_ADDRESS_SPACE_TYPE_MEM;
        Descriptor->AddrSpaceGranularity = 64;
        break;

      case TypeBus:
        Descriptor->ResType = ACPI_ADDRESS_SPACE_TYPE_BUS;
        break;

      default:
        break;
      }

      Descriptor++;
    }
    //
    // Terminate the root bridge resources.
    //
    End = (EFI_ACPI_END_TAG_DESCRIPTOR *) Descriptor;
    End->Desc = ACPI_END_TAG_DESCRIPTOR;
    End->Checksum = 0x0;

    Descriptor = (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *) (End + 1);
  }
  //
  // Terminate the host bridge resources.
  //
  End = (EFI_ACPI_END_TAG_DESCRIPTOR *) Descriptor;
  End->Desc = ACPI_END_TAG_DESCRIPTOR;
  End->Checksum = 0x0;

  DEBUG ((DEBUG_ERROR, "Call PciHostBridgeResourceConflict().\n"));
  PciHostBridgeResourceConflict (HostBridge->Handle, Resources);
  FreePool (Resources);
}

/**
  Allocate Length of MMIO or IO resource with alignment BitsOfAlignment
  from GCD range [BaseAddress, Limit).

  @param Mmio            TRUE for MMIO and FALSE for IO.
  @param Length          Length of the resource to allocate.
  @param BitsOfAlignment Alignment of the resource to allocate.
  @param BaseAddress     The starting address the allocation is from.
  @param Limit           The ending address the allocation is to.

  @retval  The base address of the allocated resource or MAX_UINT64 if allocation
           fails.
**/
UINT64
AllocateResource (
  BOOLEAN Mmio,
  UINT64  Length,
  UINTN   BitsOfAlignment,
  UINT64  BaseAddress,
  UINT64  Limit
  )
{
  EFI_STATUS Status;

  if (BaseAddress < Limit) {
    //
    // Have to make sure Aligment is handled since we are doing direct address allocation
    //
    BaseAddress = ALIGN_VALUE (BaseAddress, LShiftU64 (1, BitsOfAlignment));

    while (BaseAddress + Length <= Limit + 1) {
      if (Mmio) {
        Status = gDS->AllocateMemorySpace (
                        EfiGcdAllocateAddress,
                        EfiGcdMemoryTypeMemoryMappedIo,
                        BitsOfAlignment,
                        Length,
                        &BaseAddress,
                        gImageHandle,
                        NULL
                        );
      } else {
        Status = gDS->AllocateIoSpace (
                        EfiGcdAllocateAddress,
                        EfiGcdIoTypeIo,
                        BitsOfAlignment,
                        Length,
                        &BaseAddress,
                        gImageHandle,
                        NULL
                        );
      }

      if (!EFI_ERROR (Status)) {
        return BaseAddress;
      }
      BaseAddress += LShiftU64 (1, BitsOfAlignment);
    }
  }
  return MAX_UINT64;
}

/**

  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;
  PCI_ROOT_BRIDGE_INSTANCE              *RootBridge;
  LIST_ENTRY                            *Link;
  EFI_PHYSICAL_ADDRESS                  BaseAddress;
  UINTN                                 BitsOfAlignment;
  UINT64                                Alignment;
  EFI_STATUS                            Status;
  EFI_STATUS                            ReturnStatus;
  PCI_RESOURCE_TYPE                     Index;
  PCI_RESOURCE_TYPE                     Index1;
  PCI_RESOURCE_TYPE                     Index2;
  BOOLEAN                               ResNodeHandled[TypeMax];
  UINT64                                MaxAlignment;

  HostBridge = PCI_HOST_BRIDGE_FROM_THIS (This);

  switch (Phase) {
  case EfiPciHostBridgeBeginEnumeration:
    if (!HostBridge->CanRestarted) {
      return EFI_NOT_READY;
    }
    //
    // Reset Root Bridge
    //
    for (Link = GetFirstNode (&HostBridge->RootBridges)
          ; !IsNull (&HostBridge->RootBridges, Link)
          ; Link = GetNextNode (&HostBridge->RootBridges, Link)
          ) {
      RootBridge = ROOT_BRIDGE_FROM_LINK (Link);
      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;

        RootBridge->ResourceSubmitted = FALSE;
      }
    }

    HostBridge->CanRestarted = TRUE;
    break;

  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:
    ReturnStatus = EFI_SUCCESS;

    //
    // Make sure the resource for all root bridges has been submitted.
    //
    for (Link = GetFirstNode (&HostBridge->RootBridges)
         ; !IsNull (&HostBridge->RootBridges, Link)
         ; Link = GetNextNode (&HostBridge->RootBridges, Link)
         ) {
      RootBridge = ROOT_BRIDGE_FROM_LINK (Link);
      if (!RootBridge->ResourceSubmitted) {
        return EFI_NOT_READY;
      }
    }

    DEBUG ((EFI_D_INFO, "PciHostBridge: NotifyPhase (AllocateResources)\n"));
    for (Link = GetFirstNode (&HostBridge->RootBridges)
         ; !IsNull (&HostBridge->RootBridges, Link)
         ; Link = GetNextNode (&HostBridge->RootBridges, Link)
         ) {
      for (Index = TypeIo; Index < TypeBus; Index++) {
        ResNodeHandled[Index] = FALSE;
      }

      RootBridge = ROOT_BRIDGE_FROM_LINK (Link);
      DEBUG ((EFI_D_INFO, " RootBridge: %s\n", RootBridge->DevicePathStr));

      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;
            }
          }

          ASSERT (Index < TypeMax);
          ResNodeHandled[Index] = TRUE;
          Alignment = RootBridge->ResAllocNode[Index].Alignment;
          BitsOfAlignment = LowBitSet64 (Alignment + 1);
          BaseAddress = MAX_UINT64;

          switch (Index) {
          case TypeIo:
            BaseAddress = AllocateResource (
                            FALSE,
                            RootBridge->ResAllocNode[Index].Length,
                            MIN (15, BitsOfAlignment),
                            ALIGN_VALUE (RootBridge->Io.Base, Alignment + 1),
                            RootBridge->Io.Limit
                            );
            break;

          case TypeMem64:
            BaseAddress = AllocateResource (
                            TRUE,
                            RootBridge->ResAllocNode[Index].Length,
                            MIN (63, BitsOfAlignment),
                            ALIGN_VALUE (RootBridge->MemAbove4G.Base, Alignment + 1),
                            RootBridge->MemAbove4G.Limit
                            );
            if (BaseAddress != MAX_UINT64) {
              break;
            }
            //
            // If memory above 4GB is not available, try memory below 4GB
            //

          case TypeMem32:
            BaseAddress = AllocateResource (
                            TRUE,
                            RootBridge->ResAllocNode[Index].Length,
                            MIN (31, BitsOfAlignment),
                            ALIGN_VALUE (RootBridge->Mem.Base, Alignment + 1),
                            RootBridge->Mem.Limit
                            );
            break;

          case TypePMem64:
            BaseAddress = AllocateResource (
                            TRUE,
                            RootBridge->ResAllocNode[Index].Length,
                            MIN (63, BitsOfAlignment),
                            ALIGN_VALUE (RootBridge->PMemAbove4G.Base, Alignment + 1),
                            RootBridge->PMemAbove4G.Limit
                            );
            if (BaseAddress != MAX_UINT64) {
              break;
            }
            //
            // If memory above 4GB is not available, try memory below 4GB
            //
          case TypePMem32:
            BaseAddress = AllocateResource (
                            TRUE,
                            RootBridge->ResAllocNode[Index].Length,
                            MIN (31, BitsOfAlignment),
                            ALIGN_VALUE (RootBridge->PMem.Base, Alignment + 1),
                            RootBridge->PMem.Limit
                            );
            break;

          default:
            ASSERT (FALSE);
            break;
          }

          DEBUG ((DEBUG_INFO, "  %s: Base/Length/Alignment = %lx/%lx/%lx - ",
                  mPciResourceTypeStr[Index], BaseAddress, RootBridge->ResAllocNode[Index].Length, Alignment));
          if (BaseAddress != MAX_UINT64) {
            RootBridge->ResAllocNode[Index].Base = BaseAddress;
            RootBridge->ResAllocNode[Index].Status = ResAllocated;
            DEBUG ((DEBUG_INFO, "Success\n"));
          } else {
            ReturnStatus = EFI_OUT_OF_RESOURCES;
            DEBUG ((DEBUG_ERROR, "Out Of Resource!\n"));
          }
        }
      }
    }

    if (ReturnStatus == EFI_OUT_OF_RESOURCES) {
      ResourceConflict (HostBridge);
    }

    //
    // Set resource to zero for nodes where allocation fails
    //
    for (Link = GetFirstNode (&HostBridge->RootBridges)
          ; !IsNull (&HostBridge->RootBridges, Link)
          ; Link = GetNextNode (&HostBridge->RootBridges, Link)
          ) {
      RootBridge = ROOT_BRIDGE_FROM_LINK (Link);
      for (Index = TypeIo; Index < TypeBus; Index++) {
        if (RootBridge->ResAllocNode[Index].Status != ResAllocated) {
          RootBridge->ResAllocNode[Index].Length = 0;
        }
      }
    }
    return ReturnStatus;

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

  case EfiPciHostBridgeFreeResources:
    //
    // HostBridgeInstance->CanRestarted = FALSE;
    //
    ReturnStatus = EFI_SUCCESS;
    for (Link = GetFirstNode (&HostBridge->RootBridges)
         ; !IsNull (&HostBridge->RootBridges, Link)
         ; Link = GetNextNode (&HostBridge->RootBridges, Link)
         ) {
      RootBridge = ROOT_BRIDGE_FROM_LINK (Link);
      for (Index = TypeIo; Index < TypeBus; Index++) {
        if (RootBridge->ResAllocNode[Index].Status == ResAllocated) {
          switch (Index) {
          case TypeIo:
            Status = gDS->FreeIoSpace (RootBridge->ResAllocNode[Index].Base, RootBridge->ResAllocNode[Index].Length);
            if (EFI_ERROR (Status)) {
              ReturnStatus = Status;
            }
            break;

          case TypeMem32:
          case TypePMem32:
          case TypeMem64:
          case TypePMem64:
            Status = gDS->FreeMemorySpace (RootBridge->ResAllocNode[Index].Base, RootBridge->ResAllocNode[Index].Length);
            if (EFI_ERROR (Status)) {
              ReturnStatus = Status;
            }
            break;

          default:
            ASSERT (FALSE);
            break;
          }

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

      RootBridge->ResourceSubmitted = FALSE;
    }

    HostBridge->CanRestarted = TRUE;
    return ReturnStatus;

  case EfiPciHostBridgeEndResourceAllocation:
    //
    // The resource allocation phase is completed.  No specific action is required
    // here. This notification can be used to perform any chipset specific programming.
    //
    break;

  case EfiPciHostBridgeEndEnumeration:
    //
    // The Host Bridge Enumeration is completed. No specific action is required here.
    // This notification can be used to perform any chipset specific programming.
    //
    break;

  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                   ReturnNext;
  LIST_ENTRY                *Link;
  PCI_HOST_BRIDGE_INSTANCE  *HostBridge;
  PCI_ROOT_BRIDGE_INSTANCE  *RootBridge;

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

  HostBridge = PCI_HOST_BRIDGE_FROM_THIS (This);
  ReturnNext = (BOOLEAN) (*RootBridgeHandle == NULL);

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

    ReturnNext = (BOOLEAN) (*RootBridgeHandle == RootBridge->Handle);
  }

  if (ReturnNext) {
    ASSERT (IsNull (&HostBridge->RootBridges, Link));
    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                *Link;
  PCI_HOST_BRIDGE_INSTANCE  *HostBridge;
  PCI_ROOT_BRIDGE_INSTANCE  *RootBridge;
  EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Descriptor;
  EFI_ACPI_END_TAG_DESCRIPTOR       *End;

  if (Configuration == 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) {
      *Configuration = AllocatePool (sizeof (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR) + sizeof (EFI_ACPI_END_TAG_DESCRIPTOR));
      if (*Configuration == NULL) {
        return EFI_OUT_OF_RESOURCES;
      }

      Descriptor = (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *) *Configuration;
      Descriptor->Desc                  = ACPI_ADDRESS_SPACE_DESCRIPTOR;
      Descriptor->Len                   = sizeof (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR) - 3;
      Descriptor->ResType               = ACPI_ADDRESS_SPACE_TYPE_BUS;
      Descriptor->GenFlag               = 0;
      Descriptor->SpecificFlag          = 0;
      Descriptor->AddrSpaceGranularity  = 0;
      Descriptor->AddrRangeMin          = RootBridge->Bus.Base;
      Descriptor->AddrRangeMax          = 0;
      Descriptor->AddrTranslationOffset = 0;
      Descriptor->AddrLen               = RootBridge->Bus.Limit - RootBridge->Bus.Base + 1;

      End = (EFI_ACPI_END_TAG_DESCRIPTOR *) (Descriptor + 1);
      End->Desc = ACPI_END_TAG_DESCRIPTOR;
      End->Checksum = 0x0;

      return EFI_SUCCESS;
    }
  }

  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                *Link;
  PCI_HOST_BRIDGE_INSTANCE  *HostBridge;
  PCI_ROOT_BRIDGE_INSTANCE  *RootBridge;
  EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Descriptor;
  EFI_ACPI_END_TAG_DESCRIPTOR       *End;

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

  Descriptor = (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *) Configuration;
  End = (EFI_ACPI_END_TAG_DESCRIPTOR *) (Descriptor + 1);

  //
  // Check the Configuration is valid
  //
  if ((Descriptor->Desc != ACPI_ADDRESS_SPACE_DESCRIPTOR) ||
      (Descriptor->ResType != ACPI_ADDRESS_SPACE_TYPE_BUS) ||
      (End->Desc != ACPI_END_TAG_DESCRIPTOR)
     ) {
    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) {

      if (Descriptor->AddrLen == 0) {
        return EFI_INVALID_PARAMETER;
      }

      if ((Descriptor->AddrRangeMin < RootBridge->Bus.Base) ||
          (Descriptor->AddrRangeMin + Descriptor->AddrLen - 1 > RootBridge->Bus.Limit)
         ) {
        return EFI_INVALID_PARAMETER;
      }
      //
      // Update the Bus Range
      //
      RootBridge->ResAllocNode[TypeBus].Base    = Descriptor->AddrRangeMin;
      RootBridge->ResAllocNode[TypeBus].Length  = Descriptor->AddrLen;
      RootBridge->ResAllocNode[TypeBus].Status  = ResAllocated;
      return EFI_SUCCESS;
    }
  }

  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                        *Link;
  PCI_HOST_BRIDGE_INSTANCE          *HostBridge;
  PCI_ROOT_BRIDGE_INSTANCE          *RootBridge;
  EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Descriptor;
  PCI_RESOURCE_TYPE                 Type;

  //
  // Check the input parameter: Configuration
  //
  if (Configuration == 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) {
      DEBUG ((EFI_D_INFO, "PciHostBridge: SubmitResources for %s\n", RootBridge->DevicePathStr));
      //
      // 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 (Descriptor = (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *) Configuration; Descriptor->Desc == ACPI_ADDRESS_SPACE_DESCRIPTOR; Descriptor++) {
        if (Descriptor->ResType > ACPI_ADDRESS_SPACE_TYPE_BUS) {
          return EFI_INVALID_PARAMETER;
        }

        DEBUG ((EFI_D_INFO, " %s: Granularity/SpecificFlag = %ld / %02x%s\n",
                mAcpiAddressSpaceTypeStr[Descriptor->ResType], Descriptor->AddrSpaceGranularity, Descriptor->SpecificFlag,
                (Descriptor->SpecificFlag & EFI_ACPI_MEMORY_RESOURCE_SPECIFIC_FLAG_CACHEABLE_PREFETCHABLE) != 0 ? L" (Prefetchable)" : L""
                ));
        DEBUG ((EFI_D_INFO, "      Length/Alignment = 0x%lx / 0x%lx\n", Descriptor->AddrLen, Descriptor->AddrRangeMax));
        switch (Descriptor->ResType) {
        case ACPI_ADDRESS_SPACE_TYPE_MEM:
          if (Descriptor->AddrSpaceGranularity != 32 && Descriptor->AddrSpaceGranularity != 64) {
            return EFI_INVALID_PARAMETER;
          }
          if (Descriptor->AddrSpaceGranularity == 32 && Descriptor->AddrLen >= SIZE_4GB) {
            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) &&
              ((Descriptor->SpecificFlag & EFI_ACPI_MEMORY_RESOURCE_SPECIFIC_FLAG_CACHEABLE_PREFETCHABLE) != 0)
             ) {
            return EFI_INVALID_PARAMETER;
          }
        case ACPI_ADDRESS_SPACE_TYPE_IO:
          //
          // Check aligment, it should be of the form 2^n-1
          //
          if (GetPowerOfTwo64 (Descriptor->AddrRangeMax + 1) != (Descriptor->AddrRangeMax + 1)) {
            return EFI_INVALID_PARAMETER;
          }
          break;
        default:
          ASSERT (FALSE);
          break;
        }
      }
      if (Descriptor->Desc != ACPI_END_TAG_DESCRIPTOR) {
        return EFI_INVALID_PARAMETER;
      }

      for (Descriptor = (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *) Configuration; Descriptor->Desc == ACPI_ADDRESS_SPACE_DESCRIPTOR; Descriptor++) {
        if (Descriptor->ResType == ACPI_ADDRESS_SPACE_TYPE_MEM) {
          if (Descriptor->AddrSpaceGranularity == 32) {
            if ((Descriptor->SpecificFlag & EFI_ACPI_MEMORY_RESOURCE_SPECIFIC_FLAG_CACHEABLE_PREFETCHABLE) != 0) {
              Type = TypePMem32;
            } else {
              Type = TypeMem32;
            }
          } else {
            ASSERT (Descriptor->AddrSpaceGranularity == 64);
            if ((Descriptor->SpecificFlag & EFI_ACPI_MEMORY_RESOURCE_SPECIFIC_FLAG_CACHEABLE_PREFETCHABLE) != 0) {
              Type = TypePMem64;
            } else {
              Type = TypeMem64;
            }
          }
        } else {
          ASSERT (Descriptor->ResType == ACPI_ADDRESS_SPACE_TYPE_IO);
          Type = TypeIo;
        }
        RootBridge->ResAllocNode[Type].Length    = Descriptor->AddrLen;
        RootBridge->ResAllocNode[Type].Alignment = Descriptor->AddrRangeMax;
        RootBridge->ResAllocNode[Type].Status    = ResSubmitted;
      }
      RootBridge->ResourceSubmitted = TRUE;
      return EFI_SUCCESS;
    }
  }

  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                        *Link;
  PCI_HOST_BRIDGE_INSTANCE          *HostBridge;
  PCI_ROOT_BRIDGE_INSTANCE          *RootBridge;
  UINTN                             Index;
  UINTN                             Number;
  VOID                              *Buffer;
  EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Descriptor;
  EFI_ACPI_END_TAG_DESCRIPTOR       *End;
  UINT64                            ResStatus;

  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) {
      for (Index = 0, Number = 0; Index < TypeBus; Index++) {
        if (RootBridge->ResAllocNode[Index].Status != ResNone) {
          Number++;
        }
      }

      Buffer = AllocateZeroPool (Number * sizeof (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR) + sizeof (EFI_ACPI_END_TAG_DESCRIPTOR));
      if (Buffer == NULL) {
        return EFI_OUT_OF_RESOURCES;
      }

      Descriptor = (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *) Buffer;
      for (Index = 0; Index < TypeBus; Index++) {
        ResStatus = RootBridge->ResAllocNode[Index].Status;
        if (ResStatus != ResNone) {
          Descriptor->Desc                  = ACPI_ADDRESS_SPACE_DESCRIPTOR;
          Descriptor->Len                   = sizeof (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR) - 3;;
          Descriptor->GenFlag               = 0;
          Descriptor->AddrRangeMin          = RootBridge->ResAllocNode[Index].Base;
          Descriptor->AddrRangeMax          = 0;
          Descriptor->AddrTranslationOffset = (ResStatus == ResAllocated) ? EFI_RESOURCE_SATISFIED : PCI_RESOURCE_LESS;
          Descriptor->AddrLen               = RootBridge->ResAllocNode[Index].Length;

          switch (Index) {

          case TypeIo:
            Descriptor->ResType              = ACPI_ADDRESS_SPACE_TYPE_IO;
            break;

          case TypePMem32:
            Descriptor->SpecificFlag = EFI_ACPI_MEMORY_RESOURCE_SPECIFIC_FLAG_CACHEABLE_PREFETCHABLE;
          case TypeMem32:
            Descriptor->ResType              = ACPI_ADDRESS_SPACE_TYPE_MEM;
            Descriptor->AddrSpaceGranularity = 32;
            break;

          case TypePMem64:
            Descriptor->SpecificFlag = EFI_ACPI_MEMORY_RESOURCE_SPECIFIC_FLAG_CACHEABLE_PREFETCHABLE;
          case TypeMem64:
            Descriptor->ResType              = ACPI_ADDRESS_SPACE_TYPE_MEM;
            Descriptor->AddrSpaceGranularity = 64;
            break;
          }

          Descriptor++;
        }
      }
      End = (EFI_ACPI_END_TAG_DESCRIPTOR *) Descriptor;
      End->Desc      = ACPI_END_TAG_DESCRIPTOR;
      End->Checksum  = 0;

      *Configuration = Buffer;

      return EFI_SUCCESS;
    }
  }

  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
  )
{
  LIST_ENTRY                *Link;
  PCI_HOST_BRIDGE_INSTANCE  *HostBridge;
  PCI_ROOT_BRIDGE_INSTANCE  *RootBridge;

  if ((UINT32) Phase > EfiPciBeforeResourceCollection) {
    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) {
      return EFI_SUCCESS;
    }
  }

  return EFI_INVALID_PARAMETER;
}
