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