/** @file
HeciInit.c - HECI (Host Embedded Controller Interface) Initialization PEIM
This PEIM initializes the HECI interface (both HECI-1 and HECI-2) during the
PEI phase. It provides functions to detect the ME type/status, send/receive
HECI messages, and reset the HECI interface. It also handles DWR (Disable Warm
Reset) detection and HECI-2 initialization for HIDM mode.
Copyright (c) Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
Source: HeciInit.efi (Index 0361), HR650X Purley BIOS
**/
#include "HeciInit.h"
//
// Global data used by the HECI PEIM
//
extern UINT8 mImageGuid[]; // GUID at 0xFFD9FA54
extern UINT32 mBootModeStorage[]; // Boot mode flag storage at 0xFFD9FA64
extern UINT32 mPciConfigBase; // PciExpressLib base address
/**
Boot-mode flag storage at offset 0xFFD9FA64.
Two 8-byte values (mBootModeStorage[0].flag, mBootModeStorage[0].valid,
mBootModeStorage[1].flag, mBootModeStorage[1].valid).
**/
STATIC CONST UINT64 mEmptyBootModeStorage[2] = {0, 0};
/**
PciExpressLib base address (at 0xFFD9FA74).
This is the MMIO base for PCI Express config space.
**/
STATIC UINT32 mPciExpressLibBase = 0;
/**
Dword storage used by HeciLocatePpi for Pcd DB
**/
STATIC UINT32 mPcdDb[0x10] = {0};
// =========================================================================
// Memory and string utility functions
// =========================================================================
/**
Fills a buffer with a specified value.
**/
VOID *
EFIAPI
InternalMemSetMem (
VOID *Buffer,
UINTN Count,
UINT8 Value
)
{
return memset (Buffer, Value, Count);
}
/**
Copies a buffer, handling overlapping regions for memmove.
**/
VOID *
EFIAPI
InternalMemCopyMem (
VOID *Destination,
VOID *Source,
UINTN Count
)
{
if (Source < Destination &&
(UINT8 *)Source + Count - 1 >= (UINT8 *)Destination) {
//
// Overlapping copy (dest after src): copy backwards
//
CopyMem ((UINT8 *)Source + Count - 1,
(UINT8 *)Destination + Count - 1,
Count);
return Destination;
}
CopyMem (Destination, Source, Count & ~3);
CopyMem ((UINT8 *)Destination + (Count & ~3),
(UINT8 *)Source + (Count & ~3),
Count & 3);
return Destination;
}
/**
Sets a buffer to 32-bit values.
**/
VOID *
EFIAPI
InternalMemSetMem32 (
VOID *Buffer,
UINTN Count,
UINT32 Value
)
{
return memset32 (Buffer, Value, Count);
}
/**
Initialize a structure with pairs of values.
**/
INT32 *
EFIAPI
InternalInitStruct (
INT32 *Struct,
INT32 Count,
INT32 Value1,
INT32 Value2
)
{
do {
Struct[2 * Count - 2] = Value1;
Struct[2 * Count-- - 1] = Value2;
} while (Count);
return Struct;
}
// =========================================================================
// PEI Services table access via IDT
// =========================================================================
/**
Reads the IDTR (Interrupt Descriptor Table Register).
**/
VOID
EFIAPI
AsmReadIdtr (
VOID *Idtr
)
{
__sidt (Idtr);
}
/**
Returns the PEI Services pointer by reading the IDT base.
On IA32, the PEI Services pointer is stored just below the IDT base.
**/
EFI_PEI_SERVICES **
EFIAPI
GetPeiServices (
VOID
)
{
struct {
UINT16 Limit;
EFI_PEI_SERVICES **Table;
} Idtr;
AsmReadIdtr (&Idtr);
if (Idtr.Table == NULL) {
DEBUG ((EFI_D_ERROR, "PeiServices != ((void *) 0)\n"));
}
return Idtr.Table;
}
// =========================================================================
// PCD (Platform Configuration Database) access
// =========================================================================
/**
Locates a PPI by GUID using the PEI service.
**/
VOID *
EFIAPI
HeciLocatePpi (
VOID *Guid
)
{
EFI_PEI_SERVICES **PeiServices;
EFI_STATUS Status;
VOID *Ppi;
PeiServices = GetPeiServices ();
Status = (*PeiServices)->LocatePpi (
PeiServices,
(EFI_GUID *)Guid,
0,
NULL,
&Ppi
);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", Status));
}
return Ppi;
}
/**
Gets the PCD database pointer token.
**/
VOID *
EFIAPI
HeciGetPcd (
VOID *Token
)
{
EFI_PEI_SERVICES **PeiServices;
EFI_STATUS Status;
PeiServices = GetPeiServices ();
Status = (*PeiServices)->GetBootMode (PeiServices, &Token);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", Status));
}
return Token;
}
/**
Returns a 32-bit PCD value.
**/
UINT32
EFIAPI
HeciGetPcd32 (
VOID *Token
)
{
EFI_PEI_SERVICES **PeiServices;
EFI_STATUS Status;
PeiServices = GetPeiServices ();
Status = (*PeiServices)->GetBootMode (PeiServices, &Token);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", Status));
}
return (UINT32)Token;
}
/**
Returns a pointer PCD value.
**/
UINT32
EFIAPI
HeciGetPcdPtr (
VOID *Token
)
{
EFI_PEI_SERVICES **PeiServices;
EFI_STATUS Status;
PeiServices = GetPeiServices ();
Status = (*PeiServices)->GetBootMode (PeiServices, &Token);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", Status));
}
return (UINT32)Token;
}
// =========================================================================
// HOB (Hand-Off Block) functions
// =========================================================================
/**
Returns the HOB list pointer.
**/
VOID *
EFIAPI
HeciGetHobList (
VOID
)
{
EFI_PEI_SERVICES **PeiServices;
EFI_STATUS Status;
VOID *HobList;
PeiServices = GetPeiServices ();
Status = (*PeiServices)->GetHobList (PeiServices, &HobList);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", Status));
}
if (HobList == NULL) {
DEBUG ((EFI_D_ERROR, "HobList != ((void *) 0)\n"));
}
return HobList;
}
/**
Finds a HOB by type.
**/
VOID *
EFIAPI
HeciFindHob (
VOID *HobList,
UINT16 HobType
)
{
VOID *Hob;
if (HobList == NULL) {
DEBUG ((EFI_D_ERROR, "HobStart != ((void *) 0)\n"));
}
for (Hob = HobList; ; Hob = (VOID *)((UINT8 *)Hob + *(UINT16 *)((UINT8 *)Hob + 2))) {
if (*(UINT16 *)Hob == 0xFFFF) {
return NULL;
}
if (*(UINT16 *)Hob == HobType) {
return Hob;
}
}
}
/**
Finds the ME Firmware Status HOB by matching GUID.
This function iterates through HOBs, finds a GUID-extension HOB whose GUID
matches the ME Fw HOB GUID, and returns it.
**/
VOID *
EFIAPI
HeciFindMeFwHob (
VOID
)
{
VOID *HobList;
VOID *Hob;
UINT32 i;
for (i = 0; ; i++) {
HobList = HeciGetHobList ();
Hob = HeciFindHob (HobList, 4); // EFI_HOB_TYPE_GUID_EXTENSION
if (Hob == NULL) {
return NULL;
}
if (HeciCompareGuid (&Hob[4], &mImageGuid)) {
return Hob;
}
}
}
/**
Compare two GUIDs.
**/
BOOLEAN
EFIAPI
HeciCompareGuid (
VOID *Guid1,
VOID *Guid2
)
{
return *(UINT64 *)Guid1 == *(UINT64 *)Guid2 &&
*(UINT64 *)((UINT8 *)Guid1 + 8) == *(UINT64 *)((UINT8 *)Guid2 + 8);
}
// =========================================================================
// I/O Port Access
// =========================================================================
/**
Reads a 16-bit value from an I/O port.
**/
UINT16
EFIAPI
IoRead16 (
UINT16 Port
)
{
if ((Port & 1) != 0) {
DEBUG ((EFI_D_ERROR, "(Port & 1) == 0\n"));
}
return __inword (Port);
}
/**
Writes a 16-bit value to an I/O port.
**/
UINT16
EFIAPI
IoWrite16 (
UINT16 Port,
UINT16 Value
)
{
if ((Port & 1) != 0) {
DEBUG ((EFI_D_ERROR, "(Port & 1) == 0\n"));
}
__outword (Port, Value);
return Value;
}
/**
Reads a 32-bit value from an I/O port.
**/
UINT32
EFIAPI
IoRead32 (
UINT16 Port
)
{
if ((Port & 3) != 0) {
DEBUG ((EFI_D_ERROR, "(Port & 3) == 0\n"));
}
return __indword (Port);
}
/**
Reads a 16-bit value from memory via a pointer (may be unaligned).
**/
UINT16
EFIAPI
MemoryRead16 (
UINT16 *Address
)
{
if (((UINTN)Address & 1) != 0) {
DEBUG ((EFI_D_ERROR, "(Address & 1) == 0\n"));
}
return *Address;
}
/**
Reads an aligned 16-bit value from the HECI device registers.
**/
UINT16
EFIAPI
HeciReadVendorId (
UINT32 BusDeviceFunction
)
{
return IoRead16 (0xCF8);
}
// =========================================================================
// PCI Express config read
// =========================================================================
/**
Reads a byte from PCI Express config space.
**/
UINT8
EFIAPI
PciExpressRead8 (
UINTN Address
)
{
if ((Address & 0xF0000000) != 0) {
DEBUG ((EFI_D_ERROR, "((Address) & ~0xfffffff) == 0\n"));
}
return *(volatile UINT8 *)(mPciExpressLibBase + Address);
}
/**
Reads a 16-bit value from a HECI device config register.
Bus=22, Dev=x, Func=0 for HECI-1 or Func=1 for HECI-2.
**/
UINT32
EFIAPI
HeciPciRead32 (
UINT8 Device,
UINT8 Offset
)
{
UINTN Address;
Address = (Device & 7) | ((22 & 0x1F) << 8);
Address = (Address << 12) | Offset;
return IoRead32 (Address);
}
// =========================================================================
// Microsecond delay via MBAR polling
// =========================================================================
/**
Spin-loop delay using the HECI MBAR microsecond timer.
**/
INT32
EFIAPI
HeciMbarSpinLoop (
UINT32 Count
)
{
UINT32 StartTime;
UINT32 TimeoutCount;
TimeoutCount = Count >> 22;
Count &= 0x3FFFFF;
do {
StartTime = Count + (IoRead32 (0x508) & 0xFFFFFF);
Count = 0x400000;
while (((StartTime - IoRead32 (0x508)) & 0x800000) == 0) {
_mm_pause ();
}
} while (TimeoutCount--);
return TimeoutCount;
}
/**
Microsecond delay.
Uses the 3.579545 MHz timer to derive the delay period.
**/
UINT32
EFIAPI
HeciMicrosecondDelay (
UINT32 Microseconds
)
{
UINT64 Ticks;
//
// Convert microseconds to ticks at 3.579545 MHz
// ticks = 3579545 * us / 1000000
//
Ticks = (UINT64)3579545 * Microseconds;
HeciMbarSpinLoop ((UINT32)(Ticks / 1000000));
return Microseconds;
}
// =========================================================================
// HECI get MBAR (HECI-1)
// =========================================================================
/**
Returns the HECI-1 MMIO base address (MBAR).
**/
UINT32
EFIAPI
HeciGetMbar (
VOID
)
{
UINT32 MbarLow;
UINT32 MbarHigh;
UINT32 Mbar;
MbarLow = *((UINT32 *)HeciPciRead32 (0, HECI_PCI_MBAR_OFFSET) + 16);
if (MbarLow == 0xFFFFFFFF) {
DEBUG ((EFI_D_ERROR, "[HECI] ERROR: HECI-1 device disabled\n"));
return 0;
}
Mbar = MbarLow & 0xFFFFFFF0;
MbarHigh = *((UINT32 *)HeciPciRead32 (0, HECI_PCI_MBAR_HIGH_OFFSET) + 20);
if (HeciIsSimicsMode () && MbarHigh) {
DEBUG ((EFI_D_INFO, "[HECI] Detected 64-bit MBAR in HECI-1 under SIMICS, force 32-bit\n"));
Mbar = 0;
MbarHigh = 0;
}
if (!(MbarHigh | Mbar & 0xFFFFFFF0)) {
Mbar = HECI1_MBAR_DEFAULT;
DEBUG ((EFI_D_INFO, "[HECI] WARNING: MBAR not programmed, using default 0x%08X%08X\n", 0, HECI1_MBAR_DEFAULT));
*((UINT32 *)HeciPciRead32 (0, HECI_PCI_MBAR_HIGH_OFFSET) + 20) = 0;
*((UINT32 *)HeciPciRead32 (0, HECI_PCI_MBAR_OFFSET) + 16) = HECI1_MBAR_DEFAULT;
}
return Mbar;
}
/**
Returns the HECI-1 MMIO base address, initializing config if needed.
**/
UINT32
EFIAPI
HeciGetMbarEx (
UINT32 Device
)
{
UINT32 Mbar;
if (Device != 0) {
DEBUG ((EFI_D_ERROR, "HeciDev == HECI1_DEVICE\n"));
}
Mbar = HeciGetMbar ();
//
// Enable memory space and bus master in PCI config if not already set
//
if ((*((UINT8 *)HeciPciRead32 (0, HECI_PCI_CMD_REG_OFFSET) + 4) & 6) != 6) {
*((UINT8 *)HeciPciRead32 (0, HECI_PCI_CMD_REG_OFFSET) + 4) |= 6;
}
return Mbar;
}
// =========================================================================
// HECI reset
// =========================================================================
/**
Resets the HECI interface.
**/
EFI_STATUS
EFIAPI
HeciReset (
UINT32 Device
)
{
UINT32 Mbar;
UINT32 HostCsr;
UINT32 MeCsr;
UINT32 MeFs1;
UINT32 Timeout;
if (Device != 0) {
DEBUG ((EFI_D_ERROR, "HeciDev == HECI1_DEVICE\n"));
}
//
// Check ME state - don't reset in certain error states
//
MeFs1 = *((UINT32 *)HeciPciRead32 (0, HECI_PCI_ME_FS_OFFSET) + 64);
if ((MeFs1 & 0x0F) == 4 || // Recovery
(HIWORD(MeFs1) & 0x0F) == 2 || // SPS w/Debug
(HIWORD(MeFs1) & 0x0F) == 7) { // Disabled
DEBUG ((EFI_D_ERROR, "[HECI] Wrong ME state, can't execute reset ME FS 0x%x\n", MeFs1));
return EFI_DEVICE_ERROR;
}
Mbar = HeciGetMbarEx (0);
if (Mbar == 0xFFFFFFFF || Mbar == 0) {
DEBUG ((EFI_D_ERROR, "[HECI] Illegal MBAR 0x%x\n", Mbar));
return EFI_DEVICE_ERROR;
}
HostCsr = *(volatile UINT32 *)(Mbar + 4);
MeCsr = *(volatile UINT32 *)(Mbar + 12);
DEBUG ((EFI_D_INFO, "[HECI] Resetting HECI interface (CSR: %08X/%08X, MEFS1:%08X)\n",
HostCsr, MeCsr, MeFs1));
//
// Set host reset bits (HRA | ER) if not already set
//
if ((HostCsr & HECI_CSR_ER) == 0) {
*(volatile UINT32 *)(Mbar + 4) = HostCsr | HECI_CSR_ER | HECI_CSR_RP;
}
//
// Wait for host-side reset to complete (RDY cleared)
//
Timeout = 25000;
while (1) {
HostCsr = *(volatile UINT32 *)(Mbar + 4);
if ((HostCsr & HECI_CSR_RDY) == 0) {
break;
}
if (Timeout-- == 0) {
DEBUG ((EFI_D_ERROR,
"[HECI] HECI reset failed on host side (CSR: %08X/%08X, MEFS1: %08X)\n",
HostCsr, *(volatile UINT32 *)(Mbar + 12),
*((UINT32 *)HeciPciRead32 (0, HECI_PCI_ME_FS_OFFSET) + 64)));
return EFI_TIMEOUT;
}
HeciMicrosecondDelay (1000);
}
//
// Wait for ME-side reset ready (RDY set)
//
Timeout = 25000;
while (1) {
MeCsr = *(volatile UINT32 *)(Mbar + 12);
if ((MeCsr & HECI_CSR_RDY) != 0) {
//
// Re-initialize host side
//
*(volatile UINT32 *)(Mbar + 4) = (*(volatile UINT32 *)(Mbar + 4) & 0xFFFFFFE3) | 0x0C;
return EFI_SUCCESS;
}
if (Timeout-- == 0) {
break;
}
HeciMicrosecondDelay (1000);
}
DEBUG ((EFI_D_ERROR,
"[HECI] HECI reset failed on ME side (CSR: %08X/%08X, MEFS1: %08X)\n",
*(volatile UINT32 *)(Mbar + 4), MeCsr,
*((UINT32 *)HeciPciRead32 (0, HECI_PCI_ME_FS_OFFSET) + 64)));
return EFI_TIMEOUT;
}
// =========================================================================
// HECI wait for ME ready
// =========================================================================
/**
Waits for the ME firmware interface to become ready.
Polls the HECI circular buffer registers until the ME interface enters
the ready state or times out.
**/
EFI_STATUS
EFIAPI
HeciWaitMeReady (
VOID
)
{
UINT32 Mbar;
UINT32 Csr;
UINT32 Timeout;
Mbar = HeciGetMbarEx (0);
Timeout = 25000;
while (1) {
Csr = *(volatile UINT32 *)(Mbar + 12);
//
// Ready: interface is ready for communication
//
if ((Csr & 8) != 0) {
break;
}
//
// Error: reset the interface and retry
//
if ((Csr & 0x10) != 0) {
return HeciReset (0);
}
if (Timeout-- == 0) {
DEBUG ((EFI_D_ERROR,
"[HECI] ME interface not ready (CSR: %08X/%08X, MEFS1:%08X)\n",
*(volatile UINT32 *)(Mbar + 4), Csr,
*((UINT32 *)HeciPciRead32 (0, HECI_PCI_ME_FS_OFFSET) + 64)));
return EFI_TIMEOUT;
}
HeciMicrosecondDelay (1000);
}
return EFI_SUCCESS;
}
// =========================================================================
// HECI-1 initialization
// =========================================================================
/**
Initializes the HECI-1 interface.
Performs the following steps:
1. Verifies the HECI device is present (via vendor ID check)
2. Gets the MBAR base address
3. Configures HIDM boot mode
4. Enables PCI config (memory space + bus master)
5. Checks ME state - skips waiting if in recovery/disabled states
6. Waits for ME interface ready
7. Initializes host control/status register
**/
EFI_STATUS
EFIAPI
HeciInitialize (
UINT32 Device
)
{
UINT32 Mbar;
UINT32 MeFs1;
UINT32 HostCsr;
UINT16 VendorId;
UINT16 OperationMode;
if (Device != 0) {
DEBUG ((EFI_D_ERROR, "HeciDev == HECI1_DEVICE\n"));
}
//
// Check if device is present
//
VendorId = MemoryRead16 ((UINT16 *)HeciPciRead32 (0, 0));
if (VendorId != 0x8086) {
DEBUG ((EFI_D_ERROR, "[HECI] ERROR: Device not present\n"));
return EFI_NOT_FOUND;
}
//
// Get MBAR
//
Mbar = HeciGetMbarEx (0);
//
// Set HIDM boot mode (Boot Type = 0, Boot Target = 0, Mode = SCI)
//
*((UINT8 *)HeciPciRead32 (0, HECI_PCI_HIDM_OFFSET) + 160) = 0;
*((UINT8 *)HeciPciRead32 (0, HECI_PCI_HIDM_OFFSET) + 160) = 0;
*((UINT8 *)HeciPciRead32 (0, HECI_PCI_HIDM_OFFSET) + 160) |= HIDM_MODE_SCI;
//
// Enable bus master + memory space
//
*((UINT8 *)HeciPciRead32 (0, HECI_PCI_CMD_REG_OFFSET) + 4) = 6;
//
// Check ME state
//
MeFs1 = *((UINT32 *)HeciPciRead32 (0, HECI_PCI_ME_FS_OFFSET) + 64);
if ((MeFs1 & 0x0F) == 4) {
//
// Recovery mode - initialization will be handled later
//
return HeciWaitMeReady ();
}
OperationMode = HIWORD (MeFs1) & 0x0F;
if (OperationMode == 2 || OperationMode == 7) {
DEBUG ((EFI_D_INFO, "[HECI] Wrong ME state, will not wait for ME (ME FS 0x%x)\n", MeFs1));
return EFI_SUCCESS;
}
//
// Wait for ME interface ready
//
if (HeciWaitMeReady ()) {
return EFI_TIMEOUT;
}
//
// Initialize host CSR: set IG + RP + HRA
//
HostCsr = *(volatile UINT32 *)(Mbar + 4);
if ((HostCsr & 8) == 0) {
*(volatile UINT32 *)(Mbar + 4) = HostCsr & 0xFFFFFFE0 | 0x0E;
}
return EFI_SUCCESS;
}
// =========================================================================
// HECI check interface busy
// =========================================================================
/**
Checks if the HECI interface is busy (not ready for communication).
**/
BOOLEAN
EFIAPI
HeciIsBusy (
VOID
)
{
HECI_CB_REGISTERS *Regs;
Regs = (HECI_CB_REGISTERS *)HeciGetMbarEx (0);
return ((Regs->MeCsr & 8) == 0 || (Regs->HostCsr & 8) == 0);
}
// =========================================================================
// HECI message read from circular buffer
// =========================================================================
/**
Reads a message from the HECI circular buffer.
**/
EFI_STATUS
EFIAPI
HeciReadMsg (
UINT32 Buffer,
UINT32 Length,
UINT32 *MsgHeader,
UINT32 *Size
)
{
HECI_CB_REGISTERS *Regs;
UINT32 i;
UINT32 SlotCount;
Regs = (HECI_CB_REGISTERS *)HeciGetMbarEx (0);
//
// Check if buffer is empty (write pointer == read pointer)
//
if ((UINT8)HIWORD (Regs->MeData) == BYTE1 (Regs->MeData)) {
if (!MsgHeader) {
*Size = 0;
return HECI_ERROR_DEVICE_NOT_PRESENT;
}
*Size = 0;
return HECI_ERROR_DEVICE_NOT_PRESENT;
}
//
// Check for empty buffer in blocking case
//
if ((UINT8)BYTE2 (Regs->MeData) == BYTE1 (Regs->MeData) && !MsgHeader) {
*Size = 0;
return HECI_ERROR_ME_NOT_READY;
}
//
// Parse message header
//
i = 1000;
while (1) {
if (i-- == 0) {
*Size = 0;
return EFI_TIMEOUT;
}
if ((UINT8)BYTE2 (Regs->MeData) != BYTE1 (Regs->MeData)) {
*MsgHeader = Regs->MeData;
SlotCount = (HIWORD (*MsgHeader) & 0x1FF) + 3 >> 2;
if ((*MsgHeader & 0x1FF0000) == 0) {
*Size = 0;
Regs->HostCsr = (Regs->HostCsr & ~7) | 6;
HeciWriteMsg (Buffer, *Size);
return EFI_SUCCESS;
}
if (SlotCount + 4 > 4 * HIBYTE (Regs->MeData)) {
*Size = 0;
return HECI_ERROR_DEVICE_NOT_PRESENT;
}
if (SlotCount > *Size) {
*Size = 0;
return HECI_ERROR_BUFFER_TOO_SMALL;
}
//
// Wait for enough slots to be available
//
for (Timeout = 1000; SlotCount > (UINT8)(HIWORD (Regs->MeData) - BYTE1 (Regs->MeData)); ) {
if (Timeout-- == 0) {
*Size = 0;
return EFI_TIMEOUT;
}
HeciMicrosecondDelay (1000);
}
//
// Copy the message data
//
for (i = 0; i < SlotCount; i++) {
*((UINT32 *)Buffer + i) = Regs->MeData;
}
i = 1;
*Size = *(UINT16 *)(MsgHeader + 2) & 0x1FF;
}
HeciMicrosecondDelay (1000);
if (i) {
break;
}
}
//
*Size = (UINT16)(*(volatile UINT32 *)(MsgHeader + 2) & 0x1FF);
Regs->HostCsr |= 4; // Set HRA
return EFI_SUCCESS;
}
// =========================================================================
// HECI message receive
// =========================================================================
/**
Receives a HECI message from the specified device.
**/
EFI_STATUS
EFIAPI
HeciReceive (
UINT32 Device,
UINT32 Blocking,
UINT32 Buffer,
UINT32 *Length
)
{
EFI_STATUS Status;
UINT32 Mbar;
UINT32 BytesRead;
UINT32 MsgHeader;
UINT32 Size;
UINT32 RetryCount;
UINT32 DoneFlag;
Size = 0;
Mbar = HeciGetMbarEx (0);
if (Device != 0) {
DEBUG ((EFI_D_ERROR, "HeciDev == HECI1_DEVICE\n"));
}
BytesRead = 0;
DoneFlag = 0;
RetryCount = 0;
while (1) {
HeciGetMbarEx (0);
if (HeciIsBusy ()) {
Status = HeciReset (Device);
if (Status < 0) {
Status = HECI_ERROR_DEVICE_NOT_PRESENT;
}
} else if (HeciWaitMeReady ()) {
Status = EFI_TIMEOUT;
} else {
while (1) {
if (BytesRead >= *Length || DoneFlag) {
goto check_done;
}
Size = *Length - BytesRead;
Status = HeciReadMsg (
Buffer + (BytesRead >> 2),
&Size,
&MsgHeader
);
if (Status < 0) {
goto out;
}
DoneFlag = MsgHeader >> 31;
if (Size == 0) {
break;
}
BytesRead += Size;
}
if (BytesRead == 0 && DoneFlag == 1) {
*Length = 0;
return EFI_SUCCESS;
}
*Length = 0;
DoneFlag = 1;
Status = HECI_ERROR_DEVICE_NOT_PRESENT;
check_done:
if (DoneFlag == 1) {
goto out;
}
if (DoneFlag) {
*Length = BytesRead;
if (Status < 0) {
continue;
}
return Status;
}
Status = HECI_ERROR_BUFFER_TOO_SMALL;
out:
*Length = 0;
if (Status >= 0) {
return Status;
}
}
break;
}
DEBUG ((EFI_D_ERROR, "[HECI] Receive failed (%r)\n", Status));
return Status;
}
// =========================================================================
// HECI message send core
// =========================================================================
/**
Writes a HECI message to the circular buffer and waits for completion.
**/
EFI_STATUS
EFIAPI
HeciWriteCircularBuf (
UINT32 MsgHeader,
UINT32 Buffer
)
{
HECI_CB_REGISTERS *Regs;
UINT32 SlotCount;
UINT32 SlotAvailable;
UINT32 Timeout;
UINT32 i;
UINT32 SlotData;
UINT32 Retries;
UINT32 HostCsr;
Regs = (HECI_CB_REGISTERS *)HeciGetMbarEx (0);
HeciWriteMsg (Buffer, HIWORD (MsgHeader) & 0x1FF);
Timeout = 12500;
SlotCount = ((*(UINT16 *)(MsgHeader + 2) & 0x1FF) + 3) >> 2;
Retries = 4;
while (1) {
HostCsr = Regs->HostCsr;
SlotAvailable = HIBYTE (HostCsr) -
(UINT8)(BYTE2 (HostCsr) - BYTE1 (HostCsr));
if ((Regs->MeData & 8) == 0 || (HostCsr & 8) == 0 ||
SlotAvailable > HIBYTE (HostCsr)) {
break;
}
if (SlotCount >= SlotAvailable) {
if (Timeout-- == 0) {
return EFI_TIMEOUT;
}
HeciMicrosecondDelay (1000);
} else {
//
// Write the message header and data
//
Regs->HostData = *(volatile UINT32 *)MsgHeader;
for (i = 0; i < SlotCount; i++) {
SlotData = *((volatile UINT32 *)Buffer + i);
Regs->HostData = SlotData;
_mm_pause ();
}
//
// Check if ME-side ready for data
//
if ((Regs->MeData & 8) != 0) {
Regs->HostCsr |= 4; // Set HRA
return EFI_SUCCESS;
}
//
// Not ready - reset and retry
//
if (HeciReset (0)) {
return EFI_TIMEOUT;
}
}
if (Retries-- == 0) {
return HECI_ERROR_DEVICE_NOT_PRESENT;
}
}
if (!HeciReset (0)) {
Retries--;
continue;
}
return EFI_TIMEOUT;
}
// =========================================================================
// HECI send message
// =========================================================================
/**
Sends a HECI message.
**/
EFI_STATUS
EFIAPI
HeciSendCore (
UINT32 Device,
UINT32 HostAddr,
UINT32 MeAddr,
UINT32 MsgLength,
UINT8 HostAddrByte,
UINT8 MeAddrByte
)
{
UINT32 MsgHeader;
Mbar = HeciGetMbarEx (0);
Length &= 0x1FF;
Length = (Length << 16) & 0x1FF0000;
MsgHeader = (MeAddrByte << 8) | HostAddrByte;
return HeciWriteCircularBuf (MsgHeader ^ Length, Buffer);
}
/**
Sends a HECI message with full parameterization.
**/
EFI_STATUS
EFIAPI
HeciSend (
IN UINT32 Device,
IN UINT32 Buffer,
IN UINT32 Length,
IN UINT8 HostAddr,
IN UINT8 MeAddr
)
{
EFI_STATUS Status;
if (Device != 0) {
DEBUG ((EFI_D_ERROR, "HeciDev == HECI1_DEVICE\n"));
}
Status = HeciSendCore (Device, HostAddr, MeAddr, Buffer, Length, HostAddr, MeAddr);
if (Status < 0) {
DEBUG ((EFI_D_ERROR, "[HECI] Send msg %02X -> %02X failed (%r)\n", HostAddr, MeAddr, Status));
}
return Status;
}
// =========================================================================
// HECI send with acknowledgement
// =========================================================================
/**
Sends a HECI message with acknowledgement retry logic.
**/
EFI_STATUS
EFIAPI
HeciSendwAck (
UINT32 Device,
UINT32 HostAddr,
UINT32 MeAddr,
INT32 *Length,
UINT32 Reserved1,
UINT32 Reserved2
)
{
EFI_STATUS Status;
UINT32 Mbar;
INT32 Retries;
INT32 RetryCount;
UINT32 DataSize;
Mbar = HeciGetMbarEx (Device);
if (!Length) {
Length = &Reserved2;
}
//
// Check if circular buffer is empty (write ptr == read ptr)
//
if ((UINT8)BYTE2 (*(volatile UINT32 *)(Mbar + 12)) == BYTE1 (*(volatile UINT32 *)(Mbar + 12))) {
Status = HeciReset (Device);
if (Status < 0) {
return Status;
}
}
RetryCount = 0;
while (1) {
Status = HeciSend (Device, HostAddr, MeAddr, Reserved1, Reserved2);
if (Status < 0) {
break;
}
DataSize = *Length;
Status = HeciReceive (Device, 1, &HostAddr, &DataSize);
Retries = Status;
if (Status < 0) {
DEBUG ((EFI_D_ERROR,
"HECI%d SendwAck: Retrying after %x failed attempt - Status = %r\n",
Device >= 2 ? 3 : Device + 1,
(UINT16)RetryCount++ + 1,
Status));
if ((UINT16)RetryCount < 3) {
continue;
}
}
*Length = (INT32)DataSize;
return Retries;
}
return Status;
}
// =========================================================================
// ME status / mode detection
// =========================================================================
/**
Gets the current ME firmware status.
**/
EFI_STATUS
EFIAPI
HeciGetMeStatus (
UINT32 *MeStatus
)
{
UINT32 MeFs1;
if (MeStatus == NULL) {
return EFI_INVALID_PARAMETER;
}
MeFs1 = *((UINT32 *)HeciPciRead32 (0, HECI_PCI_ME_FS_OFFSET) + 64);
switch (MeFs1 & 0x0F) {
case 1: // Normal
*MeStatus = ME_STATUS_NORMAL;
break;
case 2: // SECBOOT
*MeStatus = ME_STATUS_SECBOOT;
break;
case 5: // M0
if ((MeFs1 & 0xF000) == 0) {
*MeStatus = ME_STATUS_NORMAL;
}
break;
case 6: // DFX
*MeStatus = ME_STATUS_DFX;
break;
case 7: // Disabled
*MeStatus = ME_STATUS_DISABLED;
break;
case 15: // Error
*MeStatus = ME_STATUS_ERROR;
break;
default:
*MeStatus = ME_STATUS_UNDEFINED;
break;
}
if ((MeFs1 & 0x800) != 0) {
*MeStatus |= 0x200;
}
if ((MeFs1 & 0x200) != 0) {
*MeStatus |= 0x80;
}
if ((MeFs1 & 0x1000000) != 0) {
*MeStatus |= 0x100;
}
DEBUG ((EFI_D_INFO, "[HECI] MEFS1 %08X -> MeStatus %X\n", MeFs1, *MeStatus));
return EFI_SUCCESS;
}
/**
Gets the current ME firmware mode.
**/
EFI_STATUS
EFIAPI
HeciGetMeMode (
UINT32 *MeMode
)
{
UINT32 MeFs1;
UINT32 OpMode;
if (MeMode == NULL) {
return EFI_INVALID_PARAMETER;
}
MeFs1 = *((UINT32 *)HeciPciRead32 (22, 0) + 64);
if ((MeFs1 & 0x0F) == 0x0F) {
*MeMode = ME_MODE_DFX;
return EFI_SUCCESS;
}
OpMode = HIWORD (MeFs1) & 0x0F;
if (OpMode == 0) {
*MeMode = ME_MODE_NORMAL;
} else if (OpMode == 2 || OpMode == 3) {
*MeMode = OpMode == 2 ? ME_MODE_SPS_WITH_DEBUG : ME_MODE_SPS_WITHOUT_DEBUG;
} else if (OpMode <= 5) {
*MeMode = ME_MODE_WS;
} else if (OpMode == 15) {
*MeMode = ME_MODE_NORMAL;
} else {
*MeMode = ME_MODE_DISABLED;
}
DEBUG ((EFI_D_INFO, "[HECI] MEFS1: %08X -> MeMode %d\n", MeFs1, *MeMode));
return EFI_SUCCESS;
}
/**
Gets the HECI-2 NM (Normal Mode) firmware status.
**/
EFI_STATUS
EFIAPI
Heci2GetNmStatus (
UINT32 *NmStatus
)
{
UINT32 NmFs;
if (NmStatus == NULL) {
return EFI_INVALID_PARAMETER;
}
*NmStatus = 15;
NmFs = *((UINT32 *)HeciPciRead32 (22, 1) + 64);
*NmStatus = NmFs;
DEBUG ((EFI_D_ERROR, "[HECI2] NMFS: %X\n", NmFs));
return EFI_SUCCESS;
}
// =========================================================================
// DWR (Disable Warm Reset) detection
// =========================================================================
/**
Checks if the system is in DWR (Disable Warm Reset) mode.
DWR is detected via the PCH PMC (Power Management Controller) by reading
a register at PWRM base + offset 300 (bit 15).
**/
BOOLEAN
EFIAPI
HeciIsDwrDetected (
VOID
)
{
UINT32 PwrmBase;
UINT32 PwrmBase;
PwrmBase = 0;
PchPwrmBaseGet (&PwrmBase);
if (PwrmBase == 0) {
return FALSE;
}
return (*(volatile UINT32 *)(PwrmBase + 300) & 0x8000) != 0;
}
// =========================================================================
// ME type detection
// =========================================================================
/**
Gets the HECI ME FS1 value from the HOB.
Reads the ME Firmware Status 1 register from the HOB data.
**/
UINT32
EFIAPI
HeciGetMeFs1FromHob (
VOID
)
{
VOID *FwHob;
UINT32 Fs1;
UINT32 FunNumber;
Fs1 = (UINT32)-1;
FwHob = HeciFindMeFwHob ();
if (FwHob == NULL || *(UINT32 *)((UINT8 *)FwHob + 28) == 0) {
//
// HOB not found or Group[0].FunNumber == 0
//
if (FwHob != NULL) {
DEBUG ((EFI_D_ERROR, "MeFwHob->Group[0].FunNumber == HECI1_DEVICE\n"));
}
DEBUG ((EFI_D_ERROR, "HECI: GetMeFs1FromHob() Can't read correctly MeFwHob info\n"));
return Fs1;
}
Fs1 = *(UINT32 *)((UINT8 *)FwHob + 32);
DEBUG ((EFI_D_INFO, "HECI: GetMeFs1FromHob() returns MEFS1 = %d\n", Fs1));
return Fs1;
}
/**
Determines the ME type (on-board type) from the MEFS register or HOB.
**/
UINT32
EFIAPI
HeciGetOnBoardMeType (
VOID
)
{
UINT32 MeType;
UINT32 MeFs1;
UINT32 OpMode;
VOID *HeciDevice;
MeType = 0;
//
// Check DWR first
//
if (HeciIsDwrDetected ()) {
DEBUG ((EFI_D_INFO, "HECI: GetOnBoardMeType() for DWR flow return Dfx type\n"));
return ME_TYPE_DFX;
}
//
// Read MEFS1 from the HECI device config
//
MeFs1 = *((UINT32 *)HeciPciRead32 (22, 0) + 64);
if (MeFs1 == 0xFFFFFFFF) {
//
// Not available - fall back to HOB
//
MeFs1 = HeciGetMeFs1FromHob ();
DEBUG ((EFI_D_INFO, "HECI: GetOnBoardMeType() reads Hfs info from HOB = %d\n", MeFs1));
}
//
// Decode ME type
//
if ((MeFs1 & 0x0F) != 0x0F) {
if ((MeFs1 & 0x0F) == 4) {
//
// Recovery mode
//
return ME_TYPE_DISABLED;
}
OpMode = HIWORD (MeFs1) & 0x0F;
DEBUG ((EFI_D_INFO, "HECI: MeOperationMode = %d\n", OpMode));
if (OpMode > 1) {
if (OpMode == 2) {
return ME_TYPE_DISABLED;
}
if (OpMode > 5) {
if (OpMode == 7) {
return ME_TYPE_DISABLED;
}
if (OpMode == 15) {
return ME_TYPE_SPS;
}
//
// Unknown type
//
DEBUG ((EFI_D_ERROR, "HECI: ME type not recognized (MEFS1: 0x%08X)\n", MeFs1));
DEBUG ((EFI_D_ERROR, " (MEFS2: 0x%08X)\n",
*((UINT32 *)HeciPciRead32 (22, 0) + 72)));
return 0;
}
}
return ME_TYPE_WS;
}
return ME_TYPE_DFX;
}
/**
Logs the ME type to the debug output.
**/
VOID
EFIAPI
HeciLogMeType (
VOID
)
{
UINT32 MeType;
MeType = HeciGetOnBoardMeType ();
DEBUG ((EFI_D_INFO, "[HECI] %s (MeType is ", "HeciPeimEntryPoint"));
switch (MeType) {
case ME_TYPE_UNDEF:
DEBUG ((EFI_D_INFO, "ME_TYPE_UNDEF"));
break;
case ME_TYPE_SPS:
DEBUG ((EFI_D_INFO, "ME_TYPE_SPS"));
break;
case ME_TYPE_WS:
DEBUG ((EFI_D_INFO, "ME_TYPE_WS"));
break;
case ME_TYPE_DFX:
DEBUG ((EFI_D_INFO, "ME_TYPE_DFX"));
break;
case ME_TYPE_DISABLED:
DEBUG ((EFI_D_INFO, "ME_TYPE_DISABLED"));
break;
default:
DEBUG ((EFI_D_INFO, "UNKNOWN"));
break;
}
DEBUG ((EFI_D_INFO, ")\n"));
}
// =========================================================================
// PCH ACPI base get
// =========================================================================
/**
Gets the PCH ACPI base address.
**/
EFI_STATUS
EFIAPI
PchAcpiBaseGet (
UINT16 *AcpiBase
)
{
UINT32 PmBase;
if (AcpiBase == NULL) {
DEBUG ((EFI_D_ERROR, "PchAcpiBaseGet Error. Invalid pointer.\n"));
return EFI_INVALID_PARAMETER;
}
PmBase = HeciPciRead32 (31, 2);
if (IoRead16 ((UINT16)PmBase) == 0xFFFF) {
return EFI_DEVICE_ERROR;
}
*AcpiBase = IoRead16 ((UINT16)(PmBase + 64)) & 0xFFFC;
return EFI_SUCCESS;
}
/**
Gets the PCH PWRM base address.
**/
EFI_STATUS
EFIAPI
PchPwrmBaseGet (
UINT32 *PwrmBase
)
{
UINT32 PmBase;
if (PwrmBase == NULL) {
DEBUG ((EFI_D_ERROR, "PchPwrmBaseGet Error. Invalid pointer.\n"));
return EFI_INVALID_PARAMETER;
}
PmBase = HeciPciRead32 (31, 2);
if (IoRead16 ((UINT16)PmBase) == 0xFFFF) {
return EFI_DEVICE_ERROR;
}
*PwrmBase = *(volatile UINT32 *)(PmBase + 72) & 0xFFFF0000;
return EFI_SUCCESS;
}
// =========================================================================
// HECI-2 functions
// =========================================================================
/**
Returns the HECI-2 MMIO base address (MBAR).
**/
UINT32
EFIAPI
Heci2GetMbar (
VOID
)
{
UINT32 MbarLow;
UINT64 Mbar;
MbarLow = *((UINT32 *)HeciPciRead32 (22, 1) + 16);
if (MbarLow == 0xFFFFFFFF) {
DEBUG ((EFI_D_ERROR, "[HECI2] ERROR: HECI-2 device disabled\n"));
return 0;
}
Mbar = (UINT64)(MbarLow & 0xFFFFFFF0) |
((UINT64)*((UINT32 *)HeciPciRead32 (22, 1) + 20) << 32);
if (HeciIsSimicsMode () && Mbar) {
DEBUG ((EFI_D_INFO, "[HECI2] Detected 64-bit MBAR in HECI-2 under SIMICS, force 32-bit\n"));
Mbar = 0;
}
if (Mbar == 0) {
Mbar = HECI2_MBAR_DEFAULT;
DEBUG ((EFI_D_INFO, "[HECI2] WARNING: MBAR not programmed, using default 0x%08X%08X\n",
0, (UINT32)Mbar));
*((UINT32 *)HeciPciRead32 (22, 1) + 20) = (UINT32)(Mbar >> 32);
*((UINT32 *)HeciPciRead32 (22, 1) + 16) = (UINT32)Mbar & 0xFFFFFFF0;
}
return (UINT32)(Mbar >> 32);
}
/**
Initializes the HECI-2 interface.
**/
EFI_STATUS
EFIAPI
Heci2Initialize (
VOID
)
{
UINT32 Mbar;
UINT16 VendorId;
VendorId = MemoryRead16 ((UINT16 *)HeciPciRead32 (22, 1));
if (VendorId != 0x8086) {
DEBUG ((EFI_D_ERROR, "[HECI2] ERROR: Device not present\n"));
return EFI_NOT_FOUND;
}
Mbar = Heci2GetMbar ();
DEBUG ((EFI_D_INFO, "[HECI2] Setting HIDM to SCI mode\n"));
//
// Configure HIDM
//
*((UINT8 *)HeciPciRead32 (22, 1) + 160) = 1; // SCI mode
*((UINT8 *)HeciPciRead32 (22, 1) + 160) |= 4; // Memory space enable
*((UINT8 *)HeciPciRead32 (22, 1) + 4) = 6; // BM + Mem
//
// Initialize host CSR
//
*(volatile UINT32 *)(Mbar + 4) &= 0xFFFFFFE1;
*(volatile UINT32 *)(Mbar + 4) |= 2;
return EFI_SUCCESS;
}
// =========================================================================
// SIMICS mode detection
// =========================================================================
/**
Detects whether running under SIMICS simulator.
**/
BOOLEAN
EFIAPI
HeciIsSimicsMode (
VOID
)
{
UINT8 RtcReg;
UINT8 SimicsVal;
//
// Read RTC index 0x4A to check for SIMICS
//
RtcReg = __inbyte (0x70);
__outbyte (0x70, RtcReg & 0x80 | 0x4A);
SimicsVal = __inbyte (0x71);
if ((UINT8)SimicsVal <= 3) {
return (BOOLEAN)(SimicsVal != 0);
}
if (SimicsVal == 0) {
//
// Alternative check through memory-based detection
//
SimicsVal = (*(volatile UINT8 *)0xFDAF0490 & 2) | 1;
}
if (SimicsVal == (UINT8)-1) {
return FALSE;
}
return SimicsVal == 1 ? TRUE : FALSE;
}
// =========================================================================
// Boot Mode read/write
// =========================================================================
/**
Reads the DWR boot mode flag.
**/
UINT8
EFIAPI
HeciReadBootMode (
UINTN Offset
)
{
return *(volatile UINT8 *)(mPciExpressLibBase + Offset);
}
/**
Writes the DWR boot mode flag.
**/
VOID
EFIAPI
HeciWriteBootMode (
UINTN Offset,
UINT8 Value
)
{
*(volatile UINT16 *)(mPciExpressLibBase + Offset) = 1280;
}
/**
DWR boot mode initialization routine.
Configures the boot mode storage to indicate DWR state.
**/
VOID
EFIAPI
HeciInitBootMode (
VOID
)
{
UINTN BootModeAddr;
UINT16 *BootModePtr;
BootModeAddr = GetPeiServices () + 1024064;
BootModePtr = (UINT16 *)BootModeAddr;
if ((BootModeAddr & 1) != 0) {
DEBUG ((EFI_D_ERROR, "(Address & 1) == 0\n"));
}
*BootModePtr = 1280;
}
// =========================================================================
// PCI Express config read (byte)
// =========================================================================
/**
Read a PCI config byte from the bus:device:function at offset.
**/
UINT8
EFIAPI
HeciPciRead8 (
UINT8 Bus,
UINT8 Device,
UINT8 Function,
UINT8 Offset
)
{
UINTN Address;
Address = ((UINTN)Bus << 0x18) | ((UINTN)Device << 0x10) |
((UINTN)Function << 8) | Offset;
return *(volatile UINT8 *)(mPciExpressLibBase + Address);
}
/**
Read a PCI config dword from bus:dev:func, returning as UINT32.
**/
UINT32
EFIAPI
HeciPciCfgRead (
UINT8 Bus,
UINT8 Device,
UINT8 Function,
UINT8 Offset
)
{
return HeciPciRead32 (Address);
}
// =========================================================================
// Module Entry Point
// =========================================================================
/**
HECI PEIM Entry Point.
This is the main entry point called by the PEI core. It determines the
ME type (SPS/WS/DFX), initializes HECI-1 (and optionally HECI-2), and
returns the appropriate status code.
Detection flow:
1. Check DWR state - if DWR detected, unload
2. Determine ME type from MEFS1/HOB
3. For SPS/WS type: initialize HECI-1, reset HECI-1, clear boot mode flags
4. For DFX type: skip and return success
**/
EFI_STATUS
EFIAPI
HeciPeimEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
UINT32 MeType;
//
// Initialize boot mode tracking
//
HeciInitBootMode ();
//
// Check for DWR (Disable Warm Reset)
//
if (HeciIsDwrDetected ()) {
DEBUG ((EFI_D_ERROR, "[HECI] ERROR: HeciPeimEntryPoint(): DWR detected - unload...\n"));
return EFI_UNSUPPORTED;
}
//
// Determine ME type for the current platform
//
MeType = HeciGetOnBoardMeType ();
HeciLogMeType ();
switch (MeType) {
case ME_TYPE_UNDEF:
case ME_TYPE_WS:
case ME_TYPE_SPS:
//
// Initialize HECI-1 interface
//
HeciPciCfgRead (0, HECI_PCI_CMD_REG_OFFSET) + 20 = 0;
HeciPciCfgRead (0, HECI_PCI_MBAR_OFFSET) + 16 = 0;
default:
return EFI_SUCCESS;
}
}