Newer
Older
AMI-Aptio-BIOS-Reversed / AmiModulePkg / PCI / PciHostBridge / PciRootBridge.c
/**
 * 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;
}