Newer
Older
AMI-Aptio-BIOS-Reversed / AmiModulePkg / PCI / PciDxeInit / PciDxeInit.c
@Ajax Dong Ajax Dong 2 days ago 16 KB Full restructure
/** @file
  PciDxeInit - PCI DXE Initialization Driver

  This driver initializes the PCI subsystem during the DXE phase. It is part of
  the AMI ModulePkg for the Lenovo HR650X platform (Purley/Intel grantley-EP).

  Responsibilities:
    - Installs the AmiPciBusSetupOverride protocol with device-specific hooks
    - Manages the PCI bus override table (per-device config callbacks)
    - Handles VGA device enumeration and selection (onboard vs offboard)
    - Manages S3 boot script save/restore via SmmLockBox
    - Initializes PCIe segments and MMIO base
    - Provides RAID driver binding for onboard SATA/AHCI controllers
    - Restricts MMIO resources (e.g., USB under 4GB)
    - Port degradation for 10GbE BAR

  Build info: DEBUG_VS2015 X64, HR6N0XMLK
  Source path: AmiModulePkg\PCI\PciDxeInit

  Copyright (c) American Megatrends Inc. (AMI). All rights reserved.
**/

#include "PciDxeInit.h"

//
// ==========================================================================
// Global Data
// ==========================================================================
//

//
// EFI library globals
//
EFI_HANDLE               gImageHandle_Private = NULL;
EFI_SYSTEM_TABLE         *gSystemTable = NULL;
EFI_RUNTIME_SERVICES     *gRuntimeServices = NULL;

//
// PCIe MMIO base address (from PCI Express Base Address PCD)
//
UINT64                   gPciExpressBase = 0;

//
// Protocol notification events
//
EFI_EVENT                gEventAmiPciBusSetupOverride = NULL;
EFI_EVENT                gEventDxeSmmReadyToLock = NULL;
EFI_EVENT                gEventS3BootScriptSave = NULL;

//
// S3 boot script state
//
S3_BOOT_SCRIPT_CONTEXT   *gS3BootScriptContext = NULL;
S3_BOOT_SCRIPT_CONTEXT   *gS3BootScriptContextBackup = NULL;
BOOLEAN                  gBootScriptBufferAllocated = FALSE;
BOOLEAN                  gBootScriptLockedBySmm = FALSE;

//
// VGA state
//
BOOLEAN                  gAtleastOneVideoFound = FALSE;
UINTN                    gNumOffboardVideoPresent = 0;

//
// SMM LockBox communication
//
BOOLEAN                  gSmmLockBoxInitialized = FALSE;

//
// Setup variable support globals
//
BOOLEAN                  gSetupVariableExists = FALSE;

//
// ==========================================================================
// Debug Support
// ==========================================================================
//

/**
  Reads the current debug level from CMOS.

  @return Debug level mask.
**/
UINTN
EFIAPI
GetCurrentDebugLevel (
    VOID
    )
{
    UINT8   CmosData;

    //
    // Read CMOS index 0x4B (debug level register)
    //
    IoWrite8 (0x70, (IoRead8 (0x70) & 0x80) | 0x4B);
    CmosData = IoRead8 (0x71);

    if (CmosData > 3) {
        if (CmosData == 0) {
            CmosData = (MmioRead8 (0xFDAF0490) & 2) | 1;
        }
    }

    if ((CmosData - 1) > 0xFD) {
        return 0;
    }

    if (CmosData == 1) {
        return BIT31 | 4;
    }

    return (UINTN)-1;
}

//
// ==========================================================================
// PciDxeInit Entry Point
// ==========================================================================
//

/**
  Module entry point for PciDxeInit.

  @param  ImageHandle  The module's image handle.
  @param  SystemTable  The UEFI system table.

  @return EFI_SUCCESS or EFI error code.
**/
EFI_STATUS
EFIAPI
PciDxeInitEntryPoint (
    IN EFI_HANDLE        ImageHandle,
    IN EFI_SYSTEM_TABLE  *SystemTable
    )
{
    EFI_BOOT_SERVICES     *BootServices;
    EFI_RUNTIME_SERVICES  *RuntimeServices;
    EFI_STATUS            Status;
    EFI_HANDLE            Handle;

    //
    // Initialize the driver
    //
    PciDxeInitDriverInit (ImageHandle);

    //
    // Save EFI system table pointers (may already be set by lib constructor)
    //
    if (gSystemTable == NULL) {
        gSystemTable        = SystemTable;
        BootServices        = SystemTable->BootServices;
        RuntimeServices     = SystemTable->RuntimeServices;
        gBS                 = BootServices;
        gRT                 = RuntimeServices;
    }

    //
    // Install the AmiPciBusSetupOverride protocol
    //
    Handle = NULL;
    Status = gBS->InstallMultipleProtocolInterfaces (
                    &Handle,
                    &gAmiPciBusSetupOverrideProtocolGuid,
                    &gAmiPciBusSetupOverride,
                    NULL
                    );
    if (EFI_ERROR (Status)) {
        DEBUG ((EFI_D_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", Status));
        ASSERT_EFI_ERROR (FALSE);
        return Status;
    }

    return Status;
}

//
// ==========================================================================
// Driver Initialization
// ==========================================================================
//

/**
  Driver initialization routine.

  @param  ImageHandle  The module's image handle.

  @return EFI_STATUS.
**/
EFI_STATUS
PciDxeInitDriverInit (
    IN  EFI_HANDLE  ImageHandle
    )
{
    EFI_STATUS                  Status;
    UINT64                      PciExpressBaseAddress;
    UINTN                       SegBusTableSize;
    UINT8                       SetupByte;
    UINT16                      CmosFlags;
    BOOLEAN                     InterruptsEnabled;
    UINT64                      DelayStart;

    //
    // Save global handles
    //
    gImageHandle_Private = ImageHandle;

    if (gImageHandle_Private == NULL) {
        DEBUG ((EFI_D_ERROR, "gImageHandle != NULL\n"));
    }
    if (gST == NULL) {
        DEBUG ((EFI_D_ERROR, "gST != NULL\n"));
    }
    if (gBS == NULL) {
        DEBUG ((EFI_D_ERROR, "gBS != NULL\n"));
    }
    if (gRT == NULL) {
        DEBUG ((EFI_D_ERROR, "gRT != NULL\n"));
    }

    //
    // Get DxeServicesTable
    //
    Status = gBS->LocateProtocol (
                    &gEfiDxeServicesTableProtocolGuid,
                    NULL,
                    (VOID **)&gDS
                    );
    ASSERT_EFI_ERROR (Status);
    if (gDS == NULL) {
        DEBUG ((EFI_D_ERROR, "gDS != NULL\n"));
    }

    //
    // Get PCI Express base address PCD or locate MmPciBase protocol
    //
    gPciExpressBase = PcdGet64 (PcdPciExpressBaseAddress);

    if (gPciExpressBase == 0) {
        Status = gBS->LocateProtocol (
                        &gEfiMmPciBaseProtocolGuid,
                        NULL,
                        (VOID **)&gPciExpressBase
                        );
        if (Status == EFI_SUCCESS && gPciExpressBase == 0) {
            DEBUG ((EFI_D_ERROR, "mPciUsra != NULL\n"));
        }
    }

    //
    // Initialize HOB list
    //
    Status = EfiGetSystemConfigurationTable (
               &gEfiHobListGuid,
               (VOID **)&gHobList
               );
    ASSERT_EFI_ERROR (Status);
    if (gHobList == NULL) {
        DEBUG ((EFI_D_ERROR, "mHobList != NULL\n"));
    }

    //
    // Enable PCI config space access
    //
    CmosFlags = (UINT16)AsmReadEflags ();
    AsmEnableInterrupt ();

    //
    // Small delay using RDTSC (~10us)
    //
    DelayStart = AsmReadTsc () & 0xFFFFFF;
    AsmDisableInterrupt ();

    //
    // Write PCI configuration enable bit
    //
    IoWrite32 (0xCF8, 0x80000000);

    //
    // Wait for enable to stabilize
    //
    while ((((UINT32)DelayStart + 357 - (UINT32)AsmReadTsc ()) & 0x800000) == 0) {
        AsmPause ();
    }
    AsmEnableInterrupt ();

    //
    // Restore interrupt state
    //
    if ((CmosFlags & BIT9) != 0) {
        AsmEnableInterrupt ();
    } else {
        AsmDisableInterrupt ();
    }

    //
    // Initialize S3 boot script library
    //
    Status = S3BootScriptLibInitialize ();
    ASSERT_EFI_ERROR (Status);

    //
    // Get PCIe segment/bus table size from PCD
    //
    SegBusTableSize = PcdGetSize (PcdPcieSegBusTable);
    ASSERT (sizeof (PCIE_SEG_BUS_TABLE) >= SegBusTableSize);

    PciExpressBaseAddress = PcdGet64 (PcdPcieSegBusTable);

    //
    // Allocate and initialize segment/bus table
    //
    if (PciExpressBaseAddress != 0) {
        gPcieSegBusTable = AllocatePool (SegBusTableSize);
        if (gPcieSegBusTable != NULL) {
            CopyMem (
              gPcieSegBusTable,
              (VOID *)(UINTN)PciExpressBaseAddress,
              SegBusTableSize
              );
        }
    }

    //
    // Read PCI Bus Setup Override setup variable
    //
    Status = GetSetupVariableByIndex (260, 0, &SetupByte);
    if (EFI_ERROR (Status)) {
        SetupByte = 0xFE;
    }

    //
    // Create readiness notification event for PCI enum
    //
    Status = gBS->CreateEvent (
                    EVT_NOTIFY_SIGNAL,
                    TPL_CALLBACK,
                    (EFI_EVENT_NOTIFY)PciDevCommonHook,
                    NULL,
                    &gEventAmiPciBusSetupOverride
                    );
    if (EFI_ERROR (Status)) {
        DEBUG ((EFI_D_ERROR, "Event != NULL\n"));
    }

    return Status;
}

//
// ==========================================================================
// PCI Device Common Hook Dispatcher
// ==========================================================================
//

/**
  Dispatches hooks registered in the override table for a given device.

  @param  This        AmiPciBusSetupOverride protocol instance.
  @param  Context     Hook context (output).
  @param  OverrideTable Current override entry.

  @return EFI_STATUS.
**/
EFI_STATUS
EFIAPI
PciInitHookDispatcher (
    IN  VOID                   *PciDevicePrivateData,
    OUT VOID                   **HookContext,
    IN  PCI_BUS_OVERRIDE_ENTRY *OverrideTable
    )
{
    PCI_BUS_OVERRIDE_ENTRY    *Entry;
    PCI_DEVICE_CALLBACK        Callback;
    EFI_STATUS                 Status;
    EFI_STATUS                 ReturnStatus;
    UINTN                      Index;

    ReturnStatus = EFI_NOT_FOUND;
    Entry = gPciOverrideTable;

    if (Entry == NULL) {
        return EFI_NOT_FOUND;
    }

    if (OverrideTable == NULL) {
        return EFI_INVALID_PARAMETER;
    }

    if (*(UINT64 *)OverrideTable != PCI_BUS_OVERRIDE_TABLE_SIGNATURE) {
        return EFI_INVALID_PARAMETER;
    }

    for (Index = 0; ; Index += sizeof (PCI_BUS_OVERRIDE_ENTRY)) {
        Callback = (PCI_DEVICE_CALLBACK)(UINTN)Entry[Index].Callback;
        if (Callback == NULL) {
            break;
        }

        if (((*(UINT16 *)(PciDevicePrivateData + FIELD_OFFSET)) & Entry[Index].Match.VenMask) == Entry[Index].Match.VenId &&
            ((*(UINT16 *)(PciDevicePrivateData + FIELD_OFFSET + 2)) & Entry[Index].Match.DevMask) == Entry[Index].Match.DevId) {

            DEBUG ((
                EFI_D_INFO,
                "DevCmnHook: B%X|D%X|F%X (VID=0x%X; DID=0x%X; Mask(0x%X:0x%X) Step=%d",
                *(UINT8 *)(PciDevicePrivateData + 219),   // Bus
                *(UINT8 *)(PciDevicePrivateData + 218),   // Device
                *(UINT8 *)(PciDevicePrivateData + 217),   // Function
                Entry[Index].Match.VenId,
                Entry[Index].Match.DevId,
                Entry[Index].Match.VenMask,
                Entry[Index].Match.DevMask,
                Entry[Index].InitStep
                ));

            Status = Callback (PciDevicePrivateData, HookContext, OverrideTable);
            DEBUG ((EFI_D_INFO, ")=%r\n", Status));

            if (EFI_ERROR (Status) && Status != EFI_NOT_FOUND) {
                return Status;
            }

            if (!EFI_ERROR (Status)) {
                ReturnStatus &= ~EFI_ERROR_BIT;
            }
        }
    }

    return ReturnStatus;
}

//
// ==========================================================================
// S3 Boot Script Management
// ==========================================================================
//

/**
  Initialize the S3 boot script library.

  Allocates the boot script buffer and registers events for:
  - DXE SMM Ready To Lock (close boot script)
  - S3 ready (save boot script to lockbox)

  @return EFI_STATUS.
**/
EFI_STATUS
S3BootScriptLibInitialize (
    VOID
    )
{
    EFI_STATUS             Status;

    DEBUG ((EFI_D_INFO, "%a() in %a module\n", __FUNCTION__, "PciDxeInit"));

    if (gEventDxeSmmReadyToLock == NULL) {
        //
        // Allocate boot script buffer
        //
        gS3BootScriptContext = (S3_BOOT_SCRIPT_CONTEXT *)AllocatePages (EFI_SIZE_TO_PAGES (0x20));
        if (gS3BootScriptContext == NULL) {
            //
            // Fallback: allocate with AllocatePages
            //
            Status = gBS->AllocatePages (
                            AllocateAnyPages,
                            EfiBootServicesData,
                            1,
                            &gS3BootScriptContext
                            );
            ASSERT_EFI_ERROR (Status);

            gS3BootScriptContext = (S3_BOOT_SCRIPT_CONTEXT *)(UINTN)gS3BootScriptContext;
            gBootScriptBufferAllocated = TRUE;
        }

        if (gS3BootScriptContext != NULL) {
            ZeroMem (gS3BootScriptContext, sizeof (S3_BOOT_SCRIPT_CONTEXT));
        }

        //
        // Create ReadyToLock event
        //
        Status = gBS->CreateEvent (
                        EVT_NOTIFY_SIGNAL,
                        TPL_CALLBACK,
                        (EFI_EVENT_NOTIFY)S3BootScriptEntryClose,
                        NULL,
                        &gEventDxeSmmReadyToLock
                        );
        if (Status == EFI_SUCCESS) {
            DEBUG ((EFI_D_INFO, "mEventDxeSmmReadyToLock != NULL\n"));
        }

        //
        // Try to locate SMM Communication protocol
        //
        Status = gBS->LocateProtocol (
                        &gEfiSmmCommunicationProtocolGuid,
                        NULL,
                        (VOID **)&gSmmCommunication
                        );
        if (Status == EFI_SUCCESS) {
            Status = gSmmCommunication->Register (
                                        gSmmCommunication,
                                        (EFI_EVENT_NOTIFY)SmmLockBoxEntryClose,
                                        &gEventS3BootScriptSave
                                        );
            ASSERT_EFI_ERROR (Status);

            Status = gSmmCommunication->Register (
                                        gSmmCommunication,
                                        (EFI_EVENT_NOTIFY)SmmLockBoxEntryClose,
                                        &gEventS3BootScriptSave
                                        );
            ASSERT_EFI_ERROR (Status);

            Status = gSmmCommunication->Register (
                                        gSmmCommunication,
                                        (EFI_EVENT_NOTIFY)SmmLockBoxBackupEntry,
                                        &gEventS3BootScriptSave
                                        );
            ASSERT_EFI_ERROR (Status);
        }
    }

    if (gSmmCommunication != NULL && gS3BootScriptContextBackup == NULL) {
        //
        // Allocate backup buffer from SMM communication buffer
        //
        Status = gSmmCommunication->GetBuffer (
                                    gSmmCommunication,
                                    32,
                                    (VOID **)&gS3BootScriptContextBackup
                                    );
        if (EFI_ERROR (Status)) {
            Status = gSmmCommunication->AllocateBuffer (
                                        gSmmCommunication,
                                        32,
                                        (VOID **)&gS3BootScriptContextBackup
                                        );
            ASSERT_EFI_ERROR (Status);

            gSmmLockBoxInitialized = TRUE;
            ZeroMem (gS3BootScriptContextBackup, 0x20);
        }
    }

    return EFI_SUCCESS;
}

/**
  Deinitialize the S3 boot script library on ReadyToBoot.

  @return EFI_STATUS.
**/
EFI_STATUS
S3BootScriptLibDeinitialize (
    VOID
    )
{
    EFI_STATUS  Status;

    Status = gBS->CloseEvent ((EFI_EVENT)&gEventAmiPciBusSetupOverride);
    ASSERT_EFI_ERROR (Status);

    if (gBootScriptBufferAllocated) {
        Status = gBS->FreePages (
                        (EFI_PHYSICAL_ADDRESS)(UINTN)gS3BootScriptContext,
                        1
                        );
        ASSERT_EFI_ERROR (Status);

        Status = PcdSet64 (PcdPcieSegBusTable, 0);
        ASSERT_EFI_ERROR (Status);
    }

    if (gSmmCommunication != NULL && gSmmLockBoxInitialized) {
        Status = gSmmCommunication->FreeBuffer (
                                    gSmmCommunication,
                                    (VOID *)gS3BootScriptContextBackup
                                    );
        ASSERT_EFI_ERROR (Status);

        Status = PcdSet64 (PcdPcieSegBusTableAlt, 0);
        ASSERT_EFI_ERROR (Status);
    }

    return EFI_SUCCESS;
}