/** @file
  Supporting functions implementation for PCI devices management.

Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.<BR>
(C) Copyright 2018 Hewlett Packard Enterprise Development LP<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#include "PciBus.h"

//
// This device structure is serviced as a header.
// Its next field points to the first root bridge device node.
//
LIST_ENTRY  mPciDevicePool;

/**
  Initialize the PCI devices pool.

**/
VOID
InitializePciDevicePool (
  VOID
  )
{
  InitializeListHead (&mPciDevicePool);
}

/**
  Insert a root bridge into PCI device pool.

  @param RootBridge     A pointer to the PCI_IO_DEVICE.

**/
VOID
InsertRootBridge (
  IN PCI_IO_DEVICE  *RootBridge
  )
{
  InsertTailList (&mPciDevicePool, &(RootBridge->Link));
}

/**
  This function is used to insert a PCI device node under
  a bridge.

  @param Bridge         The PCI bridge.
  @param PciDeviceNode  The PCI device needs inserting.

**/
VOID
InsertPciDevice (
  IN PCI_IO_DEVICE  *Bridge,
  IN PCI_IO_DEVICE  *PciDeviceNode
  )
{
  InsertTailList (&Bridge->ChildList, &(PciDeviceNode->Link));
  PciDeviceNode->Parent = Bridge;
}

/**
  Destroy root bridge and remove it from device tree.

  @param RootBridge     The bridge want to be removed.

**/
VOID
DestroyRootBridge (
  IN PCI_IO_DEVICE  *RootBridge
  )
{
  DestroyPciDeviceTree (RootBridge);

  FreePciDevice (RootBridge);
}

/**
  Destroy a pci device node.

  All direct or indirect allocated resource for this node will be freed.

  @param PciIoDevice  A pointer to the PCI_IO_DEVICE to be destroyed.

**/
VOID
FreePciDevice (
  IN PCI_IO_DEVICE  *PciIoDevice
  )
{
  ASSERT (PciIoDevice != NULL);
  //
  // Assume all children have been removed underneath this device
  //
  if (PciIoDevice->ResourcePaddingDescriptors != NULL) {
    FreePool (PciIoDevice->ResourcePaddingDescriptors);
  }

  if (PciIoDevice->DevicePath != NULL) {
    FreePool (PciIoDevice->DevicePath);
  }

  if (PciIoDevice->BusNumberRanges != NULL) {
    FreePool (PciIoDevice->BusNumberRanges);
  }

  FreePool (PciIoDevice);
}

/**
  Destroy all the pci device node under the bridge.
  Bridge itself is not included.

  @param Bridge      A pointer to the PCI_IO_DEVICE.

**/
VOID
DestroyPciDeviceTree (
  IN PCI_IO_DEVICE  *Bridge
  )
{
  LIST_ENTRY     *CurrentLink;
  PCI_IO_DEVICE  *Temp;

  while (!IsListEmpty (&Bridge->ChildList)) {
    CurrentLink = Bridge->ChildList.ForwardLink;

    //
    // Remove this node from the linked list
    //
    RemoveEntryList (CurrentLink);

    Temp = PCI_IO_DEVICE_FROM_LINK (CurrentLink);

    if (!IsListEmpty (&Temp->ChildList)) {
      DestroyPciDeviceTree (Temp);
    }

    FreePciDevice (Temp);
  }
}

/**
  Destroy all device nodes under the root bridge
  specified by Controller.

  The root bridge itself is also included.

  @param  Controller    Root bridge handle.

  @retval EFI_SUCCESS   Destroy all device nodes successfully.
  @retval EFI_NOT_FOUND Cannot find any PCI device under specified
                        root bridge.

**/
EFI_STATUS
DestroyRootBridgeByHandle (
  IN EFI_HANDLE  Controller
  )
{
  LIST_ENTRY     *CurrentLink;
  PCI_IO_DEVICE  *Temp;

  CurrentLink = mPciDevicePool.ForwardLink;

  while (CurrentLink != NULL && CurrentLink != &mPciDevicePool) {
    Temp = PCI_IO_DEVICE_FROM_LINK (CurrentLink);

    if (Temp->Handle == Controller) {
      RemoveEntryList (CurrentLink);

      DestroyPciDeviceTree (Temp);

      FreePciDevice (Temp);

      return EFI_SUCCESS;
    }

    CurrentLink = CurrentLink->ForwardLink;
  }

  return EFI_NOT_FOUND;
}

/**
  This function registers the PCI IO device.

  It creates a handle for this PCI IO device (if the handle does not exist), attaches
  appropriate protocols onto the handle, does necessary initialization, and sets up
  parent/child relationship with its bus controller.

  @param Controller     An EFI handle for the PCI bus controller.
  @param PciIoDevice    A PCI_IO_DEVICE pointer to the PCI IO device to be registered.
  @param Handle         A pointer to hold the returned EFI handle for the PCI IO device.

  @retval EFI_SUCCESS   The PCI device is successfully registered.
  @retval other         An error occurred when registering the PCI device.

**/
EFI_STATUS
RegisterPciDevice (
  IN  EFI_HANDLE     Controller,
  IN  PCI_IO_DEVICE  *PciIoDevice,
  OUT EFI_HANDLE     *Handle      OPTIONAL
  )
{
  EFI_STATUS           Status;
  VOID                 *PlatformOpRomBuffer;
  UINTN                PlatformOpRomSize;
  EFI_PCI_IO_PROTOCOL  *PciIo;
  UINT8                Data8;
  BOOLEAN              HasEfiImage;

  //
  // Install the pciio protocol, device path protocol
  //
  Status = gBS->InstallMultipleProtocolInterfaces (
                  &PciIoDevice->Handle,
                  &gEfiDevicePathProtocolGuid,
                  PciIoDevice->DevicePath,
                  &gEfiPciIoProtocolGuid,
                  &PciIoDevice->PciIo,
                  NULL
                  );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // Force Interrupt line to "Unknown" or "No Connection"
  //
  PciIo = &(PciIoDevice->PciIo);
  Data8 = PCI_INT_LINE_UNKNOWN;
  PciIo->Pci.Write (PciIo, EfiPciIoWidthUint8, 0x3C, 1, &Data8);

  //
  // Process OpRom
  //
  if (!PciIoDevice->AllOpRomProcessed) {
    //
    // Get the OpRom provided by platform
    //
    if (gPciPlatformProtocol != NULL) {
      Status = gPciPlatformProtocol->GetPciRom (
                                       gPciPlatformProtocol,
                                       PciIoDevice->Handle,
                                       &PlatformOpRomBuffer,
                                       &PlatformOpRomSize
                                       );
      if (!EFI_ERROR (Status)) {
        PciIoDevice->EmbeddedRom    = FALSE;
        PciIoDevice->RomSize        = (UINT32)PlatformOpRomSize;
        PciIoDevice->PciIo.RomSize  = PlatformOpRomSize;
        PciIoDevice->PciIo.RomImage = PlatformOpRomBuffer;
        //
        // For OpROM read from gPciPlatformProtocol:
        // Add the Rom Image to internal database for later PCI light enumeration
        //
        PciRomAddImageMapping (
          NULL,
          PciIoDevice->PciRootBridgeIo->SegmentNumber,
          PciIoDevice->BusNumber,
          PciIoDevice->DeviceNumber,
          PciIoDevice->FunctionNumber,
          PciIoDevice->PciIo.RomImage,
          PciIoDevice->PciIo.RomSize
          );
      }
    } else if (gPciOverrideProtocol != NULL) {
      Status = gPciOverrideProtocol->GetPciRom (
                                       gPciOverrideProtocol,
                                       PciIoDevice->Handle,
                                       &PlatformOpRomBuffer,
                                       &PlatformOpRomSize
                                       );
      if (!EFI_ERROR (Status)) {
        PciIoDevice->EmbeddedRom    = FALSE;
        PciIoDevice->RomSize        = (UINT32)PlatformOpRomSize;
        PciIoDevice->PciIo.RomSize  = PlatformOpRomSize;
        PciIoDevice->PciIo.RomImage = PlatformOpRomBuffer;
        //
        // For OpROM read from gPciOverrideProtocol:
        // Add the Rom Image to internal database for later PCI light enumeration
        //
        PciRomAddImageMapping (
          NULL,
          PciIoDevice->PciRootBridgeIo->SegmentNumber,
          PciIoDevice->BusNumber,
          PciIoDevice->DeviceNumber,
          PciIoDevice->FunctionNumber,
          PciIoDevice->PciIo.RomImage,
          PciIoDevice->PciIo.RomSize
          );
      }
    }
  }

  //
  // Determine if there are EFI images in the option rom
  //
  HasEfiImage = ContainEfiImage (PciIoDevice->PciIo.RomImage, PciIoDevice->PciIo.RomSize);

  if (HasEfiImage) {
    Status = gBS->InstallMultipleProtocolInterfaces (
                    &PciIoDevice->Handle,
                    &gEfiLoadFile2ProtocolGuid,
                    &PciIoDevice->LoadFile2,
                    NULL
                    );
    if (EFI_ERROR (Status)) {
      gBS->UninstallMultipleProtocolInterfaces (
             PciIoDevice->Handle,
             &gEfiDevicePathProtocolGuid,
             PciIoDevice->DevicePath,
             &gEfiPciIoProtocolGuid,
             &PciIoDevice->PciIo,
             NULL
             );
      return Status;
    }
  }

  if (!PciIoDevice->AllOpRomProcessed) {
    PciIoDevice->AllOpRomProcessed = TRUE;

    //
    // Dispatch the EFI OpRom for the PCI device.
    // The OpRom is got from platform in the above code
    // or loaded from device in the previous round of bus enumeration
    //
    if (HasEfiImage) {
      ProcessOpRomImage (PciIoDevice);
    }
  }

  if (PciIoDevice->BusOverride) {
    //
    // Install Bus Specific Driver Override Protocol
    //
    Status = gBS->InstallMultipleProtocolInterfaces (
                    &PciIoDevice->Handle,
                    &gEfiBusSpecificDriverOverrideProtocolGuid,
                    &PciIoDevice->PciDriverOverride,
                    NULL
                    );
    if (EFI_ERROR (Status)) {
      gBS->UninstallMultipleProtocolInterfaces (
             PciIoDevice->Handle,
             &gEfiDevicePathProtocolGuid,
             PciIoDevice->DevicePath,
             &gEfiPciIoProtocolGuid,
             &PciIoDevice->PciIo,
             NULL
             );
      if (HasEfiImage) {
        gBS->UninstallMultipleProtocolInterfaces (
               PciIoDevice->Handle,
               &gEfiLoadFile2ProtocolGuid,
               &PciIoDevice->LoadFile2,
               NULL
               );
      }

      return Status;
    }
  }

  Status = gBS->OpenProtocol (
                  Controller,
                  &gEfiPciRootBridgeIoProtocolGuid,
                  (VOID **)&(PciIoDevice->PciRootBridgeIo),
                  gPciBusDriverBinding.DriverBindingHandle,
                  PciIoDevice->Handle,
                  EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
                  );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  if (Handle != NULL) {
    *Handle = PciIoDevice->Handle;
  }

  //
  // Indicate the pci device is registered
  //
  PciIoDevice->Registered = TRUE;

  return EFI_SUCCESS;
}

/**
  This function is used to remove the whole PCI devices on the specified bridge from
  the root bridge.

  @param RootBridgeHandle   The root bridge device handle.
  @param Bridge             The bridge device to be removed.

**/
VOID
RemoveAllPciDeviceOnBridge (
  EFI_HANDLE     RootBridgeHandle,
  PCI_IO_DEVICE  *Bridge
  )
{
  LIST_ENTRY     *CurrentLink;
  PCI_IO_DEVICE  *Temp;

  while (!IsListEmpty (&Bridge->ChildList)) {
    CurrentLink = Bridge->ChildList.ForwardLink;
    Temp        = PCI_IO_DEVICE_FROM_LINK (CurrentLink);

    //
    // Check if the current node has been deregistered before
    // If it is not, then deregister it
    //
    if (Temp->Registered) {
      DeRegisterPciDevice (RootBridgeHandle, Temp->Handle);
    }

    //
    // Remove this node from the linked list
    //
    RemoveEntryList (CurrentLink);

    if (!IsListEmpty (&Temp->ChildList)) {
      RemoveAllPciDeviceOnBridge (RootBridgeHandle, Temp);
    }

    FreePciDevice (Temp);
  }
}

/**
  This function is used to de-register the PCI IO device.

  That includes un-installing PciIo protocol from the specified PCI
  device handle.

  @param Controller    An EFI handle for the PCI bus controller.
  @param Handle        PCI device handle.

  @retval EFI_SUCCESS  The PCI device is successfully de-registered.
  @retval other        An error occurred when de-registering the PCI device.

**/
EFI_STATUS
DeRegisterPciDevice (
  IN  EFI_HANDLE  Controller,
  IN  EFI_HANDLE  Handle
  )

{
  EFI_PCI_IO_PROTOCOL              *PciIo;
  EFI_STATUS                       Status;
  PCI_IO_DEVICE                    *PciIoDevice;
  PCI_IO_DEVICE                    *Node;
  LIST_ENTRY                       *CurrentLink;
  EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL  *PciRootBridgeIo;

  Status = gBS->OpenProtocol (
                  Handle,
                  &gEfiPciIoProtocolGuid,
                  (VOID **)&PciIo,
                  gPciBusDriverBinding.DriverBindingHandle,
                  Controller,
                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
                  );
  if (!EFI_ERROR (Status)) {
    PciIoDevice = PCI_IO_DEVICE_FROM_PCI_IO_THIS (PciIo);

    //
    // If it is already de-registered
    //
    if (!PciIoDevice->Registered) {
      return EFI_SUCCESS;
    }

    //
    // If it is PPB, first de-register its children
    //

    if (!IsListEmpty (&PciIoDevice->ChildList)) {
      CurrentLink = PciIoDevice->ChildList.ForwardLink;

      while (CurrentLink != NULL && CurrentLink != &PciIoDevice->ChildList) {
        Node   = PCI_IO_DEVICE_FROM_LINK (CurrentLink);
        Status = DeRegisterPciDevice (Controller, Node->Handle);

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

        CurrentLink = CurrentLink->ForwardLink;
      }
    }

    //
    // Close the child handle
    //
    Status = gBS->CloseProtocol (
                    Controller,
                    &gEfiPciRootBridgeIoProtocolGuid,
                    gPciBusDriverBinding.DriverBindingHandle,
                    Handle
                    );

    //
    // Un-install the Device Path protocol and PCI I/O protocol
    // and Bus Specific Driver Override protocol if needed.
    //
    if (PciIoDevice->BusOverride) {
      Status = gBS->UninstallMultipleProtocolInterfaces (
                      Handle,
                      &gEfiDevicePathProtocolGuid,
                      PciIoDevice->DevicePath,
                      &gEfiPciIoProtocolGuid,
                      &PciIoDevice->PciIo,
                      &gEfiBusSpecificDriverOverrideProtocolGuid,
                      &PciIoDevice->PciDriverOverride,
                      NULL
                      );
    } else {
      Status = gBS->UninstallMultipleProtocolInterfaces (
                      Handle,
                      &gEfiDevicePathProtocolGuid,
                      PciIoDevice->DevicePath,
                      &gEfiPciIoProtocolGuid,
                      &PciIoDevice->PciIo,
                      NULL
                      );
    }

    if (!EFI_ERROR (Status)) {
      //
      // Try to uninstall LoadFile2 protocol if exists
      //
      Status = gBS->OpenProtocol (
                      Handle,
                      &gEfiLoadFile2ProtocolGuid,
                      NULL,
                      gPciBusDriverBinding.DriverBindingHandle,
                      Controller,
                      EFI_OPEN_PROTOCOL_TEST_PROTOCOL
                      );
      if (!EFI_ERROR (Status)) {
        Status = gBS->UninstallMultipleProtocolInterfaces (
                        Handle,
                        &gEfiLoadFile2ProtocolGuid,
                        &PciIoDevice->LoadFile2,
                        NULL
                        );
      }

      //
      // Restore Status
      //
      Status = EFI_SUCCESS;
    }

    if (EFI_ERROR (Status)) {
      gBS->OpenProtocol (
             Controller,
             &gEfiPciRootBridgeIoProtocolGuid,
             (VOID **)&PciRootBridgeIo,
             gPciBusDriverBinding.DriverBindingHandle,
             Handle,
             EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
             );
      return Status;
    }

    //
    // The Device Driver should disable this device after disconnect
    // so the Pci Bus driver will not touch this device any more.
    // Restore the register field to the original value
    //
    PciIoDevice->Registered = FALSE;
    PciIoDevice->Handle     = NULL;
  } else {
    //
    // Handle may be closed before
    //
    return EFI_SUCCESS;
  }

  return EFI_SUCCESS;
}

/**
  Start to manage the PCI device on the specified root bridge or PCI-PCI Bridge.

  @param Controller          The root bridge handle.
  @param RootBridge          A pointer to the PCI_IO_DEVICE.
  @param RemainingDevicePath A pointer to the EFI_DEVICE_PATH_PROTOCOL.
  @param NumberOfChildren    Children number.
  @param ChildHandleBuffer   A pointer to the child handle buffer.

  @retval EFI_NOT_READY   Device is not allocated.
  @retval EFI_UNSUPPORTED Device only support PCI-PCI bridge.
  @retval EFI_NOT_FOUND   Can not find the specific device.
  @retval EFI_SUCCESS     Success to start Pci devices on bridge.

**/
EFI_STATUS
StartPciDevicesOnBridge (
  IN EFI_HANDLE                Controller,
  IN PCI_IO_DEVICE             *RootBridge,
  IN EFI_DEVICE_PATH_PROTOCOL  *RemainingDevicePath,
  IN OUT UINT8                 *NumberOfChildren,
  IN OUT EFI_HANDLE            *ChildHandleBuffer
  )

{
  PCI_IO_DEVICE             *PciIoDevice;
  EFI_DEV_PATH_PTR          Node;
  EFI_DEVICE_PATH_PROTOCOL  *CurrentDevicePath;
  EFI_STATUS                Status;
  LIST_ENTRY                *CurrentLink;
  UINT64                    Supports;

  PciIoDevice = NULL;
  CurrentLink = RootBridge->ChildList.ForwardLink;

  while (CurrentLink != NULL && CurrentLink != &RootBridge->ChildList) {
    PciIoDevice = PCI_IO_DEVICE_FROM_LINK (CurrentLink);
    if (RemainingDevicePath != NULL) {
      Node.DevPath = RemainingDevicePath;

      if ((Node.Pci->Device != PciIoDevice->DeviceNumber) ||
          (Node.Pci->Function != PciIoDevice->FunctionNumber))
      {
        CurrentLink = CurrentLink->ForwardLink;
        continue;
      }

      //
      // Check if the device has been assigned with required resource
      //
      if (!PciIoDevice->Allocated) {
        return EFI_NOT_READY;
      }

      //
      // Check if the current node has been registered before
      // If it is not, register it
      //
      if (!PciIoDevice->Registered) {
        Status = RegisterPciDevice (
                   Controller,
                   PciIoDevice,
                   NULL
                   );
      }

      if ((NumberOfChildren != NULL) && (ChildHandleBuffer != NULL) && PciIoDevice->Registered) {
        ChildHandleBuffer[*NumberOfChildren] = PciIoDevice->Handle;
        (*NumberOfChildren)++;
      }

      //
      // Get the next device path
      //
      CurrentDevicePath = NextDevicePathNode (RemainingDevicePath);
      if (IsDevicePathEnd (CurrentDevicePath)) {
        return EFI_SUCCESS;
      }

      //
      // If it is a PPB
      //
      if (IS_PCI_BRIDGE (&PciIoDevice->Pci)) {
        Status = StartPciDevicesOnBridge (
                   Controller,
                   PciIoDevice,
                   CurrentDevicePath,
                   NumberOfChildren,
                   ChildHandleBuffer
                   );

        PciIoDevice->PciIo.Attributes (
                             &(PciIoDevice->PciIo),
                             EfiPciIoAttributeOperationSupported,
                             0,
                             &Supports
                             );
        Supports &= (UINT64)EFI_PCI_DEVICE_ENABLE;
        PciIoDevice->PciIo.Attributes (
                             &(PciIoDevice->PciIo),
                             EfiPciIoAttributeOperationEnable,
                             Supports,
                             NULL
                             );

        return Status;
      } else {
        //
        // Currently, the PCI bus driver only support PCI-PCI bridge
        //
        return EFI_UNSUPPORTED;
      }
    } else {
      //
      // If remaining device path is NULL,
      // try to enable all the pci devices under this bridge
      //
      if (!PciIoDevice->Registered && PciIoDevice->Allocated) {
        Status = RegisterPciDevice (
                   Controller,
                   PciIoDevice,
                   NULL
                   );
      }

      if ((NumberOfChildren != NULL) && (ChildHandleBuffer != NULL) && PciIoDevice->Registered) {
        ChildHandleBuffer[*NumberOfChildren] = PciIoDevice->Handle;
        (*NumberOfChildren)++;
      }

      if (IS_PCI_BRIDGE (&PciIoDevice->Pci)) {
        Status = StartPciDevicesOnBridge (
                   Controller,
                   PciIoDevice,
                   RemainingDevicePath,
                   NumberOfChildren,
                   ChildHandleBuffer
                   );

        PciIoDevice->PciIo.Attributes (
                             &(PciIoDevice->PciIo),
                             EfiPciIoAttributeOperationSupported,
                             0,
                             &Supports
                             );
        Supports &= (UINT64)EFI_PCI_DEVICE_ENABLE;
        PciIoDevice->PciIo.Attributes (
                             &(PciIoDevice->PciIo),
                             EfiPciIoAttributeOperationEnable,
                             Supports,
                             NULL
                             );
      }

      CurrentLink = CurrentLink->ForwardLink;
    }
  }

  if (PciIoDevice == NULL) {
    return EFI_NOT_FOUND;
  } else {
    return EFI_SUCCESS;
  }
}

/**
  Start to manage all the PCI devices it found previously under
  the entire host bridge.

  @param Controller          The root bridge handle.

  @retval EFI_NOT_READY   Device is not allocated.
  @retval EFI_SUCCESS     Success to start Pci device on host bridge.

**/
EFI_STATUS
StartPciDevices (
  IN EFI_HANDLE  Controller
  )
{
  PCI_IO_DEVICE  *RootBridge;
  EFI_HANDLE     ThisHostBridge;
  LIST_ENTRY     *CurrentLink;

  RootBridge = GetRootBridgeByHandle (Controller);
  ASSERT (RootBridge != NULL);
  ThisHostBridge = RootBridge->PciRootBridgeIo->ParentHandle;

  CurrentLink = mPciDevicePool.ForwardLink;

  while (CurrentLink != NULL && CurrentLink != &mPciDevicePool) {
    RootBridge = PCI_IO_DEVICE_FROM_LINK (CurrentLink);
    //
    // Locate the right root bridge to start
    //
    if (RootBridge->PciRootBridgeIo->ParentHandle == ThisHostBridge) {
      StartPciDevicesOnBridge (
        RootBridge->Handle,
        RootBridge,
        NULL,
        NULL,
        NULL
        );
    }

    CurrentLink = CurrentLink->ForwardLink;
  }

  return EFI_SUCCESS;
}

/**
  Create root bridge device.

  @param RootBridgeHandle    Specified root bridge handle.

  @return The crated root bridge device instance, NULL means no
          root bridge device instance created.

**/
PCI_IO_DEVICE *
CreateRootBridge (
  IN EFI_HANDLE  RootBridgeHandle
  )
{
  EFI_STATUS                       Status;
  PCI_IO_DEVICE                    *Dev;
  EFI_DEVICE_PATH_PROTOCOL         *ParentDevicePath;
  EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL  *PciRootBridgeIo;

  Dev = AllocateZeroPool (sizeof (PCI_IO_DEVICE));
  if (Dev == NULL) {
    return NULL;
  }

  Dev->Signature = PCI_IO_DEVICE_SIGNATURE;
  Dev->Handle    = RootBridgeHandle;
  InitializeListHead (&Dev->ChildList);

  Status = gBS->OpenProtocol (
                  RootBridgeHandle,
                  &gEfiDevicePathProtocolGuid,
                  (VOID **)&ParentDevicePath,
                  gPciBusDriverBinding.DriverBindingHandle,
                  RootBridgeHandle,
                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
                  );

  if (EFI_ERROR (Status)) {
    FreePool (Dev);
    return NULL;
  }

  //
  // Record the root bridge parent device path
  //
  Dev->DevicePath = DuplicateDevicePath (ParentDevicePath);

  //
  // Get the pci root bridge io protocol
  //
  Status = gBS->OpenProtocol (
                  RootBridgeHandle,
                  &gEfiPciRootBridgeIoProtocolGuid,
                  (VOID **)&PciRootBridgeIo,
                  gPciBusDriverBinding.DriverBindingHandle,
                  RootBridgeHandle,
                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
                  );

  if (EFI_ERROR (Status)) {
    FreePciDevice (Dev);
    return NULL;
  }

  Dev->PciRootBridgeIo = PciRootBridgeIo;

  //
  // Initialize the PCI I/O instance structure
  //
  InitializePciIoInstance (Dev);
  InitializePciDriverOverrideInstance (Dev);
  InitializePciLoadFile2 (Dev);

  //
  // Initialize reserved resource list and
  // option rom driver list
  //
  InitializeListHead (&Dev->ReservedResourceList);
  InitializeListHead (&Dev->OptionRomDriverList);

  return Dev;
}

/**
  Get root bridge device instance by specific root bridge handle.

  @param RootBridgeHandle    Given root bridge handle.

  @return The root bridge device instance, NULL means no root bridge
          device instance found.

**/
PCI_IO_DEVICE *
GetRootBridgeByHandle (
  EFI_HANDLE  RootBridgeHandle
  )
{
  PCI_IO_DEVICE  *RootBridgeDev;
  LIST_ENTRY     *CurrentLink;

  CurrentLink = mPciDevicePool.ForwardLink;

  while (CurrentLink != NULL && CurrentLink != &mPciDevicePool) {
    RootBridgeDev = PCI_IO_DEVICE_FROM_LINK (CurrentLink);
    if (RootBridgeDev->Handle == RootBridgeHandle) {
      return RootBridgeDev;
    }

    CurrentLink = CurrentLink->ForwardLink;
  }

  return NULL;
}

/**
  Judge whether Pci device existed.

  @param Bridge       Parent bridge instance.
  @param PciIoDevice  Device instance.

  @retval TRUE        Pci device existed.
  @retval FALSE       Pci device did not exist.

**/
BOOLEAN
PciDeviceExisted (
  IN PCI_IO_DEVICE  *Bridge,
  IN PCI_IO_DEVICE  *PciIoDevice
  )
{
  PCI_IO_DEVICE  *Temp;
  LIST_ENTRY     *CurrentLink;

  CurrentLink = Bridge->ChildList.ForwardLink;

  while (CurrentLink != NULL && CurrentLink != &Bridge->ChildList) {
    Temp = PCI_IO_DEVICE_FROM_LINK (CurrentLink);

    if (Temp == PciIoDevice) {
      return TRUE;
    }

    if (!IsListEmpty (&Temp->ChildList)) {
      if (PciDeviceExisted (Temp, PciIoDevice)) {
        return TRUE;
      }
    }

    CurrentLink = CurrentLink->ForwardLink;
  }

  return FALSE;
}

/**
  Get the active VGA device on the specified Host Bridge.

  @param HostBridgeHandle    Host Bridge handle.

  @return The active VGA device on the specified Host Bridge.

**/
PCI_IO_DEVICE *
LocateVgaDeviceOnHostBridge (
  IN EFI_HANDLE  HostBridgeHandle
  )
{
  LIST_ENTRY     *CurrentLink;
  PCI_IO_DEVICE  *PciIoDevice;

  CurrentLink = mPciDevicePool.ForwardLink;

  while (CurrentLink != NULL && CurrentLink != &mPciDevicePool) {
    PciIoDevice = PCI_IO_DEVICE_FROM_LINK (CurrentLink);

    if (PciIoDevice->PciRootBridgeIo->ParentHandle == HostBridgeHandle) {
      PciIoDevice = LocateVgaDevice (PciIoDevice);

      if (PciIoDevice != NULL) {
        return PciIoDevice;
      }
    }

    CurrentLink = CurrentLink->ForwardLink;
  }

  return NULL;
}

/**
  Locate the active VGA device under the bridge.

  @param Bridge  PCI IO instance for the bridge.

  @return The active VGA device.

**/
PCI_IO_DEVICE *
LocateVgaDevice (
  IN PCI_IO_DEVICE  *Bridge
  )
{
  LIST_ENTRY     *CurrentLink;
  PCI_IO_DEVICE  *PciIoDevice;

  CurrentLink = Bridge->ChildList.ForwardLink;

  while (CurrentLink != NULL && CurrentLink != &Bridge->ChildList) {
    PciIoDevice = PCI_IO_DEVICE_FROM_LINK (CurrentLink);

    if (IS_PCI_VGA (&PciIoDevice->Pci) &&
        ((PciIoDevice->Attributes &
          (EFI_PCI_IO_ATTRIBUTE_VGA_MEMORY |
           EFI_PCI_IO_ATTRIBUTE_VGA_IO     |
           EFI_PCI_IO_ATTRIBUTE_VGA_IO_16)) != 0))
    {
      return PciIoDevice;
    }

    if (IS_PCI_BRIDGE (&PciIoDevice->Pci)) {
      PciIoDevice = LocateVgaDevice (PciIoDevice);

      if (PciIoDevice != NULL) {
        return PciIoDevice;
      }
    }

    CurrentLink = CurrentLink->ForwardLink;
  }

  return NULL;
}
