From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mga18.intel.com (mga18.intel.com [134.134.136.126]) by mx.groups.io with SMTP id smtpd.web12.5162.1572621016444543116 for ; Fri, 01 Nov 2019 08:10:16 -0700 Authentication-Results: mx.groups.io; dkim=missing; spf=pass (domain: intel.com, ip: 134.134.136.126, mailfrom: ashraf.javeed@intel.com) X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga008.jf.intel.com ([10.7.209.65]) by orsmga106.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 01 Nov 2019 08:10:16 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.68,256,1569308400"; d="scan'208";a="194687097" Received: from pidsbabios005.gar.corp.intel.com ([10.223.9.183]) by orsmga008.jf.intel.com with ESMTP; 01 Nov 2019 08:10:12 -0700 From: "Javeed, Ashraf" To: devel@edk2.groups.io Cc: Jian J Wang , Hao A Wu , Ray Ni Subject: [edk2-staging/UEFI_PCI_ENHANCE-2 PATCH 05/12] PciBusDxe: Setup sub-phases for PCI feature enumeration Date: Fri, 1 Nov 2019 20:39:45 +0530 Message-Id: <20191101150952.3340-6-ashraf.javeed@intel.com> X-Mailer: git-send-email 2.21.0.windows.1 In-Reply-To: <20191101150952.3340-1-ashraf.javeed@intel.com> References: <20191101150952.3340-1-ashraf.javeed@intel.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit BZ: https://bugzilla.tianocore.org/show_bug.cgi?id=2194 The code changes are made to setup the following internal sub-phases for enumerating the PCI features in the late phase of the PCI Bus driver. (1) PciFeatureRootBridgeScan - initial phase in configuring the other PCI features to record the primary root ports (2) PciFeatureGetDevicePolicy - get the PCI device-specific platform pol- icies and align with device capabilities (3) PciFeatureSetupPhase - align all PCI nodes in the PCI heirarchical tree (if required for that PCI feature) (4) PciFeatureConfigurationPhase - finally override to complete configu- ration of the PCI feature The code changes are made to support the configuration of other PCIe features, like MPS, which require a common value to be assigned among all the child PCI devices and its parent root port device. Signed-off-by: Ashraf Javeed Cc: Jian J Wang Cc: Hao A Wu Cc: Ray Ni --- MdeModulePkg/Bus/Pci/PciBusDxe/PciFeatureSupport.c | 859 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ MdeModulePkg/Bus/Pci/PciBusDxe/PciFeatureSupport.h | 146 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1005 insertions(+) diff --git a/MdeModulePkg/Bus/Pci/PciBusDxe/PciFeatureSupport.c b/MdeModulePkg/Bus/Pci/PciBusDxe/PciFeatureSupport.c index 8be227a..ab0e096 100644 --- a/MdeModulePkg/Bus/Pci/PciBusDxe/PciFeatureSupport.c +++ b/MdeModulePkg/Bus/Pci/PciBusDxe/PciFeatureSupport.c @@ -9,6 +9,23 @@ SPDX-License-Identifier: BSD-2-Clause-Patent #include "PciBus.h" #include "PciFeatureSupport.h" +/** + A gobal pointer to PRIMARY_ROOT_PORT_NODE buffer to track all the primary physical + PCI Root Ports (PCI Controllers) for a given PCI Root Bridge instance while + enumerating to configure the PCI features +**/ +PRIMARY_ROOT_PORT_NODE *mPrimaryRootPortList; + +/** + A global pointer to PCI_FEATURE_CONFIGURATION_COMPLETION_LIST, which stores all + the PCI Root Bridge instances that are enumerated for the other PCI features, + like MaxPayloadSize & MaxReadReqSize; during the the Start() interface of the + driver binding protocol. The records pointed by this pointer would be destroyed + when the DXE core invokes the Stop() interface. +**/ +PCI_FEATURE_CONFIGURATION_COMPLETION_LIST *mPciFeaturesConfigurationCompletionList = NULL; + + /** Main routine to indicate whether the platform has selected the Max_Payload_Size PCI feature to be configured by this driver @@ -175,3 +192,845 @@ SetupPtm ( { return (PcdGet32 (PcdOtherPciFeatures) & PCI_FEATURE_SUPPORT_FLAG_PTM) ? TRUE : FALSE; } + +/** + Helper routine to determine the existence of previously enumerated PCI device + + @retval TRUE PCI device exist + FALSE does not exist +**/ +BOOLEAN +DeviceExist ( + PCI_IO_DEVICE *PciDevice + ) +{ + EFI_PCI_IO_PROTOCOL *PciIoProtocol = &PciDevice->PciIo; + UINT16 VendorId = 0xFFFF; + + PciIoProtocol->Pci.Read ( + PciIoProtocol, + EfiPciIoWidthUint16, + PCI_VENDOR_ID_OFFSET, + 1, + &VendorId + ); + if (VendorId == 0 || VendorId == 0xFFFF) { + return FALSE; + } else { + return TRUE; + } +} + +/** + Helper routine which determines whether the given PCI Root Bridge instance + record already exist. This routine shall help avoid duplicate record creation + in case of re-enumeration of PCI configuation features. + + @param RootBridge A pointer to the PCI_IO_DEVICE for the Root Bridge + @param PciFeatureConfigRecord A pointer to a pointer for type + PCI_FEATURE_CONFIGURATION_COMPLETION_LIST + record, Use to return the specific record. + + @retval TRUE Record already exist + FALSE Record does not exist for the given PCI Root Bridge +**/ +BOOLEAN +CheckPciFeatureConfigurationRecordExist ( + IN PCI_IO_DEVICE *RootBridge, + OUT PCI_FEATURE_CONFIGURATION_COMPLETION_LIST **PciFeatureConfigRecord + ) +{ + LIST_ENTRY *Link; + PCI_FEATURE_CONFIGURATION_COMPLETION_LIST *Temp; + + if (mPciFeaturesConfigurationCompletionList) { + Link = &mPciFeaturesConfigurationCompletionList->RootBridgeLink; + + do { + Temp = PCI_FEATURE_CONFIGURATION_COMPLETION_LIST_FROM_LINK (Link); + if (Temp->RootBridgeHandle == RootBridge->Handle) { + *PciFeatureConfigRecord = Temp; + return TRUE; + } + Link = Link->ForwardLink; + } while (Link != &mPciFeaturesConfigurationCompletionList->RootBridgeLink); + } + // + // not found on the PCI feature configuration completion list + // + *PciFeatureConfigRecord = NULL; + return FALSE; +} + +/** + This routine is primarily to avoid multiple configuration of PCI features + to the same PCI Root Bridge due to EDK2 core's ConnectController calls on + all the EFI handles. This routine also provide re-enumeration of the PCI + features on the same PCI Root Bridge based on the policy of ReEnumeratePciFeatureConfiguration + of the PCI_FEATURE_CONFIGURATION_COMPLETION_LIST. + + @param RootBridge A pointer to the PCI_IO_DEVICE for the Root Bridge + + @retval TRUE PCI Feature configuration required for the PCI + Root Bridge + FALSE PCI Feature configuration is not required to be + re-enumerated for the PCI Root Bridge +**/ +BOOLEAN +CheckPciFeaturesConfigurationRequired ( + IN PCI_IO_DEVICE *RootBridge + ) +{ + LIST_ENTRY *Link; + PCI_FEATURE_CONFIGURATION_COMPLETION_LIST *Temp; + + if (mPciFeaturesConfigurationCompletionList) { + Link = &mPciFeaturesConfigurationCompletionList->RootBridgeLink; + + do { + Temp = PCI_FEATURE_CONFIGURATION_COMPLETION_LIST_FROM_LINK (Link); + if (Temp->RootBridgeHandle == RootBridge->Handle) { + return Temp->ReEnumeratePciFeatureConfiguration; + } + Link = Link->ForwardLink; + } while (Link != &mPciFeaturesConfigurationCompletionList->RootBridgeLink); + } + // + // not found on the PCI feature configuration completion list, return as required + // + return TRUE; +} + +/** + This routine finds the duplicate record if exist and assigns the re-enumeration + requirement flag, as passed as input. It creates new record for the PCI Root + Bridge and appends the list after updating its re-enumeration flag. + + @param RootBridge A pointer to PCI_IO_DEVICE of the Root Bridge + @param ReEnumerationRequired A BOOLEAN for recording the re-enumeration requirement + + @retval EFI_SUCCESS new record inserted into the list or updated the + existing record + EFI_INVALID_PARAMETER Unexpected error as CheckPciFeatureConfigurationRecordExist + reports as record exist but does not return its pointer + EFI_OUT_OF_RESOURCES Not able to create PCI features configuratin complete + record for the RootBridge +**/ +EFI_STATUS +AddRootBridgeInPciFeaturesConfigCompletionList ( + IN PCI_IO_DEVICE *RootBridge, + IN BOOLEAN ReEnumerationRequired + ) +{ + PCI_FEATURE_CONFIGURATION_COMPLETION_LIST *Temp; + + if (CheckPciFeatureConfigurationRecordExist (RootBridge, &Temp)) { + // + // this PCI Root Bridge record already exist; it may have been re-enumerated + // hence just update its enumeration required flag again to exit + // + if (Temp) { + Temp->ReEnumeratePciFeatureConfiguration = ReEnumerationRequired; + return EFI_SUCCESS; + } else { + // + // PCI feature configuration complete record reported as exist and no + // record pointer returned + // + return EFI_INVALID_PARAMETER; + } + + } else { + + Temp = AllocateZeroPool (sizeof (PCI_FEATURE_CONFIGURATION_COMPLETION_LIST)); + if (Temp == NULL) { + return EFI_OUT_OF_RESOURCES; + } + Temp->Signature = PCI_FEATURE_CONFIGURATION_SIGNATURE; + Temp->RootBridgeHandle = RootBridge->Handle; + Temp->ReEnumeratePciFeatureConfiguration = ReEnumerationRequired; + if (mPciFeaturesConfigurationCompletionList) { + InsertTailList ( + &mPciFeaturesConfigurationCompletionList->RootBridgeLink, + &Temp->RootBridgeLink + ); + } else { + // + // init the very first node of the Root Bridge + // + mPciFeaturesConfigurationCompletionList = Temp; + InitializeListHead (&mPciFeaturesConfigurationCompletionList->RootBridgeLink); + } + } + return EFI_SUCCESS; +} + +/** + Free up memory alloted for the primary physical PCI Root ports of the PCI Root + Bridge instance. Free up all the nodes of type PRIMARY_ROOT_PORT_NODE. +**/ +VOID +DestroyPrimaryRootPortNodes () +{ + LIST_ENTRY *Link; + PRIMARY_ROOT_PORT_NODE *Temp; + + if (mPrimaryRootPortList) { + Link = &mPrimaryRootPortList->NeighborRootPort; + + if (IsListEmpty (Link)) { + FreePool (mPrimaryRootPortList->OtherPciFeaturesConfigurationTable); + FreePool (mPrimaryRootPortList); + } else { + do { + if (Link->ForwardLink != &mPrimaryRootPortList->NeighborRootPort) { + Link = Link->ForwardLink; + } + Temp = PRIMARY_ROOT_PORT_NODE_FROM_LINK (Link); + Link = RemoveEntryList (Link); + FreePool (Temp->OtherPciFeaturesConfigurationTable); + FreePool (Temp); + } while (!IsListEmpty (Link)); + FreePool (mPrimaryRootPortList->OtherPciFeaturesConfigurationTable); + FreePool (mPrimaryRootPortList); + } + mPrimaryRootPortList = NULL; + } +} + +/** + Routine meant for initializing any global variables used. It primarily cleans + up the internal data structure memory allocated for the previous PCI Root Bridge + instance. This should be the first routine to call for any virtual PCI Root + Bridge instance. +**/ +VOID +SetupPciFeaturesConfigurationDefaults () +{ + // + // delete the primary root port list + // + if (mPrimaryRootPortList) { + DestroyPrimaryRootPortNodes (); + } +} + +/** + Main routine to determine the child PCI devices of a PCI bridge device + and group them under a common internal PCI features Configuration table. + + @param PciDevice A pointer to the PCI_IO_DEVICE. + @param PciFeaturesConfigTable A pointer to a pointer to the + OTHER_PCI_FEATURES_CONFIGURATION_TABLE. + Returns NULL in case of RCiEP or the PCI + device does match with any of the physical + Root ports, or it does not belong to any + Root port's PCI bus range (not a child) + + @retval EFI_SUCCESS able to determine the PCI feature + configuration table. For RCiEP since + since it is not prepared. + EFI_DEVICE_ERROR the PCI device has invalid EFI device + path +**/ +EFI_STATUS +GetPciFeaturesConfigurationTable ( + IN PCI_IO_DEVICE *PciDevice, + OUT OTHER_PCI_FEATURES_CONFIGURATION_TABLE **PciFeaturesConfigTable + ) +{ + LIST_ENTRY *Link; + PRIMARY_ROOT_PORT_NODE *Temp; + BOOLEAN NodeMatch; + EFI_DEVICE_PATH_PROTOCOL *RootPortPath; + EFI_DEVICE_PATH_PROTOCOL *PciDevicePath; + + if (mPrimaryRootPortList == NULL) { + // + // no populated PCI primary root ports to parse and match the PCI features + // configuration table + // + *PciFeaturesConfigTable = NULL; + return EFI_SUCCESS; + } + + + if (IsDevicePathEnd (PciDevice->DevicePath)){ + // + // the given PCI device does not have a valid device path + // + *PciFeaturesConfigTable = NULL; + return EFI_DEVICE_ERROR; + } + + + Link = &mPrimaryRootPortList->NeighborRootPort; + do { + Temp = PRIMARY_ROOT_PORT_NODE_FROM_LINK (Link); + RootPortPath = Temp->RootPortDevicePath; + PciDevicePath = PciDevice->DevicePath; + NodeMatch = FALSE; + // + // match the device path from the list of primary Root Ports with the given + // device; the initial nodes matching in sequence indicate that the given PCI + // device belongs to that PCI tree from the root port + // + if (IsDevicePathEnd (RootPortPath)) { + // + // critical error as no device path available in root + // + *PciFeaturesConfigTable = NULL; + return EFI_DEVICE_ERROR; + } + + if (EfiCompareDevicePath (RootPortPath, PciDevicePath)) { + // + // the given PCI device is the primary root port itself + // + *PciFeaturesConfigTable = Temp->OtherPciFeaturesConfigurationTable; + return EFI_SUCCESS; + } + // + // check this PCI device belongs to the primary root port of the root bridge + // any child PCI device will have the same initial device path nodes as + // its parent root port + // + while (!IsDevicePathEnd (RootPortPath)){ + + if (DevicePathNodeLength (RootPortPath) != DevicePathNodeLength (PciDevicePath)) { + // + // break to check the next primary root port nodes as does not match + // + NodeMatch = FALSE; + break; + } + if (CompareMem (RootPortPath, PciDevicePath, DevicePathNodeLength (RootPortPath)) != 0) { + // + // node does not match, break to check next node + // + NodeMatch = FALSE; + break; + } + NodeMatch = TRUE; + // + // advance to next node + // + RootPortPath = NextDevicePathNode (RootPortPath); + PciDevicePath = NextDevicePathNode (PciDevicePath); + } + + if (NodeMatch == TRUE) { + // + // device belongs to primary root port, return its PCI feature configuration + // table + // + *PciFeaturesConfigTable = Temp->OtherPciFeaturesConfigurationTable; + return EFI_SUCCESS; + } + + // + // advance to next Root port node + // + Link = Link->ForwardLink; + } while (Link != &mPrimaryRootPortList->NeighborRootPort); + // + // the PCI device must be RCiEP, does not belong to any primary root port + // + *PciFeaturesConfigTable = NULL; + return EFI_SUCCESS; +} + +/** + This routine determines the existance of the child PCI device for the given + PCI Root / Bridge Port device. Always assumes the input PCI device is the bridge + or PCI-PCI Bridge device. This routine should not be used with PCI endpoint device. + + @param PciDevice A pointer to the PCI_IO_DEVICE. + + @retval TRUE child device exist + FALSE no child device +**/ +BOOLEAN +IsPciRootPortEmpty ( + IN PCI_IO_DEVICE *PciDevice + ) +{ + if (IsListEmpty (&PciDevice->ChildList)){ + return TRUE; + } + return FALSE; +} + + +/** + Process each PCI device as per the pltaform and device-specific policy. + + @param RootBridge A pointer to the PCI_IO_DEVICE. + + @retval EFI_SUCCESS processing each PCI feature as per policy defined + was successful. + **/ +EFI_STATUS +SetupDevicePciFeatures ( + IN PCI_IO_DEVICE *PciDevice, + IN PCI_FEATURE_CONFIGURATION_PHASE PciConfigPhase + ) +{ + EFI_STATUS Status; + OTHER_PCI_FEATURES_CONFIGURATION_TABLE *OtherPciFeaturesConfigTable; + + OtherPciFeaturesConfigTable = NULL; + Status = GetPciFeaturesConfigurationTable (PciDevice, &OtherPciFeaturesConfigTable); + if (EFI_ERROR( Status)) { + DEBUG (( + DEBUG_WARN, "[Cfg group: 0 {error in dev path}]" + )); + } else if (OtherPciFeaturesConfigTable == NULL) { + DEBUG (( + DEBUG_INFO, "[Cfg group: 0]" + )); + } else { + DEBUG (( + DEBUG_INFO, "[Cfg group: %d]", + OtherPciFeaturesConfigTable->ID + )); + } + + if (PciConfigPhase == PciFeatureGetDevicePolicy) { + Status = GetPciDevicePlatformPolicy (PciDevice); + if (EFI_ERROR(Status)) { + DEBUG (( + DEBUG_ERROR, "Error in obtaining PCI device policy!!!\n" + )); + } + } + + return Status; +} + +/** + Traverse all the nodes from the root bridge or PCI-PCI bridge instance, to + configure the PCI features as per the device-specific platform policy, and + as per the device capability, as applicable. + + @param RootBridge A pointer to the PCI_IO_DEVICE. + + @retval EFI_SUCCESS Traversing all the nodes of the root bridge + instances were successfull. +**/ +EFI_STATUS +SetupPciFeatures ( + IN PCI_IO_DEVICE *RootBridge, + IN PCI_FEATURE_CONFIGURATION_PHASE PciConfigPhase + ) +{ + EFI_STATUS Status; + LIST_ENTRY *Link; + PCI_IO_DEVICE *Device; + + for ( Link = RootBridge->ChildList.ForwardLink + ; Link != &RootBridge->ChildList + ; Link = Link->ForwardLink + ) { + Device = PCI_IO_DEVICE_FROM_LINK (Link); + if (!DeviceExist (Device)) { + DEBUG (( + DEBUG_ERROR, "::Device [%02x|%02x|%02x] - does not exist!!!\n", + Device->BusNumber, Device->DeviceNumber, Device->FunctionNumber + )); + continue; + } + if (IS_PCI_BRIDGE (&Device->Pci)) { + DEBUG (( + DEBUG_INFO, "::Bridge [%02x|%02x|%02x] -", + Device->BusNumber, Device->DeviceNumber, Device->FunctionNumber + )); + if (Device->IsPciExp) { + Status = SetupDevicePciFeatures (Device, PciConfigPhase); + } else { + DEBUG (( DEBUG_INFO, "Not a PCIe capable device!\n")); + // + // PCI Bridge which does not have PCI Express Capability structure + // cannot process this kind of PCI Bridge device + // + + } + + SetupPciFeatures (Device, PciConfigPhase); + } else { + DEBUG (( + DEBUG_INFO, "::Device [%02x|%02x|%02x] -", + Device->BusNumber, Device->DeviceNumber, Device->FunctionNumber + )); + if (Device->IsPciExp) { + + Status = SetupDevicePciFeatures (Device, PciConfigPhase); + } else { + DEBUG (( DEBUG_INFO, "Not a PCIe capable device!\n")); + // + // PCI Device which does not have PCI Express Capability structure + // cannot process this kind of PCI device + // + } + } + } + + return EFI_SUCCESS; +} + +/** + Program the PCI device, to override the PCI features as per the policy, + resolved from previous traverse. + + @param RootBridge A pointer to the PCI_IO_DEVICE. + + @retval EFI_SUCCESS The other PCI features configuration during enumeration + of all the nodes of the PCI root bridge instance were + programmed in PCI-compliance pattern along with the + device-specific policy, as applicable. + @retval EFI_UNSUPPORTED One of the override operation maong the nodes of + the PCI hierarchy resulted in a incompatible address + range. + @retval EFI_INVALID_PARAMETER The override operation is performed with invalid input + parameters. +**/ +EFI_STATUS +ProgramDevicePciFeatures ( + IN PCI_IO_DEVICE *PciDevice + ) +{ + EFI_STATUS Status; + + return Status; +} + +/** + Program all the nodes of the specified root bridge or PCI-PCI Bridge, to + override the PCI features. + + @param RootBridge A pointer to the PCI_IO_DEVICE. + + @retval EFI_SUCCESS The other PCI features configuration during enumeration + of all the nodes of the PCI root bridge instance were + programmed in PCI-compliance pattern along with the + device-specific policy, as applicable. + @retval EFI_UNSUPPORTED One of the override operation maong the nodes of + the PCI hierarchy resulted in a incompatible address + range. + @retval EFI_INVALID_PARAMETER The override operation is performed with invalid input + parameters. +**/ +EFI_STATUS +ProgramPciFeatures ( + IN PCI_IO_DEVICE *RootBridge + ) +{ + EFI_STATUS Status; + LIST_ENTRY *Link; + PCI_IO_DEVICE *Device; + + for ( Link = RootBridge->ChildList.ForwardLink + ; Link != &RootBridge->ChildList + ; Link = Link->ForwardLink + ) { + Device = PCI_IO_DEVICE_FROM_LINK (Link); + if (!DeviceExist (Device)) { + DEBUG (( + DEBUG_ERROR, "::Device [%02x|%02x|%02x] - does not exist!!!\n", + Device->BusNumber, Device->DeviceNumber, Device->FunctionNumber + )); + continue; + } + if (IS_PCI_BRIDGE (&Device->Pci)) { + DEBUG (( + DEBUG_INFO, "::Bridge [%02x|%02x|%02x] -", + Device->BusNumber, Device->DeviceNumber, Device->FunctionNumber + )); + if (Device->IsPciExp) { + DEBUG (( DEBUG_INFO, "ready to override!\n")); + + Status = ProgramDevicePciFeatures (Device); + } else { + DEBUG (( DEBUG_INFO, "skipped!\n")); + // + // PCI Bridge which does not have PCI Express Capability structure + // cannot process this kind of PCI Bridge device + // + } + + Status = ProgramPciFeatures (Device); + } else { + DEBUG (( + DEBUG_INFO, "::Device [%02x|%02x|%02x] -", + Device->BusNumber, Device->DeviceNumber, Device->FunctionNumber + )); + if (Device->IsPciExp) { + DEBUG (( DEBUG_INFO, "ready to override!\n")); + + Status = ProgramDevicePciFeatures (Device); + } else { + DEBUG (( DEBUG_INFO, "skipped!\n")); + // + // PCI Device which does not have PCI Express Capability structure + // cannot process this kind of PCI device + // + } + } + } + + return Status; +} + +/** + Create and add a node of type PRIMARY_ROOT_PORT_NODE in the list for the primary + Root Port so that all its child PCI devices can be identified against the PCI + features configuration table group ID, of type OTHER_PCI_FEATURES_CONFIGURATION_TABLE. + + @param BridgePort A pointer to the PCI_IO_DEVICE + @param PortNumber A UINTN value to identify the PCI feature configuration + table group + + @retval EFI_SUCCESS success in adding a node of PRIMARY_ROOT_PORT_NODE + to the list + EFI_OUT_OF_RESOURCES unable to get memory for creating the node +**/ +EFI_STATUS +AddPrimaryRootPortNode ( + IN PCI_IO_DEVICE *BridgePort, + IN UINTN PortNumber + ) +{ + PRIMARY_ROOT_PORT_NODE *RootPortNode = NULL; + OTHER_PCI_FEATURES_CONFIGURATION_TABLE *PciConfigTable = NULL; + + RootPortNode = AllocateZeroPool (sizeof (PRIMARY_ROOT_PORT_NODE)); + if (RootPortNode == NULL) { + return EFI_OUT_OF_RESOURCES; + } + RootPortNode->Signature = PCI_ROOT_PORT_SIGNATURE; + RootPortNode->RootPortDevicePath = BridgePort->DevicePath; + PciConfigTable = AllocateZeroPool ( + sizeof (OTHER_PCI_FEATURES_CONFIGURATION_TABLE) + ); + if (PciConfigTable) { + PciConfigTable->ID = PortNumber; + } + RootPortNode->OtherPciFeaturesConfigurationTable = PciConfigTable; + + if (mPrimaryRootPortList != NULL) { + InsertTailList (&mPrimaryRootPortList->NeighborRootPort, &RootPortNode->NeighborRootPort); + } else { + InitializeListHead (&RootPortNode->NeighborRootPort); + mPrimaryRootPortList = RootPortNode; + } + + if (PciConfigTable == NULL) { + return EFI_OUT_OF_RESOURCES; + } + return EFI_SUCCESS; +} + +/** + Scan all the nodes of the RootBridge to identify and create a separate list + of all primary physical PCI root ports and link each with its own instance of + the PCI Feature Configuration Table. + + @param RootBridge A pointer to the PCI_IO_DEVICE of the PCI Root Bridge + + @retval EFI_OUT_OF_RESOURCES unable to allocate buffer to store PCI feature + configuration table for all the physical PCI root + ports given + EFI_NOT_FOUND No PCI Bridge device found + EFI_SUCCESS PCI Feature COnfiguration table created for all + the PCI Rooot ports found + EFI_INVALID_PARAMETER invalid parameter passed to the routine which + creates the PCI controller node for the primary + Root post list +**/ +EFI_STATUS +RecordPciRootPortBridges ( + IN PCI_IO_DEVICE *RootBridge + ) +{ + EFI_STATUS Status = EFI_NOT_FOUND; + LIST_ENTRY *Link; + PCI_IO_DEVICE *Device; + UINTN NumberOfRootPorts; + + DEBUG (( + DEBUG_INFO, "<<********** RecordPciRootPortBridges -start *************>>\n" + )); + NumberOfRootPorts = 0; + for ( Link = RootBridge->ChildList.ForwardLink + ; Link != &RootBridge->ChildList + ; Link = Link->ForwardLink + ) { + Device = PCI_IO_DEVICE_FROM_LINK (Link); + if (!DeviceExist (Device)) { + continue; + } + if (IS_PCI_BRIDGE (&Device->Pci)) { + NumberOfRootPorts++; + DEBUG (( + DEBUG_INFO, "#%d ::Bridge [%02x|%02x|%02x]", + NumberOfRootPorts, Device->BusNumber, Device->DeviceNumber, Device->FunctionNumber + )); + // + // create a primary root port list if that port is connected + // + if (!IsListEmpty (&Device->ChildList)) { + DEBUG (( + DEBUG_INFO, "- has downstream device!\n" + )); + Status = AddPrimaryRootPortNode (Device, NumberOfRootPorts); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, "PCI configuration table allocation failure for #%d ::Bridge [%02x|%02x|%02x]\n", + NumberOfRootPorts, Device->BusNumber, Device->DeviceNumber, Device->FunctionNumber + )); + } + } else { + DEBUG (( + DEBUG_INFO, "- no downstream device!\n" + )); + } + } + } + DEBUG (( + DEBUG_INFO, "<<********** RecordPciRootPortBridges - end **********>>\n" + )); + return Status; +} + +/** + Enumerate all the nodes of the specified root bridge or PCI-PCI Bridge, to + configure the other PCI features. + + @param RootBridge A pointer to the PCI_IO_DEVICE. + + @retval EFI_SUCCESS The other PCI features configuration during enumeration + of all the nodes of the PCI root bridge instance were + programmed in PCI-compliance pattern along with the + device-specific policy, as applicable. + @retval EFI_UNSUPPORTED One of the override operation maong the nodes of + the PCI hierarchy resulted in a incompatible address + range. + @retval EFI_INVALID_PARAMETER The override operation is performed with invalid input + parameters. +**/ +EFI_STATUS +EnumerateOtherPciFeatures ( + IN PCI_IO_DEVICE *RootBridge + ) +{ + EFI_STATUS Status; + CHAR16 *Str; + UINTN OtherPciFeatureConfigPhase; + + // + // check on PCI features configuration is complete and re-enumeration is required + // + if (!CheckPciFeaturesConfigurationRequired (RootBridge)) { + return EFI_ALREADY_STARTED; + } + + Str = ConvertDevicePathToText ( + DevicePathFromHandle (RootBridge->Handle), + FALSE, + FALSE + ); + DEBUG ((DEBUG_INFO, "Enumerating PCI features for Root Bridge %s\n", Str != NULL ? Str : L"")); + + for ( OtherPciFeatureConfigPhase = PciFeatureRootBridgeScan + ; OtherPciFeatureConfigPhase <= PciFeatureConfigurationComplete + ; OtherPciFeatureConfigPhase++ + ) { + switch (OtherPciFeatureConfigPhase){ + case PciFeatureRootBridgeScan: + SetupPciFeaturesConfigurationDefaults (); + // + //first scan the entire root bridge heirarchy for the primary PCI root ports + // + RecordPciRootPortBridges (RootBridge); + break; + + case PciFeatureGetDevicePolicy: + case PciFeatureSetupPhase: + DEBUG (( + DEBUG_INFO, "<<********** SetupPciFeatures - start **********>>\n" + )); + // + // enumerate the other PCI features + // + Status = SetupPciFeatures (RootBridge, OtherPciFeatureConfigPhase); + + DEBUG (( + DEBUG_INFO, "<<********** SetupPciFeatures - end **********>>\n" + )); + break; + + case PciFeatureConfigurationPhase: + // + // override the PCI features as per enumeration phase + // + DEBUG ((DEBUG_INFO, "PCI features override for Root Bridge %s\n", Str != NULL ? Str : L"")); + DEBUG (( + DEBUG_INFO, "<<********** ProgramPciFeatures - start **********>>\n" + )); + Status = ProgramPciFeatures (RootBridge); + DEBUG (( + DEBUG_INFO, "<<********** ProgramPciFeatures - end **********>>\n" + )); + break; + + case PciFeatureConfigurationComplete: + // + // clean up the temporary resource nodes created for this root bridge + // + DestroyPrimaryRootPortNodes (); + } + } + + if (Str != NULL) { + FreePool (Str); + } + // + // mark this root bridge as PCI features configuration complete, and no new + // enumeration is required + // + AddRootBridgeInPciFeaturesConfigCompletionList (RootBridge, FALSE); + return Status; +} + +/** + This routine is invoked from the Stop () interface for the EFI handle of the + RootBridge. Free up its node of type PCI_FEATURE_CONFIGURATION_COMPLETION_LIST. + + @param RootBridge A pointer to the PCI_IO_DEVICE +**/ +VOID +DestroyRootBridgePciFeaturesConfigCompletionList ( + IN PCI_IO_DEVICE *RootBridge + ) +{ + LIST_ENTRY *Link; + PCI_FEATURE_CONFIGURATION_COMPLETION_LIST *Temp; + + if (mPciFeaturesConfigurationCompletionList) { + Link = &mPciFeaturesConfigurationCompletionList->RootBridgeLink; + + do { + Temp = PCI_FEATURE_CONFIGURATION_COMPLETION_LIST_FROM_LINK (Link); + if (Temp->RootBridgeHandle == RootBridge->Handle) { + RemoveEntryList (Link); + FreePool (Temp); + return; + } + Link = Link->ForwardLink; + } while (Link != &mPciFeaturesConfigurationCompletionList->RootBridgeLink); + } + // + // not found on the PCI feature configuration completion list, return + // + return; +} diff --git a/MdeModulePkg/Bus/Pci/PciBusDxe/PciFeatureSupport.h b/MdeModulePkg/Bus/Pci/PciBusDxe/PciFeatureSupport.h index d06a5e8..b06c140 100644 --- a/MdeModulePkg/Bus/Pci/PciBusDxe/PciFeatureSupport.h +++ b/MdeModulePkg/Bus/Pci/PciBusDxe/PciFeatureSupport.h @@ -23,4 +23,150 @@ SPDX-License-Identifier: BSD-2-Clause-Patent #define PCI_FEATURE_SUPPORT_FLAG_CCC BIT13 #define PCI_FEATURE_SUPPORT_FLAG_ESYN BIT14 #define PCI_FEATURE_SUPPORT_FLAG_PTM BIT20 + +// +// defines the data structure to hold the details of the PCI Root port devices +// +typedef struct _PRIMARY_ROOT_PORT_NODE PRIMARY_ROOT_PORT_NODE; + +// +// defines the data structure to hold the configuration data for the other PCI +// features +// +typedef struct _OTHER_PCI_FEATURES_CONFIGURATION_TABLE OTHER_PCI_FEATURES_CONFIGURATION_TABLE; + +// +// Defines for the PCI features configuration completion and re-enumeration list +// +typedef struct _PCI_FEATURE_CONFIGURATION_COMPLETION_LIST PCI_FEATURE_CONFIGURATION_COMPLETION_LIST; + +// +// Signature value for the PCI Root Port node +// +#define PCI_ROOT_PORT_SIGNATURE SIGNATURE_32 ('p', 'c', 'i', 'p') + +// +// Definitions of the PCI Root Port data structure members +// +struct _PRIMARY_ROOT_PORT_NODE { + // + // Signature header + // + UINT32 Signature; + // + // linked list pointers to next node + // + LIST_ENTRY NeighborRootPort; + // + // pointer to PCI_IO_DEVICE of the primary PCI Controller device + // + EFI_DEVICE_PATH_PROTOCOL *RootPortDevicePath; + // + // pointer to the corresponding PCI feature configuration Table node + // all the child PCI devices of the controller are aligned based on this table + // + OTHER_PCI_FEATURES_CONFIGURATION_TABLE *OtherPciFeaturesConfigurationTable; +}; + +#define PRIMARY_ROOT_PORT_NODE_FROM_LINK(a) \ + CR (a, PRIMARY_ROOT_PORT_NODE, NeighborRootPort, PCI_ROOT_PORT_SIGNATURE) + +// +// Definition of the PCI Feature configuration Table members +// +struct _OTHER_PCI_FEATURES_CONFIGURATION_TABLE { + // + // Configuration Table ID + // + UINTN ID; +}; + + +// +// PCI feature configuration node signature value +// +#define PCI_FEATURE_CONFIGURATION_SIGNATURE SIGNATURE_32 ('p', 'c', 'i', 'f') + +struct _PCI_FEATURE_CONFIGURATION_COMPLETION_LIST { + // + // Signature header + // + UINT32 Signature; + // + // link to next Root Bridge whose PCI Feature configuration is complete + // + LIST_ENTRY RootBridgeLink; + // + // EFI handle of the Root Bridge whose PCI feature configuration is complete + // + EFI_HANDLE RootBridgeHandle; + // + // indication for complete re-enumeration of the PCI feature configuration + // + BOOLEAN ReEnumeratePciFeatureConfiguration; +}; + +#define PCI_FEATURE_CONFIGURATION_COMPLETION_LIST_FROM_LINK(a) \ + CR (a, PCI_FEATURE_CONFIGURATION_COMPLETION_LIST, RootBridgeLink, PCI_FEATURE_CONFIGURATION_SIGNATURE) + +// +// Declaration of the internal sub-phases within the PCI Feature enumeration +// +typedef enum { + // + // initial phase in configuring the other PCI features to record the primary + // root ports + // + PciFeatureRootBridgeScan, + // + // get the PCI device-specific platform policies and align with device capabilities + // + PciFeatureGetDevicePolicy, + // + // align all PCI nodes in the PCI heirarchical tree + // + PciFeatureSetupPhase, + // + // finally override to complete configuration of the PCI feature + // + PciFeatureConfigurationPhase, + // + // PCI feature configuration complete + // + PciFeatureConfigurationComplete + +}PCI_FEATURE_CONFIGURATION_PHASE; + + +/** + Enumerate all the nodes of the specified root bridge or PCI-PCI Bridge, to + configure the other PCI features. + + @param RootBridge A pointer to the PCI_IO_DEVICE. + + @retval EFI_SUCCESS The other PCI features configuration during enumeration + of all the nodes of the PCI root bridge instance were + programmed in PCI-compliance pattern along with the + device-specific policy, as applicable. + @retval EFI_UNSUPPORTED One of the override operation maong the nodes of + the PCI hierarchy resulted in a incompatible address + range. + @retval EFI_INVALID_PARAMETER The override operation is performed with invalid input + parameters. +**/ +EFI_STATUS +EnumerateOtherPciFeatures ( + IN PCI_IO_DEVICE *RootBridge + ); + +/** + This routine is invoked from the Stop () interface for the EFI handle of the + RootBridge. Free up its node of type PCI_FEATURE_CONFIGURATION_COMPLETION_LIST. + + @param RootBridge A pointer to the PCI_IO_DEVICE +**/ +VOID +DestroyRootBridgePciFeaturesConfigCompletionList ( + IN PCI_IO_DEVICE *RootBridge + ); #endif -- 2.21.0.windows.1