Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Ruiyu Ni <ruiyu.ni@intel.com> Reviewed-by: Chao B Zhang <chao.b.zhang@intel.com> git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@16625 6f19259b-4bc3-4df7-8a09-765794883524
		
			
				
	
	
		
			1156 lines
		
	
	
		
			31 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1156 lines
		
	
	
		
			31 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file
 | |
|   Supporting functions implementaion for PCI devices management.
 | |
| 
 | |
| Copyright (c) 2006 - 2015, 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 "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 deivce 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 destoried.
 | |
| 
 | |
| **/
 | |
| 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);
 | |
|   }
 | |
| 
 | |
|   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   Destory all devcie 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;
 | |
|   UINT8               PciExpressCapRegOffset;
 | |
|   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;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Detect if PCI Express Device
 | |
|   //
 | |
|   PciExpressCapRegOffset = 0;
 | |
|   Status = LocateCapabilityRegBlock (
 | |
|              PciIoDevice,
 | |
|              EFI_PCI_CAPABILITY_ID_PCIEXP,
 | |
|              &PciExpressCapRegOffset,
 | |
|              NULL
 | |
|              );
 | |
|   if (!EFI_ERROR (Status)) {
 | |
|     PciIoDevice->IsPciExp = TRUE;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // 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        = 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,
 | |
|           (UINT64) (UINTN) 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        = 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,
 | |
|           (UINT64) (UINTN) 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 hanle.
 | |
| 
 | |
|   @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;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Judege whether Pci device existed.
 | |
| 
 | |
|   @param Bridge       Parent bridege 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 same segment.
 | |
| 
 | |
|   @param VgaDevice    PCI IO instance for the VGA device.
 | |
| 
 | |
|   @return The active VGA device on the same segment.
 | |
| 
 | |
| **/
 | |
| PCI_IO_DEVICE *
 | |
| ActiveVGADeviceOnTheSameSegment (
 | |
|   IN PCI_IO_DEVICE        *VgaDevice
 | |
|   )
 | |
| {
 | |
|   LIST_ENTRY      *CurrentLink;
 | |
|   PCI_IO_DEVICE   *Temp;
 | |
| 
 | |
|   CurrentLink = mPciDevicePool.ForwardLink;
 | |
| 
 | |
|   while (CurrentLink != NULL && CurrentLink != &mPciDevicePool) {
 | |
| 
 | |
|     Temp = PCI_IO_DEVICE_FROM_LINK (CurrentLink);
 | |
| 
 | |
|     if (Temp->PciRootBridgeIo->SegmentNumber == VgaDevice->PciRootBridgeIo->SegmentNumber) {
 | |
| 
 | |
|       Temp = ActiveVGADeviceOnTheRootBridge (Temp);
 | |
| 
 | |
|       if (Temp != NULL) {
 | |
|         return Temp;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     CurrentLink = CurrentLink->ForwardLink;
 | |
|   }
 | |
| 
 | |
|   return NULL;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Get the active VGA device on the root bridge.
 | |
| 
 | |
|   @param RootBridge  PCI IO instance for the root bridge.
 | |
| 
 | |
|   @return The active VGA device.
 | |
| 
 | |
| **/
 | |
| PCI_IO_DEVICE *
 | |
| ActiveVGADeviceOnTheRootBridge (
 | |
|   IN PCI_IO_DEVICE        *RootBridge
 | |
|   )
 | |
| {
 | |
|   LIST_ENTRY      *CurrentLink;
 | |
|   PCI_IO_DEVICE   *Temp;
 | |
| 
 | |
|   CurrentLink = RootBridge->ChildList.ForwardLink;
 | |
| 
 | |
|   while (CurrentLink != NULL && CurrentLink != &RootBridge->ChildList) {
 | |
| 
 | |
|     Temp = PCI_IO_DEVICE_FROM_LINK (CurrentLink);
 | |
| 
 | |
|     if (IS_PCI_VGA(&Temp->Pci) &&
 | |
|         (Temp->Attributes &
 | |
|          (EFI_PCI_IO_ATTRIBUTE_VGA_MEMORY |
 | |
|           EFI_PCI_IO_ATTRIBUTE_VGA_IO     |
 | |
|           EFI_PCI_IO_ATTRIBUTE_VGA_IO_16)) != 0) {
 | |
|       return Temp;
 | |
|     }
 | |
| 
 | |
|     if (IS_PCI_BRIDGE (&Temp->Pci)) {
 | |
| 
 | |
|       Temp = ActiveVGADeviceOnTheRootBridge (Temp);
 | |
| 
 | |
|       if (Temp != NULL) {
 | |
|         return Temp;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     CurrentLink = CurrentLink->ForwardLink;
 | |
|   }
 | |
| 
 | |
|   return NULL;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|   Get HPC PCI address according to its device path.
 | |
| 
 | |
|   @param RootBridge           Root bridege Io instance.
 | |
|   @param RemainingDevicePath  Given searching device path.
 | |
|   @param PciAddress           Buffer holding searched result.
 | |
| 
 | |
|   @retval EFI_SUCCESS         PCI address was stored in PciAddress
 | |
|   @retval EFI_NOT_FOUND       Can not find the specific device path.
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| GetHpcPciAddressFromRootBridge (
 | |
|   IN  PCI_IO_DEVICE                    *RootBridge,
 | |
|   IN  EFI_DEVICE_PATH_PROTOCOL         *RemainingDevicePath,
 | |
|   OUT UINT64                           *PciAddress
 | |
|   )
 | |
| {
 | |
|   EFI_DEV_PATH_PTR          Node;
 | |
|   PCI_IO_DEVICE             *Temp;
 | |
|   EFI_DEVICE_PATH_PROTOCOL  *CurrentDevicePath;
 | |
|   LIST_ENTRY                *CurrentLink;
 | |
|   BOOLEAN                   MisMatch;
 | |
| 
 | |
|   MisMatch          = FALSE;
 | |
| 
 | |
|   CurrentDevicePath = RemainingDevicePath;
 | |
|   Node.DevPath      = CurrentDevicePath;
 | |
|   Temp              = NULL;
 | |
| 
 | |
|   while (!IsDevicePathEnd (CurrentDevicePath)) {
 | |
| 
 | |
|     CurrentLink   = RootBridge->ChildList.ForwardLink;
 | |
|     Node.DevPath  = CurrentDevicePath;
 | |
| 
 | |
|     while (CurrentLink != NULL && CurrentLink != &RootBridge->ChildList) {
 | |
|       Temp = PCI_IO_DEVICE_FROM_LINK (CurrentLink);
 | |
| 
 | |
|       if (Node.Pci->Device   == Temp->DeviceNumber &&
 | |
|           Node.Pci->Function == Temp->FunctionNumber) {
 | |
|         RootBridge = Temp;
 | |
|         break;
 | |
|       }
 | |
| 
 | |
|       CurrentLink = CurrentLink->ForwardLink;
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Check if we find the bridge
 | |
|     //
 | |
|     if (CurrentLink == &RootBridge->ChildList) {
 | |
| 
 | |
|       MisMatch = TRUE;
 | |
|       break;
 | |
| 
 | |
|     }
 | |
| 
 | |
|     CurrentDevicePath = NextDevicePathNode (CurrentDevicePath);
 | |
|   }
 | |
| 
 | |
|   if (MisMatch) {
 | |
| 
 | |
|     CurrentDevicePath = NextDevicePathNode (CurrentDevicePath);
 | |
| 
 | |
|     if (IsDevicePathEnd (CurrentDevicePath)) {
 | |
|       *PciAddress = EFI_PCI_ADDRESS (RootBridge->BusNumber, Node.Pci->Device, Node.Pci->Function, 0);
 | |
|       return EFI_SUCCESS;
 | |
|     }
 | |
| 
 | |
|     return EFI_NOT_FOUND;
 | |
|   }
 | |
| 
 | |
|   if (Temp != NULL) {
 | |
|     *PciAddress = EFI_PCI_ADDRESS (Temp->BusNumber, Temp->DeviceNumber, Temp->FunctionNumber, 0);
 | |
|   } else {
 | |
|     return EFI_NOT_FOUND;
 | |
|   }
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| 
 | |
| }
 | |
| 
 |