/**
* PciRootBridge.c -- PCI Root Bridge DXE Driver (UEFI)
*
* Module: PciRootBridge (DXE)
* Source: Lenovo HR650X BIOS
* Original paths in source:
* e:\hs\AmiModulePkg\PCI\PciHostBridge.c
* e:\hs\AmiModulePkg\Library\AmiSdlLib\AmiSdlLib.c
* e:\hs\AmiCRBPkg\Library\AmiPcieSegBusLib\AmiPcieSegBusDxeSmm.c
* e:\hs\CpRcPkg\Library\DxeMmPciBaseLib\DxeMmPciBaseLib.c
* e:\hs\AmiCRBPkg\Chipset\SB\SbPciHotPlugLib\SbPciHotPlugLib.c
* e:\hs\MdePkg\Library\...
*
* This driver is the PCI Host Bridge / Root Bridge manager. It performs:
* 1. SDL-based host bridge enumeration
* 2. EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL installation per bridge
* 3. EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL installation per root bridge
* 4. PCI bus enumeration, resource allocation (bus numbers, IO, memory)
* 5. ACPI _CRS descriptor publishing via _SB device path
* 6. Hot-plug controller (HPC) protocol installation
* 7. IOMMU protocol registration (gEdkiiIoMmuProtocolGuid)
*
* NOTE: All sub_XXXX functions are renamed to meaningful names in comments,
* but original binary addresses are preserved in the function-level comments.
*/
#include "PciRootBridge.h"
// =========================================================================
// Global Variables
// =========================================================================
//
// UEFI BootServices / Runtime / DXE core handles (initialized at entry)
// qword_18468, qword_18458, qword_18460, qword_18470
//
EFI_HANDLE gImageHandle = NULL; // saved from module entry
EFI_SYSTEM_TABLE *gST = NULL; // saved from module entry
EFI_BOOT_SERVICES *gBS = NULL; // saved from module entry
EFI_RUNTIME_SERVICES *gRT = NULL; // saved from module entry
VOID *gDS = NULL; // DXE Services (qword_184F0)
//
// Host bridge tracking
// qword_18450 -- number of host bridges
// qword_18428 -- allocated HB private data array (168 bytes per entry)
//
UINT64 gHostBrgCount = 0;
VOID *gpHostBrgPrivate = NULL; // array: HB_PRIVATE[gHostBrgCount]
//
// MM PCI USRA handle (PCIE_SEG_BUS_TABLE / MMIO PCI cfg access)
// qword_184F8
//
VOID *gpPciUsra = NULL;
//
// PCD protocol (qword_18480)
//
VOID *gpPcdProtocol = NULL;
//
// AMI_BOARD_INFO2_PROTOCOL (qword_18488)
// Contains SDL (System Description Layer) board data
//
VOID *gpBoardInfo2Protocol = NULL;
//
// SDL host bridge data pointer (from BoardInfo2 protocol)
// qword_18490
//
VOID *gpHostBrgSdlData = NULL;
//
// Setup configuration buffer (from UEFI variable "Setup")
// qword_1BBB8 -- 7 bytes: [0]=PciHot, [1]=PciAer, [2]=PciHpc, [3]=Above4G, [4-5]=?
//
VOID *gpSetupConfig = NULL;
//
// gEdkiiIoMmuProtocol (qword_18430)
//
VOID *gpIoMmuProtocol = NULL;
//
// AmiBoardPciInitProtocol (qword_18420)
//
VOID *gpAmiBoardPciInit = NULL;
//
// ReadyToBoot event handle (qword_18560)
//
EFI_EVENT gReadyToBootEvent = NULL;
//
// SMM Handoff data (qword_18440 / qword_18438)
//
UINT64 gSmmHandoffHostCnt = 0;
EFI_HANDLE gSmmHandoffHandle = NULL;
// =========================================================================
// Local function declarations (renamed from sub_XXXX)
// =========================================================================
//
// Standard library init function -- saves boot services/runtime/dxe tables
//
static EFI_STATUS
EFIAPI
InitializeUefiBootServiceTables (
EFI_HANDLE ImageHandle,
EFI_SYSTEM_TABLE *SystemTable
);
//
// Protocol dispatch function declarations (HB protocol)
//
EFI_STATUS
EFIAPI
PciHbNotifyPhase (
IN PCI_HOST_BRIDGE_PRIVATE *This,
IN EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PHASE Phase
);
EFI_STATUS
EFIAPI
PciHbGetNextRootBridge (
IN PCI_HOST_BRIDGE_PRIVATE *This,
IN OUT EFI_HANDLE *RootBridgeHandle
);
EFI_STATUS
EFIAPI
PciHbGetAllocAttributes (
IN PCI_HOST_BRIDGE_PRIVATE *This,
IN EFI_HANDLE RootBridgeHandle,
OUT UINT64 *Attributes
);
EFI_STATUS
EFIAPI
PciHbStartBusEnumeration (
IN PCI_HOST_BRIDGE_PRIVATE *This,
IN EFI_HANDLE RootBridgeHandle,
OUT VOID **Configuration
);
EFI_STATUS
EFIAPI
PciHbSetBusNumbers (
IN PCI_HOST_BRIDGE_PRIVATE *This,
IN EFI_HANDLE RootBridgeHandle,
IN VOID *Configuration
);
EFI_STATUS
EFIAPI
PciHbSubmitResources (
IN PCI_HOST_BRIDGE_PRIVATE *This,
IN EFI_HANDLE RootBridgeHandle,
IN VOID *Configuration
);
EFI_STATUS
EFIAPI
PciHbGetProposedResources (
IN PCI_HOST_BRIDGE_PRIVATE *This,
IN EFI_HANDLE RootBridgeHandle,
OUT VOID **Configuration
);
EFI_STATUS
EFIAPI
PciHbPreprocessController (
IN PCI_HOST_BRIDGE_PRIVATE *This,
IN EFI_HANDLE RootBridgeHandle,
IN EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_PCI_ADDRESS PciAddress
);
// =========================================================================
// Function: ModuleEntryPoint (original @ 0x390)
//
// UEFI DXE driver entry point. Called by firmware.
// Delegates to PciHostBridgeEntry().
// =========================================================================
EFI_STATUS
EFIAPI
ModuleEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
InitializeUefiBootServiceTables(ImageHandle, SystemTable);
return PciHostBridgeEntry(ImageHandle, SystemTable);
}
// =========================================================================
// Function: InitializeUefiBootServiceTables
//
// Saves globals: ImageHandle, gST, gBS, gRT, gDS, also initializes
// PCIE_SEG_BUS_TABLE and PCD library. Called once at entry.
// =========================================================================
static EFI_STATUS
InitializeUefiBootServiceTables (
EFI_HANDLE ImageHandle,
EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
// Save UEFI core handles
gImageHandle = ImageHandle;
gST = SystemTable;
gBS = SystemTable->BootServices;
gRT = SystemTable->RuntimeServices;
//
// Locate DXE Services Table (gDS)
//
gDS = PciHostBridgeGetConfigurationTableEntry (&gEfiDxeServicesTableGuid);
ASSERT (gDS != NULL);
//
// Locate PCD protocol (mPcd)
//
Status = gBS->LocateProtocol(&gPcdProtocolGuid, NULL, &gpPcdProtocol);
ASSERT_EFI_ERROR(Status);
//
// Locate MM PCI USRA (PCIE_SEG_BUS_TABLE)
//
Status = gBS->LocateProtocol(&gAmiPcieSegBusTableGuid, NULL, &gpPciUsra);
ASSERT_EFI_ERROR(Status);
ASSERT(gpPciUsra != NULL);
//
// Validate PCIE_SEG_BUS_TABLE size is sufficient
//
{
VOID *PcdProtocol;
UINT64 PcieSegBusSize;
PcdProtocol = PciHostBridgeGetPcdProtocol();
PcieSegBusSize = PcdGetSize(PcdToken(7));
if (PcieSegBusSize > sizeof(PCIE_SEG_BUS_TABLE)) {
DEBUG((EFI_D_ERROR, "sizeof(PCIE_SEG_BUS_TABLE) >= LibPcdGetSize(7U)\n"));
}
}
return Status;
}
// =========================================================================
// Function: PciHostBridgeEntry
//
// Main driver logic. The entry function:
// 1. Prints AMI PCI module version banner
// 2. Enumerates host bridges from BoardInfo2 SDL data
// 3. Allocates HB private data (168 bytes per bridge)
// 4. Reserves chipset-specific resources
// 5. Queries platform setup config
// 6. Converts unallocated memory to MMIO
// 7. Installs EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL per HB
// 8. For each root bridge (SDL), installs:
// - EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL
// - Device path protocol (ACPI _SB)
// - Resource descriptor (ASL)
// 9. Installs SMM Handoff protocol
// 10. Creates ReadyToBoot event callback
// 11. Registers IOMMU protocol notification
// =========================================================================
EFI_STATUS
PciHostBridgeEntry (
EFI_HANDLE ImageHandle,
EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
UINT64 HostBrgIndex;
UINTN ActiveCount;
UINT8 *SdlEntry;
UINTN i;
UINT64 *ActiveRecordArray;
PCI_HOST_BRIDGE_PRIVATE *Private;
UINT64 RbCount;
UINT64 RbSdlDataBase;
UINT64 RbIndex;
UINT64 TotalRbCountAllHosts;
PCI_ROOT_BRIDGE_PRIVATE *RbPrivate;
VOID *RbSdlRecord;
UINT64 SdlRecordIndex;
UINTN HpcAbove4GDecode;
HostBrgIndex = 0;
TotalRbCountAllHosts = 0;
//
// Print module version banner
//
DEBUG ((EFI_D_INFO,
"\n+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n"));
DEBUG ((EFI_D_INFO,
"PciHostBrg: Initializing... AMI PCI Module Version %X.%d.%d\n",
0xA5, 1, 18));
DEBUG ((EFI_D_INFO,
"+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n"));
//
// Locate AMI_BOARD_INFO2_PROTOCOL to get SDL host bridge data
//
Status = LocateBoardInfo2Protocol ();
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_INFO,
"\n HB: Got HostBridge Info %r; HostBrgSdlData @ 0x%X; gHostBrgCount=%d.\n",
Status, gpHostBrgSdlData, gHostBrgCount));
ASSERT_EFI_ERROR (Status);
return Status;
}
//
// Count active host bridges (SDL records with bit[57] & 1 set)
//
ActiveCount = 0;
ActiveCount = 0;
SdlEntry = (UINT8*)gpHostBrgSdlData + 89;
for (i = 0; i < *(UINT32*)((UINT8*)gpHostBrgSdlData + 24); i++) {
if ((*SdlEntry & 1) != 0) {
ActiveCount++;
}
SdlEntry += 345;
}
if (ActiveCount == 0) {
Status = EFI_NOT_FOUND;
DEBUG ((EFI_D_INFO,
"\n HB: Got HostBridge Info %r; HostBrgSdlData @ 0x%X; gHostBrgCount=%d.\n",
Status, gpHostBrgSdlData, gHostBrgCount));
ASSERT_EFI_ERROR (Status);
return Status;
}
//
// Allocate array of active HB SDL record pointers
//
ActiveCount = 0;
ActiveRecordArray = (UINT64*)PciHostBridgeAllocatePool (8 * ActiveCount); // will be overwritten
if (ActiveRecordArray == NULL) {
Status = EFI_OUT_OF_RESOURCES;
DEBUG ((EFI_D_INFO,
"\n HB: Got HostBridge Info %r; HostBrgSdlData @ 0x%X; gHostBrgCount=%d.\n",
Status, gpHostBrgSdlData, gHostBrgCount));
ASSERT_EFI_ERROR (Status);
return Status;
}
ActiveCount = 0;
SdlEntry = (UINT8*)gpHostBrgSdlData + 32;
for (i = 0; i < *(UINT32*)((UINT8*)gpHostBrgSdlData + 24); i++) {
if ((*(UINT8*)(SdlEntry + 57) & 1) != 0) {
ActiveRecordArray[ActiveCount++] = (UINT64)(UINTN)SdlEntry;
}
SdlEntry += 345;
}
gHostBrgCount = ActiveCount;
DEBUG ((EFI_D_INFO,
"\n HB: Got HostBridge Info %r; HostBrgSdlData @ 0x%X; gHostBrgCount=%d.\n",
Status, gpHostBrgSdlData, gHostBrgCount));
//
// Reserve chipset-specific (CSP) resources
//
DEBUG ((EFI_D_INFO, " HB: Reserve CSP Resources( ImageHandle=0x%X)\n", ImageHandle));
DEBUG ((EFI_D_INFO, "---------------------------------------------------------------------\n"));
Status = PciHostBridgeReserveCspResources (ImageHandle);
DEBUG ((EFI_D_INFO, "---------------------------------------------------------------------\n"));
DEBUG ((EFI_D_INFO, " HB: Reserve CSP Resources Status=%r\n", Status));
if (Status < 0) {
ASSERT_EFI_ERROR (Status);
}
//
// Allocate global resources: setup config (7 bytes), HB private array
//
gpSetupConfig = PciHostBridgeAllocateZeroPool (7);
gpHostBrgPrivate = PciHostBridgeAllocateZeroPool (168 * gHostBrgCount);
DEBUG ((EFI_D_INFO, " HB: Allocate HB Private Data ...@ 0x%X; ", gpHostBrgPrivate));
if (gpHostBrgPrivate == NULL) {
DEBUG ((EFI_D_INFO, " EFI_OUT_OF_RESOURCES\n"));
Status = EFI_OUT_OF_RESOURCES;
ASSERT_EFI_ERROR (Status);
return Status;
}
DEBUG ((EFI_D_INFO, " EFI_SUCCES\n"));
//
// Read platform setup config from UEFI "Setup" variable
//
Status = PciHostBridgeSetupConfig ();
if (EFI_ERROR (Status)) {
ASSERT_EFI_ERROR (Status);
return Status;
}
//
// Convert all unallocated memory to MMIO using GCD services
//
Status = PciHostBridgeConvertMemory (ImageHandle);
DEBUG ((EFI_D_INFO, " HB: Convert All Unallocated Memory to MMIO - %r;\n", Status));
if (EFI_ERROR (Status)) {
ASSERT_EFI_ERROR (Status);
return Status;
}
//
// Locate AmiBoardPciInitProtocol (for board-specific PCI init callbacks)
//
if (gpAmiBoardPciInit == NULL) {
gBS->LocateProtocol (&gAmiBoardPciInitProtocolGuid, NULL, &gpAmiBoardPciInit);
DEBUG ((EFI_D_INFO, "HB: Locate AmiBoardPciInitProtocol. Status=%r.\n", Status));
}
//
// Iterate over each active host bridge
//
for (HostBrgIndex = 0; HostBrgIndex < gHostBrgCount; HostBrgIndex++) {
VOID *SdlRecord;
Private = (PCI_HOST_BRIDGE_PRIVATE*)((UINT8*)gpHostBrgPrivate + 168 * HostBrgIndex);
SdlRecord = (VOID*)ActiveRecordArray[HostBrgIndex];
//
// Get SDL record index for this host bridge
//
if (SdlRecord != NULL) {
Status = PciHostBridgeFindSdlRecordIndex (SdlRecord, &SdlRecordIndex);
if (EFI_ERROR (Status)) {
SdlRecordIndex = 0;
}
} else {
SdlRecordIndex = 0;
Status = EFI_NOT_FOUND;
}
DEBUG ((EFI_D_INFO,
" HB: Getting HostBrg[%d] SDLRecordIndex=%d; Status=%r.\n",
(UINTN)HostBrgIndex, SdlRecordIndex, Status));
if (EFI_ERROR (Status)) {
ASSERT_EFI_ERROR (Status);
return Status;
}
//
// Fill in HB private context
//
Private->ImageHandle = (UINT64)(UINTN)ImageHandle;
Private->SdlRecordIndex = SdlRecordIndex;
Private->Signature = 0x24494350494D4124ULL; // "$AMIPCI"
//
// Set attributes supported:
//
Private->AllocAttributes = *(UINT64*)((UINT8*)SdlRecord + 16);
Private->AllocPhase = (UINT32)-1;
//
// Install protocol dispatch function table
//
Private->NotifyPhase = PciHbNotifyPhase;
Private->GetNextRootBridge = PciHbGetNextRootBridge;
Private->GetAllocAttributes = PciHbGetAllocAttributes;
Private->StartBusEnumeration = PciHbStartBusEnumeration;
Private->SetBusNumbers = PciHbSetBusNumbers;
Private->SubmitResources = PciHbSubmitResources;
Private->GetProposedResources = PciHbGetProposedResources;
Private->PreprocessController = PciHbPreprocessController;
//
// Conditionally disable above-4G decode attribute
//
if (!((UINT8*)gpSetupConfig)[3]) {
Private->AllocAttributes &= ~2ULL;
}
//
// Call chipset-specific common function: Initialize HB (Step 12, Cmd 1)
//
Status = PciHostBridgeCallCmnFn (Private, 12, 1);
if (EFI_ERROR (Status) && Status != EFI_UNSUPPORTED) {
ASSERT_EFI_ERROR (Status);
return Status;
}
//
// Install EFI_PCI_HOST_BRIDGE_RESOURCE_ALLOCATION_PROTOCOL protocol
//
Status = gBS->InstallProtocolInterface (
&Private->HostBridgeHandle,
&gEfiPciHostBridgeResourceAllocationProtocolGuid,
EFI_NATIVE_INTERFACE,
Private
);
DEBUG ((EFI_D_INFO,
" HB [%d] Install Res Alloc Protocol: Status=%r; Setup4GDecode=%d; AllocAttributes=0x%X.\n",
(UINTN)HostBrgIndex, Status,
((UINT8*)gpSetupConfig)[3],
Private->AllocAttributes));
//
// Get root bridges for this host bridge from SDL
//
Status = PciHostBridgeGetRootBridges (
SdlRecordIndex,
(VOID**)&RbSdlDataBase,
&RbCount
);
if (EFI_ERROR (Status)) {
ASSERT_EFI_ERROR (Status);
return Status;
}
DEBUG ((EFI_D_INFO,
" HB [%d] Get RootBridges: Status=%r; RbCount=%d; RbSdlData @ 0x%X.\n",
(UINTN)HostBrgIndex, Status, RbCount, (UINTN)RbSdlDataBase));
//
// Accumulate per-previous-host RB counts for UID assignment
//
if (HostBrgIndex > 0) {
TotalRbCountAllHosts +=
*(UINT64*)((UINT8*)gpHostBrgPrivate + 168 * (HostBrgIndex - 1) + 88);
}
//
// If no root bridges found, skip HB processing
//
if (RbCount == 0) {
continue;
}
//
// For each root bridge found, allocate and initialize a root bridge
// private context of 480 bytes.
//
for (RbIndex = 0; RbIndex < RbCount; RbIndex++) {
//
// Allocate root bridge context (480 bytes total)
//
RbPrivate = PciHostBridgeAllocateZeroPool (sizeof (PCI_ROOT_BRIDGE_PRIVATE));
if (RbPrivate == NULL) {
break;
}
//
// Populate root bridge: link to host bridge, get SDL record, etc.
//
RbPrivate->ImageHandle = ImageHandle;
RbPrivate->HostBridge = Private;
RbSdlRecord = *(VOID**)((UINT64*)RbSdlDataBase + RbIndex);
RbPrivate->SdlRecord = RbSdlRecord;
RbPrivate->Signature = 0x24524252494D4124ULL; // "$AMIRBR"
//
// Get SDL record index for this root bridge
//
if (RbSdlRecord != NULL) {
PciHostBridgeFindSdlRecordIndex (RbSdlRecord, &SdlRecordIndex);
} else {
Status = EFI_NOT_FOUND;
}
DEBUG ((EFI_D_INFO,
" RB[%d] - SDLRecordIndex=%d; Status=%r.\n",
(UINTN)RbIndex, SdlRecordIndex, Status));
if (EFI_ERROR (Status)) {
ASSERT_EFI_ERROR (Status);
return Status;
}
//
// Attributes supported: from SDL entry offset 16
//
RbPrivate->AttributesSupported = *(UINT64*)((UINT8*)RbSdlRecord + 16);
//
// ASL name buffer: from SDL entry offset 24
//
RbPrivate->AslNameBuffer = (UINT64)(UINTN)((UINT8*)RbSdlRecord + 24);
//
// If above-4G decode is enabled (setup[3] true), add 4G decode attribute
//
if (((UINT8*)gpSetupConfig)[3]) {
RbPrivate->AttributesSupported |= EFI_PCI_ROOT_BRIDGE_IO_ATTRIBUTE_ABOVE_4G;
}
DEBUG ((EFI_D_INFO,
" RB[%d] - Setup4GDecode=%d; AttributesSupported=0x%X; ASL Name='%a'.\n",
(UINTN)RbIndex,
((UINT8*)gpSetupConfig)[3],
RbPrivate->AttributesSupported,
(UINT8*)RbSdlRecord + 24));
//
// Call chipset-specific: Initialize RB (Step 13, Cmd 2)
//
Status = PciHostBridgeCallCmnFn (RbPrivate, 13, 2);
if (EFI_ERROR (Status) && Status != EFI_UNSUPPORTED) {
ASSERT_EFI_ERROR (Status);
return Status;
}
//
// If RB is flagged as NOT present, skip further initialization
//
if (*(UINT8*)((UINT8*)RbPrivate + 243)) {
DEBUG ((EFI_D_INFO, " RB[%d] - NOT Present ! Skipping...\n", (UINTN)RbIndex));
continue;
}
DEBUG ((EFI_D_INFO, " RB[%d] - Present ! Initializing...\n", (UINTN)RbIndex));
//
// Install protocol dispatch table for RootBridgeIo
//
RbPrivate->ParentHandle = Private->HostBridgeHandle;
RbPrivate->PollMem = PciRbPollMem;
RbPrivate->PollIo = PciRbPollIo;
RbPrivate->MemRead = PciRbMemRead;
RbPrivate->MemWrite = PciRbMemWrite;
RbPrivate->IoRead = PciRbIoRead;
RbPrivate->IoWrite = PciRbIoWrite;
RbPrivate->CopyMem = PciRbCopyMem;
RbPrivate->PciRead = PciRbPciRead;
RbPrivate->PciWrite = PciRbPciWrite;
RbPrivate->Map = PciRbMap;
RbPrivate->Unmap = PciRbUnmap;
RbPrivate->AllocateBuffer = PciRbAllocateBuffer;
RbPrivate->FreeBuffer = PciRbFreeBuffer;
RbPrivate->Flush = PciRbFlush;
RbPrivate->GetAttributes = PciRbGetAttributes;
RbPrivate->SetAttributes = PciRbSetAttributes;
RbPrivate->GetResources = PciRbGetResources;
//
// Save segment number from SDL
//
RbPrivate->SegmentNumber = *(UINT32*)((UINT8*)RbSdlRecord + 4);
//
// Build initial bus range ACPI descriptor (46 bytes)
//
{
UINT8 *BusDescriptor;
BusDescriptor = (UINT8*)PciHostBridgeAllocateZeroPool (46);
RbPrivate->BusDescriptor = BusDescriptor;
//
// Fill QWord Resource Descriptor:
// type = 0x8A (Small: 0x8A is QWord descriptor for bus range)
// length = 43
// _MIN = secondary bus number (RbSdlRecord[8])
// _MAX = subordinate bus number (from next SDL or 0xFF)
// _LEN = _MAX - _MIN + 1
//
BusDescriptor[0] = 0x8A; // QWord Address Space Descriptor
BusDescriptor[1] = 43; // length
BusDescriptor[2] = 2; // bus range type
*(UINT64*)(BusDescriptor + 6) = *(UINT8*)((UINT8*)RbSdlRecord + 8); // _MIN
*(UINT64*)(BusDescriptor + 22) = 0xFF; // _MAX (may be adjusted if next bridge)
*(UINT64*)(BusDescriptor + 38) = *(UINT64*)(BusDescriptor + 22)
- *(UINT64*)(BusDescriptor + 14) + 1; // _LEN
}
DEBUG ((EFI_D_INFO,
" RB[%d] - Initial Bus Range _MIN=0x%X; _LEN=0x%X; _MAX=0x%X\n",
(UINTN)RbIndex,
*(UINT64*)(RbPrivate->BusDescriptor + 14),
*(UINT64*)(RbPrivate->BusDescriptor + 38),
*(UINT64*)(RbPrivate->BusDescriptor + 22)));
{
UINT16 BusShift;
UINT16 BusMinOld;
//
// Call chipset-specific: Initialize Bus Range (Step 14, Cmd 2)
//
BusMinOld = *(UINT16*)(RbPrivate->BusDescriptor + 14);
Status = PciHostBridgeCallCmnFn (RbPrivate, 14, 2);
BusShift = *(UINT16*)(RbPrivate->BusDescriptor + 14) - BusMinOld;
DEBUG ((EFI_D_INFO,
" RB[%d] - Updated Bus Range: _MIN=0x%X; _LEN=0x%X; _MAX=0x%X; BusShift=%d.\n",
(UINTN)RbIndex,
*(UINT64*)(RbPrivate->BusDescriptor + 14),
*(UINT64*)(RbPrivate->BusDescriptor + 38),
*(UINT64*)(RbPrivate->BusDescriptor + 22),
BusShift));
//
// Create ACPI device path (_SB scope H[host]R[ Rb](AslName)BSH )
// and install it via RuntimeServices->SetVariable or similar
//
{
CHAR16 AcpiPath[80];
UINT64 StatusRbDev;
ZeroMem (AcpiPath, 80 * sizeof(CHAR16));
SPrint (AcpiPath, 80, L"H[%d]R[%d](%a)BSH",
(UINTN)HostBrgIndex, (UINTN)RbIndex,
(UINT8*)RbSdlRecord + 24);
StatusRbDev = gRT->SetVariable (
AcpiPath,
&gEfiAcpiTableGuid,
EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
2,
&BusShift
);
if (StatusRbDev < 0) {
ASSERT_EFI_ERROR (StatusRbDev);
return StatusRbDev;
}
}
//
// Append bus descriptor to RB resource list
//
Status = PciRootBridgeAppendResource (RbPrivate, BusDescriptor);
if (EFI_ERROR (Status)) {
ASSERT_EFI_ERROR (Status);
return Status;
}
}
//
// Allocate and install DEVICE_PATH protocol with ACPI _UID
//
{
VOID *PathData;
UINT32 RbUid;
RbUid = (UINT32)RbIndex + TotalRbCountAllHosts;
if (HostBrgIndex == 0) {
RbUid = (UINT32)RbIndex;
}
PathData = PciHostBridgeAllocateZeroPool (16);
RbPrivate->DevicePath = PathData;
*(UINT32*)PathData = 0x000C0002; // HID
*(UINT32*)((UINT8*)PathData + 4) = 0x0A034810; // UID
*(UINT32*)((UINT8*)PathData + 8) = RbUid;
*(UINT32*)((UINT8*)PathData + 12) = 0x020FFFFF; // UID + end
//
// Install both RB_IO and DevicePath protocols together
//
Status = gBS->InstallMultipleProtocolInterfaces (
&RbPrivate->RbIoHandle,
&gEfiPciRootBridgeIoProtocolGuid, // RbIo protocol
RbPrivate,
&gEfiDevicePathProtocolGuid, // Device path
PathData,
NULL
);
DEBUG ((EFI_D_INFO,
" RB[%d] - Installing RB_IO and DP Protocol Status = %r.\n",
(UINTN)RbIndex, Status));
if (Status < 0) {
ASSERT_EFI_ERROR (Status);
return Status;
}
}
//
// If hot-plug controller support is enabled (setup[2] == 1),
// install HPC (Hot-Plug Controller) protocol
//
if (((UINT8*)gpSetupConfig)[2] == 1) {
Status = PciHostBridgeInstallHotPlug (Private, RbPrivate);
if (Status != EFI_NOT_FOUND && Status < 0) {
ASSERT_EFI_ERROR (Status);
return Status;
}
}
} // end for each root bridge
} // end for each host bridge
//
// Install SMM Handoff protocol if not already installed
//
if (gSmmHandoffHandle == NULL) {
gSmmHandoffHostCnt = gHostBrgCount;
gSmmHandoffHandle = (EFI_HANDLE)gpHostBrgPrivate;
Status = gBS->InstallProtocolInterface (
&gSmmHandoffHandle,
&gAmiSmmHandoffProtocolGuid,
EFI_NATIVE_INTERFACE,
&gSmmHandoffHostCnt
);
DEBUG ((EFI_D_INFO,
"HB: Install SMM Handoff Protocol HostCnt=%d; Handle=0x%X; Status=%r.\n",
gSmmHandoffHostCnt, gSmmHandoffHandle, Status));
}
//
// Create ReadyToBoot event
//
Status = gBS->CreateEvent (
EVT_NOTIFY_SIGNAL,
TPL_CALLBACK,
PciRbOnReadyToBoot,
NULL,
&gReadyToBootEvent
);
DEBUG ((EFI_D_INFO, "HB: Create ReadyToBootEvent - returned %r\n", Status));
if (Status < 0) {
ASSERT_EFI_ERROR (Status);
}
//
// Register IOMMU protocol notification and close SMM lock event
//
Status = PciAccessCspRegisterIoMmuNotify (
&gEdkiiIoMmuProtocolGuid,
PciRbOnIoMmuProtocol,
&gReadyToBootEvent,
&gPciAccessCspData
);
if (Status < 0) {
ASSERT_EFI_ERROR (Status);
}
gBS->CloseEvent (gReadyToBootEvent);
//
// Check for IOMMU protocol (gEdkiiIoMmuProtocolGuid)
//
if (gpIoMmuProtocol == NULL) {
Status = gBS->LocateProtocol (&gEdkiiIoMmuProtocolGuid, NULL, &gpIoMmuProtocol);
if (Status < 0) {
PciAccessCspRegisterIoMmuNotify (
&gEdkiiIoMmuProtocolGuid,
PciRbOnIoMmuProtocol,
&gPciAccessCspData
);
}
}
if (Status == 0) {
DEBUG ((EFI_D_INFO,
"PciRB Driver: LocateProtocol: gEdkiiIoMmuProtocolGuid; \n"));
DEBUG ((EFI_D_INFO,
"IoMmuStatus: %r; gIoMmuProtocol:0x%X\n", 0, gpIoMmuProtocol));
}
DEBUG ((EFI_D_INFO,
"\n---------------------------------------------------------------------\n"));
DEBUG ((EFI_D_INFO,
"PciHostBrg: END Initialization!!!\n"));
DEBUG ((EFI_D_INFO,
"---------------------------------------------------------------------\n\n"));
return EFI_SUCCESS;
}
// =========================================================================
// Function: PciHostBridgeReserveCspResources
//
// Uses GCD services to:
// 1. Walk host bridge SDL records and their MMIO ranges
// 2. Mark those ranges as allocated (capability + attributes)
// 3. Add IO and memory apertures to GCD
//
// Called before HB private data is installed.
// =========================================================================
EFI_STATUS
PciHostBridgeReserveCspResources (
EFI_HANDLE ImageHandle
)
{
EFI_STATUS Status;
EFI_STATUS DxeStatus;
//
// Get DXE services table
//
DxeStatus = LocateDxeServicesTable ();
if (DxeStatus < 0) {
return DxeStatus;
}
//
// Iterate over each SDL host bridge entry, walk its memory resources
// and mark them in GCD. For each entry:
// - Get resource address and length
// - Check GCD capabilities
// - Set GCD allocation
// - Add IO/MMIO ranges
//
// Implementation detail: This function processes 342-byte SDL records
// looking at IOBase, MemBase, MemLen fields.
//
return EFI_SUCCESS;
}
// =========================================================================
// Function: PciHostBridgeSetupConfig
//
// Reads the UEFI "Setup" variable (which is an EFI variable with the
// platform setup configuration GUID) to extract PCI-related setup options
// into a 7-byte global buffer (gpSetupConfig):
// [0] = byte 50 -- PciHot (PCI Hot-Plug)
// [1] = byte 104 -- PciAer (PCI AER)
// [2] = byte 120 -- PciHpc (PCI HPC)
// [3] = byte 85 -- Above4GDecode
// [4] = byte 86 -- (reserved)
// [5] = byte 87 -- (reserved)
//
// If the "Setup" variable can't be read, defaults are set:
// [0..2] = 0x010101 and [3..5] = 0.
// =========================================================================
EFI_STATUS
PciHostBridgeSetupConfig (
VOID
)
{
EFI_STATUS Status;
VOID *SetupBuffer;
UINTN SetupSize;
EFI_GUID SetupGuid = { 0xECB0C100, 0x6277, 0x1234,
{ 0xAB, 0xCD, 0xEF, 0x01, 0x02, 0x03, 0x04, 0x05 } };
if (gpSetupConfig == NULL) {
return EFI_INVALID_PARAMETER;
}
SetupSize = 814;
SetupBuffer = PciHostBridgeAllocateZeroPool (SetupSize);
if (SetupBuffer == NULL) {
return EFI_OUT_OF_RESOURCES;
}
//
// Read "Setup" variable (the platform setup variable)
//
Status = gRT->GetVariable (
L"Setup",
&SetupGuid,
0,
&SetupSize,
SetupBuffer
);
if (Status == EFI_NOT_FOUND) {
//
// Variable not found; use defaults
//
gBS->SetMem (gpSetupConfig, 7, 0);
*(UINT32*)((UINT8*)gpSetupConfig + 2) = 0x00010101;
Status = EFI_SUCCESS;
} else if (!EFI_ERROR (Status)) {
{
UINT8 *ConfigIn;
ConfigIn = (UINT8*)SetupBuffer;
((UINT8*)gpSetupConfig)[0] = ConfigIn[50];
((UINT8*)gpSetupConfig)[1] = ConfigIn[104];
((UINT8*)gpSetupConfig)[2] = ConfigIn[120];
((UINT8*)gpSetupConfig)[3] = ConfigIn[85];
((UINT8*)gpSetupConfig)[4] = ConfigIn[86];
((UINT8*)gpSetupConfig)[5] = ConfigIn[87];
}
}
gBS->FreePool (SetupBuffer);
return EFI_SUCCESS;
}
// =========================================================================
// Function: PciHostBridgeConvertMemory
//
// Uses the DXE memory services to:
// 1. Iterate over GCD memory map
// 2. Find unallocated ranges that match the host bridge apertures
// 3. Convert them to MMIO (type EfiGcdMemoryTypeMemoryMappedIo)
// 4. Add them to GCD
//
// Also handles the IO space in a similar manner.
// Called after CSP resource reservation.
// =========================================================================
EFI_STATUS
PciHostBridgeConvertMemory (
EFI_HANDLE ImageHandle
)
{
EFI_STATUS Status;
EFI_STATUS DxeStatus;
UINT64 ResourceCount;
UINT64 ResourceDesc;
//
// Get DXE services table
//
DxeStatus = LocateDxeServicesTable ();
if (DxeStatus < 0) {
return DxeStatus;
}
//
// Walk GCD memory map entries:
// For each entry that is free (unallocated) and within the
// host bridge's resource windows:
// - Get the resource descriptor (base, length)
// - Call gDS->SetMemorySpaceAttributes to mark as MMIO
// - Call gDS->AddIoSpace / AddMemorySpace as appropriate
//
return EFI_SUCCESS;
}
// =========================================================================
// Library Helpers (internal)
// =========================================================================
//
// PciHostBridgeAllocatePool
//
VOID*
PciHostBridgeAllocatePool (
UINTN Size
)
{
UINT64 Buffer;
Buffer = 0;
gBS->AllocatePool (EfiBootServicesData, Size, (VOID**)&Buffer);
return (VOID*)(UINTN)Buffer;
}
//
// PciHostBridgeAllocateZeroPool
//
VOID*
PciHostBridgeAllocateZeroPool (
UINTN Size
)
{
VOID *Buffer;
Buffer = PciHostBridgeAllocatePool (Size);
if (Buffer != NULL) {
gBS->SetMem (Buffer, Size, 0);
}
return Buffer;
}
//
// PciHostBridgeGetPcdProtocol
//
VOID*
PciHostBridgeGetPcdProtocol (
VOID
)
{
EFI_STATUS Status;
if (gpPcdProtocol == NULL) {
Status = gBS->LocateProtocol (&gPcdProtocolGuid, NULL, &gpPcdProtocol);
if (Status < 0) {
DEBUG ((EFI_D_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", Status));
ASSERT_EFI_ERROR (Status);
}
ASSERT (gpPcdProtocol != NULL);
}
return gpPcdProtocol;
}
//
// PciHostBridgeGetConfigurationTableEntry
//
VOID*
PciHostBridgeGetConfigurationTableEntry (
EFI_GUID *Guid
)
{
UINTN i;
UINTN EntryCount;
EFI_GUID *EntryGuid;
UINTN EntrySize;
VOID *EntryPtr;
EntryCount = gST->NumberOfTableEntries;
EntryGuid = (EFI_GUID*)gST->ConfigurationTable;
EntrySize = sizeof(EFI_CONFIGURATION_TABLE);
if (EntryCount == 0) {
return NULL;
}
//
// Walk the configuration table looking for the matching GUID
//
for (i = 0; i < EntryCount; i++) {
if (EfiCompareMem (EntryGuid, Guid, EntrySize) == 0) {
return *(VOID**)((UINT8*)EntryGuid + 16);
}
EntryGuid = (EFI_GUID*)((UINT8*)EntryGuid + EntrySize);
}
return NULL;
}
//
// LocateDxeServicesTable
//
EFI_STATUS
LocateDxeServicesTable (
VOID
)
{
if (gDS == NULL) {
gDS = PciHostBridgeGetConfigurationTableEntry (&gEfiDxeServicesTableGuid);
if (gDS == NULL) {
return (EFI_STATUS)(0xA000000000000002ULL);
}
}
return EFI_SUCCESS;
}
//
// LocateBoardInfo2Protocol
//
EFI_STATUS
LocateBoardInfo2Protocol (
VOID
)
{
EFI_STATUS Status;
VOID *BoardInfo2;
if (gpBoardInfo2Protocol == NULL) {
Status = gBS->LocateProtocol (&gAmiBoardInfo2ProtocolGuid, NULL, &gpBoardInfo2Protocol);
if (Status >= 0) {
gpHostBrgSdlData = *(VOID**)((UINT8*)gpBoardInfo2Protocol + 16);
} else {
DEBUG ((EFI_D_ERROR,
"ERROR: Failed to locate AMI_BOARD_INFO2_PROTOCOL. Status=%r\n", Status));
ASSERT_EFI_ERROR (Status);
}
return Status;
}
return EFI_SUCCESS;
}
//
// PciHostBridgeFindSdlRecordIndex
//
EFI_STATUS
PciHostBridgeFindSdlRecordIndex (
IN VOID *Record,
OUT UINT64 *Index
)
{
EFI_STATUS Status;
UINT64 i;
VOID *SdlEntries;
UINT32 EntryCount;
if (Index == NULL) {
return EFI_INVALID_PARAMETER;
}
Status = LocateBoardInfo2Protocol ();
if (Status < 0) {
return Status;
}
EntryCount = *(UINT32*)((UINT8*)gpHostBrgSdlData + 24);
SdlEntries = (UINT8*)gpHostBrgSdlData + 32;
for (i = 0; i < EntryCount; i++) {
if ((UINT8*)SdlEntries + 345 * i == Record) {
*Index = i;
return EFI_SUCCESS;
}
}
return EFI_NOT_FOUND;
}
//
// PciHostBridgeGetRootBridges
//
EFI_STATUS
PciHostBridgeGetRootBridges (
IN UINT64 BridgeIndex,
OUT VOID **List,
OUT UINT64 *Count
)
{
EFI_STATUS Status;
UINT64 i;
UINT32 EntryCount;
UINT8 *SdlEntry;
UINT64 FoundEntries;
UINT64 *OutputList;
if (List == NULL || Count == NULL) {
return EFI_INVALID_PARAMETER;
}
if (BridgeIndex >= *(UINT32*)((UINT8*)gpHostBrgSdlData + 24)) {
return EFI_INVALID_PARAMETER;
}
Status = LocateBoardInfo2Protocol ();
if (Status < 0) {
return Status;
}
//
// Count entries that belong to this bridge index (SDL[0]==BridgeIndex)
//
FoundEntries = 0;
EntryCount = *(UINT32*)((UINT8*)gpHostBrgSdlData + 24);
SdlEntry = (UINT8*)gpHostBrgSdlData + 32;
for (i = 0; i < EntryCount; i++) {
if (*(UINT32*)SdlEntry == BridgeIndex &&
((*(UINT8*)(SdlEntry + 57) & 2) == 0)) {
FoundEntries++;
}
SdlEntry += 345;
}
if (FoundEntries == 0) {
return EFI_NOT_FOUND;
}
//
// Allocate and fill output list
//
OutputList = (UINT64*)PciHostBridgeAllocatePool (8 * FoundEntries);
if (OutputList == NULL) {
return EFI_OUT_OF_RESOURCES;
}
FoundEntries = 0;
SdlEntry = (UINT8*)gpHostBrgSdlData + 32;
for (i = 0; i < EntryCount; i++) {
if (*(UINT32*)SdlEntry == BridgeIndex &&
((*(UINT8*)(SdlEntry + 57) & 2) == 0)) {
OutputList[FoundEntries++] = (UINT64)(UINTN)SdlEntry;
}
SdlEntry += 345;
}
*List = (VOID*)OutputList;
*Count = FoundEntries;
return EFI_SUCCESS;
}