audk/UefiPayloadPkg/Library/PciSegmentInfoLibAcpiBoardInfo/PciSegmentInfoLibAcpiBoardI...

283 lines
9.6 KiB
C

/** @file
PCI Segment Information Library that returns one segment whose
segment base address is retrieved from AcpiBoardInfo HOB.
Copyright (c) 2020, Intel Corporation. All rights reserved.<BR>
Copyright (c) 2024, Rivos Inc. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include <PiDxe.h>
#include <Guid/AcpiBoardInfoGuid.h>
#include <Library/HobLib.h>
#include <Library/PciSegmentInfoLib.h>
#include <Library/DebugLib.h>
#include <UniversalPayload/PciRootBridges.h>
#include <Library/PciLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/PcdLib.h>
#include <Library/BaseMemoryLib.h>
#include <Guid/PciSegmentInfoGuid.h>
static PCI_SEGMENT_INFO *mPciSegments;
static UINTN mCount;
/**
Find segment info from all root bridges
@param[in] PciRootBridgeInfo Pointer of Universal Payload PCI Root Bridge Info Hob
@param[in] UplSegmentInfo Pointer of Universal UPL Segment Info
@param[out] NumberOfRootBridges Number of root bridges detected
**/
VOID
RetrieveMultiSegmentInfoFromHob (
IN UNIVERSAL_PAYLOAD_PCI_ROOT_BRIDGES *PciRootBridgeInfo,
IN UPL_PCI_SEGMENT_INFO_HOB *UplSegmentInfo,
OUT UINTN *NumberOfRootBridges
)
{
UINTN Size;
UINT8 IndexForExistingSegments;
UINT8 NumberOfExistingSegments;
UINT8 IndexForRb;
PCI_SEGMENT_INFO *pPciSegments;
if (PciRootBridgeInfo == NULL) {
mPciSegments = NULL;
return;
}
IndexForExistingSegments = 0;
NumberOfExistingSegments = 0;
IndexForRb = 0;
Size = PciRootBridgeInfo->Count * sizeof (PCI_SEGMENT_INFO);
*NumberOfRootBridges = PciRootBridgeInfo->Count;
pPciSegments = (PCI_SEGMENT_INFO *)AllocatePool (Size);
if (pPciSegments == NULL) {
ASSERT (pPciSegments != NULL);
return;
}
ZeroMem (pPciSegments, PciRootBridgeInfo->Count * sizeof (PCI_SEGMENT_INFO));
//
// RBs may share the same Segment number, but mPciSegments should not have duplicate segment data.
// 1. if RB Segment is same with existing one, update StartBus and EndBus of existing one
// 2. if RB Segment is different from all existing ones, add it to the existing Segments data buffer.
// pPciSegments is local temporary data buffer that will be freed later (size depends on numbers of RB)
// mPciSegments is global data that will be updated with the pPciSegments for caller to utilize. (size depends on numbers of different PciSegments)
//
NumberOfExistingSegments = 1;
pPciSegments[0].BaseAddress = UplSegmentInfo->SegmentInfo[0].BaseAddress;
pPciSegments[0].SegmentNumber = (UINT16)(PciRootBridgeInfo->RootBridge[0].Segment);
pPciSegments[0].StartBusNumber = (UINT8)PciRootBridgeInfo->RootBridge[0].Bus.Base;
pPciSegments[0].EndBusNumber = (UINT8)PciRootBridgeInfo->RootBridge[0].Bus.Limit;
for (IndexForRb = 1; IndexForRb < PciRootBridgeInfo->Count; IndexForRb++) {
for (IndexForExistingSegments = 0; IndexForExistingSegments < NumberOfExistingSegments; IndexForExistingSegments++) {
if (pPciSegments[IndexForExistingSegments].SegmentNumber == PciRootBridgeInfo->RootBridge[IndexForRb].Segment) {
if (pPciSegments[IndexForExistingSegments].StartBusNumber > PciRootBridgeInfo->RootBridge[IndexForRb].Bus.Base) {
pPciSegments[IndexForExistingSegments].StartBusNumber = (UINT8)PciRootBridgeInfo->RootBridge[IndexForRb].Bus.Base;
}
if (pPciSegments[IndexForExistingSegments].EndBusNumber < PciRootBridgeInfo->RootBridge[IndexForRb].Bus.Limit) {
pPciSegments[IndexForExistingSegments].EndBusNumber = (UINT8)PciRootBridgeInfo->RootBridge[IndexForRb].Bus.Limit;
}
break; // breaking after a match found
}
}
if (IndexForExistingSegments >= NumberOfExistingSegments) {
//
// No match found, add it to segments data buffer
//
pPciSegments[NumberOfExistingSegments].BaseAddress = UplSegmentInfo->SegmentInfo[IndexForRb].BaseAddress;
pPciSegments[NumberOfExistingSegments].SegmentNumber = (UINT16)(PciRootBridgeInfo->RootBridge[IndexForRb].Segment);
pPciSegments[NumberOfExistingSegments].StartBusNumber = (UINT8)PciRootBridgeInfo->RootBridge[IndexForRb].Bus.Base;
pPciSegments[NumberOfExistingSegments].EndBusNumber = (UINT8)PciRootBridgeInfo->RootBridge[IndexForRb].Bus.Limit;
NumberOfExistingSegments++;
}
}
//
// Prepare data for returning to caller
//
*NumberOfRootBridges = NumberOfExistingSegments;
Size = NumberOfExistingSegments * sizeof (PCI_SEGMENT_INFO);
mPciSegments = (PCI_SEGMENT_INFO *)AllocatePool (Size);
if (mPciSegments == NULL) {
ASSERT (FALSE);
return;
}
CopyMem (&mPciSegments[0], &pPciSegments[0], Size);
if (pPciSegments != NULL) {
FreePool (pPciSegments);
}
return;
}
/**
Find segment info from all root bridges for legacy systems
@param[in] PciRootBridgeInfo Pointer of Universal Payload PCI Root Bridge Info Hob
@param[out] NumberOfRootBridges Number of root bridges detected
**/
VOID
RetrieveSegmentInfoFromHob (
OUT UINTN *NumberOfRootBridges
)
{
EFI_HOB_GUID_TYPE *GuidHob;
ACPI_BOARD_INFO *AcpiBoardInfo;
*NumberOfRootBridges = 1;
// old model relies on gUefiAcpiBoardInfoGuid and hardcoded values for single segment only.
// This is only for backward compatibility, new platforms should adopt new model even in single segment cases.
//
mPciSegments = (PCI_SEGMENT_INFO *)AllocatePool (sizeof (PCI_SEGMENT_INFO));
ASSERT (mPciSegments != NULL);
GuidHob = GetFirstGuidHob (&gUefiAcpiBoardInfoGuid);
ASSERT (GuidHob != NULL);
if (GuidHob != NULL) {
AcpiBoardInfo = (ACPI_BOARD_INFO *)GET_GUID_HOB_DATA (GuidHob);
mPciSegments->SegmentNumber = 0;
mPciSegments->BaseAddress = AcpiBoardInfo->PcieBaseAddress;
mPciSegments->StartBusNumber = 0;
mPciSegments->EndBusNumber = 0xFF;
}
}
/**
Return info for all root bridges
@return All the root bridge info instances in an array.
**/
UNIVERSAL_PAYLOAD_PCI_ROOT_BRIDGES *
Get_RBInfo (
VOID
)
{
UNIVERSAL_PAYLOAD_PCI_ROOT_BRIDGES *PciRootBridgeInfo;
EFI_HOB_GUID_TYPE *GuidHob;
UNIVERSAL_PAYLOAD_GENERIC_HEADER *GenericHeader;
//
// Find Universal Payload PCI Root Bridge Info hob
//
GuidHob = GetFirstGuidHob (&gUniversalPayloadPciRootBridgeInfoGuid);
if ((GuidHob == NULL) || (sizeof (UNIVERSAL_PAYLOAD_GENERIC_HEADER) > GET_GUID_HOB_DATA_SIZE (GuidHob))) {
return NULL;
}
GenericHeader = (UNIVERSAL_PAYLOAD_GENERIC_HEADER *)GET_GUID_HOB_DATA (GuidHob);
if (GenericHeader->Length > GET_GUID_HOB_DATA_SIZE (GuidHob)) {
return NULL;
}
if ((GenericHeader->Revision != UNIVERSAL_PAYLOAD_PCI_ROOT_BRIDGES_REVISION) || (GenericHeader->Length < sizeof (UNIVERSAL_PAYLOAD_PCI_ROOT_BRIDGES))) {
return NULL;
}
//
// UNIVERSAL_PAYLOAD_PCI_ROOT_BRIDGES structure is used when Revision equals to UNIVERSAL_PAYLOAD_PCI_ROOT_BRIDGES_REVISION
//
PciRootBridgeInfo = (UNIVERSAL_PAYLOAD_PCI_ROOT_BRIDGES *)GET_GUID_HOB_DATA (GuidHob);
if (PciRootBridgeInfo->Count <= (GET_GUID_HOB_DATA_SIZE (GuidHob) - sizeof (UNIVERSAL_PAYLOAD_PCI_ROOT_BRIDGES)) / sizeof (UNIVERSAL_PAYLOAD_PCI_ROOT_BRIDGE)) {
return PciRootBridgeInfo;
}
return NULL;
}
/**
Return info for all root bridge segments
@return All the segment info instances in an array.
**/
UPL_PCI_SEGMENT_INFO_HOB *
Get_UPLSegInfo (
VOID
)
{
UPL_PCI_SEGMENT_INFO_HOB *UplSegmentInfo;
EFI_HOB_GUID_TYPE *GuidHob;
UNIVERSAL_PAYLOAD_GENERIC_HEADER *GenericHeader;
//
// Find Universal Payload Segment Info hob
//
GuidHob = GetFirstGuidHob (&gUplPciSegmentInfoHobGuid);
if ((GuidHob == NULL) || (sizeof (UNIVERSAL_PAYLOAD_GENERIC_HEADER) > GET_GUID_HOB_DATA_SIZE (GuidHob))) {
return NULL;
}
GenericHeader = (UNIVERSAL_PAYLOAD_GENERIC_HEADER *)GET_GUID_HOB_DATA (GuidHob);
if (GenericHeader->Length > GET_GUID_HOB_DATA_SIZE (GuidHob)) {
return NULL;
}
if ((GenericHeader->Revision != UNIVERSAL_PAYLOAD_PCI_ROOT_BRIDGES_REVISION) || (GenericHeader->Length < sizeof (UPL_PCI_SEGMENT_INFO_HOB))) {
return NULL;
}
//
// UPL_PCI_SEGMENT_INFO_HOB structure is used when Revision equals to UPL_PCI_SEGMENT_INFO_HOB_REVISION
//
UplSegmentInfo = (UPL_PCI_SEGMENT_INFO_HOB *)GET_GUID_HOB_DATA (GuidHob);
if (UplSegmentInfo->Count <= (GET_GUID_HOB_DATA_SIZE (GuidHob) - sizeof (UPL_PCI_SEGMENT_INFO_HOB)) / sizeof (UPL_SEGMENT_INFO)) {
return UplSegmentInfo;
}
return NULL;
}
/**
Return all the root bridge instances in an array.
@param Count Return the count of root bridge instances.
@return All the root bridge instances in an array.
The array should be passed into PciHostBridgeFreeRootBridges()
when it's not used.
**/
PCI_SEGMENT_INFO *
EFIAPI
GetPciSegmentInfo (
UINTN *Count
)
{
UNIVERSAL_PAYLOAD_PCI_ROOT_BRIDGES *PciRootBridgeInfo;
UPL_PCI_SEGMENT_INFO_HOB *UplSegmentInfo;
if (mPciSegments != NULL) {
*Count = mCount;
return mPciSegments;
}
UplSegmentInfo = Get_UPLSegInfo ();
if (UplSegmentInfo == NULL) {
RetrieveSegmentInfoFromHob (Count);
} else {
PciRootBridgeInfo = Get_RBInfo ();
if (PciRootBridgeInfo == NULL) {
return 0;
}
RetrieveMultiSegmentInfoFromHob (PciRootBridgeInfo, UplSegmentInfo, Count);
}
mCount = *Count;
return mPciSegments;
}