Newer
Older
AMI-Aptio-BIOS-Reversed / LenovoServerPkg / POSTStatus / MultiSkuDistinctionPei / MultiSkuDistinctionPei.c
@Ajax Dong Ajax Dong 2 days ago 17 KB Restructure the repo
/** @file
  MultiSkuDistinctionPei.c -- Multi-SKU Distinction PEI Module

  This PEIM detects the platform SKU (Stock Keeping Unit) by:
  1. Reading CMOS register 0x4A for a SKU identifier
  2. Looking up a GUID-extended HOB for CrystalRidge SKU data
  3. Installing a PPI via PeiServices to notify other PEIMs of the SKU identity

  The module determines whether the platform runs a "default" SKU or a
  specific SKU variant, and installs the appropriate PPI so downstream
  PEIMs (e.g., CrystalRidge-related) can adapt their behavior accordingly.

  Copyright (c) Lenovo. All rights reserved.
  SPDX-License-Identifier: BSD-2-Clause-Patent
**/

#include "MultiSkuDistinctionPei.h"

//=============================================================================
// External GUID definitions (defined in .data section)
//=============================================================================

EFI_GUID gSkuMatchGuid        = SKU_MATCH_GUID;
EFI_GUID gSkuNotificationGuid = SKU_NOTIFICATION_GUID;
EFI_GUID gSkuPeiServiceGuid   = SKU_PPI_SERVICE_GUID;

//=============================================================================
// PPI Descriptors (defined in .data section)
//=============================================================================

//
// Default SKU PPI descriptor -- installed when SKU sentinel is NOT present.
// Contains the notification GUID gSkuNotificationGuid.
//
EFI_PEI_PPI_DESCRIPTOR mDefaultSkuPpiDescriptor = {
  PPI_DESCRIPTOR_FLAGS,
  &gSkuNotificationGuid,
  NULL
};

//
// Active SKU PPI descriptor -- installed when SKU sentinel == 0x55.
// Contains a notification/event to trigger further SKU processing.
//
EFI_PEI_PPI_DESCRIPTOR mActiveSkuPpiDescriptor = {
  EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST,
  NULL,
  NULL
};

//=============================================================================
// Library Helper Functions
//=============================================================================

/**
  Fills a buffer with a byte value.
  Equivalent to memset().

  @param[out] Buffer  Pointer to buffer to fill.
  @param[in]  Count   Number of bytes to fill.
  @param[in]  Value   Byte value to fill with.

  @return Pointer to Buffer.
**/
VOID *
EFIAPI
SetMem (
  OUT VOID   *Buffer,
  IN  UINTN  Count,
  IN  UINT8  Value
  )
{
  return memset (Buffer, Value, Count);
}

/**
  Fills a buffer with alternating 32-bit values.
  Writes pairs of (ValueA, ValueB) into the buffer in reverse index order.

  @param[out] Buffer  Pointer to buffer.
  @param[in]  Count   Number of pairs to write.
  @param[in]  ValueA  First value of each pair.
  @param[in]  ValueB  Second value of each pair.

  @return Pointer to Buffer.
**/
INT32 *
EFIAPI
SetMem32_Pair (
  OUT INT32  *Buffer,
  IN  INT32  Count,
  IN  INT32  ValueA,
  IN  INT32  ValueB
  )
{
  do {
    Buffer[2 * Count - 2] = ValueA;
    Buffer[2 * Count - 1] = ValueB;
    Count--;
  } while (Count > 0);

  return Buffer;
}

/**
  Fills a buffer with a 32-bit value.
  Equivalent to memset32().

  @param[out] Buffer  Pointer to buffer.
  @param[in]  Count   Number of 32-bit values to write.
  @param[in]  Value   32-bit value to write.

  @return Pointer to Buffer.
**/
VOID *
EFIAPI
SetMem32 (
  OUT VOID   *Buffer,
  IN  UINTN  Count,
  IN  UINT32 Value
  )
{
  return memset32 (Buffer, Value, Count);
}

/**
  Copies memory between potentially overlapping buffers.
  Equivalent to memmove().

  @param[out] Destination  Pointer to destination buffer.
  @param[in]  Source       Pointer to source buffer.
  @param[in]  Count        Number of bytes to copy.

  @return Pointer to Destination.
**/
VOID *
EFIAPI
CopyMem (
  OUT VOID       *Destination,
  IN  CONST VOID *Source,
  IN  UINTN      Count
  )
{
  return memmove (Destination, Source, Count);
}

/**
  Reads a 64-bit unaligned value from memory.

  @param[in]  Buffer  Pointer to the potentially unaligned 64-bit value.

  @return The 64-bit value read from Buffer.
**/
UINT64
EFIAPI
ReadUnaligned64 (
  IN CONST VOID  *Buffer
  )
{
  return *(UINT64 *)Buffer;
}

/**
  Reads the Interrupt Descriptor Table Register (IDTR) via SIDT instruction.

  @param[out]  Idtr  Pointer to an IA32_DESCRIPTOR structure to receive IDTR.
**/
VOID
EFIAPI
ReadIdtr (
  OUT IA32_DESCRIPTOR  *Idtr
  )
{
  __sidt (Idtr);
}

//=============================================================================
// Debug Services
//=============================================================================

/**
  Gets the debug print instance from PEI services.
  Looks up the debug PPI via PeiServices->LocatePpi().

  @return Pointer to the debug print interface, or NULL if not available.
**/
VOID *
EFIAPI
GetDebugInstance (
  VOID
  )
{
  EFI_STATUS           Status;
  VOID                 *Instance;
  EFI_PEI_SERVICES     **PeiServices;

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

  return NULL;
}

/**
  Debug print with level filter.
  Calls the debug print function from the debug instance if the error level
  matches the current debug mask.

  @param[in]  ErrorLevel  Debug error level.
  @param[in]  Format      Format string.
  @param[in]  ...         Variable arguments.
**/
VOID
EFIAPI
DebugPrint (
  IN  UINTN       ErrorLevel,
  IN  CONST CHAR8 *Format,
  ...
  )
{
  VA_LIST            Marker;
  INT32              CurrentMask;
  INT32              (*DebugFunc)(UINTN, CONST CHAR8 *, VA_LIST);
  VOID               *DebugInstance;

  DebugInstance = GetDebugInstance ();
  if (DebugInstance == NULL) {
    return;
  }

  DebugFunc = (INT32 (*)(UINTN, CONST CHAR8 *, VA_LIST))DebugInstance;

  CurrentMask = GetSkuFromCmos ();
  if ((CurrentMask & ErrorLevel) != 0) {
    VA_START (Marker, Format);
    DebugFunc (ErrorLevel, Format, Marker);
    VA_END (Marker);
  }
}

/**
  PEI Services assertion/notification handler.
  Calls the assert handler from the debug instance with file/line information.

  @param[in]  FileName     Source file name.
  @param[in]  LineNumber   Line number.
  @param[in]  Description  Assert description.
**/
VOID
EFIAPI
PeiServicesNotify (
  IN CONST CHAR8  *FileName,
  IN UINTN        LineNumber,
  IN CONST CHAR8  *Description
  )
{
  VOID   *DebugInstance;

  DebugInstance = GetDebugInstance ();
  if (DebugInstance != NULL) {
    ((VOID (*)(CONST CHAR8 *, UINTN, CONST CHAR8 *))DebugInstance)(
      FileName,
      LineNumber,
      Description
      );
  }
}

//=============================================================================
// PEI Services Access
//=============================================================================

/**
  Gets the PEI Services pointer via the IDT-based PEI Services Table Pointer.

  @return Pointer to the EFI_PEI_SERVICES pointer.
**/
EFI_PEI_SERVICES **
EFIAPI
GetPeiServices (
  VOID
  )
{
  IA32_DESCRIPTOR     Idtr;
  EFI_PEI_SERVICES    **PeiServices;

  ReadIdtr (&Idtr);
  //
  // The PEI Services Table pointer is stored at (IDT_Base - 4).
  // This is a standard technique used by PeiServicesTablePointerLibIdt.
  //
  PeiServices = *(EFI_PEI_SERVICES ***)(Idtr.Base - 4);
  if (PeiServices == NULL) {
    PeiServicesNotify (
      "e:\\hs\\MdePkg\\Library\\PeiServicesTablePointerLibIdt\\PeiServicesTablePointer.c",
      48,
      "PeiServices != ((void *) 0)"
      );
  }

  return PeiServices;
}

//=============================================================================
// HOB Services
//=============================================================================

/**
  Retrieves the HOB list from PEI services.

  @return Pointer to the start of the HOB list.
**/
VOID *
EFIAPI
GetHobList (
  VOID
  )
{
  EFI_STATUS        Status;
  EFI_PEI_SERVICES  **PeiServices;
  VOID              *HobList;

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

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

  return HobList;
}

/**
  Walks the HOB list to find the first HOB of a given type.

  @param[in]  HobStart  Pointer to start of HOB list.
  @param[in]  Type      HOB type to search for.

  @return Pointer to matching HOB, or NULL if not found.
**/
EFI_HOB_GENERIC_HEADER *
EFIAPI
FindHobByType (
  IN  CONST VOID           *HobStart,
  IN  UINT16               Type
  )
{
  EFI_HOB_GENERIC_HEADER  *Hob;

  Hob = (EFI_HOB_GENERIC_HEADER *)HobStart;
  if (Hob == NULL) {
    PeiServicesNotify (
      "e:\\hs\\MdePkg\\Library\\PeiHobLib\\HobLib.c",
      82,
      "HobStart != ((void *) 0)"
      );
  }

  while (Hob != NULL) {
    if (Hob->HobType == EFI_HOB_TYPE_END_OF_HOB_LIST) {
      return NULL;
    }
    if (Hob->HobType == Type) {
      return Hob;
    }
    Hob = (EFI_HOB_GENERIC_HEADER *)((UINT8 *)Hob + Hob->HobLength);
  }

  return NULL;
}

/**
  Compares two GUIDs by reading them as unaligned 64-bit values.

  @param[in]  Guid1  Pointer to first GUID.
  @param[in]  Guid2  Pointer to second GUID.

  @return TRUE if GUIDs match, FALSE otherwise.
**/
BOOLEAN
EFIAPI
IsGuidMatch (
  IN CONST EFI_GUID  *Guid1,
  IN CONST EFI_GUID  *Guid2
  )
{
  UINT64  Data1A, Data1B;
  UINT64  Data2A, Data2B;

  Data1A = ReadUnaligned64 (Guid1);
  Data2A = ReadUnaligned64 ((CONST VOID *)((UINTN)Guid2 + 0));
  Data1B = ReadUnaligned64 ((CONST VOID *)((UINTN)Guid1 + 8));
  Data2B = ReadUnaligned64 ((CONST VOID *)((UINTN)Guid2 + 8));

  return (Data1A == Data2A) && (Data1B == Data2B);
}

//=============================================================================
// SKU-Specific Functions
//=============================================================================

/**
  Reads SKU identifier from CMOS register 0x4A.

  If the CMOS value is <= 3, returns it directly as a small SKU ID.
  If the CMOS value is 0, falls back to reading hardware register at
  0xFEDAF0490 and extracting bit 1.

  @return SKU identifier:
          0 = SKU not found
          1 = valid SKU found
          0xFFFFFFFF (EFI_INVALID_PARAMETER-like) = out of range
          0xFFFFFFC6 (EFI_NOT_FOUND-like) = non-matching value
**/
INT32
EFIAPI
GetSkuFromCmos (
  VOID
  )
{
  UINT8   SaveByte;
  UINT8   SkuValue;
  INT32   Result;

  //
  // Read current CMOS index register, preserve NMI bit
  //
  SaveByte = IoRead8 (CMOS_INDEX_PORT);
  IoWrite8 (CMOS_INDEX_PORT, (SaveByte & CMOS_NMI_BIT) | CMOS_REG_SKU);
  SkuValue = IoRead8 (CMOS_DATA_PORT);

  if (SkuValue <= SKU_CMOS_VALUE_MAX) {
    //
    // Valid small SKU value (0, 1, 2, or 3)
    //
    if (SkuValue == 0) {
      return 0;                     // SKU not found
    }
    return 1;                       // Valid SKU found
  }

  //
  // Value > 3: check if zero
  //
  if (SkuValue == 0) {
    //
    // Fallback: read hardware register at fixed address
    //
    UINT32  RegValue;
    RegValue  = MmioRead32 (0xFEDAF0490);
    SkuValue  = (RegValue & 2) | 1;
    if (SkuValue == 0) {
      return 0;
    }
    return (SkuValue == 0xFF) ? 0 : 1;
  }

  //
  // Non-zero value > 3: return error codes
  //
  if (SkuValue == 0xFF) {
    return 0;                       // SKU invalid
  }

  return (SkuValue != 1) ? EFI_INVALID_PARAMETER : EFI_NOT_FOUND;
}

/**
  Locates the CrystalRidge SKU GUID HOB in the HOB list.

  Walks the HOB list looking for a GUID-extended HOB (type 4) whose
  GUID matches gSkuMatchGuid.

  @return Pointer to the matching HOB, or NULL if not found.
**/
EFI_HOB_GENERIC_HEADER *
EFIAPI
FindMatchingHob (
  VOID
  )
{
  VOID                   *HobList;
  EFI_HOB_GENERIC_HEADER *Hob;

  HobList = GetHobList ();
  Hob     = NULL;

  while (TRUE) {
    Hob = FindHobByType (HobList, EFI_HOB_TYPE_GUID_EXTENSION);
    if (Hob == NULL || IsGuidMatch (&((EFI_HOB_GUID_TYPE *)Hob)->Name, &gSkuMatchGuid)) {
      break;
    }
    //
    // Advance past this HOB to continue searching
    //
    HobList = (VOID *)((UINT8 *)Hob + Hob->HobLength);
  }

  return Hob;
}

/**
  Initializes SKU information by reading from the CrystalRidge HOB.

  Locates the GUID-extended HOB matching gSkuMatchGuid in the HOB list,
  extracts the 3 SKU identifier bytes at offsets 48, 49, 50 within
  the HOB data, and stores them in the caller-provided buffer at
  offsets 24, 25, 26.

  @param[out]  SkuBuffer  Buffer to receive SKU bytes.
                           Must be at least SKU_BYTE_COUNT + 24 bytes.

  @retval EFI_SUCCESS             SKU info read successfully.
  @retval EFI_INVALID_PARAMETER   SkuBuffer is NULL.
  @retval EFI_NOT_FOUND           No matching SKU HOB found.
**/
EFI_STATUS
EFIAPI
SkuInit (
  OUT UINT8  *SkuBuffer
  )
{
  EFI_HOB_GENERIC_HEADER  *Hob;

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

  Hob = FindMatchingHob ();
  if (Hob == NULL) {
    return EFI_NOT_FOUND;
  }

  //
  // Extract SKU identifier bytes from HOB data area.
  // The CrystalRidge HOB stores 3 SKU identification bytes
  // at fixed offsets 48, 49, 50 from the HOB start.
  //
  SkuBuffer[24] = ((UINT8 *)Hob)[SKU_INFO_OFFSET + 0];
  SkuBuffer[25] = ((UINT8 *)Hob)[SKU_INFO_OFFSET + 1];
  SkuBuffer[26] = ((UINT8 *)Hob)[SKU_INFO_OFFSET + 2];

  return EFI_SUCCESS;
}

/**
  Installs the default CrystalRidge SKU PPI.

  Called from _ModuleEntryPoint when the SKU sentinel byte is NOT 0x55.
  The function attempts SkuInit(); if that succeeds and the SKU buffer
  does NOT contain the sentinel 0x55 at offset 24, it installs the
  default PPI descriptor (mDefaultSkuPpiDescriptor) via PeiServices
  to signal the default SKU path.

  @param[in]  PeiServices  Pointer to EFI_PEI_SERVICES pointer.

  @retval EFI_SUCCESS  PPI installed or already configured.
  @retval Others       Error from SkuInit or PeiServices->NotifyPpi.
**/
EFI_STATUS
EFIAPI
InstallSkuPpi (
  IN EFI_PEI_SERVICES  **PeiServices
  )
{
  EFI_STATUS  Status;
  UINT8       SkuBuffer[28];

  DebugPrint (64, "InstallSkuPpi is called\n");

  Status = SkuInit (SkuBuffer);
  if (EFI_ERROR (Status)) {
    return EFI_ALREADY_STARTED;   // EFI_ALREADY_STARTED = 0x80000000 | 6
  }

  //
  // If SKU buffer does NOT contain the sentinel 0x55 at byte 24,
  // install the "default" SKU PPI to signal downstream PEIMs.
  //
  if (SkuBuffer[24] != SKU_VALID_SENTINEL) {
    (*PeiServices)->NotifyPpi (
                      PeiServices,
                      &mDefaultSkuPpiDescriptor
                      );
  }

  return EFI_SUCCESS;
}

/**
  Entry point stub that delegates to InstallSkuPpi.

  This function is called as a PPI notification callback when the
  gSkuNotificationGuid PPI is installed. It simply re-invokes
  InstallSkuPpi to re-evaluate the SKU state.

  @param[in]  PeiServices  Pointer to EFI_PEI_SERVICES pointer.

  @retval EFI_SUCCESS  Always returns success.
**/
EFI_STATUS
EFIAPI
EntryPointStub (
  IN EFI_PEI_SERVICES  **PeiServices
  )
{
  InstallSkuPpi (PeiServices);
  return EFI_SUCCESS;
}

//=============================================================================
// Module Entry Point
//=============================================================================

/**
  PEI Module Entry Point.

  This is the main entry point for MultiSkuDistinctionPei.
  Flow:
  1. Allocate a boot services buffer (type 4, size 52) via
     PeiServices->AllocatePages()
  2. Write platform revision/sku configuration data into the buffer
  3. Call SkuInit() to read SKU from HOB
  4. If SKU sentinel byte (buffer[24]) == 0x55:
     - Install the active SKU PPI via PeiServices->InstallPpi()
     This means a CrystalRidge HOB with valid SKU data was found,
     and the SKU is already configured.
  5. Otherwise:
     - Call InstallSkuPpi(), which installs the default SKU PPI
     and sets up a notification for when SKU data becomes available.

  @param[in]  FileHandle   PEI file handle.
  @param[in]  PeiServices  Pointer to EFI_PEI_SERVICES pointer.

  @retval EFI_SUCCESS           Module initialized successfully.
  @retval EFI_OUT_OF_RESOURCES  Failed to allocate boot services buffer.
**/
EFI_STATUS
EFIAPI
_ModuleEntryPoint (
  IN EFI_PEI_FILE_HANDLE   FileHandle,
  IN EFI_PEI_SERVICES      **PeiServices
  )
{
  EFI_STATUS    Status;
  VOID          *Buffer;
  UINT8         SkuBuffer[28];
  EFI_PEI_SERVICES  **LocalPeiServices;

  LocalPeiServices = PeiServices;

  //
  // Allocate boot services data buffer
  //
  Status = (*PeiServices)->AllocatePages (
                             PeiServices,
                             EfiBootServicesData,
                             52,
                             &Buffer
                             );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // Write revision/sku data into the allocated buffer
  // (buffer initialized with revision values and sentinel)
  //
  // *((UINT32 *)Buffer + 0) = Revision;
  // *((UINT32 *)Buffer + 1) = unk_FFE6BB18;
  // *((UINT32 *)Buffer + 2) = Revision_0;
  // *((UINT32 *)Buffer + 3) = unk_FFE6BB20;
  // *((UINT8 *)Buffer  + 16) = 0x55;  // sentinel
  // *((UINT8 *)Buffer  + 17) = 0x00;
  // *((UINT8 *)Buffer  + 18) = 0x00;

  //
  // Read SKU from HOB
  //
  Status = SkuInit (SkuBuffer);
  if (!EFI_ERROR (Status)) {
    //
    // If SKU sentinel byte == 0x55, install active PPI directly
    //
    if (SkuBuffer[24] == SKU_VALID_SENTINEL) {
      return (*LocalPeiServices)->InstallPpi (
                                   LocalPeiServices,
                                   &mActiveSkuPpiDescriptor
                                   );
    } else {
      //
      // Otherwise, install default SKU PPI path
      //
      return InstallSkuPpi (LocalPeiServices);
    }
  }

  return Status;
}