From 9060e3ec6dfd6048724832947933650cde873381 Mon Sep 17 00:00:00 2001 From: davidhuang Date: Tue, 20 Oct 2009 03:43:40 +0000 Subject: [PATCH] 1. Impl PI 1.2 PCI part. Major changes include: a. Update PciBusDxe module, and move it from IntelFrameworkModulePkg to MdeModulePkg b. Move IncompatiblePciDeviceSupportDxe module from IntelFrameworkModulePkg to MdeModulePkg c. Update the related consumes in inf/dsc/fdf git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@9347 6f19259b-4bc3-4df7-8a09-765794883524 --- .../IncompatiblePciDeviceSupport.c | 387 +++ .../IncompatiblePciDeviceSupportDxe.inf | 49 + .../Bus/Pci/PciBusDxe/ComponentName.c | 176 ++ .../Bus/Pci/PciBusDxe/ComponentName.h | 152 + MdeModulePkg/Bus/Pci/PciBusDxe/PciBus.c | 382 +++ MdeModulePkg/Bus/Pci/PciBusDxe/PciBus.h | 382 +++ MdeModulePkg/Bus/Pci/PciBusDxe/PciBusDxe.inf | 116 + MdeModulePkg/Bus/Pci/PciBusDxe/PciCommand.c | 248 ++ MdeModulePkg/Bus/Pci/PciBusDxe/PciCommand.h | 238 ++ .../Bus/Pci/PciBusDxe/PciDeviceSupport.c | 1185 ++++++++ .../Bus/Pci/PciBusDxe/PciDeviceSupport.h | 289 ++ .../Bus/Pci/PciBusDxe/PciDriverOverride.c | 143 + .../Bus/Pci/PciBusDxe/PciDriverOverride.h | 86 + .../Bus/Pci/PciBusDxe/PciEnumerator.c | 2171 ++++++++++++++ .../Bus/Pci/PciBusDxe/PciEnumerator.h | 519 ++++ .../Bus/Pci/PciBusDxe/PciEnumeratorSupport.c | 2494 +++++++++++++++++ .../Bus/Pci/PciBusDxe/PciEnumeratorSupport.h | 463 +++ .../Bus/Pci/PciBusDxe/PciHotPlugSupport.c | 394 +++ .../Bus/Pci/PciBusDxe/PciHotPlugSupport.h | 189 ++ MdeModulePkg/Bus/Pci/PciBusDxe/PciIo.c | 1877 +++++++++++++ MdeModulePkg/Bus/Pci/PciBusDxe/PciIo.h | 687 +++++ MdeModulePkg/Bus/Pci/PciBusDxe/PciLib.c | 1359 +++++++++ MdeModulePkg/Bus/Pci/PciBusDxe/PciLib.h | 144 + .../Bus/Pci/PciBusDxe/PciOptionRomSupport.c | 735 +++++ .../Bus/Pci/PciBusDxe/PciOptionRomSupport.h | 143 + .../Bus/Pci/PciBusDxe/PciPowerManagement.c | 68 + .../Bus/Pci/PciBusDxe/PciPowerManagement.h | 34 + .../Bus/Pci/PciBusDxe/PciResourceSupport.c | 2344 ++++++++++++++++ .../Bus/Pci/PciBusDxe/PciResourceSupport.h | 492 ++++ MdeModulePkg/Bus/Pci/PciBusDxe/PciRomTable.c | 126 + MdeModulePkg/Bus/Pci/PciBusDxe/PciRomTable.h | 55 + MdeModulePkg/MdeModulePkg.dec | 19 + MdeModulePkg/MdeModulePkg.dsc | 2 + 33 files changed, 18148 insertions(+) create mode 100644 MdeModulePkg/Bus/Pci/IncompatiblePciDeviceSupportDxe/IncompatiblePciDeviceSupport.c create mode 100644 MdeModulePkg/Bus/Pci/IncompatiblePciDeviceSupportDxe/IncompatiblePciDeviceSupportDxe.inf create mode 100644 MdeModulePkg/Bus/Pci/PciBusDxe/ComponentName.c create mode 100644 MdeModulePkg/Bus/Pci/PciBusDxe/ComponentName.h create mode 100644 MdeModulePkg/Bus/Pci/PciBusDxe/PciBus.c create mode 100644 MdeModulePkg/Bus/Pci/PciBusDxe/PciBus.h create mode 100644 MdeModulePkg/Bus/Pci/PciBusDxe/PciBusDxe.inf create mode 100644 MdeModulePkg/Bus/Pci/PciBusDxe/PciCommand.c create mode 100644 MdeModulePkg/Bus/Pci/PciBusDxe/PciCommand.h create mode 100644 MdeModulePkg/Bus/Pci/PciBusDxe/PciDeviceSupport.c create mode 100644 MdeModulePkg/Bus/Pci/PciBusDxe/PciDeviceSupport.h create mode 100644 MdeModulePkg/Bus/Pci/PciBusDxe/PciDriverOverride.c create mode 100644 MdeModulePkg/Bus/Pci/PciBusDxe/PciDriverOverride.h create mode 100644 MdeModulePkg/Bus/Pci/PciBusDxe/PciEnumerator.c create mode 100644 MdeModulePkg/Bus/Pci/PciBusDxe/PciEnumerator.h create mode 100644 MdeModulePkg/Bus/Pci/PciBusDxe/PciEnumeratorSupport.c create mode 100644 MdeModulePkg/Bus/Pci/PciBusDxe/PciEnumeratorSupport.h create mode 100644 MdeModulePkg/Bus/Pci/PciBusDxe/PciHotPlugSupport.c create mode 100644 MdeModulePkg/Bus/Pci/PciBusDxe/PciHotPlugSupport.h create mode 100644 MdeModulePkg/Bus/Pci/PciBusDxe/PciIo.c create mode 100644 MdeModulePkg/Bus/Pci/PciBusDxe/PciIo.h create mode 100644 MdeModulePkg/Bus/Pci/PciBusDxe/PciLib.c create mode 100644 MdeModulePkg/Bus/Pci/PciBusDxe/PciLib.h create mode 100644 MdeModulePkg/Bus/Pci/PciBusDxe/PciOptionRomSupport.c create mode 100644 MdeModulePkg/Bus/Pci/PciBusDxe/PciOptionRomSupport.h create mode 100644 MdeModulePkg/Bus/Pci/PciBusDxe/PciPowerManagement.c create mode 100644 MdeModulePkg/Bus/Pci/PciBusDxe/PciPowerManagement.h create mode 100644 MdeModulePkg/Bus/Pci/PciBusDxe/PciResourceSupport.c create mode 100644 MdeModulePkg/Bus/Pci/PciBusDxe/PciResourceSupport.h create mode 100644 MdeModulePkg/Bus/Pci/PciBusDxe/PciRomTable.c create mode 100644 MdeModulePkg/Bus/Pci/PciBusDxe/PciRomTable.h diff --git a/MdeModulePkg/Bus/Pci/IncompatiblePciDeviceSupportDxe/IncompatiblePciDeviceSupport.c b/MdeModulePkg/Bus/Pci/IncompatiblePciDeviceSupportDxe/IncompatiblePciDeviceSupport.c new file mode 100644 index 0000000000..310817f777 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/IncompatiblePciDeviceSupportDxe/IncompatiblePciDeviceSupport.c @@ -0,0 +1,387 @@ +/** @file + This module is one template module for Incompatible PCI Device Support protocol. + It includes one incompatile pci devices list template. + + Incompatible PCI Device Support protocol allows the PCI bus driver to support + resource allocation for some PCI devices that do not comply with the PCI Specification. + +Copyright (c) 2009, Intel Corporation +All rights reserved. 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 +#include + +#include +#include +#include + +#include +#include + +typedef struct { + UINT64 VendorId; + UINT64 DeviceId; + UINT64 RevisionId; + UINT64 SubsystemVendorId; + UINT64 SubsystemDeviceId; +} EFI_PCI_DEVICE_HEADER_INFO; + +typedef struct { + UINT64 ResType; + UINT64 GenFlag; + UINT64 SpecificFlag; + UINT64 AddrSpaceGranularity; + UINT64 AddrRangeMin; + UINT64 AddrRangeMax; + UINT64 AddrTranslationOffset; + UINT64 AddrLen; +} EFI_PCI_RESOUCE_DESCRIPTOR; + +#define PCI_DEVICE_ID(VendorId, DeviceId, Revision, SubVendorId, SubDeviceId) \ + VendorId, DeviceId, Revision, SubVendorId, SubDeviceId + +#define PCI_BAR_TYPE_IO ACPI_ADDRESS_SPACE_TYPE_IO +#define PCI_BAR_TYPE_MEM ACPI_ADDRESS_SPACE_TYPE_MEM + +#define DEVICE_INF_TAG 0xFFF2 +#define DEVICE_RES_TAG 0xFFF1 +#define LIST_END_TAG 0x0000 + + +/** + Returns a list of ACPI resource descriptors that detail the special + resource configuration requirements for an incompatible PCI device. + + @param This Pointer to the EFI_INCOMPATIBLE_PCI_DEVICE_SUPPORT_PROTOCOL instance. + @param VendorId A unique ID to identify the manufacturer of the PCI device. + @param DeviceId A unique ID to identify the particular PCI device. + @param RevisionId A PCI device-specific revision identifier. + @param SubsystemVendorId Specifies the subsystem vendor ID. + @param SubsystemDeviceId Specifies the subsystem device ID. + @param Configuration A list of ACPI resource descriptors returned that detail + the configuration requirement. + + @retval EFI_SUCCESS Successfully got ACPI resource for specified PCI device. + @retval EFI_INVALID_PARAMETER Configuration is NULL. + @retval EFI_OUT_OF_RESOURCES No memory available. + @retval EFI_UNSUPPORTED The specified PCI device wasn't supported. + +**/ +EFI_STATUS +EFIAPI +PCheckDevice ( + IN EFI_INCOMPATIBLE_PCI_DEVICE_SUPPORT_PROTOCOL *This, + IN UINTN VendorId, + IN UINTN DeviceId, + IN UINTN RevisionId, + IN UINTN SubsystemVendorId, + IN UINTN SubsystemDeviceId, + OUT VOID **Configuration + ); + +// +// Handle onto which the Incompatible PCI Device List is installed +// +EFI_HANDLE mIncompatiblePciDeviceSupportHandle = NULL; + +// +// The Incompatible PCI Device Support Protocol instance produced by this driver +// +EFI_INCOMPATIBLE_PCI_DEVICE_SUPPORT_PROTOCOL mIncompatiblePciDeviceSupport = { + PCheckDevice +}; + +// +// The incompatible PCI devices list template +// +GLOBAL_REMOVE_IF_UNREFERENCED UINT64 mIncompatiblePciDeviceList[] = { + // + // DEVICE_INF_TAG, + // PCI_DEVICE_ID (VendorID, DeviceID, Revision, SubVendorId, SubDeviceId), + // DEVICE_RES_TAG, + // ResType, GFlag , SFlag, Granularity, RangeMin, + // RangeMax, Offset, AddrLen + // + // + // Device Adaptec 9004 + // + DEVICE_INF_TAG, + PCI_DEVICE_ID(0x9004, DEVICE_ID_NOCARE, DEVICE_ID_NOCARE, DEVICE_ID_NOCARE, DEVICE_ID_NOCARE), + DEVICE_RES_TAG, + PCI_BAR_TYPE_IO, + PCI_ACPI_UNUSED, + PCI_ACPI_UNUSED, + PCI_ACPI_UNUSED, + PCI_ACPI_UNUSED, + PCI_BAR_EVEN_ALIGN, + PCI_BAR_ALL, + PCI_BAR_NOCHANGE, + // + // Device Adaptec 9005 + // + DEVICE_INF_TAG, + PCI_DEVICE_ID(0x9005, DEVICE_ID_NOCARE, DEVICE_ID_NOCARE, DEVICE_ID_NOCARE, DEVICE_ID_NOCARE), + DEVICE_RES_TAG, + PCI_BAR_TYPE_IO, + PCI_ACPI_UNUSED, + PCI_ACPI_UNUSED, + PCI_ACPI_UNUSED, + PCI_ACPI_UNUSED, + PCI_BAR_EVEN_ALIGN, + PCI_BAR_ALL, + PCI_BAR_NOCHANGE, + // + // Device QLogic 1007 + // + DEVICE_INF_TAG, + PCI_DEVICE_ID(0x1077, DEVICE_ID_NOCARE, DEVICE_ID_NOCARE, DEVICE_ID_NOCARE, DEVICE_ID_NOCARE), + DEVICE_RES_TAG, + PCI_BAR_TYPE_IO, + PCI_ACPI_UNUSED, + PCI_ACPI_UNUSED, + PCI_ACPI_UNUSED, + PCI_ACPI_UNUSED, + PCI_BAR_EVEN_ALIGN, + PCI_BAR_ALL, + PCI_BAR_NOCHANGE, + // + // Device Agilent 103C + // + DEVICE_INF_TAG, + PCI_DEVICE_ID(0x103C, DEVICE_ID_NOCARE, DEVICE_ID_NOCARE, DEVICE_ID_NOCARE, DEVICE_ID_NOCARE), + DEVICE_RES_TAG, + PCI_BAR_TYPE_IO, + PCI_ACPI_UNUSED, + PCI_ACPI_UNUSED, + PCI_ACPI_UNUSED, + PCI_ACPI_UNUSED, + PCI_BAR_EVEN_ALIGN, + PCI_BAR_ALL, + PCI_BAR_NOCHANGE, + // + // Device Agilent 15BC + // + DEVICE_INF_TAG, + PCI_DEVICE_ID(0x15BC, DEVICE_ID_NOCARE, DEVICE_ID_NOCARE, DEVICE_ID_NOCARE, DEVICE_ID_NOCARE), + DEVICE_RES_TAG, + PCI_BAR_TYPE_IO, + PCI_ACPI_UNUSED, + PCI_ACPI_UNUSED, + PCI_ACPI_UNUSED, + PCI_ACPI_UNUSED, + PCI_BAR_EVEN_ALIGN, + PCI_BAR_ALL, + PCI_BAR_NOCHANGE, + // + // The end of the list + // + LIST_END_TAG +}; + + +/** + Entry point of the incompatible pci device support code. Setup an incompatible device list template + and install EFI Incompatible PCI Device Support protocol. + + @param ImageHandle A handle for the image that is initializing this driver. + @param SystemTable A pointer to the EFI system table. + + @retval EFI_SUCCESS Installed EFI Incompatible PCI Device Support Protocol successfully. + @retval others Failed to install protocol. + +**/ +EFI_STATUS +EFIAPI +IncompatiblePciDeviceSupportEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + // + // Install EFI Incompatible PCI Device Support Protocol on a new handle + // + Status = gBS->InstallProtocolInterface ( + &mIncompatiblePciDeviceSupportHandle, + &gEfiIncompatiblePciDeviceSupportProtocolGuid, + EFI_NATIVE_INTERFACE, + &mIncompatiblePciDeviceSupport + ); + ASSERT_EFI_ERROR (Status); + + return Status; +} + +/** + Returns a list of ACPI resource descriptors that detail the special + resource configuration requirements for an incompatible PCI device. + + @param This Pointer to the EFI_INCOMPATIBLE_PCI_DEVICE_SUPPORT_PROTOCOL instance. + @param VendorId A unique ID to identify the manufacturer of the PCI device. + @param DeviceId A unique ID to identify the particular PCI device. + @param RevisionId A PCI device-specific revision identifier. + @param SubsystemVendorId Specifies the subsystem vendor ID. + @param SubsystemDeviceId Specifies the subsystem device ID. + @param Configuration A list of ACPI resource descriptors returned that detail + the configuration requirement. + + @retval EFI_SUCCESS Successfully got ACPI resource for specified PCI device. + @retval EFI_INVALID_PARAMETER Configuration is NULL. + @retval EFI_OUT_OF_RESOURCES No memory available. + @retval EFI_UNSUPPORTED The specified PCI device wasn't supported. + +**/ +EFI_STATUS +EFIAPI +PCheckDevice ( + IN EFI_INCOMPATIBLE_PCI_DEVICE_SUPPORT_PROTOCOL *This, + IN UINTN VendorId, + IN UINTN DeviceId, + IN UINTN RevisionId, + IN UINTN SubsystemVendorId, + IN UINTN SubsystemDeviceId, + OUT VOID **Configuration + ) +{ + UINT64 Tag; + UINT64 *ListPtr; + UINT64 *TempListPtr; + EFI_PCI_DEVICE_HEADER_INFO *Header; + EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *AcpiPtr; + EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *OldAcpiPtr; + EFI_PCI_RESOUCE_DESCRIPTOR *Dsc; + EFI_ACPI_END_TAG_DESCRIPTOR *PtrEnd; + UINTN Index; + + // + // Validate the parameters + // + if (Configuration == NULL) { + return EFI_INVALID_PARAMETER; + } + // + // Initialize the return value to NULL + // + * (VOID **) Configuration = NULL; + + ListPtr = mIncompatiblePciDeviceList; + while (*ListPtr != LIST_END_TAG) { + + Tag = *ListPtr; + + switch (Tag) { + case DEVICE_INF_TAG: + Header = (EFI_PCI_DEVICE_HEADER_INFO *) (ListPtr + 1); + ListPtr = ListPtr + 1 + sizeof (EFI_PCI_DEVICE_HEADER_INFO) / sizeof (UINT64); + // + // See if the Header matches the parameters passed in + // + if (Header->VendorId != DEVICE_ID_NOCARE) { + if (Header->VendorId != VendorId) { + continue; + } + } + + if (Header->DeviceId != DEVICE_ID_NOCARE) { + if (DeviceId != Header->DeviceId) { + continue; + } + } + + if (Header->RevisionId != DEVICE_ID_NOCARE) { + if (RevisionId != Header->RevisionId) { + continue; + } + } + + if (Header->SubsystemVendorId != DEVICE_ID_NOCARE) { + if (SubsystemVendorId != Header->SubsystemVendorId) { + continue; + } + } + + if (Header->SubsystemDeviceId != DEVICE_ID_NOCARE) { + if (SubsystemDeviceId != Header->SubsystemDeviceId) { + continue; + } + } + // + // Matched an item, so construct the ACPI descriptor for the resource. + // + // + // Count the resource items so that to allocate space + // + for (Index = 0, TempListPtr = ListPtr; *TempListPtr == DEVICE_RES_TAG; Index++) { + TempListPtr = TempListPtr + 1 + ((sizeof (EFI_PCI_RESOUCE_DESCRIPTOR)) / sizeof (UINT64)); + } + // + // If there is at least one type of resource request, + // allocate an acpi resource node + // + if (Index == 0) { + return EFI_UNSUPPORTED; + } + + AcpiPtr = AllocateZeroPool (sizeof (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR) * Index + sizeof (EFI_ACPI_END_TAG_DESCRIPTOR)); + if (AcpiPtr == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + OldAcpiPtr = AcpiPtr; + // + // Fill the EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR structure + // according to the EFI_PCI_RESOUCE_DESCRIPTOR structure + // + for (; *ListPtr == DEVICE_RES_TAG;) { + + Dsc = (EFI_PCI_RESOUCE_DESCRIPTOR *) (ListPtr + 1); + + AcpiPtr->Desc = ACPI_ADDRESS_SPACE_DESCRIPTOR; + AcpiPtr->Len = sizeof (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR); + AcpiPtr->ResType = (UINT8) Dsc->ResType; + AcpiPtr->GenFlag = (UINT8) Dsc->GenFlag; + AcpiPtr->SpecificFlag = (UINT8) Dsc->SpecificFlag; + AcpiPtr->AddrSpaceGranularity = Dsc->AddrSpaceGranularity;; + AcpiPtr->AddrRangeMin = Dsc->AddrRangeMin; + AcpiPtr->AddrRangeMax = Dsc->AddrRangeMax; + AcpiPtr->AddrTranslationOffset = Dsc->AddrTranslationOffset; + AcpiPtr->AddrLen = Dsc->AddrLen; + + ListPtr = ListPtr + 1 + ((sizeof (EFI_PCI_RESOUCE_DESCRIPTOR)) / sizeof (UINT64)); + AcpiPtr++; + } + // + // Put the checksum + // + PtrEnd = (EFI_ACPI_END_TAG_DESCRIPTOR *) (AcpiPtr); + PtrEnd->Desc = ACPI_END_TAG_DESCRIPTOR; + PtrEnd->Checksum = 0; + + *(VOID **) Configuration = OldAcpiPtr; + + return EFI_SUCCESS; + + case DEVICE_RES_TAG: + // + // Adjust the pointer to the next PCI resource descriptor item + // + ListPtr = ListPtr + 1 + ((sizeof (EFI_PCI_RESOUCE_DESCRIPTOR)) / sizeof (UINT64)); + break; + + default: + return EFI_UNSUPPORTED; + } + } + + return EFI_UNSUPPORTED; +} + diff --git a/MdeModulePkg/Bus/Pci/IncompatiblePciDeviceSupportDxe/IncompatiblePciDeviceSupportDxe.inf b/MdeModulePkg/Bus/Pci/IncompatiblePciDeviceSupportDxe/IncompatiblePciDeviceSupportDxe.inf new file mode 100644 index 0000000000..29bf4dce2c --- /dev/null +++ b/MdeModulePkg/Bus/Pci/IncompatiblePciDeviceSupportDxe/IncompatiblePciDeviceSupportDxe.inf @@ -0,0 +1,49 @@ +#/** @file +# PCI Incompatible device support module template. +# +# Installs EFI PCI Incompatible Device Support protocol and includes one incompatile +# pci devices list template. +# +# Copyright (c) 2009, Intel Corporation. +# +# All rights reserved. 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. +# +# +#**/ + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = IncompatiblePciDeviceSupport + FILE_GUID = AD70855E-0CC5-4abf-8979-BE762A949EA3 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = IncompatiblePciDeviceSupportEntryPoint + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# + +[Sources.common] + IncompatiblePciDeviceSupport.c + +[Packages] + MdePkg/MdePkg.dec + +[LibraryClasses] + UefiDriverEntryPoint + UefiBootServicesTableLib + MemoryAllocationLib + DebugLib + +[Protocols] + gEfiIncompatiblePciDeviceSupportProtocolGuid ## PRODUCEDS + +[Depex] + TRUE diff --git a/MdeModulePkg/Bus/Pci/PciBusDxe/ComponentName.c b/MdeModulePkg/Bus/Pci/PciBusDxe/ComponentName.c new file mode 100644 index 0000000000..47eba5fdbe --- /dev/null +++ b/MdeModulePkg/Bus/Pci/PciBusDxe/ComponentName.c @@ -0,0 +1,176 @@ +/** @file + EFI Component Name functions implementation for PCI Bus module. + +Copyright (c) 2006 - 2009, Intel Corporation +All rights reserved. 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" + +// +// EFI Component Name Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gPciBusComponentName = { + PciBusComponentNameGetDriverName, + PciBusComponentNameGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gPciBusComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) PciBusComponentNameGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) PciBusComponentNameGetControllerName, + "en" +}; + + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mPciBusDriverNameTable[] = { + { "eng;en", (CHAR16 *) L"PCI Bus Driver" }, + { NULL , NULL } +}; + +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +PciBusComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mPciBusDriverNameTable, + DriverName, + (BOOLEAN)(This == &gPciBusComponentName) + ); +} + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +PciBusComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ) +{ + return EFI_UNSUPPORTED; +} diff --git a/MdeModulePkg/Bus/Pci/PciBusDxe/ComponentName.h b/MdeModulePkg/Bus/Pci/PciBusDxe/ComponentName.h new file mode 100644 index 0000000000..ee30e86725 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/PciBusDxe/ComponentName.h @@ -0,0 +1,152 @@ +/** @file + EFI Component Name functions declaration for PCI Bus module. + +Copyright (c) 2006 - 2009, Intel Corporation +All rights reserved. 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. + +**/ + + +#ifndef _EFI_PCI_BUS_COMPONENT_NAME_H_ +#define _EFI_PCI_BUS_COMPONENT_NAME_H_ + +extern EFI_COMPONENT_NAME_PROTOCOL gPciBusComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gPciBusComponentName2; + +// +// EFI Component Name Functions +// +/** + Retrieves a Unicode string that is the user readable name of the driver. + + This function retrieves the user readable name of a driver in the form of a + Unicode string. If the driver specified by This has a user readable name in + the language specified by Language, then a pointer to the driver name is + returned in DriverName, and EFI_SUCCESS is returned. If the driver specified + by This does not support the language specified by Language, + then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified + in RFC 4646 or ISO 639-2 language code format. + + @param DriverName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + driver specified by This in the language + specified by Language. + + @retval EFI_SUCCESS The Unicode string for the Driver specified by + This and the language specified by Language was + returned in DriverName. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER DriverName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +PciBusComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ); + + +/** + Retrieves a Unicode string that is the user readable name of the controller + that is being managed by a driver. + + This function retrieves the user readable name of the controller specified by + ControllerHandle and ChildHandle in the form of a Unicode string. If the + driver specified by This has a user readable name in the language specified by + Language, then a pointer to the controller name is returned in ControllerName, + and EFI_SUCCESS is returned. If the driver specified by This is not currently + managing the controller specified by ControllerHandle and ChildHandle, + then EFI_UNSUPPORTED is returned. If the driver specified by This does not + support the language specified by Language, then EFI_UNSUPPORTED is returned. + + @param This[in] A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or + EFI_COMPONENT_NAME_PROTOCOL instance. + + @param ControllerHandle[in] The handle of a controller that the driver + specified by This is managing. This handle + specifies the controller whose name is to be + returned. + + @param ChildHandle[in] The handle of the child controller to retrieve + the name of. This is an optional parameter that + may be NULL. It will be NULL for device + drivers. It will also be NULL for a bus drivers + that wish to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that wishes to retrieve the name of a + child controller. + + @param Language[in] A pointer to a Null-terminated ASCII string + array indicating the language. This is the + language of the driver name that the caller is + requesting, and it must match one of the + languages specified in SupportedLanguages. The + number of languages supported by a driver is up + to the driver writer. Language is specified in + RFC 4646 or ISO 639-2 language code format. + + @param ControllerName[out] A pointer to the Unicode string to return. + This Unicode string is the name of the + controller specified by ControllerHandle and + ChildHandle in the language specified by + Language from the point of view of the driver + specified by This. + + @retval EFI_SUCCESS The Unicode string for the user readable name in + the language specified by Language for the + driver specified by This was returned in + DriverName. + + @retval EFI_INVALID_PARAMETER ControllerHandle is not a valid EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid + EFI_HANDLE. + + @retval EFI_INVALID_PARAMETER Language is NULL. + + @retval EFI_INVALID_PARAMETER ControllerName is NULL. + + @retval EFI_UNSUPPORTED The driver specified by This is not currently + managing the controller specified by + ControllerHandle and ChildHandle. + + @retval EFI_UNSUPPORTED The driver specified by This does not support + the language specified by Language. + +**/ +EFI_STATUS +EFIAPI +PciBusComponentNameGetControllerName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN EFI_HANDLE ControllerHandle, + IN EFI_HANDLE ChildHandle OPTIONAL, + IN CHAR8 *Language, + OUT CHAR16 **ControllerName + ); + + +#endif diff --git a/MdeModulePkg/Bus/Pci/PciBusDxe/PciBus.c b/MdeModulePkg/Bus/Pci/PciBusDxe/PciBus.c new file mode 100644 index 0000000000..71bb5397bb --- /dev/null +++ b/MdeModulePkg/Bus/Pci/PciBusDxe/PciBus.c @@ -0,0 +1,382 @@ +/** @file + Driver Binding functions for PCI Bus module. + + Single PCI bus driver instance will manager all PCI Root Bridges in one EFI based firmware, + since all PCI Root Bridges' resources need to be managed together. + Supported() function will try to get PCI Root Bridge IO Protocol. + Start() function will get PCI Host Bridge Resource Allocation Protocol to manage all + PCI Root Bridges. So it means platform needs install PCI Root Bridge IO protocol for each + PCI Root Bus and install PCI Host Bridge Resource Allocation Protocol. + +Copyright (c) 2006 - 2009, Intel Corporation +All rights reserved. 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" + +// +// PCI Bus Driver Global Variables +// +EFI_DRIVER_BINDING_PROTOCOL gPciBusDriverBinding = { + PciBusDriverBindingSupported, + PciBusDriverBindingStart, + PciBusDriverBindingStop, + 0xa, + NULL, + NULL +}; + +EFI_HANDLE gPciHostBrigeHandles[PCI_MAX_HOST_BRIDGE_NUM]; +EFI_INCOMPATIBLE_PCI_DEVICE_SUPPORT_PROTOCOL *gEfiIncompatiblePciDeviceSupport = NULL; +UINTN gPciHostBridgeNumber = 0; +BOOLEAN gFullEnumeration = TRUE; +UINT64 gAllOne = 0xFFFFFFFFFFFFFFFFULL; +UINT64 gAllZero = 0; + +EFI_PCI_PLATFORM_PROTOCOL *gPciPlatformProtocol; +EFI_PCI_OVERRIDE_PROTOCOL *gPciOverrideProtocol; + + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_PCI_HOTPLUG_REQUEST_PROTOCOL mPciHotPlugRequest = { + PciHotPlugRequestNotify +}; + +/** + The Entry Point for PCI Bus module. The user code starts with this function. + + Installs driver module protocols and. Creates virtual device handles for ConIn, + ConOut, and StdErr. Installs Simple Text In protocol, Simple Text In Ex protocol, + Simple Pointer protocol, Absolute Pointer protocol on those virtual handlers. + Installs Graphics Output protocol and/or UGA Draw protocol if needed. + + @param[in] ImageHandle The firmware allocated handle for the EFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The entry point is executed successfully. + @retval other Some error occurred when executing this entry point. + +**/ +EFI_STATUS +EFIAPI +PciBusEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + EFI_HANDLE Handle; + + // + // Initializes PCI devices pool + // + InitializePciDevicePool (); + + // + // Install driver model protocol(s). + // + Status = EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gPciBusDriverBinding, + ImageHandle, + &gPciBusComponentName, + &gPciBusComponentName2 + ); + ASSERT_EFI_ERROR (Status); + + if (FeaturePcdGet (PcdPciBusHotplugDeviceSupport)) { + // + // If Hot Plug is supported, install EFI PCI Hot Plug Request protocol. + // + Handle = NULL; + Status = gBS->InstallProtocolInterface ( + &Handle, + &gEfiPciHotPlugRequestProtocolGuid, + EFI_NATIVE_INTERFACE, + &mPciHotPlugRequest + ); + } + + return Status; +} + +/** + Test to see if this driver supports ControllerHandle. Any ControllerHandle + than contains a gEfiPciRootBridgeIoProtocolGuid protocol can be supported. + + @param This Protocol instance pointer. + @param Controller Handle of device to test. + @param RemainingDevicePath Optional parameter use to pick a specific child. + device to start. + + @retval EFI_SUCCESS This driver supports this device. + @retval EFI_ALREADY_STARTED This driver is already running on this device. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +PciBusDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; + EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *PciRootBridgeIo; + EFI_DEV_PATH_PTR Node; + + // + // Check RemainingDevicePath validation + // + if (RemainingDevicePath != NULL) { + // + // Check if RemainingDevicePath is the End of Device Path Node, + // if yes, go on checking other conditions + // + if (!IsDevicePathEnd (RemainingDevicePath)) { + // + // If RemainingDevicePath isn't the End of Device Path Node, + // check its validation + // + 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; + } + } + } + + // + // Check if Pci Root Bridge IO protocol is installed by platform + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiPciRootBridgeIoProtocolGuid, + (VOID **) &PciRootBridgeIo, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (Status == EFI_ALREADY_STARTED) { + return EFI_SUCCESS; + } + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Close the I/O Abstraction(s) used to perform the supported test + // + gBS->CloseProtocol ( + Controller, + &gEfiPciRootBridgeIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + // + // Open the EFI Device Path protocol needed to perform the supported test + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + (VOID **) &ParentDevicePath, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (Status == EFI_ALREADY_STARTED) { + return EFI_SUCCESS; + } + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Close protocol, don't use device path protocol in the Support() function + // + gBS->CloseProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + return EFI_SUCCESS; +} + +/** + Start this driver on ControllerHandle and enumerate Pci bus and start + all device under PCI bus. + + @param This Protocol instance pointer. + @param Controller Handle of device to bind driver to. + @param RemainingDevicePath Optional parameter use to pick a specific child. + device to start. + + @retval EFI_SUCCESS This driver is added to ControllerHandle. + @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +PciBusDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + EFI_STATUS Status; + + // + // Check RemainingDevicePath validation + // + if (RemainingDevicePath != NULL) { + // + // Check if RemainingDevicePath is the End of Device Path Node, + // if yes, return EFI_SUCCESS + // + if (IsDevicePathEnd (RemainingDevicePath)) { + return EFI_SUCCESS; + } + } + + Status = gBS->LocateProtocol ( + &gEfiIncompatiblePciDeviceSupportProtocolGuid, + NULL, + (VOID **) &gEfiIncompatiblePciDeviceSupport + ); + + // + // If PCI Platform protocol is available, get it now. + // If the platform implements this, it must be installed before BDS phase + // + gPciPlatformProtocol = NULL; + gBS->LocateProtocol ( + &gEfiPciPlatformProtocolGuid, + NULL, + (VOID **) &gPciPlatformProtocol + ); + + // + // If PCI Platform protocol doesn't exist, try to Pci Override Protocol. + // + if (gPciPlatformProtocol == NULL) { + gPciOverrideProtocol = NULL; + gBS->LocateProtocol ( + &gEfiPciOverrideProtocolGuid, + NULL, + (VOID **) &gPciOverrideProtocol + ); + } + + gFullEnumeration = (BOOLEAN) ((SearchHostBridgeHandle (Controller) ? FALSE : TRUE)); + + // + // Enumerate the entire host bridge + // After enumeration, a database that records all the device information will be created + // + // + Status = PciEnumerator (Controller); + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Start all the devices under the entire host bridge. + // + StartPciDevices (Controller); + + return EFI_SUCCESS; +} + +/** + Stop this driver on ControllerHandle. Support stoping any child handles + created by this driver. + + @param This Protocol instance pointer. + @param Controller Handle of device to stop driver on. + @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number of + children is zero stop the entire bus driver. + @param ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCESS This driver is removed ControllerHandle. + @retval other This driver was not removed from this device. + +**/ +EFI_STATUS +EFIAPI +PciBusDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + EFI_STATUS Status; + UINTN Index; + BOOLEAN AllChildrenStopped; + + if (NumberOfChildren == 0) { + // + // Close the bus driver + // + gBS->CloseProtocol ( + Controller, + &gEfiDevicePathProtocolGuid, + This->DriverBindingHandle, + Controller + ); + gBS->CloseProtocol ( + Controller, + &gEfiPciRootBridgeIoProtocolGuid, + This->DriverBindingHandle, + Controller + ); + + DestroyRootBridgeByHandle ( + Controller + ); + + return EFI_SUCCESS; + } + + // + // Stop all the children + // + + AllChildrenStopped = TRUE; + + for (Index = 0; Index < NumberOfChildren; Index++) { + + // + // De register all the pci device + // + Status = DeRegisterPciDevice (Controller, ChildHandleBuffer[Index]); + + if (EFI_ERROR (Status)) { + AllChildrenStopped = FALSE; + } + } + + if (!AllChildrenStopped) { + return EFI_DEVICE_ERROR; + } + + return EFI_SUCCESS; +} + diff --git a/MdeModulePkg/Bus/Pci/PciBusDxe/PciBus.h b/MdeModulePkg/Bus/Pci/PciBusDxe/PciBus.h new file mode 100644 index 0000000000..722f31d03c --- /dev/null +++ b/MdeModulePkg/Bus/Pci/PciBusDxe/PciBus.h @@ -0,0 +1,382 @@ +/** @file + Header files and data structures needed by PCI Bus module. + +Copyright (c) 2006 - 2009, Intel Corporation +All rights reserved. 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. + +**/ + + +#ifndef _EFI_PCI_BUS_H_ +#define _EFI_PCI_BUS_H_ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +typedef struct _PCI_IO_DEVICE PCI_IO_DEVICE; +typedef struct _PCI_BAR PCI_BAR; + +#define EFI_PCI_RID(Bus, Device, Function) (((UINT32)Bus << 8) + ((UINT32)Device << 3) + (UINT32)Function) +#define EFI_PCI_BUS_OF_RID(RID) ((UINT32)RID >> 8) + +#define EFI_PCI_IOV_POLICY_ARI 0x0001 +#define EFI_PCI_IOV_POLICY_SRIOV 0x0002 +#define EFI_PCI_IOV_POLICY_MRIOV 0x0004 + +typedef enum { + PciBarTypeUnknown = 0, + PciBarTypeIo16, + PciBarTypeIo32, + PciBarTypeMem32, + PciBarTypePMem32, + PciBarTypeMem64, + PciBarTypePMem64, + PciBarTypeIo, + PciBarTypeMem, + PciBarTypeMaxType +} PCI_BAR_TYPE; + +#include "ComponentName.h" +#include "PciIo.h" +#include "PciCommand.h" +#include "PciDeviceSupport.h" +#include "PciEnumerator.h" +#include "PciEnumeratorSupport.h" +#include "PciDriverOverride.h" +#include "PciRomTable.h" +#include "PciOptionRomSupport.h" +#include "PciPowerManagement.h" +#include "PciHotPlugSupport.h" +#include "PciLib.h" + +#define VGABASE1 0x3B0 +#define VGALIMIT1 0x3BB + +#define VGABASE2 0x3C0 +#define VGALIMIT2 0x3DF + +#define ISABASE 0x100 +#define ISALIMIT 0x3FF + +// +// PCI BAR parameters +// +struct _PCI_BAR { + UINT64 BaseAddress; + UINT64 Length; + UINT64 Alignment; + PCI_BAR_TYPE BarType; + BOOLEAN Prefetchable; + UINT8 MemType; + UINT8 Offset; +}; + +// +// defined in PCI Card Specification, 8.0 +// +#define PCI_CARD_MEMORY_BASE_0 0x1C +#define PCI_CARD_MEMORY_LIMIT_0 0x20 +#define PCI_CARD_MEMORY_BASE_1 0x24 +#define PCI_CARD_MEMORY_LIMIT_1 0x28 +#define PCI_CARD_IO_BASE_0_LOWER 0x2C +#define PCI_CARD_IO_BASE_0_UPPER 0x2E +#define PCI_CARD_IO_LIMIT_0_LOWER 0x30 +#define PCI_CARD_IO_LIMIT_0_UPPER 0x32 +#define PCI_CARD_IO_BASE_1_LOWER 0x34 +#define PCI_CARD_IO_BASE_1_UPPER 0x36 +#define PCI_CARD_IO_LIMIT_1_LOWER 0x38 +#define PCI_CARD_IO_LIMIT_1_UPPER 0x3A +#define PCI_CARD_BRIDGE_CONTROL 0x3E + +#define PCI_CARD_PREFETCHABLE_MEMORY_0_ENABLE BIT8 +#define PCI_CARD_PREFETCHABLE_MEMORY_1_ENABLE BIT9 + +#define PPB_BAR_0 0 +#define PPB_BAR_1 1 +#define PPB_IO_RANGE 2 +#define PPB_MEM32_RANGE 3 +#define PPB_PMEM32_RANGE 4 +#define PPB_PMEM64_RANGE 5 +#define PPB_MEM64_RANGE 0xFF + +#define P2C_BAR_0 0 +#define P2C_MEM_1 1 +#define P2C_MEM_2 2 +#define P2C_IO_1 3 +#define P2C_IO_2 4 + +#define EFI_BRIDGE_IO32_DECODE_SUPPORTED 0x0001 +#define EFI_BRIDGE_PMEM32_DECODE_SUPPORTED 0x0002 +#define EFI_BRIDGE_PMEM64_DECODE_SUPPORTED 0x0004 +#define EFI_BRIDGE_IO16_DECODE_SUPPORTED 0x0008 +#define EFI_BRIDGE_PMEM_MEM_COMBINE_SUPPORTED 0x0010 +#define EFI_BRIDGE_MEM64_DECODE_SUPPORTED 0x0020 +#define EFI_BRIDGE_MEM32_DECODE_SUPPORTED 0x0040 + +#define PCI_MAX_HOST_BRIDGE_NUM 0x0010 + +// +// Define option for attribute +// +#define EFI_SET_SUPPORTS 0 +#define EFI_SET_ATTRIBUTES 1 + +#define PCI_IO_DEVICE_SIGNATURE SIGNATURE_32 ('p', 'c', 'i', 'o') + +struct _PCI_IO_DEVICE { + UINT32 Signature; + EFI_HANDLE Handle; + EFI_PCI_IO_PROTOCOL PciIo; + LIST_ENTRY Link; + + EFI_BUS_SPECIFIC_DRIVER_OVERRIDE_PROTOCOL PciDriverOverride; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *PciRootBridgeIo; + EFI_LOAD_FILE2_PROTOCOL LoadFile2; + + // + // PCI configuration space header type + // + PCI_TYPE00 Pci; + + // + // Bus number, Device number, Function number + // + UINT8 BusNumber; + UINT8 DeviceNumber; + UINT8 FunctionNumber; + + // + // BAR for this PCI Device + // + PCI_BAR PciBar[PCI_MAX_BAR]; + + // + // The bridge device this pci device is subject to + // + PCI_IO_DEVICE *Parent; + + // + // A linked list for children Pci Device if it is bridge device + // + LIST_ENTRY ChildList; + + // + // TURE if the PCI bus driver creates the handle for this PCI device + // + BOOLEAN Registered; + + // + // TRUE if the PCI bus driver successfully allocates the resource required by + // this PCI device + // + BOOLEAN Allocated; + + // + // The attribute this PCI device currently set + // + UINT64 Attributes; + + // + // The attributes this PCI device actually supports + // + UINT64 Supports; + + // + // The resource decode the bridge supports + // + UINT32 Decodes; + + // + // The OptionRom Size + // + UINT64 RomSize; + + // + // The OptionRom Size + // + UINT64 RomBase; + + // + // TRUE if all OpROM (in device or in platform specific position) have been processed + // + BOOLEAN AllOpRomProcessed; + + // + // TRUE if there is any EFI driver in the OptionRom + // + BOOLEAN BusOverride; + + // + // A list tracking reserved resource on a bridge device + // + LIST_ENTRY ReservedResourceList; + + // + // A list tracking image handle of platform specific overriding driver + // + LIST_ENTRY OptionRomDriverList; + + EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *ResourcePaddingDescriptors; + EFI_HPC_PADDING_ATTRIBUTES PaddingAttributes; + + BOOLEAN IsPciExp; + // + // For SR-IOV + // + UINT8 PciExpressCapabilityOffset; + UINT32 AriCapabilityOffset; + UINT32 SrIovCapabilityOffset; + UINT32 MrIovCapabilityOffset; + PCI_BAR VfPciBar[PCI_MAX_BAR]; + UINT32 SystemPageSize; + UINT16 InitialVFs; + UINT16 ReservedBusNum; +}; + +#define PCI_IO_DEVICE_FROM_PCI_IO_THIS(a) \ + CR (a, PCI_IO_DEVICE, PciIo, PCI_IO_DEVICE_SIGNATURE) + +#define PCI_IO_DEVICE_FROM_PCI_DRIVER_OVERRIDE_THIS(a) \ + CR (a, PCI_IO_DEVICE, PciDriverOverride, PCI_IO_DEVICE_SIGNATURE) + +#define PCI_IO_DEVICE_FROM_LINK(a) \ + CR (a, PCI_IO_DEVICE, Link, PCI_IO_DEVICE_SIGNATURE) + +#define PCI_IO_DEVICE_FROM_LOAD_FILE2_THIS(a) \ + CR (a, PCI_IO_DEVICE, LoadFile2, PCI_IO_DEVICE_SIGNATURE) + + + +// +// Global Variables +// +extern EFI_INCOMPATIBLE_PCI_DEVICE_SUPPORT_PROTOCOL *gEfiIncompatiblePciDeviceSupport; +extern EFI_DRIVER_BINDING_PROTOCOL gPciBusDriverBinding; +extern EFI_COMPONENT_NAME_PROTOCOL gPciBusComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gPciBusComponentName2; +extern BOOLEAN gFullEnumeration; +extern UINTN gPciHostBridgeNumber; +extern EFI_HANDLE gPciHostBrigeHandles[PCI_MAX_HOST_BRIDGE_NUM]; +extern UINT64 gAllOne; +extern UINT64 gAllZero; +extern EFI_PCI_PLATFORM_PROTOCOL *gPciPlatformProtocol; +extern EFI_PCI_OVERRIDE_PROTOCOL *gPciOverrideProtocol; + + + +/** + Macro that checks whether device is a GFX device. + + @param _p Specified device. + + @retval TRUE Device is a a GFX device. + @retval FALSE Device is not a a GFX device. + +**/ +#define IS_PCI_GFX(_p) IS_CLASS2 (_p, PCI_CLASS_DISPLAY, PCI_CLASS_DISPLAY_OTHER) + +/** + Test to see if this driver supports ControllerHandle. Any ControllerHandle + than contains a gEfiPciRootBridgeIoProtocolGuid protocol can be supported. + + @param This Protocol instance pointer. + @param Controller Handle of device to test. + @param RemainingDevicePath Optional parameter use to pick a specific child. + device to start. + + @retval EFI_SUCCESS This driver supports this device. + @retval EFI_ALREADY_STARTED This driver is already running on this device. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +PciBusDriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + Start this driver on ControllerHandle and enumerate Pci bus and start + all device under PCI bus. + + @param This Protocol instance pointer. + @param Controller Handle of device to bind driver to. + @param RemainingDevicePath Optional parameter use to pick a specific child. + device to start. + + @retval EFI_SUCCESS This driver is added to ControllerHandle. + @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle. + @retval other This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +PciBusDriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + Stop this driver on ControllerHandle. Support stoping any child handles + created by this driver. + + @param This Protocol instance pointer. + @param Controller Handle of device to stop driver on. + @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number of + children is zero stop the entire bus driver. + @param ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCESS This driver is removed ControllerHandle. + @retval other This driver was not removed from this device. + +**/ +EFI_STATUS +EFIAPI +PciBusDriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ); + +#endif diff --git a/MdeModulePkg/Bus/Pci/PciBusDxe/PciBusDxe.inf b/MdeModulePkg/Bus/Pci/PciBusDxe/PciBusDxe.inf new file mode 100644 index 0000000000..cd7865c0b0 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/PciBusDxe/PciBusDxe.inf @@ -0,0 +1,116 @@ +#/** @file +# Component description file for PciBus module. +# +# PCI bus driver. This module will probe all PCI devices and allocate MMIO and IO +# space for these devices. Please use PCD feature flag PcdPciBusHotplugDeviceSupport to enable +# hot plug supporting. +# +# Copyright (c) 2006 - 2009, Intel Corporation +# +# All rights reserved. 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. +# +# +#**/ + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = PciBusDxe + FILE_GUID = 93B80004-9FB3-11d4-9A3A-0090273FC14D + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + EFI_SPECIFICATION_VERSION = 0x00020000 + ENTRY_POINT = PciBusEntryPoint + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# +# DRIVER_BINDING = gPciBusDriverBinding +# COMPONENT_NAME = gPciBusComponentName +# COMPONENT_NAME2 = gPciBusComponentName2 +# + +[Sources.common] + PciLib.c + PciIo.c + PciBus.c + PciDeviceSupport.c + ComponentName.c + ComponentName.h + PciCommand.c + PciResourceSupport.c + PciEnumeratorSupport.c + PciEnumerator.c + PciOptionRomSupport.c + PciDriverOverride.c + PciPowerManagement.c + PciPowerManagement.h + PciDriverOverride.h + PciRomTable.c + PciHotPlugSupport.c + PciLib.h + PciHotPlugSupport.h + PciRomTable.h + PciOptionRomSupport.h + PciEnumeratorSupport.h + PciEnumerator.h + PciResourceSupport.h + PciDeviceSupport.h + PciCommand.h + PciIo.h + PciBus.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + PcdLib + DevicePathLib + UefiBootServicesTableLib + MemoryAllocationLib + ReportStatusCodeLib + BaseMemoryLib + UefiLib + BaseLib + UefiDriverEntryPoint + DebugLib + PeCoffLib + +[Protocols] + gEfiPciHotPlugRequestProtocolGuid ## BY_START + gEfiPciIoProtocolGuid ## BY_START + gEfiDevicePathProtocolGuid ## BY_START + gEfiBusSpecificDriverOverrideProtocolGuid ## BY_START + gEfiLoadedImageProtocolGuid ## CONSUMED + gEfiDecompressProtocolGuid ## CONSUMED + gEfiPciHotPlugInitProtocolGuid ## CONSUMED + gEfiPciHostBridgeResourceAllocationProtocolGuid ## CONSUMED + gEfiPciPlatformProtocolGuid ## CONSUMED + gEfiPciOverrideProtocolGuid ## CONSUMED + gEfiPciEnumerationCompleteProtocolGuid ## CONSUMED + gEfiPciRootBridgeIoProtocolGuid ## CONSUMED + gEfiIncompatiblePciDeviceSupportProtocolGuid ## CONSUMED + gEfiLoadFile2ProtocolGuid ## CONSUMED + +[FeaturePcd.common] + gEfiMdeModulePkgTokenSpaceGuid.PcdPciBusHotplugDeviceSupport + gEfiMdeModulePkgTokenSpaceGuid.PcdSrIovSupport + gEfiMdeModulePkgTokenSpaceGuid.PcdAriSupport + gEfiMdeModulePkgTokenSpaceGuid.PcdMrIovSupport + +[FixedPcd.common] + gEfiMdeModulePkgTokenSpaceGuid.PcdSrIovSystemPageSize +# [Event] +# ## +# # Notify event set by CreateEventForHpc () for PCI Hot Plug controller. +# # +# EVT_NOTIFY_SIGNAL ## PRODUCES +# +# diff --git a/MdeModulePkg/Bus/Pci/PciBusDxe/PciCommand.c b/MdeModulePkg/Bus/Pci/PciBusDxe/PciCommand.c new file mode 100644 index 0000000000..601af309b7 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/PciBusDxe/PciCommand.c @@ -0,0 +1,248 @@ +/** @file + PCI command register operations supporting functions implementation for PCI Bus module. + +Copyright (c) 2006 - 2009, Intel Corporation +All rights reserved. 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" + +/** + Operate the PCI register via PciIo function interface. + + @param PciIoDevice Pointer to instance of PCI_IO_DEVICE. + @param Command Operator command. + @param Offset The address within the PCI configuration space for the PCI controller. + @param Operation Type of Operation. + @param PtrCommand Return buffer holding old PCI command, if operation is not EFI_SET_REGISTER. + + @return Status of PciIo operation. + +**/ +EFI_STATUS +PciOperateRegister ( + IN PCI_IO_DEVICE *PciIoDevice, + IN UINT16 Command, + IN UINT8 Offset, + IN UINT8 Operation, + OUT UINT16 *PtrCommand + ) +{ + UINT16 OldCommand; + EFI_STATUS Status; + EFI_PCI_IO_PROTOCOL *PciIo; + + OldCommand = 0; + PciIo = &PciIoDevice->PciIo; + + if (Operation != EFI_SET_REGISTER) { + Status = PciIo->Pci.Read ( + PciIo, + EfiPciIoWidthUint16, + Offset, + 1, + &OldCommand + ); + + if (Operation == EFI_GET_REGISTER) { + *PtrCommand = OldCommand; + return Status; + } + } + + if (Operation == EFI_ENABLE_REGISTER) { + OldCommand = (UINT16) (OldCommand | Command); + } else if (Operation == EFI_DISABLE_REGISTER) { + OldCommand = (UINT16) (OldCommand & ~(Command)); + } else { + OldCommand = Command; + } + + return PciIo->Pci.Write ( + PciIo, + EfiPciIoWidthUint16, + Offset, + 1, + &OldCommand + ); +} + +/** + Check the cpability supporting by given device. + + @param PciIoDevice Pointer to instance of PCI_IO_DEVICE. + + @retval TRUE Cpability supportted. + @retval FALSE Cpability not supportted. + +**/ +BOOLEAN +PciCapabilitySupport ( + IN PCI_IO_DEVICE *PciIoDevice + ) +{ + if ((PciIoDevice->Pci.Hdr.Status & EFI_PCI_STATUS_CAPABILITY) != 0) { + return TRUE; + } + + return FALSE; +} + +/** + Locate capability register block per capability ID. + + @param PciIoDevice A pointer to the PCI_IO_DEVICE. + @param CapId The capability ID. + @param Offset A pointer to the offset returned. + @param NextRegBlock A pointer to the next block returned. + + @retval EFI_SUCCESS Successfuly located capability register block. + @retval EFI_UNSUPPORTED Pci device does not support capability. + @retval EFI_NOT_FOUND Pci device support but can not find register block. + +**/ +EFI_STATUS +LocateCapabilityRegBlock ( + IN PCI_IO_DEVICE *PciIoDevice, + IN UINT8 CapId, + IN OUT UINT8 *Offset, + OUT UINT8 *NextRegBlock OPTIONAL + ) +{ + UINT8 CapabilityPtr; + UINT16 CapabilityEntry; + UINT8 CapabilityID; + + // + // To check the cpability of this device supports + // + if (!PciCapabilitySupport (PciIoDevice)) { + return EFI_UNSUPPORTED; + } + + if (*Offset != 0) { + CapabilityPtr = *Offset; + } else { + + CapabilityPtr = 0; + if (IS_CARDBUS_BRIDGE (&PciIoDevice->Pci)) { + + PciIoDevice->PciIo.Pci.Read ( + &PciIoDevice->PciIo, + EfiPciIoWidthUint8, + EFI_PCI_CARDBUS_BRIDGE_CAPABILITY_PTR, + 1, + &CapabilityPtr + ); + } else { + + PciIoDevice->PciIo.Pci.Read ( + &PciIoDevice->PciIo, + EfiPciIoWidthUint8, + PCI_CAPBILITY_POINTER_OFFSET, + 1, + &CapabilityPtr + ); + } + } + + while ((CapabilityPtr >= 0x40) && ((CapabilityPtr & 0x03) == 0x00)) { + PciIoDevice->PciIo.Pci.Read ( + &PciIoDevice->PciIo, + EfiPciIoWidthUint16, + CapabilityPtr, + 1, + &CapabilityEntry + ); + + CapabilityID = (UINT8) CapabilityEntry; + + if (CapabilityID == CapId) { + *Offset = CapabilityPtr; + if (NextRegBlock != NULL) { + *NextRegBlock = (UINT8) (CapabilityEntry >> 8); + } + + return EFI_SUCCESS; + } + + CapabilityPtr = (UINT8) (CapabilityEntry >> 8); + } + + return EFI_NOT_FOUND; +} + +/** + Locate PciExpress capability register block per capability ID. + + @param PciIoDevice A pointer to the PCI_IO_DEVICE. + @param CapId The capability ID. + @param Offset A pointer to the offset returned. + @param NextRegBlock A pointer to the next block returned. + + @retval EFI_SUCCESS Successfuly located capability register block. + @retval EFI_UNSUPPORTED Pci device does not support capability. + @retval EFI_NOT_FOUND Pci device support but can not find register block. + +**/ +EFI_STATUS +LocatePciExpressCapabilityRegBlock ( + IN PCI_IO_DEVICE *PciIoDevice, + IN UINT16 CapId, + IN OUT UINT32 *Offset, + OUT UINT32 *NextRegBlock OPTIONAL + ) +{ + UINT32 CapabilityPtr; + UINT32 CapabilityEntry; + UINT16 CapabilityID; + + // + // To check the capability of this device supports + // + if (!PciIoDevice->IsPciExp) { + return EFI_UNSUPPORTED; + } + + if (*Offset != 0) { + CapabilityPtr = *Offset; + } else { + CapabilityPtr = EFI_PCIE_CAPABILITY_BASE_OFFSET; + } + + while (CapabilityPtr != 0) { + // + // Mask it to DWORD alignment per PCI spec + // + CapabilityPtr &= 0xFFC; + PciIoDevice->PciIo.Pci.Read ( + &PciIoDevice->PciIo, + EfiPciIoWidthUint32, + CapabilityPtr, + 1, + &CapabilityEntry + ); + + CapabilityID = (UINT16) CapabilityEntry; + + if (CapabilityID == CapId) { + *Offset = CapabilityPtr; + if (NextRegBlock != NULL) { + *NextRegBlock = (CapabilityEntry >> 20) & 0xFFF; + } + + return EFI_SUCCESS; + } + + CapabilityPtr = (CapabilityEntry >> 20) & 0xFFF; + } + + return EFI_NOT_FOUND; +} diff --git a/MdeModulePkg/Bus/Pci/PciBusDxe/PciCommand.h b/MdeModulePkg/Bus/Pci/PciBusDxe/PciCommand.h new file mode 100644 index 0000000000..942bea941e --- /dev/null +++ b/MdeModulePkg/Bus/Pci/PciBusDxe/PciCommand.h @@ -0,0 +1,238 @@ +/** @file + PCI command register operations supporting functions declaration for PCI Bus module. + +Copyright (c) 2006 - 2009, Intel Corporation +All rights reserved. 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. + +**/ + + +#ifndef _EFI_PCI_COMMAND_H_ +#define _EFI_PCI_COMMAND_H_ + +// +// The PCI Command register bits owned by PCI Bus driver. +// +// They should be cleared at the beginning. The other registers +// are owned by chipset, we should not touch them. +// +#define EFI_PCI_COMMAND_BITS_OWNED ( \ + EFI_PCI_COMMAND_IO_SPACE | \ + EFI_PCI_COMMAND_MEMORY_SPACE | \ + EFI_PCI_COMMAND_BUS_MASTER | \ + EFI_PCI_COMMAND_MEMORY_WRITE_AND_INVALIDATE | \ + EFI_PCI_COMMAND_VGA_PALETTE_SNOOP | \ + EFI_PCI_COMMAND_FAST_BACK_TO_BACK \ + ) + +// +// The PCI Bridge Control register bits owned by PCI Bus driver. +// +// They should be cleared at the beginning. The other registers +// are owned by chipset, we should not touch them. +// +#define EFI_PCI_BRIDGE_CONTROL_BITS_OWNED ( \ + EFI_PCI_BRIDGE_CONTROL_ISA | \ + EFI_PCI_BRIDGE_CONTROL_VGA | \ + EFI_PCI_BRIDGE_CONTROL_VGA_16 | \ + EFI_PCI_BRIDGE_CONTROL_FAST_BACK_TO_BACK \ + ) + +// +// The PCCard Bridge Control register bits owned by PCI Bus driver. +// +// They should be cleared at the beginning. The other registers +// are owned by chipset, we should not touch them. +// +#define EFI_PCCARD_BRIDGE_CONTROL_BITS_OWNED ( \ + EFI_PCI_BRIDGE_CONTROL_ISA | \ + EFI_PCI_BRIDGE_CONTROL_VGA | \ + EFI_PCI_BRIDGE_CONTROL_FAST_BACK_TO_BACK \ + ) + + +#define EFI_GET_REGISTER 1 +#define EFI_SET_REGISTER 2 +#define EFI_ENABLE_REGISTER 3 +#define EFI_DISABLE_REGISTER 4 + +/** + Operate the PCI register via PciIo function interface. + + @param PciIoDevice Pointer to instance of PCI_IO_DEVICE. + @param Command Operator command. + @param Offset The address within the PCI configuration space for the PCI controller. + @param Operation Type of Operation. + @param PtrCommand Return buffer holding old PCI command, if operation is not EFI_SET_REGISTER. + + @return Status of PciIo operation. + +**/ +EFI_STATUS +PciOperateRegister ( + IN PCI_IO_DEVICE *PciIoDevice, + IN UINT16 Command, + IN UINT8 Offset, + IN UINT8 Operation, + OUT UINT16 *PtrCommand + ); + +/** + Check the cpability supporting by given device. + + @param PciIoDevice Pointer to instance of PCI_IO_DEVICE. + + @retval TRUE Cpability supportted. + @retval FALSE Cpability not supportted. + +**/ +BOOLEAN +PciCapabilitySupport ( + IN PCI_IO_DEVICE *PciIoDevice + ); + +/** + Locate capability register block per capability ID. + + @param PciIoDevice A pointer to the PCI_IO_DEVICE. + @param CapId The capability ID. + @param Offset A pointer to the offset returned. + @param NextRegBlock A pointer to the next block returned. + + @retval EFI_SUCCESS Successfuly located capability register block. + @retval EFI_UNSUPPORTED Pci device does not support capability. + @retval EFI_NOT_FOUND Pci device support but can not find register block. + +**/ +EFI_STATUS +LocateCapabilityRegBlock ( + IN PCI_IO_DEVICE *PciIoDevice, + IN UINT8 CapId, + IN OUT UINT8 *Offset, + OUT UINT8 *NextRegBlock OPTIONAL + ); + +/** + Locate PciExpress capability register block per capability ID. + + @param PciIoDevice A pointer to the PCI_IO_DEVICE. + @param CapId The capability ID. + @param Offset A pointer to the offset returned. + @param NextRegBlock A pointer to the next block returned. + + @retval EFI_SUCCESS Successfuly located capability register block. + @retval EFI_UNSUPPORTED Pci device does not support capability. + @retval EFI_NOT_FOUND Pci device support but can not find register block. + +**/ +EFI_STATUS +LocatePciExpressCapabilityRegBlock ( + IN PCI_IO_DEVICE *PciIoDevice, + IN UINT16 CapId, + IN OUT UINT32 *Offset, + OUT UINT32 *NextRegBlock OPTIONAL + ); + +/** + Macro that reads command register. + + @param a[in] Pointer to instance of PCI_IO_DEVICE. + @param b[out] Pointer to the 16-bit value read from command register. + + @return status of PciIo operation + +**/ +#define PCI_READ_COMMAND_REGISTER(a,b) \ + PciOperateRegister (a, 0, PCI_COMMAND_OFFSET, EFI_GET_REGISTER, b) + +/** + Macro that writes command register. + + @param a[in] Pointer to instance of PCI_IO_DEVICE. + @param b[in] The 16-bit value written into command register. + + @return status of PciIo operation + +**/ +#define PCI_SET_COMMAND_REGISTER(a,b) \ + PciOperateRegister (a, b, PCI_COMMAND_OFFSET, EFI_SET_REGISTER, NULL) + +/** + Macro that enables command register. + + @param a[in] Pointer to instance of PCI_IO_DEVICE. + @param b[in] The enabled value written into command register. + + @return status of PciIo operation + +**/ +#define PCI_ENABLE_COMMAND_REGISTER(a,b) \ + PciOperateRegister (a, b, PCI_COMMAND_OFFSET, EFI_ENABLE_REGISTER, NULL) + +/** + Macro that disalbes command register. + + @param a[in] Pointer to instance of PCI_IO_DEVICE. + @param b[in] The disabled value written into command register. + + @return status of PciIo operation + +**/ +#define PCI_DISABLE_COMMAND_REGISTER(a,b) \ + PciOperateRegister (a, b, PCI_COMMAND_OFFSET, EFI_DISABLE_REGISTER, NULL) + +/** + Macro that reads PCI bridge control register. + + @param a[in] Pointer to instance of PCI_IO_DEVICE. + @param b[out] The 16-bit value read from control register. + + @return status of PciIo operation + +**/ +#define PCI_READ_BRIDGE_CONTROL_REGISTER(a,b) \ + PciOperateRegister (a, 0, PCI_BRIDGE_CONTROL_REGISTER_OFFSET, EFI_GET_REGISTER, b) + +/** + Macro that writes PCI bridge control register. + + @param a[in] Pointer to instance of PCI_IO_DEVICE. + @param b[in] The 16-bit value written into control register. + + @return status of PciIo operation + +**/ +#define PCI_SET_BRIDGE_CONTROL_REGISTER(a,b) \ + PciOperateRegister (a, b, PCI_BRIDGE_CONTROL_REGISTER_OFFSET, EFI_SET_REGISTER, NULL) + +/** + Macro that enables PCI bridge control register. + + @param a[in] Pointer to instance of PCI_IO_DEVICE. + @param b[in] The enabled value written into command register. + + @return status of PciIo operation + +**/ +#define PCI_ENABLE_BRIDGE_CONTROL_REGISTER(a,b) \ + PciOperateRegister (a, b, PCI_BRIDGE_CONTROL_REGISTER_OFFSET, EFI_ENABLE_REGISTER, NULL) + +/** + Macro that disalbes PCI bridge control register. + + @param a[in] Pointer to instance of PCI_IO_DEVICE. + @param b[in] The disabled value written into command register. + + @return status of PciIo operation + +**/ +#define PCI_DISABLE_BRIDGE_CONTROL_REGISTER(a,b) \ + PciOperateRegister (a, b, PCI_BRIDGE_CONTROL_REGISTER_OFFSET, EFI_DISABLE_REGISTER, NULL) + +#endif diff --git a/MdeModulePkg/Bus/Pci/PciBusDxe/PciDeviceSupport.c b/MdeModulePkg/Bus/Pci/PciBusDxe/PciDeviceSupport.c new file mode 100644 index 0000000000..25387bd257 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/PciBusDxe/PciDeviceSupport.c @@ -0,0 +1,1185 @@ +/** @file + Supporting functions implementaion for PCI devices management. + +Copyright (c) 2006 - 2009, Intel Corporation +All rights reserved. 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; + PCI_IO_DEVICE *ParrentPciIoDevice; + EFI_PCI_IO_PROTOCOL *ParrentPciIo; + UINT16 Data16; + UINT32 Data32; + + // + // 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); + + // + // PCI-IOV programming + // + if (((FeaturePcdGet(PcdAriSupport) & EFI_PCI_IOV_POLICY_ARI) != 0) && (PciIoDevice->AriCapabilityOffset != 0) && ((FeaturePcdGet(PcdSrIovSupport) & EFI_PCI_IOV_POLICY_SRIOV) != 0) && + (PciIoDevice->SrIovCapabilityOffset != 0)) { + // + // Check its parrent ARI forwarding capability + // + ParrentPciIoDevice = PciIoDevice->Parent; + ParrentPciIo = &(ParrentPciIoDevice->PciIo); + ParrentPciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, ParrentPciIoDevice->PciExpressCapabilityOffset + EFI_PCIE_CAPABILITY_DEVICE_CAPABILITIES_2_OFFSET, 1, &Data32); + if (Data32 & EFI_PCIE_CAPABILITY_DEVICE_CAPABILITIES_2_ARI_FORWARDING) { + // + // ARI forward support in bridge, so enable it. + // + ParrentPciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, ParrentPciIoDevice->PciExpressCapabilityOffset + EFI_PCIE_CAPABILITY_DEVICE_CONTROL_2_OFFSET, 1, &Data32); + Data32 |= EFI_PCIE_CAPABILITY_DEVICE_CONTROL_2_ARI_FORWARDING; + ParrentPciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, ParrentPciIoDevice->PciExpressCapabilityOffset + EFI_PCIE_CAPABILITY_DEVICE_CONTROL_2_OFFSET, 1, &Data32); + + // + // Set ARI Capable Hierarchy for device + // + PciIo->Pci.Read (PciIo, EfiPciIoWidthUint16, PciIoDevice->SrIovCapabilityOffset + EFI_PCIE_CAPABILITY_ID_SRIOV_CONTROL, 1, &Data16); + Data16 |= EFI_PCIE_CAPABILITY_ID_SRIOV_CONTROL_ARI_HIERARCHY; + PciIo->Pci.Write (PciIo, EfiPciIoWidthUint16, PciIoDevice->SrIovCapabilityOffset + EFI_PCIE_CAPABILITY_ID_SRIOV_CONTROL, 1, &Data16); + } + } + + // + // 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->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->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 (!IsListEmpty (&PciIoDevice->ChildList)) { + Status = StartPciDevicesOnBridge ( + Controller, + PciIoDevice, + CurrentDevicePath, + NumberOfChildren, + ChildHandleBuffer + ); + + PciIoDevice->PciIo.Attributes ( + &(PciIoDevice->PciIo), + EfiPciIoAttributeOperationSupported, + 0, + &Supports + ); + Supports &= 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 (!IsListEmpty (&PciIoDevice->ChildList)) { + Status = StartPciDevicesOnBridge ( + Controller, + PciIoDevice, + RemainingDevicePath, + NumberOfChildren, + ChildHandleBuffer + ); + + PciIoDevice->PciIo.Attributes ( + &(PciIoDevice->PciIo), + EfiPciIoAttributeOperationSupported, + 0, + &Supports + ); + Supports &= 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; + +} + diff --git a/MdeModulePkg/Bus/Pci/PciBusDxe/PciDeviceSupport.h b/MdeModulePkg/Bus/Pci/PciBusDxe/PciDeviceSupport.h new file mode 100644 index 0000000000..e97e90dea1 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/PciBusDxe/PciDeviceSupport.h @@ -0,0 +1,289 @@ +/** @file + Supporting functions declaration for PCI devices management. + +Copyright (c) 2006 - 2009, Intel Corporation +All rights reserved. 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. + +**/ + +#ifndef _EFI_PCI_DEVICE_SUPPORT_H_ +#define _EFI_PCI_DEVICE_SUPPORT_H_ + +/** + Initialize the PCI devices pool. + +**/ +VOID +InitializePciDevicePool ( + VOID + ); + +/** + Insert a root bridge into PCI device pool. + + @param RootBridge A pointer to the PCI_IO_DEVICE. + +**/ +VOID +InsertRootBridge ( + IN PCI_IO_DEVICE *RootBridge + ); + +/** + 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 + ); + +/** + Destroy root bridge and remove it from deivce tree. + + @param RootBridge The bridge want to be removed. + +**/ +VOID +DestroyRootBridge ( + IN PCI_IO_DEVICE *RootBridge + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +/** + 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 + ); + +#endif diff --git a/MdeModulePkg/Bus/Pci/PciBusDxe/PciDriverOverride.c b/MdeModulePkg/Bus/Pci/PciBusDxe/PciDriverOverride.c new file mode 100644 index 0000000000..119866b3c1 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/PciBusDxe/PciDriverOverride.c @@ -0,0 +1,143 @@ +/** @file + Functions implementation for Bus Specific Driver Override protoocl. + +Copyright (c) 2006 - 2009, Intel Corporation +All rights reserved. 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" + +/** + Initializes a PCI Driver Override Instance. + + @param PciIoDevice PCI Device instance. + +**/ +VOID +InitializePciDriverOverrideInstance ( + IN OUT PCI_IO_DEVICE *PciIoDevice + ) +{ + PciIoDevice->PciDriverOverride.GetDriver = GetDriver; +} + + +/** + Uses a bus specific algorithm to retrieve a driver image handle for a controller. + + @param This A pointer to the EFI_BUS_SPECIFIC_DRIVER_OVERRIDE_PROTOCOL instance. + @param DriverImageHandle On input, a pointer to the previous driver image handle returned + by GetDriver(). On output, a pointer to the next driver + image handle. Passing in a NULL, will return the first driver + image handle. + + @retval EFI_SUCCESS A bus specific override driver is returned in DriverImageHandle. + @retval EFI_NOT_FOUND The end of the list of override drivers was reached. + A bus specific override driver is not returned in DriverImageHandle. + @retval EFI_INVALID_PARAMETER DriverImageHandle is not a handle that was returned on a + previous call to GetDriver(). + +**/ +EFI_STATUS +EFIAPI +GetDriver ( + IN EFI_BUS_SPECIFIC_DRIVER_OVERRIDE_PROTOCOL *This, + IN OUT EFI_HANDLE *DriverImageHandle + ) +{ + PCI_IO_DEVICE *PciIoDevice; + LIST_ENTRY *CurrentLink; + PCI_DRIVER_OVERRIDE_LIST *Node; + + PciIoDevice = PCI_IO_DEVICE_FROM_PCI_DRIVER_OVERRIDE_THIS (This); + + CurrentLink = PciIoDevice->OptionRomDriverList.ForwardLink; + + while (CurrentLink != NULL && CurrentLink != &PciIoDevice->OptionRomDriverList) { + + Node = DRIVER_OVERRIDE_FROM_LINK (CurrentLink); + + if (*DriverImageHandle == NULL) { + + *DriverImageHandle = Node->DriverImageHandle; + return EFI_SUCCESS; + } + + if (*DriverImageHandle == Node->DriverImageHandle) { + + if (CurrentLink->ForwardLink == &PciIoDevice->OptionRomDriverList || + CurrentLink->ForwardLink == NULL) { + return EFI_NOT_FOUND; + } + + // + // Get next node + // + Node = DRIVER_OVERRIDE_FROM_LINK (CurrentLink->ForwardLink); + *DriverImageHandle = Node->DriverImageHandle; + return EFI_SUCCESS; + } + + CurrentLink = CurrentLink->ForwardLink; + } + + return EFI_INVALID_PARAMETER; +} + +/** + Add an overriding driver image. + + @param PciIoDevice Instance of PciIo device. + @param DriverImageHandle new added driver image. + + @retval EFI_SUCCESS Successfully added driver. + @retval EFI_OUT_OF_RESOURCES No memory resource for new driver instance. + @retval other Some error occurred when locating gEfiLoadedImageProtocolGuid. + +**/ +EFI_STATUS +AddDriver ( + IN PCI_IO_DEVICE *PciIoDevice, + IN EFI_HANDLE DriverImageHandle + ) +{ + EFI_STATUS Status; + EFI_LOADED_IMAGE_PROTOCOL *LoadedImage; + PE_COFF_LOADER_IMAGE_CONTEXT ImageContext; + PCI_DRIVER_OVERRIDE_LIST *Node; + + Status = gBS->HandleProtocol (DriverImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **) &LoadedImage); + if (EFI_ERROR (Status)) { + return Status; + } + + Node = AllocatePool (sizeof (PCI_DRIVER_OVERRIDE_LIST)); + if (Node == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Node->Signature = DRIVER_OVERRIDE_SIGNATURE; + Node->DriverImageHandle = DriverImageHandle; + + InsertTailList (&PciIoDevice->OptionRomDriverList, &(Node->Link)); + + PciIoDevice->BusOverride = TRUE; + + ImageContext.Handle = LoadedImage->ImageBase; + ImageContext.ImageRead = PeCoffLoaderImageReadFromMemory; + + // + // Get information about the image + // + PeCoffLoaderGetImageInfo (&ImageContext); + + return EFI_SUCCESS; +} + diff --git a/MdeModulePkg/Bus/Pci/PciBusDxe/PciDriverOverride.h b/MdeModulePkg/Bus/Pci/PciBusDxe/PciDriverOverride.h new file mode 100644 index 0000000000..d28fb7f846 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/PciBusDxe/PciDriverOverride.h @@ -0,0 +1,86 @@ +/** @file + Functions declaration for Bus Specific Driver Override protoocl. + +Copyright (c) 2006 - 2009, Intel Corporation +All rights reserved. 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. + +**/ + + +#ifndef _EFI_PCI_DRIVER_OVERRRIDE_H_ +#define _EFI_PCI_DRIVER_OVERRRIDE_H_ + +#define DRIVER_OVERRIDE_SIGNATURE SIGNATURE_32 ('d', 'r', 'o', 'v') + +// +// PCI driver override driver image list +// +typedef struct { + UINT32 Signature; + LIST_ENTRY Link; + EFI_HANDLE DriverImageHandle; +} PCI_DRIVER_OVERRIDE_LIST; + + +#define DRIVER_OVERRIDE_FROM_LINK(a) \ + CR (a, PCI_DRIVER_OVERRIDE_LIST, Link, DRIVER_OVERRIDE_SIGNATURE) + +/** + Initializes a PCI Driver Override Instance. + + @param PciIoDevice PCI Device instance. + +**/ +VOID +InitializePciDriverOverrideInstance ( + IN OUT PCI_IO_DEVICE *PciIoDevice + ); + +/** + Add an overriding driver image. + + @param PciIoDevice Instance of PciIo device. + @param DriverImageHandle new added driver image. + + @retval EFI_SUCCESS Successfully added driver. + @retval EFI_OUT_OF_RESOURCES No memory resource for new driver instance. + @retval other Some error occurred when locating gEfiLoadedImageProtocolGuid. + +**/ +EFI_STATUS +AddDriver ( + IN PCI_IO_DEVICE *PciIoDevice, + IN EFI_HANDLE DriverImageHandle + ); + + +/** + Uses a bus specific algorithm to retrieve a driver image handle for a controller. + + @param This A pointer to the EFI_BUS_SPECIFIC_DRIVER_OVERRIDE_PROTOCOL instance. + @param DriverImageHandle On input, a pointer to the previous driver image handle returned + by GetDriver(). On output, a pointer to the next driver + image handle. Passing in a NULL, will return the first driver + image handle. + + @retval EFI_SUCCESS A bus specific override driver is returned in DriverImageHandle. + @retval EFI_NOT_FOUND The end of the list of override drivers was reached. + A bus specific override driver is not returned in DriverImageHandle. + @retval EFI_INVALID_PARAMETER DriverImageHandle is not a handle that was returned on a + previous call to GetDriver(). + +**/ +EFI_STATUS +EFIAPI +GetDriver ( + IN EFI_BUS_SPECIFIC_DRIVER_OVERRIDE_PROTOCOL *This, + IN OUT EFI_HANDLE *DriverImageHandle + ); + +#endif diff --git a/MdeModulePkg/Bus/Pci/PciBusDxe/PciEnumerator.c b/MdeModulePkg/Bus/Pci/PciBusDxe/PciEnumerator.c new file mode 100644 index 0000000000..6c1de3f3e6 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/PciBusDxe/PciEnumerator.c @@ -0,0 +1,2171 @@ +/** @file + PCI eunmeration implementation on entire PCI bus system for PCI Bus module. + +Copyright (c) 2006 - 2009, Intel Corporation +All rights reserved. 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 routine is used to enumerate entire pci bus system + in a given platform. + + @param Controller Parent controller handle. + + @retval EFI_SUCCESS PCI enumeration finished successfully. + @retval other Some error occurred when enumerating the pci bus system. + +**/ +EFI_STATUS +PciEnumerator ( + IN EFI_HANDLE Controller + ) +{ + EFI_HANDLE Handle; + EFI_HANDLE HostBridgeHandle; + EFI_STATUS Status; + EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL *PciResAlloc; + EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *PciRootBridgeIo; + + // + // If PCI bus has already done the full enumeration, never do it again + // + if (!gFullEnumeration) { + return PciEnumeratorLight (Controller); + } + + // + // Get the rootbridge Io protocol to find the host bridge handle + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiPciRootBridgeIoProtocolGuid, + (VOID **) &PciRootBridgeIo, + gPciBusDriverBinding.DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Get the host bridge handle + // + HostBridgeHandle = PciRootBridgeIo->ParentHandle; + + // + // Get the pci host bridge resource allocation protocol + // + Status = gBS->OpenProtocol ( + HostBridgeHandle, + &gEfiPciHostBridgeResourceAllocationProtocolGuid, + (VOID **) &PciResAlloc, + gPciBusDriverBinding.DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Notify the pci bus enumeration is about to begin + // + NotifyPhase (PciResAlloc, EfiPciHostBridgeBeginEnumeration); + + // + // Start the bus allocation phase + // + Status = PciHostBridgeEnumerator (PciResAlloc); + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Submit the resource request + // + Status = PciHostBridgeResourceAllocator (PciResAlloc); + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Notify the pci bus enumeration is about to complete + // + NotifyPhase (PciResAlloc, EfiPciHostBridgeEndEnumeration); + + // + // Process P2C + // + Status = PciHostBridgeP2CProcess (PciResAlloc); + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Process attributes for devices on this host bridge + // + Status = PciHostBridgeDeviceAttribute (PciResAlloc); + if (EFI_ERROR (Status)) { + return Status; + } + + gFullEnumeration = FALSE; + + Status = gBS->InstallProtocolInterface ( + &Handle, + &gEfiPciEnumerationCompleteProtocolGuid, + EFI_NATIVE_INTERFACE, + NULL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + return EFI_SUCCESS; +} + +/** + Enumerate PCI root bridge. + + @param PciResAlloc Pointer to protocol instance of EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL. + @param RootBridgeDev Instance of root bridge device. + + @retval EFI_SUCCESS Successfully enumerated root bridge. + @retval other Failed to enumerate root bridge. + +**/ +EFI_STATUS +PciRootBridgeEnumerator ( + IN EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL *PciResAlloc, + IN PCI_IO_DEVICE *RootBridgeDev + ) +{ + EFI_STATUS Status; + EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Configuration; + UINT8 SubBusNumber; + UINT8 StartBusNumber; + UINT8 PaddedBusRange; + EFI_HANDLE RootBridgeHandle; + + SubBusNumber = 0; + StartBusNumber = 0; + PaddedBusRange = 0; + + // + // Get the root bridge handle + // + RootBridgeHandle = RootBridgeDev->Handle; + + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_PROGRESS_CODE, + EFI_IO_BUS_PCI | EFI_IOB_PCI_PC_BUS_ENUM, + RootBridgeDev->DevicePath + ); + + // + // Get the Bus information + // + Status = PciResAlloc->StartBusEnumeration ( + PciResAlloc, + RootBridgeHandle, + (VOID **) &Configuration + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Get the bus number to start with + // + StartBusNumber = (UINT8) (Configuration->AddrRangeMin); + PaddedBusRange = (UINT8) (Configuration->AddrRangeMax); + + // + // Initialize the subordinate bus number + // + SubBusNumber = StartBusNumber; + + // + // Reset all assigned PCI bus number + // + ResetAllPpbBusNumber ( + RootBridgeDev, + StartBusNumber + ); + + // + // Assign bus number + // + Status = PciScanBus ( + RootBridgeDev, + (UINT8) (Configuration->AddrRangeMin), + &SubBusNumber, + &PaddedBusRange + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + + // + // Assign max bus number scanned + // + Configuration->AddrLen = SubBusNumber - StartBusNumber + 1 + PaddedBusRange; + + // + // Set bus number + // + Status = PciResAlloc->SetBusNumbers ( + PciResAlloc, + RootBridgeHandle, + Configuration + ); + + FreePool (Configuration); + + if (EFI_ERROR (Status)) { + return Status; + } + + return EFI_SUCCESS; +} + +/** + This routine is used to process all PCI devices' Option Rom + on a certain root bridge. + + @param Bridge Given parent's root bridge. + @param RomBase Base address of ROM driver loaded from. + @param MaxLength Maximum rom size. + +**/ +VOID +ProcessOptionRom ( + IN PCI_IO_DEVICE *Bridge, + IN UINT64 RomBase, + IN UINT64 MaxLength + ) +{ + LIST_ENTRY *CurrentLink; + PCI_IO_DEVICE *Temp; + + // + // Go through bridges to reach all devices + // + CurrentLink = Bridge->ChildList.ForwardLink; + while (CurrentLink != NULL && CurrentLink != &Bridge->ChildList) { + Temp = PCI_IO_DEVICE_FROM_LINK (CurrentLink); + if (!IsListEmpty (&Temp->ChildList)) { + + // + // Go further to process the option rom under this bridge + // + ProcessOptionRom (Temp, RomBase, MaxLength); + } + + if (Temp->RomSize != 0 && Temp->RomSize <= MaxLength) { + + // + // Load and process the option rom + // + LoadOpRomImage (Temp, RomBase); + } + + CurrentLink = CurrentLink->ForwardLink; + } +} + +/** + This routine is used to assign bus number to the given PCI bus system + + @param Bridge Parent root bridge instance. + @param StartBusNumber Number of beginning. + @param SubBusNumber The number of sub bus. + + @retval EFI_SUCCESS Successfully assigned bus number. + @retval EFI_DEVICE_ERROR Failed to assign bus number. + +**/ +EFI_STATUS +PciAssignBusNumber ( + IN PCI_IO_DEVICE *Bridge, + IN UINT8 StartBusNumber, + OUT UINT8 *SubBusNumber + ) +{ + EFI_STATUS Status; + PCI_TYPE00 Pci; + UINT8 Device; + UINT8 Func; + UINT64 Address; + UINTN SecondBus; + UINT16 Register; + UINT8 Register8; + EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *PciRootBridgeIo; + + PciRootBridgeIo = Bridge->PciRootBridgeIo; + + SecondBus = 0; + Register = 0; + + *SubBusNumber = StartBusNumber; + + // + // First check to see whether the parent is ppb + // + for (Device = 0; Device <= PCI_MAX_DEVICE; Device++) { + for (Func = 0; Func <= PCI_MAX_FUNC; Func++) { + + // + // Check to see whether a pci device is present + // + Status = PciDevicePresent ( + PciRootBridgeIo, + &Pci, + StartBusNumber, + Device, + Func + ); + + if (!EFI_ERROR (Status) && + (IS_PCI_BRIDGE (&Pci) || IS_CARDBUS_BRIDGE (&Pci))) { + + // + // Reserved one bus for cardbus bridge + // + SecondBus = ++(*SubBusNumber); + + Register = (UINT16) ((SecondBus << 8) | (UINT16) StartBusNumber); + + Address = EFI_PCI_ADDRESS (StartBusNumber, Device, Func, 0x18); + + Status = PciRootBridgeIo->Pci.Write ( + PciRootBridgeIo, + EfiPciWidthUint16, + Address, + 1, + &Register + ); + + // + // Initialize SubBusNumber to SecondBus + // + Address = EFI_PCI_ADDRESS (StartBusNumber, Device, Func, 0x1A); + Status = PciRootBridgeIo->Pci.Write ( + PciRootBridgeIo, + EfiPciWidthUint8, + Address, + 1, + SubBusNumber + ); + // + // If it is PPB, resursively search down this bridge + // + if (IS_PCI_BRIDGE (&Pci)) { + + Register8 = 0xFF; + Status = PciRootBridgeIo->Pci.Write ( + PciRootBridgeIo, + EfiPciWidthUint8, + Address, + 1, + &Register8 + ); + + Status = PciAssignBusNumber ( + Bridge, + (UINT8) (SecondBus), + SubBusNumber + ); + + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + } + + // + // Set the current maximum bus number under the PPB + // + Address = EFI_PCI_ADDRESS (StartBusNumber, Device, Func, 0x1A); + + Status = PciRootBridgeIo->Pci.Write ( + PciRootBridgeIo, + EfiPciWidthUint8, + Address, + 1, + SubBusNumber + ); + + } + + if (Func == 0 && !IS_PCI_MULTI_FUNC (&Pci)) { + + // + // Skip sub functions, this is not a multi function device + // + Func = PCI_MAX_FUNC; + } + } + } + + return EFI_SUCCESS; +} + +/** + This routine is used to determine the root bridge attribute by interfacing + the host bridge resource allocation protocol. + + @param PciResAlloc Protocol instance of EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL + @param RootBridgeDev Root bridge instance + + @retval EFI_SUCCESS Successfully got root bridge's attribute. + @retval other Failed to get attribute. + +**/ +EFI_STATUS +DetermineRootBridgeAttributes ( + IN EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL *PciResAlloc, + IN PCI_IO_DEVICE *RootBridgeDev + ) +{ + UINT64 Attributes; + EFI_STATUS Status; + EFI_HANDLE RootBridgeHandle; + + Attributes = 0; + RootBridgeHandle = RootBridgeDev->Handle; + + // + // Get root bridge attribute by calling into pci host bridge resource allocation protocol + // + Status = PciResAlloc->GetAllocAttributes ( + PciResAlloc, + RootBridgeHandle, + &Attributes + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Here is the point where PCI bus driver calls HOST bridge allocation protocol + // Currently we hardcoded for ea815 + // + if ((Attributes & EFI_PCI_HOST_BRIDGE_COMBINE_MEM_PMEM) != 0) { + RootBridgeDev->Decodes |= EFI_BRIDGE_PMEM_MEM_COMBINE_SUPPORTED; + } + + if ((Attributes & EFI_PCI_HOST_BRIDGE_MEM64_DECODE) != 0) { + RootBridgeDev->Decodes |= EFI_BRIDGE_PMEM64_DECODE_SUPPORTED; + } + + RootBridgeDev->Decodes |= EFI_BRIDGE_MEM32_DECODE_SUPPORTED; + RootBridgeDev->Decodes |= EFI_BRIDGE_PMEM32_DECODE_SUPPORTED; + RootBridgeDev->Decodes |= EFI_BRIDGE_IO16_DECODE_SUPPORTED; + + return EFI_SUCCESS; +} + +/** + Get Max Option Rom size on specified bridge. + + @param Bridge Given bridge device instance. + + @return Max size of option rom needed. + +**/ +UINT64 +GetMaxOptionRomSize ( + IN PCI_IO_DEVICE *Bridge + ) +{ + LIST_ENTRY *CurrentLink; + PCI_IO_DEVICE *Temp; + UINT64 MaxOptionRomSize; + UINT64 TempOptionRomSize; + + MaxOptionRomSize = 0; + + // + // Go through bridges to reach all devices + // + CurrentLink = Bridge->ChildList.ForwardLink; + while (CurrentLink != NULL && CurrentLink != &Bridge->ChildList) { + Temp = PCI_IO_DEVICE_FROM_LINK (CurrentLink); + if (!IsListEmpty (&Temp->ChildList)) { + + // + // Get max option rom size under this bridge + // + TempOptionRomSize = GetMaxOptionRomSize (Temp); + + // + // Compare with the option rom size of the bridge + // Get the larger one + // + if (Temp->RomSize > TempOptionRomSize) { + TempOptionRomSize = Temp->RomSize; + } + + } else { + + // + // For devices get the rom size directly + // + TempOptionRomSize = Temp->RomSize; + } + + // + // Get the largest rom size on this bridge + // + if (TempOptionRomSize > MaxOptionRomSize) { + MaxOptionRomSize = TempOptionRomSize; + } + + CurrentLink = CurrentLink->ForwardLink; + } + + return MaxOptionRomSize; +} + +/** + Process attributes of devices on this host bridge + + @param PciResAlloc Protocol instance of EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL. + + @retval EFI_SUCCESS Successfully process attribute. + @retval EFI_NOT_FOUND Can not find the specific root bridge device. + @retval other Failed to determine the root bridge device's attribute. + +**/ +EFI_STATUS +PciHostBridgeDeviceAttribute ( + IN EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL *PciResAlloc + ) +{ + EFI_HANDLE RootBridgeHandle; + PCI_IO_DEVICE *RootBridgeDev; + EFI_STATUS Status; + + RootBridgeHandle = NULL; + + while (PciResAlloc->GetNextRootBridge (PciResAlloc, &RootBridgeHandle) == EFI_SUCCESS) { + + // + // Get RootBridg Device by handle + // + RootBridgeDev = GetRootBridgeByHandle (RootBridgeHandle); + + if (RootBridgeDev == NULL) { + return EFI_NOT_FOUND; + } + + // + // Set the attributes for devcies behind the Root Bridge + // + Status = DetermineDeviceAttribute (RootBridgeDev); + if (EFI_ERROR (Status)) { + return Status; + } + + } + + return EFI_SUCCESS; +} + +/** + Get resource allocation status from the ACPI resource descriptor. + + @param AcpiConfig Point to Acpi configuration table. + @param IoResStatus Return the status of I/O resource. + @param Mem32ResStatus Return the status of 32-bit Memory resource. + @param PMem32ResStatus Return the status of 32-bit Prefetchable Memory resource. + @param Mem64ResStatus Return the status of 64-bit Memory resource. + @param PMem64ResStatus Return the status of 64-bit Prefetchable Memory resource. + +**/ +VOID +GetResourceAllocationStatus ( + VOID *AcpiConfig, + OUT UINT64 *IoResStatus, + OUT UINT64 *Mem32ResStatus, + OUT UINT64 *PMem32ResStatus, + OUT UINT64 *Mem64ResStatus, + OUT UINT64 *PMem64ResStatus + ) +{ + UINT8 *Temp; + UINT64 ResStatus; + EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *ACPIAddressDesc; + + Temp = (UINT8 *) AcpiConfig; + + while (*Temp == ACPI_ADDRESS_SPACE_DESCRIPTOR) { + + ACPIAddressDesc = (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *) Temp; + ResStatus = ACPIAddressDesc->AddrTranslationOffset; + + switch (ACPIAddressDesc->ResType) { + case 0: + if (ACPIAddressDesc->AddrSpaceGranularity == 32) { + if (ACPIAddressDesc->SpecificFlag == 0x06) { + // + // Pmem32 + // + *PMem32ResStatus = ResStatus; + } else { + // + // Mem32 + // + *Mem32ResStatus = ResStatus; + } + } + + if (ACPIAddressDesc->AddrSpaceGranularity == 64) { + if (ACPIAddressDesc->SpecificFlag == 0x06) { + // + // PMem64 + // + *PMem64ResStatus = ResStatus; + } else { + // + // Mem64 + // + *Mem64ResStatus = ResStatus; + } + } + + break; + + case 1: + // + // Io + // + *IoResStatus = ResStatus; + break; + + default: + break; + } + + Temp += sizeof (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR); + } +} + +/** + Remove a PCI device from device pool and mark its bar. + + @param PciDevice Instance of Pci device. + + @retval EFI_SUCCESS Successfully remove the PCI device. + @retval EFI_ABORTED Pci device is a root bridge or a PCI-PCI bridge. + +**/ +EFI_STATUS +RejectPciDevice ( + IN PCI_IO_DEVICE *PciDevice + ) +{ + PCI_IO_DEVICE *Bridge; + PCI_IO_DEVICE *Temp; + LIST_ENTRY *CurrentLink; + + // + // Remove the padding resource from a bridge + // + if ( IS_PCI_BRIDGE(&PciDevice->Pci) && + PciDevice->ResourcePaddingDescriptors != NULL ) { + FreePool (PciDevice->ResourcePaddingDescriptors); + PciDevice->ResourcePaddingDescriptors = NULL; + return EFI_SUCCESS; + } + + // + // Skip RB and PPB + // + if (IS_PCI_BRIDGE (&PciDevice->Pci) || (PciDevice->Parent == NULL)) { + return EFI_ABORTED; + } + + if (IS_CARDBUS_BRIDGE (&PciDevice->Pci)) { + // + // Get the root bridge device + // + Bridge = PciDevice; + while (Bridge->Parent != NULL) { + Bridge = Bridge->Parent; + } + + RemoveAllPciDeviceOnBridge (Bridge->Handle, PciDevice); + + // + // Mark its bar + // + InitializeP2C (PciDevice); + } + + // + // Remove the device + // + Bridge = PciDevice->Parent; + CurrentLink = Bridge->ChildList.ForwardLink; + while (CurrentLink != NULL && CurrentLink != &Bridge->ChildList) { + Temp = PCI_IO_DEVICE_FROM_LINK (CurrentLink); + if (Temp == PciDevice) { + InitializePciDevice (Temp); + RemoveEntryList (CurrentLink); + FreePciDevice (Temp); + return EFI_SUCCESS; + } + + CurrentLink = CurrentLink->ForwardLink; + } + + return EFI_ABORTED; +} + +/** + Determine whethter a PCI device can be rejected. + + @param PciResNode Pointer to Pci resource node instance. + + @retval TRUE The PCI device can be rejected. + @retval TRUE The PCI device cannot be rejected. + +**/ +BOOLEAN +IsRejectiveDevice ( + IN PCI_RESOURCE_NODE *PciResNode + ) +{ + PCI_IO_DEVICE *Temp; + + Temp = PciResNode->PciDev; + + // + // Ensure the device is present + // + if (Temp == NULL) { + return FALSE; + } + + // + // PPB and RB should go ahead + // + if (IS_PCI_BRIDGE (&Temp->Pci) || (Temp->Parent == NULL)) { + return TRUE; + } + + // + // Skip device on Bus0 + // + if ((Temp->Parent != NULL) && (Temp->BusNumber == 0)) { + return FALSE; + } + + // + // Skip VGA + // + if (IS_PCI_VGA (&Temp->Pci)) { + return FALSE; + } + + return TRUE; +} + +/** + Compare two resource nodes and get the larger resource consumer. + + @param PciResNode1 resource node 1 want to be compared + @param PciResNode2 resource node 2 want to be compared + + @return Larger resource node. + +**/ +PCI_RESOURCE_NODE * +GetLargerConsumerDevice ( + IN PCI_RESOURCE_NODE *PciResNode1, + IN PCI_RESOURCE_NODE *PciResNode2 + ) +{ + if (PciResNode2 == NULL) { + return PciResNode1; + } + + if ((IS_PCI_BRIDGE(&(PciResNode2->PciDev->Pci)) || (PciResNode2->PciDev->Parent == NULL)) \ + && (PciResNode2->ResourceUsage != PciResUsagePadding) ) + { + return PciResNode1; + } + + if (PciResNode1 == NULL) { + return PciResNode2; + } + + if ((PciResNode1->Length) > (PciResNode2->Length)) { + return PciResNode1; + } + + return PciResNode2; +} + + +/** + Get the max resource consumer in the host resource pool. + + @param ResPool Pointer to resource pool node. + + @return The max resource consumer in the host resource pool. + +**/ +PCI_RESOURCE_NODE * +GetMaxResourceConsumerDevice ( + IN PCI_RESOURCE_NODE *ResPool + ) +{ + PCI_RESOURCE_NODE *Temp; + LIST_ENTRY *CurrentLink; + PCI_RESOURCE_NODE *PciResNode; + PCI_RESOURCE_NODE *PPBResNode; + + PciResNode = NULL; + + CurrentLink = ResPool->ChildList.ForwardLink; + while (CurrentLink != NULL && CurrentLink != &ResPool->ChildList) { + + Temp = RESOURCE_NODE_FROM_LINK (CurrentLink); + + if (!IsRejectiveDevice (Temp)) { + CurrentLink = CurrentLink->ForwardLink; + continue; + } + + if ((IS_PCI_BRIDGE (&(Temp->PciDev->Pci)) || (Temp->PciDev->Parent == NULL)) \ + && (Temp->ResourceUsage != PciResUsagePadding)) + { + PPBResNode = GetMaxResourceConsumerDevice (Temp); + PciResNode = GetLargerConsumerDevice (PciResNode, PPBResNode); + } else { + PciResNode = GetLargerConsumerDevice (PciResNode, Temp); + } + + CurrentLink = CurrentLink->ForwardLink; + } + + return PciResNode; +} + +/** + Adjust host bridge allocation so as to reduce resource requirement + + @param IoPool Pointer to instance of I/O resource Node. + @param Mem32Pool Pointer to instance of 32-bit memory resource Node. + @param PMem32Pool Pointer to instance of 32-bit Prefetchable memory resource node. + @param Mem64Pool Pointer to instance of 64-bit memory resource node. + @param PMem64Pool Pointer to instance of 64-bit Prefetchable memory resource node. + @param IoResStatus Status of I/O resource Node. + @param Mem32ResStatus Status of 32-bit memory resource Node. + @param PMem32ResStatus Status of 32-bit Prefetchable memory resource node. + @param Mem64ResStatus Status of 64-bit memory resource node. + @param PMem64ResStatus Status of 64-bit Prefetchable memory resource node. + + @retval EFI_SUCCESS Successfully adjusted resoruce on host bridge. + @retval EFI_ABORTED Host bridge hasn't this resource type or no resource be adjusted. + +**/ +EFI_STATUS +PciHostBridgeAdjustAllocation ( + IN PCI_RESOURCE_NODE *IoPool, + IN PCI_RESOURCE_NODE *Mem32Pool, + IN PCI_RESOURCE_NODE *PMem32Pool, + IN PCI_RESOURCE_NODE *Mem64Pool, + IN PCI_RESOURCE_NODE *PMem64Pool, + IN UINT64 IoResStatus, + IN UINT64 Mem32ResStatus, + IN UINT64 PMem32ResStatus, + IN UINT64 Mem64ResStatus, + IN UINT64 PMem64ResStatus + ) +{ + BOOLEAN AllocationAjusted; + PCI_RESOURCE_NODE *PciResNode; + PCI_RESOURCE_NODE *ResPool[5]; + PCI_IO_DEVICE *RemovedPciDev[5]; + UINT64 ResStatus[5]; + UINTN RemovedPciDevNum; + UINTN DevIndex; + UINTN ResType; + EFI_STATUS Status; + EFI_RESOURCE_ALLOC_FAILURE_ERROR_DATA_PAYLOAD AllocFailExtendedData; + + PciResNode = NULL; + ZeroMem (RemovedPciDev, 5 * sizeof (PCI_IO_DEVICE *)); + RemovedPciDevNum = 0; + + ResPool[0] = IoPool; + ResPool[1] = Mem32Pool; + ResPool[2] = PMem32Pool; + ResPool[3] = Mem64Pool; + ResPool[4] = PMem64Pool; + + ResStatus[0] = IoResStatus; + ResStatus[1] = Mem32ResStatus; + ResStatus[2] = PMem32ResStatus; + ResStatus[3] = Mem64ResStatus; + ResStatus[4] = PMem64ResStatus; + + AllocationAjusted = FALSE; + + for (ResType = 0; ResType < 5; ResType++) { + + if (ResStatus[ResType] == EFI_RESOURCE_SATISFIED) { + continue; + } + + if (ResStatus[ResType] == EFI_RESOURCE_NOT_SATISFIED) { + // + // Host bridge hasn't this resource type + // + return EFI_ABORTED; + } + + // + // Hostbridge hasn't enough resource + // + PciResNode = GetMaxResourceConsumerDevice (ResPool[ResType]); + if (PciResNode == NULL) { + continue; + } + + // + // Check if the device has been removed before + // + for (DevIndex = 0; DevIndex < RemovedPciDevNum; DevIndex++) { + if (PciResNode->PciDev == RemovedPciDev[DevIndex]) { + break; + } + } + + if (DevIndex != RemovedPciDevNum) { + continue; + } + + // + // Remove the device if it isn't in the array + // + Status = RejectPciDevice (PciResNode->PciDev); + if (Status == EFI_SUCCESS) { + + // + // Raise the EFI_IOB_EC_RESOURCE_CONFLICT status code + // + // + // Have no way to get ReqRes, AllocRes & Bar here + // + ZeroMem (&AllocFailExtendedData, sizeof (AllocFailExtendedData)); + AllocFailExtendedData.DevicePathSize = sizeof (EFI_DEVICE_PATH_PROTOCOL); + AllocFailExtendedData.DevicePath = (UINT8 *) PciResNode->PciDev->DevicePath; + AllocFailExtendedData.Bar = PciResNode->Bar; + + REPORT_STATUS_CODE_WITH_EXTENDED_DATA ( + EFI_PROGRESS_CODE, + EFI_IO_BUS_PCI | EFI_IOB_EC_RESOURCE_CONFLICT, + (VOID *) &AllocFailExtendedData, + sizeof (AllocFailExtendedData) + ); + + // + // Add it to the array and indicate at least a device has been rejected + // + RemovedPciDev[RemovedPciDevNum++] = PciResNode->PciDev; + AllocationAjusted = TRUE; + } + } + // + // End for + // + + if (AllocationAjusted) { + return EFI_SUCCESS; + } else { + return EFI_ABORTED; + } +} + +/** + Summary requests for all resource type, and contruct ACPI resource + requestor instance. + + @param Bridge detecting bridge + @param IoNode Pointer to instance of I/O resource Node + @param Mem32Node Pointer to instance of 32-bit memory resource Node + @param PMem32Node Pointer to instance of 32-bit Pmemory resource node + @param Mem64Node Pointer to instance of 64-bit memory resource node + @param PMem64Node Pointer to instance of 64-bit Pmemory resource node + @param Config Output buffer holding new constructed APCI resource requestor + + @retval EFI_SUCCESS Successfully constructed ACPI resource. + @retval EFI_OUT_OF_RESOURCES No memory availabe. + +**/ +EFI_STATUS +ConstructAcpiResourceRequestor ( + IN PCI_IO_DEVICE *Bridge, + IN PCI_RESOURCE_NODE *IoNode, + IN PCI_RESOURCE_NODE *Mem32Node, + IN PCI_RESOURCE_NODE *PMem32Node, + IN PCI_RESOURCE_NODE *Mem64Node, + IN PCI_RESOURCE_NODE *PMem64Node, + OUT VOID **Config + ) +{ + UINT8 NumConfig; + UINT8 Aperture; + UINT8 *Configuration; + EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Ptr; + EFI_ACPI_END_TAG_DESCRIPTOR *PtrEnd; + + NumConfig = 0; + Aperture = 0; + + *Config = NULL; + + // + // if there is io request, add to the io aperture + // + if (ResourceRequestExisted (IoNode)) { + NumConfig++; + Aperture |= 0x01; + } + + // + // if there is mem32 request, add to the mem32 aperture + // + if (ResourceRequestExisted (Mem32Node)) { + NumConfig++; + Aperture |= 0x02; + } + + // + // if there is pmem32 request, add to the pmem32 aperture + // + if (ResourceRequestExisted (PMem32Node)) { + NumConfig++; + Aperture |= 0x04; + } + + // + // if there is mem64 request, add to the mem64 aperture + // + if (ResourceRequestExisted (Mem64Node)) { + NumConfig++; + Aperture |= 0x08; + } + + // + // if there is pmem64 request, add to the pmem64 aperture + // + if (ResourceRequestExisted (PMem64Node)) { + NumConfig++; + Aperture |= 0x10; + } + + if (NumConfig != 0) { + + // + // If there is at least one type of resource request, + // allocate a acpi resource node + // + Configuration = AllocateZeroPool (sizeof (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR) * NumConfig + sizeof (EFI_ACPI_END_TAG_DESCRIPTOR)); + if (Configuration == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Ptr = (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *) Configuration; + + // + // Deal with io aperture + // + if ((Aperture & 0x01) != 0) { + Ptr->Desc = ACPI_ADDRESS_SPACE_DESCRIPTOR; + Ptr->Len = sizeof (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR) - 3; + // + // Io + // + Ptr->ResType = ACPI_ADDRESS_SPACE_TYPE_IO; + // + // non ISA range + // + Ptr->SpecificFlag = 1; + Ptr->AddrLen = IoNode->Length; + Ptr->AddrRangeMax = IoNode->Alignment; + + Ptr++; + } + // + // Deal with mem32 aperture + // + if ((Aperture & 0x02) != 0) { + Ptr->Desc = ACPI_ADDRESS_SPACE_DESCRIPTOR; + Ptr->Len = sizeof (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR) - 3; + // + // Mem + // + Ptr->ResType = ACPI_ADDRESS_SPACE_TYPE_MEM; + // + // Nonprefechable + // + Ptr->SpecificFlag = 0; + // + // 32 bit + // + Ptr->AddrSpaceGranularity = 32; + Ptr->AddrLen = Mem32Node->Length; + Ptr->AddrRangeMax = Mem32Node->Alignment; + + Ptr++; + } + + // + // Deal with Pmem32 aperture + // + if ((Aperture & 0x04) != 0) { + Ptr->Desc = ACPI_ADDRESS_SPACE_DESCRIPTOR; + Ptr->Len = sizeof (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR) - 3; + // + // Mem + // + Ptr->ResType = ACPI_ADDRESS_SPACE_TYPE_MEM; + // + // prefechable + // + Ptr->SpecificFlag = 0x6; + // + // 32 bit + // + Ptr->AddrSpaceGranularity = 32; + Ptr->AddrLen = PMem32Node->Length; + Ptr->AddrRangeMax = PMem32Node->Alignment; + + Ptr++; + } + // + // Deal with mem64 aperture + // + if ((Aperture & 0x08) != 0) { + Ptr->Desc = ACPI_ADDRESS_SPACE_DESCRIPTOR; + Ptr->Len = sizeof (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR) - 3; + // + // Mem + // + Ptr->ResType = ACPI_ADDRESS_SPACE_TYPE_MEM; + // + // nonprefechable + // + Ptr->SpecificFlag = 0; + // + // 64 bit + // + Ptr->AddrSpaceGranularity = 64; + Ptr->AddrLen = Mem64Node->Length; + Ptr->AddrRangeMax = Mem64Node->Alignment; + + Ptr++; + } + // + // Deal with Pmem64 aperture + // + if ((Aperture & 0x10) != 0) { + Ptr->Desc = ACPI_ADDRESS_SPACE_DESCRIPTOR; + Ptr->Len = sizeof (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR) - 3; + // + // Mem + // + Ptr->ResType = ACPI_ADDRESS_SPACE_TYPE_MEM; + // + // prefechable + // + Ptr->SpecificFlag = 0x06; + // + // 64 bit + // + Ptr->AddrSpaceGranularity = 64; + Ptr->AddrLen = PMem64Node->Length; + Ptr->AddrRangeMax = PMem64Node->Alignment; + + Ptr++; + } + + // + // put the checksum + // + PtrEnd = (EFI_ACPI_END_TAG_DESCRIPTOR *) Ptr; + + PtrEnd->Desc = ACPI_END_TAG_DESCRIPTOR; + PtrEnd->Checksum = 0; + + } else { + + // + // If there is no resource request + // + Configuration = AllocateZeroPool (sizeof (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR) + sizeof (EFI_ACPI_END_TAG_DESCRIPTOR)); + if (Configuration == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Ptr = (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *) (Configuration); + Ptr->Desc = ACPI_ADDRESS_SPACE_DESCRIPTOR; + + PtrEnd = (EFI_ACPI_END_TAG_DESCRIPTOR *) (Ptr + 1); + PtrEnd->Desc = ACPI_END_TAG_DESCRIPTOR; + PtrEnd->Checksum = 0; + } + + *Config = Configuration; + + return EFI_SUCCESS; +} + +/** + Get resource base from an acpi configuration descriptor. + + @param Config An acpi configuration descriptor. + @param IoBase Output of I/O resource base address. + @param Mem32Base Output of 32-bit memory base address. + @param PMem32Base Output of 32-bit prefetchable memory base address. + @param Mem64Base Output of 64-bit memory base address. + @param PMem64Base Output of 64-bit prefetchable memory base address. + +**/ +VOID +GetResourceBase ( + IN VOID *Config, + OUT UINT64 *IoBase, + OUT UINT64 *Mem32Base, + OUT UINT64 *PMem32Base, + OUT UINT64 *Mem64Base, + OUT UINT64 *PMem64Base + ) +{ + UINT8 *Temp; + EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Ptr; + UINT64 ResStatus; + + ASSERT (Config != NULL); + + *IoBase = 0xFFFFFFFFFFFFFFFFULL; + *Mem32Base = 0xFFFFFFFFFFFFFFFFULL; + *PMem32Base = 0xFFFFFFFFFFFFFFFFULL; + *Mem64Base = 0xFFFFFFFFFFFFFFFFULL; + *PMem64Base = 0xFFFFFFFFFFFFFFFFULL; + + Temp = (UINT8 *) Config; + + while (*Temp == ACPI_ADDRESS_SPACE_DESCRIPTOR) { + + Ptr = (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *) Temp; + ResStatus = Ptr->AddrTranslationOffset; + + if (ResStatus == EFI_RESOURCE_SATISFIED) { + + switch (Ptr->ResType) { + + // + // Memory type aperture + // + case 0: + + // + // Check to see the granularity + // + if (Ptr->AddrSpaceGranularity == 32) { + if ((Ptr->SpecificFlag & 0x06) != 0) { + *PMem32Base = Ptr->AddrRangeMin; + } else { + *Mem32Base = Ptr->AddrRangeMin; + } + } + + if (Ptr->AddrSpaceGranularity == 64) { + if ((Ptr->SpecificFlag & 0x06) != 0) { + *PMem64Base = Ptr->AddrRangeMin; + } else { + *Mem64Base = Ptr->AddrRangeMin; + } + } + break; + + case 1: + + // + // Io type aperture + // + *IoBase = Ptr->AddrRangeMin; + break; + + default: + break; + + } + // + // End switch + // + } + // + // End for + // + Temp += sizeof (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR); + } +} + +/** + Enumerate pci bridge, allocate resource and determine attribute + for devices on this bridge. + + @param BridgeDev Pointer to instance of bridge device. + + @retval EFI_SUCCESS Successfully enumerated PCI bridge. + @retval other Failed to enumerate. + +**/ +EFI_STATUS +PciBridgeEnumerator ( + IN PCI_IO_DEVICE *BridgeDev + ) +{ + UINT8 SubBusNumber; + UINT8 StartBusNumber; + EFI_PCI_IO_PROTOCOL *PciIo; + EFI_STATUS Status; + + SubBusNumber = 0; + StartBusNumber = 0; + PciIo = &(BridgeDev->PciIo); + Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint8, 0x19, 1, &StartBusNumber); + + if (EFI_ERROR (Status)) { + return Status; + } + + Status = PciAssignBusNumber ( + BridgeDev, + StartBusNumber, + &SubBusNumber + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + Status = PciPciDeviceInfoCollector (BridgeDev, StartBusNumber); + + if (EFI_ERROR (Status)) { + return Status; + } + + Status = PciBridgeResourceAllocator (BridgeDev); + + if (EFI_ERROR (Status)) { + return Status; + } + + Status = DetermineDeviceAttribute (BridgeDev); + + if (EFI_ERROR (Status)) { + return Status; + } + + return EFI_SUCCESS; + +} + +/** + Allocate all kinds of resource for PCI bridge. + + @param Bridge Pointer to bridge instance. + + @retval EFI_SUCCESS Successfully allocated resource for PCI bridge. + @retval other Failed to allocate resource for bridge. + +**/ +EFI_STATUS +PciBridgeResourceAllocator ( + IN PCI_IO_DEVICE *Bridge + ) +{ + PCI_RESOURCE_NODE *IoBridge; + PCI_RESOURCE_NODE *Mem32Bridge; + PCI_RESOURCE_NODE *PMem32Bridge; + PCI_RESOURCE_NODE *Mem64Bridge; + PCI_RESOURCE_NODE *PMem64Bridge; + UINT64 IoBase; + UINT64 Mem32Base; + UINT64 PMem32Base; + UINT64 Mem64Base; + UINT64 PMem64Base; + EFI_STATUS Status; + + IoBridge = CreateResourceNode ( + Bridge, + 0, + 0xFFF, + 0, + PciBarTypeIo16, + PciResUsageTypical + ); + + Mem32Bridge = CreateResourceNode ( + Bridge, + 0, + 0xFFFFF, + 0, + PciBarTypeMem32, + PciResUsageTypical + ); + + PMem32Bridge = CreateResourceNode ( + Bridge, + 0, + 0xFFFFF, + 0, + PciBarTypePMem32, + PciResUsageTypical + ); + + Mem64Bridge = CreateResourceNode ( + Bridge, + 0, + 0xFFFFF, + 0, + PciBarTypeMem64, + PciResUsageTypical + ); + + PMem64Bridge = CreateResourceNode ( + Bridge, + 0, + 0xFFFFF, + 0, + PciBarTypePMem64, + PciResUsageTypical + ); + + // + // Create resourcemap by going through all the devices subject to this root bridge + // + CreateResourceMap ( + Bridge, + IoBridge, + Mem32Bridge, + PMem32Bridge, + Mem64Bridge, + PMem64Bridge + ); + + Status = GetResourceBaseFromBridge ( + Bridge, + &IoBase, + &Mem32Base, + &PMem32Base, + &Mem64Base, + &PMem64Base + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Program IO resources + // + ProgramResource ( + IoBase, + IoBridge + ); + + // + // Program Mem32 resources + // + ProgramResource ( + Mem32Base, + Mem32Bridge + ); + + // + // Program PMem32 resources + // + ProgramResource ( + PMem32Base, + PMem32Bridge + ); + + // + // Program Mem64 resources + // + ProgramResource ( + Mem64Base, + Mem64Bridge + ); + + // + // Program PMem64 resources + // + ProgramResource ( + PMem64Base, + PMem64Bridge + ); + + DestroyResourceTree (IoBridge); + DestroyResourceTree (Mem32Bridge); + DestroyResourceTree (PMem32Bridge); + DestroyResourceTree (PMem64Bridge); + DestroyResourceTree (Mem64Bridge); + + gBS->FreePool (IoBridge); + gBS->FreePool (Mem32Bridge); + gBS->FreePool (PMem32Bridge); + gBS->FreePool (PMem64Bridge); + gBS->FreePool (Mem64Bridge); + + return EFI_SUCCESS; +} + +/** + Get resource base address for a pci bridge device. + + @param Bridge Given Pci driver instance. + @param IoBase Output for base address of I/O type resource. + @param Mem32Base Output for base address of 32-bit memory type resource. + @param PMem32Base Ooutput for base address of 32-bit Pmemory type resource. + @param Mem64Base Output for base address of 64-bit memory type resource. + @param PMem64Base Output for base address of 64-bit Pmemory type resource. + + @retval EFI_SUCCESS Successfully got resource base address. + @retval EFI_OUT_OF_RESOURCES PCI bridge is not available. + +**/ +EFI_STATUS +GetResourceBaseFromBridge ( + IN PCI_IO_DEVICE *Bridge, + OUT UINT64 *IoBase, + OUT UINT64 *Mem32Base, + OUT UINT64 *PMem32Base, + OUT UINT64 *Mem64Base, + OUT UINT64 *PMem64Base + ) +{ + if (!Bridge->Allocated) { + return EFI_OUT_OF_RESOURCES; + } + + *IoBase = gAllOne; + *Mem32Base = gAllOne; + *PMem32Base = gAllOne; + *Mem64Base = gAllOne; + *PMem64Base = gAllOne; + + if (IS_PCI_BRIDGE (&Bridge->Pci)) { + + if (Bridge->PciBar[PPB_IO_RANGE].Length > 0) { + *IoBase = Bridge->PciBar[PPB_IO_RANGE].BaseAddress; + } + + if (Bridge->PciBar[PPB_MEM32_RANGE].Length > 0) { + *Mem32Base = Bridge->PciBar[PPB_MEM32_RANGE].BaseAddress; + } + + if (Bridge->PciBar[PPB_PMEM32_RANGE].Length > 0) { + *PMem32Base = Bridge->PciBar[PPB_PMEM32_RANGE].BaseAddress; + } + + if (Bridge->PciBar[PPB_PMEM64_RANGE].Length > 0) { + *PMem64Base = Bridge->PciBar[PPB_PMEM64_RANGE].BaseAddress; + } else { + *PMem64Base = gAllOne; + } + + } + + if (IS_CARDBUS_BRIDGE (&Bridge->Pci)) { + if (Bridge->PciBar[P2C_IO_1].Length > 0) { + *IoBase = Bridge->PciBar[P2C_IO_1].BaseAddress; + } else { + if (Bridge->PciBar[P2C_IO_2].Length > 0) { + *IoBase = Bridge->PciBar[P2C_IO_2].BaseAddress; + } + } + + if (Bridge->PciBar[P2C_MEM_1].Length > 0) { + if (Bridge->PciBar[P2C_MEM_1].BarType == PciBarTypePMem32) { + *PMem32Base = Bridge->PciBar[P2C_MEM_1].BaseAddress; + } + + if (Bridge->PciBar[P2C_MEM_1].BarType == PciBarTypeMem32) { + *Mem32Base = Bridge->PciBar[P2C_MEM_1].BaseAddress; + } + } + + if (Bridge->PciBar[P2C_MEM_2].Length > 0) { + if (Bridge->PciBar[P2C_MEM_2].BarType == PciBarTypePMem32) { + *PMem32Base = Bridge->PciBar[P2C_MEM_2].BaseAddress; + } + + if (Bridge->PciBar[P2C_MEM_2].BarType == PciBarTypeMem32) { + *Mem32Base = Bridge->PciBar[P2C_MEM_2].BaseAddress; + } + } + } + + return EFI_SUCCESS; +} + +/** + These are the notifications from the PCI bus driver that it is about to enter a certain + phase of the PCI enumeration process. + + This member function can be used to notify the host bridge driver to perform specific actions, + including any chipset-specific initialization, so that the chipset is ready to enter the next phase. + Eight notification points are defined at this time. See belows: + EfiPciHostBridgeBeginEnumeration Resets the host bridge PCI apertures and internal data + structures. The PCI enumerator should issue this notification + before starting a fresh enumeration process. Enumeration cannot + be restarted after sending any other notification such as + EfiPciHostBridgeBeginBusAllocation. + EfiPciHostBridgeBeginBusAllocation The bus allocation phase is about to begin. No specific action is + required here. This notification can be used to perform any + chipset-specific programming. + EfiPciHostBridgeEndBusAllocation The bus allocation and bus programming phase is complete. No + specific action is required here. This notification can be used to + perform any chipset-specific programming. + EfiPciHostBridgeBeginResourceAllocation + The resource allocation phase is about to begin. No specific + action is required here. This notification can be used to perform + any chipset-specific programming. + EfiPciHostBridgeAllocateResources Allocates resources per previously submitted requests for all the PCI + root bridges. These resource settings are returned on the next call to + GetProposedResources(). Before calling NotifyPhase() with a Phase of + EfiPciHostBridgeAllocateResource, the PCI bus enumerator is responsible + for gathering I/O and memory requests for + all the PCI root bridges and submitting these requests using + SubmitResources(). This function pads the resource amount + to suit the root bridge hardware, takes care of dependencies between + the PCI root bridges, and calls the Global Coherency Domain (GCD) + with the allocation request. In the case of padding, the allocated range + could be bigger than what was requested. + EfiPciHostBridgeSetResources Programs the host bridge hardware to decode previously allocated + resources (proposed resources) for all the PCI root bridges. After the + hardware is programmed, reassigning resources will not be supported. + The bus settings are not affected. + EfiPciHostBridgeFreeResources Deallocates resources that were previously allocated for all the PCI + root bridges and resets the I/O and memory apertures to their initial + state. The bus settings are not affected. If the request to allocate + resources fails, the PCI enumerator can use this notification to + deallocate previous resources, adjust the requests, and retry + allocation. + EfiPciHostBridgeEndResourceAllocation The resource allocation phase is completed. No specific action is + required here. This notification can be used to perform any chipsetspecific + programming. + + @param[in] PciResAlloc The instance pointer of EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL + @param[in] Phase The phase during enumeration + + @retval EFI_NOT_READY This phase cannot be entered at this time. For example, this error + is valid for a Phase of EfiPciHostBridgeAllocateResources if + SubmitResources() has not been called for one or more + PCI root bridges before this call + @retval EFI_DEVICE_ERROR Programming failed due to a hardware error. This error is valid + for a Phase of EfiPciHostBridgeSetResources. + @retval EFI_INVALID_PARAMETER Invalid phase parameter + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + This error is valid for a Phase of EfiPciHostBridgeAllocateResources if the + previously submitted resource requests cannot be fulfilled or + were only partially fulfilled. + @retval EFI_SUCCESS The notification was accepted without any errors. + +**/ +EFI_STATUS +NotifyPhase ( + IN EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL *PciResAlloc, + EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PHASE Phase + ) +{ + EFI_HANDLE HostBridgeHandle; + EFI_HANDLE RootBridgeHandle; + EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *PciRootBridgeIo; + EFI_STATUS Status; + + HostBridgeHandle = NULL; + RootBridgeHandle = NULL; + if (gPciPlatformProtocol != NULL) { + // + // Get Host Bridge Handle. + // + PciResAlloc->GetNextRootBridge (PciResAlloc, &RootBridgeHandle); + + // + // Get the rootbridge Io protocol to find the host bridge handle + // + Status = gBS->HandleProtocol ( + RootBridgeHandle, + &gEfiPciRootBridgeIoProtocolGuid, + (VOID **) &PciRootBridgeIo + ); + + if (EFI_ERROR (Status)) { + return EFI_NOT_FOUND; + } + + HostBridgeHandle = PciRootBridgeIo->ParentHandle; + + // + // Call PlatformPci::PlatformNotify() if the protocol is present. + // + gPciPlatformProtocol->PlatformNotify ( + gPciPlatformProtocol, + HostBridgeHandle, + Phase, + ChipsetEntry + ); + } else if (gPciOverrideProtocol != NULL){ + // + // Get Host Bridge Handle. + // + PciResAlloc->GetNextRootBridge (PciResAlloc, &RootBridgeHandle); + + // + // Get the rootbridge Io protocol to find the host bridge handle + // + Status = gBS->HandleProtocol ( + RootBridgeHandle, + &gEfiPciRootBridgeIoProtocolGuid, + (VOID **) &PciRootBridgeIo + ); + + if (EFI_ERROR (Status)) { + return EFI_NOT_FOUND; + } + + HostBridgeHandle = PciRootBridgeIo->ParentHandle; + + // + // Call PlatformPci::PhaseNotify() if the protocol is present. + // + gPciOverrideProtocol->PlatformNotify ( + gPciOverrideProtocol, + HostBridgeHandle, + Phase, + ChipsetEntry + ); + } + + Status = PciResAlloc->NotifyPhase ( + PciResAlloc, + Phase + ); + + if (gPciPlatformProtocol != NULL) { + // + // Call PlatformPci::PlatformNotify() if the protocol is present. + // + gPciPlatformProtocol->PlatformNotify ( + gPciPlatformProtocol, + HostBridgeHandle, + Phase, + ChipsetExit + ); + + } else if (gPciOverrideProtocol != NULL) { + // + // Call PlatformPci::PhaseNotify() if the protocol is present. + // + gPciOverrideProtocol->PlatformNotify ( + gPciOverrideProtocol, + HostBridgeHandle, + Phase, + ChipsetExit + ); + } + + return EFI_SUCCESS; +} + +/** + Provides the hooks from the PCI bus driver to every PCI controller (device/function) at various + stages of the PCI enumeration process that allow the host bridge driver to preinitialize individual + PCI controllers before enumeration. + + This function is called during the PCI enumeration process. No specific action is expected from this + member function. It allows the host bridge driver to preinitialize individual PCI controllers before + enumeration. + + @param Bridge Pointer to the EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL instance. + @param Bus The bus number of the pci device. + @param Device The device number of the pci device. + @param Func The function number of the pci device. + @param Phase The phase of the PCI device enumeration. + + @retval EFI_SUCCESS The requested parameters were returned. + @retval EFI_INVALID_PARAMETER RootBridgeHandle is not a valid root bridge handle. + @retval EFI_INVALID_PARAMETER Phase is not a valid phase that is defined in + EFI_PCI_CONTROLLER_RESOURCE_ALLOCATION_PHASE. + @retval EFI_DEVICE_ERROR Programming failed due to a hardware error. The PCI enumerator should + not enumerate this device, including its child devices if it is a PCI-to-PCI + bridge. + +**/ +EFI_STATUS +PreprocessController ( + IN PCI_IO_DEVICE *Bridge, + IN UINT8 Bus, + IN UINT8 Device, + IN UINT8 Func, + IN EFI_PCI_CONTROLLER_RESOURCE_ALLOCATION_PHASE Phase + ) +{ + EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_PCI_ADDRESS RootBridgePciAddress; + EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL *PciResAlloc; + EFI_HANDLE RootBridgeHandle; + EFI_HANDLE HostBridgeHandle; + EFI_STATUS Status; + + // + // Get the host bridge handle + // + HostBridgeHandle = Bridge->PciRootBridgeIo->ParentHandle; + + // + // Get the pci host bridge resource allocation protocol + // + Status = gBS->OpenProtocol ( + HostBridgeHandle, + &gEfiPciHostBridgeResourceAllocationProtocolGuid, + (VOID **) &PciResAlloc, + NULL, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + // + // Get Root Brige Handle + // + while (Bridge->Parent != NULL) { + Bridge = Bridge->Parent; + } + + RootBridgeHandle = Bridge->Handle; + + RootBridgePciAddress.Register = 0; + RootBridgePciAddress.Function = Func; + RootBridgePciAddress.Device = Device; + RootBridgePciAddress.Bus = Bus; + RootBridgePciAddress.ExtendedRegister = 0; + + if (gPciPlatformProtocol != NULL) { + // + // Call PlatformPci::PrepController() if the protocol is present. + // + gPciPlatformProtocol->PlatformPrepController ( + gPciPlatformProtocol, + HostBridgeHandle, + RootBridgeHandle, + RootBridgePciAddress, + Phase, + ChipsetEntry + ); + } else if (gPciOverrideProtocol != NULL) { + // + // Call PlatformPci::PrepController() if the protocol is present. + // + gPciOverrideProtocol->PlatformPrepController ( + gPciOverrideProtocol, + HostBridgeHandle, + RootBridgeHandle, + RootBridgePciAddress, + Phase, + ChipsetEntry + ); + } + + Status = PciResAlloc->PreprocessController ( + PciResAlloc, + RootBridgeHandle, + RootBridgePciAddress, + Phase + ); + + if (gPciPlatformProtocol != NULL) { + // + // Call PlatformPci::PrepController() if the protocol is present. + // + gPciPlatformProtocol->PlatformPrepController ( + gPciPlatformProtocol, + HostBridgeHandle, + RootBridgeHandle, + RootBridgePciAddress, + Phase, + ChipsetExit + ); + } else if (gPciOverrideProtocol != NULL) { + // + // Call PlatformPci::PrepController() if the protocol is present. + // + gPciOverrideProtocol->PlatformPrepController ( + gPciOverrideProtocol, + HostBridgeHandle, + RootBridgeHandle, + RootBridgePciAddress, + Phase, + ChipsetExit + ); + } + + return EFI_SUCCESS; +} + +/** + This function allows the PCI bus driver to be notified to act as requested when a hot-plug event has + happened on the hot-plug controller. Currently, the operations include add operation and remove operation.. + + @param This A pointer to the hot plug request protocol. + @param Operation The operation the PCI bus driver is requested to make. + @param Controller The handle of the hot-plug controller. + @param RemainingDevicePath The remaining device path for the PCI-like hot-plug device. + @param NumberOfChildren The number of child handles. + For a add operation, it is an output parameter. + For a remove operation, it's an input parameter. + @param ChildHandleBuffer The buffer which contains the child handles. + + @retval EFI_INVALID_PARAMETER Operation is not a legal value. + Controller is NULL or not a valid handle. + NumberOfChildren is NULL. + ChildHandleBuffer is NULL while Operation is add. + @retval EFI_OUT_OF_RESOURCES There are no enough resources to start the devices. + @retval EFI_NOT_FOUND Can not find bridge according to controller handle. + @retval EFI_SUCCESS The handles for the specified device have been created or destroyed + as requested, and for an add operation, the new handles are + returned in ChildHandleBuffer. +**/ +EFI_STATUS +EFIAPI +PciHotPlugRequestNotify ( + IN EFI_PCI_HOTPLUG_REQUEST_PROTOCOL * This, + IN EFI_PCI_HOTPLUG_OPERATION Operation, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL * RemainingDevicePath OPTIONAL, + IN OUT UINT8 *NumberOfChildren, + IN OUT EFI_HANDLE * ChildHandleBuffer + ) +{ + PCI_IO_DEVICE *Bridge; + PCI_IO_DEVICE *Temp; + EFI_PCI_IO_PROTOCOL *PciIo; + UINTN Index; + EFI_HANDLE RootBridgeHandle; + EFI_STATUS Status; + + // + // Check input parameter validity + // + if ((Controller == NULL) || (NumberOfChildren == NULL)){ + return EFI_INVALID_PARAMETER; + } + + if ((Operation != EfiPciHotPlugRequestAdd) && (Operation != EfiPciHotplugRequestRemove)) { + return EFI_INVALID_PARAMETER; + } + + if (Operation == EfiPciHotPlugRequestAdd){ + if (ChildHandleBuffer == NULL) { + return EFI_INVALID_PARAMETER; + } + } else if ((Operation == EfiPciHotplugRequestRemove) && (*NumberOfChildren != 0)) { + if (ChildHandleBuffer == NULL) { + return EFI_INVALID_PARAMETER; + } + } + + Status = gBS->OpenProtocol ( + Controller, + &gEfiPciIoProtocolGuid, + (VOID **) &PciIo, + gPciBusDriverBinding.DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + return EFI_NOT_FOUND; + } + + Bridge = PCI_IO_DEVICE_FROM_PCI_IO_THIS (PciIo); + + // + // Get root bridge handle + // + Temp = Bridge; + while (Temp->Parent != NULL) { + Temp = Temp->Parent; + } + + RootBridgeHandle = Temp->Handle; + + if (Operation == EfiPciHotPlugRequestAdd) { + + if (NumberOfChildren != NULL) { + *NumberOfChildren = 0; + } + + if (IsListEmpty (&Bridge->ChildList)) { + + Status = PciBridgeEnumerator (Bridge); + + if (EFI_ERROR (Status)) { + return Status; + } + } + + Status = StartPciDevicesOnBridge ( + RootBridgeHandle, + Bridge, + RemainingDevicePath, + NumberOfChildren, + ChildHandleBuffer + ); + + return Status; + } + + if (Operation == EfiPciHotplugRequestRemove) { + + if (*NumberOfChildren == 0) { + // + // Remove all devices on the bridge + // + RemoveAllPciDeviceOnBridge (RootBridgeHandle, Bridge); + return EFI_SUCCESS; + + } + + for (Index = 0; Index < *NumberOfChildren; Index++) { + // + // De register all the pci device + // + Status = DeRegisterPciDevice (RootBridgeHandle, ChildHandleBuffer[Index]); + + if (EFI_ERROR (Status)) { + return Status; + } + + } + // + // End for + // + return EFI_SUCCESS; + } + + return EFI_SUCCESS; +} + +/** + Search hostbridge according to given handle + + @param RootBridgeHandle Host bridge handle. + + @retval TRUE Found host bridge handle. + @retval FALSE Not found hot bridge handle. + +**/ +BOOLEAN +SearchHostBridgeHandle ( + IN EFI_HANDLE RootBridgeHandle + ) +{ + EFI_HANDLE HostBridgeHandle; + EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *PciRootBridgeIo; + UINTN Index; + EFI_STATUS Status; + + // + // Get the rootbridge Io protocol to find the host bridge handle + // + Status = gBS->OpenProtocol ( + RootBridgeHandle, + &gEfiPciRootBridgeIoProtocolGuid, + (VOID **) &PciRootBridgeIo, + gPciBusDriverBinding.DriverBindingHandle, + RootBridgeHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + return FALSE; + } + + HostBridgeHandle = PciRootBridgeIo->ParentHandle; + for (Index = 0; Index < gPciHostBridgeNumber; Index++) { + if (HostBridgeHandle == gPciHostBrigeHandles[Index]) { + return TRUE; + } + } + + return FALSE; +} + +/** + Add host bridge handle to global variable for enumerating. + + @param HostBridgeHandle Host bridge handle. + + @retval EFI_SUCCESS Successfully added host bridge. + @retval EFI_ABORTED Host bridge is NULL, or given host bridge + has been in host bridge list. + +**/ +EFI_STATUS +AddHostBridgeEnumerator ( + IN EFI_HANDLE HostBridgeHandle + ) +{ + UINTN Index; + + if (HostBridgeHandle == NULL) { + return EFI_ABORTED; + } + + for (Index = 0; Index < gPciHostBridgeNumber; Index++) { + if (HostBridgeHandle == gPciHostBrigeHandles[Index]) { + return EFI_ABORTED; + } + } + + if (Index < PCI_MAX_HOST_BRIDGE_NUM) { + gPciHostBrigeHandles[Index] = HostBridgeHandle; + gPciHostBridgeNumber++; + } + + return EFI_SUCCESS; +} + diff --git a/MdeModulePkg/Bus/Pci/PciBusDxe/PciEnumerator.h b/MdeModulePkg/Bus/Pci/PciBusDxe/PciEnumerator.h new file mode 100644 index 0000000000..3c0ca2fbfd --- /dev/null +++ b/MdeModulePkg/Bus/Pci/PciBusDxe/PciEnumerator.h @@ -0,0 +1,519 @@ +/** @file + PCI bus enumeration logic function declaration for PCI bus module. + +Copyright (c) 2006 - 2009, Intel Corporation +All rights reserved. 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. + +**/ + +#ifndef _EFI_PCI_ENUMERATOR_H_ +#define _EFI_PCI_ENUMERATOR_H_ + +#include "PciResourceSupport.h" + +/** + This routine is used to enumerate entire pci bus system + in a given platform. + + @param Controller Parent controller handle. + + @retval EFI_SUCCESS PCI enumeration finished successfully. + @retval other Some error occurred when enumerating the pci bus system. + +**/ +EFI_STATUS +PciEnumerator ( + IN EFI_HANDLE Controller + ); + +/** + Enumerate PCI root bridge. + + @param PciResAlloc Pointer to protocol instance of EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL. + @param RootBridgeDev Instance of root bridge device. + + @retval EFI_SUCCESS Successfully enumerated root bridge. + @retval other Failed to enumerate root bridge. + +**/ +EFI_STATUS +PciRootBridgeEnumerator ( + IN EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL *PciResAlloc, + IN PCI_IO_DEVICE *RootBridgeDev + ); + +/** + This routine is used to process all PCI devices' Option Rom + on a certain root bridge. + + @param Bridge Given parent's root bridge. + @param RomBase Base address of ROM driver loaded from. + @param MaxLength Maximum rom size. + +**/ +VOID +ProcessOptionRom ( + IN PCI_IO_DEVICE *Bridge, + IN UINT64 RomBase, + IN UINT64 MaxLength + ); + +/** + This routine is used to assign bus number to the given PCI bus system + + @param Bridge Parent root bridge instance. + @param StartBusNumber Number of beginning. + @param SubBusNumber The number of sub bus. + + @retval EFI_SUCCESS Successfully assigned bus number. + @retval EFI_DEVICE_ERROR Failed to assign bus number. + +**/ +EFI_STATUS +PciAssignBusNumber ( + IN PCI_IO_DEVICE *Bridge, + IN UINT8 StartBusNumber, + OUT UINT8 *SubBusNumber + ); + +/** + This routine is used to determine the root bridge attribute by interfacing + the host bridge resource allocation protocol. + + @param PciResAlloc Protocol instance of EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL + @param RootBridgeDev Root bridge instance + + @retval EFI_SUCCESS Successfully got root bridge's attribute. + @retval other Failed to get attribute. + +**/ +EFI_STATUS +DetermineRootBridgeAttributes ( + IN EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL *PciResAlloc, + IN PCI_IO_DEVICE *RootBridgeDev + ); + +/** + Get Max Option Rom size on specified bridge. + + @param Bridge Given bridge device instance. + + @return Max size of option rom needed. + +**/ +UINT64 +GetMaxOptionRomSize ( + IN PCI_IO_DEVICE *Bridge + ); + +/** + Process attributes of devices on this host bridge + + @param PciResAlloc Protocol instance of EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL. + + @retval EFI_SUCCESS Successfully process attribute. + @retval EFI_NOT_FOUND Can not find the specific root bridge device. + @retval other Failed to determine the root bridge device's attribute. + +**/ +EFI_STATUS +PciHostBridgeDeviceAttribute ( + IN EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL *PciResAlloc + ); + +/** + Get resource allocation status from the ACPI resource descriptor. + + @param AcpiConfig Point to Acpi configuration table. + @param IoResStatus Return the status of I/O resource. + @param Mem32ResStatus Return the status of 32-bit Memory resource. + @param PMem32ResStatus Return the status of 32-bit Prefetchable Memory resource. + @param Mem64ResStatus Return the status of 64-bit Memory resource. + @param PMem64ResStatus Return the status of 64-bit Prefetchable Memory resource. + +**/ +VOID +GetResourceAllocationStatus ( + VOID *AcpiConfig, + OUT UINT64 *IoResStatus, + OUT UINT64 *Mem32ResStatus, + OUT UINT64 *PMem32ResStatus, + OUT UINT64 *Mem64ResStatus, + OUT UINT64 *PMem64ResStatus + ); + +/** + Remove a PCI device from device pool and mark its bar. + + @param PciDevice Instance of Pci device. + + @retval EFI_SUCCESS Successfully remove the PCI device. + @retval EFI_ABORTED Pci device is a root bridge or a PCI-PCI bridge. + +**/ +EFI_STATUS +RejectPciDevice ( + IN PCI_IO_DEVICE *PciDevice + ); + +/** + Determine whethter a PCI device can be rejected. + + @param PciResNode Pointer to Pci resource node instance. + + @retval TRUE The PCI device can be rejected. + @retval TRUE The PCI device cannot be rejected. + +**/ +BOOLEAN +IsRejectiveDevice ( + IN PCI_RESOURCE_NODE *PciResNode + ); + +/** + Compare two resource nodes and get the larger resource consumer. + + @param PciResNode1 resource node 1 want to be compared + @param PciResNode2 resource node 2 want to be compared + + @return Larger resource node. + +**/ +PCI_RESOURCE_NODE * +GetLargerConsumerDevice ( + IN PCI_RESOURCE_NODE *PciResNode1, + IN PCI_RESOURCE_NODE *PciResNode2 + ); + +/** + Get the max resource consumer in the host resource pool. + + @param ResPool Pointer to resource pool node. + + @return The max resource consumer in the host resource pool. + +**/ +PCI_RESOURCE_NODE * +GetMaxResourceConsumerDevice ( + IN PCI_RESOURCE_NODE *ResPool + ); + +/** + Adjust host bridge allocation so as to reduce resource requirement + + @param IoPool Pointer to instance of I/O resource Node. + @param Mem32Pool Pointer to instance of 32-bit memory resource Node. + @param PMem32Pool Pointer to instance of 32-bit Prefetchable memory resource node. + @param Mem64Pool Pointer to instance of 64-bit memory resource node. + @param PMem64Pool Pointer to instance of 64-bit Prefetchable memory resource node. + @param IoResStatus Status of I/O resource Node. + @param Mem32ResStatus Status of 32-bit memory resource Node. + @param PMem32ResStatus Status of 32-bit Prefetchable memory resource node. + @param Mem64ResStatus Status of 64-bit memory resource node. + @param PMem64ResStatus Status of 64-bit Prefetchable memory resource node. + + @retval EFI_SUCCESS Successfully adjusted resoruce on host bridge. + @retval EFI_ABORTED Host bridge hasn't this resource type or no resource be adjusted. + +**/ +EFI_STATUS +PciHostBridgeAdjustAllocation ( + IN PCI_RESOURCE_NODE *IoPool, + IN PCI_RESOURCE_NODE *Mem32Pool, + IN PCI_RESOURCE_NODE *PMem32Pool, + IN PCI_RESOURCE_NODE *Mem64Pool, + IN PCI_RESOURCE_NODE *PMem64Pool, + IN UINT64 IoResStatus, + IN UINT64 Mem32ResStatus, + IN UINT64 PMem32ResStatus, + IN UINT64 Mem64ResStatus, + IN UINT64 PMem64ResStatus + ); + +/** + Summary requests for all resource type, and contruct ACPI resource + requestor instance. + + @param Bridge detecting bridge + @param IoNode Pointer to instance of I/O resource Node + @param Mem32Node Pointer to instance of 32-bit memory resource Node + @param PMem32Node Pointer to instance of 32-bit Pmemory resource node + @param Mem64Node Pointer to instance of 64-bit memory resource node + @param PMem64Node Pointer to instance of 64-bit Pmemory resource node + @param Config Output buffer holding new constructed APCI resource requestor + + @retval EFI_SUCCESS Successfully constructed ACPI resource. + @retval EFI_OUT_OF_RESOURCES No memory availabe. + +**/ +EFI_STATUS +ConstructAcpiResourceRequestor ( + IN PCI_IO_DEVICE *Bridge, + IN PCI_RESOURCE_NODE *IoNode, + IN PCI_RESOURCE_NODE *Mem32Node, + IN PCI_RESOURCE_NODE *PMem32Node, + IN PCI_RESOURCE_NODE *Mem64Node, + IN PCI_RESOURCE_NODE *PMem64Node, + OUT VOID **Config + ); + +/** + Get resource base from an acpi configuration descriptor. + + @param Config An acpi configuration descriptor. + @param IoBase Output of I/O resource base address. + @param Mem32Base Output of 32-bit memory base address. + @param PMem32Base Output of 32-bit prefetchable memory base address. + @param Mem64Base Output of 64-bit memory base address. + @param PMem64Base Output of 64-bit prefetchable memory base address. + +**/ +VOID +GetResourceBase ( + IN VOID *Config, + OUT UINT64 *IoBase, + OUT UINT64 *Mem32Base, + OUT UINT64 *PMem32Base, + OUT UINT64 *Mem64Base, + OUT UINT64 *PMem64Base + ); + +/** + Enumerate pci bridge, allocate resource and determine attribute + for devices on this bridge. + + @param BridgeDev Pointer to instance of bridge device. + + @retval EFI_SUCCESS Successfully enumerated PCI bridge. + @retval other Failed to enumerate. + +**/ +EFI_STATUS +PciBridgeEnumerator ( + IN PCI_IO_DEVICE *BridgeDev + ); + +/** + Allocate all kinds of resource for PCI bridge. + + @param Bridge Pointer to bridge instance. + + @retval EFI_SUCCESS Successfully allocated resource for PCI bridge. + @retval other Failed to allocate resource for bridge. + +**/ +EFI_STATUS +PciBridgeResourceAllocator ( + IN PCI_IO_DEVICE *Bridge + ); + +/** + Get resource base address for a pci bridge device. + + @param Bridge Given Pci driver instance. + @param IoBase Output for base address of I/O type resource. + @param Mem32Base Output for base address of 32-bit memory type resource. + @param PMem32Base Ooutput for base address of 32-bit Pmemory type resource. + @param Mem64Base Output for base address of 64-bit memory type resource. + @param PMem64Base Output for base address of 64-bit Pmemory type resource. + + @retval EFI_SUCCESS Successfully got resource base address. + @retval EFI_OUT_OF_RESOURCES PCI bridge is not available. + +**/ +EFI_STATUS +GetResourceBaseFromBridge ( + IN PCI_IO_DEVICE *Bridge, + OUT UINT64 *IoBase, + OUT UINT64 *Mem32Base, + OUT UINT64 *PMem32Base, + OUT UINT64 *Mem64Base, + OUT UINT64 *PMem64Base + ); + +/** + Process Option Rom on this host bridge + + @param PciResAlloc Pointer to instance of EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL. + + @retval EFI_NOT_FOUND Can not find the root bridge instance. + @retval EFI_SUCCESS Success process. +**/ +EFI_STATUS +PciHostBridgeP2CProcess ( + IN EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL *PciResAlloc + ); + +/** + These are the notifications from the PCI bus driver that it is about to enter a certain + phase of the PCI enumeration process. + + This member function can be used to notify the host bridge driver to perform specific actions, + including any chipset-specific initialization, so that the chipset is ready to enter the next phase. + Eight notification points are defined at this time. See belows: + EfiPciHostBridgeBeginEnumeration Resets the host bridge PCI apertures and internal data + structures. The PCI enumerator should issue this notification + before starting a fresh enumeration process. Enumeration cannot + be restarted after sending any other notification such as + EfiPciHostBridgeBeginBusAllocation. + EfiPciHostBridgeBeginBusAllocation The bus allocation phase is about to begin. No specific action is + required here. This notification can be used to perform any + chipset-specific programming. + EfiPciHostBridgeEndBusAllocation The bus allocation and bus programming phase is complete. No + specific action is required here. This notification can be used to + perform any chipset-specific programming. + EfiPciHostBridgeBeginResourceAllocation + The resource allocation phase is about to begin. No specific + action is required here. This notification can be used to perform + any chipset-specific programming. + EfiPciHostBridgeAllocateResources Allocates resources per previously submitted requests for all the PCI + root bridges. These resource settings are returned on the next call to + GetProposedResources(). Before calling NotifyPhase() with a Phase of + EfiPciHostBridgeAllocateResource, the PCI bus enumerator is responsible + for gathering I/O and memory requests for + all the PCI root bridges and submitting these requests using + SubmitResources(). This function pads the resource amount + to suit the root bridge hardware, takes care of dependencies between + the PCI root bridges, and calls the Global Coherency Domain (GCD) + with the allocation request. In the case of padding, the allocated range + could be bigger than what was requested. + EfiPciHostBridgeSetResources Programs the host bridge hardware to decode previously allocated + resources (proposed resources) for all the PCI root bridges. After the + hardware is programmed, reassigning resources will not be supported. + The bus settings are not affected. + EfiPciHostBridgeFreeResources Deallocates resources that were previously allocated for all the PCI + root bridges and resets the I/O and memory apertures to their initial + state. The bus settings are not affected. If the request to allocate + resources fails, the PCI enumerator can use this notification to + deallocate previous resources, adjust the requests, and retry + allocation. + EfiPciHostBridgeEndResourceAllocation The resource allocation phase is completed. No specific action is + required here. This notification can be used to perform any chipsetspecific + programming. + + @param[in] PciResAlloc The instance pointer of EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL + @param[in] Phase The phase during enumeration + + @retval EFI_NOT_READY This phase cannot be entered at this time. For example, this error + is valid for a Phase of EfiPciHostBridgeAllocateResources if + SubmitResources() has not been called for one or more + PCI root bridges before this call + @retval EFI_DEVICE_ERROR Programming failed due to a hardware error. This error is valid + for a Phase of EfiPciHostBridgeSetResources. + @retval EFI_INVALID_PARAMETER Invalid phase parameter + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + This error is valid for a Phase of EfiPciHostBridgeAllocateResources if the + previously submitted resource requests cannot be fulfilled or + were only partially fulfilled. + @retval EFI_SUCCESS The notification was accepted without any errors. + +**/ +EFI_STATUS +NotifyPhase ( + IN EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL *PciResAlloc, + EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PHASE Phase + ); + +/** + Provides the hooks from the PCI bus driver to every PCI controller (device/function) at various + stages of the PCI enumeration process that allow the host bridge driver to preinitialize individual + PCI controllers before enumeration. + + This function is called during the PCI enumeration process. No specific action is expected from this + member function. It allows the host bridge driver to preinitialize individual PCI controllers before + enumeration. + + @param Bridge Pointer to the EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL instance. + @param Bus The bus number of the pci device. + @param Device The device number of the pci device. + @param Func The function number of the pci device. + @param Phase The phase of the PCI device enumeration. + + @retval EFI_SUCCESS The requested parameters were returned. + @retval EFI_INVALID_PARAMETER RootBridgeHandle is not a valid root bridge handle. + @retval EFI_INVALID_PARAMETER Phase is not a valid phase that is defined in + EFI_PCI_CONTROLLER_RESOURCE_ALLOCATION_PHASE. + @retval EFI_DEVICE_ERROR Programming failed due to a hardware error. The PCI enumerator should + not enumerate this device, including its child devices if it is a PCI-to-PCI + bridge. + +**/ +EFI_STATUS +PreprocessController ( + IN PCI_IO_DEVICE *Bridge, + IN UINT8 Bus, + IN UINT8 Device, + IN UINT8 Func, + IN EFI_PCI_CONTROLLER_RESOURCE_ALLOCATION_PHASE Phase + ); + +/** + This function allows the PCI bus driver to be notified to act as requested when a hot-plug event has + happened on the hot-plug controller. Currently, the operations include add operation and remove operation.. + + @param This A pointer to the hot plug request protocol. + @param Operation The operation the PCI bus driver is requested to make. + @param Controller The handle of the hot-plug controller. + @param RemainingDevicePath The remaining device path for the PCI-like hot-plug device. + @param NumberOfChildren The number of child handles. + For a add operation, it is an output parameter. + For a remove operation, it's an input parameter. + @param ChildHandleBuffer The buffer which contains the child handles. + + @retval EFI_INVALID_PARAMETER Operation is not a legal value. + Controller is NULL or not a valid handle. + NumberOfChildren is NULL. + ChildHandleBuffer is NULL while Operation is add. + @retval EFI_OUT_OF_RESOURCES There are no enough resources to start the devices. + @retval EFI_NOT_FOUND Can not find bridge according to controller handle. + @retval EFI_SUCCESS The handles for the specified device have been created or destroyed + as requested, and for an add operation, the new handles are + returned in ChildHandleBuffer. +**/ +EFI_STATUS +EFIAPI +PciHotPlugRequestNotify ( + IN EFI_PCI_HOTPLUG_REQUEST_PROTOCOL * This, + IN EFI_PCI_HOTPLUG_OPERATION Operation, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL * RemainingDevicePath OPTIONAL, + IN OUT UINT8 *NumberOfChildren, + IN OUT EFI_HANDLE * ChildHandleBuffer + ); + +/** + Search hostbridge according to given handle + + @param RootBridgeHandle Host bridge handle. + + @retval TRUE Found host bridge handle. + @retval FALSE Not found hot bridge handle. + +**/ +BOOLEAN +SearchHostBridgeHandle ( + IN EFI_HANDLE RootBridgeHandle + ); + +/** + Add host bridge handle to global variable for enumerating. + + @param HostBridgeHandle Host bridge handle. + + @retval EFI_SUCCESS Successfully added host bridge. + @retval EFI_ABORTED Host bridge is NULL, or given host bridge + has been in host bridge list. + +**/ +EFI_STATUS +AddHostBridgeEnumerator ( + IN EFI_HANDLE HostBridgeHandle + ); + +#endif diff --git a/MdeModulePkg/Bus/Pci/PciBusDxe/PciEnumeratorSupport.c b/MdeModulePkg/Bus/Pci/PciBusDxe/PciEnumeratorSupport.c new file mode 100644 index 0000000000..8f3ed5eb37 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/PciBusDxe/PciEnumeratorSupport.c @@ -0,0 +1,2494 @@ +/** @file + PCI emumeration support functions implementation for PCI Bus module. + +Copyright (c) 2006 - 2009, Intel Corporation +All rights reserved. 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 routine is used to check whether the pci device is present. + + @param PciRootBridgeIo Pointer to instance of EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL. + @param Pci Output buffer for PCI device configuration space. + @param Bus PCI bus NO. + @param Device PCI device NO. + @param Func PCI Func NO. + + @retval EFI_NOT_FOUND PCI device not present. + @retval EFI_SUCCESS PCI device is found. + +**/ +EFI_STATUS +PciDevicePresent ( + IN EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *PciRootBridgeIo, + OUT PCI_TYPE00 *Pci, + IN UINT8 Bus, + IN UINT8 Device, + IN UINT8 Func + ) +{ + UINT64 Address; + EFI_STATUS Status; + + // + // Create PCI address map in terms of Bus, Device and Func + // + Address = EFI_PCI_ADDRESS (Bus, Device, Func, 0); + + // + // Read the Vendor ID register + // + Status = PciRootBridgeIo->Pci.Read ( + PciRootBridgeIo, + EfiPciWidthUint32, + Address, + 1, + Pci + ); + + if (!EFI_ERROR (Status) && (Pci->Hdr).VendorId != 0xffff) { + // + // Read the entire config header for the device + // + Status = PciRootBridgeIo->Pci.Read ( + PciRootBridgeIo, + EfiPciWidthUint32, + Address, + sizeof (PCI_TYPE00) / sizeof (UINT32), + Pci + ); + + return EFI_SUCCESS; + } + + return EFI_NOT_FOUND; +} + +/** + Collect all the resource information under this root bridge. + + A database that records all the information about pci device subject to this + root bridge will then be created. + + @param Bridge Parent bridge instance. + @param StartBusNumber Bus number of begining. + + @retval EFI_SUCCESS PCI device is found. + @retval other Some error occurred when reading PCI bridge information. + +**/ +EFI_STATUS +PciPciDeviceInfoCollector ( + IN PCI_IO_DEVICE *Bridge, + IN UINT8 StartBusNumber + ) +{ + EFI_STATUS Status; + PCI_TYPE00 Pci; + UINT8 Device; + UINT8 Func; + UINT8 SecBus; + PCI_IO_DEVICE *PciIoDevice; + EFI_PCI_IO_PROTOCOL *PciIo; + + Status = EFI_SUCCESS; + SecBus = 0; + + for (Device = 0; Device <= PCI_MAX_DEVICE; Device++) { + + for (Func = 0; Func <= PCI_MAX_FUNC; Func++) { + + // + // Check to see whether PCI device is present + // + Status = PciDevicePresent ( + Bridge->PciRootBridgeIo, + &Pci, + (UINT8) StartBusNumber, + (UINT8) Device, + (UINT8) Func + ); + if (!EFI_ERROR (Status)) { + + // + // Call back to host bridge function + // + PreprocessController (Bridge, (UINT8) StartBusNumber, Device, Func, EfiPciBeforeResourceCollection); + + // + // Collect all the information about the PCI device discovered + // + Status = PciSearchDevice ( + Bridge, + &Pci, + (UINT8) StartBusNumber, + Device, + Func, + &PciIoDevice + ); + + // + // Recursively scan PCI busses on the other side of PCI-PCI bridges + // + // + if (!EFI_ERROR (Status) && (IS_PCI_BRIDGE (&Pci) || IS_CARDBUS_BRIDGE (&Pci))) { + + // + // If it is PPB, we need to get the secondary bus to continue the enumeration + // + PciIo = &(PciIoDevice->PciIo); + + Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint8, PCI_BRIDGE_SECONDARY_BUS_REGISTER_OFFSET, 1, &SecBus); + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Get resource padding for PPB + // + GetResourcePaddingPpb (PciIoDevice); + + // + // Deep enumerate the next level bus + // + Status = PciPciDeviceInfoCollector ( + PciIoDevice, + (UINT8) (SecBus) + ); + + } + + if (Func == 0 && !IS_PCI_MULTI_FUNC (&Pci)) { + + // + // Skip sub functions, this is not a multi function device + // + Func = PCI_MAX_FUNC; + } + } + + } + } + + return EFI_SUCCESS; +} + +/** + Seach required device and create PCI device instance. + + @param Bridge Parent bridge instance. + @param Pci Input PCI device information block. + @param Bus PCI bus NO. + @param Device PCI device NO. + @param Func PCI func NO. + @param PciDevice Output of searched PCI device instance. + + @retval EFI_SUCCESS Successfully created PCI device instance. + @retval EFI_OUT_OF_RESOURCES Cannot get PCI device information. + +**/ +EFI_STATUS +PciSearchDevice ( + IN PCI_IO_DEVICE *Bridge, + IN PCI_TYPE00 *Pci, + IN UINT8 Bus, + IN UINT8 Device, + IN UINT8 Func, + OUT PCI_IO_DEVICE **PciDevice + ) +{ + PCI_IO_DEVICE *PciIoDevice; + + PciIoDevice = NULL; + + if (!IS_PCI_BRIDGE (Pci)) { + + if (IS_CARDBUS_BRIDGE (Pci)) { + PciIoDevice = GatherP2CInfo ( + Bridge, + Pci, + Bus, + Device, + Func + ); + if ((PciIoDevice != NULL) && gFullEnumeration) { + InitializeP2C (PciIoDevice); + } + } else { + + // + // Create private data for Pci Device + // + PciIoDevice = GatherDeviceInfo ( + Bridge, + Pci, + Bus, + Device, + Func + ); + + } + + } else { + + // + // Create private data for PPB + // + PciIoDevice = GatherPpbInfo ( + Bridge, + Pci, + Bus, + Device, + Func + ); + + // + // Special initialization for PPB including making the PPB quiet + // + if ((PciIoDevice != NULL) && gFullEnumeration) { + InitializePpb (PciIoDevice); + } + } + + if (PciIoDevice == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Update the bar information for this PCI device so as to support some specific device + // + UpdatePciInfo (PciIoDevice); + + if (PciIoDevice->DevicePath == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Detect this function has option rom + // + if (gFullEnumeration) { + + if (!IS_CARDBUS_BRIDGE (Pci)) { + + GetOpRomInfo (PciIoDevice); + + } + + ResetPowerManagementFeature (PciIoDevice); + + } + + // + // Insert it into a global tree for future reference + // + InsertPciDevice (Bridge, PciIoDevice); + + // + // Determine PCI device attributes + // + + if (PciDevice != NULL) { + *PciDevice = PciIoDevice; + } + + return EFI_SUCCESS; +} + +/** + Create PCI device instance for PCI device. + + @param Bridge Parent bridge instance. + @param Pci Input PCI device information block. + @param Bus PCI device Bus NO. + @param Device PCI device Device NO. + @param Func PCI device's func NO. + + @return Created PCI device instance. + +**/ +PCI_IO_DEVICE * +GatherDeviceInfo ( + IN PCI_IO_DEVICE *Bridge, + IN PCI_TYPE00 *Pci, + IN UINT8 Bus, + IN UINT8 Device, + IN UINT8 Func + ) +{ + UINTN Offset; + UINTN BarIndex; + PCI_IO_DEVICE *PciIoDevice; + EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *PciRootBridgeIo; + + PciRootBridgeIo = Bridge->PciRootBridgeIo; + PciIoDevice = CreatePciIoDevice ( + PciRootBridgeIo, + Pci, + Bus, + Device, + Func + ); + + if (PciIoDevice == NULL) { + return NULL; + } + + // + // Create a device path for this PCI device and store it into its private data + // + CreatePciDevicePath ( + Bridge->DevicePath, + PciIoDevice + ); + + // + // If it is a full enumeration, disconnect the device in advance + // + if (gFullEnumeration) { + + PCI_DISABLE_COMMAND_REGISTER (PciIoDevice, EFI_PCI_COMMAND_BITS_OWNED); + + } + + // + // Start to parse the bars + // + for (Offset = 0x10, BarIndex = 0; Offset <= 0x24 && BarIndex < PCI_MAX_BAR; BarIndex++) { + Offset = PciParseBar (PciIoDevice, Offset, BarIndex); + } + + // + // Parse the SR-IOV VF bars + // + if ((PciIoDevice->SrIovCapabilityOffset != 0) && ((FeaturePcdGet(PcdSrIovSupport)& EFI_PCI_IOV_POLICY_SRIOV) != 0)) { + for (Offset = PciIoDevice->SrIovCapabilityOffset + EFI_PCIE_CAPABILITY_ID_SRIOV_BAR0, BarIndex = 0; + Offset <= PciIoDevice->SrIovCapabilityOffset + EFI_PCIE_CAPABILITY_ID_SRIOV_BAR5; + BarIndex++) { + + ASSERT (BarIndex < PCI_MAX_BAR); + Offset = PciIovParseVfBar (PciIoDevice, Offset, BarIndex); + } + } + return PciIoDevice; +} + +/** + Create PCI device instance for PCI-PCI bridge. + + @param Bridge Parent bridge instance. + @param Pci Input PCI device information block. + @param Bus PCI device Bus NO. + @param Device PCI device Device NO. + @param Func PCI device's func NO. + + @return Created PCI device instance. + +**/ +PCI_IO_DEVICE * +GatherPpbInfo ( + IN PCI_IO_DEVICE *Bridge, + IN PCI_TYPE00 *Pci, + IN UINT8 Bus, + IN UINT8 Device, + IN UINT8 Func + ) +{ + EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *PciRootBridgeIo; + PCI_IO_DEVICE *PciIoDevice; + EFI_STATUS Status; + UINT8 Value; + EFI_PCI_IO_PROTOCOL *PciIo; + UINT8 Temp; + + PciRootBridgeIo = Bridge->PciRootBridgeIo; + PciIoDevice = CreatePciIoDevice ( + PciRootBridgeIo, + Pci, + Bus, + Device, + Func + ); + + if (PciIoDevice == NULL) { + return NULL; + } + + // + // Create a device path for this PCI device and store it into its private data + // + CreatePciDevicePath ( + Bridge->DevicePath, + PciIoDevice + ); + + if (gFullEnumeration) { + PCI_DISABLE_COMMAND_REGISTER (PciIoDevice, EFI_PCI_COMMAND_BITS_OWNED); + + // + // Initalize the bridge control register + // + PCI_DISABLE_BRIDGE_CONTROL_REGISTER (PciIoDevice, EFI_PCI_BRIDGE_CONTROL_BITS_OWNED); + + } + + // + // PPB can have two BARs + // + if (PciParseBar (PciIoDevice, 0x10, PPB_BAR_0) == 0x14) { + // + // Not 64-bit bar + // + PciParseBar (PciIoDevice, 0x14, PPB_BAR_1); + } + + PciIo = &PciIoDevice->PciIo; + + // + // Test whether it support 32 decode or not + // + PciIo->Pci.Read (PciIo, EfiPciIoWidthUint8, 0x1C, 1, &Temp); + PciIo->Pci.Write (PciIo, EfiPciIoWidthUint8, 0x1C, 1, &gAllOne); + PciIo->Pci.Read (PciIo, EfiPciIoWidthUint8, 0x1C, 1, &Value); + PciIo->Pci.Write (PciIo, EfiPciIoWidthUint8, 0x1C, 1, &Temp); + + if (Value != 0) { + if ((Value & 0x01) != 0) { + PciIoDevice->Decodes |= EFI_BRIDGE_IO32_DECODE_SUPPORTED; + } else { + PciIoDevice->Decodes |= EFI_BRIDGE_IO16_DECODE_SUPPORTED; + } + } + + Status = BarExisted ( + PciIoDevice, + 0x24, + NULL, + NULL + ); + + // + // Test if it supports 64 memory or not + // + if (!EFI_ERROR (Status)) { + + Status = BarExisted ( + PciIoDevice, + 0x28, + NULL, + NULL + ); + + if (!EFI_ERROR (Status)) { + PciIoDevice->Decodes |= EFI_BRIDGE_PMEM32_DECODE_SUPPORTED; + PciIoDevice->Decodes |= EFI_BRIDGE_PMEM64_DECODE_SUPPORTED; + } else { + PciIoDevice->Decodes |= EFI_BRIDGE_PMEM32_DECODE_SUPPORTED; + } + } + + // + // Memory 32 code is required for ppb + // + PciIoDevice->Decodes |= EFI_BRIDGE_MEM32_DECODE_SUPPORTED; + + GetResourcePaddingPpb (PciIoDevice); + + return PciIoDevice; +} + + +/** + Create PCI device instance for PCI Card bridge device. + + @param Bridge Parent bridge instance. + @param Pci Input PCI device information block. + @param Bus PCI device Bus NO. + @param Device PCI device Device NO. + @param Func PCI device's func NO. + + @return Created PCI device instance. + +**/ +PCI_IO_DEVICE * +GatherP2CInfo ( + IN PCI_IO_DEVICE *Bridge, + IN PCI_TYPE00 *Pci, + IN UINT8 Bus, + IN UINT8 Device, + IN UINT8 Func + ) +{ + EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *PciRootBridgeIo; + PCI_IO_DEVICE *PciIoDevice; + + PciRootBridgeIo = Bridge->PciRootBridgeIo; + PciIoDevice = CreatePciIoDevice ( + PciRootBridgeIo, + Pci, + Bus, + Device, + Func + ); + + if (PciIoDevice == NULL) { + return NULL; + } + + // + // Create a device path for this PCI device and store it into its private data + // + CreatePciDevicePath ( + Bridge->DevicePath, + PciIoDevice + ); + + if (gFullEnumeration) { + PCI_DISABLE_COMMAND_REGISTER (PciIoDevice, EFI_PCI_COMMAND_BITS_OWNED); + + // + // Initalize the bridge control register + // + PCI_DISABLE_BRIDGE_CONTROL_REGISTER (PciIoDevice, EFI_PCCARD_BRIDGE_CONTROL_BITS_OWNED); + } + + // + // P2C only has one bar that is in 0x10 + // + PciParseBar (PciIoDevice, 0x10, P2C_BAR_0); + + // + // Read PciBar information from the bar register + // + GetBackPcCardBar (PciIoDevice); + PciIoDevice->Decodes = EFI_BRIDGE_MEM32_DECODE_SUPPORTED | + EFI_BRIDGE_PMEM32_DECODE_SUPPORTED | + EFI_BRIDGE_IO32_DECODE_SUPPORTED; + + return PciIoDevice; +} + +/** + Create device path for pci deivce. + + @param ParentDevicePath Parent bridge's path. + @param PciIoDevice Pci device instance. + + @return Device path protocol instance for specific pci device. + +**/ +EFI_DEVICE_PATH_PROTOCOL * +CreatePciDevicePath ( + IN EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath, + IN PCI_IO_DEVICE *PciIoDevice + ) +{ + + PCI_DEVICE_PATH PciNode; + + // + // Create PCI device path + // + PciNode.Header.Type = HARDWARE_DEVICE_PATH; + PciNode.Header.SubType = HW_PCI_DP; + SetDevicePathNodeLength (&PciNode.Header, sizeof (PciNode)); + + PciNode.Device = PciIoDevice->DeviceNumber; + PciNode.Function = PciIoDevice->FunctionNumber; + PciIoDevice->DevicePath = AppendDevicePathNode (ParentDevicePath, &PciNode.Header); + + return PciIoDevice->DevicePath; +} + +/** + Check whether the PCI IOV VF bar is existed or not. + + @param PciIoDevice A pointer to the PCI_IO_DEVICE. + @param Offset The offset. + @param BarLengthValue The bar length value returned. + @param OriginalBarValue The original bar value returned. + + @retval EFI_NOT_FOUND The bar doesn't exist. + @retval EFI_SUCCESS The bar exist. + +**/ +EFI_STATUS +VfBarExisted ( + IN PCI_IO_DEVICE *PciIoDevice, + IN UINTN Offset, + OUT UINT32 *BarLengthValue, + OUT UINT32 *OriginalBarValue + ) +{ + EFI_PCI_IO_PROTOCOL *PciIo; + UINT32 OriginalValue; + UINT32 Value; + EFI_TPL OldTpl; + + // + // Ensure it is called properly + // + ASSERT (PciIoDevice->SrIovCapabilityOffset != 0); + if (PciIoDevice->SrIovCapabilityOffset == 0) { + return EFI_NOT_FOUND; + } + + PciIo = &PciIoDevice->PciIo; + + // + // Preserve the original value + // + + PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, (UINT32)Offset, 1, &OriginalValue); + + // + // Raise TPL to high level to disable timer interrupt while the BAR is probed + // + OldTpl = gBS->RaiseTPL (TPL_HIGH_LEVEL); + + PciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, (UINT32)Offset, 1, &gAllOne); + PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, (UINT32)Offset, 1, &Value); + + // + // Write back the original value + // + PciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, (UINT32)Offset, 1, &OriginalValue); + + // + // Restore TPL to its original level + // + gBS->RestoreTPL (OldTpl); + + if (BarLengthValue != NULL) { + *BarLengthValue = Value; + } + + if (OriginalBarValue != NULL) { + *OriginalBarValue = OriginalValue; + } + + if (Value == 0) { + return EFI_NOT_FOUND; + } else { + return EFI_SUCCESS; + } +} + +/** + Check whether the bar is existed or not. + + @param PciIoDevice A pointer to the PCI_IO_DEVICE. + @param Offset The offset. + @param BarLengthValue The bar length value returned. + @param OriginalBarValue The original bar value returned. + + @retval EFI_NOT_FOUND The bar doesn't exist. + @retval EFI_SUCCESS The bar exist. + +**/ +EFI_STATUS +BarExisted ( + IN PCI_IO_DEVICE *PciIoDevice, + IN UINTN Offset, + OUT UINT32 *BarLengthValue, + OUT UINT32 *OriginalBarValue + ) +{ + EFI_PCI_IO_PROTOCOL *PciIo; + UINT32 OriginalValue; + UINT32 Value; + EFI_TPL OldTpl; + + PciIo = &PciIoDevice->PciIo; + + // + // Preserve the original value + // + PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, (UINT8) Offset, 1, &OriginalValue); + + // + // Raise TPL to high level to disable timer interrupt while the BAR is probed + // + OldTpl = gBS->RaiseTPL (TPL_HIGH_LEVEL); + + PciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, (UINT8) Offset, 1, &gAllOne); + PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, (UINT8) Offset, 1, &Value); + + // + // Write back the original value + // + PciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, (UINT8) Offset, 1, &OriginalValue); + + // + // Restore TPL to its original level + // + gBS->RestoreTPL (OldTpl); + + if (BarLengthValue != NULL) { + *BarLengthValue = Value; + } + + if (OriginalBarValue != NULL) { + *OriginalBarValue = OriginalValue; + } + + if (Value == 0) { + return EFI_NOT_FOUND; + } else { + return EFI_SUCCESS; + } +} + +/** + Test whether the device can support given attributes. + + @param PciIoDevice Pci device instance. + @param Command Input command register value, and + returned supported register value. + @param BridgeControl Inout bridge control value for PPB or P2C, and + returned supported bridge control value. + @param OldCommand Returned and stored old command register offset. + @param OldBridgeControl Returned and stored old Bridge control value for PPB or P2C. + +**/ +VOID +PciTestSupportedAttribute ( + IN PCI_IO_DEVICE *PciIoDevice, + IN OUT UINT16 *Command, + IN OUT UINT16 *BridgeControl, + OUT UINT16 *OldCommand, + OUT UINT16 *OldBridgeControl + ) +{ + EFI_TPL OldTpl; + + // + // Preserve the original value + // + PCI_READ_COMMAND_REGISTER (PciIoDevice, OldCommand); + + // + // Raise TPL to high level to disable timer interrupt while the BAR is probed + // + OldTpl = gBS->RaiseTPL (TPL_HIGH_LEVEL); + + PCI_SET_COMMAND_REGISTER (PciIoDevice, *Command); + PCI_READ_COMMAND_REGISTER (PciIoDevice, Command); + + // + // Write back the original value + // + PCI_SET_COMMAND_REGISTER (PciIoDevice, *OldCommand); + + // + // Restore TPL to its original level + // + gBS->RestoreTPL (OldTpl); + + if (IS_PCI_BRIDGE (&PciIoDevice->Pci) || IS_CARDBUS_BRIDGE (&PciIoDevice->Pci)) { + + // + // Preserve the original value + // + PCI_READ_BRIDGE_CONTROL_REGISTER (PciIoDevice, OldBridgeControl); + + // + // Raise TPL to high level to disable timer interrupt while the BAR is probed + // + OldTpl = gBS->RaiseTPL (TPL_HIGH_LEVEL); + + PCI_SET_BRIDGE_CONTROL_REGISTER (PciIoDevice, *BridgeControl); + PCI_READ_BRIDGE_CONTROL_REGISTER (PciIoDevice, BridgeControl); + + // + // Write back the original value + // + PCI_SET_BRIDGE_CONTROL_REGISTER (PciIoDevice, *OldBridgeControl); + + // + // Restore TPL to its original level + // + gBS->RestoreTPL (OldTpl); + + } else { + *OldBridgeControl = 0; + *BridgeControl = 0; + } +} + +/** + Set the supported or current attributes of a PCI device. + + @param PciIoDevice Structure pointer for PCI device. + @param Command Command register value. + @param BridgeControl Bridge control value for PPB or P2C. + @param Option Make a choice of EFI_SET_SUPPORTS or EFI_SET_ATTRIBUTES. + +**/ +VOID +PciSetDeviceAttribute ( + IN PCI_IO_DEVICE *PciIoDevice, + IN UINT16 Command, + IN UINT16 BridgeControl, + IN UINTN Option + ) +{ + UINT64 Attributes; + + Attributes = 0; + + if ((Command & EFI_PCI_COMMAND_IO_SPACE) != 0) { + Attributes |= EFI_PCI_IO_ATTRIBUTE_IO; + } + + if ((Command & EFI_PCI_COMMAND_MEMORY_SPACE) != 0) { + Attributes |= EFI_PCI_IO_ATTRIBUTE_MEMORY; + } + + if ((Command & EFI_PCI_COMMAND_BUS_MASTER) != 0) { + Attributes |= EFI_PCI_IO_ATTRIBUTE_BUS_MASTER; + } + + if ((Command & EFI_PCI_COMMAND_VGA_PALETTE_SNOOP) != 0) { + Attributes |= EFI_PCI_IO_ATTRIBUTE_VGA_PALETTE_IO; + } + + if ((BridgeControl & EFI_PCI_BRIDGE_CONTROL_ISA) != 0) { + Attributes |= EFI_PCI_IO_ATTRIBUTE_ISA_IO; + } + + if ((BridgeControl & EFI_PCI_BRIDGE_CONTROL_VGA) != 0) { + Attributes |= EFI_PCI_IO_ATTRIBUTE_VGA_IO; + Attributes |= EFI_PCI_IO_ATTRIBUTE_VGA_MEMORY; + Attributes |= EFI_PCI_IO_ATTRIBUTE_VGA_PALETTE_IO; + } + + if ((BridgeControl & EFI_PCI_BRIDGE_CONTROL_VGA_16) != 0) { + Attributes |= EFI_PCI_IO_ATTRIBUTE_VGA_IO_16; + Attributes |= EFI_PCI_IO_ATTRIBUTE_VGA_PALETTE_IO_16; + } + + if (Option == EFI_SET_SUPPORTS) { + + Attributes |= EFI_PCI_IO_ATTRIBUTE_MEMORY_WRITE_COMBINE | + EFI_PCI_IO_ATTRIBUTE_MEMORY_CACHED | + EFI_PCI_IO_ATTRIBUTE_MEMORY_DISABLE | + EFI_PCI_IO_ATTRIBUTE_EMBEDDED_DEVICE | + EFI_PCI_IO_ATTRIBUTE_EMBEDDED_ROM | + EFI_PCI_IO_ATTRIBUTE_DUAL_ADDRESS_CYCLE; + + if ((Attributes & EFI_PCI_IO_ATTRIBUTE_IO) != 0) { + Attributes |= EFI_PCI_IO_ATTRIBUTE_ISA_MOTHERBOARD_IO; + Attributes |= EFI_PCI_IO_ATTRIBUTE_ISA_IO; + } + + if (IS_PCI_BRIDGE (&PciIoDevice->Pci) || IS_CARDBUS_BRIDGE (&PciIoDevice->Pci)) { + // + // For bridge, it should support IDE attributes + // + Attributes |= EFI_PCI_IO_ATTRIBUTE_IDE_SECONDARY_IO; + Attributes |= EFI_PCI_IO_ATTRIBUTE_IDE_PRIMARY_IO; + } else { + + if (IS_PCI_IDE (&PciIoDevice->Pci)) { + Attributes |= EFI_PCI_IO_ATTRIBUTE_IDE_SECONDARY_IO; + Attributes |= EFI_PCI_IO_ATTRIBUTE_IDE_PRIMARY_IO; + } + + if (IS_PCI_VGA (&PciIoDevice->Pci)) { + Attributes |= EFI_PCI_IO_ATTRIBUTE_VGA_MEMORY; + Attributes |= EFI_PCI_IO_ATTRIBUTE_VGA_IO; + } + } + + PciIoDevice->Supports = Attributes; + PciIoDevice->Supports &= ( (PciIoDevice->Parent->Supports) | \ + EFI_PCI_IO_ATTRIBUTE_IO | EFI_PCI_IO_ATTRIBUTE_MEMORY | \ + EFI_PCI_IO_ATTRIBUTE_BUS_MASTER ); + + } else { + PciIoDevice->Attributes = Attributes; + } +} + +/** + Determine if the device can support Fast Back to Back attribute. + + @param PciIoDevice Pci device instance. + @param StatusIndex Status register value. + + @retval EFI_SUCCESS This device support Fast Back to Back attribute. + @retval EFI_UNSUPPORTED This device doesn't support Fast Back to Back attribute. + +**/ +EFI_STATUS +GetFastBackToBackSupport ( + IN PCI_IO_DEVICE *PciIoDevice, + IN UINT8 StatusIndex + ) +{ + EFI_PCI_IO_PROTOCOL *PciIo; + EFI_STATUS Status; + UINT32 StatusRegister; + + // + // Read the status register + // + PciIo = &PciIoDevice->PciIo; + Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint16, StatusIndex, 1, &StatusRegister); + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + // + // Check the Fast B2B bit + // + if ((StatusRegister & EFI_PCI_FAST_BACK_TO_BACK_CAPABLE) != 0) { + return EFI_SUCCESS; + } else { + return EFI_UNSUPPORTED; + } +} + +/** + Process the option ROM for all the children of the specified parent PCI device. + It can only be used after the first full Option ROM process. + + @param PciIoDevice Pci device instance. + +**/ +VOID +ProcessOptionRomLight ( + IN PCI_IO_DEVICE *PciIoDevice + ) +{ + PCI_IO_DEVICE *Temp; + LIST_ENTRY *CurrentLink; + + // + // For RootBridge, PPB , P2C, go recursively to traverse all its children + // + CurrentLink = PciIoDevice->ChildList.ForwardLink; + while (CurrentLink != NULL && CurrentLink != &PciIoDevice->ChildList) { + + Temp = PCI_IO_DEVICE_FROM_LINK (CurrentLink); + + if (!IsListEmpty (&Temp->ChildList)) { + ProcessOptionRomLight (Temp); + } + + PciRomGetImageMapping (Temp); + + // + // The OpRom has already been processed in the first round + // + Temp->AllOpRomProcessed = TRUE; + + CurrentLink = CurrentLink->ForwardLink; + } +} + +/** + Determine the related attributes of all devices under a Root Bridge. + + @param PciIoDevice PCI device instance. + +**/ +EFI_STATUS +DetermineDeviceAttribute ( + IN PCI_IO_DEVICE *PciIoDevice + ) +{ + UINT16 Command; + UINT16 BridgeControl; + UINT16 OldCommand; + UINT16 OldBridgeControl; + BOOLEAN FastB2BSupport; + PCI_IO_DEVICE *Temp; + LIST_ENTRY *CurrentLink; + EFI_STATUS Status; + + // + // For Root Bridge, just copy it by RootBridgeIo proctocol + // so as to keep consistent with the actual attribute + // + if (PciIoDevice->Parent == NULL) { + Status = PciIoDevice->PciRootBridgeIo->GetAttributes ( + PciIoDevice->PciRootBridgeIo, + &PciIoDevice->Supports, + &PciIoDevice->Attributes + ); + if (EFI_ERROR (Status)) { + return Status; + } + } else { + + // + // Set the attributes to be checked for common PCI devices and PPB or P2C + // Since some devices only support part of them, it is better to set the + // attribute according to its command or bridge control register + // + Command = EFI_PCI_COMMAND_IO_SPACE | + EFI_PCI_COMMAND_MEMORY_SPACE | + EFI_PCI_COMMAND_BUS_MASTER | + EFI_PCI_COMMAND_VGA_PALETTE_SNOOP; + + BridgeControl = EFI_PCI_BRIDGE_CONTROL_ISA | EFI_PCI_BRIDGE_CONTROL_VGA | EFI_PCI_BRIDGE_CONTROL_VGA_16; + + // + // Test whether the device can support attributes above + // + PciTestSupportedAttribute (PciIoDevice, &Command, &BridgeControl, &OldCommand, &OldBridgeControl); + + // + // Set the supported attributes for specified PCI device + // + PciSetDeviceAttribute (PciIoDevice, Command, BridgeControl, EFI_SET_SUPPORTS); + + // + // Set the current attributes for specified PCI device + // + PciSetDeviceAttribute (PciIoDevice, OldCommand, OldBridgeControl, EFI_SET_ATTRIBUTES); + + // + // Enable other supported attributes but not defined in PCI_IO_PROTOCOL + // + PCI_ENABLE_COMMAND_REGISTER (PciIoDevice, EFI_PCI_COMMAND_MEMORY_WRITE_AND_INVALIDATE); + } + + FastB2BSupport = TRUE; + + // + // P2C can not support FB2B on the secondary side + // + if (IS_CARDBUS_BRIDGE (&PciIoDevice->Pci)) { + FastB2BSupport = FALSE; + } + + // + // For RootBridge, PPB , P2C, go recursively to traverse all its children + // + CurrentLink = PciIoDevice->ChildList.ForwardLink; + while (CurrentLink != NULL && CurrentLink != &PciIoDevice->ChildList) { + + Temp = PCI_IO_DEVICE_FROM_LINK (CurrentLink); + Status = DetermineDeviceAttribute (Temp); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Detect Fast Bact to Bact support for the device under the bridge + // + Status = GetFastBackToBackSupport (Temp, PCI_PRIMARY_STATUS_OFFSET); + if (FastB2BSupport && EFI_ERROR (Status)) { + FastB2BSupport = FALSE; + } + + CurrentLink = CurrentLink->ForwardLink; + } + // + // Set or clear Fast Back to Back bit for the whole bridge + // + if (!IsListEmpty (&PciIoDevice->ChildList)) { + + if (IS_PCI_BRIDGE (&PciIoDevice->Pci)) { + + Status = GetFastBackToBackSupport (PciIoDevice, PCI_BRIDGE_STATUS_REGISTER_OFFSET); + + if (EFI_ERROR (Status) || (!FastB2BSupport)) { + FastB2BSupport = FALSE; + PCI_DISABLE_BRIDGE_CONTROL_REGISTER (PciIoDevice, EFI_PCI_BRIDGE_CONTROL_FAST_BACK_TO_BACK); + } else { + PCI_ENABLE_BRIDGE_CONTROL_REGISTER (PciIoDevice, EFI_PCI_BRIDGE_CONTROL_FAST_BACK_TO_BACK); + } + } + + CurrentLink = PciIoDevice->ChildList.ForwardLink; + while (CurrentLink != NULL && CurrentLink != &PciIoDevice->ChildList) { + Temp = PCI_IO_DEVICE_FROM_LINK (CurrentLink); + if (FastB2BSupport) { + PCI_ENABLE_COMMAND_REGISTER (Temp, EFI_PCI_COMMAND_FAST_BACK_TO_BACK); + } else { + PCI_DISABLE_COMMAND_REGISTER (Temp, EFI_PCI_COMMAND_FAST_BACK_TO_BACK); + } + + CurrentLink = CurrentLink->ForwardLink; + } + } + // + // End for IsListEmpty + // + return EFI_SUCCESS; +} + +/** + This routine is used to update the bar information for those incompatible PCI device. + + @param PciIoDevice Input Pci device instance. Output Pci device instance with updated + Bar information. + + @retval EFI_SUCCESS Successfully updated bar information. + @retval EFI_UNSUPPORTED Given PCI device doesn't belong to incompatible PCI device list. + +**/ +EFI_STATUS +UpdatePciInfo ( + IN OUT PCI_IO_DEVICE *PciIoDevice + ) +{ + EFI_STATUS Status; + UINTN BarIndex; + UINTN BarEndIndex; + BOOLEAN SetFlag; + VOID *Configuration; + EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Ptr; + + Configuration = NULL; + Status = EFI_SUCCESS; + + if (gEfiIncompatiblePciDeviceSupport == NULL) { + // + // It can only be supported after the Incompatible PCI Device + // Support Protocol has been installed + // + Status = gBS->LocateProtocol ( + &gEfiIncompatiblePciDeviceSupportProtocolGuid, + NULL, + (VOID **) &gEfiIncompatiblePciDeviceSupport + ); + } + if (Status == EFI_SUCCESS) { + // + // Check whether the device belongs to incompatible devices from protocol or not + // If it is , then get its special requirement in the ACPI table + // + Status = gEfiIncompatiblePciDeviceSupport->CheckDevice ( + gEfiIncompatiblePciDeviceSupport, + PciIoDevice->Pci.Hdr.VendorId, + PciIoDevice->Pci.Hdr.DeviceId, + PciIoDevice->Pci.Hdr.RevisionID, + PciIoDevice->Pci.Device.SubsystemVendorID, + PciIoDevice->Pci.Device.SubsystemID, + &Configuration + ); + + } + + if (EFI_ERROR (Status) || Configuration == NULL ) { + return EFI_UNSUPPORTED; + } + + // + // Update PCI device information from the ACPI table + // + Ptr = (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *) Configuration; + + while (Ptr->Desc != ACPI_END_TAG_DESCRIPTOR) { + + if (Ptr->Desc != ACPI_ADDRESS_SPACE_DESCRIPTOR) { + // + // The format is not support + // + break; + } + + BarIndex = (UINTN) Ptr->AddrTranslationOffset; + BarEndIndex = BarIndex; + + // + // Update all the bars in the device + // + if (BarIndex == PCI_BAR_ALL) { + BarIndex = 0; + BarEndIndex = PCI_MAX_BAR - 1; + } + + if (BarIndex > PCI_MAX_BAR) { + Ptr++; + continue; + } + + for (; BarIndex <= BarEndIndex; BarIndex++) { + SetFlag = FALSE; + switch (Ptr->ResType) { + case ACPI_ADDRESS_SPACE_TYPE_MEM: + + // + // Make sure the bar is memory type + // + if (CheckBarType (PciIoDevice, (UINT8) BarIndex, PciBarTypeMem)) { + SetFlag = TRUE; + } + break; + + case ACPI_ADDRESS_SPACE_TYPE_IO: + + // + // Make sure the bar is IO type + // + if (CheckBarType (PciIoDevice, (UINT8) BarIndex, PciBarTypeIo)) { + SetFlag = TRUE; + } + break; + } + + if (SetFlag) { + + // + // Update the new alignment for the device + // + SetNewAlign (&(PciIoDevice->PciBar[BarIndex].Alignment), Ptr->AddrRangeMax); + + // + // Update the new length for the device + // + if (Ptr->AddrLen != PCI_BAR_NOCHANGE) { + PciIoDevice->PciBar[BarIndex].Length = Ptr->AddrLen; + } + } + } + + Ptr++; + } + + FreePool (Configuration); + + return EFI_SUCCESS; +} + +/** + This routine will update the alignment with the new alignment. + + @param Alignment Input Old alignment. Output updated alignment. + @param NewAlignment New alignment. + +**/ +VOID +SetNewAlign ( + IN OUT UINT64 *Alignment, + IN UINT64 NewAlignment + ) +{ + UINT64 OldAlignment; + UINTN ShiftBit; + + // + // The new alignment is the same as the original, + // so skip it + // + if (NewAlignment == PCI_BAR_OLD_ALIGN) { + return ; + } + // + // Check the validity of the parameter + // + if (NewAlignment != PCI_BAR_EVEN_ALIGN && + NewAlignment != PCI_BAR_SQUAD_ALIGN && + NewAlignment != PCI_BAR_DQUAD_ALIGN ) { + *Alignment = NewAlignment; + return ; + } + + OldAlignment = (*Alignment) + 1; + ShiftBit = 0; + + // + // Get the first non-zero hex value of the length + // + while ((OldAlignment & 0x0F) == 0x00) { + OldAlignment = RShiftU64 (OldAlignment, 4); + ShiftBit += 4; + } + + // + // Adjust the alignment to even, quad or double quad boundary + // + if (NewAlignment == PCI_BAR_EVEN_ALIGN) { + if ((OldAlignment & 0x01) != 0) { + OldAlignment = OldAlignment + 2 - (OldAlignment & 0x01); + } + } else if (NewAlignment == PCI_BAR_SQUAD_ALIGN) { + if ((OldAlignment & 0x03) != 0) { + OldAlignment = OldAlignment + 4 - (OldAlignment & 0x03); + } + } else if (NewAlignment == PCI_BAR_DQUAD_ALIGN) { + if ((OldAlignment & 0x07) != 0) { + OldAlignment = OldAlignment + 8 - (OldAlignment & 0x07); + } + } + + // + // Update the old value + // + NewAlignment = LShiftU64 (OldAlignment, ShiftBit) - 1; + *Alignment = NewAlignment; + + return ; +} + +/** + Parse PCI IOV VF bar information and fill them into PCI device instance. + + @param PciIoDevice Pci device instance. + @param Offset Bar offset. + @param BarIndex Bar index. + + @return Next bar offset. + +**/ +UINTN +PciIovParseVfBar ( + IN PCI_IO_DEVICE *PciIoDevice, + IN UINTN Offset, + IN UINTN BarIndex + ) +{ + UINT32 Value; + UINT32 OriginalValue; + UINT32 Mask; + UINT32 Data; + UINT8 Index; + EFI_STATUS Status; + + // + // Ensure it is called properly + // + ASSERT (PciIoDevice->SrIovCapabilityOffset != 0); + if (PciIoDevice->SrIovCapabilityOffset == 0) { + return 0; + } + + OriginalValue = 0; + Value = 0; + + Status = VfBarExisted ( + PciIoDevice, + Offset, + &Value, + &OriginalValue + ); + + if (EFI_ERROR (Status)) { + PciIoDevice->VfPciBar[BarIndex].BaseAddress = 0; + PciIoDevice->VfPciBar[BarIndex].Length = 0; + PciIoDevice->VfPciBar[BarIndex].Alignment = 0; + + // + // Scan all the BARs anyway + // + PciIoDevice->VfPciBar[BarIndex].Offset = (UINT8) Offset; + return Offset + 4; + } + + PciIoDevice->VfPciBar[BarIndex].Offset = (UINT8) Offset; + if (Value & 0x01) { + // + // Device I/Os. Impossible + // + ASSERT (FALSE); + return Offset + 4; + + } else { + + Mask = 0xfffffff0; + + PciIoDevice->VfPciBar[BarIndex].BaseAddress = OriginalValue & Mask; + + switch (Value & 0x07) { + + // + //memory space; anywhere in 32 bit address space + // + case 0x00: + if (Value & 0x08) { + PciIoDevice->VfPciBar[BarIndex].BarType = PciBarTypePMem32; + } else { + PciIoDevice->VfPciBar[BarIndex].BarType = PciBarTypeMem32; + } + + PciIoDevice->VfPciBar[BarIndex].Length = (~(Value & Mask)) + 1; + PciIoDevice->VfPciBar[BarIndex].Alignment = PciIoDevice->VfPciBar[BarIndex].Length - 1; + + // + // Adjust Length + // + PciIoDevice->VfPciBar[BarIndex].Length = MultU64x32 (PciIoDevice->VfPciBar[BarIndex].Length, PciIoDevice->InitialVFs); + // + // Adjust Alignment + // + if (PciIoDevice->VfPciBar[BarIndex].Alignment < PciIoDevice->SystemPageSize - 1) { + PciIoDevice->VfPciBar[BarIndex].Alignment = PciIoDevice->SystemPageSize - 1; + } + + break; + + // + // memory space; anywhere in 64 bit address space + // + case 0x04: + if (Value & 0x08) { + PciIoDevice->VfPciBar[BarIndex].BarType = PciBarTypePMem64; + } else { + PciIoDevice->VfPciBar[BarIndex].BarType = PciBarTypeMem64; + } + + // + // According to PCI 2.2,if the bar indicates a memory 64 decoding, next bar + // is regarded as an extension for the first bar. As a result + // the sizing will be conducted on combined 64 bit value + // Here just store the masked first 32bit value for future size + // calculation + // + PciIoDevice->VfPciBar[BarIndex].Length = Value & Mask; + PciIoDevice->VfPciBar[BarIndex].Alignment = PciIoDevice->VfPciBar[BarIndex].Length - 1; + + if (PciIoDevice->VfPciBar[BarIndex].Alignment < PciIoDevice->SystemPageSize - 1) { + PciIoDevice->VfPciBar[BarIndex].Alignment = PciIoDevice->SystemPageSize - 1; + } + + // + // Increment the offset to point to next DWORD + // + Offset += 4; + + Status = VfBarExisted ( + PciIoDevice, + Offset, + &Value, + &OriginalValue + ); + + if (EFI_ERROR (Status)) { + return Offset + 4; + } + + // + // Fix the length to support some spefic 64 bit BAR + // + Data = Value; + Index = 0; + for (Data = Value; Data != 0; Data >>= 1) { + Index ++; + } + Value |= ((UINT32)(-1) << Index); + + // + // Calculate the size of 64bit bar + // + PciIoDevice->VfPciBar[BarIndex].BaseAddress |= LShiftU64 ((UINT64) OriginalValue, 32); + + PciIoDevice->VfPciBar[BarIndex].Length = PciIoDevice->VfPciBar[BarIndex].Length | LShiftU64 ((UINT64) Value, 32); + PciIoDevice->VfPciBar[BarIndex].Length = (~(PciIoDevice->VfPciBar[BarIndex].Length)) + 1; + PciIoDevice->VfPciBar[BarIndex].Alignment = PciIoDevice->VfPciBar[BarIndex].Length - 1; + + // + // Adjust Length + // + PciIoDevice->VfPciBar[BarIndex].Length = MultU64x32 (PciIoDevice->VfPciBar[BarIndex].Length, PciIoDevice->InitialVFs); + // + // Adjust Alignment + // + if (PciIoDevice->VfPciBar[BarIndex].Alignment < PciIoDevice->SystemPageSize - 1) { + PciIoDevice->VfPciBar[BarIndex].Alignment = PciIoDevice->SystemPageSize - 1; + } + + break; + + // + // reserved + // + default: + PciIoDevice->VfPciBar[BarIndex].BarType = PciBarTypeUnknown; + PciIoDevice->VfPciBar[BarIndex].Length = (~(Value & Mask)) + 1; + PciIoDevice->VfPciBar[BarIndex].Alignment = PciIoDevice->VfPciBar[BarIndex].Length - 1; + + if (PciIoDevice->VfPciBar[BarIndex].Alignment < PciIoDevice->SystemPageSize - 1) { + PciIoDevice->VfPciBar[BarIndex].Alignment = PciIoDevice->SystemPageSize - 1; + } + + break; + } + } + + // + // Check the length again so as to keep compatible with some special bars + // + if (PciIoDevice->VfPciBar[BarIndex].Length == 0) { + PciIoDevice->VfPciBar[BarIndex].BarType = PciBarTypeUnknown; + PciIoDevice->VfPciBar[BarIndex].BaseAddress = 0; + PciIoDevice->VfPciBar[BarIndex].Alignment = 0; + } + + // + // Increment number of bar + // + return Offset + 4; +} + +/** + Parse PCI bar information and fill them into PCI device instance. + + @param PciIoDevice Pci device instance. + @param Offset Bar offset. + @param BarIndex Bar index. + + @return Next bar offset. + +**/ +UINTN +PciParseBar ( + IN PCI_IO_DEVICE *PciIoDevice, + IN UINTN Offset, + IN UINTN BarIndex + ) +{ + UINT32 Value; + UINT32 OriginalValue; + UINT32 Mask; + UINT32 Data; + UINT8 Index; + EFI_STATUS Status; + + OriginalValue = 0; + Value = 0; + + Status = BarExisted ( + PciIoDevice, + Offset, + &Value, + &OriginalValue + ); + + if (EFI_ERROR (Status)) { + PciIoDevice->PciBar[BarIndex].BaseAddress = 0; + PciIoDevice->PciBar[BarIndex].Length = 0; + PciIoDevice->PciBar[BarIndex].Alignment = 0; + + // + // Some devices don't fully comply to PCI spec 2.2. So be to scan all the BARs anyway + // + PciIoDevice->PciBar[BarIndex].Offset = (UINT8) Offset; + return Offset + 4; + } + + PciIoDevice->PciBar[BarIndex].Offset = (UINT8) Offset; + if ((Value & 0x01) != 0) { + // + // Device I/Os + // + Mask = 0xfffffffc; + + if ((Value & 0xFFFF0000) != 0) { + // + // It is a IO32 bar + // + PciIoDevice->PciBar[BarIndex].BarType = PciBarTypeIo32; + PciIoDevice->PciBar[BarIndex].Length = ((~(Value & Mask)) + 1); + PciIoDevice->PciBar[BarIndex].Alignment = PciIoDevice->PciBar[BarIndex].Length - 1; + + } else { + // + // It is a IO16 bar + // + PciIoDevice->PciBar[BarIndex].BarType = PciBarTypeIo16; + PciIoDevice->PciBar[BarIndex].Length = 0x0000FFFF & ((~(Value & Mask)) + 1); + PciIoDevice->PciBar[BarIndex].Alignment = PciIoDevice->PciBar[BarIndex].Length - 1; + + } + // + // Workaround. Some platforms inplement IO bar with 0 length + // Need to treat it as no-bar + // + if (PciIoDevice->PciBar[BarIndex].Length == 0) { + PciIoDevice->PciBar[BarIndex].BarType = (PCI_BAR_TYPE) 0; + } + + PciIoDevice->PciBar[BarIndex].Prefetchable = FALSE; + PciIoDevice->PciBar[BarIndex].BaseAddress = OriginalValue & Mask; + + } else { + + Mask = 0xfffffff0; + + PciIoDevice->PciBar[BarIndex].BaseAddress = OriginalValue & Mask; + + switch (Value & 0x07) { + + // + //memory space; anywhere in 32 bit address space + // + case 0x00: + if ((Value & 0x08) != 0) { + PciIoDevice->PciBar[BarIndex].BarType = PciBarTypePMem32; + } else { + PciIoDevice->PciBar[BarIndex].BarType = PciBarTypeMem32; + } + + PciIoDevice->PciBar[BarIndex].Length = (~(Value & Mask)) + 1; + if (PciIoDevice->PciBar[BarIndex].Length < (SIZE_4KB)) { + // + // Force minimum 4KByte alignment for Virtualization technology for Directed I/O + // + PciIoDevice->PciBar[BarIndex].Alignment = (SIZE_4KB - 1); + } else { + PciIoDevice->PciBar[BarIndex].Alignment = PciIoDevice->PciBar[BarIndex].Length - 1; + } + break; + + // + // memory space; anywhere in 64 bit address space + // + case 0x04: + if ((Value & 0x08) != 0) { + PciIoDevice->PciBar[BarIndex].BarType = PciBarTypePMem64; + } else { + PciIoDevice->PciBar[BarIndex].BarType = PciBarTypeMem64; + } + + // + // According to PCI 2.2,if the bar indicates a memory 64 decoding, next bar + // is regarded as an extension for the first bar. As a result + // the sizing will be conducted on combined 64 bit value + // Here just store the masked first 32bit value for future size + // calculation + // + PciIoDevice->PciBar[BarIndex].Length = Value & Mask; + PciIoDevice->PciBar[BarIndex].Alignment = PciIoDevice->PciBar[BarIndex].Length - 1; + + // + // Increment the offset to point to next DWORD + // + Offset += 4; + + Status = BarExisted ( + PciIoDevice, + Offset, + &Value, + &OriginalValue + ); + + if (EFI_ERROR (Status)) { + // + // the high 32 bit does not claim any BAR, we need to re-check the low 32 bit BAR again + // + if (PciIoDevice->PciBar[BarIndex].Length == 0) { + // + // some device implement MMIO bar with 0 length, need to treat it as no-bar + // + PciIoDevice->PciBar[BarIndex].BarType = PciBarTypeUnknown; + } + return Offset + 4; + } + + // + // Fix the length to support some spefic 64 bit BAR + // + Data = Value; + Index = 0; + for (Data = Value; Data != 0; Data >>= 1) { + Index ++; + } + Value |= ((UINT32)(-1) << Index); + + // + // Calculate the size of 64bit bar + // + PciIoDevice->PciBar[BarIndex].BaseAddress |= LShiftU64 ((UINT64) OriginalValue, 32); + + PciIoDevice->PciBar[BarIndex].Length = PciIoDevice->PciBar[BarIndex].Length | LShiftU64 ((UINT64) Value, 32); + PciIoDevice->PciBar[BarIndex].Length = (~(PciIoDevice->PciBar[BarIndex].Length)) + 1; + if (PciIoDevice->PciBar[BarIndex].Length < (SIZE_4KB)) { + // + // Force minimum 4KByte alignment for Virtualization technology for Directed I/O + // + PciIoDevice->PciBar[BarIndex].Alignment = (SIZE_4KB - 1); + } else { + PciIoDevice->PciBar[BarIndex].Alignment = PciIoDevice->PciBar[BarIndex].Length - 1; + } + + break; + + // + // reserved + // + default: + PciIoDevice->PciBar[BarIndex].BarType = PciBarTypeUnknown; + PciIoDevice->PciBar[BarIndex].Length = (~(Value & Mask)) + 1; + if (PciIoDevice->PciBar[BarIndex].Length < (SIZE_4KB)) { + // + // Force minimum 4KByte alignment for Virtualization technology for Directed I/O + // + PciIoDevice->PciBar[BarIndex].Alignment = (SIZE_4KB - 1); + } else { + PciIoDevice->PciBar[BarIndex].Alignment = PciIoDevice->PciBar[BarIndex].Length - 1; + } + break; + } + } + + // + // Check the length again so as to keep compatible with some special bars + // + if (PciIoDevice->PciBar[BarIndex].Length == 0) { + PciIoDevice->PciBar[BarIndex].BarType = PciBarTypeUnknown; + PciIoDevice->PciBar[BarIndex].BaseAddress = 0; + PciIoDevice->PciBar[BarIndex].Alignment = 0; + } + + // + // Increment number of bar + // + return Offset + 4; +} + +/** + This routine is used to initialize the bar of a PCI device. + + @param PciIoDevice Pci device instance. + + @note It can be called typically when a device is going to be rejected. + +**/ +VOID +InitializePciDevice ( + IN PCI_IO_DEVICE *PciIoDevice + ) +{ + EFI_PCI_IO_PROTOCOL *PciIo; + UINT8 Offset; + + PciIo = &(PciIoDevice->PciIo); + + // + // Put all the resource apertures + // Resource base is set to all ones so as to indicate its resource + // has not been alloacted + // + for (Offset = 0x10; Offset <= 0x24; Offset += sizeof (UINT32)) { + PciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, Offset, 1, &gAllOne); + } +} + +/** + This routine is used to initialize the bar of a PCI-PCI Bridge device. + + @param PciIoDevice PCI-PCI bridge device instance. + +**/ +VOID +InitializePpb ( + IN PCI_IO_DEVICE *PciIoDevice + ) +{ + EFI_PCI_IO_PROTOCOL *PciIo; + + PciIo = &(PciIoDevice->PciIo); + + // + // Put all the resource apertures including IO16 + // Io32, pMem32, pMem64 to quiescent state + // Resource base all ones, Resource limit all zeros + // + PciIo->Pci.Write (PciIo, EfiPciIoWidthUint8, 0x1C, 1, &gAllOne); + PciIo->Pci.Write (PciIo, EfiPciIoWidthUint8, 0x1D, 1, &gAllZero); + + PciIo->Pci.Write (PciIo, EfiPciIoWidthUint16, 0x20, 1, &gAllOne); + PciIo->Pci.Write (PciIo, EfiPciIoWidthUint16, 0x22, 1, &gAllZero); + + PciIo->Pci.Write (PciIo, EfiPciIoWidthUint16, 0x24, 1, &gAllOne); + PciIo->Pci.Write (PciIo, EfiPciIoWidthUint16, 0x26, 1, &gAllZero); + + PciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, 0x28, 1, &gAllOne); + PciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, 0x2C, 1, &gAllZero); + + // + // Don't support use io32 as for now + // + PciIo->Pci.Write (PciIo, EfiPciIoWidthUint16, 0x30, 1, &gAllOne); + PciIo->Pci.Write (PciIo, EfiPciIoWidthUint16, 0x32, 1, &gAllZero); + + // + // Force Interrupt line to zero for cards that come up randomly + // + PciIo->Pci.Write (PciIo, EfiPciIoWidthUint8, 0x3C, 1, &gAllZero); +} + +/** + This routine is used to initialize the bar of a PCI Card Bridge device. + + @param PciIoDevice PCI Card bridge device. + +**/ +VOID +InitializeP2C ( + IN PCI_IO_DEVICE *PciIoDevice + ) +{ + EFI_PCI_IO_PROTOCOL *PciIo; + + PciIo = &(PciIoDevice->PciIo); + + // + // Put all the resource apertures including IO16 + // Io32, pMem32, pMem64 to quiescent state( + // Resource base all ones, Resource limit all zeros + // + PciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, 0x1c, 1, &gAllOne); + PciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, 0x20, 1, &gAllZero); + + PciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, 0x24, 1, &gAllOne); + PciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, 0x28, 1, &gAllZero); + + PciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, 0x2c, 1, &gAllOne); + PciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, 0x30, 1, &gAllZero); + + PciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, 0x34, 1, &gAllOne); + PciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, 0x38, 1, &gAllZero); + + // + // Force Interrupt line to zero for cards that come up randomly + // + PciIo->Pci.Write (PciIo, EfiPciIoWidthUint8, 0x3C, 1, &gAllZero); +} + +/** + Create and initiliaze general PCI I/O device instance for + PCI device/bridge device/hotplug bridge device. + + @param PciRootBridgeIo Pointer to instance of EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL. + @param Pci Input Pci information block. + @param Bus Device Bus NO. + @param Device Device device NO. + @param Func Device func NO. + + @return Instance of PCI device. NULL means no instance created. + +**/ +PCI_IO_DEVICE * +CreatePciIoDevice ( + IN EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *PciRootBridgeIo, + IN PCI_TYPE00 *Pci, + IN UINT8 Bus, + IN UINT8 Device, + IN UINT8 Func + ) +{ + PCI_IO_DEVICE *PciIoDevice; + EFI_PCI_IO_PROTOCOL *PciIo; + EFI_STATUS Status; + + PciIoDevice = AllocateZeroPool (sizeof (PCI_IO_DEVICE)); + if (PciIoDevice == NULL) { + return NULL; + } + + PciIoDevice->Signature = PCI_IO_DEVICE_SIGNATURE; + PciIoDevice->Handle = NULL; + PciIoDevice->PciRootBridgeIo = PciRootBridgeIo; + PciIoDevice->DevicePath = NULL; + PciIoDevice->BusNumber = Bus; + PciIoDevice->DeviceNumber = Device; + PciIoDevice->FunctionNumber = Func; + PciIoDevice->Decodes = 0; + + if (gFullEnumeration) { + PciIoDevice->Allocated = FALSE; + } else { + PciIoDevice->Allocated = TRUE; + } + + PciIoDevice->Registered = FALSE; + PciIoDevice->Attributes = 0; + PciIoDevice->Supports = 0; + PciIoDevice->BusOverride = FALSE; + PciIoDevice->AllOpRomProcessed = FALSE; + + PciIoDevice->IsPciExp = FALSE; + + CopyMem (&(PciIoDevice->Pci), Pci, sizeof (PCI_TYPE01)); + + // + // Initialize the PCI I/O instance structure + // + InitializePciIoInstance (PciIoDevice); + InitializePciDriverOverrideInstance (PciIoDevice); + InitializePciLoadFile2 (PciIoDevice); + PciIo = &PciIoDevice->PciIo; + + // + // Detect if PCI Express Device + // + PciIoDevice->PciExpressCapabilityOffset = 0; + Status = LocateCapabilityRegBlock ( + PciIoDevice, + EFI_PCI_CAPABILITY_ID_PCIEXP, + &PciIoDevice->PciExpressCapabilityOffset, + NULL + ); + if (!EFI_ERROR (Status)) { + PciIoDevice->IsPciExp = TRUE; + } + + // + // Initialize for PCI IOV + // + + // + // Check ARI for function 0 only + // + Status = LocatePciExpressCapabilityRegBlock ( + PciIoDevice, + EFI_PCIE_CAPABILITY_ID_ARI, + &PciIoDevice->AriCapabilityOffset, + NULL + ); + if (!EFI_ERROR (Status)) { + DEBUG (( + EFI_D_INFO, + "PCI-IOV B%x.D%x.F%x - ARI Cap offset - 0x%x\n", + (UINTN)Bus, + (UINTN)Device, + (UINTN)Func, + (UINTN)PciIoDevice->AriCapabilityOffset + )); + } + + Status = LocatePciExpressCapabilityRegBlock ( + PciIoDevice, + EFI_PCIE_CAPABILITY_ID_SRIOV, + &PciIoDevice->SrIovCapabilityOffset, + NULL + ); + if (!EFI_ERROR (Status)) { + DEBUG (( + EFI_D_INFO, + "PCI-IOV B%x.D%x.F%x - SRIOV Cap offset - 0x%x\n", + (UINTN)Bus, + (UINTN)Device, + (UINTN)Func, + (UINTN)PciIoDevice->SrIovCapabilityOffset + )); + } + + Status = LocatePciExpressCapabilityRegBlock ( + PciIoDevice, + EFI_PCIE_CAPABILITY_ID_MRIOV, + &PciIoDevice->MrIovCapabilityOffset, + NULL + ); + if (!EFI_ERROR (Status)) { + DEBUG (( + EFI_D_INFO, + "PCI-IOV B%x.D%x.F%x - MRIOV Cap offset - 0x%x\n", + (UINTN)Bus, + (UINTN)Device, + (UINTN)Func, + (UINTN)PciIoDevice->MrIovCapabilityOffset + )); + } + + // + // Calculate SystemPageSize + // + if ((PciIoDevice->SrIovCapabilityOffset != 0) && ((FeaturePcdGet(PcdSrIovSupport)& EFI_PCI_IOV_POLICY_SRIOV) != 0)) { + + PciIo->Pci.Read ( + PciIo, + EfiPciIoWidthUint32, + PciIoDevice->SrIovCapabilityOffset + EFI_PCIE_CAPABILITY_ID_SRIOV_SUPPORTED_PAGE_SIZE, + 1, + &PciIoDevice->SystemPageSize + ); + DEBUG ((EFI_D_INFO, "PCI-IOV B%x.D%x.F%x - SupportedPageSize - 0x%x\n", (UINTN)Bus, (UINTN)Device, (UINTN)Func, PciIoDevice->SystemPageSize)); + + PciIoDevice->SystemPageSize = (PcdGet32(PcdSrIovSystemPageSize) & PciIoDevice->SystemPageSize); + ASSERT (PciIoDevice->SystemPageSize != 0); + + PciIo->Pci.Write ( + PciIo, + EfiPciIoWidthUint32, + PciIoDevice->SrIovCapabilityOffset + EFI_PCIE_CAPABILITY_ID_SRIOV_SYSTEM_PAGE_SIZE, + 1, + &PciIoDevice->SystemPageSize + ); + DEBUG ((EFI_D_INFO, "PCI-IOV B%x.D%x.F%x - SystemPageSize - 0x%x\n", (UINTN)Bus, (UINTN)Device, (UINTN)Func, PciIoDevice->SystemPageSize)); + // + // Adjust SystemPageSize for Alignment usage later + // + PciIoDevice->SystemPageSize <<= 12; + } + + // Calculate BusReservation for PCI IOV + // + if ((PciIoDevice->SrIovCapabilityOffset != 0) && ((FeaturePcdGet(PcdSrIovSupport)& EFI_PCI_IOV_POLICY_SRIOV) != 0)) { + UINT16 VFStride; + UINT16 FirstVFOffset; + UINT32 PFRID; + UINT32 LastVF; + + // + // Read First FirstVFOffset, InitialVFs, and VFStride + // + PciIo->Pci.Read ( + PciIo, + EfiPciIoWidthUint16, + PciIoDevice->SrIovCapabilityOffset + EFI_PCIE_CAPABILITY_ID_SRIOV_FIRSTVF, + 1, + &FirstVFOffset + ); + DEBUG ((EFI_D_INFO, "PCI-IOV B%x.D%x.F%x - FirstVFOffset - 0x%x\n", (UINTN)Bus, (UINTN)Device, (UINTN)Func, (UINTN)FirstVFOffset)); + + PciIo->Pci.Read ( + PciIo, + EfiPciIoWidthUint16, + PciIoDevice->SrIovCapabilityOffset + EFI_PCIE_CAPABILITY_ID_SRIOV_INITIALVFS, + 1, + &PciIoDevice->InitialVFs + ); + DEBUG ((EFI_D_INFO, "PCI-IOV B%x.D%x.F%x - InitialVFs - 0x%x\n", (UINTN)Bus, (UINTN)Device, (UINTN)Func, (UINTN)PciIoDevice->InitialVFs)); + + PciIo->Pci.Read ( + PciIo, + EfiPciIoWidthUint16, + PciIoDevice->SrIovCapabilityOffset + EFI_PCIE_CAPABILITY_ID_SRIOV_VFSTRIDE, + 1, + &VFStride + ); + DEBUG ((EFI_D_INFO, "PCI-IOV B%x.D%x.F%x - VFStride - 0x%x\n", (UINTN)Bus, (UINTN)Device, (UINTN)Func, (UINTN)VFStride)); + + // + // Calculate LastVF + // + PFRID = EFI_PCI_RID(Bus, Device, Func); + LastVF = PFRID + FirstVFOffset + (PciIoDevice->InitialVFs - 1) * VFStride; + + // + // Calculate ReservedBusNum for this PF + // + PciIoDevice->ReservedBusNum = (UINT16)(EFI_PCI_BUS_OF_RID (LastVF) - Bus + 1); + DEBUG ((EFI_D_INFO, "PCI-IOV B%x.D%x.F%x - reserved bus number - 0x%x\n", (UINTN)Bus, (UINTN)Device, (UINTN)Func, (UINTN)PciIoDevice->ReservedBusNum)); + } + + + // + // Initialize the reserved resource list + // + InitializeListHead (&PciIoDevice->ReservedResourceList); + + // + // Initialize the driver list + // + InitializeListHead (&PciIoDevice->OptionRomDriverList); + + // + // Initialize the child list + // + InitializeListHead (&PciIoDevice->ChildList); + + return PciIoDevice; +} + +/** + This routine is used to enumerate entire pci bus system + in a given platform. + + It is only called on the second start on the same Root Bridge. + + @param Controller Parent bridge handler. + + @retval EFI_SUCCESS PCI enumeration finished successfully. + @retval other Some error occurred when enumerating the pci bus system. + +**/ +EFI_STATUS +PciEnumeratorLight ( + IN EFI_HANDLE Controller + ) +{ + + EFI_STATUS Status; + EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *PciRootBridgeIo; + PCI_IO_DEVICE *RootBridgeDev; + UINT16 MinBus; + UINT16 MaxBus; + EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Descriptors; + + MinBus = 0; + MaxBus = PCI_MAX_BUS; + Descriptors = NULL; + + // + // If this root bridge has been already enumerated, then return successfully + // + if (GetRootBridgeByHandle (Controller) != NULL) { + return EFI_SUCCESS; + } + + // + // Open pci root bridge io protocol + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiPciRootBridgeIoProtocolGuid, + (VOID **) &PciRootBridgeIo, + gPciBusDriverBinding.DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + if (EFI_ERROR (Status) && Status != EFI_ALREADY_STARTED) { + return Status; + } + + Status = PciRootBridgeIo->Configuration (PciRootBridgeIo, (VOID **) &Descriptors); + + if (EFI_ERROR (Status)) { + return Status; + } + + while (PciGetBusRange (&Descriptors, &MinBus, &MaxBus, NULL) == EFI_SUCCESS) { + + // + // Create a device node for root bridge device with a NULL host bridge controller handle + // + RootBridgeDev = CreateRootBridge (Controller); + + if (RootBridgeDev == NULL) { + Descriptors++; + continue; + } + + // + // Record the root bridgeio protocol + // + RootBridgeDev->PciRootBridgeIo = PciRootBridgeIo; + + Status = PciPciDeviceInfoCollector ( + RootBridgeDev, + (UINT8) MinBus + ); + + if (!EFI_ERROR (Status)) { + + // + // Remove those PCI devices which are rejected when full enumeration + // + RemoveRejectedPciDevices (RootBridgeDev->Handle, RootBridgeDev); + + // + // Process option rom light + // + ProcessOptionRomLight (RootBridgeDev); + + // + // Determine attributes for all devices under this root bridge + // + DetermineDeviceAttribute (RootBridgeDev); + + // + // If successfully, insert the node into device pool + // + InsertRootBridge (RootBridgeDev); + } else { + + // + // If unsuccessly, destroy the entire node + // + DestroyRootBridge (RootBridgeDev); + } + + Descriptors++; + } + + return EFI_SUCCESS; +} + +/** + Get bus range from PCI resource descriptor list. + + @param Descriptors A pointer to the address space descriptor. + @param MinBus The min bus returned. + @param MaxBus The max bus returned. + @param BusRange The bus range returned. + + @retval EFI_SUCCESS Successfully got bus range. + @retval EFI_NOT_FOUND Can not find the specific bus. + +**/ +EFI_STATUS +PciGetBusRange ( + IN EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR **Descriptors, + OUT UINT16 *MinBus, + OUT UINT16 *MaxBus, + OUT UINT16 *BusRange + ) +{ + while ((*Descriptors)->Desc != ACPI_END_TAG_DESCRIPTOR) { + if ((*Descriptors)->ResType == ACPI_ADDRESS_SPACE_TYPE_BUS) { + if (MinBus != NULL) { + *MinBus = (UINT16) (*Descriptors)->AddrRangeMin; + } + + if (MaxBus != NULL) { + *MaxBus = (UINT16) (*Descriptors)->AddrRangeMax; + } + + if (BusRange != NULL) { + *BusRange = (UINT16) (*Descriptors)->AddrLen; + } + + return EFI_SUCCESS; + } + + (*Descriptors)++; + } + + return EFI_NOT_FOUND; +} + +/** + This routine can be used to start the root bridge. + + @param RootBridgeDev Pci device instance. + + @retval EFI_SUCCESS This device started. + @retval other Failed to get PCI Root Bridge I/O protocol. + +**/ +EFI_STATUS +StartManagingRootBridge ( + IN PCI_IO_DEVICE *RootBridgeDev + ) +{ + EFI_HANDLE RootBridgeHandle; + EFI_STATUS Status; + EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *PciRootBridgeIo; + + // + // Get the root bridge handle + // + RootBridgeHandle = RootBridgeDev->Handle; + PciRootBridgeIo = NULL; + + // + // Get the pci root bridge io protocol + // + Status = gBS->OpenProtocol ( + RootBridgeHandle, + &gEfiPciRootBridgeIoProtocolGuid, + (VOID **) &PciRootBridgeIo, + gPciBusDriverBinding.DriverBindingHandle, + RootBridgeHandle, + EFI_OPEN_PROTOCOL_BY_DRIVER + ); + + if (EFI_ERROR (Status) && Status != EFI_ALREADY_STARTED) { + return Status; + } + + // + // Store the PciRootBridgeIo protocol into root bridge private data + // + RootBridgeDev->PciRootBridgeIo = PciRootBridgeIo; + + return EFI_SUCCESS; + +} + +/** + This routine can be used to check whether a PCI device should be rejected when light enumeration. + + @param PciIoDevice Pci device instance. + + @retval TRUE This device should be rejected. + @retval FALSE This device shouldn't be rejected. + +**/ +BOOLEAN +IsPciDeviceRejected ( + IN PCI_IO_DEVICE *PciIoDevice + ) +{ + EFI_STATUS Status; + UINT32 TestValue; + UINT32 OldValue; + UINT32 Mask; + UINT8 BarOffset; + + // + // PPB should be skip! + // + if (IS_PCI_BRIDGE (&PciIoDevice->Pci)) { + return FALSE; + } + + if (IS_CARDBUS_BRIDGE (&PciIoDevice->Pci)) { + // + // Only test base registers for P2C + // + for (BarOffset = 0x1C; BarOffset <= 0x38; BarOffset += 2 * sizeof (UINT32)) { + + Mask = (BarOffset < 0x2C) ? 0xFFFFF000 : 0xFFFFFFFC; + Status = BarExisted (PciIoDevice, BarOffset, &TestValue, &OldValue); + if (EFI_ERROR (Status)) { + continue; + } + + TestValue = TestValue & Mask; + if ((TestValue != 0) && (TestValue == (OldValue & Mask))) { + // + // The bar isn't programed, so it should be rejected + // + return TRUE; + } + } + + return FALSE; + } + + for (BarOffset = 0x14; BarOffset <= 0x24; BarOffset += sizeof (UINT32)) { + // + // Test PCI devices + // + Status = BarExisted (PciIoDevice, BarOffset, &TestValue, &OldValue); + if (EFI_ERROR (Status)) { + continue; + } + + if ((TestValue & 0x01) != 0) { + + // + // IO Bar + // + Mask = 0xFFFFFFFC; + TestValue = TestValue & Mask; + if ((TestValue != 0) && (TestValue == (OldValue & Mask))) { + return TRUE; + } + + } else { + + // + // Mem Bar + // + Mask = 0xFFFFFFF0; + TestValue = TestValue & Mask; + + if ((TestValue & 0x07) == 0x04) { + + // + // Mem64 or PMem64 + // + BarOffset += sizeof (UINT32); + if ((TestValue != 0) && (TestValue == (OldValue & Mask))) { + + // + // Test its high 32-Bit BAR + // + Status = BarExisted (PciIoDevice, BarOffset, &TestValue, &OldValue); + if (TestValue == OldValue) { + return TRUE; + } + } + + } else { + + // + // Mem32 or PMem32 + // + if ((TestValue != 0) && (TestValue == (OldValue & Mask))) { + return TRUE; + } + } + } + } + + return FALSE; +} + +/** + Reset all bus number from specific bridge. + + @param Bridge Parent specific bridge. + @param StartBusNumber Start bus number. + +**/ +VOID +ResetAllPpbBusNumber ( + IN PCI_IO_DEVICE *Bridge, + IN UINT8 StartBusNumber + ) +{ + EFI_STATUS Status; + PCI_TYPE00 Pci; + UINT8 Device; + UINT32 Register; + UINT8 Func; + UINT64 Address; + UINT8 SecondaryBus; + EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *PciRootBridgeIo; + + PciRootBridgeIo = Bridge->PciRootBridgeIo; + + for (Device = 0; Device <= PCI_MAX_DEVICE; Device++) { + for (Func = 0; Func <= PCI_MAX_FUNC; Func++) { + + // + // Check to see whether a pci device is present + // + Status = PciDevicePresent ( + PciRootBridgeIo, + &Pci, + StartBusNumber, + Device, + Func + ); + + if (!EFI_ERROR (Status) && (IS_PCI_BRIDGE (&Pci))) { + + Register = 0; + Address = EFI_PCI_ADDRESS (StartBusNumber, Device, Func, 0x18); + Status = PciRootBridgeIo->Pci.Read ( + PciRootBridgeIo, + EfiPciWidthUint32, + Address, + 1, + &Register + ); + SecondaryBus = (UINT8)(Register >> 8); + + if (SecondaryBus != 0) { + ResetAllPpbBusNumber (Bridge, SecondaryBus); + } + + // + // Reset register 18h, 19h, 1Ah on PCI Bridge + // + Register &= 0xFF000000; + Status = PciRootBridgeIo->Pci.Write ( + PciRootBridgeIo, + EfiPciWidthUint32, + Address, + 1, + &Register + ); + } + + if (Func == 0 && !IS_PCI_MULTI_FUNC (&Pci)) { + // + // Skip sub functions, this is not a multi function device + // + Func = PCI_MAX_FUNC; + } + } + } +} + diff --git a/MdeModulePkg/Bus/Pci/PciBusDxe/PciEnumeratorSupport.h b/MdeModulePkg/Bus/Pci/PciBusDxe/PciEnumeratorSupport.h new file mode 100644 index 0000000000..31238b45cf --- /dev/null +++ b/MdeModulePkg/Bus/Pci/PciBusDxe/PciEnumeratorSupport.h @@ -0,0 +1,463 @@ +/** @file + PCI emumeration support functions declaration for PCI Bus module. + +Copyright (c) 2006 - 2009, Intel Corporation +All rights reserved. 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. + +**/ + +#ifndef _EFI_PCI_ENUMERATOR_SUPPORT_H_ +#define _EFI_PCI_ENUMERATOR_SUPPORT_H_ + +/** + This routine is used to check whether the pci device is present. + + @param PciRootBridgeIo Pointer to instance of EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL. + @param Pci Output buffer for PCI device configuration space. + @param Bus PCI bus NO. + @param Device PCI device NO. + @param Func PCI Func NO. + + @retval EFI_NOT_FOUND PCI device not present. + @retval EFI_SUCCESS PCI device is found. + +**/ +EFI_STATUS +PciDevicePresent ( + IN EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *PciRootBridgeIo, + OUT PCI_TYPE00 *Pci, + IN UINT8 Bus, + IN UINT8 Device, + IN UINT8 Func + ); + +/** + Collect all the resource information under this root bridge. + + A database that records all the information about pci device subject to this + root bridge will then be created. + + @param Bridge Parent bridge instance. + @param StartBusNumber Bus number of begining. + + @retval EFI_SUCCESS PCI device is found. + @retval other Some error occurred when reading PCI bridge information. + +**/ +EFI_STATUS +PciPciDeviceInfoCollector ( + IN PCI_IO_DEVICE *Bridge, + IN UINT8 StartBusNumber + ); + +/** + Seach required device and create PCI device instance. + + @param Bridge Parent bridge instance. + @param Pci Input PCI device information block. + @param Bus PCI bus NO. + @param Device PCI device NO. + @param Func PCI func NO. + @param PciDevice Output of searched PCI device instance. + + @retval EFI_SUCCESS Successfully created PCI device instance. + @retval EFI_OUT_OF_RESOURCES Cannot get PCI device information. + +**/ +EFI_STATUS +PciSearchDevice ( + IN PCI_IO_DEVICE *Bridge, + IN PCI_TYPE00 *Pci, + IN UINT8 Bus, + IN UINT8 Device, + IN UINT8 Func, + OUT PCI_IO_DEVICE **PciDevice + ); + +/** + Create PCI device instance for PCI device. + + @param Bridge Parent bridge instance. + @param Pci Input PCI device information block. + @param Bus PCI device Bus NO. + @param Device PCI device Device NO. + @param Func PCI device's func NO. + + @return Created PCI device instance. + +**/ +PCI_IO_DEVICE * +GatherDeviceInfo ( + IN PCI_IO_DEVICE *Bridge, + IN PCI_TYPE00 *Pci, + IN UINT8 Bus, + IN UINT8 Device, + IN UINT8 Func + ); + +/** + Create PCI device instance for PCI-PCI bridge. + + @param Bridge Parent bridge instance. + @param Pci Input PCI device information block. + @param Bus PCI device Bus NO. + @param Device PCI device Device NO. + @param Func PCI device's func NO. + + @return Created PCI device instance. + +**/ +PCI_IO_DEVICE * +GatherPpbInfo ( + IN PCI_IO_DEVICE *Bridge, + IN PCI_TYPE00 *Pci, + IN UINT8 Bus, + IN UINT8 Device, + IN UINT8 Func + ); + +/** + Create PCI device instance for PCI Card bridge device. + + @param Bridge Parent bridge instance. + @param Pci Input PCI device information block. + @param Bus PCI device Bus NO. + @param Device PCI device Device NO. + @param Func PCI device's func NO. + + @return Created PCI device instance. + +**/ +PCI_IO_DEVICE * +GatherP2CInfo ( + IN PCI_IO_DEVICE *Bridge, + IN PCI_TYPE00 *Pci, + IN UINT8 Bus, + IN UINT8 Device, + IN UINT8 Func + ); + +/** + Create device path for pci deivce. + + @param ParentDevicePath Parent bridge's path. + @param PciIoDevice Pci device instance. + + @return device path protocol instance for specific pci device. + +**/ +EFI_DEVICE_PATH_PROTOCOL * +CreatePciDevicePath ( + IN EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath, + IN PCI_IO_DEVICE *PciIoDevice + ); + +/** + Check whether the PCI IOV VF bar is existed or not. + + @param PciIoDevice A pointer to the PCI_IO_DEVICE. + @param Offset The offset. + @param BarLengthValue The bar length value returned. + @param OriginalBarValue The original bar value returned. + + @retval EFI_NOT_FOUND The bar doesn't exist. + @retval EFI_SUCCESS The bar exist. + +**/ +EFI_STATUS +VfBarExisted ( + IN PCI_IO_DEVICE *PciIoDevice, + IN UINTN Offset, + OUT UINT32 *BarLengthValue, + OUT UINT32 *OriginalBarValue + ); + +/** + Check whether the bar is existed or not. + + @param PciIoDevice A pointer to the PCI_IO_DEVICE. + @param Offset The offset. + @param BarLengthValue The bar length value returned. + @param OriginalBarValue The original bar value returned. + + @retval EFI_NOT_FOUND The bar doesn't exist. + @retval EFI_SUCCESS The bar exist. + +**/ +EFI_STATUS +BarExisted ( + IN PCI_IO_DEVICE *PciIoDevice, + IN UINTN Offset, + OUT UINT32 *BarLengthValue, + OUT UINT32 *OriginalBarValue + ); + +/** + Test whether the device can support given attributes. + + @param PciIoDevice Pci device instance. + @param Command Input command register value, and + returned supported register value. + @param BridgeControl Inout bridge control value for PPB or P2C, and + returned supported bridge control value. + @param OldCommand Returned and stored old command register offset. + @param OldBridgeControl Returned and stored old Bridge control value for PPB or P2C. + +**/ +VOID +PciTestSupportedAttribute ( + IN PCI_IO_DEVICE *PciIoDevice, + IN OUT UINT16 *Command, + IN OUT UINT16 *BridgeControl, + OUT UINT16 *OldCommand, + OUT UINT16 *OldBridgeControl + ); + +/** + Set the supported or current attributes of a PCI device. + + @param PciIoDevice Structure pointer for PCI device. + @param Command Command register value. + @param BridgeControl Bridge control value for PPB or P2C. + @param Option Make a choice of EFI_SET_SUPPORTS or EFI_SET_ATTRIBUTES. + +**/ +VOID +PciSetDeviceAttribute ( + IN PCI_IO_DEVICE *PciIoDevice, + IN UINT16 Command, + IN UINT16 BridgeControl, + IN UINTN Option + ); + +/** + Determine if the device can support Fast Back to Back attribute. + + @param PciIoDevice Pci device instance. + @param StatusIndex Status register value. + + @retval EFI_SUCCESS This device support Fast Back to Back attribute. + @retval EFI_UNSUPPORTED This device doesn't support Fast Back to Back attribute. + +**/ +EFI_STATUS +GetFastBackToBackSupport ( + IN PCI_IO_DEVICE *PciIoDevice, + IN UINT8 StatusIndex + ); + +/** + Determine the related attributes of all devices under a Root Bridge. + + @param PciIoDevice PCI device instance. + +**/ +EFI_STATUS +DetermineDeviceAttribute ( + IN PCI_IO_DEVICE *PciIoDevice + ); + +/** + This routine is used to update the bar information for those incompatible PCI device. + + @param PciIoDevice Input Pci device instance. Output Pci device instance with updated + Bar information. + + @retval EFI_SUCCESS Successfully updated bar information. + @retval EFI_UNSUPPORTED Given PCI device doesn't belong to incompatible PCI device list. + +**/ +EFI_STATUS +UpdatePciInfo ( + IN OUT PCI_IO_DEVICE *PciIoDevice + ); + +/** + This routine will update the alignment with the new alignment. + + @param Alignment Input Old alignment. Output updated alignment. + @param NewAlignment New alignment. + +**/ +VOID +SetNewAlign ( + IN OUT UINT64 *Alignment, + IN UINT64 NewAlignment + ); + +/** + Parse PCI bar information and fill them into PCI device instance. + + @param PciIoDevice Pci device instance. + @param Offset Bar offset. + @param BarIndex Bar index. + + @return Next bar offset. + +**/ +UINTN +PciParseBar ( + IN PCI_IO_DEVICE *PciIoDevice, + IN UINTN Offset, + IN UINTN BarIndex + ); + +/** + Parse PCI IOV VF bar information and fill them into PCI device instance. + + @param PciIoDevice Pci device instance. + @param Offset Bar offset. + @param BarIndex Bar index. + + @return Next bar offset. + +**/ +UINTN +PciIovParseVfBar ( + IN PCI_IO_DEVICE *PciIoDevice, + IN UINTN Offset, + IN UINTN BarIndex + ); + +/** + This routine is used to initialize the bar of a PCI device. + + @param PciIoDevice Pci device instance. + + @note It can be called typically when a device is going to be rejected. + +**/ +VOID +InitializePciDevice ( + IN PCI_IO_DEVICE *PciIoDevice + ); + +/** + This routine is used to initialize the bar of a PCI-PCI Bridge device. + + @param PciIoDevice PCI-PCI bridge device instance. + +**/ +VOID +InitializePpb ( + IN PCI_IO_DEVICE *PciIoDevice + ); + +/** + This routine is used to initialize the bar of a PCI Card Bridge device. + + @param PciIoDevice PCI Card bridge device. + +**/ +VOID +InitializeP2C ( + IN PCI_IO_DEVICE *PciIoDevice + ); + +/** + Create and initiliaze general PCI I/O device instance for + PCI device/bridge device/hotplug bridge device. + + @param PciRootBridgeIo Pointer to instance of EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL. + @param Pci Input Pci information block. + @param Bus Device Bus NO. + @param Device Device device NO. + @param Func Device func NO. + + @return Instance of PCI device. NULL means no instance created. + +**/ +PCI_IO_DEVICE * +CreatePciIoDevice ( + IN EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *PciRootBridgeIo, + IN PCI_TYPE00 *Pci, + IN UINT8 Bus, + IN UINT8 Device, + IN UINT8 Func + ); + +/** + This routine is used to enumerate entire pci bus system + in a given platform. + + It is only called on the second start on the same Root Bridge. + + @param Controller Parent bridge handler. + + @retval EFI_SUCCESS PCI enumeration finished successfully. + @retval other Some error occurred when enumerating the pci bus system. + +**/ +EFI_STATUS +PciEnumeratorLight ( + IN EFI_HANDLE Controller + ); + +/** + Get bus range from PCI resource descriptor list. + + @param Descriptors A pointer to the address space descriptor. + @param MinBus The min bus returned. + @param MaxBus The max bus returned. + @param BusRange The bus range returned. + + @retval EFI_SUCCESS Successfully got bus range. + @retval EFI_NOT_FOUND Can not find the specific bus. + +**/ +EFI_STATUS +PciGetBusRange ( + IN EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR **Descriptors, + OUT UINT16 *MinBus, + OUT UINT16 *MaxBus, + OUT UINT16 *BusRange + ); + +/** + This routine can be used to start the root bridge. + + @param RootBridgeDev Pci device instance. + + @retval EFI_SUCCESS This device started. + @retval other Failed to get PCI Root Bridge I/O protocol. + +**/ +EFI_STATUS +StartManagingRootBridge ( + IN PCI_IO_DEVICE *RootBridgeDev + ); + +/** + This routine can be used to check whether a PCI device should be rejected when light enumeration. + + @param PciIoDevice Pci device instance. + + @retval TRUE This device should be rejected. + @retval FALSE This device shouldn't be rejected. + +**/ +BOOLEAN +IsPciDeviceRejected ( + IN PCI_IO_DEVICE *PciIoDevice + ); + +/** + Reset all bus number from specific bridge. + + @param Bridge Parent specific bridge. + @param StartBusNumber Start bus number. + +**/ +VOID +ResetAllPpbBusNumber ( + IN PCI_IO_DEVICE *Bridge, + IN UINT8 StartBusNumber + ); + +#endif diff --git a/MdeModulePkg/Bus/Pci/PciBusDxe/PciHotPlugSupport.c b/MdeModulePkg/Bus/Pci/PciBusDxe/PciHotPlugSupport.c new file mode 100644 index 0000000000..20c2c2fcfe --- /dev/null +++ b/MdeModulePkg/Bus/Pci/PciBusDxe/PciHotPlugSupport.c @@ -0,0 +1,394 @@ +/** @file + PCI Hot Plug support functions implementation for PCI Bus module.. + +Copyright (c) 2006 - 2009, Intel Corporation +All rights reserved. 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" + +EFI_PCI_HOT_PLUG_INIT_PROTOCOL *gPciHotPlugInit = NULL; +EFI_HPC_LOCATION *gPciRootHpcPool = NULL; +UINTN gPciRootHpcCount = 0; +ROOT_HPC_DATA *gPciRootHpcData = NULL; + + +/** + Event notification function to set Hot Plug controller status. + + @param Event The event that invoke this function. + @param Context The calling context, pointer to ROOT_HPC_DATA. + +**/ +VOID +EFIAPI +PciHPCInitialized ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + ROOT_HPC_DATA *HpcData; + + HpcData = (ROOT_HPC_DATA *) Context; + HpcData->Initialized = TRUE; +} + +/** + Compare two device pathes to check if they are exactly same. + + @param DevicePath1 A pointer to the first device path data structure. + @param DevicePath2 A pointer to the second device path data structure. + + @retval TRUE They are same. + @retval FALSE They are not same. + +**/ +BOOLEAN +EfiCompareDevicePath ( + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath1, + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath2 + ) +{ + UINTN Size1; + UINTN Size2; + + Size1 = GetDevicePathSize (DevicePath1); + Size2 = GetDevicePathSize (DevicePath2); + + if (Size1 != Size2) { + return FALSE; + } + + if (CompareMem (DevicePath1, DevicePath2, Size1) != 0) { + return FALSE; + } + + return TRUE; +} + +/** + Check hot plug support and initialize root hot plug private data. + + If Hot Plug is supported by the platform, call PCI Hot Plug Init protocol + to get PCI Hot Plug controller's information and constructor the root hot plug + private data structure. + + @retval EFI_SUCCESS They are same. + @retval EFI_UNSUPPORTED No PCI Hot Plug controler on the platform. + @retval EFI_OUT_OF_RESOURCES No memory to constructor root hot plug private + data structure. + +**/ +EFI_STATUS +InitializeHotPlugSupport ( + VOID + ) +{ + EFI_STATUS Status; + EFI_HPC_LOCATION *HpcList; + UINTN HpcCount; + + // + // Locate the PciHotPlugInit Protocol + // If it doesn't exist, that means there is no + // hot plug controller supported on the platform + // the PCI Bus driver is running on. HotPlug Support + // is an optional feature, so absence of the protocol + // won't incur the penalty. + // + Status = gBS->LocateProtocol ( + &gEfiPciHotPlugInitProtocolGuid, + NULL, + (VOID **) &gPciHotPlugInit + ); + + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + Status = gPciHotPlugInit->GetRootHpcList ( + gPciHotPlugInit, + &HpcCount, + &HpcList + ); + + if (!EFI_ERROR (Status)) { + + gPciRootHpcPool = HpcList; + gPciRootHpcCount = HpcCount; + gPciRootHpcData = AllocateZeroPool (sizeof (ROOT_HPC_DATA) * gPciRootHpcCount); + if (gPciRootHpcData == NULL) { + return EFI_OUT_OF_RESOURCES; + } + } + + return EFI_SUCCESS; +} + +/** + Test whether device path is for root pci hot plug bus. + + @param HpbDevicePath A pointer to device path data structure to be tested. + @param HpIndex If HpIndex is not NULL, return the index of root hot + plug in global array when TRUE is retuned. + + @retval TRUE The device path is for root pci hot plug bus. + @retval FALSE The device path is not for root pci hot plug bus. + +**/ +BOOLEAN +IsRootPciHotPlugBus ( + IN EFI_DEVICE_PATH_PROTOCOL *HpbDevicePath, + OUT UINTN *HpIndex OPTIONAL + ) +{ + UINTN Index; + + for (Index = 0; Index < gPciRootHpcCount; Index++) { + + if (EfiCompareDevicePath (gPciRootHpcPool[Index].HpbDevicePath, HpbDevicePath)) { + + if (HpIndex != NULL) { + *HpIndex = Index; + } + + return TRUE; + } + } + + return FALSE; +} + +/** + Test whether device path is for root pci hot plug controller. + + @param HpcDevicePath A pointer to device path data structure to be tested. + @param HpIndex If HpIndex is not NULL, return the index of root hot + plug in global array when TRUE is retuned. + + @retval TRUE The device path is for root pci hot plug controller. + @retval FALSE The device path is not for root pci hot plug controller. + +**/ +BOOLEAN +IsRootPciHotPlugController ( + IN EFI_DEVICE_PATH_PROTOCOL *HpcDevicePath, + OUT UINTN *HpIndex + ) +{ + UINTN Index; + + for (Index = 0; Index < gPciRootHpcCount; Index++) { + + if (EfiCompareDevicePath (gPciRootHpcPool[Index].HpcDevicePath, HpcDevicePath)) { + + if (HpIndex != NULL) { + *HpIndex = Index; + } + + return TRUE; + } + } + + return FALSE; +} + +/** + Creating event object for PCI Hot Plug controller. + + @param HpIndex Index of hot plug device in global array. + @param Event The retuned event that invoke this function. + + @return Status of create event invoken. + +**/ +EFI_STATUS +CreateEventForHpc ( + IN UINTN HpIndex, + OUT EFI_EVENT *Event + ) +{ + EFI_STATUS Status; + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + PciHPCInitialized, + gPciRootHpcData + HpIndex, + &((gPciRootHpcData + HpIndex)->Event) + ); + + if (!EFI_ERROR (Status)) { + *Event = (gPciRootHpcData + HpIndex)->Event; + } + + return Status; +} + +/** + Wait for all root PCI Hot Plug controller finished initializing. + + @param TimeoutInMicroSeconds Microseconds to wait for all root HPCs' initialization. + + @retval EFI_SUCCESS All HPCs initialization finished. + @retval EFI_TIMEOUT Not ALL HPCs initialization finished in Microseconds. + +**/ +EFI_STATUS +AllRootHPCInitialized ( + IN UINTN TimeoutInMicroSeconds + ) +{ + UINT32 Delay; + UINTN Index; + + Delay = (UINT32) ((TimeoutInMicroSeconds / 30) + 1); + + do { + for (Index = 0; Index < gPciRootHpcCount; Index++) { + + if (!gPciRootHpcData[Index].Initialized) { + break; + } + } + + if (Index == gPciRootHpcCount) { + return EFI_SUCCESS; + } + + // + // Stall for 30 microseconds.. + // + gBS->Stall (30); + + Delay--; + + } while (Delay > 0); + + return EFI_TIMEOUT; +} + +/** + Check whether PCI-PCI bridge has PCI Hot Plug capability register block. + + @param PciIoDevice A Pointer to the PCI-PCI bridge. + + @retval TRUE PCI device is HPC. + @retval FALSE PCI device is not HPC. + +**/ +BOOLEAN +IsSHPC ( + IN PCI_IO_DEVICE *PciIoDevice + ) +{ + + EFI_STATUS Status; + UINT8 Offset; + + if (PciIoDevice == NULL) { + return FALSE; + } + + Offset = 0; + Status = LocateCapabilityRegBlock ( + PciIoDevice, + EFI_PCI_CAPABILITY_ID_HOTPLUG, + &Offset, + NULL + ); + + // + // If the PCI-PCI bridge has the hot plug controller build-in, + // then return TRUE; + // + if (!EFI_ERROR (Status)) { + return TRUE; + } + + return FALSE; +} + +/** + Get resource padding if the specified PCI bridge is a hot plug bus. + + @param PciIoDevice PCI bridge instance. + +**/ +VOID +GetResourcePaddingForHpb ( + IN PCI_IO_DEVICE *PciIoDevice + ) +{ + EFI_STATUS Status; + EFI_HPC_STATE State; + UINT64 PciAddress; + EFI_HPC_PADDING_ATTRIBUTES Attributes; + EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Descriptors; + + if (IsPciHotPlugBus (PciIoDevice)) { + // + // If PCI-PCI bridge device is PCI Hot Plug bus. + // + PciAddress = EFI_PCI_ADDRESS (PciIoDevice->BusNumber, PciIoDevice->DeviceNumber, PciIoDevice->FunctionNumber, 0); + Status = gPciHotPlugInit->GetResourcePadding ( + gPciHotPlugInit, + PciIoDevice->DevicePath, + PciAddress, + &State, + (VOID **) &Descriptors, + &Attributes + ); + + if (EFI_ERROR (Status)) { + return; + } + + if ((State & EFI_HPC_STATE_ENABLED) != 0 && (State & EFI_HPC_STATE_INITIALIZED) != 0) { + PciIoDevice->ResourcePaddingDescriptors = Descriptors; + PciIoDevice->PaddingAttributes = Attributes; + } + + return; + } +} + +/** + Test whether PCI device is hot plug bus. + + @param PciIoDevice PCI device instance. + + @retval TRUE PCI device is a hot plug bus. + @retval FALSE PCI device is not a hot plug bus. + +**/ +BOOLEAN +IsPciHotPlugBus ( + PCI_IO_DEVICE *PciIoDevice + ) +{ + if (IsSHPC (PciIoDevice)) { + // + // If the PPB has the hot plug controller build-in, + // then return TRUE; + // + return TRUE; + } + + // + // Otherwise, see if it is a Root HPC + // + if(IsRootPciHotPlugBus (PciIoDevice->DevicePath, NULL)) { + return TRUE; + } + + return FALSE; +} + diff --git a/MdeModulePkg/Bus/Pci/PciBusDxe/PciHotPlugSupport.h b/MdeModulePkg/Bus/Pci/PciBusDxe/PciHotPlugSupport.h new file mode 100644 index 0000000000..a84e256b90 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/PciBusDxe/PciHotPlugSupport.h @@ -0,0 +1,189 @@ +/** @file + PCI Hot Plug support functions declaration for PCI Bus module. + +Copyright (c) 2006 - 2009, Intel Corporation +All rights reserved. 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. + +**/ + +#ifndef _EFI_PCI_HOT_PLUG_SUPPORT_H_ +#define _EFI_PCI_HOT_PLUG_SUPPORT_H_ + +// +// stall 1 second, its unit is 100ns +// +#define STALL_1_SECOND 1000000 + +// +// PCI Hot Plug controller private data +// +typedef struct { + EFI_EVENT Event; + BOOLEAN Initialized; + VOID *Padding; +} ROOT_HPC_DATA; + +// +// Reference of some global variabes +// +extern EFI_PCI_HOT_PLUG_INIT_PROTOCOL *gPciHotPlugInit; +extern EFI_HPC_LOCATION *gPciRootHpcPool; +extern ROOT_HPC_DATA *gPciRootHpcData; + +/** + Event notification function to set Hot Plug controller status. + + @param Event The event that invoke this function. + @param Context The calling context, pointer to ROOT_HPC_DATA. + +**/ +VOID +EFIAPI +PciHPCInitialized ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +/** + Compare two device pathes to check if they are exactly same. + + @param DevicePath1 A pointer to the first device path data structure. + @param DevicePath2 A pointer to the second device path data structure. + + @retval TRUE They are same. + @retval FALSE They are not same. + +**/ +BOOLEAN +EfiCompareDevicePath ( + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath1, + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath2 + ); + +/** + Check hot plug support and initialize root hot plug private data. + + If Hot Plug is supported by the platform, call PCI Hot Plug Init protocol + to get PCI Hot Plug controller's information and constructor the root hot plug + private data structure. + + @retval EFI_SUCCESS They are same. + @retval EFI_UNSUPPORTED No PCI Hot Plug controler on the platform. + @retval EFI_OUT_OF_RESOURCES No memory to constructor root hot plug private + data structure. + +**/ +EFI_STATUS +InitializeHotPlugSupport ( + VOID + ); + +/** + Test whether PCI device is hot plug bus. + + @param PciIoDevice PCI device instance. + + @retval TRUE PCI device is a hot plug bus. + @retval FALSE PCI device is not a hot plug bus. + +**/ +BOOLEAN +IsPciHotPlugBus ( + PCI_IO_DEVICE *PciIoDevice + ); + +/** + Test whether device path is for root pci hot plug bus. + + @param HpbDevicePath A pointer to device path data structure to be tested. + @param HpIndex If HpIndex is not NULL, return the index of root hot + plug in global array when TRUE is retuned. + + @retval TRUE The device path is for root pci hot plug bus. + @retval FALSE The device path is not for root pci hot plug bus. + +**/ +BOOLEAN +IsRootPciHotPlugBus ( + IN EFI_DEVICE_PATH_PROTOCOL *HpbDevicePath, + OUT UINTN *HpIndex OPTIONAL + ); + +/** + Test whether device path is for root pci hot plug controller. + + @param HpcDevicePath A pointer to device path data structure to be tested. + @param HpIndex If HpIndex is not NULL, return the index of root hot + plug in global array when TRUE is retuned. + + @retval TRUE The device path is for root pci hot plug controller. + @retval FALSE The device path is not for root pci hot plug controller. + +**/ +BOOLEAN +IsRootPciHotPlugController ( + IN EFI_DEVICE_PATH_PROTOCOL *HpcDevicePath, + OUT UINTN *HpIndex + ); + +/** + Creating event object for PCI Hot Plug controller. + + @param HpIndex Index of hot plug device in global array. + @param Event The retuned event that invoke this function. + + @return Status of create event invoken. + +**/ +EFI_STATUS +CreateEventForHpc ( + IN UINTN HpIndex, + OUT EFI_EVENT *Event + ); + +/** + Wait for all root PCI Hot Plug controller finished initializing. + + @param TimeoutInMicroSeconds Microseconds to wait for all root HPCs' initialization. + + @retval EFI_SUCCESS All HPCs initialization finished. + @retval EFI_TIMEOUT Not ALL HPCs initialization finished in Microseconds. + +**/ +EFI_STATUS +AllRootHPCInitialized ( + IN UINTN TimeoutInMicroSeconds + ); + +/** + Check whether PCI-PCI bridge has PCI Hot Plug capability register block. + + @param PciIoDevice A Pointer to the PCI-PCI bridge. + + @retval TRUE PCI device is HPC. + @retval FALSE PCI device is not HPC. + +**/ +BOOLEAN +IsSHPC ( + IN PCI_IO_DEVICE *PciIoDevice + ); + +/** + Get resource padding if the specified PCI bridge is a hot plug bus. + + @param PciIoDevice PCI bridge instance. + +**/ +VOID +GetResourcePaddingForHpb ( + IN PCI_IO_DEVICE *PciIoDevice + ); + +#endif diff --git a/MdeModulePkg/Bus/Pci/PciBusDxe/PciIo.c b/MdeModulePkg/Bus/Pci/PciBusDxe/PciIo.c new file mode 100644 index 0000000000..5cc5967245 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/PciBusDxe/PciIo.c @@ -0,0 +1,1877 @@ +/** @file + EFI PCI IO protocol functions implementation for PCI Bus module. + +Copyright (c) 2006 - 2009, Intel Corporation +All rights reserved. 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" + +// +// Pci Io Protocol Interface +// +EFI_PCI_IO_PROTOCOL mPciIoInterface = { + PciIoPollMem, + PciIoPollIo, + { + PciIoMemRead, + PciIoMemWrite + }, + { + PciIoIoRead, + PciIoIoWrite + }, + { + PciIoConfigRead, + PciIoConfigWrite + }, + PciIoCopyMem, + PciIoMap, + PciIoUnmap, + PciIoAllocateBuffer, + PciIoFreeBuffer, + PciIoFlush, + PciIoGetLocation, + PciIoAttributes, + PciIoGetBarAttributes, + PciIoSetBarAttributes, + 0, + NULL +}; + +/** + Report a error Status code of PCI bus driver controller. + + @param PciIoDevice Pci device instance. + @param Code Status code value. + +**/ +EFI_STATUS +ReportErrorStatusCode ( + IN PCI_IO_DEVICE *PciIoDevice, + IN EFI_STATUS_CODE_VALUE Code + ) +{ + return REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_ERROR_CODE | EFI_ERROR_MINOR, + Code, + PciIoDevice->DevicePath + ); +} + +/** + Initializes a PCI I/O Instance. + + @param PciIoDevice Pci device instance. + +**/ +VOID +InitializePciIoInstance ( + IN PCI_IO_DEVICE *PciIoDevice + ) +{ + CopyMem (&PciIoDevice->PciIo, &mPciIoInterface, sizeof (EFI_PCI_IO_PROTOCOL)); +} + +/** + Verifies access to a PCI Base Address Register (BAR). + + @param PciIoDevice Pci device instance. + @param BarIndex The BAR index of the standard PCI Configuration header to use as the + base address for the memory or I/O operation to perform. + @param Type Operation type could be memory or I/O. + @param Width Signifies the width of the memory or I/O operations. + @param Count The number of memory or I/O operations to perform. + @param Offset The offset within the PCI configuration space for the PCI controller. + + @retval EFI_INVALID_PARAMETER Invalid Width/BarIndex or Bar type. + @retval EFI_SUCCESS Successfully verified. + +**/ +EFI_STATUS +PciIoVerifyBarAccess ( + IN PCI_IO_DEVICE *PciIoDevice, + IN UINT8 BarIndex, + IN PCI_BAR_TYPE Type, + IN IN EFI_PCI_IO_PROTOCOL_WIDTH Width, + IN IN UINTN Count, + IN UINT64 *Offset + ) +{ + if (Width < 0 || Width >= EfiPciIoWidthMaximum) { + return EFI_INVALID_PARAMETER; + } + + if (BarIndex == EFI_PCI_IO_PASS_THROUGH_BAR) { + return EFI_SUCCESS; + } + + // + // BarIndex 0-5 is legal + // + if (BarIndex >= PCI_MAX_BAR) { + return EFI_INVALID_PARAMETER; + } + + if (!CheckBarType (PciIoDevice, BarIndex, Type)) { + return EFI_INVALID_PARAMETER; + } + + // + // If Width is EfiPciIoWidthFifoUintX then convert to EfiPciIoWidthUintX + // If Width is EfiPciIoWidthFillUintX then convert to EfiPciIoWidthUintX + // + if (Width >= EfiPciWidthFifoUint8 && Width <= EfiPciWidthFifoUint64) { + Count = 1; + } + + Width = (EFI_PCI_IO_PROTOCOL_WIDTH) (Width & 0x03); + + if ((*Offset + Count * (UINTN)(1 << Width)) - 1 >= PciIoDevice->PciBar[BarIndex].Length) { + return EFI_INVALID_PARAMETER; + } + + *Offset = *Offset + PciIoDevice->PciBar[BarIndex].BaseAddress; + + return EFI_SUCCESS; +} + +/** + Verifies access to a PCI Configuration Header. + + @param PciIoDevice Pci device instance. + @param Width Signifies the width of the memory or I/O operations. + @param Count The number of memory or I/O operations to perform. + @param Offset The offset within the PCI configuration space for the PCI controller. + + @retval EFI_INVALID_PARAMETER Invalid Width + @retval EFI_UNSUPPORTED Offset overflowed. + @retval EFI_SUCCESS Successfully verified. + +**/ +EFI_STATUS +PciIoVerifyConfigAccess ( + IN PCI_IO_DEVICE *PciIoDevice, + IN EFI_PCI_IO_PROTOCOL_WIDTH Width, + IN UINTN Count, + IN UINT64 *Offset + ) +{ + UINT64 ExtendOffset; + + if (Width < 0 || Width >= EfiPciIoWidthMaximum) { + return EFI_INVALID_PARAMETER; + } + + // + // If Width is EfiPciIoWidthFillUintX then convert to EfiPciIoWidthUintX + // + Width = (EFI_PCI_IO_PROTOCOL_WIDTH) (Width & 0x03); + + if (PciIoDevice->IsPciExp) { + if ((*Offset + Count * (UINTN)(1 << Width)) - 1 >= PCI_EXP_MAX_CONFIG_OFFSET) { + return EFI_UNSUPPORTED; + } + + ExtendOffset = LShiftU64 (*Offset, 32); + *Offset = EFI_PCI_ADDRESS (PciIoDevice->BusNumber, PciIoDevice->DeviceNumber, PciIoDevice->FunctionNumber, 0); + *Offset = (*Offset) | ExtendOffset; + + } else { + if ((*Offset + Count * (UINTN)(1 << Width)) - 1 >= PCI_MAX_CONFIG_OFFSET) { + return EFI_UNSUPPORTED; + } + + *Offset = EFI_PCI_ADDRESS (PciIoDevice->BusNumber, PciIoDevice->DeviceNumber, PciIoDevice->FunctionNumber, *Offset); + } + + return EFI_SUCCESS; +} + +/** + Reads from the memory space of a PCI controller. Returns either when the polling exit criteria is + satisfied or after a defined duration. + + @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. + @param Width Signifies the width of the memory or I/O operations. + @param BarIndex The BAR index of the standard PCI Configuration header to use as the + base address for the memory operation to perform. + @param Offset The offset within the selected BAR to start the memory operation. + @param Mask Mask used for the polling criteria. + @param Value The comparison value used for the polling exit criteria. + @param Delay The number of 100 ns units to poll. + @param Result Pointer to the last value read from the memory location. + + @retval EFI_SUCCESS The last data returned from the access matched the poll exit criteria. + @retval EFI_UNSUPPORTED BarIndex not valid for this PCI controller. + @retval EFI_UNSUPPORTED Offset is not valid for the BarIndex of this PCI controller. + @retval EFI_TIMEOUT Delay expired before a match occurred. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + +**/ +EFI_STATUS +EFIAPI +PciIoPollMem ( + IN EFI_PCI_IO_PROTOCOL *This, + IN EFI_PCI_IO_PROTOCOL_WIDTH Width, + IN UINT8 BarIndex, + IN UINT64 Offset, + IN UINT64 Mask, + IN UINT64 Value, + IN UINT64 Delay, + OUT UINT64 *Result + ) +{ + EFI_STATUS Status; + PCI_IO_DEVICE *PciIoDevice; + + PciIoDevice = PCI_IO_DEVICE_FROM_PCI_IO_THIS (This); + + if (Width < 0 || Width >= EfiPciIoWidthMaximum) { + return EFI_INVALID_PARAMETER; + } + + Status = PciIoVerifyBarAccess (PciIoDevice, BarIndex, PciBarTypeMem, Width, 1, &Offset); + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + if (Width > EfiPciIoWidthUint64) { + return EFI_INVALID_PARAMETER; + } + + Status = PciIoDevice->PciRootBridgeIo->PollMem ( + PciIoDevice->PciRootBridgeIo, + (EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_WIDTH) Width, + Offset, + Mask, + Value, + Delay, + Result + ); + + if (EFI_ERROR (Status)) { + ReportErrorStatusCode (PciIoDevice, EFI_IO_BUS_PCI | EFI_IOB_EC_CONTROLLER_ERROR); + } + + return Status; +} + +/** + Reads from the memory space of a PCI controller. Returns either when the polling exit criteria is + satisfied or after a defined duration. + + @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. + @param Width Signifies the width of the memory or I/O operations. + @param BarIndex The BAR index of the standard PCI Configuration header to use as the + base address for the memory operation to perform. + @param Offset The offset within the selected BAR to start the memory operation. + @param Mask Mask used for the polling criteria. + @param Value The comparison value used for the polling exit criteria. + @param Delay The number of 100 ns units to poll. + @param Result Pointer to the last value read from the memory location. + + @retval EFI_SUCCESS The last data returned from the access matched the poll exit criteria. + @retval EFI_UNSUPPORTED BarIndex not valid for this PCI controller. + @retval EFI_UNSUPPORTED Offset is not valid for the BarIndex of this PCI controller. + @retval EFI_TIMEOUT Delay expired before a match occurred. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + +**/ +EFI_STATUS +EFIAPI +PciIoPollIo ( + IN EFI_PCI_IO_PROTOCOL *This, + IN EFI_PCI_IO_PROTOCOL_WIDTH Width, + IN UINT8 BarIndex, + IN UINT64 Offset, + IN UINT64 Mask, + IN UINT64 Value, + IN UINT64 Delay, + OUT UINT64 *Result + ) +{ + EFI_STATUS Status; + PCI_IO_DEVICE *PciIoDevice; + + PciIoDevice = PCI_IO_DEVICE_FROM_PCI_IO_THIS (This); + + if (Width < 0 || Width > EfiPciIoWidthUint64) { + return EFI_INVALID_PARAMETER; + } + + Status = PciIoVerifyBarAccess (PciIoDevice, BarIndex, PciBarTypeIo, Width, 1, &Offset); + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + Status = PciIoDevice->PciRootBridgeIo->PollIo ( + PciIoDevice->PciRootBridgeIo, + (EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_WIDTH) Width, + Offset, + Mask, + Value, + Delay, + Result + ); + + if (EFI_ERROR (Status)) { + ReportErrorStatusCode (PciIoDevice, EFI_IO_BUS_PCI | EFI_IOB_EC_CONTROLLER_ERROR); + } + + return Status; +} + +/** + Enable a PCI driver to access PCI controller registers in the PCI memory or I/O space. + + @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. + @param Width Signifies the width of the memory or I/O operations. + @param BarIndex The BAR index of the standard PCI Configuration header to use as the + base address for the memory or I/O operation to perform. + @param Offset The offset within the selected BAR to start the memory or I/O operation. + @param Count The number of memory or I/O operations to perform. + @param Buffer For read operations, the destination buffer to store the results. For write + operations, the source buffer to write data from. + + @retval EFI_SUCCESS The data was read from or written to the PCI controller. + @retval EFI_UNSUPPORTED BarIndex not valid for this PCI controller. + @retval EFI_UNSUPPORTED The address range specified by Offset, Width, and Count is not + valid for the PCI BAR specified by BarIndex. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + +**/ +EFI_STATUS +EFIAPI +PciIoMemRead ( + IN EFI_PCI_IO_PROTOCOL *This, + IN EFI_PCI_IO_PROTOCOL_WIDTH Width, + IN UINT8 BarIndex, + IN UINT64 Offset, + IN UINTN Count, + IN OUT VOID *Buffer + ) +{ + EFI_STATUS Status; + PCI_IO_DEVICE *PciIoDevice; + + PciIoDevice = PCI_IO_DEVICE_FROM_PCI_IO_THIS (This); + + if (Width < 0 || Width >= EfiPciIoWidthMaximum) { + return EFI_INVALID_PARAMETER; + } + + if (Buffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + Status = PciIoVerifyBarAccess (PciIoDevice, BarIndex, PciBarTypeMem, Width, Count, &Offset); + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + Status = PciIoDevice->PciRootBridgeIo->Mem.Read ( + PciIoDevice->PciRootBridgeIo, + (EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_WIDTH) Width, + Offset, + Count, + Buffer + ); + + if (EFI_ERROR (Status)) { + ReportErrorStatusCode (PciIoDevice, EFI_IO_BUS_PCI | EFI_IOB_EC_READ_ERROR); + } + + return Status; +} + +/** + Enable a PCI driver to access PCI controller registers in the PCI memory or I/O space. + + @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. + @param Width Signifies the width of the memory or I/O operations. + @param BarIndex The BAR index of the standard PCI Configuration header to use as the + base address for the memory or I/O operation to perform. + @param Offset The offset within the selected BAR to start the memory or I/O operation. + @param Count The number of memory or I/O operations to perform. + @param Buffer For read operations, the destination buffer to store the results. For write + operations, the source buffer to write data from. + + @retval EFI_SUCCESS The data was read from or written to the PCI controller. + @retval EFI_UNSUPPORTED BarIndex not valid for this PCI controller. + @retval EFI_UNSUPPORTED The address range specified by Offset, Width, and Count is not + valid for the PCI BAR specified by BarIndex. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + +**/ +EFI_STATUS +EFIAPI +PciIoMemWrite ( + IN EFI_PCI_IO_PROTOCOL *This, + IN EFI_PCI_IO_PROTOCOL_WIDTH Width, + IN UINT8 BarIndex, + IN UINT64 Offset, + IN UINTN Count, + IN OUT VOID *Buffer + ) +{ + EFI_STATUS Status; + PCI_IO_DEVICE *PciIoDevice; + + PciIoDevice = PCI_IO_DEVICE_FROM_PCI_IO_THIS (This); + + if (Width < 0 || Width >= EfiPciIoWidthMaximum) { + return EFI_INVALID_PARAMETER; + } + + if (Buffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + Status = PciIoVerifyBarAccess (PciIoDevice, BarIndex, PciBarTypeMem, Width, Count, &Offset); + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + Status = PciIoDevice->PciRootBridgeIo->Mem.Write ( + PciIoDevice->PciRootBridgeIo, + (EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_WIDTH) Width, + Offset, + Count, + Buffer + ); + + if (EFI_ERROR (Status)) { + ReportErrorStatusCode (PciIoDevice, EFI_IO_BUS_PCI | EFI_IOB_EC_WRITE_ERROR); + } + + return Status; +} + +/** + Enable a PCI driver to access PCI controller registers in the PCI memory or I/O space. + + @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. + @param Width Signifies the width of the memory or I/O operations. + @param BarIndex The BAR index of the standard PCI Configuration header to use as the + base address for the memory or I/O operation to perform. + @param Offset The offset within the selected BAR to start the memory or I/O operation. + @param Count The number of memory or I/O operations to perform. + @param Buffer For read operations, the destination buffer to store the results. For write + operations, the source buffer to write data from. + + @retval EFI_SUCCESS The data was read from or written to the PCI controller. + @retval EFI_UNSUPPORTED BarIndex not valid for this PCI controller. + @retval EFI_UNSUPPORTED The address range specified by Offset, Width, and Count is not + valid for the PCI BAR specified by BarIndex. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + +**/ +EFI_STATUS +EFIAPI +PciIoIoRead ( + IN EFI_PCI_IO_PROTOCOL *This, + IN EFI_PCI_IO_PROTOCOL_WIDTH Width, + IN UINT8 BarIndex, + IN UINT64 Offset, + IN UINTN Count, + IN OUT VOID *Buffer + ) +{ + EFI_STATUS Status; + PCI_IO_DEVICE *PciIoDevice; + + PciIoDevice = PCI_IO_DEVICE_FROM_PCI_IO_THIS (This); + + if (Width < 0 || Width >= EfiPciIoWidthMaximum) { + return EFI_INVALID_PARAMETER; + } + + if (Buffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + Status = PciIoVerifyBarAccess (PciIoDevice, BarIndex, PciBarTypeIo, Width, Count, &Offset); + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + Status = PciIoDevice->PciRootBridgeIo->Io.Read ( + PciIoDevice->PciRootBridgeIo, + (EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_WIDTH) Width, + Offset, + Count, + Buffer + ); + + if (EFI_ERROR (Status)) { + ReportErrorStatusCode (PciIoDevice, EFI_IO_BUS_PCI | EFI_IOB_EC_READ_ERROR); + } + + return Status; +} + +/** + Enable a PCI driver to access PCI controller registers in the PCI memory or I/O space. + + @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. + @param Width Signifies the width of the memory or I/O operations. + @param BarIndex The BAR index of the standard PCI Configuration header to use as the + base address for the memory or I/O operation to perform. + @param Offset The offset within the selected BAR to start the memory or I/O operation. + @param Count The number of memory or I/O operations to perform. + @param Buffer For read operations, the destination buffer to store the results. For write + operations, the source buffer to write data from. + + @retval EFI_SUCCESS The data was read from or written to the PCI controller. + @retval EFI_UNSUPPORTED BarIndex not valid for this PCI controller. + @retval EFI_UNSUPPORTED The address range specified by Offset, Width, and Count is not + valid for the PCI BAR specified by BarIndex. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + +**/ +EFI_STATUS +EFIAPI +PciIoIoWrite ( + IN EFI_PCI_IO_PROTOCOL *This, + IN EFI_PCI_IO_PROTOCOL_WIDTH Width, + IN UINT8 BarIndex, + IN UINT64 Offset, + IN UINTN Count, + IN OUT VOID *Buffer + ) +{ + EFI_STATUS Status; + PCI_IO_DEVICE *PciIoDevice; + + PciIoDevice = PCI_IO_DEVICE_FROM_PCI_IO_THIS (This); + + if (Width < 0 || Width >= EfiPciIoWidthMaximum) { + return EFI_INVALID_PARAMETER; + } + + if (Buffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + Status = PciIoVerifyBarAccess (PciIoDevice, BarIndex, PciBarTypeIo, Width, Count, &Offset); + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + Status = PciIoDevice->PciRootBridgeIo->Io.Write ( + PciIoDevice->PciRootBridgeIo, + (EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_WIDTH) Width, + Offset, + Count, + Buffer + ); + + if (EFI_ERROR (Status)) { + ReportErrorStatusCode (PciIoDevice, EFI_IO_BUS_PCI | EFI_IOB_EC_WRITE_ERROR); + } + + return Status; +} + +/** + Enable a PCI driver to access PCI controller registers in PCI configuration space. + + @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. + @param Width Signifies the width of the memory operations. + @param Offset The offset within the PCI configuration space for the PCI controller. + @param Count The number of PCI configuration operations to perform. + @param Buffer For read operations, the destination buffer to store the results. For write + operations, the source buffer to write data from. + + + @retval EFI_SUCCESS The data was read from or written to the PCI controller. + @retval EFI_UNSUPPORTED The address range specified by Offset, Width, and Count is not + valid for the PCI configuration header of the PCI controller. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval EFI_INVALID_PARAMETER Buffer is NULL or Width is invalid. + +**/ +EFI_STATUS +EFIAPI +PciIoConfigRead ( + IN EFI_PCI_IO_PROTOCOL *This, + IN EFI_PCI_IO_PROTOCOL_WIDTH Width, + IN UINT32 Offset, + IN UINTN Count, + IN OUT VOID *Buffer + ) +{ + EFI_STATUS Status; + PCI_IO_DEVICE *PciIoDevice; + UINT64 Address; + + PciIoDevice = PCI_IO_DEVICE_FROM_PCI_IO_THIS (This); + + Address = Offset; + Status = PciIoVerifyConfigAccess (PciIoDevice, Width, Count, &Address); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = PciIoDevice->PciRootBridgeIo->Pci.Read ( + PciIoDevice->PciRootBridgeIo, + (EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_WIDTH) Width, + Address, + Count, + Buffer + ); + + if (EFI_ERROR (Status)) { + ReportErrorStatusCode (PciIoDevice, EFI_IO_BUS_PCI | EFI_IOB_EC_READ_ERROR); + } + + return Status; +} + +/** + Enable a PCI driver to access PCI controller registers in PCI configuration space. + + @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. + @param Width Signifies the width of the memory operations. + @param Offset The offset within the PCI configuration space for the PCI controller. + @param Count The number of PCI configuration operations to perform. + @param Buffer For read operations, the destination buffer to store the results. For write + operations, the source buffer to write data from. + + + @retval EFI_SUCCESS The data was read from or written to the PCI controller. + @retval EFI_UNSUPPORTED The address range specified by Offset, Width, and Count is not + valid for the PCI configuration header of the PCI controller. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval EFI_INVALID_PARAMETER Buffer is NULL or Width is invalid. + +**/ +EFI_STATUS +EFIAPI +PciIoConfigWrite ( + IN EFI_PCI_IO_PROTOCOL *This, + IN EFI_PCI_IO_PROTOCOL_WIDTH Width, + IN UINT32 Offset, + IN UINTN Count, + IN OUT VOID *Buffer + ) +{ + EFI_STATUS Status; + PCI_IO_DEVICE *PciIoDevice; + UINT64 Address; + + PciIoDevice = PCI_IO_DEVICE_FROM_PCI_IO_THIS (This); + + Address = Offset; + Status = PciIoVerifyConfigAccess (PciIoDevice, Width, Count, &Address); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = PciIoDevice->PciRootBridgeIo->Pci.Write ( + PciIoDevice->PciRootBridgeIo, + (EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_WIDTH) Width, + Address, + Count, + Buffer + ); + + if (EFI_ERROR (Status)) { + ReportErrorStatusCode (PciIoDevice, EFI_IO_BUS_PCI | EFI_IOB_EC_WRITE_ERROR); + } + + return Status; +} + +/** + Enables a PCI driver to copy one region of PCI memory space to another region of PCI + memory space. + + @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. + @param Width Signifies the width of the memory operations. + @param DestBarIndex The BAR index in the standard PCI Configuration header to use as the + base address for the memory operation to perform. + @param DestOffset The destination offset within the BAR specified by DestBarIndex to + start the memory writes for the copy operation. + @param SrcBarIndex The BAR index in the standard PCI Configuration header to use as the + base address for the memory operation to perform. + @param SrcOffset The source offset within the BAR specified by SrcBarIndex to start + the memory reads for the copy operation. + @param Count The number of memory operations to perform. Bytes moved is Width + size * Count, starting at DestOffset and SrcOffset. + + @retval EFI_SUCCESS The data was copied from one memory region to another memory region. + @retval EFI_UNSUPPORTED DestBarIndex not valid for this PCI controller. + @retval EFI_UNSUPPORTED SrcBarIndex not valid for this PCI controller. + @retval EFI_UNSUPPORTED The address range specified by DestOffset, Width, and Count + is not valid for the PCI BAR specified by DestBarIndex. + @retval EFI_UNSUPPORTED The address range specified by SrcOffset, Width, and Count is + not valid for the PCI BAR specified by SrcBarIndex. + @retval EFI_INVALID_PARAMETER Width is invalid. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + +**/ +EFI_STATUS +EFIAPI +PciIoCopyMem ( + IN EFI_PCI_IO_PROTOCOL *This, + IN EFI_PCI_IO_PROTOCOL_WIDTH Width, + IN UINT8 DestBarIndex, + IN UINT64 DestOffset, + IN UINT8 SrcBarIndex, + IN UINT64 SrcOffset, + IN UINTN Count + ) +{ + EFI_STATUS Status; + PCI_IO_DEVICE *PciIoDevice; + + PciIoDevice = PCI_IO_DEVICE_FROM_PCI_IO_THIS (This); + + if (Width < 0 || Width >= EfiPciIoWidthMaximum) { + return EFI_INVALID_PARAMETER; + } + + if (Width == EfiPciIoWidthFifoUint8 || + Width == EfiPciIoWidthFifoUint16 || + Width == EfiPciIoWidthFifoUint32 || + Width == EfiPciIoWidthFifoUint64 || + Width == EfiPciIoWidthFillUint8 || + Width == EfiPciIoWidthFillUint16 || + Width == EfiPciIoWidthFillUint32 || + Width == EfiPciIoWidthFillUint64) { + return EFI_INVALID_PARAMETER; + } + + Status = PciIoVerifyBarAccess (PciIoDevice, DestBarIndex, PciBarTypeMem, Width, Count, &DestOffset); + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + Status = PciIoVerifyBarAccess (PciIoDevice, SrcBarIndex, PciBarTypeMem, Width, Count, &SrcOffset); + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + Status = PciIoDevice->PciRootBridgeIo->CopyMem ( + PciIoDevice->PciRootBridgeIo, + (EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_WIDTH) Width, + DestOffset, + SrcOffset, + Count + ); + + if (EFI_ERROR (Status)) { + ReportErrorStatusCode (PciIoDevice, EFI_IO_BUS_PCI | EFI_IOB_EC_CONTROLLER_ERROR); + } + + return Status; +} + +/** + Provides the PCI controller-specific addresses needed to access system memory. + + @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. + @param Operation Indicates if the bus master is going to read or write to system memory. + @param HostAddress The system memory address to map to the PCI controller. + @param NumberOfBytes On input the number of bytes to map. On output the number of bytes + that were mapped. + @param DeviceAddress The resulting map address for the bus master PCI controller to use to + access the hosts HostAddress. + @param Mapping A resulting value to pass to Unmap(). + + @retval EFI_SUCCESS The range was mapped for the returned NumberOfBytes. + @retval EFI_UNSUPPORTED The HostAddress cannot be mapped as a common buffer. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval EFI_DEVICE_ERROR The system hardware could not map the requested address. + +**/ +EFI_STATUS +EFIAPI +PciIoMap ( + IN EFI_PCI_IO_PROTOCOL *This, + IN EFI_PCI_IO_PROTOCOL_OPERATION Operation, + IN VOID *HostAddress, + IN OUT UINTN *NumberOfBytes, + OUT EFI_PHYSICAL_ADDRESS *DeviceAddress, + OUT VOID **Mapping + ) +{ + EFI_STATUS Status; + PCI_IO_DEVICE *PciIoDevice; + + PciIoDevice = PCI_IO_DEVICE_FROM_PCI_IO_THIS (This); + + if (Operation < 0 || Operation >= EfiPciIoOperationMaximum) { + return EFI_INVALID_PARAMETER; + } + + if (HostAddress == NULL || NumberOfBytes == NULL || DeviceAddress == NULL || Mapping == NULL) { + return EFI_INVALID_PARAMETER; + } + + if ((PciIoDevice->Attributes & EFI_PCI_IO_ATTRIBUTE_DUAL_ADDRESS_CYCLE) != 0) { + Operation = (EFI_PCI_IO_PROTOCOL_OPERATION) (Operation + EfiPciOperationBusMasterRead64); + } + + Status = PciIoDevice->PciRootBridgeIo->Map ( + PciIoDevice->PciRootBridgeIo, + (EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_OPERATION) Operation, + HostAddress, + NumberOfBytes, + DeviceAddress, + Mapping + ); + + if (EFI_ERROR (Status)) { + ReportErrorStatusCode (PciIoDevice, EFI_IO_BUS_PCI | EFI_IOB_EC_CONTROLLER_ERROR); + } + + return Status; +} + +/** + Completes the Map() operation and releases any corresponding resources. + + @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. + @param Mapping The mapping value returned from Map(). + + @retval EFI_SUCCESS The range was unmapped. + @retval EFI_DEVICE_ERROR The data was not committed to the target system memory. + +**/ +EFI_STATUS +EFIAPI +PciIoUnmap ( + IN EFI_PCI_IO_PROTOCOL *This, + IN VOID *Mapping + ) +{ + EFI_STATUS Status; + PCI_IO_DEVICE *PciIoDevice; + + PciIoDevice = PCI_IO_DEVICE_FROM_PCI_IO_THIS (This); + + Status = PciIoDevice->PciRootBridgeIo->Unmap ( + PciIoDevice->PciRootBridgeIo, + Mapping + ); + + if (EFI_ERROR (Status)) { + ReportErrorStatusCode (PciIoDevice, EFI_IO_BUS_PCI | EFI_IOB_EC_CONTROLLER_ERROR); + } + + return Status; +} + +/** + Allocates pages that are suitable for an EfiPciIoOperationBusMasterCommonBuffer + mapping. + + @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. + @param Type This parameter is not used and must be ignored. + @param MemoryType The type of memory to allocate, EfiBootServicesData or + EfiRuntimeServicesData. + @param Pages The number of pages to allocate. + @param HostAddress A pointer to store the base system memory address of the + allocated range. + @param Attributes The requested bit mask of attributes for the allocated range. + + @retval EFI_SUCCESS The requested memory pages were allocated. + @retval EFI_UNSUPPORTED Attributes is unsupported. The only legal attribute bits are + MEMORY_WRITE_COMBINE and MEMORY_CACHED. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES The memory pages could not be allocated. + +**/ +EFI_STATUS +EFIAPI +PciIoAllocateBuffer ( + IN EFI_PCI_IO_PROTOCOL *This, + IN EFI_ALLOCATE_TYPE Type, + IN EFI_MEMORY_TYPE MemoryType, + IN UINTN Pages, + OUT VOID **HostAddress, + IN UINT64 Attributes + ) +{ + EFI_STATUS Status; + PCI_IO_DEVICE *PciIoDevice; + + if ((Attributes & + (~(EFI_PCI_ATTRIBUTE_MEMORY_WRITE_COMBINE | EFI_PCI_ATTRIBUTE_MEMORY_CACHED))) != 0){ + return EFI_UNSUPPORTED; + } + + PciIoDevice = PCI_IO_DEVICE_FROM_PCI_IO_THIS (This); + + if ((PciIoDevice->Attributes & EFI_PCI_IO_ATTRIBUTE_DUAL_ADDRESS_CYCLE) != 0) { + Attributes |= EFI_PCI_ATTRIBUTE_DUAL_ADDRESS_CYCLE; + } + + Status = PciIoDevice->PciRootBridgeIo->AllocateBuffer ( + PciIoDevice->PciRootBridgeIo, + Type, + MemoryType, + Pages, + HostAddress, + Attributes + ); + + if (EFI_ERROR (Status)) { + ReportErrorStatusCode (PciIoDevice, EFI_IO_BUS_PCI | EFI_IOB_EC_CONTROLLER_ERROR); + } + + return Status; +} + +/** + Frees memory that was allocated with AllocateBuffer(). + + @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. + @param Pages The number of pages to free. + @param HostAddress The base system memory address of the allocated range. + + @retval EFI_SUCCESS The requested memory pages were freed. + @retval EFI_INVALID_PARAMETER The memory range specified by HostAddress and Pages + was not allocated with AllocateBuffer(). + +**/ +EFI_STATUS +EFIAPI +PciIoFreeBuffer ( + IN EFI_PCI_IO_PROTOCOL *This, + IN UINTN Pages, + IN VOID *HostAddress + ) +{ + EFI_STATUS Status; + PCI_IO_DEVICE *PciIoDevice; + + PciIoDevice = PCI_IO_DEVICE_FROM_PCI_IO_THIS (This); + + Status = PciIoDevice->PciRootBridgeIo->FreeBuffer ( + PciIoDevice->PciRootBridgeIo, + Pages, + HostAddress + ); + + if (EFI_ERROR (Status)) { + ReportErrorStatusCode (PciIoDevice, EFI_IO_BUS_PCI | EFI_IOB_EC_CONTROLLER_ERROR); + } + + return Status; +} + +/** + Flushes all PCI posted write transactions from a PCI host bridge to system memory. + + @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. + + @retval EFI_SUCCESS The PCI posted write transactions were flushed from the PCI host + bridge to system memory. + @retval EFI_DEVICE_ERROR The PCI posted write transactions were not flushed from the PCI + host bridge due to a hardware error. + +**/ +EFI_STATUS +EFIAPI +PciIoFlush ( + IN EFI_PCI_IO_PROTOCOL *This + ) +{ + EFI_STATUS Status; + PCI_IO_DEVICE *PciIoDevice; + + PciIoDevice = PCI_IO_DEVICE_FROM_PCI_IO_THIS (This); + + Status = PciIoDevice->PciRootBridgeIo->Flush ( + PciIoDevice->PciRootBridgeIo + ); + if (EFI_ERROR (Status)) { + ReportErrorStatusCode (PciIoDevice, EFI_IO_BUS_PCI | EFI_IOB_EC_CONTROLLER_ERROR); + } + + return Status; +} + +/** + Retrieves this PCI controller's current PCI bus number, device number, and function number. + + @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. + @param SegmentNumber The PCI controller's current PCI segment number. + @param BusNumber The PCI controller's current PCI bus number. + @param DeviceNumber The PCI controller's current PCI device number. + @param FunctionNumber The PCI controller's current PCI function number. + + @retval EFI_SUCCESS The PCI controller location was returned. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + +**/ +EFI_STATUS +EFIAPI +PciIoGetLocation ( + IN EFI_PCI_IO_PROTOCOL *This, + OUT UINTN *Segment, + OUT UINTN *Bus, + OUT UINTN *Device, + OUT UINTN *Function + ) +{ + PCI_IO_DEVICE *PciIoDevice; + + PciIoDevice = PCI_IO_DEVICE_FROM_PCI_IO_THIS (This); + + if (Segment == NULL || Bus == NULL || Device == NULL || Function == NULL) { + return EFI_INVALID_PARAMETER; + } + + *Segment = PciIoDevice->PciRootBridgeIo->SegmentNumber; + *Bus = PciIoDevice->BusNumber; + *Device = PciIoDevice->DeviceNumber; + *Function = PciIoDevice->FunctionNumber; + + return EFI_SUCCESS; +} + +/** + Check BAR type for PCI resource. + + @param PciIoDevice PCI device instance. + @param BarIndex The BAR index of the standard PCI Configuration header to use as the + base address for the memory or I/O operation to perform. + @param BarType Memory or I/O. + + @retval TRUE Pci device's bar type is same with input BarType. + @retval TRUE Pci device's bar type is not same with input BarType. + +**/ +BOOLEAN +CheckBarType ( + IN PCI_IO_DEVICE *PciIoDevice, + IN UINT8 BarIndex, + IN PCI_BAR_TYPE BarType + ) +{ + switch (BarType) { + + case PciBarTypeMem: + + if (PciIoDevice->PciBar[BarIndex].BarType != PciBarTypeMem32 && + PciIoDevice->PciBar[BarIndex].BarType != PciBarTypePMem32 && + PciIoDevice->PciBar[BarIndex].BarType != PciBarTypePMem64 && + PciIoDevice->PciBar[BarIndex].BarType != PciBarTypeMem64 ) { + return FALSE; + } + + return TRUE; + + case PciBarTypeIo: + if (PciIoDevice->PciBar[BarIndex].BarType != PciBarTypeIo32 && + PciIoDevice->PciBar[BarIndex].BarType != PciBarTypeIo16){ + return FALSE; + } + + return TRUE; + + default: + break; + } + + return FALSE; +} + +/** + Set/Disable new attributes to a Root Bridge. + + @param PciIoDevice Pci device instance. + @param Attributes New attribute want to be set. + @param Operation Set or Disable. + + @retval EFI_UNSUPPORTED If root bridge does not support change attribute. + @retval EFI_SUCCESS Successfully set new attributs. + +**/ +EFI_STATUS +ModifyRootBridgeAttributes ( + IN PCI_IO_DEVICE *PciIoDevice, + IN UINT64 Attributes, + IN EFI_PCI_IO_PROTOCOL_ATTRIBUTE_OPERATION Operation + ) +{ + UINT64 PciRootBridgeSupports; + UINT64 PciRootBridgeAttributes; + UINT64 NewPciRootBridgeAttributes; + EFI_STATUS Status; + + // + // Get the current attributes of this PCI device's PCI Root Bridge + // + Status = PciIoDevice->PciRootBridgeIo->GetAttributes ( + PciIoDevice->PciRootBridgeIo, + &PciRootBridgeSupports, + &PciRootBridgeAttributes + ); + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + // + // Record the new attribute of the Root Bridge + // + if (Operation == EfiPciIoAttributeOperationEnable) { + NewPciRootBridgeAttributes = PciRootBridgeAttributes | Attributes; + } else { + NewPciRootBridgeAttributes = PciRootBridgeAttributes & (~Attributes); + } + + // + // Call the PCI Root Bridge to attempt to modify the attributes + // + if ((NewPciRootBridgeAttributes ^ PciRootBridgeAttributes) != 0) { + + Status = PciIoDevice->PciRootBridgeIo->SetAttributes ( + PciIoDevice->PciRootBridgeIo, + NewPciRootBridgeAttributes, + NULL, + NULL + ); + if (EFI_ERROR (Status)) { + // + // The PCI Root Bridge could not modify the attributes, so return the error. + // + return EFI_UNSUPPORTED; + } + } + + // + // Also update the attributes for this Root Bridge structure + // + PciIoDevice->Attributes = NewPciRootBridgeAttributes; + + return EFI_SUCCESS; +} + +/** + Check whether this device can be enable/disable to snoop. + + @param PciIoDevice Pci device instance. + @param Operation Enable/Disable. + + @retval EFI_UNSUPPORTED Pci device is not GFX device or not support snoop. + @retval EFI_SUCCESS Snoop can be supported. + +**/ +EFI_STATUS +SupportPaletteSnoopAttributes ( + IN PCI_IO_DEVICE *PciIoDevice, + IN EFI_PCI_IO_PROTOCOL_ATTRIBUTE_OPERATION Operation + ) +{ + PCI_IO_DEVICE *Temp; + UINT16 VGACommand; + + // + // Snoop attribute can be only modified by GFX + // + if (!IS_PCI_GFX (&PciIoDevice->Pci)) { + return EFI_UNSUPPORTED; + } + + // + // Get the boot VGA on the same segement + // + Temp = ActiveVGADeviceOnTheSameSegment (PciIoDevice); + + if (Temp == NULL) { + // + // If there is no VGA device on the segement, set + // this graphics card to decode the palette range + // + return EFI_SUCCESS; + } + + // + // Check these two agents are on the same path + // + if (!PciDevicesOnTheSamePath (Temp, PciIoDevice)) { + // + // they are not on the same path, so snoop can be enabled or disabled + // + return EFI_SUCCESS; + } + // + // Check if they are on the same bus + // + if (Temp->Parent == PciIoDevice->Parent) { + + PCI_READ_COMMAND_REGISTER (Temp, &VGACommand); + + // + // If they are on the same bus, either one can + // be set to snoop, the other set to decode + // + if ((VGACommand & EFI_PCI_COMMAND_VGA_PALETTE_SNOOP) != 0) { + // + // VGA has set to snoop, so GFX can be only set to disable snoop + // + if (Operation == EfiPciIoAttributeOperationEnable) { + return EFI_UNSUPPORTED; + } + } else { + // + // VGA has disabled to snoop, so GFX can be only enabled + // + if (Operation == EfiPciIoAttributeOperationDisable) { + return EFI_UNSUPPORTED; + } + } + + return EFI_SUCCESS; + } + + // + // If they are on the same path but on the different bus + // The first agent is set to snoop, the second one set to + // decode + // + + if (Temp->BusNumber < PciIoDevice->BusNumber) { + // + // GFX should be set to decode + // + if (Operation == EfiPciIoAttributeOperationDisable) { + PCI_ENABLE_COMMAND_REGISTER (Temp, EFI_PCI_COMMAND_VGA_PALETTE_SNOOP); + Temp->Attributes |= EFI_PCI_COMMAND_VGA_PALETTE_SNOOP; + } else { + return EFI_UNSUPPORTED; + } + + } else { + // + // GFX should be set to snoop + // + if (Operation == EfiPciIoAttributeOperationEnable) { + PCI_DISABLE_COMMAND_REGISTER (Temp, EFI_PCI_COMMAND_VGA_PALETTE_SNOOP); + Temp->Attributes &= (~EFI_PCI_COMMAND_VGA_PALETTE_SNOOP); + } else { + return EFI_UNSUPPORTED; + } + + } + + return EFI_SUCCESS; +} + +/** + Performs an operation on the attributes that this PCI controller supports. The operations include + getting the set of supported attributes, retrieving the current attributes, setting the current + attributes, enabling attributes, and disabling attributes. + + @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. + @param Operation The operation to perform on the attributes for this PCI controller. + @param Attributes The mask of attributes that are used for Set, Enable, and Disable + operations. + @param Result A pointer to the result mask of attributes that are returned for the Get + and Supported operations. + + @retval EFI_SUCCESS The operation on the PCI controller's attributes was completed. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_UNSUPPORTED one or more of the bits set in + Attributes are not supported by this PCI controller or one of + its parent bridges when Operation is Set, Enable or Disable. + +**/ +EFI_STATUS +EFIAPI +PciIoAttributes ( + IN EFI_PCI_IO_PROTOCOL * This, + IN EFI_PCI_IO_PROTOCOL_ATTRIBUTE_OPERATION Operation, + IN UINT64 Attributes, + OUT UINT64 *Result OPTIONAL + ) +{ + EFI_STATUS Status; + + PCI_IO_DEVICE *PciIoDevice; + PCI_IO_DEVICE *UpStreamBridge; + PCI_IO_DEVICE *Temp; + + UINT64 Supports; + UINT64 UpStreamAttributes; + UINT16 BridgeControl; + UINT16 Command; + + PciIoDevice = PCI_IO_DEVICE_FROM_PCI_IO_THIS (This); + + switch (Operation) { + case EfiPciIoAttributeOperationGet: + if (Result == NULL) { + return EFI_INVALID_PARAMETER; + } + + *Result = PciIoDevice->Attributes; + return EFI_SUCCESS; + + case EfiPciIoAttributeOperationSupported: + if (Result == NULL) { + return EFI_INVALID_PARAMETER; + } + + *Result = PciIoDevice->Supports; + return EFI_SUCCESS; + + case EfiPciIoAttributeOperationSet: + Status = PciIoDevice->PciIo.Attributes ( + &(PciIoDevice->PciIo), + EfiPciIoAttributeOperationEnable, + Attributes, + NULL + ); + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + Status = PciIoDevice->PciIo.Attributes ( + &(PciIoDevice->PciIo), + EfiPciIoAttributeOperationDisable, + (~Attributes) & (PciIoDevice->Supports), + NULL + ); + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + return EFI_SUCCESS; + + case EfiPciIoAttributeOperationEnable: + case EfiPciIoAttributeOperationDisable: + break; + + default: + return EFI_INVALID_PARAMETER; + } + // + // Just a trick for ENABLE attribute + // EFI_PCI_DEVICE_ENABLE is not defined in UEFI spec, which is the internal usage. + // So, this logic doesn't confrom to UEFI spec, which should be removed. + // But this trick logic is still kept for some binary drivers that depend on it. + // + if ((Attributes & EFI_PCI_DEVICE_ENABLE) == EFI_PCI_DEVICE_ENABLE) { + Attributes &= (PciIoDevice->Supports); + + // + // Raise the EFI_P_PC_ENABLE Status code + // + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_PROGRESS_CODE, + EFI_IO_BUS_PCI | EFI_P_PC_ENABLE, + PciIoDevice->DevicePath + ); + } + + // + // If no attributes can be supported, then return. + // Otherwise, set the attributes that it can support. + // + Supports = (PciIoDevice->Supports) & Attributes; + if (Supports != Attributes) { + return EFI_UNSUPPORTED; + } + + // + // For Root Bridge, just call RootBridgeIo to set attributes; + // + if (PciIoDevice->Parent == NULL) { + Status = ModifyRootBridgeAttributes (PciIoDevice, Attributes, Operation); + return Status; + } + + Command = 0; + BridgeControl = 0; + + // + // Check VGA and VGA16, they can not be set at the same time + // + if (((Attributes & EFI_PCI_IO_ATTRIBUTE_VGA_IO) != 0 && + (Attributes & EFI_PCI_IO_ATTRIBUTE_VGA_IO_16) != 0) || + ((Attributes & EFI_PCI_IO_ATTRIBUTE_VGA_IO) != 0 && + (Attributes & EFI_PCI_IO_ATTRIBUTE_VGA_PALETTE_IO_16) != 0) || + ((Attributes & EFI_PCI_IO_ATTRIBUTE_VGA_PALETTE_IO) != 0 && + (Attributes & EFI_PCI_IO_ATTRIBUTE_VGA_IO_16) != 0) || + ((Attributes & EFI_PCI_IO_ATTRIBUTE_VGA_PALETTE_IO) != 0 && + (Attributes & EFI_PCI_IO_ATTRIBUTE_VGA_PALETTE_IO_16) != 0) ) { + return EFI_UNSUPPORTED; + } + + // + // For PPB & P2C, set relevant attribute bits + // + if (IS_PCI_BRIDGE (&PciIoDevice->Pci) || IS_CARDBUS_BRIDGE (&PciIoDevice->Pci)) { + + if ((Attributes & (EFI_PCI_IO_ATTRIBUTE_VGA_IO | EFI_PCI_IO_ATTRIBUTE_VGA_IO_16)) != 0) { + BridgeControl |= EFI_PCI_BRIDGE_CONTROL_VGA; + } + + if ((Attributes & EFI_PCI_IO_ATTRIBUTE_ISA_IO) != 0) { + BridgeControl |= EFI_PCI_BRIDGE_CONTROL_ISA; + } + + if ((Attributes & (EFI_PCI_IO_ATTRIBUTE_VGA_PALETTE_IO | EFI_PCI_IO_ATTRIBUTE_VGA_PALETTE_IO_16)) != 0) { + Command |= EFI_PCI_IO_ATTRIBUTE_VGA_PALETTE_IO; + } + + if ((Attributes & (EFI_PCI_IO_ATTRIBUTE_VGA_PALETTE_IO_16 | EFI_PCI_IO_ATTRIBUTE_VGA_IO_16)) != 0) { + BridgeControl |= EFI_PCI_BRIDGE_CONTROL_VGA_16; + } + + } else { + // + // Do with the attributes on VGA + // Only for VGA's legacy resource, we just can enable once. + // + if ((Attributes & + (EFI_PCI_IO_ATTRIBUTE_VGA_IO | + EFI_PCI_IO_ATTRIBUTE_VGA_IO_16 | + EFI_PCI_IO_ATTRIBUTE_VGA_MEMORY)) != 0) { + // + // Check if a VGA has been enabled before enabling a new one + // + if (Operation == EfiPciIoAttributeOperationEnable) { + // + // Check if there have been an active VGA device on the same segment + // + Temp = ActiveVGADeviceOnTheSameSegment (PciIoDevice); + if (Temp != NULL && Temp != PciIoDevice) { + // + // An active VGA has been detected, so can not enable another + // + return EFI_UNSUPPORTED; + } + } + } + + // + // Do with the attributes on GFX + // + if ((Attributes & (EFI_PCI_IO_ATTRIBUTE_VGA_PALETTE_IO | EFI_PCI_IO_ATTRIBUTE_VGA_PALETTE_IO_16)) != 0) { + + if (Operation == EfiPciIoAttributeOperationEnable) { + // + // Check if snoop can be enabled in current configuration + // + Status = SupportPaletteSnoopAttributes (PciIoDevice, Operation); + + if (EFI_ERROR (Status)) { + + // + // Enable operation is forbidden, so mask the bit in attributes + // so as to keep consistent with the actual Status + // + // Attributes &= (~EFI_PCI_IO_ATTRIBUTE_VGA_PALETTE_IO); + // + // + // + return EFI_UNSUPPORTED; + + } + } + + // + // It can be supported, so get ready to set the bit + // + Command |= EFI_PCI_COMMAND_VGA_PALETTE_SNOOP; + } + } + + if ((Attributes & EFI_PCI_IO_ATTRIBUTE_IO) != 0) { + Command |= EFI_PCI_COMMAND_IO_SPACE; + } + + if ((Attributes & EFI_PCI_IO_ATTRIBUTE_MEMORY) != 0) { + Command |= EFI_PCI_COMMAND_MEMORY_SPACE; + } + + if ((Attributes & EFI_PCI_IO_ATTRIBUTE_BUS_MASTER) != 0) { + Command |= EFI_PCI_COMMAND_BUS_MASTER; + } + // + // The upstream bridge should be also set to revelant attribute + // expect for IO, Mem and BusMaster + // + UpStreamAttributes = Attributes & + (~(EFI_PCI_IO_ATTRIBUTE_IO | + EFI_PCI_IO_ATTRIBUTE_MEMORY | + EFI_PCI_IO_ATTRIBUTE_BUS_MASTER + ) + ); + UpStreamBridge = PciIoDevice->Parent; + + if (Operation == EfiPciIoAttributeOperationEnable) { + // + // Enable relevant attributes to command register and bridge control register + // + Status = PCI_ENABLE_COMMAND_REGISTER (PciIoDevice, Command); + if (BridgeControl != 0) { + Status = PCI_ENABLE_BRIDGE_CONTROL_REGISTER (PciIoDevice, BridgeControl); + } + + PciIoDevice->Attributes |= Attributes; + + // + // Enable attributes of the upstream bridge + // + Status = UpStreamBridge->PciIo.Attributes ( + &(UpStreamBridge->PciIo), + EfiPciIoAttributeOperationEnable, + UpStreamAttributes, + NULL + ); + } else { + + // + // Disable relevant attributes to command register and bridge control register + // + Status = PCI_DISABLE_COMMAND_REGISTER (PciIoDevice, Command); + if (BridgeControl != 0) { + Status = PCI_DISABLE_BRIDGE_CONTROL_REGISTER (PciIoDevice, BridgeControl); + } + + PciIoDevice->Attributes &= (~Attributes); + Status = EFI_SUCCESS; + + } + + if (EFI_ERROR (Status)) { + ReportErrorStatusCode (PciIoDevice, EFI_IO_BUS_PCI | EFI_IOB_EC_CONTROLLER_ERROR); + } + + return Status; +} + +/** + Gets the attributes that this PCI controller supports setting on a BAR using + SetBarAttributes(), and retrieves the list of resource descriptors for a BAR. + + @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. + @param BarIndex The BAR index of the standard PCI Configuration header to use as the + base address for resource range. The legal range for this field is 0..5. + @param Supports A pointer to the mask of attributes that this PCI controller supports + setting for this BAR with SetBarAttributes(). + @param Resources A pointer to the ACPI 2.0 resource descriptors that describe the current + configuration of this BAR of the PCI controller. + + @retval EFI_SUCCESS If Supports is not NULL, then the attributes that the PCI + controller supports are returned in Supports. If Resources + is not NULL, then the ACPI 2.0 resource descriptors that the PCI + controller is currently using are returned in Resources. + @retval EFI_INVALID_PARAMETER Both Supports and Attributes are NULL. + @retval EFI_UNSUPPORTED BarIndex not valid for this PCI controller. + @retval EFI_OUT_OF_RESOURCES There are not enough resources available to allocate + Resources. + +**/ +EFI_STATUS +EFIAPI +PciIoGetBarAttributes ( + IN EFI_PCI_IO_PROTOCOL * This, + IN UINT8 BarIndex, + OUT UINT64 *Supports, OPTIONAL + OUT VOID **Resources OPTIONAL + ) +{ + + UINT8 *Configuration; + UINT8 NumConfig; + PCI_IO_DEVICE *PciIoDevice; + EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Ptr; + EFI_ACPI_END_TAG_DESCRIPTOR *PtrEnd; + + NumConfig = 0; + + PciIoDevice = PCI_IO_DEVICE_FROM_PCI_IO_THIS (This); + + if (Supports == NULL && Resources == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (BarIndex >= PCI_MAX_BAR) { + return EFI_UNSUPPORTED; + } + + // + // This driver does not support modifications to the WRITE_COMBINE or + // CACHED attributes for BAR ranges. + // + if (Supports != NULL) { + *Supports = PciIoDevice->Supports & EFI_PCI_IO_ATTRIBUTE_MEMORY_CACHED & EFI_PCI_IO_ATTRIBUTE_MEMORY_WRITE_COMBINE; + } + + if (Resources != NULL) { + + if (PciIoDevice->PciBar[BarIndex].BarType != PciBarTypeUnknown) { + NumConfig = 1; + } + + Configuration = AllocateZeroPool (sizeof (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR) * NumConfig + sizeof (EFI_ACPI_END_TAG_DESCRIPTOR)); + if (Configuration == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Ptr = (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *) Configuration; + + if (NumConfig == 1) { + Ptr->Desc = ACPI_ADDRESS_SPACE_DESCRIPTOR; + Ptr->Len = sizeof (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR) - 3; + + Ptr->AddrRangeMin = PciIoDevice->PciBar[BarIndex].BaseAddress; + Ptr->AddrLen = PciIoDevice->PciBar[BarIndex].Length; + Ptr->AddrRangeMax = PciIoDevice->PciBar[BarIndex].Alignment; + + switch (PciIoDevice->PciBar[BarIndex].BarType) { + case PciBarTypeIo16: + case PciBarTypeIo32: + // + // Io + // + Ptr->ResType = ACPI_ADDRESS_SPACE_TYPE_IO; + break; + + case PciBarTypeMem32: + // + // Mem + // + Ptr->ResType = ACPI_ADDRESS_SPACE_TYPE_MEM; + // + // 32 bit + // + Ptr->AddrSpaceGranularity = 32; + break; + + case PciBarTypePMem32: + // + // Mem + // + Ptr->ResType = ACPI_ADDRESS_SPACE_TYPE_MEM; + // + // prefechable + // + Ptr->SpecificFlag = 0x6; + // + // 32 bit + // + Ptr->AddrSpaceGranularity = 32; + break; + + case PciBarTypeMem64: + // + // Mem + // + Ptr->ResType = ACPI_ADDRESS_SPACE_TYPE_MEM; + // + // 64 bit + // + Ptr->AddrSpaceGranularity = 64; + break; + + case PciBarTypePMem64: + // + // Mem + // + Ptr->ResType = ACPI_ADDRESS_SPACE_TYPE_MEM; + // + // prefechable + // + Ptr->SpecificFlag = 0x6; + // + // 64 bit + // + Ptr->AddrSpaceGranularity = 64; + break; + + default: + break; + } + + Ptr = (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *) ((UINT8 *) Ptr + sizeof (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR)); + } + + // + // put the checksum + // + PtrEnd = (EFI_ACPI_END_TAG_DESCRIPTOR *) ((UINT8 *) Ptr); + PtrEnd->Desc = ACPI_END_TAG_DESCRIPTOR; + PtrEnd->Checksum = 0; + + *Resources = Configuration; + } + + return EFI_SUCCESS; +} + +/** + Sets the attributes for a range of a BAR on a PCI controller. + + @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. + @param Attributes The mask of attributes to set for the resource range specified by + BarIndex, Offset, and Length. + @param BarIndex The BAR index of the standard PCI Configuration header to use as the + base address for resource range. The legal range for this field is 0..5. + @param Offset A pointer to the BAR relative base address of the resource range to be + modified by the attributes specified by Attributes. + @param Length A pointer to the length of the resource range to be modified by the + attributes specified by Attributes. + + @retval EFI_SUCCESS The set of attributes specified by Attributes for the resource + range specified by BarIndex, Offset, and Length were + set on the PCI controller, and the actual resource range is returned + in Offset and Length. + @retval EFI_INVALID_PARAMETER Offset or Length is NULL. + @retval EFI_UNSUPPORTED BarIndex not valid for this PCI controller. + @retval EFI_OUT_OF_RESOURCES There are not enough resources to set the attributes on the + resource range specified by BarIndex, Offset, and + Length. + +**/ +EFI_STATUS +EFIAPI +PciIoSetBarAttributes ( + IN EFI_PCI_IO_PROTOCOL *This, + IN UINT64 Attributes, + IN UINT8 BarIndex, + IN OUT UINT64 *Offset, + IN OUT UINT64 *Length + ) +{ + EFI_STATUS Status; + PCI_IO_DEVICE *PciIoDevice; + UINT64 NonRelativeOffset; + UINT64 Supports; + + PciIoDevice = PCI_IO_DEVICE_FROM_PCI_IO_THIS (This); + + // + // Make sure Offset and Length are not NULL + // + if (Offset == NULL || Length == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (PciIoDevice->PciBar[BarIndex].BarType == PciBarTypeUnknown) { + return EFI_UNSUPPORTED; + } + // + // This driver does not support setting the WRITE_COMBINE or the CACHED attributes. + // If Attributes is not 0, then return EFI_UNSUPPORTED. + // + Supports = PciIoDevice->Supports & EFI_PCI_IO_ATTRIBUTE_MEMORY_CACHED & EFI_PCI_IO_ATTRIBUTE_MEMORY_WRITE_COMBINE; + + if (Attributes != (Attributes & Supports)) { + return EFI_UNSUPPORTED; + } + // + // Attributes must be supported. Make sure the BAR range describd by BarIndex, Offset, and + // Length are valid for this PCI device. + // + NonRelativeOffset = *Offset; + Status = PciIoVerifyBarAccess ( + PciIoDevice, + BarIndex, + PciBarTypeMem, + EfiPciIoWidthUint8, + (UINT32) *Length, + &NonRelativeOffset + ); + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + return EFI_SUCCESS; +} + +/** + Program parent bridge's attribute recurrently. + + @param PciIoDevice Child Pci device instance + @param Operation The operation to perform on the attributes for this PCI controller. + @param Attributes The mask of attributes that are used for Set, Enable, and Disable + operations. + + @retval EFI_SUCCESS The operation on the PCI controller's attributes was completed. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_UNSUPPORTED one or more of the bits set in + Attributes are not supported by this PCI controller or one of + its parent bridges when Operation is Set, Enable or Disable. + +**/ +EFI_STATUS +UpStreamBridgesAttributes ( + IN PCI_IO_DEVICE *PciIoDevice, + IN EFI_PCI_IO_PROTOCOL_ATTRIBUTE_OPERATION Operation, + IN UINT64 Attributes + ) +{ + PCI_IO_DEVICE *Parent; + EFI_PCI_IO_PROTOCOL *PciIo; + + Parent = PciIoDevice->Parent; + + while (Parent != NULL && IS_PCI_BRIDGE (&Parent->Pci)) { + + // + // Get the PciIo Protocol + // + PciIo = &Parent->PciIo; + + PciIo->Attributes (PciIo, Operation, Attributes, NULL); + + Parent = Parent->Parent; + } + + return EFI_SUCCESS; +} + +/** + Test whether two Pci devices has same parent bridge. + + @param PciDevice1 The first pci device for testing. + @param PciDevice2 The second pci device for testing. + + @retval TRUE Two Pci device has the same parent bridge. + @retval FALSE Two Pci device has not the same parent bridge. + +**/ +BOOLEAN +PciDevicesOnTheSamePath ( + IN PCI_IO_DEVICE *PciDevice1, + IN PCI_IO_DEVICE *PciDevice2 + ) +{ + BOOLEAN Existed1; + BOOLEAN Existed2; + + if (PciDevice1->Parent == PciDevice2->Parent) { + return TRUE; + } + + Existed1 = PciDeviceExisted (PciDevice1->Parent, PciDevice2); + Existed2 = PciDeviceExisted (PciDevice2->Parent, PciDevice1); + + return (BOOLEAN) (Existed1 || Existed2); +} + diff --git a/MdeModulePkg/Bus/Pci/PciBusDxe/PciIo.h b/MdeModulePkg/Bus/Pci/PciBusDxe/PciIo.h new file mode 100644 index 0000000000..fbfd2b4ff9 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/PciBusDxe/PciIo.h @@ -0,0 +1,687 @@ +/** @file + EFI PCI IO protocol functions declaration for PCI Bus module. + +Copyright (c) 2006 - 2009, Intel Corporation +All rights reserved. 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. + +**/ + +#ifndef _EFI_PCI_IO_PROTOCOL_H_ +#define _EFI_PCI_IO_PROTOCOL_H_ + +/** + Initializes a PCI I/O Instance. + + @param PciIoDevice Pci device instance. + +**/ +VOID +InitializePciIoInstance ( + IN PCI_IO_DEVICE *PciIoDevice + ); + +/** + Verifies access to a PCI Base Address Register (BAR). + + @param PciIoDevice Pci device instance. + @param BarIndex The BAR index of the standard PCI Configuration header to use as the + base address for the memory or I/O operation to perform. + @param Type Operation type could be memory or I/O. + @param Width Signifies the width of the memory or I/O operations. + @param Count The number of memory or I/O operations to perform. + @param Offset The offset within the PCI configuration space for the PCI controller. + + @retval EFI_INVALID_PARAMETER Invalid Width/BarIndex or Bar type. + @retval EFI_SUCCESS Successfully verified. + +**/ +EFI_STATUS +PciIoVerifyBarAccess ( + IN PCI_IO_DEVICE *PciIoDevice, + IN UINT8 BarIndex, + IN PCI_BAR_TYPE Type, + IN IN EFI_PCI_IO_PROTOCOL_WIDTH Width, + IN IN UINTN Count, + IN UINT64 *Offset + ); + +/** + Verifies access to a PCI Configuration Header. + + @param PciIoDevice Pci device instance. + @param Width Signifies the width of the memory or I/O operations. + @param Count The number of memory or I/O operations to perform. + @param Offset The offset within the PCI configuration space for the PCI controller. + + @retval EFI_INVALID_PARAMETER Invalid Width + @retval EFI_UNSUPPORTED Offset overflowed. + @retval EFI_SUCCESS Successfully verified. + +**/ +EFI_STATUS +PciIoVerifyConfigAccess ( + IN PCI_IO_DEVICE *PciIoDevice, + IN EFI_PCI_IO_PROTOCOL_WIDTH Width, + IN UINTN Count, + IN UINT64 *Offset + ); + +/** + Reads from the memory space of a PCI controller. Returns either when the polling exit criteria is + satisfied or after a defined duration. + + @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. + @param Width Signifies the width of the memory or I/O operations. + @param BarIndex The BAR index of the standard PCI Configuration header to use as the + base address for the memory operation to perform. + @param Offset The offset within the selected BAR to start the memory operation. + @param Mask Mask used for the polling criteria. + @param Value The comparison value used for the polling exit criteria. + @param Delay The number of 100 ns units to poll. + @param Result Pointer to the last value read from the memory location. + + @retval EFI_SUCCESS The last data returned from the access matched the poll exit criteria. + @retval EFI_UNSUPPORTED BarIndex not valid for this PCI controller. + @retval EFI_UNSUPPORTED Offset is not valid for the BarIndex of this PCI controller. + @retval EFI_TIMEOUT Delay expired before a match occurred. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + +**/ +EFI_STATUS +EFIAPI +PciIoPollMem ( + IN EFI_PCI_IO_PROTOCOL *This, + IN EFI_PCI_IO_PROTOCOL_WIDTH Width, + IN UINT8 BarIndex, + IN UINT64 Offset, + IN UINT64 Mask, + IN UINT64 Value, + IN UINT64 Delay, + OUT UINT64 *Result + ); + +/** + Reads from the memory space of a PCI controller. Returns either when the polling exit criteria is + satisfied or after a defined duration. + + @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. + @param Width Signifies the width of the memory or I/O operations. + @param BarIndex The BAR index of the standard PCI Configuration header to use as the + base address for the memory operation to perform. + @param Offset The offset within the selected BAR to start the memory operation. + @param Mask Mask used for the polling criteria. + @param Value The comparison value used for the polling exit criteria. + @param Delay The number of 100 ns units to poll. + @param Result Pointer to the last value read from the memory location. + + @retval EFI_SUCCESS The last data returned from the access matched the poll exit criteria. + @retval EFI_UNSUPPORTED BarIndex not valid for this PCI controller. + @retval EFI_UNSUPPORTED Offset is not valid for the BarIndex of this PCI controller. + @retval EFI_TIMEOUT Delay expired before a match occurred. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + +**/ +EFI_STATUS +EFIAPI +PciIoPollIo ( + IN EFI_PCI_IO_PROTOCOL *This, + IN EFI_PCI_IO_PROTOCOL_WIDTH Width, + IN UINT8 BarIndex, + IN UINT64 Offset, + IN UINT64 Mask, + IN UINT64 Value, + IN UINT64 Delay, + OUT UINT64 *Result + ); + +/** + Enable a PCI driver to access PCI controller registers in the PCI memory or I/O space. + + @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. + @param Width Signifies the width of the memory or I/O operations. + @param BarIndex The BAR index of the standard PCI Configuration header to use as the + base address for the memory or I/O operation to perform. + @param Offset The offset within the selected BAR to start the memory or I/O operation. + @param Count The number of memory or I/O operations to perform. + @param Buffer For read operations, the destination buffer to store the results. For write + operations, the source buffer to write data from. + + @retval EFI_SUCCESS The data was read from or written to the PCI controller. + @retval EFI_UNSUPPORTED BarIndex not valid for this PCI controller. + @retval EFI_UNSUPPORTED The address range specified by Offset, Width, and Count is not + valid for the PCI BAR specified by BarIndex. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + +**/ +EFI_STATUS +EFIAPI +PciIoMemRead ( + IN EFI_PCI_IO_PROTOCOL *This, + IN EFI_PCI_IO_PROTOCOL_WIDTH Width, + IN UINT8 BarIndex, + IN UINT64 Offset, + IN UINTN Count, + IN OUT VOID *Buffer + ); + +/** + Enable a PCI driver to access PCI controller registers in the PCI memory or I/O space. + + @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. + @param Width Signifies the width of the memory or I/O operations. + @param BarIndex The BAR index of the standard PCI Configuration header to use as the + base address for the memory or I/O operation to perform. + @param Offset The offset within the selected BAR to start the memory or I/O operation. + @param Count The number of memory or I/O operations to perform. + @param Buffer For read operations, the destination buffer to store the results. For write + operations, the source buffer to write data from. + + @retval EFI_SUCCESS The data was read from or written to the PCI controller. + @retval EFI_UNSUPPORTED BarIndex not valid for this PCI controller. + @retval EFI_UNSUPPORTED The address range specified by Offset, Width, and Count is not + valid for the PCI BAR specified by BarIndex. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + +**/ +EFI_STATUS +EFIAPI +PciIoMemWrite ( + IN EFI_PCI_IO_PROTOCOL *This, + IN EFI_PCI_IO_PROTOCOL_WIDTH Width, + IN UINT8 BarIndex, + IN UINT64 Offset, + IN UINTN Count, + IN OUT VOID *Buffer + ); + +/** + Enable a PCI driver to access PCI controller registers in the PCI memory or I/O space. + + @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. + @param Width Signifies the width of the memory or I/O operations. + @param BarIndex The BAR index of the standard PCI Configuration header to use as the + base address for the memory or I/O operation to perform. + @param Offset The offset within the selected BAR to start the memory or I/O operation. + @param Count The number of memory or I/O operations to perform. + @param Buffer For read operations, the destination buffer to store the results. For write + operations, the source buffer to write data from. + + @retval EFI_SUCCESS The data was read from or written to the PCI controller. + @retval EFI_UNSUPPORTED BarIndex not valid for this PCI controller. + @retval EFI_UNSUPPORTED The address range specified by Offset, Width, and Count is not + valid for the PCI BAR specified by BarIndex. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + +**/ +EFI_STATUS +EFIAPI +PciIoIoRead ( + IN EFI_PCI_IO_PROTOCOL *This, + IN EFI_PCI_IO_PROTOCOL_WIDTH Width, + IN UINT8 BarIndex, + IN UINT64 Offset, + IN UINTN Count, + IN OUT VOID *Buffer + ); + +/** + Enable a PCI driver to access PCI controller registers in the PCI memory or I/O space. + + @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. + @param Width Signifies the width of the memory or I/O operations. + @param BarIndex The BAR index of the standard PCI Configuration header to use as the + base address for the memory or I/O operation to perform. + @param Offset The offset within the selected BAR to start the memory or I/O operation. + @param Count The number of memory or I/O operations to perform. + @param Buffer For read operations, the destination buffer to store the results. For write + operations, the source buffer to write data from. + + @retval EFI_SUCCESS The data was read from or written to the PCI controller. + @retval EFI_UNSUPPORTED BarIndex not valid for this PCI controller. + @retval EFI_UNSUPPORTED The address range specified by Offset, Width, and Count is not + valid for the PCI BAR specified by BarIndex. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + +**/ +EFI_STATUS +EFIAPI +PciIoIoWrite ( + IN EFI_PCI_IO_PROTOCOL *This, + IN EFI_PCI_IO_PROTOCOL_WIDTH Width, + IN UINT8 BarIndex, + IN UINT64 Offset, + IN UINTN Count, + IN OUT VOID *Buffer + ); + +/** + Enable a PCI driver to access PCI controller registers in PCI configuration space. + + @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. + @param Width Signifies the width of the memory operations. + @param Offset The offset within the PCI configuration space for the PCI controller. + @param Count The number of PCI configuration operations to perform. + @param Buffer For read operations, the destination buffer to store the results. For write + operations, the source buffer to write data from. + + + @retval EFI_SUCCESS The data was read from or written to the PCI controller. + @retval EFI_UNSUPPORTED The address range specified by Offset, Width, and Count is not + valid for the PCI configuration header of the PCI controller. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval EFI_INVALID_PARAMETER Buffer is NULL or Width is invalid. + +**/ +EFI_STATUS +EFIAPI +PciIoConfigRead ( + IN EFI_PCI_IO_PROTOCOL *This, + IN EFI_PCI_IO_PROTOCOL_WIDTH Width, + IN UINT32 Offset, + IN UINTN Count, + IN OUT VOID *Buffer + ); + +/** + Enable a PCI driver to access PCI controller registers in PCI configuration space. + + @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. + @param Width Signifies the width of the memory operations. + @param Offset The offset within the PCI configuration space for the PCI controller. + @param Count The number of PCI configuration operations to perform. + @param Buffer For read operations, the destination buffer to store the results. For write + operations, the source buffer to write data from. + + + @retval EFI_SUCCESS The data was read from or written to the PCI controller. + @retval EFI_UNSUPPORTED The address range specified by Offset, Width, and Count is not + valid for the PCI configuration header of the PCI controller. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval EFI_INVALID_PARAMETER Buffer is NULL or Width is invalid. + +**/ +EFI_STATUS +EFIAPI +PciIoConfigWrite ( + IN EFI_PCI_IO_PROTOCOL *This, + IN EFI_PCI_IO_PROTOCOL_WIDTH Width, + IN UINT32 Offset, + IN UINTN Count, + IN OUT VOID *Buffer + ); + +/** + Enables a PCI driver to copy one region of PCI memory space to another region of PCI + memory space. + + @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. + @param Width Signifies the width of the memory operations. + @param DestBarIndex The BAR index in the standard PCI Configuration header to use as the + base address for the memory operation to perform. + @param DestOffset The destination offset within the BAR specified by DestBarIndex to + start the memory writes for the copy operation. + @param SrcBarIndex The BAR index in the standard PCI Configuration header to use as the + base address for the memory operation to perform. + @param SrcOffset The source offset within the BAR specified by SrcBarIndex to start + the memory reads for the copy operation. + @param Count The number of memory operations to perform. Bytes moved is Width + size * Count, starting at DestOffset and SrcOffset. + + @retval EFI_SUCCESS The data was copied from one memory region to another memory region. + @retval EFI_UNSUPPORTED DestBarIndex not valid for this PCI controller. + @retval EFI_UNSUPPORTED SrcBarIndex not valid for this PCI controller. + @retval EFI_UNSUPPORTED The address range specified by DestOffset, Width, and Count + is not valid for the PCI BAR specified by DestBarIndex. + @retval EFI_UNSUPPORTED The address range specified by SrcOffset, Width, and Count is + not valid for the PCI BAR specified by SrcBarIndex. + @retval EFI_INVALID_PARAMETER Width is invalid. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + +**/ +EFI_STATUS +EFIAPI +PciIoCopyMem ( + IN EFI_PCI_IO_PROTOCOL *This, + IN EFI_PCI_IO_PROTOCOL_WIDTH Width, + IN UINT8 DestBarIndex, + IN UINT64 DestOffset, + IN UINT8 SrcBarIndex, + IN UINT64 SrcOffset, + IN UINTN Count + ); + +/** + Provides the PCI controller-specific addresses needed to access system memory. + + @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. + @param Operation Indicates if the bus master is going to read or write to system memory. + @param HostAddress The system memory address to map to the PCI controller. + @param NumberOfBytes On input the number of bytes to map. On output the number of bytes + that were mapped. + @param DeviceAddress The resulting map address for the bus master PCI controller to use to + access the hosts HostAddress. + @param Mapping A resulting value to pass to Unmap(). + + @retval EFI_SUCCESS The range was mapped for the returned NumberOfBytes. + @retval EFI_UNSUPPORTED The HostAddress cannot be mapped as a common buffer. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + @retval EFI_DEVICE_ERROR The system hardware could not map the requested address. + +**/ +EFI_STATUS +EFIAPI +PciIoMap ( + IN EFI_PCI_IO_PROTOCOL *This, + IN EFI_PCI_IO_PROTOCOL_OPERATION Operation, + IN VOID *HostAddress, + IN OUT UINTN *NumberOfBytes, + OUT EFI_PHYSICAL_ADDRESS *DeviceAddress, + OUT VOID **Mapping + ); + +/** + Completes the Map() operation and releases any corresponding resources. + + @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. + @param Mapping The mapping value returned from Map(). + + @retval EFI_SUCCESS The range was unmapped. + @retval EFI_DEVICE_ERROR The data was not committed to the target system memory. + +**/ +EFI_STATUS +EFIAPI +PciIoUnmap ( + IN EFI_PCI_IO_PROTOCOL *This, + IN VOID *Mapping + ); + +/** + Allocates pages that are suitable for an EfiPciIoOperationBusMasterCommonBuffer + mapping. + + @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. + @param Type This parameter is not used and must be ignored. + @param MemoryType The type of memory to allocate, EfiBootServicesData or + EfiRuntimeServicesData. + @param Pages The number of pages to allocate. + @param HostAddress A pointer to store the base system memory address of the + allocated range. + @param Attributes The requested bit mask of attributes for the allocated range. + + @retval EFI_SUCCESS The requested memory pages were allocated. + @retval EFI_UNSUPPORTED Attributes is unsupported. The only legal attribute bits are + MEMORY_WRITE_COMBINE and MEMORY_CACHED. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_OUT_OF_RESOURCES The memory pages could not be allocated. + +**/ +EFI_STATUS +EFIAPI +PciIoAllocateBuffer ( + IN EFI_PCI_IO_PROTOCOL *This, + IN EFI_ALLOCATE_TYPE Type, + IN EFI_MEMORY_TYPE MemoryType, + IN UINTN Pages, + OUT VOID **HostAddress, + IN UINT64 Attributes + ); + +/** + Frees memory that was allocated with AllocateBuffer(). + + @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. + @param Pages The number of pages to free. + @param HostAddress The base system memory address of the allocated range. + + @retval EFI_SUCCESS The requested memory pages were freed. + @retval EFI_INVALID_PARAMETER The memory range specified by HostAddress and Pages + was not allocated with AllocateBuffer(). + +**/ +EFI_STATUS +EFIAPI +PciIoFreeBuffer ( + IN EFI_PCI_IO_PROTOCOL *This, + IN UINTN Pages, + IN VOID *HostAddress + ); + +/** + Flushes all PCI posted write transactions from a PCI host bridge to system memory. + + @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. + + @retval EFI_SUCCESS The PCI posted write transactions were flushed from the PCI host + bridge to system memory. + @retval EFI_DEVICE_ERROR The PCI posted write transactions were not flushed from the PCI + host bridge due to a hardware error. + +**/ +EFI_STATUS +EFIAPI +PciIoFlush ( + IN EFI_PCI_IO_PROTOCOL *This + ); + +/** + Retrieves this PCI controller's current PCI bus number, device number, and function number. + + @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. + @param SegmentNumber The PCI controller's current PCI segment number. + @param BusNumber The PCI controller's current PCI bus number. + @param DeviceNumber The PCI controller's current PCI device number. + @param FunctionNumber The PCI controller's current PCI function number. + + @retval EFI_SUCCESS The PCI controller location was returned. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + +**/ +EFI_STATUS +EFIAPI +PciIoGetLocation ( + IN EFI_PCI_IO_PROTOCOL *This, + OUT UINTN *Segment, + OUT UINTN *Bus, + OUT UINTN *Device, + OUT UINTN *Function + ); + +/** + Check BAR type for PCI resource. + + @param PciIoDevice PCI device instance. + @param BarIndex The BAR index of the standard PCI Configuration header to use as the + base address for the memory or I/O operation to perform. + @param BarType Memory or I/O. + + @retval TRUE Pci device's bar type is same with input BarType. + @retval TRUE Pci device's bar type is not same with input BarType. + +**/ +BOOLEAN +CheckBarType ( + IN PCI_IO_DEVICE *PciIoDevice, + IN UINT8 BarIndex, + IN PCI_BAR_TYPE BarType + ); + +/** + Set/Disable new attributes to a Root Bridge. + + @param PciIoDevice Pci device instance. + @param Attributes New attribute want to be set. + @param Operation Set or Disable. + + @retval EFI_UNSUPPORTED If root bridge does not support change attribute. + @retval EFI_SUCCESS Successfully set new attributs. + +**/ +EFI_STATUS +ModifyRootBridgeAttributes ( + IN PCI_IO_DEVICE *PciIoDevice, + IN UINT64 Attributes, + IN EFI_PCI_IO_PROTOCOL_ATTRIBUTE_OPERATION Operation + ); + +/** + Check whether this device can be enable/disable to snoop. + + @param PciIoDevice Pci device instance. + @param Operation Enable/Disable. + + @retval EFI_UNSUPPORTED Pci device is not GFX device or not support snoop. + @retval EFI_SUCCESS Snoop can be supported. + +**/ +EFI_STATUS +SupportPaletteSnoopAttributes ( + IN PCI_IO_DEVICE *PciIoDevice, + IN EFI_PCI_IO_PROTOCOL_ATTRIBUTE_OPERATION Operation + ); + +/** + Performs an operation on the attributes that this PCI controller supports. The operations include + getting the set of supported attributes, retrieving the current attributes, setting the current + attributes, enabling attributes, and disabling attributes. + + @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. + @param Operation The operation to perform on the attributes for this PCI controller. + @param Attributes The mask of attributes that are used for Set, Enable, and Disable + operations. + @param Result A pointer to the result mask of attributes that are returned for the Get + and Supported operations. + + @retval EFI_SUCCESS The operation on the PCI controller's attributes was completed. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_UNSUPPORTED one or more of the bits set in + Attributes are not supported by this PCI controller or one of + its parent bridges when Operation is Set, Enable or Disable. + +**/ +EFI_STATUS +EFIAPI +PciIoAttributes ( + IN EFI_PCI_IO_PROTOCOL * This, + IN EFI_PCI_IO_PROTOCOL_ATTRIBUTE_OPERATION Operation, + IN UINT64 Attributes, + OUT UINT64 *Result OPTIONAL + ); + +/** + Gets the attributes that this PCI controller supports setting on a BAR using + SetBarAttributes(), and retrieves the list of resource descriptors for a BAR. + + @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. + @param BarIndex The BAR index of the standard PCI Configuration header to use as the + base address for resource range. The legal range for this field is 0..5. + @param Supports A pointer to the mask of attributes that this PCI controller supports + setting for this BAR with SetBarAttributes(). + @param Resources A pointer to the ACPI 2.0 resource descriptors that describe the current + configuration of this BAR of the PCI controller. + + @retval EFI_SUCCESS If Supports is not NULL, then the attributes that the PCI + controller supports are returned in Supports. If Resources + is not NULL, then the ACPI 2.0 resource descriptors that the PCI + controller is currently using are returned in Resources. + @retval EFI_INVALID_PARAMETER Both Supports and Attributes are NULL. + @retval EFI_UNSUPPORTED BarIndex not valid for this PCI controller. + @retval EFI_OUT_OF_RESOURCES There are not enough resources available to allocate + Resources. + +**/ +EFI_STATUS +EFIAPI +PciIoGetBarAttributes ( + IN EFI_PCI_IO_PROTOCOL * This, + IN UINT8 BarIndex, + OUT UINT64 *Supports, OPTIONAL + OUT VOID **Resources OPTIONAL + ); + +/** + Sets the attributes for a range of a BAR on a PCI controller. + + @param This A pointer to the EFI_PCI_IO_PROTOCOL instance. + @param Attributes The mask of attributes to set for the resource range specified by + BarIndex, Offset, and Length. + @param BarIndex The BAR index of the standard PCI Configuration header to use as the + base address for resource range. The legal range for this field is 0..5. + @param Offset A pointer to the BAR relative base address of the resource range to be + modified by the attributes specified by Attributes. + @param Length A pointer to the length of the resource range to be modified by the + attributes specified by Attributes. + + @retval EFI_SUCCESS The set of attributes specified by Attributes for the resource + range specified by BarIndex, Offset, and Length were + set on the PCI controller, and the actual resource range is returned + in Offset and Length. + @retval EFI_INVALID_PARAMETER Offset or Length is NULL. + @retval EFI_UNSUPPORTED BarIndex not valid for this PCI controller. + @retval EFI_OUT_OF_RESOURCES There are not enough resources to set the attributes on the + resource range specified by BarIndex, Offset, and + Length. + +**/ +EFI_STATUS +EFIAPI +PciIoSetBarAttributes ( + IN EFI_PCI_IO_PROTOCOL *This, + IN UINT64 Attributes, + IN UINT8 BarIndex, + IN OUT UINT64 *Offset, + IN OUT UINT64 *Length + ); + +/** + Program parent bridge's attribute recurrently. + + @param PciIoDevice Child Pci device instance + @param Operation The operation to perform on the attributes for this PCI controller. + @param Attributes The mask of attributes that are used for Set, Enable, and Disable + operations. + + @retval EFI_SUCCESS The operation on the PCI controller's attributes was completed. + @retval EFI_INVALID_PARAMETER One or more parameters are invalid. + @retval EFI_UNSUPPORTED one or more of the bits set in + Attributes are not supported by this PCI controller or one of + its parent bridges when Operation is Set, Enable or Disable. + +**/ +EFI_STATUS +UpStreamBridgesAttributes ( + IN PCI_IO_DEVICE *PciIoDevice, + IN EFI_PCI_IO_PROTOCOL_ATTRIBUTE_OPERATION Operation, + IN UINT64 Attributes + ); + +/** + Test whether two Pci devices has same parent bridge. + + @param PciDevice1 The first pci device for testing. + @param PciDevice2 The second pci device for testing. + + @retval TRUE Two Pci device has the same parent bridge. + @retval FALSE Two Pci device has not the same parent bridge. + +**/ +BOOLEAN +PciDevicesOnTheSamePath ( + IN PCI_IO_DEVICE *PciDevice1, + IN PCI_IO_DEVICE *PciDevice2 + ); + +#endif diff --git a/MdeModulePkg/Bus/Pci/PciBusDxe/PciLib.c b/MdeModulePkg/Bus/Pci/PciBusDxe/PciLib.c new file mode 100644 index 0000000000..f40000672f --- /dev/null +++ b/MdeModulePkg/Bus/Pci/PciBusDxe/PciLib.c @@ -0,0 +1,1359 @@ +/** @file + Internal library implementation for PCI Bus module. + +Copyright (c) 2006 - 2009, Intel Corporation +All rights reserved. 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" + + +/** + Retrieve the PCI Card device BAR information via PciIo interface. + + @param PciIoDevice PCI Card device instance. + +**/ +VOID +GetBackPcCardBar ( + IN PCI_IO_DEVICE *PciIoDevice + ) +{ + UINT32 Address; + + if (!FeaturePcdGet (PcdPciBusHotplugDeviceSupport)) { + return; + } + + // + // Read PciBar information from the bar register + // + if (!gFullEnumeration) { + Address = 0; + PciIoDevice->PciIo.Pci.Read ( + &(PciIoDevice->PciIo), + EfiPciIoWidthUint32, + PCI_CARD_MEMORY_BASE_0, + 1, + &Address + ); + + (PciIoDevice->PciBar)[P2C_MEM_1].BaseAddress = (UINT64) (Address); + (PciIoDevice->PciBar)[P2C_MEM_1].Length = 0x2000000; + (PciIoDevice->PciBar)[P2C_MEM_1].BarType = PciBarTypeMem32; + + Address = 0; + PciIoDevice->PciIo.Pci.Read ( + &(PciIoDevice->PciIo), + EfiPciIoWidthUint32, + PCI_CARD_MEMORY_BASE_1, + 1, + &Address + ); + (PciIoDevice->PciBar)[P2C_MEM_2].BaseAddress = (UINT64) (Address); + (PciIoDevice->PciBar)[P2C_MEM_2].Length = 0x2000000; + (PciIoDevice->PciBar)[P2C_MEM_2].BarType = PciBarTypePMem32; + + Address = 0; + PciIoDevice->PciIo.Pci.Read ( + &(PciIoDevice->PciIo), + EfiPciIoWidthUint32, + PCI_CARD_IO_BASE_0_LOWER, + 1, + &Address + ); + (PciIoDevice->PciBar)[P2C_IO_1].BaseAddress = (UINT64) (Address); + (PciIoDevice->PciBar)[P2C_IO_1].Length = 0x100; + (PciIoDevice->PciBar)[P2C_IO_1].BarType = PciBarTypeIo16; + + Address = 0; + PciIoDevice->PciIo.Pci.Read ( + &(PciIoDevice->PciIo), + EfiPciIoWidthUint32, + PCI_CARD_IO_BASE_1_LOWER, + 1, + &Address + ); + (PciIoDevice->PciBar)[P2C_IO_2].BaseAddress = (UINT64) (Address); + (PciIoDevice->PciBar)[P2C_IO_2].Length = 0x100; + (PciIoDevice->PciBar)[P2C_IO_2].BarType = PciBarTypeIo16; + + } + + if (gPciHotPlugInit != NULL && FeaturePcdGet (PcdPciBusHotplugDeviceSupport)) { + GetResourcePaddingForHpb (PciIoDevice); + } +} + +/** + Remove rejected pci device from specific root bridge + handle. + + @param RootBridgeHandle Specific parent root bridge handle. + @param Bridge Bridge device instance. + +**/ +VOID +RemoveRejectedPciDevices ( + IN EFI_HANDLE RootBridgeHandle, + IN PCI_IO_DEVICE *Bridge + ) +{ + PCI_IO_DEVICE *Temp; + LIST_ENTRY *CurrentLink; + LIST_ENTRY *LastLink; + + if (!FeaturePcdGet (PcdPciBusHotplugDeviceSupport)) { + return; + } + + CurrentLink = Bridge->ChildList.ForwardLink; + + while (CurrentLink != NULL && CurrentLink != &Bridge->ChildList) { + + Temp = PCI_IO_DEVICE_FROM_LINK (CurrentLink); + + if (IS_PCI_BRIDGE (&Temp->Pci)) { + // + // Remove rejected devices recusively + // + RemoveRejectedPciDevices (RootBridgeHandle, Temp); + } else { + // + // Skip rejection for all PPBs, while detect rejection for others + // + if (IsPciDeviceRejected (Temp)) { + + // + // For P2C, remove all devices on it + // + if (!IsListEmpty (&Temp->ChildList)) { + RemoveAllPciDeviceOnBridge (RootBridgeHandle, Temp); + } + + // + // Finally remove itself + // + LastLink = CurrentLink->BackLink; + RemoveEntryList (CurrentLink); + FreePciDevice (Temp); + + CurrentLink = LastLink; + } + } + + CurrentLink = CurrentLink->ForwardLink; + } +} + +/** + Submits the I/O and memory resource requirements for the specified PCI Host Bridge. + + @param PciResAlloc Point to protocol instance of EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL. + + @retval EFI_SUCCESS Successfully finished resource allocation. + @retval EFI_NOT_FOUND Cannot get root bridge instance. + @retval EFI_OUT_OF_RESOURCES Platform failed to program the resources if no hot plug supported. + @retval other Some error occurred when allocating resources for the PCI Host Bridge. + + @note Feature flag PcdPciBusHotplugDeviceSupport determine whether need support hotplug. + +**/ +EFI_STATUS +PciHostBridgeResourceAllocator ( + IN EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL *PciResAlloc + ) +{ + PCI_IO_DEVICE *RootBridgeDev; + EFI_HANDLE RootBridgeHandle; + VOID *AcpiConfig; + EFI_STATUS Status; + UINT64 IoBase; + UINT64 Mem32Base; + UINT64 PMem32Base; + UINT64 Mem64Base; + UINT64 PMem64Base; + UINT64 IoResStatus; + UINT64 Mem32ResStatus; + UINT64 PMem32ResStatus; + UINT64 Mem64ResStatus; + UINT64 PMem64ResStatus; + UINT64 MaxOptionRomSize; + PCI_RESOURCE_NODE *IoBridge; + PCI_RESOURCE_NODE *Mem32Bridge; + PCI_RESOURCE_NODE *PMem32Bridge; + PCI_RESOURCE_NODE *Mem64Bridge; + PCI_RESOURCE_NODE *PMem64Bridge; + PCI_RESOURCE_NODE IoPool; + PCI_RESOURCE_NODE Mem32Pool; + PCI_RESOURCE_NODE PMem32Pool; + PCI_RESOURCE_NODE Mem64Pool; + PCI_RESOURCE_NODE PMem64Pool; + BOOLEAN ReAllocate; + EFI_DEVICE_HANDLE_EXTENDED_DATA_PAYLOAD HandleExtendedData; + EFI_RESOURCE_ALLOC_FAILURE_ERROR_DATA_PAYLOAD AllocFailExtendedData; + + // + // Reallocate flag + // + ReAllocate = FALSE; + + // + // It may try several times if the resource allocation fails + // + while (TRUE) { + // + // Initialize resource pool + // + InitializeResourcePool (&IoPool, PciBarTypeIo16); + InitializeResourcePool (&Mem32Pool, PciBarTypeMem32); + InitializeResourcePool (&PMem32Pool, PciBarTypePMem32); + InitializeResourcePool (&Mem64Pool, PciBarTypeMem64); + InitializeResourcePool (&PMem64Pool, PciBarTypePMem64); + + RootBridgeDev = NULL; + RootBridgeHandle = 0; + + while (PciResAlloc->GetNextRootBridge (PciResAlloc, &RootBridgeHandle) == EFI_SUCCESS) { + // + // Get Root Bridge Device by handle + // + RootBridgeDev = GetRootBridgeByHandle (RootBridgeHandle); + + if (RootBridgeDev == NULL) { + return EFI_NOT_FOUND; + } + + // + // Create the entire system resource map from the information collected by + // enumerator. Several resource tree was created + // + + IoBridge = CreateResourceNode ( + RootBridgeDev, + 0, + 0xFFF, + 0, + PciBarTypeIo16, + PciResUsageTypical + ); + + Mem32Bridge = CreateResourceNode ( + RootBridgeDev, + 0, + 0xFFFFF, + 0, + PciBarTypeMem32, + PciResUsageTypical + ); + + PMem32Bridge = CreateResourceNode ( + RootBridgeDev, + 0, + 0xFFFFF, + 0, + PciBarTypePMem32, + PciResUsageTypical + ); + + Mem64Bridge = CreateResourceNode ( + RootBridgeDev, + 0, + 0xFFFFF, + 0, + PciBarTypeMem64, + PciResUsageTypical + ); + + PMem64Bridge = CreateResourceNode ( + RootBridgeDev, + 0, + 0xFFFFF, + 0, + PciBarTypePMem64, + PciResUsageTypical + ); + + // + // Create resourcemap by going through all the devices subject to this root bridge + // + CreateResourceMap ( + RootBridgeDev, + IoBridge, + Mem32Bridge, + PMem32Bridge, + Mem64Bridge, + PMem64Bridge + ); + + // + // Get the max ROM size that the root bridge can process + // + RootBridgeDev->RomSize = Mem32Bridge->Length; + + // + // Skip to enlarge the resource request during realloction + // + if (!ReAllocate) { + // + // Get Max Option Rom size for current root bridge + // + MaxOptionRomSize = GetMaxOptionRomSize (RootBridgeDev); + + // + // Enlarger the mem32 resource to accomdate the option rom + // if the mem32 resource is not enough to hold the rom + // + if (MaxOptionRomSize > Mem32Bridge->Length) { + + Mem32Bridge->Length = MaxOptionRomSize; + RootBridgeDev->RomSize = MaxOptionRomSize; + + // + // Alignment should be adjusted as well + // + if (Mem32Bridge->Alignment < MaxOptionRomSize - 1) { + Mem32Bridge->Alignment = MaxOptionRomSize - 1; + } + } + } + + // + // Based on the all the resource tree, contruct ACPI resource node to + // submit the resource aperture to pci host bridge protocol + // + Status = ConstructAcpiResourceRequestor ( + RootBridgeDev, + IoBridge, + Mem32Bridge, + PMem32Bridge, + Mem64Bridge, + PMem64Bridge, + &AcpiConfig + ); + + // + // Insert these resource nodes into the database + // + InsertResourceNode (&IoPool, IoBridge); + InsertResourceNode (&Mem32Pool, Mem32Bridge); + InsertResourceNode (&PMem32Pool, PMem32Bridge); + InsertResourceNode (&Mem64Pool, Mem64Bridge); + InsertResourceNode (&PMem64Pool, PMem64Bridge); + + if (Status == EFI_SUCCESS) { + // + // Submit the resource requirement + // + Status = PciResAlloc->SubmitResources ( + PciResAlloc, + RootBridgeDev->Handle, + AcpiConfig + ); + } + + // + // Free acpi resource node + // + if (AcpiConfig != NULL) { + FreePool (AcpiConfig); + } + + if (EFI_ERROR (Status)) { + // + // Destroy all the resource tree + // + DestroyResourceTree (&IoPool); + DestroyResourceTree (&Mem32Pool); + DestroyResourceTree (&PMem32Pool); + DestroyResourceTree (&Mem64Pool); + DestroyResourceTree (&PMem64Pool); + return Status; + } + } + // + // End while, at least one Root Bridge should be found. + // + ASSERT (RootBridgeDev != NULL); + + // + // Notify platform to start to program the resource + // + Status = NotifyPhase (PciResAlloc, EfiPciHostBridgeAllocateResources); + if (!FeaturePcdGet (PcdPciBusHotplugDeviceSupport)) { + // + // If Hot Plug is not supported + // + if (EFI_ERROR (Status)) { + // + // Allocation failed, then return + // + return EFI_OUT_OF_RESOURCES; + } + // + // Allocation succeed. + // Get host bridge handle for status report, and then skip the main while + // + HandleExtendedData.Handle = RootBridgeDev->PciRootBridgeIo->ParentHandle; + + break; + + } else { + // + // If Hot Plug is supported + // + if (!EFI_ERROR (Status)) { + // + // Allocation succeed, then continue the following + // + break; + } + + // + // If the resource allocation is unsuccessful, free resources on bridge + // + + RootBridgeDev = NULL; + RootBridgeHandle = 0; + + IoResStatus = EFI_RESOURCE_SATISFIED; + Mem32ResStatus = EFI_RESOURCE_SATISFIED; + PMem32ResStatus = EFI_RESOURCE_SATISFIED; + Mem64ResStatus = EFI_RESOURCE_SATISFIED; + PMem64ResStatus = EFI_RESOURCE_SATISFIED; + + while (PciResAlloc->GetNextRootBridge (PciResAlloc, &RootBridgeHandle) == EFI_SUCCESS) { + // + // Get RootBridg Device by handle + // + RootBridgeDev = GetRootBridgeByHandle (RootBridgeHandle); + if (RootBridgeDev == NULL) { + return EFI_NOT_FOUND; + } + + // + // Get host bridge handle for status report + // + HandleExtendedData.Handle = RootBridgeDev->PciRootBridgeIo->ParentHandle; + + // + // Get acpi resource node for all the resource types + // + AcpiConfig = NULL; + + Status = PciResAlloc->GetProposedResources ( + PciResAlloc, + RootBridgeDev->Handle, + &AcpiConfig + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + if (AcpiConfig != NULL) { + // + // Adjust resource allocation policy for each RB + // + GetResourceAllocationStatus ( + AcpiConfig, + &IoResStatus, + &Mem32ResStatus, + &PMem32ResStatus, + &Mem64ResStatus, + &PMem64ResStatus + ); + FreePool (AcpiConfig); + } + } + // + // End while + // + + // + // Raise the EFI_IOB_EC_RESOURCE_CONFLICT status code + // + // + // It is very difficult to follow the spec here + // Device path , Bar index can not be get here + // + ZeroMem (&AllocFailExtendedData, sizeof (AllocFailExtendedData)); + + REPORT_STATUS_CODE_WITH_EXTENDED_DATA ( + EFI_PROGRESS_CODE, + EFI_IO_BUS_PCI | EFI_IOB_EC_RESOURCE_CONFLICT, + (VOID *) &AllocFailExtendedData, + sizeof (AllocFailExtendedData) + ); + + Status = PciHostBridgeAdjustAllocation ( + &IoPool, + &Mem32Pool, + &PMem32Pool, + &Mem64Pool, + &PMem64Pool, + IoResStatus, + Mem32ResStatus, + PMem32ResStatus, + Mem64ResStatus, + PMem64ResStatus + ); + + // + // Destroy all the resource tree + // + DestroyResourceTree (&IoPool); + DestroyResourceTree (&Mem32Pool); + DestroyResourceTree (&PMem32Pool); + DestroyResourceTree (&Mem64Pool); + DestroyResourceTree (&PMem64Pool); + + NotifyPhase (PciResAlloc, EfiPciHostBridgeFreeResources); + + if (EFI_ERROR (Status)) { + return Status; + } + + ReAllocate = TRUE; + } + } + // + // End main while + // + + // + // Raise the EFI_IOB_PCI_RES_ALLOC status code + // + REPORT_STATUS_CODE_WITH_EXTENDED_DATA ( + EFI_PROGRESS_CODE, + EFI_IO_BUS_PCI | EFI_IOB_PCI_PC_RES_ALLOC, + (VOID *) &HandleExtendedData, + sizeof (HandleExtendedData) + ); + + // + // Notify pci bus driver starts to program the resource + // + NotifyPhase (PciResAlloc, EfiPciHostBridgeSetResources); + + RootBridgeDev = NULL; + + RootBridgeHandle = 0; + + while (PciResAlloc->GetNextRootBridge (PciResAlloc, &RootBridgeHandle) == EFI_SUCCESS) { + // + // Get RootBridg Device by handle + // + RootBridgeDev = GetRootBridgeByHandle (RootBridgeHandle); + + if (RootBridgeDev == NULL) { + return EFI_NOT_FOUND; + } + + // + // Get acpi resource node for all the resource types + // + AcpiConfig = NULL; + Status = PciResAlloc->GetProposedResources ( + PciResAlloc, + RootBridgeDev->Handle, + &AcpiConfig + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Get the resource base by interpreting acpi resource node + // + // + GetResourceBase ( + AcpiConfig, + &IoBase, + &Mem32Base, + &PMem32Base, + &Mem64Base, + &PMem64Base + ); + + // + // Process option rom for this root bridge + // + ProcessOptionRom (RootBridgeDev, Mem32Base, RootBridgeDev->RomSize); + + // + // Create the entire system resource map from the information collected by + // enumerator. Several resource tree was created + // + GetResourceMap ( + RootBridgeDev, + &IoBridge, + &Mem32Bridge, + &PMem32Bridge, + &Mem64Bridge, + &PMem64Bridge, + &IoPool, + &Mem32Pool, + &PMem32Pool, + &Mem64Pool, + &PMem64Pool + ); + + // + // Program IO resources + // + ProgramResource ( + IoBase, + IoBridge + ); + + // + // Program Mem32 resources + // + ProgramResource ( + Mem32Base, + Mem32Bridge + ); + + // + // Program PMem32 resources + // + ProgramResource ( + PMem32Base, + PMem32Bridge + ); + + // + // Program Mem64 resources + // + ProgramResource ( + Mem64Base, + Mem64Bridge + ); + + // + // Program PMem64 resources + // + ProgramResource ( + PMem64Base, + PMem64Bridge + ); + + FreePool (AcpiConfig); + } + + // + // Destroy all the resource tree + // + DestroyResourceTree (&IoPool); + DestroyResourceTree (&Mem32Pool); + DestroyResourceTree (&PMem32Pool); + DestroyResourceTree (&Mem64Pool); + DestroyResourceTree (&PMem64Pool); + + // + // Notify the resource allocation phase is to end + // + NotifyPhase (PciResAlloc, EfiPciHostBridgeEndResourceAllocation); + + return EFI_SUCCESS; +} + +/** + Scan pci bus and assign bus number to the given PCI bus system. + + @param Bridge Bridge device instance. + @param StartBusNumber start point. + @param SubBusNumber Point to sub bus number. + @param PaddedBusRange Customized bus number. + + @retval EFI_SUCCESS Successfully scanned and assigned bus number. + @retval other Some error occurred when scanning pci bus. + + @note Feature flag PcdPciBusHotplugDeviceSupport determine whether need support hotplug. + +**/ +EFI_STATUS +PciScanBus ( + IN PCI_IO_DEVICE *Bridge, + IN UINT8 StartBusNumber, + OUT UINT8 *SubBusNumber, + OUT UINT8 *PaddedBusRange + ) +{ + EFI_STATUS Status; + PCI_TYPE00 Pci; + UINT8 Device; + UINT8 Func; + UINT64 Address; + UINTN SecondBus; + UINT16 Register; + UINTN HpIndex; + PCI_IO_DEVICE *PciDevice; + EFI_EVENT Event; + EFI_HPC_STATE State; + UINT64 PciAddress; + EFI_HPC_PADDING_ATTRIBUTES Attributes; + EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Descriptors; + UINT16 BusRange; + EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *PciRootBridgeIo; + BOOLEAN BusPadding; + UINT32 TempReservedBusNum; + + PciRootBridgeIo = Bridge->PciRootBridgeIo; + SecondBus = 0; + Register = 0; + State = 0; + Attributes = (EFI_HPC_PADDING_ATTRIBUTES) 0; + BusRange = 0; + BusPadding = FALSE; + PciDevice = NULL; + PciAddress = 0; + + for (Device = 0; Device <= PCI_MAX_DEVICE; Device++) { + TempReservedBusNum = 0; + for (Func = 0; Func <= PCI_MAX_FUNC; Func++) { + + // + // Check to see whether a pci device is present + // + Status = PciDevicePresent ( + PciRootBridgeIo, + &Pci, + StartBusNumber, + Device, + Func + ); + + if (EFI_ERROR (Status)) { + if (Func == 0) { + // + // Skip sub functions, this is not a multi function device + // + Func = PCI_MAX_FUNC; + } + + continue; + } + + DEBUG((EFI_D_INFO, "Found DEV(%02d,%02d,%02d)\n", StartBusNumber, Device, Func )); + + if (FeaturePcdGet (PcdPciBusHotplugDeviceSupport)) { + // + // Get the PCI device information + // + Status = PciSearchDevice ( + Bridge, + &Pci, + StartBusNumber, + Device, + Func, + &PciDevice + ); + + ASSERT (!EFI_ERROR (Status)); + + PciAddress = EFI_PCI_ADDRESS (StartBusNumber, Device, Func, 0); + + if (!IS_PCI_BRIDGE (&Pci)) { + // + // PCI bridges will be called later + // Here just need for PCI device or PCI to cardbus controller + // EfiPciBeforeChildBusEnumeration for PCI Device Node + // + PreprocessController ( + PciDevice, + PciDevice->BusNumber, + PciDevice->DeviceNumber, + PciDevice->FunctionNumber, + EfiPciBeforeChildBusEnumeration + ); + } + + // + // For Pci Hotplug controller devcie only + // + if (gPciHotPlugInit != NULL) { + // + // Check if it is a Hotplug PCI controller + // + if (IsRootPciHotPlugController (PciDevice->DevicePath, &HpIndex)) { + + if (!gPciRootHpcData[HpIndex].Initialized) { + + Status = CreateEventForHpc (HpIndex, &Event); + + ASSERT (!EFI_ERROR (Status)); + + Status = gPciHotPlugInit->InitializeRootHpc ( + gPciHotPlugInit, + gPciRootHpcPool[HpIndex].HpcDevicePath, + PciAddress, + Event, + &State + ); + + PreprocessController ( + PciDevice, + PciDevice->BusNumber, + PciDevice->DeviceNumber, + PciDevice->FunctionNumber, + EfiPciBeforeChildBusEnumeration + ); + } + } + } + } + + if (IS_PCI_BRIDGE (&Pci) || IS_CARDBUS_BRIDGE (&Pci)) { + // + // For PPB + // + if (!FeaturePcdGet (PcdPciBusHotplugDeviceSupport)) { + // + // If Hot Plug is not supported, + // get the bridge information + // + Status = PciSearchDevice ( + Bridge, + &Pci, + StartBusNumber, + Device, + Func, + &PciDevice + ); + + if (EFI_ERROR (Status)) { + return Status; + } + } else { + // + // If Hot Plug is supported, + // Get the bridge information + // + BusPadding = FALSE; + if (gPciHotPlugInit != NULL) { + + if (IsRootPciHotPlugBus (PciDevice->DevicePath, &HpIndex)) { + + // + // If it is initialized, get the padded bus range + // + Status = gPciHotPlugInit->GetResourcePadding ( + gPciHotPlugInit, + gPciRootHpcPool[HpIndex].HpbDevicePath, + PciAddress, + &State, + (VOID **) &Descriptors, + &Attributes + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + BusRange = 0; + Status = PciGetBusRange ( + &Descriptors, + NULL, + NULL, + &BusRange + ); + + FreePool (Descriptors); + + if (EFI_ERROR (Status)) { + return Status; + } + + BusPadding = TRUE; + } + } + } + + // + // Add feature to support customized secondary bus number + // + if (*SubBusNumber == 0) { + *SubBusNumber = *PaddedBusRange; + *PaddedBusRange = 0; + } + + (*SubBusNumber)++; + SecondBus = *SubBusNumber; + + Register = (UINT16) ((SecondBus << 8) | (UINT16) StartBusNumber); + Address = EFI_PCI_ADDRESS (StartBusNumber, Device, Func, PCI_BRIDGE_PRIMARY_BUS_REGISTER_OFFSET); + + Status = PciRootBridgeIo->Pci.Write ( + PciRootBridgeIo, + EfiPciWidthUint16, + Address, + 1, + &Register + ); + + + // + // If it is PPB, resursively search down this bridge + // + if (IS_PCI_BRIDGE (&Pci)) { + + // + // Temporarily initialize SubBusNumber to maximum bus number to ensure the + // PCI configuration transaction to go through any PPB + // + Register = 0xFF; + Address = EFI_PCI_ADDRESS (StartBusNumber, Device, Func, PCI_BRIDGE_SUBORDINATE_BUS_REGISTER_OFFSET); + Status = PciRootBridgeIo->Pci.Write ( + PciRootBridgeIo, + EfiPciWidthUint8, + Address, + 1, + &Register + ); + + // + // Nofify EfiPciBeforeChildBusEnumeration for PCI Brige + // + PreprocessController ( + PciDevice, + PciDevice->BusNumber, + PciDevice->DeviceNumber, + PciDevice->FunctionNumber, + EfiPciBeforeChildBusEnumeration + ); + + DEBUG((EFI_D_INFO, "Scan PPB(%02d,%02d,%02d)\n", PciDevice->BusNumber, PciDevice->DeviceNumber,PciDevice->FunctionNumber)); + Status = PciScanBus ( + PciDevice, + (UINT8) (SecondBus), + SubBusNumber, + PaddedBusRange + ); + if (EFI_ERROR (Status)) { + return Status; + } + } + + if (FeaturePcdGet (PcdPciBusHotplugDeviceSupport) && BusPadding) { + // + // Ensure the device is enabled and initialized + // + if ((Attributes == EfiPaddingPciRootBridge) && + (State & EFI_HPC_STATE_ENABLED) != 0 && + (State & EFI_HPC_STATE_INITIALIZED) != 0) { + *PaddedBusRange = (UINT8) ((UINT8) (BusRange) +*PaddedBusRange); + } else { + *SubBusNumber = (UINT8) ((UINT8) (BusRange) +*SubBusNumber); + } + } + + // + // Set the current maximum bus number under the PPB + // + Address = EFI_PCI_ADDRESS (StartBusNumber, Device, Func, PCI_BRIDGE_SUBORDINATE_BUS_REGISTER_OFFSET); + + Status = PciRootBridgeIo->Pci.Write ( + PciRootBridgeIo, + EfiPciWidthUint8, + Address, + 1, + SubBusNumber + ); + } else { + // + // It is device. Check PCI IOV for Bus reservation + // + if (PciDevice == NULL) { + // + // No PciDevice found, conitue Scan + // + continue; + } + // + // Go through each function, just reserve the MAX ReservedBusNum for one device + // + if ((PciDevice->AriCapabilityOffset != 0) && ((FeaturePcdGet(PcdSrIovSupport)& EFI_PCI_IOV_POLICY_SRIOV) != 0)) { + + if (TempReservedBusNum < PciDevice->ReservedBusNum) { + + (*SubBusNumber) = (UINT8)((*SubBusNumber) + PciDevice->ReservedBusNum - TempReservedBusNum); + TempReservedBusNum = PciDevice->ReservedBusNum; + + if (Func == 0) { + DEBUG ((EFI_D_INFO, "PCI-IOV ScanBus - SubBusNumber - 0x%x\n", *SubBusNumber)); + } else { + DEBUG ((EFI_D_INFO, "PCI-IOV ScanBus - SubBusNumber - 0x%x (Update)\n", *SubBusNumber)); + } + } + } + } + + if (Func == 0 && !IS_PCI_MULTI_FUNC (&Pci)) { + + // + // Skip sub functions, this is not a multi function device + // + + Func = PCI_MAX_FUNC; + } + } + } + + return EFI_SUCCESS; +} + +/** + Process Option Rom on the specified root bridge. + + @param Bridge Pci root bridge device instance. + + @retval EFI_SUCCESS Success process. + @retval other Some error occurred when processing Option Rom on the root bridge. + +**/ +EFI_STATUS +PciRootBridgeP2CProcess ( + IN PCI_IO_DEVICE *Bridge + ) +{ + LIST_ENTRY *CurrentLink; + PCI_IO_DEVICE *Temp; + EFI_HPC_STATE State; + UINT64 PciAddress; + EFI_STATUS Status; + + CurrentLink = Bridge->ChildList.ForwardLink; + + while (CurrentLink != NULL && CurrentLink != &Bridge->ChildList) { + + Temp = PCI_IO_DEVICE_FROM_LINK (CurrentLink); + + if (IS_CARDBUS_BRIDGE (&Temp->Pci)) { + + if (gPciHotPlugInit != NULL && Temp->Allocated && FeaturePcdGet (PcdPciBusHotplugDeviceSupport)) { + + // + // Raise the EFI_IOB_PCI_HPC_INIT status code + // + REPORT_STATUS_CODE_WITH_DEVICE_PATH ( + EFI_PROGRESS_CODE, + EFI_IO_BUS_PCI | EFI_IOB_PCI_PC_HPC_INIT, + Temp->DevicePath + ); + + PciAddress = EFI_PCI_ADDRESS (Temp->BusNumber, Temp->DeviceNumber, Temp->FunctionNumber, 0); + Status = gPciHotPlugInit->InitializeRootHpc ( + gPciHotPlugInit, + Temp->DevicePath, + PciAddress, + NULL, + &State + ); + + if (!EFI_ERROR (Status)) { + Status = PciBridgeEnumerator (Temp); + + if (EFI_ERROR (Status)) { + return Status; + } + } + + CurrentLink = CurrentLink->ForwardLink; + continue; + + } + } + + if (!IsListEmpty (&Temp->ChildList)) { + Status = PciRootBridgeP2CProcess (Temp); + } + + CurrentLink = CurrentLink->ForwardLink; + } + + return EFI_SUCCESS; +} + +/** + Process Option Rom on the specified host bridge. + + @param PciResAlloc Pointer to instance of EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL. + + @retval EFI_SUCCESS Success process. + @retval EFI_NOT_FOUND Can not find the root bridge instance. + @retval other Some error occurred when processing Option Rom on the host bridge. + +**/ +EFI_STATUS +PciHostBridgeP2CProcess ( + IN EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL *PciResAlloc + ) +{ + EFI_HANDLE RootBridgeHandle; + PCI_IO_DEVICE *RootBridgeDev; + EFI_STATUS Status; + + if (!FeaturePcdGet (PcdPciBusHotplugDeviceSupport)) { + return EFI_SUCCESS; + } + + RootBridgeHandle = NULL; + + while (PciResAlloc->GetNextRootBridge (PciResAlloc, &RootBridgeHandle) == EFI_SUCCESS) { + + // + // Get RootBridg Device by handle + // + RootBridgeDev = GetRootBridgeByHandle (RootBridgeHandle); + + if (RootBridgeDev == NULL) { + return EFI_NOT_FOUND; + } + + Status = PciRootBridgeP2CProcess (RootBridgeDev); + if (EFI_ERROR (Status)) { + return Status; + } + + } + + return EFI_SUCCESS; +} + +/** + This function is used to enumerate the entire host bridge + in a given platform. + + @param PciResAlloc A pointer to the PCI Host Resource Allocation protocol. + + @retval EFI_SUCCESS Successfully enumerated the host bridge. + @retval EFI_OUT_OF_RESOURCES No enough memory available. + @retval other Some error occurred when enumerating the host bridge. + +**/ +EFI_STATUS +PciHostBridgeEnumerator ( + IN EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL *PciResAlloc + ) +{ + EFI_HANDLE RootBridgeHandle; + PCI_IO_DEVICE *RootBridgeDev; + EFI_STATUS Status; + EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *PciRootBridgeIo; + UINT16 MinBus; + EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Descriptors; + EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Configuration; + UINT8 StartBusNumber; + LIST_ENTRY RootBridgeList; + LIST_ENTRY *Link; + + if (FeaturePcdGet (PcdPciBusHotplugDeviceSupport)) { + InitializeHotPlugSupport (); + } + + InitializeListHead (&RootBridgeList); + + // + // Notify the bus allocation phase is about to start + // + NotifyPhase (PciResAlloc, EfiPciHostBridgeBeginBusAllocation); + + DEBUG((EFI_D_INFO, "PCI Bus First Scanning\n")); + RootBridgeHandle = NULL; + while (PciResAlloc->GetNextRootBridge (PciResAlloc, &RootBridgeHandle) == EFI_SUCCESS) { + + // + // if a root bridge instance is found, create root bridge device for it + // + + RootBridgeDev = CreateRootBridge (RootBridgeHandle); + + if (RootBridgeDev == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Enumerate all the buses under this root bridge + // + Status = PciRootBridgeEnumerator ( + PciResAlloc, + RootBridgeDev + ); + + if (gPciHotPlugInit != NULL && FeaturePcdGet (PcdPciBusHotplugDeviceSupport)) { + InsertTailList (&RootBridgeList, &(RootBridgeDev->Link)); + } else { + DestroyRootBridge (RootBridgeDev); + } + if (EFI_ERROR (Status)) { + return Status; + } + } + + // + // Notify the bus allocation phase is finished for the first time + // + NotifyPhase (PciResAlloc, EfiPciHostBridgeEndBusAllocation); + + if (gPciHotPlugInit != NULL && FeaturePcdGet (PcdPciBusHotplugDeviceSupport)) { + // + // Reset all assigned PCI bus number in all PPB + // + RootBridgeHandle = NULL; + Link = GetFirstNode (&RootBridgeList); + while ((PciResAlloc->GetNextRootBridge (PciResAlloc, &RootBridgeHandle) == EFI_SUCCESS) && + (!IsNull (&RootBridgeList, Link))) { + RootBridgeDev = PCI_IO_DEVICE_FROM_LINK (Link); + // + // Get the Bus information + // + Status = PciResAlloc->StartBusEnumeration ( + PciResAlloc, + RootBridgeHandle, + (VOID **) &Configuration + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Get the bus number to start with + // + StartBusNumber = (UINT8) (Configuration->AddrRangeMin); + + ResetAllPpbBusNumber ( + RootBridgeDev, + StartBusNumber + ); + + FreePool (Configuration); + Link = GetNextNode (&RootBridgeList, Link); + DestroyRootBridge (RootBridgeDev); + } + + // + // Wait for all HPC initialized + // + Status = AllRootHPCInitialized (STALL_1_SECOND * 15); + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Notify the bus allocation phase is about to start for the 2nd time + // + NotifyPhase (PciResAlloc, EfiPciHostBridgeBeginBusAllocation); + + DEBUG((EFI_D_INFO, "PCI Bus Second Scanning\n")); + RootBridgeHandle = NULL; + while (PciResAlloc->GetNextRootBridge (PciResAlloc, &RootBridgeHandle) == EFI_SUCCESS) { + + // + // if a root bridge instance is found, create root bridge device for it + // + RootBridgeDev = CreateRootBridge (RootBridgeHandle); + + if (RootBridgeDev == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Enumerate all the buses under this root bridge + // + Status = PciRootBridgeEnumerator ( + PciResAlloc, + RootBridgeDev + ); + + DestroyRootBridge (RootBridgeDev); + if (EFI_ERROR (Status)) { + return Status; + } + } + + // + // Notify the bus allocation phase is to end for the 2nd time + // + NotifyPhase (PciResAlloc, EfiPciHostBridgeEndBusAllocation); + } + + // + // Notify the resource allocation phase is to start + // + NotifyPhase (PciResAlloc, EfiPciHostBridgeBeginResourceAllocation); + + RootBridgeHandle = NULL; + while (PciResAlloc->GetNextRootBridge (PciResAlloc, &RootBridgeHandle) == EFI_SUCCESS) { + + // + // if a root bridge instance is found, create root bridge device for it + // + RootBridgeDev = CreateRootBridge (RootBridgeHandle); + + if (RootBridgeDev == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = StartManagingRootBridge (RootBridgeDev); + + if (EFI_ERROR (Status)) { + return Status; + } + + PciRootBridgeIo = RootBridgeDev->PciRootBridgeIo; + Status = PciRootBridgeIo->Configuration (PciRootBridgeIo, (VOID **) &Descriptors); + + if (EFI_ERROR (Status)) { + return Status; + } + + Status = PciGetBusRange (&Descriptors, &MinBus, NULL, NULL); + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Determine root bridge attribute by calling interface of Pcihostbridge + // protocol + // + DetermineRootBridgeAttributes ( + PciResAlloc, + RootBridgeDev + ); + + // + // Collect all the resource information under this root bridge + // A database that records all the information about pci device subject to this + // root bridge will then be created + // + Status = PciPciDeviceInfoCollector ( + RootBridgeDev, + (UINT8) MinBus + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + InsertRootBridge (RootBridgeDev); + + // + // Record the hostbridge handle + // + AddHostBridgeEnumerator (RootBridgeDev->PciRootBridgeIo->ParentHandle); + } + + return EFI_SUCCESS; +} diff --git a/MdeModulePkg/Bus/Pci/PciBusDxe/PciLib.h b/MdeModulePkg/Bus/Pci/PciBusDxe/PciLib.h new file mode 100644 index 0000000000..43e3e48f2b --- /dev/null +++ b/MdeModulePkg/Bus/Pci/PciBusDxe/PciLib.h @@ -0,0 +1,144 @@ +/** @file + Internal library declaration for PCI Bus module. + +Copyright (c) 2006 - 2009, Intel Corporation +All rights reserved. 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. + +**/ + +#ifndef _EFI_PCI_LIB_H_ +#define _EFI_PCI_LIB_H_ + + +typedef struct { + EFI_HANDLE Handle; +} EFI_DEVICE_HANDLE_EXTENDED_DATA_PAYLOAD; + +typedef struct { + UINT32 Bar; + UINT16 DevicePathSize; + UINT16 ReqResSize; + UINT16 AllocResSize; + UINT8 *DevicePath; + UINT8 *ReqRes; + UINT8 *AllocRes; +} EFI_RESOURCE_ALLOC_FAILURE_ERROR_DATA_PAYLOAD; + + +/** + Retrieve the PCI Card device BAR information via PciIo interface. + + @param PciIoDevice PCI Card device instance. + +**/ +VOID +GetBackPcCardBar ( + IN PCI_IO_DEVICE *PciIoDevice + ); + +/** + Remove rejected pci device from specific root bridge + handle. + + @param RootBridgeHandle Specific parent root bridge handle. + @param Bridge Bridge device instance. + +**/ +VOID +RemoveRejectedPciDevices ( + IN EFI_HANDLE RootBridgeHandle, + IN PCI_IO_DEVICE *Bridge + ); + +/** + Submits the I/O and memory resource requirements for the specified PCI Host Bridge. + + @param PciResAlloc Point to protocol instance of EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL. + + @retval EFI_SUCCESS Successfully finished resource allocation. + @retval EFI_NOT_FOUND Cannot get root bridge instance. + @retval EFI_OUT_OF_RESOURCES Platform failed to program the resources if no hot plug supported. + @retval other Some error occurred when allocating resources for the PCI Host Bridge. + + @note Feature flag PcdPciBusHotplugDeviceSupport determine whether need support hotplug. + +**/ +EFI_STATUS +PciHostBridgeResourceAllocator ( + IN EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL *PciResAlloc + ); + +/** + Scan pci bus and assign bus number to the given PCI bus system. + + @param Bridge Bridge device instance. + @param StartBusNumber start point. + @param SubBusNumber Point to sub bus number. + @param PaddedBusRange Customized bus number. + + @retval EFI_SUCCESS Successfully scanned and assigned bus number. + @retval other Some error occurred when scanning pci bus. + + @note Feature flag PcdPciBusHotplugDeviceSupport determine whether need support hotplug. + +**/ +EFI_STATUS +PciScanBus ( + IN PCI_IO_DEVICE *Bridge, + IN UINT8 StartBusNumber, + OUT UINT8 *SubBusNumber, + OUT UINT8 *PaddedBusRange + ); + +/** + Process Option Rom on the specified root bridge. + + @param Bridge Pci root bridge device instance. + + @retval EFI_SUCCESS Success process. + @retval other Some error occurred when processing Option Rom on the root bridge. + +**/ +EFI_STATUS +PciRootBridgeP2CProcess ( + IN PCI_IO_DEVICE *Bridge + ); + +/** + Process Option Rom on the specified host bridge. + + @param PciResAlloc Pointer to instance of EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL. + + @retval EFI_SUCCESS Success process. + @retval EFI_NOT_FOUND Can not find the root bridge instance. + @retval other Some error occurred when processing Option Rom on the host bridge. + +**/ +EFI_STATUS +PciHostBridgeP2CProcess ( + IN EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL *PciResAlloc + ); + +/** + This function is used to enumerate the entire host bridge + in a given platform. + + @param PciResAlloc A pointer to the PCI Host Resource Allocation protocol. + + @retval EFI_SUCCESS Successfully enumerated the host bridge. + @retval EFI_OUT_OF_RESOURCES No enough memory available. + @retval other Some error occurred when enumerating the host bridge. + +**/ +EFI_STATUS +PciHostBridgeEnumerator ( + IN EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL *PciResAlloc + ); + +#endif diff --git a/MdeModulePkg/Bus/Pci/PciBusDxe/PciOptionRomSupport.c b/MdeModulePkg/Bus/Pci/PciBusDxe/PciOptionRomSupport.c new file mode 100644 index 0000000000..19803efb2c --- /dev/null +++ b/MdeModulePkg/Bus/Pci/PciBusDxe/PciOptionRomSupport.c @@ -0,0 +1,735 @@ +/** @file + PCI Rom supporting funtions implementation for PCI Bus module. + +Copyright (c) 2006 - 2009, Intel Corporation +All rights reserved. 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" + +/** + Load the EFI Image from Option ROM + + @param PciIoDevice PCI IO device instance. + @param FilePath The file path of the EFI Image + @param BufferSize On input the size of Buffer in bytes. On output with a return + code of EFI_SUCCESS, the amount of data transferred to Buffer. + On output with a return code of EFI_BUFFER_TOO_SMALL, + the size of Buffer required to retrieve the requested file. + @param Buffer The memory buffer to transfer the file to. If Buffer is NULL, + then no the size of the requested file is returned in BufferSize. + + @retval EFI_SUCCESS The file was loaded. + @retval EFI_INVALID_PARAMETER FilePath is not a valid device path, or + BufferSize is NULL. + @retval EFI_NOT_FOUND Not found PCI Option Rom on PCI device. + @retval EFI_DEVICE_ERROR Failed to decompress PCI Option Rom image. + @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to read the current directory entry. + BufferSize has been updated with the size needed to complete the request. +**/ +EFI_STATUS +LocalLoadFile2 ( + IN PCI_IO_DEVICE *PciIoDevice, + IN EFI_DEVICE_PATH_PROTOCOL *FilePath, + IN OUT UINTN *BufferSize, + IN VOID *Buffer OPTIONAL + ) +{ + EFI_STATUS Status; + MEDIA_RELATIVE_OFFSET_RANGE_DEVICE_PATH *EfiOpRomImageNode; + EFI_PCI_EXPANSION_ROM_HEADER *EfiRomHeader; + PCI_DATA_STRUCTURE *Pcir; + UINT32 ImageSize; + UINT8 *ImageBuffer; + UINT32 ImageLength; + UINT32 DestinationSize; + UINT32 ScratchSize; + VOID *Scratch; + EFI_DECOMPRESS_PROTOCOL *Decompress; + + EfiOpRomImageNode = (MEDIA_RELATIVE_OFFSET_RANGE_DEVICE_PATH *) FilePath; + if ((EfiOpRomImageNode == NULL) || + (DevicePathType (FilePath) != MEDIA_DEVICE_PATH) || + (DevicePathSubType (FilePath) != MEDIA_RELATIVE_OFFSET_RANGE_DP) || + (DevicePathNodeLength (FilePath) != sizeof (MEDIA_RELATIVE_OFFSET_RANGE_DEVICE_PATH)) || + (!IsDevicePathEnd (NextDevicePathNode (FilePath))) || + (EfiOpRomImageNode->StartingOffset > EfiOpRomImageNode->EndingOffset) || + (EfiOpRomImageNode->EndingOffset >= PciIoDevice->RomSize) || + (BufferSize == NULL) + ) { + return EFI_INVALID_PARAMETER; + } + + EfiRomHeader = (EFI_PCI_EXPANSION_ROM_HEADER *) ( + (UINT8 *) PciIoDevice->PciIo.RomImage + EfiOpRomImageNode->StartingOffset + ); + if (EfiRomHeader->Signature != PCI_EXPANSION_ROM_HEADER_SIGNATURE) { + return EFI_NOT_FOUND; + } + + + Pcir = (PCI_DATA_STRUCTURE *) ((UINT8 *) EfiRomHeader + EfiRomHeader->PcirOffset); + + + if ((Pcir->CodeType == PCI_CODE_TYPE_EFI_IMAGE) && + (EfiRomHeader->EfiSignature == EFI_PCI_EXPANSION_ROM_HEADER_EFISIGNATURE) && + ((EfiRomHeader->EfiSubsystem == EFI_IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER) || + (EfiRomHeader->EfiSubsystem == EFI_IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER)) && + (EfiRomHeader->CompressionType <= EFI_PCI_EXPANSION_ROM_HEADER_COMPRESSED) + ) { + + ImageSize = (UINT32) EfiRomHeader->InitializationSize * 512; + ImageBuffer = (UINT8 *) EfiRomHeader + EfiRomHeader->EfiImageHeaderOffset; + ImageLength = ImageSize - EfiRomHeader->EfiImageHeaderOffset; + + if (EfiRomHeader->CompressionType != EFI_PCI_EXPANSION_ROM_HEADER_COMPRESSED) { + // + // Uncompressed: Copy the EFI Image directly to user's buffer + // + if (Buffer == NULL || *BufferSize < ImageLength) { + *BufferSize = ImageLength; + return EFI_BUFFER_TOO_SMALL; + } + + *BufferSize = ImageLength; + CopyMem (Buffer, ImageBuffer, ImageLength); + return EFI_SUCCESS; + + } else { + // + // Compressed: Uncompress before copying + // + Status = gBS->LocateProtocol (&gEfiDecompressProtocolGuid, NULL, (VOID **) &Decompress); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + Status = Decompress->GetInfo ( + Decompress, + ImageBuffer, + ImageLength, + &DestinationSize, + &ScratchSize + ); + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + if (Buffer == NULL || *BufferSize < DestinationSize) { + *BufferSize = DestinationSize; + return EFI_BUFFER_TOO_SMALL; + } + + *BufferSize = DestinationSize; + Scratch = AllocatePool (ScratchSize); + if (Scratch == NULL) { + return EFI_DEVICE_ERROR; + } + + Status = Decompress->Decompress ( + Decompress, + ImageBuffer, + ImageLength, + Buffer, + DestinationSize, + Scratch, + ScratchSize + ); + FreePool (Scratch); + + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + return EFI_SUCCESS; + } + } + + return EFI_NOT_FOUND; +} + +/** + Initialize a PCI LoadFile2 instance. + + @param PciIoDevice PCI IO Device. + +**/ +VOID +InitializePciLoadFile2 ( + IN PCI_IO_DEVICE *PciIoDevice + ) +{ + PciIoDevice->LoadFile2.LoadFile = LoadFile2; +} + +/** + Causes the driver to load a specified file. + + @param This Indicates a pointer to the calling context. + @param FilePath The device specific path of the file to load. + @param BootPolicy Should always be FALSE. + @param BufferSize On input the size of Buffer in bytes. On output with a return + code of EFI_SUCCESS, the amount of data transferred to Buffer. + On output with a return code of EFI_BUFFER_TOO_SMALL, + the size of Buffer required to retrieve the requested file. + @param Buffer The memory buffer to transfer the file to. If Buffer is NULL, + then no the size of the requested file is returned in BufferSize. + + @retval EFI_SUCCESS The file was loaded. + @retval EFI_UNSUPPORTED BootPolicy is TRUE. + @retval EFI_INVALID_PARAMETER FilePath is not a valid device path, or + BufferSize is NULL. + @retval EFI_NOT_FOUND Not found PCI Option Rom on PCI device. + @retval EFI_DEVICE_ERROR Failed to decompress PCI Option Rom image. + @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to read the current directory entry. + BufferSize has been updated with the size needed to complete the request. + +**/ +EFI_STATUS +EFIAPI +LoadFile2 ( + IN EFI_LOAD_FILE2_PROTOCOL *This, + IN EFI_DEVICE_PATH_PROTOCOL *FilePath, + IN BOOLEAN BootPolicy, + IN OUT UINTN *BufferSize, + IN VOID *Buffer OPTIONAL + ) +{ + PCI_IO_DEVICE *PciIoDevice; + + if (BootPolicy) { + return EFI_UNSUPPORTED; + } + PciIoDevice = PCI_IO_DEVICE_FROM_LOAD_FILE2_THIS (This); + + return LocalLoadFile2 ( + PciIoDevice, + FilePath, + BufferSize, + Buffer + ); +} + +/** + Get Pci device's oprom information. + + @param PciIoDevice Input Pci device instance. + Output Pci device instance with updated OptionRom size. + + @retval EFI_NOT_FOUND Pci device has not Option Rom. + @retval EFI_SUCCESS Pci device has Option Rom. + +**/ +EFI_STATUS +GetOpRomInfo ( + IN OUT PCI_IO_DEVICE *PciIoDevice + ) +{ + UINT8 RomBarIndex; + UINT32 AllOnes; + UINT64 Address; + EFI_STATUS Status; + UINT8 Bus; + UINT8 Device; + UINT8 Function; + EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *PciRootBridgeIo; + + Bus = PciIoDevice->BusNumber; + Device = PciIoDevice->DeviceNumber; + Function = PciIoDevice->FunctionNumber; + + PciRootBridgeIo = PciIoDevice->PciRootBridgeIo; + + // + // Offset is 0x30 if is not ppb + // + + // + // 0x30 + // + RomBarIndex = PCI_EXPANSION_ROM_BASE; + + if (IS_PCI_BRIDGE (&PciIoDevice->Pci)) { + // + // If is ppb, 0x38 + // + RomBarIndex = PCI_BRIDGE_ROMBAR; + } + // + // The bit0 is 0 to prevent the enabling of the Rom address decoder + // + AllOnes = 0xfffffffe; + Address = EFI_PCI_ADDRESS (Bus, Device, Function, RomBarIndex); + + Status = PciRootBridgeIo->Pci.Write ( + PciRootBridgeIo, + EfiPciWidthUint32, + Address, + 1, + &AllOnes + ); + if (EFI_ERROR (Status)) { + return EFI_NOT_FOUND; + } + + // + // Read back + // + Status = PciRootBridgeIo->Pci.Read( + PciRootBridgeIo, + EfiPciWidthUint32, + Address, + 1, + &AllOnes + ); + if (EFI_ERROR (Status)) { + return EFI_NOT_FOUND; + } + + // + // Bits [1, 10] are reserved + // + AllOnes &= 0xFFFFF800; + if ((AllOnes == 0) || (AllOnes == 0xFFFFF800)) { + return EFI_NOT_FOUND; + } + + PciIoDevice->RomSize = (UINT64) ((~AllOnes) + 1); + return EFI_SUCCESS; +} + +/** + Check if the RomImage contains EFI Images. + + @param RomImage The ROM address of Image for check. + @param RomSize Size of ROM for check. + + @retval TRUE ROM contain EFI Image. + @retval FALSE ROM not contain EFI Image. + +**/ +BOOLEAN +ContainEfiImage ( + IN VOID *RomImage, + IN UINT64 RomSize + ) +{ + PCI_EXPANSION_ROM_HEADER *RomHeader; + PCI_DATA_STRUCTURE *RomPcir; + BOOLEAN FirstCheck; + + FirstCheck = TRUE; + RomHeader = RomImage; + + while ((UINT8 *) RomHeader < (UINT8 *) RomImage + RomSize) { + if (RomHeader->Signature != PCI_EXPANSION_ROM_HEADER_SIGNATURE) { + if (FirstCheck) { + return FALSE; + } else { + RomHeader = (PCI_EXPANSION_ROM_HEADER *) ((UINT8 *) RomHeader + 512); + continue; + } + } + + FirstCheck = FALSE; + RomPcir = (PCI_DATA_STRUCTURE *) ((UINT8 *) RomHeader + RomHeader->PcirOffset); + + if (RomPcir->CodeType == PCI_CODE_TYPE_EFI_IMAGE) { + return TRUE; + } + + RomHeader = (PCI_EXPANSION_ROM_HEADER *) ((UINT8 *) RomHeader + RomPcir->Length * 512); + } + + return FALSE; +} + + +/** + Load Option Rom image for specified PCI device. + + @param PciDevice Pci device instance. + @param RomBase Base address of Option Rom. + + @retval EFI_OUT_OF_RESOURCES No enough memory to hold image. + @retval EFI_SUCESS Successfully loaded Option Rom. + +**/ +EFI_STATUS +LoadOpRomImage ( + IN PCI_IO_DEVICE *PciDevice, + IN UINT64 RomBase + ) +{ + UINT8 RomBarIndex; + UINT8 Indicator; + UINT16 OffsetPcir; + UINT32 RomBarOffset; + UINT32 RomBar; + EFI_STATUS RetStatus; + BOOLEAN FirstCheck; + UINT8 *Image; + PCI_EXPANSION_ROM_HEADER *RomHeader; + PCI_DATA_STRUCTURE *RomPcir; + UINT64 RomSize; + UINT64 RomImageSize; + UINT8 *RomInMemory; + UINT8 CodeType; + + RomSize = PciDevice->RomSize; + + Indicator = 0; + RomImageSize = 0; + RomInMemory = NULL; + CodeType = 0xFF; + + // + // Get the RomBarIndex + // + + // + // 0x30 + // + RomBarIndex = PCI_EXPANSION_ROM_BASE; + if (IS_PCI_BRIDGE (&(PciDevice->Pci))) { + // + // if is ppb + // + + // + // 0x38 + // + RomBarIndex = PCI_BRIDGE_ROMBAR; + } + // + // Allocate memory for Rom header and PCIR + // + RomHeader = AllocatePool (sizeof (PCI_EXPANSION_ROM_HEADER)); + if (RomHeader == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + RomPcir = AllocatePool (sizeof (PCI_DATA_STRUCTURE)); + if (RomPcir == NULL) { + FreePool (RomHeader); + return EFI_OUT_OF_RESOURCES; + } + + RomBar = (UINT32) RomBase; + + // + // Enable RomBar + // + RomDecode (PciDevice, RomBarIndex, RomBar, TRUE); + + RomBarOffset = RomBar; + RetStatus = EFI_NOT_FOUND; + FirstCheck = TRUE; + + do { + PciDevice->PciRootBridgeIo->Mem.Read ( + PciDevice->PciRootBridgeIo, + EfiPciWidthUint8, + RomBarOffset, + sizeof (PCI_EXPANSION_ROM_HEADER), + (UINT8 *) RomHeader + ); + + if (RomHeader->Signature != PCI_EXPANSION_ROM_HEADER_SIGNATURE) { + RomBarOffset = RomBarOffset + 512; + if (FirstCheck) { + break; + } else { + RomImageSize = RomImageSize + 512; + continue; + } + } + + FirstCheck = FALSE; + OffsetPcir = RomHeader->PcirOffset; + PciDevice->PciRootBridgeIo->Mem.Read ( + PciDevice->PciRootBridgeIo, + EfiPciWidthUint8, + RomBarOffset + OffsetPcir, + sizeof (PCI_DATA_STRUCTURE), + (UINT8 *) RomPcir + ); + if (RomPcir->CodeType == PCI_CODE_TYPE_PCAT_IMAGE) { + CodeType = PCI_CODE_TYPE_PCAT_IMAGE; + } + Indicator = RomPcir->Indicator; + RomImageSize = RomImageSize + RomPcir->ImageLength * 512; + RomBarOffset = RomBarOffset + RomPcir->ImageLength * 512; + } while (((Indicator & 0x80) == 0x00) && ((RomBarOffset - RomBar) < RomSize)); + + // + // Some Legacy Cards do not report the correct ImageLength so used the maximum + // of the legacy length and the PCIR Image Length + // + if (CodeType == PCI_CODE_TYPE_PCAT_IMAGE) { + RomImageSize = MAX(RomImageSize, (((EFI_LEGACY_EXPANSION_ROM_HEADER *)RomHeader)->Size512 * 512)); + } + + if (RomImageSize > 0) { + RetStatus = EFI_SUCCESS; + Image = AllocatePool ((UINT32) RomImageSize); + if (Image == NULL) { + RomDecode (PciDevice, RomBarIndex, RomBar, FALSE); + FreePool (RomHeader); + FreePool (RomPcir); + return EFI_OUT_OF_RESOURCES; + } + + // + // Copy Rom image into memory + // + PciDevice->PciRootBridgeIo->Mem.Read ( + PciDevice->PciRootBridgeIo, + EfiPciWidthUint8, + RomBar, + (UINT32) RomImageSize, + Image + ); + RomInMemory = Image; + } + + RomDecode (PciDevice, RomBarIndex, RomBar, FALSE); + + PciDevice->PciIo.RomSize = RomImageSize; + PciDevice->PciIo.RomImage = RomInMemory; + + // + // For OpROM read from PCI device: + // Add the Rom Image to internal database for later PCI light enumeration + // + PciRomAddImageMapping ( + NULL, + PciDevice->PciRootBridgeIo->SegmentNumber, + PciDevice->BusNumber, + PciDevice->DeviceNumber, + PciDevice->FunctionNumber, + (UINT64) (UINTN) PciDevice->PciIo.RomImage, + PciDevice->PciIo.RomSize + ); + + // + // Free allocated memory + // + FreePool (RomHeader); + FreePool (RomPcir); + + return RetStatus; +} + +/** + Enable/Disable Option Rom decode. + + @param PciDevice Pci device instance. + @param RomBarIndex The BAR index of the standard PCI Configuration header to use as the + base address for resource range. The legal range for this field is 0..5. + @param RomBar Base address of Option Rom. + @param Enable Flag for enable/disable decode. + +**/ +VOID +RomDecode ( + IN PCI_IO_DEVICE *PciDevice, + IN UINT8 RomBarIndex, + IN UINT32 RomBar, + IN BOOLEAN Enable + ) +{ + UINT32 Value32; + UINT32 Offset; + EFI_PCI_IO_PROTOCOL *PciIo; + + PciIo = &PciDevice->PciIo; + if (Enable) { + // + // Clear all bars + // + for (Offset = 0x10; Offset <= 0x24; Offset += sizeof (UINT32)) { + PciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, Offset, 1, &gAllZero); + } + + // + // set the Rom base address: now is hardcode + // enable its decoder + // + Value32 = RomBar | 0x1; + PciIo->Pci.Write ( + PciIo, + (EFI_PCI_IO_PROTOCOL_WIDTH) EfiPciWidthUint32, + RomBarIndex, + 1, + &Value32 + ); + + // + // Programe all upstream bridge + // + ProgrameUpstreamBridgeForRom(PciDevice, RomBar, TRUE); + + // + // Setting the memory space bit in the function's command register + // + PCI_ENABLE_COMMAND_REGISTER(PciDevice, EFI_PCI_COMMAND_MEMORY_SPACE); + + } else { + + // + // disable command register decode to memory + // + PCI_DISABLE_COMMAND_REGISTER(PciDevice, EFI_PCI_COMMAND_MEMORY_SPACE); + + // + // Destroy the programmed bar in all the upstream bridge. + // + ProgrameUpstreamBridgeForRom(PciDevice, RomBar, FALSE); + + // + // disable rom decode + // + Value32 = 0xFFFFFFFE; + PciIo->Pci.Write ( + PciIo, + (EFI_PCI_IO_PROTOCOL_WIDTH) EfiPciWidthUint32, + RomBarIndex, + 1, + &Value32 + ); + + } +} + +/** + Load and start the Option Rom image. + + @param PciDevice Pci device instance. + + @retval EFI_SUCCESS Successfully loaded and started PCI Option Rom image. + @retval EFI_NOT_FOUND Failed to process PCI Option Rom image. + +**/ +EFI_STATUS +ProcessOpRomImage ( + IN PCI_IO_DEVICE *PciDevice + ) +{ + UINT8 Indicator; + UINT32 ImageSize; + VOID *RomBar; + UINT8 *RomBarOffset; + EFI_HANDLE ImageHandle; + EFI_STATUS Status; + EFI_STATUS RetStatus; + BOOLEAN FirstCheck; + EFI_PCI_EXPANSION_ROM_HEADER *EfiRomHeader; + PCI_DATA_STRUCTURE *Pcir; + EFI_DEVICE_PATH_PROTOCOL *PciOptionRomImageDevicePath; + MEDIA_RELATIVE_OFFSET_RANGE_DEVICE_PATH EfiOpRomImageNode; + VOID *Buffer; + UINTN BufferSize; + + Indicator = 0; + + // + // Get the Address of the Option Rom image + // + RomBar = PciDevice->PciIo.RomImage; + RomBarOffset = (UINT8 *) RomBar; + RetStatus = EFI_NOT_FOUND; + FirstCheck = TRUE; + + do { + EfiRomHeader = (EFI_PCI_EXPANSION_ROM_HEADER *) RomBarOffset; + if (EfiRomHeader->Signature != PCI_EXPANSION_ROM_HEADER_SIGNATURE) { + RomBarOffset += 512; + if (FirstCheck) { + break; + } else { + continue; + } + } + + FirstCheck = FALSE; + Pcir = (PCI_DATA_STRUCTURE *) (RomBarOffset + EfiRomHeader->PcirOffset); + ImageSize = (UINT32) (Pcir->ImageLength * 512); + Indicator = Pcir->Indicator; + + // + // Create Pci Option Rom Image device path header + // + EfiOpRomImageNode.Header.Type = MEDIA_DEVICE_PATH; + EfiOpRomImageNode.Header.SubType = MEDIA_RELATIVE_OFFSET_RANGE_DP; + SetDevicePathNodeLength (&EfiOpRomImageNode.Header, sizeof (EfiOpRomImageNode)); + EfiOpRomImageNode.StartingOffset = (UINTN) RomBarOffset - (UINTN) RomBar; + EfiOpRomImageNode.EndingOffset = (UINTN) RomBarOffset + ImageSize - 1 - (UINTN) RomBar; + + PciOptionRomImageDevicePath = AppendDevicePathNode (PciDevice->DevicePath, &EfiOpRomImageNode.Header); + ASSERT (PciOptionRomImageDevicePath != NULL); + + // + // load image and start image + // + BufferSize = 0; + Buffer = NULL; + Status = EFI_SUCCESS; + ImageHandle = NULL; + + if (!EFI_ERROR (Status)) { + Status = gBS->LoadImage ( + FALSE, + gPciBusDriverBinding.DriverBindingHandle, + PciOptionRomImageDevicePath, + Buffer, + BufferSize, + &ImageHandle + ); + } + + // + // load image and start image + // + if (!EFI_ERROR (Status)) { + Status = gBS->LoadImage ( + FALSE, + gPciBusDriverBinding.DriverBindingHandle, + PciOptionRomImageDevicePath, + Buffer, + BufferSize, + &ImageHandle + ); + } + + FreePool (PciOptionRomImageDevicePath); + + if (!EFI_ERROR (Status)) { + Status = gBS->StartImage (ImageHandle, NULL, NULL); + if (!EFI_ERROR (Status)) { + AddDriver (PciDevice, ImageHandle); + PciRomAddImageMapping ( + ImageHandle, + PciDevice->PciRootBridgeIo->SegmentNumber, + PciDevice->BusNumber, + PciDevice->DeviceNumber, + PciDevice->FunctionNumber, + (UINT64) (UINTN) PciDevice->PciIo.RomImage, + PciDevice->PciIo.RomSize + ); + RetStatus = EFI_SUCCESS; + } + } + + RomBarOffset += ImageSize; + + } while (((Indicator & 0x80) == 0x00) && ((UINTN) (RomBarOffset - (UINT8 *) RomBar) < PciDevice->RomSize)); + + return RetStatus; +} + diff --git a/MdeModulePkg/Bus/Pci/PciBusDxe/PciOptionRomSupport.h b/MdeModulePkg/Bus/Pci/PciBusDxe/PciOptionRomSupport.h new file mode 100644 index 0000000000..4615a5fe33 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/PciBusDxe/PciOptionRomSupport.h @@ -0,0 +1,143 @@ +/** @file + PCI Rom supporting funtions declaration for PCI Bus module. + +Copyright (c) 2006 - 2009, Intel Corporation +All rights reserved. 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. + +**/ + +#ifndef _EFI_PCI_OPTION_ROM_SUPPORT_H_ +#define _EFI_PCI_OPTION_ROM_SUPPORT_H_ + + +/** + Initialize a PCI LoadFile2 instance. + + @param PciIoDevice PCI IO Device. + +**/ +VOID +InitializePciLoadFile2 ( + IN PCI_IO_DEVICE *PciIoDevice + ); + +/** + Causes the driver to load a specified file. + + @param This Indicates a pointer to the calling context. + @param FilePath The device specific path of the file to load. + @param BootPolicy Should always be FALSE. + @param BufferSize On input the size of Buffer in bytes. On output with a return + code of EFI_SUCCESS, the amount of data transferred to Buffer. + On output with a return code of EFI_BUFFER_TOO_SMALL, + the size of Buffer required to retrieve the requested file. + @param Buffer The memory buffer to transfer the file to. If Buffer is NULL, + then no the size of the requested file is returned in BufferSize. + + @retval EFI_SUCCESS The file was loaded. + @retval EFI_UNSUPPORTED BootPolicy is TRUE. + @retval EFI_INVALID_PARAMETER FilePath is not a valid device path, or + BufferSize is NULL. + @retval EFI_NOT_FOUND Not found PCI Option Rom on PCI device. + @retval EFI_DEVICE_ERROR Failed to decompress PCI Option Rom image. + @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to read the current directory entry. + BufferSize has been updated with the size needed to complete the request. + +**/ +EFI_STATUS +EFIAPI +LoadFile2 ( + IN EFI_LOAD_FILE2_PROTOCOL *This, + IN EFI_DEVICE_PATH_PROTOCOL *FilePath, + IN BOOLEAN BootPolicy, + IN OUT UINTN *BufferSize, + IN VOID *Buffer OPTIONAL + ); + +/** + Check if the RomImage contains EFI Images. + + @param RomImage The ROM address of Image for check. + @param RomSize Size of ROM for check. + + @retval TRUE ROM contain EFI Image. + @retval FALSE ROM not contain EFI Image. + +**/ +BOOLEAN +ContainEfiImage ( + IN VOID *RomImage, + IN UINT64 RomSize + ); + + +/** + Get Pci device's oprom information. + + @param PciIoDevice Input Pci device instance. + Output Pci device instance with updated OptionRom size. + + @retval EFI_NOT_FOUND Pci device has not Option Rom. + @retval EFI_SUCCESS Pci device has Option Rom. + +**/ +EFI_STATUS +GetOpRomInfo ( + IN OUT PCI_IO_DEVICE *PciIoDevice + ); + +/** + Load Option Rom image for specified PCI device. + + @param PciDevice Pci device instance. + @param RomBase Base address of Option Rom. + + @retval EFI_OUT_OF_RESOURCES No enough memory to hold image. + @retval EFI_SUCESS Successfully loaded Option Rom. + +**/ +EFI_STATUS +LoadOpRomImage ( + IN PCI_IO_DEVICE *PciDevice, + IN UINT64 RomBase + ); + +/** + Enable/Disable Option Rom decode. + + @param PciDevice Pci device instance. + @param RomBarIndex The BAR index of the standard PCI Configuration header to use as the + base address for resource range. The legal range for this field is 0..5. + @param RomBar Base address of Option Rom. + @param Enable Flag for enable/disable decode. + +**/ +VOID +RomDecode ( + IN PCI_IO_DEVICE *PciDevice, + IN UINT8 RomBarIndex, + IN UINT32 RomBar, + IN BOOLEAN Enable + ); + +/** + Load and start the Option Rom image. + + @param PciDevice Pci device instance. + + @retval EFI_SUCCESS Successfully loaded and started PCI Option Rom image. + @retval EFI_NOT_FOUND Failed to process PCI Option Rom image. + +**/ +EFI_STATUS +ProcessOpRomImage ( + IN PCI_IO_DEVICE *PciDevice + ); + +#endif diff --git a/MdeModulePkg/Bus/Pci/PciBusDxe/PciPowerManagement.c b/MdeModulePkg/Bus/Pci/PciBusDxe/PciPowerManagement.c new file mode 100644 index 0000000000..860d427f35 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/PciBusDxe/PciPowerManagement.c @@ -0,0 +1,68 @@ +/** @file + Power management support fucntions implementation for PCI Bus module. + +Copyright (c) 2006 - 2009, Intel Corporation +All rights reserved. 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 function is intended to turn off PWE assertion and + put the device to D0 state if the device supports + PCI Power Management. + + @param PciIoDevice PCI device instance. + + @retval EFI_UNSUPPORTED PCI Device does not support power management. + @retval EFI_SUCCESS Turned off PWE successfully. + +**/ +EFI_STATUS +ResetPowerManagementFeature ( + IN PCI_IO_DEVICE *PciIoDevice + ) +{ + EFI_STATUS Status; + UINT8 PowerManagementRegBlock; + UINT16 PowerManagementCSR; + + PowerManagementRegBlock = 0; + + Status = LocateCapabilityRegBlock ( + PciIoDevice, + EFI_PCI_CAPABILITY_ID_PMI, + &PowerManagementRegBlock, + NULL + ); + + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + // + // Turn off the PWE assertion and put the device into D0 State + // + PowerManagementCSR = 0x8000; + + // + // Write PMCSR + // + PciIoDevice->PciIo.Pci.Write ( + &PciIoDevice->PciIo, + EfiPciIoWidthUint16, + PowerManagementRegBlock + 4, + 1, + &PowerManagementCSR + ); + + return EFI_SUCCESS; +} + diff --git a/MdeModulePkg/Bus/Pci/PciBusDxe/PciPowerManagement.h b/MdeModulePkg/Bus/Pci/PciBusDxe/PciPowerManagement.h new file mode 100644 index 0000000000..88462709e3 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/PciBusDxe/PciPowerManagement.h @@ -0,0 +1,34 @@ +/** @file + Power management support fucntions delaration for PCI Bus module. + +Copyright (c) 2006 - 2009, Intel Corporation +All rights reserved. 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. + +**/ + +#ifndef _EFI_PCI_POWER_MANAGEMENT_H_ +#define _EFI_PCI_POWER_MANAGEMENT_H_ + +/** + This function is intended to turn off PWE assertion and + put the device to D0 state if the device supports + PCI Power Management. + + @param PciIoDevice PCI device instance. + + @retval EFI_UNSUPPORTED PCI Device does not support power management. + @retval EFI_SUCCESS Turned off PWE successfully. + +**/ +EFI_STATUS +ResetPowerManagementFeature ( + IN PCI_IO_DEVICE *PciIoDevice + ); + +#endif diff --git a/MdeModulePkg/Bus/Pci/PciBusDxe/PciResourceSupport.c b/MdeModulePkg/Bus/Pci/PciBusDxe/PciResourceSupport.c new file mode 100644 index 0000000000..2cacd441c8 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/PciBusDxe/PciResourceSupport.c @@ -0,0 +1,2344 @@ +/** @file + PCI resouces support functions implemntation for PCI Bus module. + +Copyright (c) 2006 - 2009, Intel Corporation +All rights reserved. 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" + +/** + The function is used to skip VGA range. + + @param Start Returned start address including VGA range. + @param Length The length of VGA range. + +**/ +VOID +SkipVGAAperture ( + OUT UINT64 *Start, + IN UINT64 Length + ) +{ + UINT64 Original; + UINT64 Mask; + UINT64 StartOffset; + UINT64 LimitOffset; + + ASSERT (Start != NULL); + // + // For legacy VGA, bit 10 to bit 15 is not decoded + // + Mask = 0x3FF; + + Original = *Start; + StartOffset = Original & Mask; + LimitOffset = ((*Start) + Length - 1) & Mask; + if (LimitOffset >= VGABASE1) { + *Start = *Start - StartOffset + VGALIMIT2 + 1; + } +} + +/** + This function is used to skip ISA aliasing aperture. + + @param Start Returned start address including ISA aliasing aperture. + @param Length The length of ISA aliasing aperture. + +**/ +VOID +SkipIsaAliasAperture ( + OUT UINT64 *Start, + IN UINT64 Length + ) +{ + + UINT64 Original; + UINT64 Mask; + UINT64 StartOffset; + UINT64 LimitOffset; + + ASSERT (Start != NULL); + + // + // For legacy ISA, bit 10 to bit 15 is not decoded + // + Mask = 0x3FF; + + Original = *Start; + StartOffset = Original & Mask; + LimitOffset = ((*Start) + Length - 1) & Mask; + + if (LimitOffset >= ISABASE) { + *Start = *Start - StartOffset + ISALIMIT + 1; + } +} + +/** + This function inserts a resource node into the resource list. + The resource list is sorted in descend order. + + @param Bridge PCI resource node for bridge. + @param ResNode Resource node want to be inserted. + +**/ +VOID +InsertResourceNode ( + IN OUT PCI_RESOURCE_NODE *Bridge, + IN PCI_RESOURCE_NODE *ResNode + ) +{ + LIST_ENTRY *CurrentLink; + PCI_RESOURCE_NODE *Temp; + UINT64 ResNodeAlignRest; + UINT64 TempAlignRest; + + ASSERT (Bridge != NULL); + ASSERT (ResNode != NULL); + + InsertHeadList (&Bridge->ChildList, &ResNode->Link); + + CurrentLink = Bridge->ChildList.ForwardLink->ForwardLink; + while (CurrentLink != &Bridge->ChildList) { + Temp = RESOURCE_NODE_FROM_LINK (CurrentLink); + + if (ResNode->Alignment > Temp->Alignment) { + break; + } else if (ResNode->Alignment == Temp->Alignment) { + ResNodeAlignRest = ResNode->Length & ResNode->Alignment; + TempAlignRest = Temp->Length & Temp->Alignment; + if ((ResNodeAlignRest == 0) || (ResNodeAlignRest >= TempAlignRest)) { + break; + } + } + + SwapListEntries (&ResNode->Link, CurrentLink); + + CurrentLink = ResNode->Link.ForwardLink; + } +} + +/** + This routine is used to merge two different resource trees in need of + resoure degradation. + + For example, if an upstream PPB doesn't support, + prefetchable memory decoding, the PCI bus driver will choose to call this function + to merge prefectchable memory resource list into normal memory list. + + If the TypeMerge is TRUE, Res resource type is changed to the type of destination resource + type. + If Dst is NULL or Res is NULL, ASSERT (). + + @param Dst Point to destination resource tree. + @param Res Point to source resource tree. + @param TypeMerge If the TypeMerge is TRUE, Res resource type is changed to the type of + destination resource type. + +**/ +VOID +MergeResourceTree ( + IN PCI_RESOURCE_NODE *Dst, + IN PCI_RESOURCE_NODE *Res, + IN BOOLEAN TypeMerge + ) +{ + + LIST_ENTRY *CurrentLink; + PCI_RESOURCE_NODE *Temp; + + ASSERT (Dst != NULL); + ASSERT (Res != NULL); + + while (!IsListEmpty (&Res->ChildList)) { + CurrentLink = Res->ChildList.ForwardLink; + + Temp = RESOURCE_NODE_FROM_LINK (CurrentLink); + + if (TypeMerge) { + Temp->ResType = Dst->ResType; + } + + RemoveEntryList (CurrentLink); + InsertResourceNode (Dst, Temp); + } +} + +/** + This function is used to calculate the IO16 aperture + for a bridge. + + @param Bridge PCI resource node for bridge. + +**/ +VOID +CalculateApertureIo16 ( + IN PCI_RESOURCE_NODE *Bridge + ) +{ + EFI_STATUS Status; + UINT64 Aperture; + LIST_ENTRY *CurrentLink; + PCI_RESOURCE_NODE *Node; + UINT64 Offset; + BOOLEAN IsaEnable; + BOOLEAN VGAEnable; + EFI_PCI_PLATFORM_POLICY PciPolicy; + + // + // Always assume there is ISA device and VGA device on the platform + // will be customized later + // + IsaEnable = FALSE; + VGAEnable = FALSE; + + // + // Check PciPlatform policy + // + if (gPciPlatformProtocol != NULL) { + Status = gPciPlatformProtocol->GetPlatformPolicy ( + gPciPlatformProtocol, + &PciPolicy + ); + if (!EFI_ERROR (Status)) { + if ((PciPolicy & EFI_RESERVE_ISA_IO_ALIAS) != 0) { + IsaEnable = TRUE; + } + if ((PciPolicy & EFI_RESERVE_VGA_IO_ALIAS) != 0) { + VGAEnable = TRUE; + } + } + } else if (gPciOverrideProtocol != NULL) { + Status = gPciOverrideProtocol->GetPlatformPolicy ( + gPciOverrideProtocol, + &PciPolicy + ); + if (!EFI_ERROR (Status)) { + if ((PciPolicy & EFI_RESERVE_ISA_IO_ALIAS) != 0) { + IsaEnable = TRUE; + } + if ((PciPolicy & EFI_RESERVE_VGA_IO_ALIAS) != 0) { + VGAEnable = TRUE; + } + } + } + + Aperture = 0; + + if (Bridge == NULL) { + return ; + } + + CurrentLink = Bridge->ChildList.ForwardLink; + + // + // Assume the bridge is aligned + // + while (CurrentLink != &Bridge->ChildList) { + + Node = RESOURCE_NODE_FROM_LINK (CurrentLink); + + // + // Consider the aperture alignment + // + Offset = Aperture & (Node->Alignment); + + if (Offset != 0) { + + Aperture = Aperture + (Node->Alignment + 1) - Offset; + + } + + // + // IsaEnable and VGAEnable can not be implemented now. + // If both of them are enabled, then the IO resource would + // become too limited to meet the requirement of most of devices. + // + if (IsaEnable || VGAEnable) { + if (!IS_PCI_BRIDGE (&(Node->PciDev->Pci)) && !IS_CARDBUS_BRIDGE (&(Node->PciDev->Pci))) { + // + // Check if there is need to support ISA/VGA decoding + // If so, we need to avoid isa/vga aliasing range + // + if (IsaEnable) { + SkipIsaAliasAperture ( + &Aperture, + Node->Length + ); + Offset = Aperture & (Node->Alignment); + if (Offset != 0) { + Aperture = Aperture + (Node->Alignment + 1) - Offset; + } + } else if (VGAEnable) { + SkipVGAAperture ( + &Aperture, + Node->Length + ); + Offset = Aperture & (Node->Alignment); + if (Offset != 0) { + Aperture = Aperture + (Node->Alignment + 1) - Offset; + } + } + } + } + + Node->Offset = Aperture; + + // + // Increment aperture by the length of node + // + Aperture += Node->Length; + + CurrentLink = CurrentLink->ForwardLink; + } + + // + // At last, adjust the aperture with the bridge's + // alignment + // + Offset = Aperture & (Bridge->Alignment); + + if (Offset != 0) { + Aperture = Aperture + (Bridge->Alignment + 1) - Offset; + } + + Bridge->Length = Aperture; + // + // At last, adjust the bridge's alignment to the first child's alignment + // if the bridge has at least one child + // + CurrentLink = Bridge->ChildList.ForwardLink; + if (CurrentLink != &Bridge->ChildList) { + Node = RESOURCE_NODE_FROM_LINK (CurrentLink); + if (Node->Alignment > Bridge->Alignment) { + Bridge->Alignment = Node->Alignment; + } + } +} + +/** + This function is used to calculate the resource aperture + for a given bridge device. + + @param Bridge PCI resouce node for given bridge device. + +**/ +VOID +CalculateResourceAperture ( + IN PCI_RESOURCE_NODE *Bridge + ) +{ + UINT64 Aperture; + LIST_ENTRY *CurrentLink; + PCI_RESOURCE_NODE *Node; + + UINT64 Offset; + + Aperture = 0; + + if (Bridge == NULL) { + return ; + } + + if (Bridge->ResType == PciBarTypeIo16) { + + CalculateApertureIo16 (Bridge); + return ; + } + + CurrentLink = Bridge->ChildList.ForwardLink; + + // + // Assume the bridge is aligned + // + while (CurrentLink != &Bridge->ChildList) { + + Node = RESOURCE_NODE_FROM_LINK (CurrentLink); + + // + // Apply padding resource if available + // + Offset = Aperture & (Node->Alignment); + + if (Offset != 0) { + + Aperture = Aperture + (Node->Alignment + 1) - Offset; + + } + + // + // Recode current aperture as a offset + // this offset will be used in future real allocation + // + Node->Offset = Aperture; + + // + // Increment aperture by the length of node + // + Aperture += Node->Length; + + // + // Consider the aperture alignment + // + CurrentLink = CurrentLink->ForwardLink; + } + + // + // At last, adjust the aperture with the bridge's + // alignment + // + Offset = Aperture & (Bridge->Alignment); + if (Offset != 0) { + Aperture = Aperture + (Bridge->Alignment + 1) - Offset; + } + + // + // If the bridge has already padded the resource and the + // amount of padded resource is larger, then keep the + // padded resource + // + if (Bridge->Length < Aperture) { + Bridge->Length = Aperture; + } + + // + // At last, adjust the bridge's alignment to the first child's alignment + // if the bridge has at least one child + // + CurrentLink = Bridge->ChildList.ForwardLink; + if (CurrentLink != &Bridge->ChildList) { + Node = RESOURCE_NODE_FROM_LINK (CurrentLink); + if (Node->Alignment > Bridge->Alignment) { + Bridge->Alignment = Node->Alignment; + } + } +} + +/** + Get IO/Memory resource infor for given PCI device. + + @param PciDev Pci device instance. + @param IoNode Resource info node for IO . + @param Mem32Node Resource info node for 32-bit memory. + @param PMem32Node Resource info node for 32-bit Prefetchable Memory. + @param Mem64Node Resource info node for 64-bit memory. + @param PMem64Node Resource info node for 64-bit Prefetchable Memory. + +**/ +VOID +GetResourceFromDevice ( + IN PCI_IO_DEVICE *PciDev, + IN OUT PCI_RESOURCE_NODE *IoNode, + IN OUT PCI_RESOURCE_NODE *Mem32Node, + IN OUT PCI_RESOURCE_NODE *PMem32Node, + IN OUT PCI_RESOURCE_NODE *Mem64Node, + IN OUT PCI_RESOURCE_NODE *PMem64Node + ) +{ + + UINT8 Index; + PCI_RESOURCE_NODE *Node; + BOOLEAN ResourceRequested; + + Node = NULL; + ResourceRequested = FALSE; + + for (Index = 0; Index < PCI_MAX_BAR; Index++) { + + switch ((PciDev->PciBar)[Index].BarType) { + + case PciBarTypeMem32: + + Node = CreateResourceNode ( + PciDev, + (PciDev->PciBar)[Index].Length, + (PciDev->PciBar)[Index].Alignment, + Index, + PciBarTypeMem32, + PciResUsageTypical + ); + + InsertResourceNode ( + Mem32Node, + Node + ); + + ResourceRequested = TRUE; + break; + + case PciBarTypeMem64: + + Node = CreateResourceNode ( + PciDev, + (PciDev->PciBar)[Index].Length, + (PciDev->PciBar)[Index].Alignment, + Index, + PciBarTypeMem64, + PciResUsageTypical + ); + + InsertResourceNode ( + Mem64Node, + Node + ); + + ResourceRequested = TRUE; + break; + + case PciBarTypePMem64: + + Node = CreateResourceNode ( + PciDev, + (PciDev->PciBar)[Index].Length, + (PciDev->PciBar)[Index].Alignment, + Index, + PciBarTypePMem64, + PciResUsageTypical + ); + + InsertResourceNode ( + PMem64Node, + Node + ); + + ResourceRequested = TRUE; + break; + + case PciBarTypePMem32: + + Node = CreateResourceNode ( + PciDev, + (PciDev->PciBar)[Index].Length, + (PciDev->PciBar)[Index].Alignment, + Index, + PciBarTypePMem32, + PciResUsageTypical + ); + + InsertResourceNode ( + PMem32Node, + Node + ); + ResourceRequested = TRUE; + break; + + case PciBarTypeIo16: + case PciBarTypeIo32: + + Node = CreateResourceNode ( + PciDev, + (PciDev->PciBar)[Index].Length, + (PciDev->PciBar)[Index].Alignment, + Index, + PciBarTypeIo16, + PciResUsageTypical + ); + + InsertResourceNode ( + IoNode, + Node + ); + ResourceRequested = TRUE; + break; + + case PciBarTypeUnknown: + break; + + default: + break; + } + } + + // + // Add VF resource + // + for (Index = 0; Index < PCI_MAX_BAR; Index++) { + + switch ((PciDev->VfPciBar)[Index].BarType) { + + case PciBarTypeMem32: + + Node = CreateVfResourceNode ( + PciDev, + (PciDev->VfPciBar)[Index].Length, + (PciDev->VfPciBar)[Index].Alignment, + Index, + PciBarTypeMem32, + PciResUsageTypical + ); + + InsertResourceNode ( + Mem32Node, + Node + ); + + break; + + case PciBarTypeMem64: + + Node = CreateVfResourceNode ( + PciDev, + (PciDev->VfPciBar)[Index].Length, + (PciDev->VfPciBar)[Index].Alignment, + Index, + PciBarTypeMem64, + PciResUsageTypical + ); + + InsertResourceNode ( + Mem64Node, + Node + ); + + break; + + case PciBarTypePMem64: + + Node = CreateVfResourceNode ( + PciDev, + (PciDev->VfPciBar)[Index].Length, + (PciDev->VfPciBar)[Index].Alignment, + Index, + PciBarTypePMem64, + PciResUsageTypical + ); + + InsertResourceNode ( + PMem64Node, + Node + ); + + break; + + case PciBarTypePMem32: + + Node = CreateVfResourceNode ( + PciDev, + (PciDev->VfPciBar)[Index].Length, + (PciDev->VfPciBar)[Index].Alignment, + Index, + PciBarTypePMem32, + PciResUsageTypical + ); + + InsertResourceNode ( + PMem32Node, + Node + ); + break; + + case PciBarTypeIo16: + case PciBarTypeIo32: + break; + + case PciBarTypeUnknown: + break; + + default: + break; + } + } + // If there is no resource requested from this device, + // then we indicate this device has been allocated naturally. + // + if (!ResourceRequested) { + PciDev->Allocated = TRUE; + } +} + +/** + This function is used to create a resource node. + + @param PciDev Pci device instance. + @param Length Length of Io/Memory resource. + @param Alignment Alignment of resource. + @param Bar Bar index. + @param ResType Type of resource: IO/Memory. + @param ResUsage Resource usage. + + @return PCI resource node created for given PCI device. + NULL means PCI resource node is not created. + +**/ +PCI_RESOURCE_NODE * +CreateResourceNode ( + IN PCI_IO_DEVICE *PciDev, + IN UINT64 Length, + IN UINT64 Alignment, + IN UINT8 Bar, + IN PCI_BAR_TYPE ResType, + IN PCI_RESOURCE_USAGE ResUsage + ) +{ + PCI_RESOURCE_NODE *Node; + + Node = NULL; + + Node = AllocateZeroPool (sizeof (PCI_RESOURCE_NODE)); + ASSERT (Node != NULL); + if (Node == NULL) { + return NULL; + } + + Node->Signature = PCI_RESOURCE_SIGNATURE; + Node->PciDev = PciDev; + Node->Length = Length; + Node->Alignment = Alignment; + Node->Bar = Bar; + Node->ResType = ResType; + Node->Reserved = FALSE; + Node->ResourceUsage = ResUsage; + InitializeListHead (&Node->ChildList); + + return Node; +} + +/** + This function is used to create a IOV VF resource node. + + @param PciDev Pci device instance. + @param Length Length of Io/Memory resource. + @param Alignment Alignment of resource. + @param Bar Bar index. + @param ResType Type of resource: IO/Memory. + @param ResUsage Resource usage. + + @return PCI resource node created for given VF PCI device. + NULL means PCI resource node is not created. + +**/ +PCI_RESOURCE_NODE * +CreateVfResourceNode ( + IN PCI_IO_DEVICE *PciDev, + IN UINT64 Length, + IN UINT64 Alignment, + IN UINT8 Bar, + IN PCI_BAR_TYPE ResType, + IN PCI_RESOURCE_USAGE ResUsage + ) +{ + PCI_RESOURCE_NODE *Node; + + DEBUG (( + EFI_D_INFO, + "PCI-IOV B%x.D%x.F%x - VfResource (Bar - 0x%x) (Type - 0x%x) (Length - 0x%x)\n", + (UINTN)PciDev->BusNumber, + (UINTN)PciDev->DeviceNumber, + (UINTN)PciDev->FunctionNumber, + (UINTN)Bar, + (UINTN)ResType, + (UINTN)Length + )); + + Node = CreateResourceNode (PciDev, Length, Alignment, Bar, ResType, ResUsage); + if (Node == NULL) { + return Node; + } + + Node->Virtual = TRUE; + + return Node; +} + +/** + This function is used to extract resource request from + device node list. + + @param Bridge Pci device instance. + @param IoNode Resource info node for IO. + @param Mem32Node Resource info node for 32-bit memory. + @param PMem32Node Resource info node for 32-bit Prefetchable Memory. + @param Mem64Node Resource info node for 64-bit memory. + @param PMem64Node Resource info node for 64-bit Prefetchable Memory. + +**/ +VOID +CreateResourceMap ( + IN PCI_IO_DEVICE *Bridge, + IN OUT PCI_RESOURCE_NODE *IoNode, + IN OUT PCI_RESOURCE_NODE *Mem32Node, + IN OUT PCI_RESOURCE_NODE *PMem32Node, + IN OUT PCI_RESOURCE_NODE *Mem64Node, + IN OUT PCI_RESOURCE_NODE *PMem64Node + ) +{ + PCI_IO_DEVICE *Temp; + PCI_RESOURCE_NODE *IoBridge; + PCI_RESOURCE_NODE *Mem32Bridge; + PCI_RESOURCE_NODE *PMem32Bridge; + PCI_RESOURCE_NODE *Mem64Bridge; + PCI_RESOURCE_NODE *PMem64Bridge; + LIST_ENTRY *CurrentLink; + + CurrentLink = Bridge->ChildList.ForwardLink; + + while (CurrentLink != NULL && CurrentLink != &Bridge->ChildList) { + + Temp = PCI_IO_DEVICE_FROM_LINK (CurrentLink); + + // + // Create resource nodes for this device by scanning the + // Bar array in the device private data + // If the upstream bridge doesn't support this device, + // no any resource node will be created for this device + // + GetResourceFromDevice ( + Temp, + IoNode, + Mem32Node, + PMem32Node, + Mem64Node, + PMem64Node + ); + + if (IS_PCI_BRIDGE (&Temp->Pci)) { + + // + // If the device has children, create a bridge resource node for this PPB + // Note: For PPB, memory aperture is aligned with 1MB and IO aperture + // is aligned with 4KB + // This device is typically a bridge device like PPB and P2C + // Note: 0x1000 aligned + // + IoBridge = CreateResourceNode ( + Temp, + 0, + 0xFFF, + PPB_IO_RANGE, + PciBarTypeIo16, + PciResUsageTypical + ); + + Mem32Bridge = CreateResourceNode ( + Temp, + 0, + 0xFFFFF, + PPB_MEM32_RANGE, + PciBarTypeMem32, + PciResUsageTypical + ); + + PMem32Bridge = CreateResourceNode ( + Temp, + 0, + 0xFFFFF, + PPB_PMEM32_RANGE, + PciBarTypePMem32, + PciResUsageTypical + ); + + Mem64Bridge = CreateResourceNode ( + Temp, + 0, + 0xFFFFF, + PPB_MEM64_RANGE, + PciBarTypeMem64, + PciResUsageTypical + ); + + PMem64Bridge = CreateResourceNode ( + Temp, + 0, + 0xFFFFF, + PPB_PMEM64_RANGE, + PciBarTypePMem64, + PciResUsageTypical + ); + + // + // Recursively create resouce map on this bridge + // + CreateResourceMap ( + Temp, + IoBridge, + Mem32Bridge, + PMem32Bridge, + Mem64Bridge, + PMem64Bridge + ); + + if (ResourceRequestExisted (IoBridge)) { + InsertResourceNode ( + IoNode, + IoBridge + ); + } else { + FreePool (IoBridge); + IoBridge = NULL; + } + + // + // If there is node under this resource bridge, + // then calculate bridge's aperture of this type + // and insert it into the respective resource tree. + // If no, delete this resource bridge + // + if (ResourceRequestExisted (Mem32Bridge)) { + InsertResourceNode ( + Mem32Node, + Mem32Bridge + ); + } else { + FreePool (Mem32Bridge); + Mem32Bridge = NULL; + } + + // + // If there is node under this resource bridge, + // then calculate bridge's aperture of this type + // and insert it into the respective resource tree. + // If no, delete this resource bridge + // + if (ResourceRequestExisted (PMem32Bridge)) { + InsertResourceNode ( + PMem32Node, + PMem32Bridge + ); + } else { + FreePool (PMem32Bridge); + PMem32Bridge = NULL; + } + + // + // If there is node under this resource bridge, + // then calculate bridge's aperture of this type + // and insert it into the respective resource tree. + // If no, delete this resource bridge + // + if (ResourceRequestExisted (Mem64Bridge)) { + InsertResourceNode ( + Mem64Node, + Mem64Bridge + ); + } else { + FreePool (Mem64Bridge); + Mem64Bridge = NULL; + } + + // + // If there is node under this resource bridge, + // then calculate bridge's aperture of this type + // and insert it into the respective resource tree. + // If no, delete this resource bridge + // + if (ResourceRequestExisted (PMem64Bridge)) { + InsertResourceNode ( + PMem64Node, + PMem64Bridge + ); + } else { + FreePool (PMem64Bridge); + PMem64Bridge = NULL; + } + + } + + // + // If it is P2C, apply hard coded resource padding + // + if (IS_CARDBUS_BRIDGE (&Temp->Pci)) { + ResourcePaddingForCardBusBridge ( + Temp, + IoNode, + Mem32Node, + PMem32Node, + Mem64Node, + PMem64Node + ); + } + + CurrentLink = CurrentLink->ForwardLink; + } + + // + // To do some platform specific resource padding ... + // + ResourcePaddingPolicy ( + Bridge, + IoNode, + Mem32Node, + PMem32Node, + Mem64Node, + PMem64Node + ); + + // + // Degrade resource if necessary + // + DegradeResource ( + Bridge, + Mem32Node, + PMem32Node, + Mem64Node, + PMem64Node + ); + + // + // Calculate resource aperture for this bridge device + // + CalculateResourceAperture (Mem32Node); + CalculateResourceAperture (PMem32Node); + CalculateResourceAperture (Mem64Node); + CalculateResourceAperture (PMem64Node); + CalculateResourceAperture (IoNode); +} + +/** + This function is used to do the resource padding for a specific platform. + + @param PciDev Pci device instance. + @param IoNode Resource info node for IO. + @param Mem32Node Resource info node for 32-bit memory. + @param PMem32Node Resource info node for 32-bit Prefetchable Memory. + @param Mem64Node Resource info node for 64-bit memory. + @param PMem64Node Resource info node for 64-bit Prefetchable Memory. + +**/ +VOID +ResourcePaddingPolicy ( + IN PCI_IO_DEVICE *PciDev, + IN PCI_RESOURCE_NODE *IoNode, + IN PCI_RESOURCE_NODE *Mem32Node, + IN PCI_RESOURCE_NODE *PMem32Node, + IN PCI_RESOURCE_NODE *Mem64Node, + IN PCI_RESOURCE_NODE *PMem64Node + ) +{ + // + // Create padding resource node + // + if (PciDev->ResourcePaddingDescriptors != NULL) { + ApplyResourcePadding ( + PciDev, + IoNode, + Mem32Node, + PMem32Node, + Mem64Node, + PMem64Node + ); + } +} + +/** + This function is used to degrade resource if the upstream bridge + doesn't support certain resource. Degradation path is + PMEM64 -> MEM64 -> MEM32 + PMEM64 -> PMEM32 -> MEM32 + IO32 -> IO16. + + @param Bridge Pci device instance. + @param Mem32Node Resource info node for 32-bit memory. + @param PMem32Node Resource info node for 32-bit Prefetchable Memory. + @param Mem64Node Resource info node for 64-bit memory. + @param PMem64Node Resource info node for 64-bit Prefetchable Memory. + +**/ +VOID +DegradeResource ( + IN PCI_IO_DEVICE *Bridge, + IN PCI_RESOURCE_NODE *Mem32Node, + IN PCI_RESOURCE_NODE *PMem32Node, + IN PCI_RESOURCE_NODE *Mem64Node, + IN PCI_RESOURCE_NODE *PMem64Node + ) +{ + BOOLEAN HasOprom; + PCI_IO_DEVICE *Temp; + LIST_ENTRY *CurrentLink; + + // + // For RootBridge, PPB , P2C, go recursively to traverse all its children + // to find if this bridge and downstream has OptionRom. + // + HasOprom = FALSE; + CurrentLink = Bridge->ChildList.ForwardLink; + while (CurrentLink != NULL && CurrentLink != &Bridge->ChildList) { + + Temp = PCI_IO_DEVICE_FROM_LINK (CurrentLink); + if (Temp->RomSize != 0) { + HasOprom = TRUE; + break; + } + CurrentLink = CurrentLink->ForwardLink; + } + + // + // If bridge doesn't support Prefetchable + // memory64, degrade it to Prefetchable memory32 + // + if (!BridgeSupportResourceDecode (Bridge, EFI_BRIDGE_PMEM64_DECODE_SUPPORTED)) { + MergeResourceTree ( + PMem32Node, + PMem64Node, + TRUE + ); + } else { + // + // if no PMem32 request and no OptionRom request, still keep PMem64. Otherwise degrade to PMem32 + // + if ((PMem32Node != NULL && (PMem32Node->Length != 0 && Bridge->Parent != NULL)) || HasOprom) { + // + // Fixed the issue that there is no resource for 64-bit (above 4G) + // + MergeResourceTree ( + PMem32Node, + PMem64Node, + TRUE + ); + } + } + + + // + // If bridge doesn't support Mem64 + // degrade it to mem32 + // + if (!BridgeSupportResourceDecode (Bridge, EFI_BRIDGE_MEM64_DECODE_SUPPORTED)) { + MergeResourceTree ( + Mem32Node, + Mem64Node, + TRUE + ); + } + + // + // If bridge doesn't support Pmem32 + // degrade it to mem32 + // + if (!BridgeSupportResourceDecode (Bridge, EFI_BRIDGE_PMEM32_DECODE_SUPPORTED)) { + MergeResourceTree ( + Mem32Node, + PMem32Node, + TRUE + ); + } + + // + // if bridge supports combined Pmem Mem decoding + // merge these two type of resource + // + if (BridgeSupportResourceDecode (Bridge, EFI_BRIDGE_PMEM_MEM_COMBINE_SUPPORTED)) { + MergeResourceTree ( + Mem32Node, + PMem32Node, + FALSE + ); + + MergeResourceTree ( + Mem64Node, + PMem64Node, + FALSE + ); + } +} + +/** + Test whether bridge device support decode resource. + + @param Bridge Bridge device instance. + @param Decode Decode type according to resource type. + + @return TRUE The bridge device support decode resource. + @return FALSE The bridge device don't support decode resource. + +**/ +BOOLEAN +BridgeSupportResourceDecode ( + IN PCI_IO_DEVICE *Bridge, + IN UINT32 Decode + ) +{ + if (((Bridge->Decodes) & Decode) != 0) { + return TRUE; + } + + return FALSE; +} + +/** + This function is used to program the resource allocated + for each resource node under specified bridge. + + @param Base Base address of resource to be progammed. + @param Bridge PCI resource node for the bridge device. + + @retval EFI_SUCCESS Successfully to program all resouces + on given PCI bridge device. + @retval EFI_OUT_OF_RESOURCES Base is all one. + +**/ +EFI_STATUS +ProgramResource ( + IN UINT64 Base, + IN PCI_RESOURCE_NODE *Bridge + ) +{ + LIST_ENTRY *CurrentLink; + PCI_RESOURCE_NODE *Node; + EFI_STATUS Status; + + if (Base == gAllOne) { + return EFI_OUT_OF_RESOURCES; + } + + CurrentLink = Bridge->ChildList.ForwardLink; + + while (CurrentLink != &Bridge->ChildList) { + + Node = RESOURCE_NODE_FROM_LINK (CurrentLink); + + if (!IS_PCI_BRIDGE (&(Node->PciDev->Pci))) { + + if (IS_CARDBUS_BRIDGE (&(Node->PciDev->Pci))) { + // + // Program the PCI Card Bus device + // + ProgramP2C (Base, Node); + } else { + // + // Program the PCI device BAR + // + ProgramBar (Base, Node); + } + } else { + // + // Program the PCI devices under this bridge + // + Status = ProgramResource (Base + Node->Offset, Node); + if (EFI_ERROR (Status)) { + return Status; + } + + ProgramPpbApperture (Base, Node); + } + + CurrentLink = CurrentLink->ForwardLink; + } + + return EFI_SUCCESS; +} + +/** + Program Bar register for PCI device. + + @param Base Base address for PCI device resource to be progammed. + @param Node Point to resoure node structure. + +**/ +VOID +ProgramBar ( + IN UINT64 Base, + IN PCI_RESOURCE_NODE *Node + ) +{ + EFI_PCI_IO_PROTOCOL *PciIo; + UINT64 Address; + UINT32 Address32; + + ASSERT (Node->Bar < PCI_MAX_BAR); + + // + // Check VF BAR + // + if (Node->Virtual) { + ProgramVfBar (Base, Node); + } + + Address = 0; + PciIo = &(Node->PciDev->PciIo); + + Address = Base + Node->Offset; + + // + // Indicate pci bus driver has allocated + // resource for this device + // It might be a temporary solution here since + // pci device could have multiple bar + // + Node->PciDev->Allocated = TRUE; + + switch ((Node->PciDev->PciBar[Node->Bar]).BarType) { + + case PciBarTypeIo16: + case PciBarTypeIo32: + case PciBarTypeMem32: + case PciBarTypePMem32: + + PciIo->Pci.Write ( + PciIo, + EfiPciIoWidthUint32, + (Node->PciDev->PciBar[Node->Bar]).Offset, + 1, + &Address + ); + + Node->PciDev->PciBar[Node->Bar].BaseAddress = Address; + + break; + + case PciBarTypeMem64: + case PciBarTypePMem64: + + Address32 = (UINT32) (Address & 0x00000000FFFFFFFF); + + PciIo->Pci.Write ( + PciIo, + EfiPciIoWidthUint32, + (Node->PciDev->PciBar[Node->Bar]).Offset, + 1, + &Address32 + ); + + Address32 = (UINT32) RShiftU64 (Address, 32); + + PciIo->Pci.Write ( + PciIo, + EfiPciIoWidthUint32, + (UINT8) ((Node->PciDev->PciBar[Node->Bar]).Offset + 4), + 1, + &Address32 + ); + + Node->PciDev->PciBar[Node->Bar].BaseAddress = Address; + + break; + + default: + break; + } +} + +/** + Program IOV VF Bar register for PCI device. + + @param Base Base address for PCI device resource to be progammed. + @param Node Point to resoure node structure. + +**/ +EFI_STATUS +ProgramVfBar ( + IN UINT64 Base, + IN PCI_RESOURCE_NODE *Node + ) +{ + EFI_PCI_IO_PROTOCOL *PciIo; + UINT64 Address; + UINT32 Address32; + + ASSERT (Node->Bar < PCI_MAX_BAR); + ASSERT (Node->Virtual); + + Address = 0; + PciIo = &(Node->PciDev->PciIo); + + Address = Base + Node->Offset; + + // + // Indicate pci bus driver has allocated + // resource for this device + // It might be a temporary solution here since + // pci device could have multiple bar + // + Node->PciDev->Allocated = TRUE; + + switch ((Node->PciDev->VfPciBar[Node->Bar]).BarType) { + + case PciBarTypeMem32: + case PciBarTypePMem32: + + PciIo->Pci.Write ( + PciIo, + EfiPciIoWidthUint32, + (Node->PciDev->VfPciBar[Node->Bar]).Offset, + 1, + &Address + ); + + Node->PciDev->VfPciBar[Node->Bar].BaseAddress = Address; + + DEBUG (( + EFI_D_INFO, + "PCI-IOV B%x.D%x.F%x - VF Bar (Offset - 0x%x) 32Mem (Address - 0x%x)\n", + (UINTN)Node->PciDev->BusNumber, + (UINTN)Node->PciDev->DeviceNumber, + (UINTN)Node->PciDev->FunctionNumber, + (UINTN)(Node->PciDev->VfPciBar[Node->Bar]).Offset, + (UINTN)Address + )); + + break; + + case PciBarTypeMem64: + case PciBarTypePMem64: + + Address32 = (UINT32) (Address & 0x00000000FFFFFFFF); + + PciIo->Pci.Write ( + PciIo, + EfiPciIoWidthUint32, + (Node->PciDev->VfPciBar[Node->Bar]).Offset, + 1, + &Address32 + ); + + Address32 = (UINT32) RShiftU64 (Address, 32); + + PciIo->Pci.Write ( + PciIo, + EfiPciIoWidthUint32, + ((Node->PciDev->VfPciBar[Node->Bar]).Offset + 4), + 1, + &Address32 + ); + + Node->PciDev->VfPciBar[Node->Bar].BaseAddress = Address; + + DEBUG (( + EFI_D_INFO, + "PCI-IOV B%x.D%x.F%x - VF Bar (Offset - 0x%x) 64Mem (Address - 0x%lx)\n", + (UINTN)Node->PciDev->BusNumber, + (UINTN)Node->PciDev->DeviceNumber, + (UINTN)Node->PciDev->FunctionNumber, + (UINTN)(Node->PciDev->VfPciBar[Node->Bar]).Offset, + (UINT64)Address + )); + + break; + + case PciBarTypeIo16: + case PciBarTypeIo32: + break; + + default: + break; + } + + return EFI_SUCCESS; +} + +/** + Program PCI-PCI bridge apperture. + + @param Base Base address for resource. + @param Node Point to resoure node structure. + +**/ +VOID +ProgramPpbApperture ( + IN UINT64 Base, + IN PCI_RESOURCE_NODE *Node + ) +{ + EFI_PCI_IO_PROTOCOL *PciIo; + UINT64 Address; + UINT32 Address32; + + Address = 0; + // + // If no device resource of this PPB, return anyway + // Apperture is set default in the initialization code + // + if (Node->Length == 0 || Node->ResourceUsage == PciResUsagePadding) { + // + // For padding resource node, just ignore when programming + // + return ; + } + + PciIo = &(Node->PciDev->PciIo); + Address = Base + Node->Offset; + + // + // Indicate the PPB resource has been allocated + // + Node->PciDev->Allocated = TRUE; + + switch (Node->Bar) { + + case PPB_BAR_0: + case PPB_BAR_1: + PciIo->Pci.Write ( + PciIo, + EfiPciIoWidthUint32, + (Node->PciDev->PciBar[Node->Bar]).Offset, + 1, + &Address + ); + + Node->PciDev->PciBar[Node->Bar].BaseAddress = Address; + Node->PciDev->PciBar[Node->Bar].Length = Node->Length; + + break; + + case PPB_IO_RANGE: + + Address32 = ((UINT32) (Address)) >> 8; + PciIo->Pci.Write ( + PciIo, + EfiPciIoWidthUint8, + 0x1C, + 1, + &Address32 + ); + + Address32 >>= 8; + PciIo->Pci.Write ( + PciIo, + EfiPciIoWidthUint16, + 0x30, + 1, + &Address32 + ); + + Address32 = (UINT32) (Address + Node->Length - 1); + Address32 = ((UINT32) (Address32)) >> 8; + PciIo->Pci.Write ( + PciIo, + EfiPciIoWidthUint8, + 0x1D, + 1, + &Address32 + ); + + Address32 >>= 8; + PciIo->Pci.Write ( + PciIo, + EfiPciIoWidthUint16, + 0x32, + 1, + &Address32 + ); + + Node->PciDev->PciBar[Node->Bar].BaseAddress = Address; + Node->PciDev->PciBar[Node->Bar].Length = Node->Length; + break; + + case PPB_MEM32_RANGE: + + Address32 = ((UINT32) (Address)) >> 16; + PciIo->Pci.Write ( + PciIo, + EfiPciIoWidthUint16, + 0x20, + 1, + &Address32 + ); + + Address32 = (UINT32) (Address + Node->Length - 1); + Address32 = ((UINT32) (Address32)) >> 16; + PciIo->Pci.Write ( + PciIo, + EfiPciIoWidthUint16, + 0x22, + 1, + &Address32 + ); + + Node->PciDev->PciBar[Node->Bar].BaseAddress = Address; + Node->PciDev->PciBar[Node->Bar].Length = Node->Length; + break; + + case PPB_PMEM32_RANGE: + case PPB_PMEM64_RANGE: + + Address32 = ((UINT32) (Address)) >> 16; + PciIo->Pci.Write ( + PciIo, + EfiPciIoWidthUint16, + 0x24, + 1, + &Address32 + ); + + Address32 = (UINT32) (Address + Node->Length - 1); + Address32 = ((UINT32) (Address32)) >> 16; + PciIo->Pci.Write ( + PciIo, + EfiPciIoWidthUint16, + 0x26, + 1, + &Address32 + ); + + Address32 = (UINT32) RShiftU64 (Address, 32); + PciIo->Pci.Write ( + PciIo, + EfiPciIoWidthUint32, + 0x28, + 1, + &Address32 + ); + + Address32 = (UINT32) RShiftU64 ((Address + Node->Length - 1), 32); + PciIo->Pci.Write ( + PciIo, + EfiPciIoWidthUint32, + 0x2C, + 1, + &Address32 + ); + + Node->PciDev->PciBar[Node->Bar].BaseAddress = Address; + Node->PciDev->PciBar[Node->Bar].Length = Node->Length; + break; + + default: + break; + } +} + +/** + Program parent bridge for Option Rom. + + @param PciDevice Pci deivce instance. + @param OptionRomBase Base address for Optiona Rom. + @param Enable Enable or disable PCI memory. + +**/ +VOID +ProgrameUpstreamBridgeForRom ( + IN PCI_IO_DEVICE *PciDevice, + IN UINT32 OptionRomBase, + IN BOOLEAN Enable + ) +{ + PCI_IO_DEVICE *Parent; + PCI_RESOURCE_NODE Node; + // + // For root bridge, just return. + // + Parent = PciDevice->Parent; + ZeroMem (&Node, sizeof (Node)); + while (Parent != NULL) { + if (!IS_PCI_BRIDGE (&Parent->Pci)) { + break; + } + + Node.PciDev = Parent; + Node.Length = PciDevice->RomSize; + Node.Alignment = 0; + Node.Bar = PPB_MEM32_RANGE; + Node.ResType = PciBarTypeMem32; + Node.Offset = 0; + + // + // Program PPB to only open a single <= 16MB apperture + // + if (Enable) { + ProgramPpbApperture (OptionRomBase, &Node); + PCI_ENABLE_COMMAND_REGISTER (Parent, EFI_PCI_COMMAND_MEMORY_SPACE); + } else { + InitializePpb (Parent); + PCI_DISABLE_COMMAND_REGISTER (Parent, EFI_PCI_COMMAND_MEMORY_SPACE); + } + + Parent = Parent->Parent; + } +} + +/** + Test whether resource exists for a bridge. + + @param Bridge Point to resource node for a bridge. + + @retval TRUE There is resource on the given bridge. + @retval FALSE There isn't resource on the given bridge. + +**/ +BOOLEAN +ResourceRequestExisted ( + IN PCI_RESOURCE_NODE *Bridge + ) +{ + if (Bridge != NULL) { + if (!IsListEmpty (&Bridge->ChildList) || Bridge->Length != 0) { + return TRUE; + } + } + + return FALSE; +} + +/** + Initialize resource pool structure. + + @param ResourcePool Point to resource pool structure. This pool + is reset to all zero when returned. + @param ResourceType Type of resource. + +**/ +VOID +InitializeResourcePool ( + IN OUT PCI_RESOURCE_NODE *ResourcePool, + IN PCI_BAR_TYPE ResourceType + ) +{ + ZeroMem (ResourcePool, sizeof (PCI_RESOURCE_NODE)); + ResourcePool->ResType = ResourceType; + ResourcePool->Signature = PCI_RESOURCE_SIGNATURE; + InitializeListHead (&ResourcePool->ChildList); +} + + +/** + Get all resource information for given Pci device. + + @param PciDev Pci device instance. + @param IoBridge Io resource node. + @param Mem32Bridge 32-bit memory node. + @param PMem32Bridge 32-bit Pmemory node. + @param Mem64Bridge 64-bit memory node. + @param PMem64Bridge 64-bit PMemory node. + @param IoPool Link list header for Io resource. + @param Mem32Pool Link list header for 32-bit memory. + @param PMem32Pool Link list header for 32-bit Prefetchable memory. + @param Mem64Pool Link list header for 64-bit memory. + @param PMem64Pool Link list header for 64-bit Prefetchable memory. + +**/ +VOID +GetResourceMap ( + IN PCI_IO_DEVICE *PciDev, + IN PCI_RESOURCE_NODE **IoBridge, + IN PCI_RESOURCE_NODE **Mem32Bridge, + IN PCI_RESOURCE_NODE **PMem32Bridge, + IN PCI_RESOURCE_NODE **Mem64Bridge, + IN PCI_RESOURCE_NODE **PMem64Bridge, + IN PCI_RESOURCE_NODE *IoPool, + IN PCI_RESOURCE_NODE *Mem32Pool, + IN PCI_RESOURCE_NODE *PMem32Pool, + IN PCI_RESOURCE_NODE *Mem64Pool, + IN PCI_RESOURCE_NODE *PMem64Pool + ) +{ + + PCI_RESOURCE_NODE *Temp; + LIST_ENTRY *CurrentLink; + + CurrentLink = IoPool->ChildList.ForwardLink; + + // + // Get Io resource map + // + while (CurrentLink != &IoPool->ChildList) { + + Temp = RESOURCE_NODE_FROM_LINK (CurrentLink); + + if (Temp->PciDev == PciDev) { + *IoBridge = Temp; + } + + CurrentLink = CurrentLink->ForwardLink; + } + + // + // Get Mem32 resource map + // + CurrentLink = Mem32Pool->ChildList.ForwardLink; + + while (CurrentLink != &Mem32Pool->ChildList) { + + Temp = RESOURCE_NODE_FROM_LINK (CurrentLink); + + if (Temp->PciDev == PciDev) { + *Mem32Bridge = Temp; + } + + CurrentLink = CurrentLink->ForwardLink; + } + + // + // Get Pmem32 resource map + // + CurrentLink = PMem32Pool->ChildList.ForwardLink; + + while (CurrentLink != &PMem32Pool->ChildList) { + + Temp = RESOURCE_NODE_FROM_LINK (CurrentLink); + + if (Temp->PciDev == PciDev) { + *PMem32Bridge = Temp; + } + + CurrentLink = CurrentLink->ForwardLink; + } + + // + // Get Mem64 resource map + // + CurrentLink = Mem64Pool->ChildList.ForwardLink; + + while (CurrentLink != &Mem64Pool->ChildList) { + + Temp = RESOURCE_NODE_FROM_LINK (CurrentLink); + + if (Temp->PciDev == PciDev) { + *Mem64Bridge = Temp; + } + + CurrentLink = CurrentLink->ForwardLink; + } + + // + // Get Pmem64 resource map + // + CurrentLink = PMem64Pool->ChildList.ForwardLink; + + while (CurrentLink != &PMem64Pool->ChildList) { + + Temp = RESOURCE_NODE_FROM_LINK (CurrentLink); + + if (Temp->PciDev == PciDev) { + *PMem64Bridge = Temp; + } + + CurrentLink = CurrentLink->ForwardLink; + } +} + +/** + Destory given resource tree. + + @param Bridge PCI resource root node of resource tree. + +**/ +VOID +DestroyResourceTree ( + IN PCI_RESOURCE_NODE *Bridge + ) +{ + PCI_RESOURCE_NODE *Temp; + LIST_ENTRY *CurrentLink; + + while (!IsListEmpty (&Bridge->ChildList)) { + + CurrentLink = Bridge->ChildList.ForwardLink; + + Temp = RESOURCE_NODE_FROM_LINK (CurrentLink); + ASSERT (Temp); + + RemoveEntryList (CurrentLink); + + if (IS_PCI_BRIDGE (&(Temp->PciDev->Pci))) { + DestroyResourceTree (Temp); + } + + FreePool (Temp); + } +} + +/** + Insert resource padding for P2C. + + @param PciDev Pci device instance. + @param IoNode Resource info node for IO. + @param Mem32Node Resource info node for 32-bit memory. + @param PMem32Node Resource info node for 32-bit Prefetchable Memory. + @param Mem64Node Resource info node for 64-bit memory. + @param PMem64Node Resource info node for 64-bit Prefetchable Memory. + +**/ +VOID +ResourcePaddingForCardBusBridge ( + IN PCI_IO_DEVICE *PciDev, + IN PCI_RESOURCE_NODE *IoNode, + IN PCI_RESOURCE_NODE *Mem32Node, + IN PCI_RESOURCE_NODE *PMem32Node, + IN PCI_RESOURCE_NODE *Mem64Node, + IN PCI_RESOURCE_NODE *PMem64Node + ) +{ + PCI_RESOURCE_NODE *Node; + + Node = NULL; + + // + // Memory Base/Limit Register 0 + // Bar 1 denodes memory range 0 + // + Node = CreateResourceNode ( + PciDev, + 0x2000000, + 0x1ffffff, + 1, + PciBarTypeMem32, + PciResUsagePadding + ); + + InsertResourceNode ( + Mem32Node, + Node + ); + + // + // Memory Base/Limit Register 1 + // Bar 2 denodes memory range1 + // + Node = CreateResourceNode ( + PciDev, + 0x2000000, + 0x1ffffff, + 2, + PciBarTypePMem32, + PciResUsagePadding + ); + + InsertResourceNode ( + PMem32Node, + Node + ); + + // + // Io Base/Limit + // Bar 3 denodes io range 0 + // + Node = CreateResourceNode ( + PciDev, + 0x100, + 0xff, + 3, + PciBarTypeIo16, + PciResUsagePadding + ); + + InsertResourceNode ( + IoNode, + Node + ); + + // + // Io Base/Limit + // Bar 4 denodes io range 0 + // + Node = CreateResourceNode ( + PciDev, + 0x100, + 0xff, + 4, + PciBarTypeIo16, + PciResUsagePadding + ); + + InsertResourceNode ( + IoNode, + Node + ); +} + +/** + Program PCI Card device register for given resource node. + + @param Base Base address of PCI Card device to be programmed. + @param Node Given resource node. + +**/ +VOID +ProgramP2C ( + IN UINT64 Base, + IN PCI_RESOURCE_NODE *Node + ) +{ + EFI_PCI_IO_PROTOCOL *PciIo; + UINT64 Address; + UINT64 TempAddress; + UINT16 BridgeControl; + + Address = 0; + PciIo = &(Node->PciDev->PciIo); + + Address = Base + Node->Offset; + + // + // Indicate pci bus driver has allocated + // resource for this device + // It might be a temporary solution here since + // pci device could have multiple bar + // + Node->PciDev->Allocated = TRUE; + + switch (Node->Bar) { + + case P2C_BAR_0: + PciIo->Pci.Write ( + PciIo, + EfiPciIoWidthUint32, + (Node->PciDev->PciBar[Node->Bar]).Offset, + 1, + &Address + ); + + Node->PciDev->PciBar[Node->Bar].BaseAddress = Address; + Node->PciDev->PciBar[Node->Bar].Length = Node->Length; + break; + + case P2C_MEM_1: + PciIo->Pci.Write ( + PciIo, + EfiPciIoWidthUint32, + PCI_CARD_MEMORY_BASE_0, + 1, + &Address + ); + + TempAddress = Address + Node->Length - 1; + PciIo->Pci.Write ( + PciIo, + EfiPciIoWidthUint32, + PCI_CARD_MEMORY_LIMIT_0, + 1, + &TempAddress + ); + + if (Node->ResType == PciBarTypeMem32) { + // + // Set non-prefetchable bit + // + PciIo->Pci.Read ( + PciIo, + EfiPciIoWidthUint16, + PCI_CARD_BRIDGE_CONTROL, + 1, + &BridgeControl + ); + + BridgeControl &= (UINT16) ~PCI_CARD_PREFETCHABLE_MEMORY_0_ENABLE; + PciIo->Pci.Write ( + PciIo, + EfiPciIoWidthUint16, + PCI_CARD_BRIDGE_CONTROL, + 1, + &BridgeControl + ); + + } else { + // + // Set pre-fetchable bit + // + PciIo->Pci.Read ( + PciIo, + EfiPciIoWidthUint16, + PCI_CARD_BRIDGE_CONTROL, + 1, + &BridgeControl + ); + + BridgeControl |= PCI_CARD_PREFETCHABLE_MEMORY_0_ENABLE; + PciIo->Pci.Write ( + PciIo, + EfiPciIoWidthUint16, + PCI_CARD_BRIDGE_CONTROL, + 1, + &BridgeControl + ); + } + + Node->PciDev->PciBar[Node->Bar].BaseAddress = Address; + Node->PciDev->PciBar[Node->Bar].Length = Node->Length; + Node->PciDev->PciBar[Node->Bar].BarType = Node->ResType; + + break; + + case P2C_MEM_2: + PciIo->Pci.Write ( + PciIo, + EfiPciIoWidthUint32, + PCI_CARD_MEMORY_BASE_1, + 1, + &Address + ); + + TempAddress = Address + Node->Length - 1; + + PciIo->Pci.Write ( + PciIo, + EfiPciIoWidthUint32, + PCI_CARD_MEMORY_LIMIT_1, + 1, + &TempAddress + ); + + if (Node->ResType == PciBarTypeMem32) { + + // + // Set non-prefetchable bit + // + PciIo->Pci.Read ( + PciIo, + EfiPciIoWidthUint16, + PCI_CARD_BRIDGE_CONTROL, + 1, + &BridgeControl + ); + + BridgeControl &= (UINT16) ~(PCI_CARD_PREFETCHABLE_MEMORY_1_ENABLE); + PciIo->Pci.Write ( + PciIo, + EfiPciIoWidthUint16, + PCI_CARD_BRIDGE_CONTROL, + 1, + &BridgeControl + ); + + } else { + + // + // Set pre-fetchable bit + // + PciIo->Pci.Read ( + PciIo, + EfiPciIoWidthUint16, + PCI_CARD_BRIDGE_CONTROL, + 1, + &BridgeControl + ); + + BridgeControl |= PCI_CARD_PREFETCHABLE_MEMORY_1_ENABLE; + PciIo->Pci.Write ( + PciIo, + EfiPciIoWidthUint16, + PCI_CARD_BRIDGE_CONTROL, + 1, + &BridgeControl + ); + } + + Node->PciDev->PciBar[Node->Bar].BaseAddress = Address; + Node->PciDev->PciBar[Node->Bar].Length = Node->Length; + Node->PciDev->PciBar[Node->Bar].BarType = Node->ResType; + break; + + case P2C_IO_1: + PciIo->Pci.Write ( + PciIo, + EfiPciIoWidthUint32, + PCI_CARD_IO_BASE_0_LOWER, + 1, + &Address + ); + + TempAddress = Address + Node->Length - 1; + PciIo->Pci.Write ( + PciIo, + EfiPciIoWidthUint32, + PCI_CARD_IO_LIMIT_0_LOWER, + 1, + &TempAddress + ); + + Node->PciDev->PciBar[Node->Bar].BaseAddress = Address; + Node->PciDev->PciBar[Node->Bar].Length = Node->Length; + Node->PciDev->PciBar[Node->Bar].BarType = Node->ResType; + + break; + + case P2C_IO_2: + PciIo->Pci.Write ( + PciIo, + EfiPciIoWidthUint32, + PCI_CARD_IO_BASE_1_LOWER, + 1, + &Address + ); + + TempAddress = Address + Node->Length - 1; + PciIo->Pci.Write ( + PciIo, + EfiPciIoWidthUint32, + PCI_CARD_IO_LIMIT_1_LOWER, + 1, + &TempAddress + ); + + Node->PciDev->PciBar[Node->Bar].BaseAddress = Address; + Node->PciDev->PciBar[Node->Bar].Length = Node->Length; + Node->PciDev->PciBar[Node->Bar].BarType = Node->ResType; + break; + + default: + break; + } +} + +/** + Create padding resource node. + + @param PciDev Pci device instance. + @param IoNode Resource info node for IO. + @param Mem32Node Resource info node for 32-bit memory. + @param PMem32Node Resource info node for 32-bit Prefetchable Memory. + @param Mem64Node Resource info node for 64-bit memory. + @param PMem64Node Resource info node for 64-bit Prefetchable Memory. + +**/ +VOID +ApplyResourcePadding ( + IN PCI_IO_DEVICE *PciDev, + IN PCI_RESOURCE_NODE *IoNode, + IN PCI_RESOURCE_NODE *Mem32Node, + IN PCI_RESOURCE_NODE *PMem32Node, + IN PCI_RESOURCE_NODE *Mem64Node, + IN PCI_RESOURCE_NODE *PMem64Node + ) +{ + EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Ptr; + PCI_RESOURCE_NODE *Node; + UINT8 DummyBarIndex; + + DummyBarIndex = 0; + Ptr = PciDev->ResourcePaddingDescriptors; + + while (((EFI_ACPI_END_TAG_DESCRIPTOR *) Ptr)->Desc != ACPI_END_TAG_DESCRIPTOR) { + + if (Ptr->Desc == ACPI_ADDRESS_SPACE_DESCRIPTOR && Ptr->ResType == ACPI_ADDRESS_SPACE_TYPE_IO) { + if (Ptr->AddrLen != 0) { + + Node = CreateResourceNode ( + PciDev, + Ptr->AddrLen, + Ptr->AddrRangeMax, + DummyBarIndex, + PciBarTypeIo16, + PciResUsagePadding + ); + InsertResourceNode ( + IoNode, + Node + ); + } + + Ptr++; + continue; + } + + if (Ptr->Desc == ACPI_ADDRESS_SPACE_DESCRIPTOR && Ptr->ResType == ACPI_ADDRESS_SPACE_TYPE_MEM) { + + if (Ptr->AddrSpaceGranularity == 32) { + + // + // prefechable + // + if (Ptr->SpecificFlag == 0x6) { + if (Ptr->AddrLen != 0) { + Node = CreateResourceNode ( + PciDev, + Ptr->AddrLen, + Ptr->AddrRangeMax, + DummyBarIndex, + PciBarTypePMem32, + PciResUsagePadding + ); + InsertResourceNode ( + PMem32Node, + Node + ); + } + + Ptr++; + continue; + } + + // + // Non-prefechable + // + if (Ptr->SpecificFlag == 0) { + if (Ptr->AddrLen != 0) { + Node = CreateResourceNode ( + PciDev, + Ptr->AddrLen, + Ptr->AddrRangeMax, + DummyBarIndex, + PciBarTypeMem32, + PciResUsagePadding + ); + InsertResourceNode ( + Mem32Node, + Node + ); + } + + Ptr++; + continue; + } + } + + if (Ptr->AddrSpaceGranularity == 64) { + + // + // prefechable + // + if (Ptr->SpecificFlag == 0x6) { + if (Ptr->AddrLen != 0) { + Node = CreateResourceNode ( + PciDev, + Ptr->AddrLen, + Ptr->AddrRangeMax, + DummyBarIndex, + PciBarTypePMem64, + PciResUsagePadding + ); + InsertResourceNode ( + PMem64Node, + Node + ); + } + + Ptr++; + continue; + } + + // + // Non-prefechable + // + if (Ptr->SpecificFlag == 0) { + if (Ptr->AddrLen != 0) { + Node = CreateResourceNode ( + PciDev, + Ptr->AddrLen, + Ptr->AddrRangeMax, + DummyBarIndex, + PciBarTypeMem64, + PciResUsagePadding + ); + InsertResourceNode ( + Mem64Node, + Node + ); + } + + Ptr++; + continue; + } + } + } + + Ptr++; + } +} + +/** + Get padding resource for PCI-PCI bridge. + + @param PciIoDevice PCI-PCI bridge device instance. + + @note Feature flag PcdPciBusHotplugDeviceSupport determines + whether need to pad resource for them. +**/ +VOID +GetResourcePaddingPpb ( + IN PCI_IO_DEVICE *PciIoDevice + ) +{ + if (gPciHotPlugInit != NULL && FeaturePcdGet (PcdPciBusHotplugDeviceSupport)) { + if (PciIoDevice->ResourcePaddingDescriptors == NULL) { + GetResourcePaddingForHpb (PciIoDevice); + } + } +} + diff --git a/MdeModulePkg/Bus/Pci/PciBusDxe/PciResourceSupport.h b/MdeModulePkg/Bus/Pci/PciBusDxe/PciResourceSupport.h new file mode 100644 index 0000000000..faa6c0d60c --- /dev/null +++ b/MdeModulePkg/Bus/Pci/PciBusDxe/PciResourceSupport.h @@ -0,0 +1,492 @@ +/** @file + PCI resouces support functions declaration for PCI Bus module. + +Copyright (c) 2006 - 2009, Intel Corporation +All rights reserved. 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. + +**/ + +#ifndef _EFI_PCI_RESOURCE_SUPPORT_H_ +#define _EFI_PCI_RESOURCE_SUPPORT_H_ + +typedef enum { + PciResUsageTypical = 0, + PciResUsagePadding, + PciResUsageOptionRomProcessing +} PCI_RESOURCE_USAGE; + +#define PCI_RESOURCE_SIGNATURE SIGNATURE_32 ('p', 'c', 'r', 'c') + +typedef struct { + UINT32 Signature; + LIST_ENTRY Link; + LIST_ENTRY ChildList; + PCI_IO_DEVICE *PciDev; + UINT64 Alignment; + UINT64 Offset; + UINT8 Bar; + PCI_BAR_TYPE ResType; + UINT64 Length; + BOOLEAN Reserved; + PCI_RESOURCE_USAGE ResourceUsage; + BOOLEAN Virtual; +} PCI_RESOURCE_NODE; + +#define RESOURCE_NODE_FROM_LINK(a) \ + CR (a, PCI_RESOURCE_NODE, Link, PCI_RESOURCE_SIGNATURE) + +/** + The function is used to skip VGA range. + + @param Start Returned start address including VGA range. + @param Length The length of VGA range. + +**/ +VOID +SkipVGAAperture ( + OUT UINT64 *Start, + IN UINT64 Length + ); + +/** + This function is used to skip ISA aliasing aperture. + + @param Start Returned start address including ISA aliasing aperture. + @param Length The length of ISA aliasing aperture. + +**/ +VOID +SkipIsaAliasAperture ( + OUT UINT64 *Start, + IN UINT64 Length + ); + +/** + This function inserts a resource node into the resource list. + The resource list is sorted in descend order. + + @param Bridge PCI resource node for bridge. + @param ResNode Resource node want to be inserted. + +**/ +VOID +InsertResourceNode ( + IN OUT PCI_RESOURCE_NODE *Bridge, + IN PCI_RESOURCE_NODE *ResNode + ); + +/** + This routine is used to merge two different resource trees in need of + resoure degradation. + + For example, if an upstream PPB doesn't support, + prefetchable memory decoding, the PCI bus driver will choose to call this function + to merge prefectchable memory resource list into normal memory list. + + If the TypeMerge is TRUE, Res resource type is changed to the type of destination resource + type. + If Dst is NULL or Res is NULL, ASSERT (). + + @param Dst Point to destination resource tree. + @param Res Point to source resource tree. + @param TypeMerge If the TypeMerge is TRUE, Res resource type is changed to the type of + destination resource type. + +**/ +VOID +MergeResourceTree ( + IN PCI_RESOURCE_NODE *Dst, + IN PCI_RESOURCE_NODE *Res, + IN BOOLEAN TypeMerge + ); + +/** + This function is used to calculate the IO16 aperture + for a bridge. + + @param Bridge PCI resource node for bridge. + +**/ +VOID +CalculateApertureIo16 ( + IN PCI_RESOURCE_NODE *Bridge + ); + +/** + This function is used to calculate the resource aperture + for a given bridge device. + + @param Bridge PCI resouce node for given bridge device. + +**/ +VOID +CalculateResourceAperture ( + IN PCI_RESOURCE_NODE *Bridge + ); + +/** + Get IO/Memory resource infor for given PCI device. + + @param PciDev Pci device instance. + @param IoNode Resource info node for IO . + @param Mem32Node Resource info node for 32-bit memory. + @param PMem32Node Resource info node for 32-bit Prefetchable Memory. + @param Mem64Node Resource info node for 64-bit memory. + @param PMem64Node Resource info node for 64-bit Prefetchable Memory. + +**/ +VOID +GetResourceFromDevice ( + IN PCI_IO_DEVICE *PciDev, + IN OUT PCI_RESOURCE_NODE *IoNode, + IN OUT PCI_RESOURCE_NODE *Mem32Node, + IN OUT PCI_RESOURCE_NODE *PMem32Node, + IN OUT PCI_RESOURCE_NODE *Mem64Node, + IN OUT PCI_RESOURCE_NODE *PMem64Node + ); + +/** + This function is used to create a resource node. + + @param PciDev Pci device instance. + @param Length Length of Io/Memory resource. + @param Alignment Alignment of resource. + @param Bar Bar index. + @param ResType Type of resource: IO/Memory. + @param ResUsage Resource usage. + + @return PCI resource node created for given PCI device. + NULL means PCI resource node is not created. + +**/ +PCI_RESOURCE_NODE * +CreateResourceNode ( + IN PCI_IO_DEVICE *PciDev, + IN UINT64 Length, + IN UINT64 Alignment, + IN UINT8 Bar, + IN PCI_BAR_TYPE ResType, + IN PCI_RESOURCE_USAGE ResUsage + ); + +/** + This function is used to extract resource request from + IOV VF device node list. + + @param Bridge Pci device instance. + @param IoNode Resource info node for IO. + @param Mem32Node Resource info node for 32-bit memory. + @param PMem32Node Resource info node for 32-bit Prefetchable Memory. + @param Mem64Node Resource info node for 64-bit memory. + @param PMem64Node Resource info node for 64-bit Prefetchable Memory. + +**/ +PCI_RESOURCE_NODE * +CreateVfResourceNode ( + IN PCI_IO_DEVICE *PciDev, + IN UINT64 Length, + IN UINT64 Alignment, + IN UINT8 Bar, + IN PCI_BAR_TYPE ResType, + IN PCI_RESOURCE_USAGE ResUsage + ); + +/** + This function is used to extract resource request from + device node list. + + @param Bridge Pci device instance. + @param IoNode Resource info node for IO. + @param Mem32Node Resource info node for 32-bit memory. + @param PMem32Node Resource info node for 32-bit Prefetchable Memory. + @param Mem64Node Resource info node for 64-bit memory. + @param PMem64Node Resource info node for 64-bit Prefetchable Memory. + +**/ +VOID +CreateResourceMap ( + IN PCI_IO_DEVICE *Bridge, + IN OUT PCI_RESOURCE_NODE *IoNode, + IN OUT PCI_RESOURCE_NODE *Mem32Node, + IN OUT PCI_RESOURCE_NODE *PMem32Node, + IN OUT PCI_RESOURCE_NODE *Mem64Node, + IN OUT PCI_RESOURCE_NODE *PMem64Node + ); + +/** + This function is used to do the resource padding for a specific platform. + + @param PciDev Pci device instance. + @param IoNode Resource info node for IO. + @param Mem32Node Resource info node for 32-bit memory. + @param PMem32Node Resource info node for 32-bit Prefetchable Memory. + @param Mem64Node Resource info node for 64-bit memory. + @param PMem64Node Resource info node for 64-bit Prefetchable Memory. + +**/ +VOID +ResourcePaddingPolicy ( + IN PCI_IO_DEVICE *PciDev, + IN PCI_RESOURCE_NODE *IoNode, + IN PCI_RESOURCE_NODE *Mem32Node, + IN PCI_RESOURCE_NODE *PMem32Node, + IN PCI_RESOURCE_NODE *Mem64Node, + IN PCI_RESOURCE_NODE *PMem64Node + ); + +/** + This function is used to degrade resource if the upstream bridge + doesn't support certain resource. Degradation path is + PMEM64 -> MEM64 -> MEM32 + PMEM64 -> PMEM32 -> MEM32 + IO32 -> IO16. + + @param Bridge Pci device instance. + @param Mem32Node Resource info node for 32-bit memory. + @param PMem32Node Resource info node for 32-bit Prefetchable Memory. + @param Mem64Node Resource info node for 64-bit memory. + @param PMem64Node Resource info node for 64-bit Prefetchable Memory. + +**/ +VOID +DegradeResource ( + IN PCI_IO_DEVICE *Bridge, + IN PCI_RESOURCE_NODE *Mem32Node, + IN PCI_RESOURCE_NODE *PMem32Node, + IN PCI_RESOURCE_NODE *Mem64Node, + IN PCI_RESOURCE_NODE *PMem64Node + ); + +/** + Test whether bridge device support decode resource. + + @param Bridge Bridge device instance. + @param Decode Decode type according to resource type. + + @return TRUE The bridge device support decode resource. + @return FALSE The bridge device don't support decode resource. + +**/ +BOOLEAN +BridgeSupportResourceDecode ( + IN PCI_IO_DEVICE *Bridge, + IN UINT32 Decode + ); + +/** + This function is used to program the resource allocated + for each resource node under specified bridge. + + @param Base Base address of resource to be progammed. + @param Bridge PCI resource node for the bridge device. + + @retval EFI_SUCCESS Successfully to program all resouces + on given PCI bridge device. + @retval EFI_OUT_OF_RESOURCES Base is all one. + +**/ +EFI_STATUS +ProgramResource ( + IN UINT64 Base, + IN PCI_RESOURCE_NODE *Bridge + ); + +/** + Program Bar register for PCI device. + + @param Base Base address for PCI device resource to be progammed. + @param Node Point to resoure node structure. + +**/ +VOID +ProgramBar ( + IN UINT64 Base, + IN PCI_RESOURCE_NODE *Node + ); + +/** + Program IOV VF Bar register for PCI device. + + @param Base Base address for PCI device resource to be progammed. + @param Node Point to resoure node structure. + +**/ +EFI_STATUS +ProgramVfBar ( + IN UINT64 Base, + IN PCI_RESOURCE_NODE *Node + ); + +/** + Program PCI-PCI bridge apperture. + + @param Base Base address for resource. + @param Node Point to resoure node structure. + +**/ +VOID +ProgramPpbApperture ( + IN UINT64 Base, + IN PCI_RESOURCE_NODE *Node + ); + +/** + Program parent bridge for Option Rom. + + @param PciDevice Pci deivce instance. + @param OptionRomBase Base address for Optiona Rom. + @param Enable Enable or disable PCI memory. + +**/ +VOID +ProgrameUpstreamBridgeForRom ( + IN PCI_IO_DEVICE *PciDevice, + IN UINT32 OptionRomBase, + IN BOOLEAN Enable + ); + +/** + Test whether resource exists for a bridge. + + @param Bridge Point to resource node for a bridge. + + @retval TRUE There is resource on the given bridge. + @retval FALSE There isn't resource on the given bridge. + +**/ +BOOLEAN +ResourceRequestExisted ( + IN PCI_RESOURCE_NODE *Bridge + ); + +/** + Initialize resource pool structure. + + @param ResourcePool Point to resource pool structure. This pool + is reset to all zero when returned. + @param ResourceType Type of resource. + +**/ +VOID +InitializeResourcePool ( + IN OUT PCI_RESOURCE_NODE *ResourcePool, + IN PCI_BAR_TYPE ResourceType + ); + +/** + Get all resource information for given Pci device. + + @param PciDev Pci device instance. + @param IoBridge Io resource node. + @param Mem32Bridge 32-bit memory node. + @param PMem32Bridge 32-bit Pmemory node. + @param Mem64Bridge 64-bit memory node. + @param PMem64Bridge 64-bit PMemory node. + @param IoPool Link list header for Io resource. + @param Mem32Pool Link list header for 32-bit memory. + @param PMem32Pool Link list header for 32-bit Prefetchable memory. + @param Mem64Pool Link list header for 64-bit memory. + @param PMem64Pool Link list header for 64-bit Prefetchable memory. + +**/ +VOID +GetResourceMap ( + IN PCI_IO_DEVICE *PciDev, + IN PCI_RESOURCE_NODE **IoBridge, + IN PCI_RESOURCE_NODE **Mem32Bridge, + IN PCI_RESOURCE_NODE **PMem32Bridge, + IN PCI_RESOURCE_NODE **Mem64Bridge, + IN PCI_RESOURCE_NODE **PMem64Bridge, + IN PCI_RESOURCE_NODE *IoPool, + IN PCI_RESOURCE_NODE *Mem32Pool, + IN PCI_RESOURCE_NODE *PMem32Pool, + IN PCI_RESOURCE_NODE *Mem64Pool, + IN PCI_RESOURCE_NODE *PMem64Pool + ); + +/** + Destory given resource tree. + + @param Bridge PCI resource root node of resource tree. + +**/ +VOID +DestroyResourceTree ( + IN PCI_RESOURCE_NODE *Bridge + ); + +/** + Insert resource padding for P2C. + + @param PciDev Pci device instance. + @param IoNode Resource info node for IO. + @param Mem32Node Resource info node for 32-bit memory. + @param PMem32Node Resource info node for 32-bit Prefetchable Memory. + @param Mem64Node Resource info node for 64-bit memory. + @param PMem64Node Resource info node for 64-bit Prefetchable Memory. + +**/ +VOID +ResourcePaddingForCardBusBridge ( + IN PCI_IO_DEVICE *PciDev, + IN PCI_RESOURCE_NODE *IoNode, + IN PCI_RESOURCE_NODE *Mem32Node, + IN PCI_RESOURCE_NODE *PMem32Node, + IN PCI_RESOURCE_NODE *Mem64Node, + IN PCI_RESOURCE_NODE *PMem64Node + ); + +/** + Program PCI Card device register for given resource node. + + @param Base Base address of PCI Card device to be programmed. + @param Node Given resource node. + +**/ +VOID +ProgramP2C ( + IN UINT64 Base, + IN PCI_RESOURCE_NODE *Node + ); + +/** + Create padding resource node. + + @param PciDev Pci device instance. + @param IoNode Resource info node for IO. + @param Mem32Node Resource info node for 32-bit memory. + @param PMem32Node Resource info node for 32-bit Prefetchable Memory. + @param Mem64Node Resource info node for 64-bit memory. + @param PMem64Node Resource info node for 64-bit Prefetchable Memory. + +**/ +VOID +ApplyResourcePadding ( + IN PCI_IO_DEVICE *PciDev, + IN PCI_RESOURCE_NODE *IoNode, + IN PCI_RESOURCE_NODE *Mem32Node, + IN PCI_RESOURCE_NODE *PMem32Node, + IN PCI_RESOURCE_NODE *Mem64Node, + IN PCI_RESOURCE_NODE *PMem64Node + ); + +/** + Get padding resource for PCI-PCI bridge. + + @param PciIoDevice PCI-PCI bridge device instance. + + @note Feature flag PcdPciBusHotplugDeviceSupport determines + whether need to pad resource for them. +**/ +VOID +GetResourcePaddingPpb ( + IN PCI_IO_DEVICE *PciIoDevice + ); + +#endif diff --git a/MdeModulePkg/Bus/Pci/PciBusDxe/PciRomTable.c b/MdeModulePkg/Bus/Pci/PciBusDxe/PciRomTable.c new file mode 100644 index 0000000000..9addcf920a --- /dev/null +++ b/MdeModulePkg/Bus/Pci/PciBusDxe/PciRomTable.c @@ -0,0 +1,126 @@ +/** @file + Set up ROM Table for PCI Bus module. + +Copyright (c) 2006 - 2009, Intel Corporation +All rights reserved. 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" + +// +// PCI ROM image information +// +typedef struct { + EFI_HANDLE ImageHandle; + UINTN Seg; + UINT8 Bus; + UINT8 Dev; + UINT8 Func; + UINT64 RomAddress; + UINT64 RomLength; +} EFI_PCI_ROM_IMAGE_MAPPING; + +UINTN mNumberOfPciRomImages = 0; +UINTN mMaxNumberOfPciRomImages = 0; +EFI_PCI_ROM_IMAGE_MAPPING *mRomImageTable = NULL; + +/** + Add the Rom Image to internal database for later PCI light enumeration. + + @param ImageHandle Option Rom image handle. + @param Seg Segment of PCI space. + @param Bus Bus NO of PCI space. + @param Dev Dev NO of PCI space. + @param Func Func NO of PCI space. + @param RomAddress Base address of OptionRom. + @param RomLength Length of rom image. + +**/ +VOID +PciRomAddImageMapping ( + IN EFI_HANDLE ImageHandle, + IN UINTN Seg, + IN UINT8 Bus, + IN UINT8 Dev, + IN UINT8 Func, + IN UINT64 RomAddress, + IN UINT64 RomLength + ) +{ + EFI_PCI_ROM_IMAGE_MAPPING *TempMapping; + + if (mNumberOfPciRomImages >= mMaxNumberOfPciRomImages) { + + mMaxNumberOfPciRomImages += 0x20; + + TempMapping = NULL; + TempMapping = AllocatePool (mMaxNumberOfPciRomImages * sizeof (EFI_PCI_ROM_IMAGE_MAPPING)); + if (TempMapping == NULL) { + return ; + } + + CopyMem (TempMapping, mRomImageTable, mNumberOfPciRomImages * sizeof (EFI_PCI_ROM_IMAGE_MAPPING)); + + if (mRomImageTable != NULL) { + FreePool (mRomImageTable); + } + + mRomImageTable = TempMapping; + } + + mRomImageTable[mNumberOfPciRomImages].ImageHandle = ImageHandle; + mRomImageTable[mNumberOfPciRomImages].Seg = Seg; + mRomImageTable[mNumberOfPciRomImages].Bus = Bus; + mRomImageTable[mNumberOfPciRomImages].Dev = Dev; + mRomImageTable[mNumberOfPciRomImages].Func = Func; + mRomImageTable[mNumberOfPciRomImages].RomAddress = RomAddress; + mRomImageTable[mNumberOfPciRomImages].RomLength = RomLength; + mNumberOfPciRomImages++; +} + +/** + Get Option rom driver's mapping for PCI device. + + @param PciIoDevice Device instance. + + @retval TRUE Found Image mapping. + @retval FALSE Cannot found image mapping. + +**/ +BOOLEAN +PciRomGetImageMapping ( + IN PCI_IO_DEVICE *PciIoDevice + ) +{ + EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *PciRootBridgeIo; + UINTN Index; + BOOLEAN Found; + + PciRootBridgeIo = PciIoDevice->PciRootBridgeIo; + Found = FALSE; + + for (Index = 0; Index < mNumberOfPciRomImages; Index++) { + if (mRomImageTable[Index].Seg == PciRootBridgeIo->SegmentNumber && + mRomImageTable[Index].Bus == PciIoDevice->BusNumber && + mRomImageTable[Index].Dev == PciIoDevice->DeviceNumber && + mRomImageTable[Index].Func == PciIoDevice->FunctionNumber ) { + Found = TRUE; + + if (mRomImageTable[Index].ImageHandle != NULL) { + AddDriver (PciIoDevice, mRomImageTable[Index].ImageHandle); + } else { + PciIoDevice->PciIo.RomImage = (VOID *) (UINTN) mRomImageTable[Index].RomAddress; + PciIoDevice->PciIo.RomSize = (UINTN) mRomImageTable[Index].RomLength; + } + } + } + + return Found; +} diff --git a/MdeModulePkg/Bus/Pci/PciBusDxe/PciRomTable.h b/MdeModulePkg/Bus/Pci/PciBusDxe/PciRomTable.h new file mode 100644 index 0000000000..e1b41a7c86 --- /dev/null +++ b/MdeModulePkg/Bus/Pci/PciBusDxe/PciRomTable.h @@ -0,0 +1,55 @@ +/** @file + Set up ROM Table for PCI Bus module. + +Copyright (c) 2006 - 2009, Intel Corporation +All rights reserved. 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. + +**/ + +#ifndef _EFI_PCI_ROM_TABLE_H_ +#define _EFI_PCI_ROM_TABLE_H_ + +/** + Add the Rom Image to internal database for later PCI light enumeration. + + @param ImageHandle Option Rom image handle. + @param Seg Segment of PCI space. + @param Bus Bus NO of PCI space. + @param Dev Dev NO of PCI space. + @param Func Func NO of PCI space. + @param RomAddress Base address of OptionRom. + @param RomLength Length of rom image. + +**/ +VOID +PciRomAddImageMapping ( + IN EFI_HANDLE ImageHandle, + IN UINTN Seg, + IN UINT8 Bus, + IN UINT8 Dev, + IN UINT8 Func, + IN UINT64 RomAddress, + IN UINT64 RomLength + ); + +/** + Get Option rom driver's mapping for PCI device. + + @param PciIoDevice Device instance. + + @retval TRUE Found Image mapping. + @retval FALSE Cannot found image mapping. + +**/ +BOOLEAN +PciRomGetImageMapping ( + IN PCI_IO_DEVICE *PciIoDevice + ); + +#endif diff --git a/MdeModulePkg/MdeModulePkg.dec b/MdeModulePkg/MdeModulePkg.dec index 7a167ac619..d3ca875c22 100644 --- a/MdeModulePkg/MdeModulePkg.dec +++ b/MdeModulePkg/MdeModulePkg.dec @@ -225,6 +225,19 @@ # If FALSE, DXE IPL will not support UEFI decompression to save space. gEfiMdeModulePkgTokenSpaceGuid.PcdDxeIplSupportUefiDecompress|TRUE|BOOLEAN|0x0001200c + ## This PCD specifies whether PciBus supports the hot plug device. + gEfiMdeModulePkgTokenSpaceGuid.PcdPciBusHotplugDeviceSupport|TRUE|BOOLEAN|0x0001003d + + ## This PCD specifies whether the Single Root I/O virtualization support. + gEfiMdeModulePkgTokenSpaceGuid.PcdSrIovSupport|TRUE|BOOLEAN|0x10000044 + + ## This PCD specifies whether the Alternative Routing-ID support. + gEfiMdeModulePkgTokenSpaceGuid.PcdAriSupport|TRUE|BOOLEAN|0x10000045 + + ## This PCD specifies whether the Multi Root I/O virtualization support. + gEfiMdeModulePkgTokenSpaceGuid.PcdMrIovSupport|FALSE|BOOLEAN|0x10000046 + + [PcdsFeatureFlag.IA32] ## # This feature flag specifies whether DxeIpl switches to long mode to enter DXE phase. @@ -293,6 +306,12 @@ ## FFS filename to find the ACPI tables gEfiMdeModulePkgTokenSpaceGuid.PcdAcpiTableStorageFile|{ 0x25, 0x4e, 0x37, 0x7e, 0x01, 0x8e, 0xee, 0x4f, 0x87, 0xf2, 0x39, 0xc, 0x23, 0xc6, 0x6, 0xcd }|VOID*|16 + ## Single root I/O virtualization virtual function memory BAR alignment + # BITN set indicates 2 of n+12 power + # BIT0 set indicates 4KB alignment + # BIT1 set indicates 8KB alignment + gEfiMdeModulePkgTokenSpaceGuid.PcdSrIovSystemPageSize|0x1|UINT32|0x10000047 + [PcdsFixedAtBuild,PcdsPatchableInModule] ## Maximun number of performance log entries during PEI phase. gEfiMdeModulePkgTokenSpaceGuid.PcdMaxPeiPerformanceLogEntries|40|UINT8|0x0001002f diff --git a/MdeModulePkg/MdeModulePkg.dsc b/MdeModulePkg/MdeModulePkg.dsc index 0b4e258490..4d1d941848 100644 --- a/MdeModulePkg/MdeModulePkg.dsc +++ b/MdeModulePkg/MdeModulePkg.dsc @@ -235,6 +235,8 @@ [Components.common] MdeModulePkg/Application/HelloWorld/HelloWorld.inf + MdeModulePkg/Bus/Pci/PciBusDxe/PciBusDxe.inf + MdeModulePkg/Bus/Pci/IncompatiblePciDeviceSupportDxe/IncompatiblePciDeviceSupportDxe.inf MdeModulePkg/Bus/Pci/EhciDxe/EhciDxe.inf MdeModulePkg/Bus/Pci/UhciDxe/UhciDxe.inf MdeModulePkg/Bus/Scsi/ScsiBusDxe/ScsiBusDxe.inf -- 2.30.2