/** @file Supporting functions implementation for PCI devices management. Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.
(C) Copyright 2018 Hewlett Packard Enterprise Development LP
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; }