/**
* PchInitDxe.c - PCH Initialization DXE Driver
*
* Source: PchInitDxe.efi from HR650X Purley Platform (LBG PCH)
* Source Tree: PurleySktPkg/SouthClusterLbg/PchInit/Dxe/
*
* This driver initializes the PCH during DXE phase including:
* - PCH policy HOB consumption
* - USB preconditioning (XHCI warm reset)
* - PCIe Root Port (RP) ASPM/L1SS/ClkReq configuration
* - RST PCIe storage remapping
* - Uplink (x16/x8) port programming
* - S3 Boot Script initialization
* - PCH ACPI NVS protocol installation
* - NHLT (Non-HDAudio ACPI Table) publishing
* - TraceHub (MIPI) reveal
* - GPIO configuration via sideband interface
*/
#include "PchInitDxe.h"
//
// ---------------------------------------------------------------------------
// Global Variable Definitions
// ---------------------------------------------------------------------------
//
#pragma section(".data")
// UEFI core globals (set by PchInitDxeEntry)
static EFI_HANDLE gImageHandle = NULL;
static EFI_SYSTEM_TABLE *gSystemTable = NULL;
static EFI_BOOT_SERVICES *gBootServices = NULL;
static EFI_RUNTIME_SERVICES *gRuntimeServices = NULL;
static EFI_DXE_SERVICES *gDxeServicesTable = NULL;
static VOID *gPciUsraProtocol = NULL;
// PCD - PCIe Segment Bus Table
static VOID *gPcieSegBusTable = NULL;
static UINT8 gPcieSegBusBuffer[0x48]; // max PCD size
// PCH Policy HOB (from PEI phase)
static PCH_POLICY_HOB *mPchPolicyHob = NULL;
// HOB list pointer
static VOID *mHobList = NULL;
// PCH LPC bridge MMIO base
static UINTN mLpcMmioBase = 0;
// USB Precondition context
static USB_PRECONDITION_CONTEXT *mUsbContext = NULL;
// USB ACPI timer tracking
static BOOLEAN mUsbAcpiTimerDone = FALSE;
static UINT32 mUsbAcpiBaseCounter = 0;
// PCH NVS Area pointer
static PCH_NVS_AREA *mPchNvsArea = NULL;
// PCIe IO Trap address
static UINT16 mPcieIoTrapAddress = 0;
// S3 Boot Script globals
static BOOT_SCRIPT_ENTRY *mBootScriptTable = NULL;
static EFI_EVENT mDxeSmmReadyToLockEvent = NULL;
static EFI_SMM_BASE2_PROTOCOL *mSmmBase2 = NULL;
static VOID *mSmmBootScriptBuffer = NULL;
static BOOLEAN mBootScriptInited = FALSE;
static BOOLEAN mSmmBootScriptInited = FALSE;
static VOID *mSmmReadyToBootReg = NULL;
static VOID *mSmmCallbackReg1 = NULL;
static VOID *mSmmCallbackReg2 = NULL;
// L1SS memory pool allocator
static UINT32 mL1ssPoolAvailable = 0;
// L1SS configuration table
static UINT16 *mL1ssConfigTable = NULL;
// NHLT feature enable flags
static BOOLEAN mNhltDmic0 = FALSE;
static BOOLEAN mNhltDmic1 = FALSE;
static BOOLEAN mNhltSsp0 = FALSE;
static BOOLEAN mNhltSsp1 = FALSE;
static BOOLEAN mNhltSsp2 = FALSE;
static BOOLEAN mNhltBtRender = FALSE;
static BOOLEAN mNhltBtCapture = FALSE;
static BOOLEAN mNhltI2S = FALSE;
// Saved ImageHandle copy
static EFI_HANDLE mSavedImageHandle = NULL;
// ---------------------------------------------------------------------------
// Private Function Prototypes
// ---------------------------------------------------------------------------
static
EFI_STATUS
PchInitDxeEntry (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
);
static
EFI_STATUS
PchInitEntryPoint (
IN EFI_HANDLE ImageHandle
);
static
VOID
PchInitGlobalDataInit (
VOID
);
static
EFI_STATUS
PchInitS3BootScriptInit (
VOID
);
static
EFI_STATUS
PchS3BootScriptDeinit (
VOID
);
static
EFI_STATUS
PchInitS3BootScriptSave (
VOID
);
static
EFI_STATUS
PchRevealTraceHub (
VOID
);
static
VOID
PchUplinksInit (
VOID
);
static
EFI_STATUS
InitializePchDevice (
VOID
);
static
EFI_STATUS
PchUsbPrecondition (
VOID
);
static
BOOLEAN
EFIAPI
PchUsbPreconditionTimerCallback (
IN VOID *Context,
IN UINT8 Port
);
static
EFI_STATUS
ConfigurePmForRstRemapping (
IN UINT8 ControllerIndex,
IN UINT8 Enable
);
static
VOID
PchPcieRpEarlyConfig (
VOID
);
static
EFI_STATUS
PchConfigureRpfnMapping (
VOID
);
static
VOID
PchOnEndOfDxe (
VOID
);
static
VOID
EFIAPI
PchOnEndOfDxeWrapper (
IN EFI_EVENT Event,
IN VOID *Context
);
static
VOID
EFIAPI
PchOnReadyToBoot (
IN EFI_EVENT Event,
IN VOID *Context
);
static
VOID
EFIAPI
PchOnReadyToBootEx (
IN EFI_EVENT Event,
IN VOID *Context
);
static
VOID
EFIAPI
PchOnExitBootServices (
IN EFI_EVENT Event,
IN VOID *Context
);
static
VOID
EFIAPI
PchOnProtocolNotify (
IN EFI_EVENT Event,
IN VOID *Context
);
static
VOID
EFIAPI
PchOnSmmReadyToLockNotify (
IN EFI_EVENT Event,
IN VOID *Context
);
static
VOID
EFIAPI
PchOnSmmReadyToBootNotify (
IN EFI_EVENT Event,
IN VOID *Context
);
static
EFI_STATUS
PchAcpiOnEndOfDxe (
VOID
);
static
EFI_STATUS
InstallPchNvsProtocol (
IN EFI_HANDLE ImageHandle
);
static
VOID
PchUpdateNvsArea (
VOID
);
static
EFI_STATUS
PublishNhltAcpiTable (
IN PCH_POLICY_HOB *Policy
);
static
EFI_STATUS
LockDownUnusedRstPcie (
IN UINT8 Controller
);
static
EFI_STATUS
ConfigureRstPcieStorageRemapping (
IN UINT8 Controller,
IN UINT8 Function,
IN UINT8 Device
);
static
EFI_STATUS
RstPcieStorageRemappingLateConfig (
VOID
);
static
EFI_STATUS
DetectPcieStorageDevices (
VOID
);
static
EFI_STATUS
PcieEndPointL1ssConfig (
IN UINTN Address,
IN UINT32 *Config,
IN UINT8 L1ssOffset,
IN UINTN Index,
IN UINT16 *NextL1ssAddr
);
// ---------------------------------------------------------------------------
// Module Entry Point
// ---------------------------------------------------------------------------
/**
* Standard UEFI DXE driver entry point.
*/
EFI_STATUS
EFIAPI
_ModuleEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
Status = PchInitDxeEntry (ImageHandle, SystemTable);
if (EFI_ERROR (Status)) {
Status = PchS3BootScriptDeinit ();
}
return Status;
}
// ===========================================================================
// SECTION 1: Initialization Sequence
// ===========================================================================
/**
* AutoGen-style DXE entry. Initializes all UEFI core globals,
* configures PCH debug port, copies PCIe segment bus table from PCD,
* and initializes the S3 boot script system.
*/
static
EFI_STATUS
PchInitDxeEntry (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
UINT64 PcdValue;
UINTN PcdSize;
UINT16 PostCode;
BOOLEAN PostCode0;
UINT32 TimerValue;
// --- Save UEFI core globals ---
gImageHandle = ImageHandle;
gSystemTable = SystemTable;
gBootServices = SystemTable->BootServices;
gRuntimeServices = SystemTable->RuntimeServices;
// Get DxeServicesTable
Status = gSystemTable->GetConfigurationTable (
&gEfiDxeServicesTableGuid,
(VOID **)&gDxeServicesTable
);
ASSERT_EFI_ERROR (Status);
ASSERT (gDxeServicesTable != NULL);
// Get PCI USRA protocol
if (gPciUsraProtocol == NULL) {
Status = gBootServices->LocateProtocol (
&gEfiPciUsraProtocolGuid,
NULL,
&gPciUsraProtocol
);
ASSERT_EFI_ERROR (Status);
ASSERT (gPciUsraProtocol != NULL);
}
// Initialize HOB list
PchInitGlobalDataInit ();
// Copy PCIe Segment Bus Table from PCD
PcdValue = PcdGet64 (PcdPcieSegmentBusTable);
PcdSize = PcdGetSize (PcdPcieSegmentBusTable);
ASSERT (sizeof (gPcieSegBusBuffer) >= PcdSize);
CopyMem (gPcieSegBusBuffer, (VOID *)(UINTN)PcdValue, PcdSize);
// PCH debug port sequence (POST code 0x80)
PostCode = IoRead8 (0x80);
PostCode0 = (PostCode & 0x200) != 0;
TimerValue = IoRead32 (0x508) & 0xFFFFFF;
IoWrite8 (0x80, 0); // enable POST
while (((TimerValue + 357 - IoRead32 (0x508)) & 0x800000) == 0) {
IoDelayShort (); // delay loop
}
IoWrite8 (0x80, 1); // disable POST
if (PostCode0) {
IoWrite8 (0x80, 0); // restore original state
} else {
IoWrite8 (0x80, 1);
}
// Initialize S3 boot script system
Status = PchInitS3BootScriptInit ();
ASSERT_EFI_ERROR (Status);
return Status;
}
/**
* Main PCH initialization entry point (PchInit.c).
* Consumes PCH policy HOB, loads RSTe Option ROMs, initializes
* PCH devices, installs ACPI NVS protocol, and registers UEFI events.
*/
static
EFI_STATUS
PchInitEntryPoint (
IN EFI_HANDLE ImageHandle
)
{
EFI_STATUS Status;
EFI_HOB_GUID_TYPE *GuidHob;
PCH_POLICY_HOB *PolicyHob;
EFI_EVENT Event;
VOID *Registration;
UINTN Index;
DEBUG ((DEBUG_INFO, "Uefi PchInitEntryPoint() Start\n"));
DEBUG ((DEBUG_INFO, "Common PchInitEntryPoint() Start\n"));
mSavedImageHandle = ImageHandle;
// --- Step 1: Locate PCH Policy HOB ---
GuidHob = GetNextGuidHob (&gPchPolicyHobGuid);
ASSERT (GuidHob != NULL);
mPchPolicyHob = (PCH_POLICY_HOB *)(GuidHob + 1);
// Load SATA RSTe Option ROM if configured
if ((*(UINT32 *)((UINT8 *)mPchPolicyHob + 2231) & 0x2000) != 0) {
Status = gBootServices->LocateProtocol (
&gPchSataRsteProtocolGuid,
NULL,
&Registration
);
if (EFI_ERROR (Status)) {
Status = gBootServices->ConnectController (
ImageHandle,
&gPchSataRsteProtocolGuid,
NULL,
FALSE
);
if (!EFI_ERROR (Status)) {
DEBUG ((DEBUG_INFO, "Sata RSTe EFI OpRom load triggered\n"));
} else {
DEBUG ((DEBUG_ERROR, "Sata RSTe EFI OpRom load NOT triggered (Status:%r)!!!\n", Status));
}
}
}
// Load Secondary SATA RSTe Option ROM
if ((*(UINT32 *)((UINT8 *)mPchPolicyHob + 3440) & 0x2000) != 0) {
Status = gBootServices->LocateProtocol (
&gSecondarySataRsteProtocolGuid,
NULL,
&Registration
);
if (EFI_ERROR (Status)) {
Status = gBootServices->ConnectController (
mSavedImageHandle,
&gSecondarySataRsteProtocolGuid,
NULL,
FALSE
);
if (!EFI_ERROR (Status)) {
DEBUG ((DEBUG_INFO, "Secondary Sata RSTe EFI OpRom load triggered\n"));
} else {
DEBUG ((DEBUG_ERROR, "Secondary Sata RSTe EFI OpRom load NOT triggered (Status:%r)!!!\n", Status));
}
}
}
DEBUG ((DEBUG_INFO, "Common PchInitEntryPoint() End\n"));
// --- Step 2: Initialize PCH Devices ---
DEBUG ((DEBUG_INFO, "InitializePchDevice() Start\n"));
PchCycleDecodingPwrmBaseGet (&Registration);
{
PCH_POLICY_HOB *DeviceConfig;
UINT8 *Buffer;
Buffer = AllocateZeroPool (sizeof (PCH_POLICY_HOB));
ASSERT (Buffer != NULL);
if (Buffer != NULL) {
Buffer[0] = 1; // config version
PchInitDeviceConfig (Buffer);
// USB Precondition
if ((mPchPolicyHob->UsbOtgEnable & 1) != 0) {
PchUsbPrecondition ();
*((UINT64 *)Buffer + 1) = (UINT64)mUsbContext;
}
// Configure Power Management for RST Remapping
Status = ConfigurePmForRstRemapping (
*(UINT8 *)((UINT8 *)mPchPolicyHob + 3508),
*(UINT8 *)((UINT8 *)mPchPolicyHob + 3509)
);
ASSERT_EFI_ERROR (Status);
// Early PCIe RP configuration (ASPM/L1SS)
PchPcieRpEarlyConfig ();
// Configure RP Function Number mapping
Status = PchConfigureRpfnMapping ();
ASSERT_EFI_ERROR (Status);
// Install device config protocol
Status = gBootServices->InstallMultipleProtocolInterfaces (
&Event,
&gPchDeviceConfigProtocolGuid,
Buffer,
NULL
);
ASSERT_EFI_ERROR (Status);
} else {
ASSERT (FALSE); // allocation failure
}
}
DEBUG ((DEBUG_INFO, "InitializePchDevice() End\n"));
// --- Step 3: Install PCH NVS Protocol ---
Status = InstallPchNvsProtocol (ImageHandle);
if (EFI_ERROR (Status)) {
return Status;
}
// --- Step 4: Register Protocol Notify ---
Status = UefiLibRegisterProtocolNotify (
&gPchSmmProtocolNotifyGuid,
PchOnProtocolNotify,
&Registration
);
// --- Step 5: Register EndOfDxe Event ---
Status = gBootServices->CreateEvent (
EVT_NOTIFY_SIGNAL,
TPL_CALLBACK,
PchOnEndOfDxeWrapper,
NULL,
&Event
);
ASSERT_EFI_ERROR (Status);
Status = gBootServices->SetTimer (
Event,
TimerEventSignal,
PchOnEndOfDxeWrapper,
0
);
ASSERT_EFI_ERROR (Status);
// --- Step 6: Register ReadyToBoot Event ---
Status = UefiLibCreateReadyToBootEvent (&Event);
ASSERT_EFI_ERROR (Status);
// --- Step 7: Register Additional ReadyToBoot Notify ---
Status = gBootServices->CreateEvent (
EVT_NOTIFY_SIGNAL,
TPL_NOTIFY,
PchOnReadyToBootEx,
NULL,
&Event
);
ASSERT_EFI_ERROR (Status);
// --- Step 8: Register ExitBootServices Event ---
Status = UefiLibCreateLegacyBootEvent (&Registration);
ASSERT_EFI_ERROR (Status);
DEBUG ((DEBUG_INFO, "Uefi PchInitEntryPoint() End\n"));
return Status;
}
// ===========================================================================
// SECTION 2: PCH Device Configuration
// ===========================================================================
/**
* Main PCH device initialization. Programs PMC base, PCR registers,
* PCIe RPs, USB, and other PCH controllers.
*/
static
EFI_STATUS
InitializePchDevice (
VOID
)
{
EFI_STATUS Status;
UINTN PwrmBase;
UINTN AcpiBase;
UINTN MmioBase;
UINTN Index;
UINTN RpCount;
UINT16 DeviceId;
UINT8 PchSku;
UINT32 Data32;
// --- Get base addresses ---
Status = PchCycleDecodingPwrmBaseGet (&PwrmBase);
Status = PchCycleDecodingAcpiBaseGet (&AcpiBase);
MmioBase = PchPciExpressBaseAddress (0, 0, 0);
PchSku = PchInfoLibGetPchSku ();
// --- Program PMC registers ---
// PMC base: read PCR, set SLP_S0 and various PM registers
PchPcrRead (PID_PCH_IOSF, 0x1D0, 4, (UINT8 *)&Data32);
Data32 |= BIT0; // enable PMC
PchPcrWrite (PID_PCH_IOSF, 0x1D0, 4, Data32);
// --- Program P2SB registers ---
// Configure P2SB MMIO base and hide
// --- Program GPIO communities ---
// For each GPIO community, configure PCR pad config registers
// --- Program SATA controller ---
// Configure SATA BARs, interrupt, and port enable
// -- USB Precondition (XHCI warm reset) --
if ((*(UINT8 *)((UINT8 *)mPchPolicyHob + 2263) & 1) != 0) {
PchUsbPrecondition ();
}
// --- RSTe Power Management configuration ---
Status = ConfigurePmForRstRemapping (
mPchPolicyHob->RstRemapController0,
mPchPolicyHob->RstRemapEnable
);
ASSERT_EFI_ERROR (Status);
// --- Early PCIe RP configuration ---
PchPcieRpEarlyConfig ();
// --- RP Function Number mapping ---
Status = PchConfigureRpfnMapping ();
ASSERT_EFI_ERROR (Status);
return EFI_SUCCESS;
}
/**
* Early configuration of all PCIe Root Ports.
* Programs ASPM, L1SS, clock request, and LTR per port.
*/
static
VOID
PchPcieRpEarlyConfig (
VOID
)
{
UINTN RpCount;
UINTN Index;
UINTN RpMmioBase;
UINT8 Device;
UINT8 Function;
UINT32 RpConfig;
UINT16 AspmVal;
INT32 L1ssVal;
RpCount = (PchInfoLibGetPchSku () == PCH_SKU_LEWISBURG) ? 20 : 12;
for (Index = 0; Index < RpCount; Index++) {
// Get RP device/function
GetPchPcieRpDevFun ((UINT8)Index, &Device, &Function);
RpMmioBase = PchPciExpressBaseAddress (0, Device, Function);
// Read config space header
// Check for device present (Vendor ID != 0xFFFF)
// Program ASPM for this RP
AspmVal = (Index << 8) |
(mPchPolicyHob->PcieRpMaxPayload[Index] << 4) |
mPchPolicyHob->PcieRpMaxReadReq[Index];
PcieRpConfigureAspm (2, RpMmioBase + 0x44C, &AspmVal, &RpConfig);
// Program L1SS for this RP
L1ssVal = (Index << 16) |
(mPchPolicyHob->PcieRpMaxPayload[Index] << 8) |
mPchPolicyHob->PcieRpMaxReadReq[Index];
PcieRpConfigureL1ss (2, RpMmioBase + 0x4B4, &L1ssVal, (VOID *)&AspmVal, 1, 1);
// Configure GPIO CLKREQ for this RP
if (*(UINT32 *)(RpMmioBase + 0x450) & BIT16) {
GpioConfigurePcieRpClkReq (2, RpMmioBase + 0x450, NULL, RpMmioBase + 0x450);
}
}
}
/**
* Power management configuration for RST PCIe storage remapping.
* Programs per-port power management registers.
*/
static
EFI_STATUS
ConfigurePmForRstRemapping (
IN UINT8 ControllerIndex,
IN UINT8 Enable
)
{
EFI_STATUS Status;
UINT8 SataMode;
UINTN SataMmioBase;
UINTN Index;
UINTN PortCount;
SataMode = mPchPolicyHob->SataControllerMode;
if (SataMode == 0) {
DEBUG ((DEBUG_INFO, "Can't perform RST PCIe Storage Remapping when Sata Controller is Disabled\n"));
return EFI_UNSUPPORTED;
}
if (SataMode != 2) { // not RAID mode
DEBUG ((DEBUG_INFO, "Can't perform RST PCIe storage remapping when Sata Controller mode is not RAID mode\n"));
return EFI_UNSUPPORTED;
}
DEBUG ((DEBUG_INFO, "ConfigurePmForRstRemapping: Low Power Programming - Recommended Setting\n"));
// Get SATA controller MMIO base
SataMmioBase = MmioWrite32 (0, 23, 0);
PortCount = (PchInfoLibGetPchSku () == PCH_SKU_LEWISBURG) ? 8 : 3;
for (Index = 0; Index < PortCount; Index++) {
// Program port power management registers
// Disable/Enable RST remap per port based on policy
UINT8 *PortCfg = (UINT8 *)mPchPolicyHob + 2131 + (Index * 4);
if ((*PortCfg & 2) == 0) {
// Port not used by RST - configure power management
Mmiowrite32 (SataMmioBase + 0x98 + (Index * 0x100), 0x80000000);
Mmiowrite32 (SataMmioBase + 0x9C + (Index * 0x100), 0x00000000);
}
}
// Configure per-port GPIO CLKREQ
GpioConfigurePcieRpClkReq (1, SataMmioBase + 0x90, NULL, SataMmioBase + 0x90);
GpioConfigurePcieRpClkReq (2, SataMmioBase + 0x9C, NULL, SataMmioBase + 0x9C);
// Program SATA controller power management
Mmiowrite32 (SataMmioBase + 0x9C, 0x80000000 | (Enable << 16));
return EFI_SUCCESS;
}
// ===========================================================================
// SECTION 3: USB Precondition
// ===========================================================================
/**
* XHCI USB preconditioning. Performs warm reset and port
* programming to prepare USB ports for OS usage.
*/
static
EFI_STATUS
PchUsbPrecondition (
VOID
)
{
EFI_STATUS Status;
USB_PRECONDITION_CONTEXT *Context;
EFI_PCI_IO_PROTOCOL *PciIo;
UINTN XhciBase;
UINT32 Usb2PortCount;
UINT32 MmioBase;
UINTN Index;
if ((mPchPolicyHob->UsbOtgEnable & 1) == 0) {
return EFI_SUCCESS;
}
// Locate XHCI PCI IO protocol
Status = gBootServices->LocateProtocol (
&gEfiPciIoProtocolGuid,
NULL,
(VOID **)&PciIo
);
ASSERT_EFI_ERROR (Status);
// Get XHCI MMIO base
XhciBase = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, 0x10, 1, &XhciBase);
XhciBase &= 0xFFFFFFF0;
DEBUG ((DEBUG_INFO, "XHCI base address %lx\n", XhciBase));
// Enable XHCI MMIO
MmioBase = MmioWrite32 (0, 20, 0);
*(UINT32 *)(MmioBase + 16) = (UINT32)XhciBase;
MmioWrite8 ((UINT8 *)(MmioBase + 4), 6); // enable bus master
// Get USB2 port count from XHCI capability registers
Usb2PortCount = (*(UINT32 *)(XhciBase + 0x8008) >> 8) & 0xFF;
// Allocate precondition context
Context = AllocateZeroPool (sizeof (USB_PRECONDITION_CONTEXT));
ASSERT (Context != NULL);
if (Context != NULL) {
Context->Signature = USB_PRECOND_SIGNATURE;
Context->PortCount = Usb2PortCount;
// Build port bitmap
for (Index = 0; Index < Usb2PortCount; Index++) {
UINT32 PortSc = *(UINT32 *)(XhciBase + 0x480 + (Index * 16));
if (PortSc & BIT0) {
Context->PortBitmap |= (1 << Index);
*(UINT32 *)(XhciBase + 0x480 + (Index * 16)) =
(PortSc & 0xFFFFFDED) | 0x210;
}
}
// Set timer handler
mUsbAcpiTimerDone = FALSE;
PchCycleDecodingAcpiBaseGet (&MmioBase);
if (MmioBase) {
mUsbAcpiBaseCounter = IoRead32 (MmioBase + 8) & 0xFFFFFF;
}
CopyMem (Context + 3, &mUsbAcpiBaseCounter, 32);
*((UINT8 *)Context + 96) = 0;
Context->TimerCallback = PchUsbPreconditionTimerCallback;
mUsbContext = (USB_PRECONDITION_CONTEXT *)(Context + 1);
}
// Restore XHCI MMIO config
Mmiowrite32 (MmioBase + 4, MmioRead32 (MmioBase + 4) & 0xFFF9);
*(UINT32 *)(MmioBase + 16) = 0;
// Close XHCI MMIO window
PciIo->Pci.Write (PciIo, EfiPciIoWidthUint32, 0x10, 1, &XhciBase);
return EFI_SUCCESS;
}
/**
* Timer callback for USB preconditioning.
* Polls ACPI timer for timeout, programs XHCI port registers.
*/
static
BOOLEAN
EFIAPI
PchUsbPreconditionTimerCallback (
IN VOID *Context,
IN UINT8 Port
)
{
USB_PRECONDITION_CONTEXT *UsbContext;
UINT32 XhciBar;
UINTN Index;
UINTN PortCount;
UsbContext = BASE_CR (Context, USB_PRECONDITION_CONTEXT, Context);
if (UsbContext->Signature != USB_PRECOND_SIGNATURE) {
ASSERT (UsbContext->Signature == USB_PRECOND_SIGNATURE);
return FALSE;
}
if (UsbContext->Done) {
return FALSE;
}
// Wait for ACPI timer synchronization
if (!mUsbAcpiTimerDone) {
UINTN LoopCount;
UINT32 AcpiBase;
UINT32 CurrentTimer;
LoopCount = 0;
while (LoopCount < 500) {
PchCycleDecodingAcpiBaseGet (&AcpiBase);
if (AcpiBase) {
CurrentTimer = IoRead32 (AcpiBase + 8) & 0xFFFFFF;
if (CurrentTimer >= mUsbAcpiBaseCounter &&
CurrentTimer <= mUsbAcpiBaseCounter + 0x100000 + 179001) {
// Timer progressed, OK
}
if (CurrentTimer >= mUsbAcpiBaseCounter &&
CurrentTimer <= mUsbAcpiBaseCounter + 179001) {
mUsbAcpiTimerDone = TRUE;
mUsbAcpiBaseCounter = 0;
break;
}
if (CurrentTimer + 0x100000 > mUsbAcpiBaseCounter + 179001) {
mUsbAcpiBaseCounter = CurrentTimer;
}
}
MicroSecondDelay (100);
LoopCount++;
if (mUsbAcpiTimerDone) {
break;
}
}
}
// Get XHCI MMIO base and program ports
XhciBar = MmioWrite32 (
(UINT8)UsbContext->BarIndex,
(UINT8)UsbContext->Bus,
(UINT8)UsbContext->Device
);
if (UsbContext->PortBitmap != 0) {
BOOLEAN PortsPending = FALSE;
PortCount = UsbContext->PortCount;
for (Index = 0; Index < PortCount; Index++) {
if ((1 << Index) & UsbContext->PortBitmap) {
UINT32 *PortReg = (UINT32 *)((XhciBar & 0xFFFFFFF0) + (Index * 16));
if (*PortReg & BIT0) {
PortsPending = TRUE;
// Program port for warm reset
*PortReg = (*PortReg & 0xFF01FFFD) | 0x103E0;
} else {
UsbContext->PortBitmap &= ~(1 << Index);
}
}
}
if (PortsPending) {
MicroSecondDelay (20000);
for (Index = 0; Index < PortCount; Index++) {
if ((1 << Index) & UsbContext->PortBitmap) {
UINT32 *PortReg = (UINT32 *)((XhciBar & 0xFFFFFFF0) + (Index * 16));
*PortReg = (*PortReg & 0xFF01FFFD) | 0x10201;
}
}
}
UsbContext->Done = TRUE;
}
return (UsbContext->PortBitmap & (1 << Port)) != 0;
}
// ===========================================================================
// SECTION 4: Uplink Port Programming
// ===========================================================================
/**
* Configures PCH Uplink ports (x16 and x8 PCIe ports).
* Programs max payload size and max read size for uplink connections.
*/
static
VOID
PchUplinksInit (
VOID
)
{
EFI_STATUS Status;
UINTN Bus;
UINTN Device;
UINT16 VendorId;
UINTN RpOffset;
UINT8 MaxPayload;
UINT8 MaxReadSize;
if (mPchPolicyHob == NULL) {
DEBUG ((DEBUG_ERROR, "\nERROR!!! PchUplinksInit() - mPchPolicyHob is a NULL pointer.\n"));
return;
}
// Determine PCH SKU
UINT8 PchSku = PchInfoLibGetPchSku ();
// --- Find x16 Uplink Port ---
BOOLEAN FoundX16 = FALSE;
for (Device = 0; Device < 0xFF; Device++) {
MmioBase = MmioWrite32 (Device, 0, 0);
VendorId = MmioRead32 (MmioBase + 2);
if ((UINT16)VendorId == 0x37C0) {
// Found x16 Uplink (Device ID = 0x37C0)
FoundX16 = TRUE;
// Program Max Payload Size
MaxPayload = *(UINT8 *)((UINT8 *)mPchPolicyHob + 2116);
UINTN MpsReg = MmioBase + 52;
switch (MaxPayload) {
case 0: IoWrite32 (MpsReg, 0); break;
case 3: IoWrite32 (MpsReg, 5); break;
case 4: IoWrite32 (MpsReg, 6); break;
case 5: IoWrite32 (MpsReg, 9); break;
case 6: IoWrite32 (MpsReg, 10); break;
default: IoWrite32 (MpsReg, 0); break;
}
// Program Max Read Size
MaxReadSize = *(UINT8 *)((UINT8 *)mPchPolicyHob + 2118);
UINTN MrsReg = MmioBase + 34;
switch (MaxReadSize) {
case 0: IoWrite32 (MrsReg, 0); break;
case 1: IoWrite32 (MrsReg, 1); break;
case 2: IoWrite32 (MrsReg, 2); break;
default:
DEBUG ((DEBUG_ERROR, "PchUplink x16 programming error! Unsupported Max Payload Size!\n"));
break;
}
// Program downstream ports (function 0-5)
for (UINTN Func = 0; Func < 6; Func++) {
UINTN DsMmio = MmioWrite32 (Device + 1, Func, 0);
MmioRead32 (DsMmio + 2);
MmioWrite8 (DsMmio + 272, 0x2000);
}
break;
}
}
if (!FoundX16) {
DEBUG ((DEBUG_ERROR, "PchUplink programming error! Can not find the Uplink x16 port.\n"));
}
// --- Find x8 Uplink Port (for certain PCH SKUs) ---
if (PchSku == 3) {
MmioBase = mLpcMmioBase;
if (mLpcMmioBase == 0) {
mLpcMmioBase = MmioWrite32 (0, 31, 0);
}
}
UINT16 LpcVidDid = MmioRead32 (mLpcMmioBase + 2);
if (LpcVidDid != 0xA1C3 && LpcVidDid != 0xA246 && LpcVidDid != 0xA242) {
// No secondary uplink needed
if (!FoundX16) return;
goto ProgramDownstream;
}
// Find x8 Uplink
for (Device = 0; Device < 0xFF; Device++) {
MmioBase = MmioWrite32 (Device, 0, 0);
VendorId = MmioRead32 (MmioBase + 2);
if ((UINT16)VendorId == 0x37C1) {
// Found x8 Uplink (Device ID = 0x37C1)
// Program Max Payload Size
MaxPayload = *(UINT8 *)((UINT8 *)mPchPolicyHob + 2117);
switch (MaxPayload) {
case 0: IoWrite32 (MmioBase + 52, 0); break;
case 3: IoWrite32 (MmioBase + 52, 5); break;
case 4: IoWrite32 (MmioBase + 52, 6); break;
case 5: IoWrite32 (MmioBase + 52, 9); break;
case 6: IoWrite32 (MmioBase + 52, 10); break;
default: IoWrite32 (MmioBase + 52, 16); break;
}
// Program Max Read Size
MaxReadSize = *(UINT8 *)((UINT8 *)mPchPolicyHob + 2119);
switch (MaxReadSize) {
case 0: IoWrite32 (MmioBase + 34, -8); break;
case 1: IoWrite32 (MmioBase + 34, 1); break;
case 2: IoWrite32 (MmioBase + 34, 2); break;
default:
DEBUG ((DEBUG_ERROR, "PchUplink x8 programming error! Unsupported Max Payload Size!\n"));
break;
}
ProgramDownstream:
// Program downstream port at function 2
MmioBase = MmioWrite32 (Device + 1, 2, 0);
MmioRead32 (MmioBase + 2);
MmioWrite8 (MmioBase + 272, 0x2000);
break;
}
}
if (!FoundX16) {
DEBUG ((DEBUG_ERROR, "PchUplink programming error! Can not find the Uplink x8 port.\n"));
}
}
// ===========================================================================
// SECTION 5: ACPI NVS Protocol
// ===========================================================================
/**
* Allocates and installs the PCH NVS protocol.
* The NVS area is a 0x26D byte buffer shared with ACPI ASL code.
*/
static
EFI_STATUS
InstallPchNvsProtocol (
IN EFI_HANDLE ImageHandle
)
{
EFI_STATUS Status;
VOID *NvsBuffer;
UINTN Index;
DEBUG ((DEBUG_INFO, "Install PCH NVS protocol\n"));
// Allocate NVS buffer (0x26D bytes)
Status = gBootServices->AllocatePages (
AllocateAnyPages,
EfiACPIMemoryNVS,
EFI_SIZE_TO_PAGES (0x26D),
&NvsBuffer
);
ASSERT_EFI_ERROR (Status);
ZeroMem (NvsBuffer, 0x26D);
// Set signature
*(UINT32 *)NvsBuffer = 0x01070000;
// Install protocol
Status = gBootServices->InstallMultipleProtocolInterfaces (
&ImageHandle,
&gPchNvsProtocolGuid,
NvsBuffer,
NULL
);
ASSERT_EFI_ERROR (Status);
// Copy GPIO group info into NVS buffer
{
UINT8 *GpioInfo = (UINT8 *)GpioGetGroupInfoTable ();
for (Index = 0; Index < 3; Index++) {
*((UINT8 *)NvsBuffer + Index + 377) = *(GpioInfo + Index * 64 - 33);
*((UINT8 *)NvsBuffer + Index + 380) = *(GpioInfo + Index * 64);
*((UINT8 *)NvsBuffer + Index + 383) = *(GpioInfo + Index * 64 - 63);
*((UINT16 *)NvsBuffer + Index + 193) = *((UINT16 *)GpioInfo + Index * 32 - 31);
*((UINT8 *)NvsBuffer + Index + 392) = *(GpioInfo + Index * 64 - 60);
*((UINT32 *)NvsBuffer + Index + 98) = *((UINT32 *)GpioInfo + Index * 16 - 14);
*((UINT16 *)NvsBuffer + Index + 203) = *((UINT16 *)GpioInfo + Index * 32 - 26);
*((UINT32 *)NvsBuffer + Index + 103) = *((UINT32 *)GpioInfo + Index * 16 - 12);
*((UINT16 *)NvsBuffer + Index + 212) = *((UINT16 *)GpioInfo + Index * 32 - 22);
*((UINT16 *)NvsBuffer + Index + 215) = *((UINT16 *)GpioInfo + Index * 32 - 21);
*((UINT16 *)NvsBuffer + Index + 218) = *((UINT16 *)GpioInfo + Index * 32 - 20);
*((UINT16 *)NvsBuffer + Index + 221) = *((UINT16 *)GpioInfo + Index * 32 - 19);
*((UINT32 *)NvsBuffer + Index + 112) = *((UINT32 *)GpioInfo + Index * 16 - 21);
*((UINT32 *)NvsBuffer + Index + 115) = *((UINT32 *)GpioInfo + Index * 16 - 20);
*((UINT32 *)NvsBuffer + Index + 118) = *((UINT32 *)GpioInfo + Index * 16 - 19);
*((UINT32 *)NvsBuffer + Index + 121) = *((UINT32 *)GpioInfo + Index * 16 - 18);
}
}
// Register ACPI EndOfDxe processing
Status = gBootServices->CreateEvent (
EVT_NOTIFY_SIGNAL,
TPL_NOTIFY,
PchAcpiOnEndOfDxe,
NULL,
&Event
);
ASSERT_EFI_ERROR (Status);
return Status;
}
/**
* Populates the PCH NVS area with runtime configuration data
* including PCH SKU, LPC device ID, PCIe RP config, GPIO info, etc.
*/
static
VOID
PchUpdateNvsArea (
VOID
)
{
UINTN Index;
UINT16 PchSku;
UINT16 LpcDeviceId;
UINTN P2sbVal;
if (mPchNvsArea == NULL) {
return;
}
DEBUG ((DEBUG_INFO, "PchUpdateNvsArea\n"));
PchSku = PchInfoLibGetPchSku ();
mPchNvsArea->PchSku = PchSku;
// Set GPIO port config
mPchNvsArea->GpioPadCfg[0] = *(UINT8 *)((UINT8 *)mPchPolicyHob + 2028);
// Set SATA port presence
UINTN RpCount = (PchSku == 1) ? 20 : 12;
for (Index = 0; Index < RpCount; Index++) {
mPchNvsArea->GpioPadCfg[Index] = *(UINT8 *)((UINT8 *)mPchPolicyHob + 46 + (Index * 100));
}
// Set PCIe segment bus number for NVS
mPchNvsArea->GpioBaseAddr = PcieAddressDecode (0);
// Fill in PCIe RP BDF per port
Index = 8;
UINTN Offset = 204;
for (Index = 0; Index < RpCount; Index++) {
UINT8 Device;
UINT8 Function;
UINT64 RpBdf;
GetPchPcieRpDevFun ((UINT8)Index, &Device, &Function);
mPchNvsArea->PcieRpBusDevFun[Index] = (UINT32)((UINT8)Function | (Device << 16));
mPchNvsArea->PcieRpMaxPayload[Offset] =
*(UINT16 *)((UINT8 *)mPchPolicyHob + 48 + (Index * 100));
mPchNvsArea->PcieRpMaxReadReq[Offset] =
*(UINT16 *)((UINT8 *)mPchPolicyHob + 50 + (Index * 100));
Offset += 2;
Index += 1;
}
// Set PCH SKU and LPC Device ID
mPchNvsArea->PchSku = PchSku;
LpcDeviceId = PchInfoLibGetLpcDeviceId ();
mPchNvsArea->LpcDeviceId = LpcDeviceId;
// Get P2SB value
PchP2sbRead ((UINTN)mPchNvsArea, NULL, (INT32 *)&P2sbVal);
switch (P2sbVal & 3) {
case 1: mPchNvsArea->PchIoApicBase = 0xFEC01000; break;
case 2: mPchNvsArea->PchIoApicBase = 0xFEC02000; break;
case 3: mPchNvsArea->PchIoApicBase = 0xFEC03000; break;
default: mPchNvsArea->PchIoApicBase = 0xFEC00000; break;
}
// Set TraceHub enable flag
mPchNvsArea->Tolud[8] = (*(UINT8 *)((UINT8 *)mPchPolicyHob + 2727) & 1);
// Configure GPIO pad ownership
{
INT32 IoApic0, IoApic1, IoApic2;
GpioGetPchGpiStatus (&IoApic0, &IoApic1, &IoApic2);
mPchNvsArea->PchIoApicBaseLength =
(3 << (2 * IoApic2)) | (2 << (2 * IoApic1)) | (1 << (2 * IoApic0));
}
// Set per-port GPIO pad native function status
for (INT32 Pin = 256; Pin < 0x10D; Pin++) {
if (Pin != IoApic0 && Pin != IoApic1 && Pin != IoApic2) {
UINT8 GpioGroup;
UINT32 Ownership;
GpioGetGroupInfoTable (&GpioGroup);
for (UINT8 Pad = 0; Pad < *(UINT32 *)(60 * (UINT8)Pin + GpioGetGroupInfoTable() + 56); Pad++) {
UINT32 PadAddr;
GpioGetPadConfigAddr (0x1000000 + Pad, 0, 0x1000000 + Pad - 0x1000000, &PadAddr);
if (PadAddr) {
UINT32 LateCfg[4];
ConfigureRstPcieStorageRemapLate (0x1000000 + Pad, LateCfg);
if (((LateCfg[0] >> 13) & 0x1F) == 9 && ((LateCfg[0] >> 13) & 0xE0) == 0x20) {
mPchNvsArea->SataPortInterface[Pin - 256] |= (1 << Pad);
}
}
}
}
}
// Set SATA port SSD presence
mPchNvsArea->SataPortInterface[11] =
((*(UINT8 *)((UINT8 *)mPchPolicyHob + 2903) & 3) == 2);
// Find SATA port with speed 20 / multiplier 2
if (*(UINT8 *)((UINT8 *)mPchPolicyHob + 3036)) {
UINT8 *SataInfo = (UINT8 *)mPchPolicyHob + 3041;
for (Index = 0; Index < *(UINT8 *)((UINT8 *)mPchPolicyHob + 3036); Index++) {
if (*(SataInfo - 1) == 20 && *SataInfo == 2) {
mPchNvsArea->SataPortInterface[12] = *(SataInfo + 2);
}
SataInfo += 4;
}
}
}
// ===========================================================================
// SECTION 6: ACPI EndOfDxe Processing (NHLT + NVS Patch)
// ===========================================================================
/**
* ACPI EndOfDxe handler.
* Publishes NHLT ACPI table, updates NVS area, and patches
* NVS addresses into ASL name space.
*/
static
EFI_STATUS
PchAcpiOnEndOfDxe (
VOID
)
{
EFI_STATUS Status;
UINT16 HdAudioVidDid;
DEBUG ((DEBUG_INFO, "PchAcpiOnEndOfDxe() Start\n"));
PchInfoLibGetPchSku ();
gBootServices->CloseEvent (gEvent);
// --- HD Audio ACPI Init ---
DEBUG ((DEBUG_INFO, "PchHdAudioAcpiInit() Start\n"));
// Check HD Audio presence
HdAudioVidDid = MmioRead32 ((UINT16 *)MmioWrite32 (0, 31, 3) + 2);
if (HdAudioVidDid == 0xFFFF ||
(*(UINT8 *)((UINT8 *)mPchPolicyHob + 2735) & 4) == 0) {
DEBUG ((DEBUG_INFO, "AudioDSP: Non-HDAudio ACPI Table (NHLT) not set!\n"));
} else {
EFI_STATUS NhltStatus;
NhltStatus = PublishNhltAcpiTable (
(UINT32 *)((UINT8 *)mPchPolicyHob + 2735)
);
DEBUG ((DEBUG_INFO, "PchHdAudioAcpiInit() End - Status = %r\n", NhltStatus));
}
// Update NVS area
PchUpdateNvsArea ();
// Initialize ASL update library
Status = AslUpdateLibInit ();
ASSERT_EFI_ERROR (Status);
// Patch PCH NVS address into ASL (Name: NPCH, value: NVS base)
{
UINT16 NvsSegment = 0x26D;
UINT32 NvsAddr = (UINT32)(UINTN)mPchNvsArea;
Status = AslUpdateNameValue (
SIGNATURE_32 ('N', 'P', 'C', 'H'), // 0x4843504E
(INT16 *)&NvsAddr,
4
);
ASSERT_EFI_ERROR (Status);
Status = AslUpdateNameValue (
SIGNATURE_32 ('N', 'P', 'C', 'L'), // 0x4C43504E
&NvsSegment,
2
);
ASSERT_EFI_ERROR (Status);
}
DEBUG ((DEBUG_INFO, "PchAcpiOnEndOfDxe() End\n"));
return EFI_SUCCESS;
}
// ===========================================================================
// SECTION 7: NHLT ACPI Table Publishing
// ===========================================================================
/**
* Publishes the Non-HD Audio ACPI Table (NHLT) for audio DSP.
* Locates the NHLT from existing ACPI tables (XSDT/RSDT),
* constructs endpoint descriptors, and installs the table.
*/
static
EFI_STATUS
PublishNhltAcpiTable (
IN PCH_POLICY_HOB *Policy
)
{
EFI_STATUS Status;
EFI_ACPI_TABLE_PROTOCOL *AcpiTable;
VOID *NhltBuffer;
UINTN NhltSize;
EFI_ACPI_SDT_PROTOCOL *AcpiSdt;
EFI_ACPI_TABLE_VERSION TableVersion;
VOID *Table;
DEBUG ((DEBUG_INFO, "PublishNhltAcpiTable() Start\n"));
// Parse feature mask to set global enable flags
switch (Policy->FeatureMask & 3) {
case 1: mNhltDmic1 = TRUE; break;
case 2: mNhltSsp0 = TRUE; break;
case 3: mNhltDmic0 = TRUE; break;
}
if (Policy->FeatureMask & 4) {
mNhltSsp1 = TRUE;
mNhltSsp2 = TRUE;
}
if (Policy->FeatureMask & 8) {
mNhltBtRender = TRUE;
mNhltBtCapture = TRUE;
mNhltI2S = TRUE;
}
// Locate ACPI table protocol
Status = gBootServices->LocateProtocol (
&gEfiAcpiTableProtocolGuid,
NULL,
(VOID **)&AcpiTable
);
if (!EFI_ERROR (Status) && AcpiTable != NULL) {
// Construct NHLT endpoints
VOID *Endpoints;
UINTN TotalSize;
NhltEndpointConstructor (&NhltBuffer, &TotalSize);
// Construct NHLT header
DEBUG ((DEBUG_INFO, "NhltAcpiHeaderConstructor() Start\n"));
((EFI_ACPI_DESCRIPTION_HEADER *)NhltBuffer)->Signature =
EFI_ACPI_3_0_NHLT_SIGNATURE;
((EFI_ACPI_DESCRIPTION_HEADER *)NhltBuffer)->Length =
(UINT32)(TotalSize + 37);
((EFI_ACPI_DESCRIPTION_HEADER *)NhltBuffer)->Revision = 0;
CopyMem (NhltBuffer + 10, "INTEL ", 6);
NhltBuffer[28] = 2;
DEBUG ((DEBUG_INFO, "NhltAcpiHeaderConstructor(), NhltAcpiTable->Header.Length = %d B\n",
((EFI_ACPI_DESCRIPTION_HEADER *)NhltBuffer)->Length));
// Install ACPI table
Status = AcpiTable->SetAcpiTable (
AcpiTable,
NhltBuffer,
(BOOLEAN)((EFI_ACPI_DESCRIPTION_HEADER *)NhltBuffer)->Length,
&TableVersion
);
DEBUG ((DEBUG_INFO, "PublishNhltAcpiTable() End\n"));
} else {
Status = EFI_NOT_FOUND;
}
// Locate NHLT from existing RSDT/XSDT for NVS update
DEBUG ((DEBUG_INFO, "LocateNhltAcpiTable() Start\n"));
Status = gSystemTable->GetConfigurationTable (
&gEfiAcpiTableGuid,
&Table
);
if (EFI_ERROR (Status) || Table == NULL) {
DEBUG ((DEBUG_ERROR, "EFI_ERROR or Rsdp == NULL\n"));
return EFI_NOT_FOUND;
}
// Scan XSDT/RSDT for NHLT signature
{
UINT32 *EntryArray;
UINTN EntryCount;
UINTN Index;
// RSDT/XSDT parsing
EntryArray = (UINT32 *)((UINT8 *)Table + 36);
EntryCount = (*(UINT32 *)((UINT8 *)Table + 4) - 36) / 8;
for (Index = 0; Index < EntryCount; Index++) {
EFI_ACPI_DESCRIPTION_HEADER *TableHeader;
TableHeader = (EFI_ACPI_DESCRIPTION_HEADER *)(UINTN)EntryArray[Index];
if (TableHeader->Signature == EFI_ACPI_3_0_NHLT_SIGNATURE) {
DEBUG ((DEBUG_INFO, "Found NhltTable, Address = 0x%016x\n", (UINTN)TableHeader));
// Update NVS area with NHLT address/length
mPchNvsArea->NhltAddress = (UINT64)(UINTN)TableHeader;
mPchNvsArea->NhltLength = TableHeader->Length;
mPchNvsArea->PostProcessingModuleMask = Policy->PostProcessingModuleMask;
NhltAcpiTableDump ((UINTN)TableHeader, (UINTN)mPchNvsArea, 0);
break;
}
}
}
return Status;
}
// ===========================================================================
// SECTION 8: EndOfDxe Processing
// ===========================================================================
/**
* EndOfDxe callback. Locks down SATA, programs PCIe IO Trap,
* reveals TraceHub, configures P2SB.
*/
static
VOID
PchOnEndOfDxe (
VOID
)
{
UINTN SmbusMmio;
UINT16 SmbusVidDid;
UINT16 *IoTrapAddr;
EFI_STATUS Status;
DEBUG ((DEBUG_INFO, "Common PchOnEndOfDxe() Start\n"));
// Lock down unused RST PCIe on primary SATA
LockDownUnusedRstPcie (1);
// Lock down unused RST PCIe on secondary SATA (if present)
SmbusMmio = MmioWrite32 (0, 17, 5);
SmbusVidDid = MmioRead32 ((UINT16 *)(SmbusMmio + 2));
if (SmbusVidDid != 0xFFFF) {
LockDownUnusedRstPcie (2);
}
// Get PCIe IO Trap address
Status = gBootServices->LocateProtocol (
&gPchIoTrapProtocolGuid,
NULL,
(VOID **)&IoTrapAddr
);
ASSERT_EFI_ERROR (Status);
mPcieIoTrapAddress = *IoTrapAddr;
DEBUG ((DEBUG_INFO, "PcieIoTrapAddress: %0x\n", mPcieIoTrapAddress));
// Process locks in InitializePchDevice
if ((*(UINT8 *)((UINT8 *)mPchPolicyHob + 2763) & 1) == 0) {
INT32 AspmVal = 1;
INT32 L1ssVal = -1;
PchPciExpressBaseAddress (198, 0x0C, 0xFFFFFFFF, 1);
PcieRpConfigureAspm (2, 0xFDB00000 + 0x1C, &AspmVal, &L1ssVal);
PcieRpConfigureL1ss (2, 0xFDA0FE0C, (UINT32)&AspmVal, (UINT32)&AspmVal, 1, 1);
PchPciExpressBaseAddress (187, 0xB1C, 0xFFFFFFFF, 256);
}
Status = InitializePchDevice ();
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "Error when processing locks in PchOnEndOfDxe \n"));
ASSERT (FALSE);
}
// Program PCIe IO Trap boot script
if (mPcieIoTrapAddress) {
UINT8 BootScriptEntry[19];
UINT32 Zero = 0;
BootScriptEntry[0] = 0;
BootScriptEntry[1] = 23; // IO_WRITE opcode
BootScriptEntry[2] = 2; // width = UINT16
*(UINT64 *)&BootScriptEntry[3] = mPcieIoTrapAddress;
BootScriptEntry[11] = 1;
*(UINT32 *)&BootScriptEntry[12] = 0;
// Write entry to boot script
VOID *EntryBuf = BootScriptAllocateEntry (23);
if (EntryBuf != NULL) {
CopyMem (EntryBuf, BootScriptEntry, 19);
CopyMem ((UINT8 *)EntryBuf + 19, &Zero, 4);
BootScriptSaveAsSmmCallback (EntryBuf);
}
} else {
ASSERT (FALSE);
}
// --- P2SB Configuration ---
DEBUG ((DEBUG_INFO, "ConfigureP2sbOnEndOfDxe() Start\n"));
PchSteppingCheck ();
PchInfoLibGetPchSku ();
if ((mPchPolicyHob->P2sbUnlock & 1) == 0) {
UINTN P2sbBase = MmioWrite32 (0, 31, 1);
BOOLEAN AcpiBaseSet;
PchCycleDecodingAcpiBaseIsSet (P2sbBase, &AcpiBaseSet);
*(UINT8 *)(P2sbBase + 227) |= BIT7; // P2SB hide
if (!AcpiBaseSet) {
*(UINT8 *)(P2sbBase + 225) = 1; // ACPI base disable
}
}
DEBUG ((DEBUG_INFO, "ConfigureP2sbOnEndOfDxe() End\n"));
DEBUG ((DEBUG_INFO, "Common PchOnEndOfDxe() End\n"));
}
/**
* Wrapper for PchOnEndOfDxe registered with EndOfDxe event.
*/
static
VOID
EFIAPI
PchOnEndOfDxeWrapper (
IN EFI_EVENT Event,
IN VOID *Context
)
{
DEBUG ((DEBUG_INFO, "Uefi PchOnEndOfDxe() Start\n"));
gBootServices->CloseEvent (Event);
PchOnEndOfDxe ();
DEBUG ((DEBUG_INFO, "Uefi PchOnEndOfDxe() End\n"));
}
/**
* ReadyToBoot callback.
* Programs boot script for PCIe IO Trap address.
*/
static
VOID
EFIAPI
PchOnReadyToBoot (
IN EFI_EVENT Event,
IN VOID *Context
)
{
DEBUG ((DEBUG_INFO, "Uefi PchOnReadyToBoot() Start\n"));
if (Event != NULL) {
gBootServices->CloseEvent (Event);
}
if (mPcieIoTrapAddress) {
IoWrite16 (mPcieIoTrapAddress, 0);
} else {
ASSERT (FALSE);
}
DEBUG ((DEBUG_INFO, "Uefi PchOnReadyToBoot() End\n"));
}
/**
* Additional ReadyToBoot notification callback.
*/
static
VOID
EFIAPI
PchOnReadyToBootEx (
IN EFI_EVENT Event,
IN VOID *Context
)
{
if (Event != NULL) {
gBootServices->CloseEvent (Event);
}
}
/**
* ExitBootServices callback.
* Sets flag in NVS area indicating OS is taking over.
*/
static
VOID
EFIAPI
PchOnExitBootServices (
IN EFI_EVENT Event,
IN VOID *Context
)
{
// Signal OS handoff in NVS area
mPchNvsArea->DebugFlag = 1;
}
// ===========================================================================
// SECTION 9: Protocol Notify Callback
// ===========================================================================
/**
* Protocol installed notification callback.
* Triggers Uplink init and xHCI MMIO programming when
* PchSmmProtocol is installed.
*/
static
VOID
EFIAPI
PchOnProtocolNotify (
IN EFI_EVENT Event,
IN VOID *Context
)
{
UINTN MmioBase;
DEBUG ((DEBUG_INFO, "Uefi PchOnProtocolNotify() Start\n"));
// Check if PchSmmProtocol is already installed
Status = gBootServices->LocateProtocol (
&gPchSmmProtocolNotifyGuid,
NULL,
&Registration
);
if (!EFI_ERROR (Status)) {
return; // Already installed, skip
}
gBootServices->CloseEvent (Event);
// Initialize Uplink ports
PchUplinksInit ();
// Configure xHCI MMIO
MmioBase = MmioWrite32 (0, 31, 0);
*(UINT8 *)(MmioBase + 4) |= 3;
// Check PCH stepping for LPC config
if (*(UINT8 *)PciExpressLibRead (0xFC) == 4) {
PchLpcWriteIo (462, 0);
PchLpcWriteIo (464, 45248);
}
// Reveal TraceHub if PMC not configured
UINTN PwrmBase;
PchCycleDecodingPwrmBaseGet (&PwrmBase);
if ((*(UINT32 *)(PwrmBase + 1488) & 1) == 0) {
PchRevealTraceHub ();
}
// Check xHCI status
MmioBase = MmioWrite32 (0, 20, 2);
UINT16 XhciVidDid = MmioRead32 ((UINT16 *)MmioBase);
if (XhciVidDid != 0xFFFF) {
if ((*(UINT32 *)(MmioBase + 16) & 0xFFFFF000) != 0 ||
*(UINT32 *)(MmioBase + 20) != 0) {
*(UINT8 *)(MmioBase + 4) |= 2; // enable bus master
}
}
}
// ===========================================================================
// SECTION 10: TraceHub Configuration
// ===========================================================================
/**
* Reveals TraceHub (MIPI) controller by programming
* SW_LBAR, FW_LBAR, and MTB_LBAR registers.
*/
static
EFI_STATUS
PchRevealTraceHub (
VOID
)
{
UINT32 *PmcMmio;
DEBUG ((DEBUG_INFO, "PchRevealTraceHub() - Start\n"));
// Check if TraceHub is disabled in policy
if ((*(UINT8 *)((UINT8 *)mPchPolicyHob + 3308) & 4) == 0) {
PchPciExpressBaseAddress (188, 0x938, 0xFFFFFFFE, 0);
}
// Program TraceHub FW_LBAR and SW_LBAR
PmcMmio = (UINT32 *)MmioWrite32 (0, 31, 7);
DEBUG ((DEBUG_INFO, "PchRevealTraceHub() - BEFORE: SW_LBAR = 0x%08x\n", PmcMmio[6]));
PmcMmio[7] = 0; // FW_LBAR high
PmcMmio[6] = 0xFE200000; // SW_LBAR low
DEBUG ((DEBUG_INFO, "PchRevealTraceHub() - FW_LBAR = 0x%08x\n", PmcMmio[28]));
DEBUG ((DEBUG_INFO, "PchRevealTraceHub() - MTB_LBAR = 0x%08x\n", PmcMmio[4]));
DEBUG ((DEBUG_INFO, "PchRevealTraceHub() - SW_LBAR = 0x%08x\n", PmcMmio[6]));
return EFI_SUCCESS;
}
// ===========================================================================
// SECTION 11: SATA Lockdown
// ===========================================================================
/**
* Locks down unused RST PCIe ports on a SATA controller.
*/
static
EFI_STATUS
LockDownUnusedRstPcie (
IN UINT8 Controller
)
{
UINTN SataMmio;
UINT8 *PortCfg;
UINTN PortCount;
if (Controller == 1) {
// Primary SATA (Device 23, Function 0)
SataMmio = MmioWrite32 (0, 23, 0);
PortCount = (PchInfoLibGetPchSku () == PCH_SKU_LEWISBURG) ? 8 : 3;
PortCfg = (UINT8 *)mPchPolicyHob + 2131;
} else if (Controller == 2) {
// Secondary SATA (Device 17, Function 5)
SataMmio = MmioWrite32 (0, 17, 5);
PortCfg = (UINT8 *)mPchPolicyHob + 3364;
PortCount = 6;
} else {
DEBUG ((DEBUG_ERROR, "Error: Invalid SATA controller during SATA late DXE configuration!\n"));
ASSERT (FALSE);
return EFI_INVALID_PARAMETER;
}
UINT16 EnableMask = 0;
UINT32 PortEnable;
if (PchInfoLibGetPchSku () == 2) {
// Lewisburg-Refresh: use secondary port config
PortCount = 6;
UINT16 SasPorts = MmioRead32 ((UINT16 *)(SataMmio + 146));
for (Index = 0; Index < (UINTN)((PchInfoLibGetPchSku () == 1) ? 8 : 3); Index++) {
UINT32 PortConfig = *(UINT32 *)((UINT8 *)mPchPolicyHob + 8 * Index + 2143);
if ((PortConfig & 2) != 0 || ((256 << Index) & SasPorts) != 0 ||
(*(UINT8 *)((UINT8 *)mPchPolicyHob + 2131) & 2) != 0 || (PortConfig & 8) != 0) {
EnableMask |= (PortConfig & 1) << Index;
}
}
MmioWrite8 ((UINT16 *)(SataMmio + 144), (~(UINT8)EnableMask & 7) << 8);
GpioConfigurePcieRpClkReq (1, SataMmio + 144, NULL, SataMmio + 144);
MmioWrite8 ((UINT16 *)(SataMmio + 146), EnableMask);
} else {
for (Index = 0; Index < PortCount; Index++) {
if ((*(PortCfg + 12) & 2) != 0 ||
((1 << Index) & *(UINT32 *)(SataMmio + 148)) != 0 ||
(*PortCfg & 2) != 0 ||
(*(PortCfg + 12) & 8) != 0) {
EnableMask |= (*(PortCfg + 12) & 1) << Index;
}
PortCfg += 4;
}
*(UINT32 *)(SataMmio + 144) |= ((UINT8)~EnableMask << 16);
GpioConfigurePcieRpClkReq (2, SataMmio + 144, NULL, SataMmio + 144);
MmioWrite8 ((UINT16 *)(SataMmio + 148), EnableMask);
}
GpioConfigurePcieRpClkReq (1, SataMmio + 144, NULL, SataMmio + 144);
*(UINT32 *)(SataMmio + 156) |= BIT31; // SATA lockdown
GpioConfigurePcieRpClkReq (2, SataMmio + 156, NULL, SataMmio + 156);
return EFI_SUCCESS;
}
// ===========================================================================
// SECTION 12: S3 Boot Script
// ===========================================================================
/**
* Initializes the S3 boot script system.
* Allocates boot script buffer, registers SMM callbacks.
*/
static
EFI_STATUS
PchInitS3BootScriptInit (
VOID
)
{
EFI_STATUS Status;
UINT64 BufferSize;
VOID *BootScriptBuffer;
EFI_SMM_BASE2_PROTOCOL *SmmBase2;
VOID *Registration;
// Check if boot script is already initialized (via PCD 137)
if (mBootScriptTable == NULL) {
Status = gBootServices->AllocatePages (
AllocateAnyPages,
EfiReservedMemoryType,
1,
&BootScriptBuffer
);
ASSERT_EFI_ERROR (Status);
mBootScriptTable = (BOOT_SCRIPT_ENTRY *)BootScriptBuffer;
mBootScriptInited = TRUE;
// Set PCD to store boot script pointer
PcdSet64 (PcdS3BootScriptTablePtr, (UINT64)BootScriptBuffer);
ZeroMem (BootScriptBuffer, 0x20);
// Register for SmmReadyToLock notification
mDxeSmmReadyToLockEvent = UefiLibRegisterProtocolNotify (
&gEfiDxeSmmReadyToLockProtocolGuid,
PchOnSmmReadyToLockNotify,
&Registration
);
ASSERT (mDxeSmmReadyToLockEvent != NULL);
}
// Save boot script table pointer
mBootScriptTable = (BOOT_SCRIPT_ENTRY *)(UINTN)PcdGet64 (PcdS3BootScriptTablePtr);
// Check for SMM base2 protocol
Status = gBootServices->LocateProtocol (
&gEfiSmmBase2ProtocolGuid,
NULL,
(VOID **)&SmmBase2
);
if (!EFI_ERROR (Status) && SmmBase2 != NULL) {
BOOLEAN InSmram;
Status = SmmBase2->InSmram (SmmBase2, &InSmram);
if (!EFI_ERROR (Status) && InSmram &&
SmmBase2->GetSmstLocation (SmmBase2, &gSmmSystemTable) != NULL) {
// Allocate SMM boot script buffer
Status = SmmBase2->SmmAllocatePool (
SmmBase2,
EfiRuntimeServicesData,
0x20,
&mSmmBootScriptBuffer
);
if (EFI_ERROR (Status)) {
BufferSize = (UINTN)PcdGet64 (PcdS3BootScriptTableSize);
Status = SmmBase2->SmmAllocatePool (
SmmBase2,
EfiRuntimeServicesData,
(UINTN)BufferSize,
&mSmmBootScriptBuffer
);
ASSERT_EFI_ERROR (Status);
mSmmBootScriptInited = TRUE;
PcdSet64 (PcdS3BootScriptTableSmmPtr, (UINT64)mSmmBootScriptBuffer);
ZeroMem (mSmmBootScriptBuffer, 0x20);
// Register SMM communication handlers
Status = SmmBase2->SmmRegisterProtocolNotify (
SmmBase2,
&gEfiSmmReadyToLockProtocolGuid,
PchOnSmmReadyToLockNotify,
&Registration
);
ASSERT_EFI_ERROR (Status);
Status = SmmBase2->SmmRegisterProtocolNotify (
SmmBase2,
&gEfiSmmReadyToBootProtocolGuid,
PchOnSmmReadyToBootNotify,
&Registration
);
ASSERT_EFI_ERROR (Status);
}
}
}
return EFI_SUCCESS;
}
// ===========================================================================
// SECTION 13: Protocol Notify (SMM)
// ===========================================================================
/**
* SMM ReadyToLock notification handler.
*/
static
VOID
EFIAPI
PchOnSmmReadyToLockNotify (
IN EFI_EVENT Event,
IN VOID *Context
)
{
DEBUG ((DEBUG_INFO, "%a() in %a module\n", __FUNCTION__, "BootScriptSave.c"));
}
/**
* SMM ReadyToBoot notification handler.
*/
static
VOID
EFIAPI
PchOnSmmReadyToBootNotify (
IN EFI_EVENT Event,
IN VOID *Context
)
{
}
/**
* SMM communication callback for boot script operations.
*/
static
VOID
EFIAPI
PchOnSmmCommunication (
IN EFI_EVENT Event,
IN VOID *Context
)
{
// Handle SMM communication for boot script
}
// ===========================================================================
// SECTION 14: Helper Function Stubs
// ===========================================================================
/**
* Initialize HOB list pointer from system configuration table.
*/
static
VOID
PchInitGlobalDataInit (
VOID
)
{
EFI_STATUS Status;
if (mHobList == NULL) {
Status = gSystemTable->GetConfigurationTable (
&gEfiHobListGuid,
&mHobList
);
ASSERT_EFI_ERROR (Status);
ASSERT (mHobList != NULL);
}
}
/**
* S3 boot script deinitialization (on driver unload).
*/
static
EFI_STATUS
PchS3BootScriptDeinit (
VOID
)
{
// Close events, free buffers
if (mDxeSmmReadyToLockEvent != NULL) {
gBootServices->CloseEvent (mDxeSmmReadyToLockEvent);
}
if (mBootScriptInited && mBootScriptTable != NULL) {
PcdSet64 (PcdS3BootScriptTablePtr, 0);
mBootScriptInited = FALSE;
}
return EFI_SUCCESS;
}
/**
* PCH stepping check for feature support.
* Verifies PCH stepping/mask to determine which features are available.
*/
UINTN
EFIAPI
PchSteppingCheck (
VOID
)
{
// Returns PCH stepping mask
return 0;
}
/**
* Get PCH SKU (Lewisburg = 1, Lewisburg-Refresh = 2).
* Returns the PCH SKU from silicon register.
*/
UINT8
EFIAPI
PchInfoLibGetPchSku (
VOID
)
{
return PCH_SKU_LEWISBURG;
}
/**
* Get LPC device identifier.
*/
UINT16
EFIAPI
PchInfoLibGetLpcDeviceId (
VOID
)
{
return 0;
}
/**
* Read PCH Private Configuration Register (PCR).
* Access via Port ID + Offset mapped to MMIO.
*/
EFI_STATUS
EFIAPI
PchPcrRead (
IN UINT8 Pid,
IN UINT16 Offset,
IN UINT8 Size,
OUT UINT8 *Buffer
)
{
if ((Size - 1) & Offset) {
DEBUG ((DEBUG_ERROR, "PchPcrRead error. Invalid Offset: %x Size: %x", Offset, Size));
ASSERT (FALSE);
return EFI_INVALID_PARAMETER;
}
UINTN Address = (Offset | ((Pid | 0xFD00) << 16));
switch (Size) {
case 1: *Buffer = *(volatile UINT8 *)Address; break;
case 2: *(UINT16 *)Buffer = MmioRead32 ((UINT16 *)Address); break;
case 4: *(UINT32 *)Buffer = *(volatile UINT32 *)Address; break;
default: return EFI_INVALID_PARAMETER;
}
return EFI_SUCCESS;
}
/**
* Write PCH Private Configuration Register (PCR).
* Validates offset alignment and access restrictions via SBI.
*/
EFI_STATUS
EFIAPI
PchPcrWrite (
IN UINT8 Pid,
IN UINT16 Offset,
IN UINT8 Size,
IN UINT32 Data
)
{
UINTN Address = (Offset | ((Pid | 0xFD00) << 16));
if ((Offset & 3) != 0) {
DEBUG ((DEBUG_ERROR, "PchPcrWrite error. Invalid Offset: %x Size: %x", Offset, Size));
ASSERT (FALSE);
return EFI_INVALID_PARAMETER;
}
*(volatile UINT32 *)Address = Data;
return EFI_SUCCESS;
}
/**
* P2SB (Primary to Sideband) read access.
*/
EFI_STATUS
EFIAPI
PchP2sbRead (
IN UINTN MmioBase,
OUT VOID *Buffer
)
{
// Implementation: read P2SB register via MMIO
return EFI_SUCCESS;
}
/**
* Check if ACPI base address decode is enabled.
*/
BOOLEAN
EFIAPI
PchCycleDecodingAcpiBaseIsSet (
IN UINTN MmioBase,
OUT BOOLEAN *IsSet
)
{
return FALSE;
}
/**
* Get ACPI base address from PCH.
*/
EFI_STATUS
EFIAPI
PchCycleDecodingAcpiBaseGet (
OUT UINTN *AcpiBase
)
{
return EFI_SUCCESS;
}
/**
* Get Power Management base address from PCH.
*/
EFI_STATUS
EFIAPI
PchCycleDecodingPwrmBaseGet (
OUT UINTN *PwrmBase
)
{
return EFI_SUCCESS;
}
/**
* Register a protocol notification handler.
*/
EFI_EVENT
EFIAPI
UefiLibRegisterProtocolNotify (
IN EFI_GUID *ProtocolGuid,
IN EFI_EVENT_NOTIFY NotifyFunction,
OUT VOID **Registration
)
{
EFI_STATUS Status;
EFI_EVENT Event;
ASSERT (ProtocolGuid != NULL);
ASSERT (NotifyFunction != NULL);
ASSERT (Registration != NULL);
Status = gBootServices->CreateEvent (
EVT_NOTIFY_SIGNAL,
TPL_CALLBACK,
NotifyFunction,
NULL,
&Event
);
ASSERT_EFI_ERROR (Status);
Status = gBootServices->RegisterProtocolNotify (
ProtocolGuid,
Event,
Registration
);
ASSERT_EFI_ERROR (Status);
return Event;
}
/**
* Create a ReadyToBoot event.
*/
EFI_STATUS
EFIAPI
UefiLibCreateReadyToBootEvent (
OUT EFI_EVENT *Event
)
{
return gBootServices->CreateEvent (
EVT_NOTIFY_SIGNAL,
TPL_CALLBACK,
PchOnReadyToBoot,
NULL,
Event
);
}
/**
* Create a Legacy Boot event.
*/
EFI_STATUS
EFIAPI
UefiLibCreateLegacyBootEvent (
OUT VOID **Registration
)
{
EFI_EVENT Event;
return gBootServices->CreateEvent (
EVT_NOTIFY_SIGNAL,
TPL_CALLBACK,
PchOnExitBootServices,
NULL,
&Event
);
}
/**
* Get PCH PCIe Root Port device and function from port number.
*/
EFI_STATUS
EFIAPI
GetPchPcieRpDevFun (
IN UINT8 RpNumber,
OUT UINT8 *Device,
OUT UINT8 *Function
)
{
// Each PCH PCIe RP occupies 1 device with 1 function
*Device = RpNumber + 1;
*Function = 0;
return EFI_SUCCESS;
}
/**
* Get PCH PCIe Root Port count.
*/
UINTN
EFIAPI
GetPchPcieRpNumber (
VOID
)
{
return MAX_PCIE_ROOT_PORTS;
}
/**
* Allocate memory pool.
*/
VOID *
EFIAPI
MemoryAllocatePool (
IN UINTN Size
)
{
return AllocatePool (Size);
}
/**
* Free memory pool.
*/
VOID
EFIAPI
MemoryFreePool (
IN VOID *Buffer
)
{
FreePool (Buffer);
}
/**
* Microsecond stall wrapper.
*/
VOID
EFIAPI
MemoryStall (
IN UINTN Microseconds
)
{
gBootServices->Stall (Microseconds);
}
/**
* IO delay - short wait for PCH reset sequencing.
*/
static
VOID
IoDelayShort (
VOID
)
{
// Short delay for PCH register programming
}
/**
* IO delay end marker.
*/
static
VOID
IoDelayEnd (
VOID
)
{
}