diff options
Diffstat (limited to 'CorebootModulePkg/PciBusNoEnumerationDxe/PciDeviceSupport.c')
-rw-r--r-- | CorebootModulePkg/PciBusNoEnumerationDxe/PciDeviceSupport.c | 973 |
1 files changed, 973 insertions, 0 deletions
diff --git a/CorebootModulePkg/PciBusNoEnumerationDxe/PciDeviceSupport.c b/CorebootModulePkg/PciBusNoEnumerationDxe/PciDeviceSupport.c new file mode 100644 index 0000000000..97a4e01c27 --- /dev/null +++ b/CorebootModulePkg/PciBusNoEnumerationDxe/PciDeviceSupport.c @@ -0,0 +1,973 @@ +/*++ + +Copyright (c) 2005 - 2006, 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. + +Module Name: + + PciDeviceSupport.c + +Abstract: + + This file provides routine to support Pci device node manipulation + +Revision History + +--*/ + +#include "PciBus.h" + +// +// This device structure is serviced as a header. +// Its Next field points to the first root bridge device node +// +LIST_ENTRY gPciDevicePool; + +EFI_STATUS +InitializePciDevicePool ( + VOID + ) +/*++ + +Routine Description: + + Initialize the gPciDevicePool + +Arguments: + +Returns: + + None + +--*/ +{ + InitializeListHead (&gPciDevicePool); + + return EFI_SUCCESS; +} + +EFI_STATUS +InsertRootBridge ( + IN PCI_IO_DEVICE *RootBridge + ) +/*++ + +Routine Description: + + Insert a root bridge into PCI device pool + +Arguments: + + RootBridge - A pointer to the PCI_IO_DEVICE. + +Returns: + + None + +--*/ +{ + InsertTailList (&gPciDevicePool, &(RootBridge->Link)); + + return EFI_SUCCESS; +} + +EFI_STATUS +InsertPciDevice ( + PCI_IO_DEVICE *Bridge, + PCI_IO_DEVICE *PciDeviceNode + ) +/*++ + +Routine Description: + + This function is used to insert a PCI device node under + a bridge + +Arguments: + Bridge - A pointer to the PCI_IO_DEVICE. + PciDeviceNode - A pointer to the PCI_IO_DEVICE. + +Returns: + + None + +--*/ + +{ + + InsertTailList (&Bridge->ChildList, &(PciDeviceNode->Link)); + PciDeviceNode->Parent = Bridge; + + return EFI_SUCCESS; +} + +EFI_STATUS +DestroyRootBridge ( + IN PCI_IO_DEVICE *RootBridge + ) +/*++ + +Routine Description: + + +Arguments: + + RootBridge - A pointer to the PCI_IO_DEVICE. + +Returns: + + None + +--*/ +{ + DestroyPciDeviceTree (RootBridge); + + gBS->FreePool (RootBridge); + + return EFI_SUCCESS; +} + +EFI_STATUS +DestroyPciDeviceTree ( + IN PCI_IO_DEVICE *Bridge + ) +/*++ + +Routine Description: + + Destroy all the pci device node under the bridge. + Bridge itself is not included. + +Arguments: + + Bridge - A pointer to the PCI_IO_DEVICE. + +Returns: + + None + +--*/ +{ + 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 (IS_PCI_BRIDGE (&(Temp->Pci))) { + DestroyPciDeviceTree (Temp); + } + gBS->FreePool (Temp); + } + + return EFI_SUCCESS; +} + +EFI_STATUS +DestroyRootBridgeByHandle ( + EFI_HANDLE Controller + ) +/*++ + +Routine Description: + + Destroy all device nodes under the root bridge + specified by Controller. + The root bridge itself is also included. + +Arguments: + + Controller - An efi handle. + +Returns: + + None + +--*/ +{ + + LIST_ENTRY *CurrentLink; + PCI_IO_DEVICE *Temp; + + CurrentLink = gPciDevicePool.ForwardLink; + + while (CurrentLink && CurrentLink != &gPciDevicePool) { + Temp = PCI_IO_DEVICE_FROM_LINK (CurrentLink); + + if (Temp->Handle == Controller) { + + RemoveEntryList (CurrentLink); + + DestroyPciDeviceTree (Temp); + + gBS->FreePool(Temp); + + return EFI_SUCCESS; + } + + CurrentLink = CurrentLink->ForwardLink; + } + + return EFI_NOT_FOUND; +} + +EFI_STATUS +RegisterPciDevice ( + IN EFI_HANDLE Controller, + IN PCI_IO_DEVICE *PciIoDevice, + OUT EFI_HANDLE *Handle OPTIONAL + ) +/*++ + +Routine Description: + + 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. + +Arguments: + + Controller - An EFI handle for the PCI bus controller. + PciIoDevice - A PCI_IO_DEVICE pointer to the PCI IO device to be registered. + Handle - A pointer to hold the EFI handle for the PCI IO device. + +Returns: + + EFI_SUCCESS - The PCI device is successfully registered. + Others - An error occurred when registering the PCI device. + +--*/ +{ + EFI_STATUS Status; + UINT8 PciExpressCapRegOffset; + + // + // Install the pciio protocol, device path protocol and + // Bus Specific Driver Override Protocol + // + + if (PciIoDevice->BusOverride) { + Status = gBS->InstallMultipleProtocolInterfaces ( + &PciIoDevice->Handle, + &gEfiDevicePathProtocolGuid, + PciIoDevice->DevicePath, + &gEfiPciIoProtocolGuid, + &PciIoDevice->PciIo, + &gEfiBusSpecificDriverOverrideProtocolGuid, + &PciIoDevice->PciDriverOverride, + NULL + ); + } else { + Status = gBS->InstallMultipleProtocolInterfaces ( + &PciIoDevice->Handle, + &gEfiDevicePathProtocolGuid, + PciIoDevice->DevicePath, + &gEfiPciIoProtocolGuid, + &PciIoDevice->PciIo, + NULL + ); + } + + if (EFI_ERROR (Status)) { + return Status; + } else { + 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; + } + + // + // Detect if PCI Express Device + // + PciExpressCapRegOffset = 0; + Status = LocateCapabilityRegBlock ( + PciIoDevice, + EFI_PCI_CAPABILITY_ID_PCIEXP, + &PciExpressCapRegOffset, + NULL + ); + if (!EFI_ERROR (Status)) { + PciIoDevice->IsPciExp = TRUE; + DEBUG ((EFI_D_ERROR, "PciExp - %x (B-%x, D-%x, F-%x)\n", PciIoDevice->IsPciExp, PciIoDevice->BusNumber, PciIoDevice->DeviceNumber, PciIoDevice->FunctionNumber)); + } + + // + // Indicate the pci device is registered + // + PciIoDevice->Registered = TRUE; + + return EFI_SUCCESS; +} + + +EFI_STATUS +DeRegisterPciDevice ( + IN EFI_HANDLE Controller, + IN EFI_HANDLE Handle + ) +/*++ + +Routine Description: + + This function is used to de-register the PCI device from the EFI, + That includes un-installing PciIo protocol from the specified PCI + device handle. + +Arguments: + + Controller - An efi handle. + Handle - An efi handle. + +Returns: + + None + +--*/ +{ + 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 (IS_PCI_BRIDGE (&(PciIoDevice->Pci))) { + + CurrentLink = PciIoDevice->ChildList.ForwardLink; + + while (CurrentLink && CurrentLink != &PciIoDevice->ChildList) { + Node = PCI_IO_DEVICE_FROM_LINK (CurrentLink); + Status = DeRegisterPciDevice (Controller, Node->Handle); + + if (EFI_ERROR (Status)) { + return Status; + } + + CurrentLink = CurrentLink->ForwardLink; + } + } + + // + // First disconnect this device + // +// PciIoDevice->PciIo.Attributes(&(PciIoDevice->PciIo), +// EfiPciIoAttributeOperationDisable, +// EFI_PCI_DEVICE_ENABLE, +// NULL +// ); + + // + // Close the child handle + // + Status = gBS->CloseProtocol ( + Controller, + &gEfiPciRootBridgeIoProtocolGuid, + gPciBusDriverBinding.DriverBindingHandle, + Handle + ); + + // + // Un-install the device path protocol and pci io protocol + // + 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)) { + 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; +} + +EFI_STATUS +EnableBridgeAttributes ( + IN PCI_IO_DEVICE *PciIoDevice + ) +{ + PCI_TYPE01 PciData; + + // + // NOTE: We should not set EFI_PCI_DEVICE_ENABLE for a bridge + // directly, because some legacy BIOS will NOT assign + // IO or Memory resource for a bridge who has no child + // device. So we add check IO or Memory here. + // + + PciIoDevice->PciIo.Pci.Read ( + &PciIoDevice->PciIo, + EfiPciIoWidthUint8, + 0, + sizeof (PciData), + &PciData + ); + + if ((((PciData.Bridge.IoBase & 0xF) == 0) && + (PciData.Bridge.IoBase != 0 || PciData.Bridge.IoLimit != 0)) || + (((PciData.Bridge.IoBase & 0xF) == 1) && + ((PciData.Bridge.IoBase & 0xF0) != 0 || (PciData.Bridge.IoLimit & 0xF0) != 0 || PciData.Bridge.IoBaseUpper16 != 0 || PciData.Bridge.IoLimitUpper16 != 0))) { + PciIoDevice->PciIo.Attributes( + &(PciIoDevice->PciIo), + EfiPciIoAttributeOperationEnable, + (EFI_PCI_IO_ATTRIBUTE_IO | EFI_PCI_IO_ATTRIBUTE_BUS_MASTER), + NULL + ); + } + if ((PciData.Bridge.MemoryBase & 0xFFF0) != 0 || (PciData.Bridge.MemoryLimit & 0xFFF0) != 0) { + PciIoDevice->PciIo.Attributes( + &(PciIoDevice->PciIo), + EfiPciIoAttributeOperationEnable, + (EFI_PCI_IO_ATTRIBUTE_MEMORY | EFI_PCI_IO_ATTRIBUTE_BUS_MASTER), + NULL + ); + } + if ((((PciData.Bridge.PrefetchableMemoryBase & 0xF) == 0) && + (PciData.Bridge.PrefetchableMemoryBase != 0 || PciData.Bridge.PrefetchableMemoryLimit != 0)) || + (((PciData.Bridge.PrefetchableMemoryBase & 0xF) == 1) && + ((PciData.Bridge.PrefetchableMemoryBase & 0xFFF0) != 0 || (PciData.Bridge.PrefetchableMemoryLimit & 0xFFF0) != 0 || PciData.Bridge.PrefetchableBaseUpper32 != 0 || PciData.Bridge.PrefetchableLimitUpper32 != 0))) { + PciIoDevice->PciIo.Attributes( + &(PciIoDevice->PciIo), + EfiPciIoAttributeOperationEnable, + (EFI_PCI_IO_ATTRIBUTE_MEMORY | EFI_PCI_IO_ATTRIBUTE_BUS_MASTER), + NULL + ); + } + + return EFI_SUCCESS; +} + +EFI_STATUS +StartPciDevicesOnBridge ( + IN EFI_HANDLE Controller, + IN PCI_IO_DEVICE *RootBridge, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +/*++ + +Routine Description: + + Start to manage the PCI device on specified the root bridge or PCI-PCI Bridge + +Arguments: + + Controller - An efi handle. + RootBridge - A pointer to the PCI_IO_DEVICE. + RemainingDevicePath - A pointer to the EFI_DEVICE_PATH_PROTOCOL. + NumberOfChildren - Children number. + ChildHandleBuffer - A pointer to the child handle buffer. + +Returns: + + None + +--*/ +{ + PCI_IO_DEVICE *Temp; + PCI_IO_DEVICE *PciIoDevice; + EFI_DEV_PATH_PTR Node; + EFI_DEVICE_PATH_PROTOCOL *CurrentDevicePath; + EFI_STATUS Status; + LIST_ENTRY *CurrentLink; + + CurrentLink = RootBridge->ChildList.ForwardLink; + + while (CurrentLink && CurrentLink != &RootBridge->ChildList) { + + Temp = PCI_IO_DEVICE_FROM_LINK (CurrentLink); + if (RemainingDevicePath != NULL) { + + Node.DevPath = RemainingDevicePath; + + if (Node.Pci->Device != Temp->DeviceNumber || + Node.Pci->Function != Temp->FunctionNumber) { + CurrentLink = CurrentLink->ForwardLink; + continue; + } + + // + // Check if the device has been assigned with required resource + // + if (!Temp->Allocated) { + return EFI_NOT_READY; + } + + // + // Check if the current node has been registered before + // If it is not, register it + // + if (!Temp->Registered) { + PciIoDevice = Temp; + + Status = RegisterPciDevice ( + Controller, + PciIoDevice, + NULL + ); + + } + + // + // Get the next device path + // + CurrentDevicePath = NextDevicePathNode (RemainingDevicePath); + if (IsDevicePathEnd (CurrentDevicePath)) { + return EFI_SUCCESS; + } + + // + // If it is a PPB + // + if (IS_PCI_BRIDGE (&(Temp->Pci))) { + Status = StartPciDevicesOnBridge ( + Controller, + Temp, + CurrentDevicePath + ); + EnableBridgeAttributes (Temp); + + 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 (!Temp->Registered && Temp->Allocated) { + + PciIoDevice = Temp; + + Status = RegisterPciDevice ( + Controller, + PciIoDevice, + NULL + ); + + } + + if (IS_PCI_BRIDGE (&(Temp->Pci))) { + Status = StartPciDevicesOnBridge ( + Controller, + Temp, + RemainingDevicePath + ); + EnableBridgeAttributes (Temp); + } + + CurrentLink = CurrentLink->ForwardLink; + continue; + } + } + + return EFI_NOT_FOUND; +} + +EFI_STATUS +StartPciDevices ( + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +/*++ + +Routine Description: + + Start to manage the PCI device according to RemainingDevicePath + If RemainingDevicePath == NULL, the PCI bus driver will start + to manage all the PCI devices it found previously + +Arguments: + Controller - An efi handle. + RemainingDevicePath - A pointer to the EFI_DEVICE_PATH_PROTOCOL. + +Returns: + + None + +--*/ +{ + EFI_DEV_PATH_PTR Node; + PCI_IO_DEVICE *RootBridge; + LIST_ENTRY *CurrentLink; + + if (RemainingDevicePath != NULL) { + + // + // Check if the RemainingDevicePath is valid + // + Node.DevPath = RemainingDevicePath; + if (Node.DevPath->Type != HARDWARE_DEVICE_PATH || + Node.DevPath->SubType != HW_PCI_DP || + DevicePathNodeLength (Node.DevPath) != sizeof (PCI_DEVICE_PATH) + ) { + return EFI_UNSUPPORTED; + } + } + + CurrentLink = gPciDevicePool.ForwardLink; + + while (CurrentLink && CurrentLink != &gPciDevicePool) { + + RootBridge = PCI_IO_DEVICE_FROM_LINK (CurrentLink); + // + // Locate the right root bridge to start + // + if (RootBridge->Handle == Controller) { + StartPciDevicesOnBridge ( + Controller, + RootBridge, + RemainingDevicePath + ); + } + + CurrentLink = CurrentLink->ForwardLink; + } + + return EFI_SUCCESS; +} + +PCI_IO_DEVICE * +CreateRootBridge ( + IN EFI_HANDLE RootBridgeHandle + ) +/*++ + +Routine Description: + + +Arguments: + RootBridgeHandle - An efi handle. + +Returns: + + None + +--*/ +{ + + EFI_STATUS Status; + PCI_IO_DEVICE *Dev; + + Dev = NULL; + Status = gBS->AllocatePool ( + EfiBootServicesData, + sizeof (PCI_IO_DEVICE), + (VOID **) &Dev + ); + + if (EFI_ERROR (Status)) { + return NULL; + } + + ZeroMem (Dev, sizeof (PCI_IO_DEVICE)); + Dev->Signature = PCI_IO_DEVICE_SIGNATURE; + Dev->Handle = RootBridgeHandle; + InitializeListHead (&Dev->ChildList); + + return Dev; +} + +PCI_IO_DEVICE * +GetRootBridgeByHandle ( + EFI_HANDLE RootBridgeHandle + ) +/*++ + +Routine Description: + + +Arguments: + + RootBridgeHandle - An efi handle. + +Returns: + + None + +--*/ +{ + PCI_IO_DEVICE *RootBridgeDev; + LIST_ENTRY *CurrentLink; + + CurrentLink = gPciDevicePool.ForwardLink; + + while (CurrentLink && CurrentLink != &gPciDevicePool) { + + RootBridgeDev = PCI_IO_DEVICE_FROM_LINK (CurrentLink); + if (RootBridgeDev->Handle == RootBridgeHandle) { + return RootBridgeDev; + } + + CurrentLink = CurrentLink->ForwardLink; + } + + return NULL; +} + +BOOLEAN +RootBridgeExisted ( + IN EFI_HANDLE RootBridgeHandle + ) +/*++ + +Routine Description: + + This function searches if RootBridgeHandle has already existed + in current device pool. + + If so, it means the given root bridge has been already enumerated. + +Arguments: + + RootBridgeHandle - An efi handle. + +Returns: + + None + +--*/ +{ + PCI_IO_DEVICE *Bridge; + + Bridge = GetRootBridgeByHandle (RootBridgeHandle); + + if (Bridge != NULL) { + return TRUE; + } + + return FALSE; +} + +BOOLEAN +PciDeviceExisted ( + IN PCI_IO_DEVICE *Bridge, + IN PCI_IO_DEVICE *PciIoDevice + ) +/*++ + +Routine Description: + +Arguments: + + Bridge - A pointer to the PCI_IO_DEVICE. + PciIoDevice - A pointer to the PCI_IO_DEVICE. + +Returns: + + None + +--*/ +{ + + PCI_IO_DEVICE *Temp; + LIST_ENTRY *CurrentLink; + + CurrentLink = Bridge->ChildList.ForwardLink; + + while (CurrentLink && 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; +} + +PCI_IO_DEVICE * +ActiveVGADeviceOnTheSameSegment ( + IN PCI_IO_DEVICE *VgaDevice + ) +/*++ + +Routine Description: + +Arguments: + + VgaDevice - A pointer to the PCI_IO_DEVICE. + +Returns: + + None + +--*/ +{ + LIST_ENTRY *CurrentLink; + PCI_IO_DEVICE *Temp; + + CurrentLink = gPciDevicePool.ForwardLink; + + while (CurrentLink && CurrentLink != &gPciDevicePool) { + + 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; +} + +PCI_IO_DEVICE * +ActiveVGADeviceOnTheRootBridge ( + IN PCI_IO_DEVICE *RootBridge + ) +/*++ + +Routine Description: + +Arguments: + + RootBridge - A pointer to the PCI_IO_DEVICE. + +Returns: + + None + +--*/ +{ + LIST_ENTRY *CurrentLink; + PCI_IO_DEVICE *Temp; + + CurrentLink = RootBridge->ChildList.ForwardLink; + + while (CurrentLink && 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))) { + return Temp; + } + + if (IS_PCI_BRIDGE (&Temp->Pci)) { + + Temp = ActiveVGADeviceOnTheRootBridge (Temp); + + if (Temp != NULL) { + return Temp; + } + } + + CurrentLink = CurrentLink->ForwardLink; + } + + return NULL; +} |