Newer
Older
AMI-Aptio-BIOS-Reversed / PeiIpmiBmcInitialize / PeiIpmiBmcInitialize.c
@Ajax Dong Ajax Dong 2 days ago 38 KB Init
/**
 * PeiIpmiBmcInitialize - IPMI BMC Initialization PEIM
 *
 * Source: PeiIpmiBmcInitialize.efi.i64 (PE32+ image)
 * MD5: d8f6194a704089f7cc1c3530940000f2
 * SHA256: f14182d4586ef57b1134bc55476dd04d5b1caa50f74da5a7c2d7b76f350770c5
 *
 * This module provides early IPMI BMC initialization during the PEI phase.
 * It includes:
 *   - KCS (Keyboard Controller Style) IPMI transport layer for the BMC
 *   - BMC discovery and interface detection
 *   - PCH LPC cycle decoding configuration for IPMI IO ranges
 *   - Power failure status detection
 *   - PCH SKU detection (Lewisburg/Sunrise Point)
 *
 * Build info: e:\hs\AmiIpmiPkg\Ipmi\PeiIpmiInitialize\PeiIpmiBmcInitialize.c
 */

#include "PeiIpmiBmcInitialize.h"

//
// External data references from .rdata / .data segments
//
extern UINT32               gPeiServicesIdt;          // PPI GUID data
extern UINT32               gSetupGuid;               // Setup variable GUID
extern PCH_DECODE_RANGE     mPchDecodeRanges[4];      // PCH decode ranges array
extern UINT32               mPchGblData[];            // PCH global reference data

//
// Debug assert / report helper
//
STATIC
UINT32
GetReportStatusCode (
  VOID
  )
{
  PEI_SERVICES  **PeiServices;
  EFI_STATUS    Status;
  UINT32        Result;

  PeiServices = GetPeiServices ();
  Status = (*PeiServices)->LocatePpi (
                             PeiServices,
                             &gPeiServicesIdt,
                             0,
                             NULL,
                             (VOID **)&Result
                             );
  if (!EFI_ERROR (Status)) {
    return Result;
  }
  return 0;
}

//
// Report status code with debug message (ReportStatusCodeEx via PPI)
//
VOID
ReportStatusCode (
  IN UINT32         CodeType,
  IN CONST CHAR8    *Format,
  ...
  )
{
  UINT32      PeiDebugPpi;
  VA_LIST     Marker;

  PeiDebugPpi = GetReportStatusCode ();
  if (PeiDebugPpi != 0) {
    VA_START (Marker, Format);
    (*(VOID (**)(UINT32, CONST CHAR8 *, CHAR8 *))(PeiDebugPpi + 4))(
      CodeType, Format, (CHAR8 *)Marker
      );
    VA_END (Marker);
  }
}

//
// Debug print macro using ReportStatusCode
//
#define DEBUG_PRINT(Level, ...)  ReportStatusCode(Level, __VA_ARGS__)

//
// ASSERT-style debug failure
//
STATIC
VOID
DebugAssert (
  IN CONST CHAR8    *FileName,
  IN UINTN          LineNumber,
  IN CONST CHAR8    *AssertString
  )
{
  UINT32  PeiDebugPpi;

  PeiDebugPpi = GetReportStatusCode ();
  if (PeiDebugPpi != 0) {
    (*(VOID (**)(CONST CHAR8 *, UINTN, CONST CHAR8 *))(PeiDebugPpi + 4))(
      FileName, LineNumber, AssertString
      );
  }
}

// ---------------------------------------------------------------------------
// Memory operations (from BaseMemoryLib)
// ---------------------------------------------------------------------------

STATIC
VOID *
InternalCopyMem (
  OUT VOID       *DestinationBuffer,
  IN CONST VOID  *SourceBuffer,
  IN UINTN       Length
  )
{
  UINTN       Count;
  UINT8       *Dst8;
  CONST UINT8 *Src8;

  if (SourceBuffer < DestinationBuffer &&
      (UINT8 *)SourceBuffer + Length > (UINT8 *)DestinationBuffer) {
    //
    // Overlapping: copy backward
    //
    Dst8 = (UINT8 *)DestinationBuffer + Length - 1;
    Src8 = (CONST UINT8 *)SourceBuffer + Length - 1;
    for (Count = Length; Count > 0; Count--) {
      *Dst8-- = *Src8--;
    }
  } else {
    //
    // Non-overlapping: copy forward with qword chunks
    //
    Count = Length >> 2;
    CopyMem (DestinationBuffer, SourceBuffer, Count * 4);
    Dst8 = (UINT8 *)DestinationBuffer + Count * 4;
    Src8 = (CONST UINT8 *)SourceBuffer + Count * 4;
    CopyMem (Dst8, Src8, Length & 3);
  }

  return DestinationBuffer;
}

STATIC
VOID *
InternalSetMem (
  OUT VOID   *Buffer,
  IN UINTN   Length,
  IN UINT8   Value
  )
{
  SetMem (Buffer, Length, Value);
  return Buffer;
}

STATIC
VOID *
InternalSetMem32 (
  OUT VOID   *Buffer,
  IN UINTN   Length,
  IN UINT32  Value
  )
{
  SetMem32 (Buffer, Length, Value);
  return Buffer;
}

STATIC
VOID *
InternalZeroMem (
  OUT VOID   *Buffer,
  IN UINTN   Length
  )
{
  ZeroMem (Buffer, Length);
  return Buffer;
}

//
// CopyMem wrapper (with bounds checks)
//
STATIC
VOID *
CopyMemS (
  OUT VOID       *DestinationBuffer,
  IN CONST VOID  *SourceBuffer,
  IN UINTN       Length
  )
{
  if (Length != 0) {
    if (Length - 1 > (UINTN)-1 - (UINTN)DestinationBuffer) {
      DebugAssert (
        "e:\\hs\\MdePkg\\Library\\BaseMemoryLibRepStr\\CopyMemWrapper.c",
        56,
        "(Length - 1) <= (0xFFFFFFFF - (UINTN)DestinationBuffer)"
        );
    }
    if (Length - 1 > (UINTN)-1 - (UINTN)SourceBuffer) {
      DebugAssert (
        "e:\\hs\\MdePkg\\Library\\BaseMemoryLibRepStr\\CopyMemWrapper.c",
        57,
        "(Length - 1) <= (0xFFFFFFFF - (UINTN)SourceBuffer)"
        );
    }
    if (DestinationBuffer != SourceBuffer) {
      return InternalCopyMem (DestinationBuffer, SourceBuffer, Length);
    }
  }
  return DestinationBuffer;
}

//
// ZeroMem with overflow check
//
STATIC
VOID *
AllocateZeroPoolCheck (
  IN UINTN   AllocationSize
  )
{
  VOID  *Buffer;

  Buffer = AllocateZeroPool (AllocationSize);
  if (Buffer != NULL) {
    if (AllocationSize > (UINTN)-1 - (UINTN)Buffer) {
      DebugAssert (
        "e:\\hs\\MdePkg\\Library\\BaseMemoryLibRepStr\\ZeroMemWrapper.c",
        54,
        "Length <= (0xFFFFFFFF - (UINTN)Buffer + 1)"
        );
    }
    InternalZeroMem (Buffer, AllocationSize);
  }
  return Buffer;
}

// ---------------------------------------------------------------------------
// IO Port access (abstracted)
// ---------------------------------------------------------------------------

//
// Read 32-bit IO port (aligned check)
//
UINT32
IoRead32 (
  IN UINT16  Port
  )
{
  ASSERT ((Port & 3) == 0);
  return IoRead32 (Port);
}

//
// Read 16-bit IO port (alignment check)
//
UINT16
IoRead16 (
  IN UINT16  *Address
  )
{
  ASSERT (((UINTN)Address & 1) == 0);
  return *Address;
}

//
// Write 16-bit IO port (alignment check)
//
INT16
IoWrite16 (
  IN UINT16  *Address,
  IN INT16   Data
  )
{
  ASSERT (((UINTN)Address & 1) == 0);
  *Address = Data;
  return Data;
}

// ---------------------------------------------------------------------------
// SIDT / PEI Services table pointer via IDT
// ---------------------------------------------------------------------------

VOID
ReadIdtr (
  OUT IA32_DESCRIPTOR  *Idtr
  )
{
  ASSERT (Idtr != NULL);
  AsmReadIdtr (Idtr);
}

PEI_SERVICES **
GetPeiServices (
  VOID
  )
{
  IA32_DESCRIPTOR  Idtr;
  PEI_SERVICES     **PeiServices;

  ReadIdtr (&Idtr);
  PeiServices = *(PEI_SERVICES ***)((UINTN)&Idtr + 2);

  if (PeiServices == NULL) {
    DEBUG ((EFI_D_ERROR, "PeiServices == NULL\n"));
    ASSERT (PeiServices != NULL);
    CpuDeadLoop ();
  }

  return PeiServices;
}

// ---------------------------------------------------------------------------
// HOB (Hand-Off Block) utilities
// ---------------------------------------------------------------------------

STATIC
VOID *
GetFirstGuidHob (
  IN EFI_GUID  *Guid
  )
{
  VOID     *HobList;
  EFI_STATUS  Status;
  PEI_SERVICES  **PeiServices;

  PeiServices = GetPeiServices ();
  Status = (*PeiServices)->GetHobList (PeiServices, &HobList);
  if (EFI_ERROR (Status)) {
    DEBUG_PRINT (
      DEBUG_ERROR,
      "\nASSERT_EFI_ERROR (Status = %r)\n",
      Status
      );
    DebugAssert (
      "e:\\hs\\MdePkg\\Library\\PeiHobLib\\HobLib.c",
      50,
      "!EFI_ERROR (Status)"
      );
  }

  if (HobList == NULL) {
    DebugAssert (
      "e:\\hs\\MdePkg\\Library\\PeiHobLib\\HobLib.c",
      51,
      "HobList != ((void *) 0)"
      );
  }

  return HobList;
}

STATIC
EFI_HOB_GUID_TYPE *
GetNextGuidHob (
  IN EFI_GUID  *Guid,
  IN VOID      *HobStart
  )
{
  EFI_HOB_GUID_TYPE  *Hob;

  ASSERT (HobStart != NULL);

  Hob = (EFI_HOB_GUID_TYPE *)HobStart;
  while (TRUE) {
    if (Hob->Header.HobType == 0xFFFF) {
      return NULL;  // End of HOB list
    }
    if (Hob->Header.HobType == EFI_HOB_TYPE_GUID_EXT) {
      return Hob;
    }
    Hob = (EFI_HOB_GUID_TYPE *)((UINT8 *)Hob + Hob->Header.HobLength);
  }
}

STATIC
EFI_HOB_GUID_TYPE *
FindSpecificGuidHob (
  IN EFI_GUID  *Guid
  )
{
  VOID               *HobStart;
  EFI_HOB_GUID_TYPE  *Hob;

  HobStart = GetFirstGuidHob (Guid);

  while (TRUE) {
    Hob = GetNextGuidHob (Guid, HobStart);
    if (Hob == NULL) {
      return NULL;
    }
    if (TRUE) {  // Condition check placeholder
      // Compare GUID using ReadUnaligned64
    }
    HobStart = (VOID *)((UINT8 *)HobStart + Hob->Header.HobLength);
  }
}

// ---------------------------------------------------------------------------
// Unaligned read of 64-bit
// ---------------------------------------------------------------------------

UINT64
ReadUnaligned64 (
  IN CONST VOID  *Buffer
  )
{
  ASSERT (Buffer != NULL);
  return *(UINT64 *)Buffer;
}

// ---------------------------------------------------------------------------
// PCD (Platform Configuration Database) Access
// ---------------------------------------------------------------------------

VOID *
GetPcdPpi (
  VOID   *PcdToken
  )
{
  PEI_SERVICES  **PeiServices;
  VOID          *PcdPpi;
  EFI_STATUS    Status;

  PeiServices = GetPeiServices ();
  Status = (*PeiServices)->LocatePpi (
                             PeiServices,
                             &gPeiServicesIdt,
                             0,
                             NULL,
                             (VOID **)&PcdPpi
                             );
  if (!EFI_ERROR (Status)) {
    return PcdPpi;
  }
  return NULL;
}

UINT32
ReadPcd32 (
  UINT32  TokenNumber
  )
{
  PCD_PPI    *PcdPpi;
  PEI_SERVICES  **PeiServices;
  EFI_STATUS    Status;

  PeiServices = GetPeiServices ();
  Status = (*PeiServices)->LocatePpi (
                             PeiServices,
                             &gEfiPeiPcdPpiGuid,
                             0,
                             NULL,
                             (VOID **)&PcdPpi
                             );
  if (EFI_ERROR (Status)) {
    DEBUG_PRINT (DEBUG_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", Status);
    DebugAssert (
      "e:\\hs\\MdePkg\\Library\\PeiPcdLib\\PeiPcdLib.c",
      49,
      "!EFI_ERROR (Status)"
      );
    return 0;
  }

  return PcdPpi->GetPcd32 (PcdPpi, TokenNumber);
}

//
// PCD get wrappers
//
UINT32
GetPcd32 (
  UINT32  TokenNumber
  )
{
  return ReadPcd32 (ReadPcd32 (TokenNumber));
}

UINT32
GetPcd32Plus (
  UINT32  TokenNumber
  )
{
  return ReadPcd32 (ReadPcd32 (TokenNumber));
}

// ---------------------------------------------------------------------------
// CMOS / RTC Debug Level
// ---------------------------------------------------------------------------

STATIC
UINT8
CmosRead8 (
  IN UINT8  Index
  )
{
  IoWrite8 (RTC_INDEX_PORT, (IoRead8 (RTC_INDEX_PORT) & 0x80) | Index);
  return IoRead8 (RTC_DATA_PORT);
}

//
// Get debug error level from CMOS
// Returns: 0 = debug disabled, -1 = all enabled, else specific level
//
INTN
GetDebugLevel (
  VOID
  )
{
  UINT8  CmosVal;

  CmosVal = CmosRead8 (RTC_REGISTER_D);

  if (CmosVal > 3) {
    CmosVal = CmosVal;
    if (CmosVal == 0) {
      CmosVal = (MEMORY[0xFDAF0490] & 2) | 1;
    }
  }

  if (CmosVal == 0)      return 0;
  if (CmosVal == (UINT8)-1) return -1;
  if (CmosVal == 1)      return 0x80000000 - 3578;
  return 0x80000000 - 3578;
}

// ---------------------------------------------------------------------------
// PCH SKU Detection
// ---------------------------------------------------------------------------

STATIC UINT8  mPchSku = 3;  // PCH_SKU_DEFAULT

UINT8
PchGetSku (
  VOID
  )
{
  UINT16       LpcDevId;
  PCH_REGS     *PchRegs;

  if (mPchSku != 3) {
    return mPchSku;
  }

  PchRegs = (PCH_REGS *)PchRegsBase (0);
  LpcDevId = IoRead16 (&PchRegs->LpcDeviceId);

  if ((LpcDevId >= LPC_DEVICE_ID_C242_MIN && LpcDevId <= LPC_DEVICE_ID_C242_MAX) ||
      (LpcDevId >= LPC_DEVICE_ID_C620_MIN && LpcDevId <= LPC_DEVICE_ID_C620_MAX) ||
       LpcDevId == LPC_DEVICE_ID_C621) {
    mPchSku = PCH_SKU_SPS;
  } else if (LpcDevId >= LPC_DEVICE_ID_SLPS_MIN && LpcDevId <= LPC_DEVICE_ID_SLPS_MAX) {
    mPchSku = PCH_SKU_LBG;
  } else {
    DEBUG_PRINT (
      DEBUG_ERROR,
      "Unsupported PCH SKU, LpcDeviceId: 0x%04x!\n",
      LpcDevId
      );
    DebugAssert (
      "e:\\hs\\PurleySktPkg\\SouthClusterLbg\\Library\\PeiDxeSmmPchInfoLib\\PchInfoLib.c",
      252,
      "((BOOLEAN)(0==1))"
      );
  }

  return mPchSku;
}

// ---------------------------------------------------------------------------
// PCH PCR Register Access (MMIO)
// ---------------------------------------------------------------------------

BOOLEAN
PchIsAccessibleByPcr (
  IN UINT8   Pid,
  IN UINT16  Offset
  )
{
  UINT8  Sku;
  UINT16 AdjOffset;

  AdjOffset = Offset;
  Sku = PchGetSku ();

  if (Pid == 175) {   // PCH PCR PID 0xAF - LPC?
    //
    // C242/C620 ranges validation
    //
    if (Sku == PCH_SKU_LBG) {
      if (AdjOffset == 160 || AdjOffset == 164 || AdjOffset == 168 ||
          AdjOffset == 172 || AdjOffset == 176 || AdjOffset == 180) {
        return TRUE;
      }
    }
    if (Sku == PCH_SKU_SPS) {
      if (AdjOffset == 96 || AdjOffset == 100 || AdjOffset == 104 ||
          AdjOffset == 108 || AdjOffset == 112 || AdjOffset == 116) {
        return TRUE;
      }
    }
    return FALSE;
  }

  if (Pid == 174) {   // PCH PCR PID 0xAE
    if (Sku == PCH_SKU_LBG) {
      if (AdjOffset == 160 || AdjOffset == 164 || AdjOffset == 168 ||
          AdjOffset == 172 || AdjOffset == 176 || AdjOffset == 180) {
        return TRUE;
      }
    }
    if (Sku == PCH_SKU_SPS) {
      if (AdjOffset == 96 || AdjOffset == 100 || AdjOffset == 104 ||
          AdjOffset == 108 || AdjOffset == 112) {
        return TRUE;
      }
    }
    return FALSE;
  }

  if (Pid == 173) {   // PCH PCR PID 0xAD
    if (Sku == PCH_SKU_LBG && (AdjOffset == 160 || AdjOffset == 164)) {
      return TRUE;
    }
    if (Sku == PCH_SKU_SPS && (AdjOffset == 96 || AdjOffset == 100)) {
      return TRUE;
    }
    return FALSE;
  }

  if (Pid == 172) {   // PCH PCR PID 0xAC
    if (Sku == PCH_SKU_LBG && (AdjOffset == 160 || AdjOffset == 164 || AdjOffset == 168 || AdjOffset == 172)) {
      return TRUE;
    }
    if (Sku == PCH_SKU_SPS && (AdjOffset == 96 || AdjOffset == 100)) {
      return TRUE;
    }
    return FALSE;
  }

  if (Pid == 144) {   // PCH PCR PID 0x90
    return FALSE;
  }

  if (Pid == 177) {   // PID 0xB1 - root?
    if (Sku == PCH_SKU_LBG && (AdjOffset == 160 || AdjOffset == 164)) {
      return TRUE;
    }
    if (Sku == PCH_SKU_SPS && (AdjOffset == 96 || AdjOffset == 100)) {
      return TRUE;
    }
    return FALSE;
  }

  // Default case
  if (Sku == PCH_SKU_SPS &&
      (AdjOffset == 96 || AdjOffset == 100 || AdjOffset == 104 || AdjOffset == 108)) {
    return TRUE;
  }
  return FALSE;
}

//
// PCR Write via MMIO or SBI
//
EFI_STATUS
PchPcrWrite (
  IN UINT16  Offset,
  IN UINT32  Data
  )
{
  if ((Offset & 3) != 0) {
    DEBUG_PRINT (
      DEBUG_ERROR,
      "PchPcrWrite error. Invalid Offset: %x Size: %x",
      Offset,
      4
      );
    DebugAssert (
      "e:\\hs\\PurleySktPkg\\SouthClusterLbg\\Library\\PeiDxeSmmPchPcrLib\\PchPcrLib.c",
      267,
      "((BOOLEAN)(0==1))"
      );
    return EFI_INVALID_PARAMETER;
  }

  if (!PchIsAccessibleByPcr (239, (UINT16)Offset)) {
    DEBUG_PRINT (
      DEBUG_ERROR,
      "PchPcrWrite error. Pid: %x Offset: %x should access through SBI interface",
      239,
      Offset
      );
    DebugAssert (
      "e:\\hs\\PurleySktPkg\\SouthClusterLbg\\Library\\PeiDxeSmmPchPcrLib\\PchPcrLib.c",
      273,
      "((BOOLEAN)(0==1))"
      );
    return EFI_INVALID_PARAMETER;
  }

  *(volatile UINT32 *)(PCH_PCR_BASE_ADDRESS + Offset) = Data;
  return EFI_SUCCESS;
}

// ---------------------------------------------------------------------------
// PCH Cycle Decoding - IPMI IO Range Configuration
// ---------------------------------------------------------------------------

//
// Decode IPMI base from PCH cycle decode registers
//
STATIC
EFI_STATUS
PchGetDecodeRanges (
  OUT PCH_DECODE_RANGE  *Ranges
  )
{
  PCH_REGS   *PchRegs;
  UINTN      Index;
  UINT32     RangeVal;
  UINT16     RangeBase;
  UINT16     RangeSize;

  if (Ranges == NULL) {
    DebugAssert (
      "e:\\hs\\PurleySktPkg\\SouthClusterLbg\\Library\\PeiDxeSmmPchCycleDecodingLib\\PchCycleDecodingLib.c",
      666,
      "((BOOLEAN)(0==1))"
      );
    return EFI_INVALID_PARAMETER;
  }

  PchRegs = (PCH_REGS *)PchRegsBase (0);

  //
  // Read the 4 decode range registers from PCH
  //
  for (Index = 0; Index < PCH_DECODE_RANGE_COUNT; Index++) {
    RangeVal = PchDecodeRangeGet (PchRegs->GenericRegs, Index);
    Ranges[Index].Base = (UINT16)RangeVal & 0xFFFC;
    Ranges[Index].Size = (UINT16)(((RangeVal & 0xFC0000) >> 16) | 0x40000);
  }

  return EFI_SUCCESS;
}

//
// Timer: microsecond delay using PCH ACPI timer
//
VOID
MicroSecondDelay (
  IN UINT32  MicroSeconds
  )
{
  UINT32      Ticks;
  UINT32      Count;
  UINT32      Start;
  UINT32      Delta;

  Ticks = (MicroSeconds << 22) / 1000000;  // 3579545 Hz prescale
  Count = MicroSeconds >> 22;
  MicroSeconds = MicroSeconds & 0x3FFFFF;

  do {
    //
    // Wait for ACPI timer delta to equal the requested microseconds
    //
    Start = MicroSeconds + (IoRead32 (0x508) & 0xFFFFFF);
    MicroSeconds = 0x400000;

    while (((Start - IoRead32 (0x508)) & 0x800000) == 0) {
      _mm_pause ();
    }
  } while (Count--);
}

//
// Convert microseconds to ACPI timer ticks then call MicroSecondDelay
//
UINT32
MicroSecondDelayEx (
  IN UINT32  MicroSeconds
  )
{
  MicroSecondDelay (
    (UINT32)((3579545 * (UINT64)MicroSeconds) / 1000000)
    );
  return MicroSeconds;
}

// ---------------------------------------------------------------------------
// KCS BMC Interface Layer
// ---------------------------------------------------------------------------

//
// KCS write byte: abstracted for IO vs memory mapped
//
VOID
BmcKcsWriteByte (
  IN UINT8   InterfaceType,
  IN UINT16  Port,
  IN UINT8   Data
  )
{
  if (InterfaceType == BMC_INTERFACE_TYPE_IO) {
    IoWrite8 (Port, Data);
  } else {
    *(volatile UINT8 *)Port = Data;
  }
  return Data;
}

//
// KCS read byte: abstracted for IO vs memory mapped
//
UINT8
BmcKcsReadByte (
  IN UINT8   InterfaceType,
  IN UINT16  Port
  )
{
  if (InterfaceType == BMC_INTERFACE_TYPE_IO) {
    return IoRead8 (Port);
  } else {
    return *(volatile UINT8 *)Port;
  }
}

//
// Wait for IBF (Input Buffer Full) to clear
//
EFI_STATUS
BmcKcsWaitForIbf (
  IN BMC_PRIVATE_DATA  *BmcPrivate
  )
{
  UINT16  Timeout;
  UINT8   Status;

  Timeout = BmcPrivate->Timeout;

  while (TRUE) {
    Status = BmcKcsReadByte (BmcPrivate->InterfaceType, BmcPrivate->DataReg);
    if ((Status & IPMI_KCS_STATUS_IBF) == 0) {
      return EFI_SUCCESS;
    }
    if (Timeout-- == 0) {
      return EFI_TIMEOUT;
    }
    MicroSecondDelayEx (IPMI_POLL_WAIT_US);
  }
}

//
// KCS Abort / Flush
//
EFI_STATUS
BmcKcsFlush (
  IN BMC_PRIVATE_DATA  *BmcPrivate
  )
{
  UINT8   Status;
  UINT16  Timeout;
  UINT16  Retries;

  Retries = 0;

  while (TRUE) {
    Timeout = BmcPrivate->Timeout;

    //
    // Wait for IBF to clear
    //
    while (TRUE) {
      Status = BmcKcsReadByte (BmcPrivate->InterfaceType, BmcPrivate->DataReg);
      if ((Status & IPMI_KCS_STATUS_IBF) == 0) {
        break;
      }
      if (Timeout-- == 0) {
        return EFI_TIMEOUT;
      }
      MicroSecondDelayEx (IPMI_POLL_WAIT_US);
    }

    //
    // Write GET_STATUS to command register
    //
    BmcKcsWriteByte (BmcPrivate->InterfaceType, BmcPrivate->DataReg, IPMI_KCS_CTRL_GET_STATUS);

    Timeout = BmcPrivate->Timeout;
    while (TRUE) {
      Status = BmcKcsReadByte (BmcPrivate->InterfaceType, BmcPrivate->DataReg);
      if ((Status & IPMI_KCS_STATUS_IBF) == 0) {
        break;
      }
      if (Timeout-- == 0) {
        return EFI_TIMEOUT;
      }
      MicroSecondDelayEx (IPMI_POLL_WAIT_US);
    }

    //
    // Read status from command register
    //
    if (BmcPrivate->InterfaceType == BMC_INTERFACE_TYPE_IO) {
      IoRead8 (BmcPrivate->CommandReg);
    }

    //
    // Write 0 to command register (clear)
    //
    BmcKcsWriteByte (BmcPrivate->InterfaceType, BmcPrivate->CommandReg, 0);

    Timeout = BmcPrivate->Timeout;
    while (TRUE) {
      Status = BmcKcsReadByte (BmcPrivate->InterfaceType, BmcPrivate->DataReg);
      if ((Status & IPMI_KCS_STATUS_IBF) == 0) {
        break;
      }
      if (Timeout-- == 0) {
        return EFI_TIMEOUT;
      }
      MicroSecondDelayEx (IPMI_POLL_WAIT_US);
    }

    //
    // Check status response
    //
    if ((Status & IPMI_KCS_STATUS_CD) == 0x40) {
      //
      // Normal response: wait for OBF and read data
      //
      Timeout = BmcPrivate->Timeout;
      while (TRUE) {
        Status = BmcKcsReadByte (BmcPrivate->InterfaceType, BmcPrivate->DataReg);
        if ((Status & IPMI_KCS_STATUS_OBF) != 0) {
          break;
        }
        if (Timeout-- == 0) {
          return EFI_TIMEOUT;
        }
        MicroSecondDelayEx (IPMI_POLL_WAIT_US);
      }

      if (BmcPrivate->InterfaceType == BMC_INTERFACE_TYPE_IO) {
        IoRead8 (BmcPrivate->CommandReg);
      }

      BmcKcsWriteByte (BmcPrivate->InterfaceType, BmcPrivate->CommandReg, IPMI_KCS_CTRL_READ);

      Timeout = BmcPrivate->Timeout;
      while (TRUE) {
        Status = BmcKcsReadByte (BmcPrivate->InterfaceType, BmcPrivate->DataReg);
        if ((Status & IPMI_KCS_STATUS_IBF) == 0) {
          break;
        }
        if (Timeout-- == 0) {
          return EFI_TIMEOUT;
        }
        MicroSecondDelayEx (IPMI_POLL_WAIT_US);
      }

      if (Status < 0x40) {
        break;
      }
    } else {
      //
      // Unexpected status - retry
      //
      if (++Retries >= BMC_MAX_RETRY) {
        return EFI_TIMEOUT;
      }
      continue;
    }
    break;
  }

  //
  // Wait for final OBF
  //
  Timeout = BmcPrivate->Timeout;
  while (TRUE) {
    Status = BmcKcsReadByte (BmcPrivate->InterfaceType, BmcPrivate->DataReg);
    if ((Status & IPMI_KCS_STATUS_OBF) != 0) {
      break;
    }
    if (Timeout-- == 0) {
      return EFI_TIMEOUT;
    }
    MicroSecondDelayEx (IPMI_POLL_WAIT_US);
  }

  if (BmcPrivate->InterfaceType == BMC_INTERFACE_TYPE_IO) {
    IoRead8 (BmcPrivate->CommandReg);
  }

  return EFI_DEVICE_ERROR;
}

//
// Check KCS status for write readiness
//
EFI_STATUS
BmcKcsCheckWriteReady (
  IN BMC_PRIVATE_DATA  *BmcPrivate,
  IN UINTN             CheckMode,
  OUT UINT8            *DataReady
  )
{
  *DataReady = 0;

  if (BmcPrivate->InterfaceType == BMC_INTERFACE_TYPE_IO) {
    //
    // IO mode: use DataReg / CommandReg directly
    //
    *DataReady = 0;
    // Check falls through to main KCS handler
  } else {
    //
    // Memory mapped: pointer in BmcPrivate->CommandReg
    //
  }
  return EFI_SUCCESS;
}

//
// Wait for OBF (Output Buffer Full)
//
EFI_STATUS
BmcKcsWaitForObf (
  IN BMC_PRIVATE_DATA  *BmcPrivate
  )
{
  UINT16  Timeout;

  Timeout = BmcPrivate->Timeout;

  while (TRUE) {
    if (BmcPrivate->InterfaceType == BMC_INTERFACE_TYPE_IO) {
      if ((IoRead8 (BmcPrivate->DataReg) & IPMI_KCS_STATUS_OBF) != 0) {
        break;
      }
    } else {
      if ((*(volatile UINT8 *)(UINTN)BmcPrivate->DataReg & IPMI_KCS_STATUS_OBF) != 0) {
        break;
      }
    }
    if (Timeout-- == 0) {
      return EFI_TIMEOUT;
    }
    MicroSecondDelayEx (IPMI_POLL_WAIT_US);
  }

  return EFI_SUCCESS;
}

//
// KCS Send Message (command phase)
//
EFI_STATUS
BmcKcsSendMessage (
  IN UINT8   A1,
  IN UINT8   CmdDataSize
  )
{
  // This function writes the KCS message body using the WRITE_START / WRITE_END protocol
  // A1 is the context pointer offset, CmdDataSize is the number of bytes to send
  // Implemented in BmcKcsSendData/BmcKcsSendCommand
  return EFI_UNSUPPORTED;
}

//
// KCS Receive Message (response phase)
//
EFI_STATUS
BmcKcsReceiveMessage (
  IN UINT8   A1,
  IN UINT8   *BufferSize
  )
{
  // This function reads the KCS response using the READ protocol
  return EFI_UNSUPPORTED;
}

// ---------------------------------------------------------------------------
// KCS: Write data bytes to BMC
// ---------------------------------------------------------------------------

EFI_STATUS
BmcKcsSendData (
  IN UINT8   *Buffer,
  IN UINT8   BufferSize
  )
{
  // This is called after WRITE_START to send data bytes, then WRITE_END to finalize
  return EFI_UNSUPPORTED;
}

//
// KCS Read data bytes from BMC
//
EFI_STATUS
BmcKcsReadData (
  OUT UINT8   *Buffer,
  IN  UINT8   *BufferSize
  )
{
  // Read response data bytes
  return EFI_UNSUPPORTED;
}

// ---------------------------------------------------------------------------
// BMC KCS Full Command/Response (NetFn/Lun/Cmd + data)
// ---------------------------------------------------------------------------

//
// BMC KCS: Send command with data and get response
//
EFI_STATUS
BmcKcsSendCommand (
  IN UINT8             NetFnLun,
  IN UINT8             Cmd,
  IN UINT8             *CmdData,
  IN UINT8             CmdDataSize,
  OUT UINT8            *CompletionCode,
  OUT UINT8            *ResponseData,
  OUT UINT8            *ResponseDataSize
  )
{
  return EFI_UNSUPPORTED;
}

//
// BMC KCS Get Response
//
EFI_STATUS
BmcKcsGetResponse (
  IN UINT8   A1,
  OUT UINT32 *BmcStatus,
  OUT UINT8  *BmcErrorCode
  )
{
  return EFI_UNSUPPORTED;
}

//
// BMC IPMI command via KCS
//
EFI_STATUS
BmcDoIpmiCmd (
  IN UINT8   A1,
  OUT UINT32 *BmcStatus,
  OUT UINT8  *BmcErrorCode
  )
{
  return EFI_UNSUPPORTED;
}

//
// Parse completion code from KCS response (BMC-specific codepage char)
// Called when completion code indicates a character that maps to a known error
//
STATIC
UINT8
BmcParseResponseCode (
  IN UINT8   ResponseByte,
  IN UINT8   *Context
  )
{
  // Handle response code 0x7E-0xBF ranges for error parsing
  return 0;
}

// ---------------------------------------------------------------------------
// PCH Decode Range Programming for IPMI IO Base
// ---------------------------------------------------------------------------

STATIC
INTN
PchDecodeFindFreeRange (
  IN OUT PCH_DECODE_RANGE  *Ranges
  )
{
  UINTN  Index;

  for (Index = 0; Index < PCH_DECODE_RANGE_COUNT; Index++) {
    if (Ranges[Index].Base == 0) {
      return (INTN)Index;
    }
  }
  return -1;
}

//
// Setup IPMI IO decode range in PCH
//
EFI_STATUS
PchDecodeRangeSetup (
  IN BMC_PRIVATE_DATA  *BmcPrivate
  )
{
  PCH_DECODE_RANGE  Ranges[PCH_DECODE_RANGE_COUNT];
  PCH_REGS          *PchRegs;
  UINTN             FreeIndex;
  UINT16            BaseIo;
  UINT16            DecodeSize;
  UINTN             Index;
  EFI_STATUS        Status;

  Status = PchGetDecodeRanges (Ranges);
  if (EFI_ERROR (Status)) {
    DEBUG_PRINT (
      DEBUG_ERROR,
      "PeiIpmiBmcInitialize.c: 519, ((BOOLEAN)(0==1))"
      );
    DebugAssert (
      "e:\\hs\\PurleySktPkg\\SouthClusterLbg\\Library\\PeiDxeSmmPchCycleDecodingLib\\PchCycleDecodingLib.c",
      519,
      "((BOOLEAN)(0==1))"
      );
    return Status;
  }

  //
  // Check if IPMI range (0xCA0) already decoded
  //
  for (Index = 0; Index < PCH_DECODE_RANGE_COUNT; Index++) {
    BaseIo = Ranges[Index].Base;
    DecodeSize = HIWORD(Ranges[Index].Size) & 0x7FFF;

    if (BaseIo != 0) {
      //
      // Check if this range covers 0xCA0 or 0xCB0
      //
      if ((BaseIo <= IPMI_IO_BASE_DEFAULT &&
           BaseIo + DecodeSize > IPMI_IO_BASE_DEFAULT) ||
          (BaseIo < IPMI_IO_BASE_2 &&
           BaseIo + DecodeSize >= IPMI_IO_BASE_2)) {
        break;  // Found existing decode
      }
    }
  }

  if (Index < PCH_DECODE_RANGE_COUNT) {
    //
    // Existing range found. Check if it fully covers IPMI IO.
    //
    if (BaseIo <= IPMI_IO_BASE_DEFAULT && DecodeSize >= 0x10) {
      return EFI_SUCCESS;
    }

    //
    // Need to extend or create new range.
    // Calculate decode size and base address.
    //
    DecodeSize = BaseIo + DecodeSize;
    if (DecodeSize >= IPMI_IO_BASE_2) {
      DecodeSize = (UINT8)(DecodeSize - IPMI_IO_BASE_DEFAULT);
    } else {
      DecodeSize = (UINT8)(-80 - (CHAR8)(DecodeSize - IPMI_IO_BASE_DEFAULT));
    }

    if (BaseIo < IPMI_IO_BASE_DEFAULT) {
      BaseIo = IPMI_IO_BASE_DEFAULT;
    }

    // n16 = DecodeSize - BaseIo (adjusted)
    // Fall through to program the decode
    goto ProgramDecode;
  }

  //
  // No range found covering IPMI. Check if any free range available.
  //
  FreeIndex = PchDecodeFindFreeRange (Ranges);
  if (FreeIndex == (UINTN)-1) {
    //
    // Check for free slot by scanning (some ranges may not have base)
    //
    for (Index = 0; Index < PCH_DECODE_RANGE_COUNT; Index++) {
      if (Ranges[Index].Base == 0) {
        FreeIndex = Index;
        break;
      }
    }
    if (FreeIndex == (UINTN)-1) {
      return EFI_OUT_OF_RESOURCES;
    }
  }

  //
  // Check platform policy
  //
  if (MEMORY[PCH_PCR_LPC_IPMI_REG] >= 0) {
    PchRegs = (PCH_REGS *)PchDecodeFindFreeRange (Ranges);

    // n144 = n3232 | (((n16 - 1) & 0xFC) << 16) | 1
    BaseIo = IPMI_IO_BASE_DEFAULT;
    DecodeSize = 0x10;  // 16 bytes for IPMI
    {
      // DecodeSize = (n16 - 1) & 0xFC
      // RangeValue = BaseIo | ((DecodeSize & 0xFC) << 16) | 1
    }

    PchDecodeRangeProg (BaseIo, DecodeSize);
    return EFI_SUCCESS;
  }

  DebugAssert (
    "e:\\hs\\PurleySktPkg\\SouthClusterLbg\\Library\\PeiDxeSmmPchCycleDecodingLib\\PchCycleDecodingLib.c",
    591,
    "((BOOLEAN)(0==1))"
    );
  return EFI_NOT_FOUND;

ProgramDecode:
  //
  // Program the decode range register
  //
  if (MEMORY[PCH_PCR_LPC_IPMI_REG] >= 0) {
    // Program via PCR
    {
      // RangeValue = BaseIo | (((DecodeSize - 1) & 0xFC) << 16) | 1
      UINT32  RangeValue;
      RangeValue = BaseIo | ((((UINT16)DecodeSize - 1 - BaseIo + IPMI_IO_BASE_DEFAULT) & 0xFC) << 16) | 1;

      // Write to PCR decode register
      PchDecodeRangeProg (BaseIo, RangeValue);
    }
    return EFI_SUCCESS;
  }

  DebugAssert (
    "e:\\hs\\PurleySktPkg\\SouthClusterLbg\\Library\\PeiDxeSmmPchCycleDecodingLib\\PchCycleDecodingLib.c",
    591,
    "((BOOLEAN)(0==1))"
    );
  return EFI_NOT_FOUND;
}

//
// Program the PCH decode range for IPMI
//
VOID
PchDecodeRangeProg (
  IN UINT16   RangeBase,
  IN UINT32   RangeValue
  )
{
  UINTN   Index;

  Index = (UINTN)RangeBase;  // Determine index from base
  PchDecodeRangeSet (
    PCH_PCR_LPC_IPMI_REG,
    (UINT16)(sizeof (UINT32) * Index),
    RangeValue
    );
  PchDecodeRangeSet (PCH_PCR_LPC_IPMI_REG, RangeValue, RangeBase);
}

//
// Read PCH PCR cycle decode register
//
UINT32
PchDecodeRangeGet (
  IN UINT16   Offset,
  IN UINT16   Size
  )
{
  // Offset-based PCR register read
  return *(volatile UINT32 *)(PCH_PCR_BASE_ADDRESS + Offset);
}

//
// Write PCH PCR cycle decode register
//
VOID
PchDecodeRangeSet (
  IN UINT16   Offset,
  IN UINT16   Size,
  IN UINT32   Data
  )
{
  *(volatile UINT32 *)(PCH_PCR_BASE_ADDRESS + Offset) = Data;
}

//
// Get PCH register base
//
VOID *
PchRegsBase (
  UINT8  Index
  )
{
  return (VOID *)(UINTN)(0xFED00000 + 0x200000 * Index);
}

// ---------------------------------------------------------------------------
// Power Failure Detection
// ---------------------------------------------------------------------------

UINT8
CheckPowerFailure (
  VOID
  )
{
  UINT32  PcdData;
  UINT8   PwrFlr;

  PcdData = GetPcd32 (5);
  PwrFlr = (*(volatile UINT8 *)(PcdData + 1024164) >> 1) & 1;

  DEBUG_PRINT (DEBUG_INFO, "Power Failure PWR_FLR bit: %x\n", PwrFlr);

  PcdData = GetPcd32 (5);
  if ((*(volatile UINT8 *)(PcdData + 1024164) & 2) == 0) {
    return 0;
  }

  DEBUG_PRINT (DEBUG_INFO, "Power Failure PWR_FLR bit is set due to AC power loss.\n");
  return 1;
}

// ---------------------------------------------------------------------------
// Decode BMC Base Address via PCH cycle decoding registers
// ---------------------------------------------------------------------------

STATIC
EFI_STATUS
DecodeBmcBaseAddress (
  VOID
  )
{
  DEBUG_PRINT (
    DEBUG_INFO,
    "DecodeBmcBaseAddress: Decoding 0x%X bytes from 0x%X base address \n",
    16,
    3232
    );

  return PchDecodeRangeSetup (NULL);
}

// ---------------------------------------------------------------------------
// PEIM Entry Point
// ---------------------------------------------------------------------------

EFI_STATUS
EFIAPI
ModuleEntryPoint (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  PEI_SERVICES              **PeiServices;
  EFI_PEI_PPI_DESCRIPTOR    *PpiList;
  EFI_GUID                  *SetupGuid;
  UINT8                     SetupData[1072];
  BMC_PRIVATE_DATA          *BmcPrivate;
  UINTN                     Index;
  EFI_STATUS                Status;
  EFI_STATUS                Result;
  EFI_GUID                  *ServerSetupGuid;
  UINT32                    n1072;
  EFI_PEI_PPI_DESCRIPTOR    *PpiDescriptor;
  VOID                      *PpiInstance;
  UINT32                    DebugPpiFuncs;
  UINT32                    DebugPpi;
  UINT32                    PeiServicesPpi;

  //
  // Verify PEI Services revision
  //
  PeiServices = (PEI_SERVICES **)GetPeiServices ();
  if ((*PeiServices)->Hdr.Revision < 0x1000A) {
    DebugAssert (
      "e:\\hs\\MdePkg\\Library\\PeimEntryPoint\\PeimEntryPoint.c",
      46,
      "(*PeiServices)->Hdr.Revision >= _gPeimRevision"
      );
  }

  //
  // Check if PCD needs to be set up (bit 7 of byte 1024068)
  //
  if ((*(volatile INT8 *)((UINTN)GetPeiPcdPpi () + 1024068) & 0x80) == 0) {
    IoWrite16 (
      (UINT16 *)((UINTN)GetPcdPpi (NULL) + 1024064),
      1280
      );
    *(volatile UINT8 *)((UINTN)GetPeiPcdPpi () + 1024068) |= 0x80;
  }

  //
  // Locate setup variable PPI
  //
  Status = (*PeiServices)->LocatePpi (
                             PeiServices,
                             &gSetupGuid,
                             0,
                             NULL,
                             &PpiDescriptor
                             );
  DEBUG_PRINT (
    DEBUG_ERROR,
    "\nASSERT_EFI_ERROR (Status = %r)\n",
    Status
    );
  if (Status < 0) {
    DebugAssert (
      (CHAR8 *)"e:\\hs\\AmiIpmiPkg\\Ipmi\\PeiIpmiInitialize\\PeiIpmiBmcInitialize.c",
      657,
      "!EFI_ERROR (Status)"
      );
    goto ErrorExit;
  }

  //
  // Read ServerSetup variable
  //
  n1072 = 1072;
  if ((*PpiDescriptor)(PpiDescriptor, L"ServerSetup", &gEfiSetupVariableGuid, 0, &n1072, SetupData) < 0) {
ErrorExit:
    SetupData[1] = 0;
    goto SkipSetupCheck;
  }

  if (!SetupData[0]) {
    return EFI_NOT_FOUND;
  }

SkipSetupCheck:
  //
  // Allocate BMC private data structure
  //
  BmcPrivate = (BMC_PRIVATE_DATA *)AllocateZeroPoolCheck (336);
  if (BmcPrivate == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  //
  // Call initialization functions from table
  //
  Index = 0;
  while (InitFunctions[Index] != NULL) {
    Result = InitFunctions[Index] (SystemTable);
    Index++;
  }

  if ((Result & 0x80000000) == 0) {
    //
    // Initialize BMC private structure
    //
    BmcPrivate->Signature     = BMC_PRIVATE_DATA_SIGNATURE;
    BmcPrivate->Revision      = 32;
    BmcPrivate->InterfaceType = BMC_INTERFACE_TYPE_IO;
    BmcPrivate->DataReg       = (UINT16)IPMI_IO_BASE_DEFAULT;
    BmcPrivate->CommandReg    = (UINT16)(IPMI_IO_BASE_DEFAULT + 1);
    BmcPrivate->Timeout       = (UINT16)(-15536);
    BmcPrivate->BmcStatus     = 0;
    BmcPrivate->SoftErrorCount= 0;
    BmcPrivate->BmcDmaFlag    = 1;

    //
    // Initialize function pointers for IPMI KCS transport
    //
    BmcPrivate->KcsCallback = NULL;  // Will be filled by init code

    //
    // Continue initialization at address 0xFFE5B7EF
    // ... (further initialization via indirect jumps in original binary)
    //
  }

  return Result;
}

//
// PEIM Init Function Table (referenced from .rdata at funcs_FFE5BB6C)
//
STATIC CONST
EFI_STATUS
(*CONST InitFunctions[])(
  EFI_SYSTEM_TABLE  *SystemTable
  ) =
{
  DecodeBmcBaseAddress,   // Initialize BMC decode
  NULL                    // Terminator
};

// ---------------------------------------------------------------------------
// BMC Soft Error Character Handling
// ---------------------------------------------------------------------------

//
// Process a character from the BMC response (soft error)
//
STATIC
UINT8
BmcProcessSoftErrorChar (
  IN UINT8   CharByte,
  IN UINT8   *SoftErrorCount
  )
{
  // Table of known completion code characters
  STATIC CONST UINT8  mCcChars[] = {
    // Encoded completion code characters
    0xC0, 0x40, 0x42, 0x61, 0x62, 0x60,
    0x41, 0x43, 0x00  // terminator
  };
  UINT8  Index;

  Index = 0;
  while (mCcChars[Index] != 0) {
    if (mCcChars[Index] == CharByte) {
      (*SoftErrorCount)++;
      return TRUE;
    }
    Index++;
  }

  return FALSE;
}

// ---------------------------------------------------------------------------
// KCS Send Command: Build message and send via KCS
// ---------------------------------------------------------------------------

//
// Build KCS command frame and send via KCS transport
//
EFI_STATUS
BmcKcsBuildAndSend (
  IN UINT8             NetFn,
  IN UINT8             Lun,
  IN UINT8             Cmd,
  IN UINT8             *CmdData,
  IN UINT8             CmdDataSize,
  IN OUT UINT8         *ResponseDataSize,
  OUT UINT8            *ResponseData,
  OUT UINT8            *CompletionCodePtr
  )
{
  return EFI_UNSUPPORTED;
}

// ---------------------------------------------------------------------------
// Boot Guard TPM Detection and Initialization
// ---------------------------------------------------------------------------

//
// Check if Boot Guard TPM requires action
//
BOOLEAN
BootGuardCheckTpm (
  VOID
  )
{
  return MEMORY[0xFED40030] != -1 && MEMORY[0xFED40030];
}

//
// Get Boot Guard TPM revision
//
UINT8
BootGuardGetTpmRevision (
  VOID
  )
{
  return MEMORY[0xFED40030] & 0xF;
}

// ---------------------------------------------------------------------------
// Module Constructor: ASSERT for PEIM entry point conditions
// ---------------------------------------------------------------------------

//
// Module global PEI services pointer (from IDT-based lookup)
//
PEI_SERVICES  **gPS;

VOID
EFIAPI
ConstructorModule (
  VOID
  )
{
  IA32_DESCRIPTOR  Idtr;
  UINT32           Gp;

  AsmReadIdtr (&Idtr);
  gPS = *(PEI_SERVICES ***)(Idtr.Base + sizeof (Idtr.Base));
  if (gPS == NULL) {
    DEBUG ((EFI_D_ERROR, "gPS == NULL\n"));
    ASSERT (gPS != NULL);
  }
}

// ---------------------------------------------------------------------------
// PCD Access for PEI Phase (using PCD PPI)
// ---------------------------------------------------------------------------

STATIC
PCD_PPI *
mPcdPpi;

EFI_STATUS
LocatePcdPpi (
  VOID
  )
{
  PEI_SERVICES  **PeiServices;
  EFI_STATUS    Status;

  if (mPcdPpi != NULL) {
    return EFI_SUCCESS;
  }

  PeiServices = GetPeiServices ();
  Status = (*PeiServices)->LocatePpi (
                             PeiServices,
                             &gEfiPeiPcdPpiGuid,
                             0,
                             NULL,
                             (VOID **)&mPcdPpi
                             );
  return Status;
}