/**
* 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;
}