/** @file
SlotDataUpdateDxeNeonCityEPECB - PCIe Slot Data Update Driver for NeonCity EP EC B platform.
This DXE driver registers PCIe slot configuration data for the NeonCity EP EC B
platform via the Intel UBA (Universal BIOS Aggregator) protocol. It provides
slot-specific data tables ("PSLT" - Platform Slot Table entries) defining PCIe
slot identifiers for the platform's physical slot layout.
This is one of many platform-type-specific slot data update drivers that are
part of the Intel Purley UBA framework. Each platform type (e.g., NeonCity EP EC B)
provides its own slot mapping table, and the UBA protocol aggregates them.
Source: PurleyRpPkg/Uba/UbaMain/Dxe/TypeNeonCityEPECB/SlotDataUpdateDxe/
Build: VS2015 x64 DEBUG on Intel Purley (Xeon Scalable) platform
Copyright (c) 2017, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include <Uefi.h>
#include <Library/DebugLib.h>
#include <Library/IoLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/BaseMemoryLib.h>
#include <Protocol/UbaSlotDataUpdate.h>
//
// ---------------------------------------------------------------------------
// Local GUIDs
// ---------------------------------------------------------------------------
//
//
// UBA Debug Protocol GUID used by assertion handler.
// {36232936-0E76-31C8-A13A-3AF2FC1C3932}
//
EFI_GUID gUbaDebugProtocolGuid =
{ 0x36232936, 0x0E76, 0x31C8, { 0xA1, 0x3A, 0x3A, 0xF2, 0xFC, 0x1C, 0x39, 0x32 } };
//
// UBA Slot Data Update Protocol GUID.
// {E03E0D46-5263-4845-B0A4-58D57B3177E2}
//
EFI_GUID gUbaSlotDataUpdateProtocolGuid =
{ 0xE03E0D46, 0x5263, 0x4845, { 0xB0, 0xA4, 0x58, 0xD5, 0x7B, 0x31, 0x77, 0xE2 } };
//
// Platform Slot Config GUID 1 (maps to 40-byte PSLT).
// {226763AE-972C-4E3C-80D1-73B25E8CBBA3}
//
EFI_GUID gSlotConfigGuid40 =
{ 0x226763AE, 0x972C, 0x4E3C, { 0x80, 0xD1, 0x73, 0xB2, 0x5E, 0x8C, 0xBB, 0xA3 } };
//
// Platform Slot Config GUID 2 (maps to 32-byte PSLT).
// {B93613E1-48F0-4B32-B3A8-4FEDFC7C1365}
//
EFI_GUID gSlotConfigGuid32 =
{ 0xB93613E1, 0x48F0, 0x4B32, { 0xB3, 0xA8, 0x4F, 0xED, 0xFC, 0x7C, 0x13, 0x65 } };
//
// HOB List GUID (defined in MdePkg).
// gEfiHobListGuid = {0x7739F24C, 0x93D7, 0x11D4, {0x9A, 0x3A, 0x00, 0x90, 0x27, 0x3F, 0xC1, 0x4D}}
//
EFI_GUID gEfiHobListGuid =
{ 0x7739F24C, 0x93D7, 0x11D4, { 0x9A, 0x3A, 0x00, 0x90, 0x27, 0x3F, 0xC1, 0x4D } };
//
// ---------------------------------------------------------------------------
// Global Variables
// ---------------------------------------------------------------------------
//
EFI_SYSTEM_TABLE *gSystemTable = NULL;
EFI_BOOT_SERVICES *gBootServices = NULL;
EFI_RUNTIME_SERVICES *gRuntimeServices = NULL;
EFI_HANDLE gImageHandle = NULL;
//
// Cached UBA debug protocol pointer (for ASSERT output).
//
VOID *gUbaDebugProtocol = NULL;
//
// Cached HOB list pointer.
//
VOID *gHobList = NULL;
//
// ---------------------------------------------------------------------------
// Static data: Platform Slot Table (PSLT) entries
// ---------------------------------------------------------------------------
//
// PSLT entry 1 (40 bytes): Dual-slot configuration.
// SlotIdentifier = 0x4B0 -> SlotDataAccessor1
// SlotIdentifier2 = 0x4B4 -> SlotDataAccessor2
//
GLOBAL_REMOVE_IF_UNREFERENCED
UINT64 mSlotConfig40[] = {
SIGNATURE_64 ('P', 'S', 'L', 'T', 1, 0, 0, 0), // +0x00: "PSLT" + version (8 bytes)
0, // +0x08: Reserved (8 bytes)
0x4B0, // +0x10: SlotIdentifier1 (pointer to SlotDataAccessor1)
0, // +0x18: Reserved / unused
0x4B4 // +0x20: SlotIdentifier2 (pointer to SlotDataAccessor2)
};
//
// PSLT entry 2 (32 bytes): Single-slot configuration.
// SlotIdentifier = 0x4B0 -> SlotDataAccessor1
//
GLOBAL_REMOVE_IF_UNREFERENCED
UINT64 mSlotConfig32[] = {
SIGNATURE_64 ('P', 'S', 'L', 'T', 1, 0, 0, 0), // +0x00: "PSLT" + version (8 bytes)
0, // +0x08: Reserved (8 bytes)
0x4B0, // +0x10: SlotIdentifier (pointer to SlotDataAccessor1)
0 // +0x18: Reserved / unused
};
//
// ---------------------------------------------------------------------------
// Function Prototypes (forward declarations)
// ---------------------------------------------------------------------------
//
VOID *LocateDebugProtocol (VOID);
UINT64 ReadUnaligned64 (UINT64 *Buffer);
BOOLEAN CompareGuidValue (UINT64 *Guid1, UINT64 *Guid2);
VOID *LocateHobList (VOID);
UINTN PlatformSlotDataInit (UINTN Flag, CHAR8 *PlatformString);
VOID UbaDebugAssert (CHAR8 *FileName, UINTN LineNumber, CHAR8 *Description);
//
// ---------------------------------------------------------------------------
// Slot Data Accessor Functions (referenced by PSLT entries)
// ---------------------------------------------------------------------------
//
/**
Slot data accessor function 1.
Returns the first parameter unchanged. This is a pass-through callback
used as a slot identifier within the UBA framework. The address of this
function (0x4B0) is stored in PSLT entries as a slot data reference.
@param[in] Value Input value to pass through.
@return The same value as the input parameter.
**/
UINT8
SlotDataAccessor1 (
UINT8 Value
)
{
return Value;
}
/**
Slot data accessor function 2.
Returns the second parameter unchanged. This is a pass-through callback
used as a slot identifier within the UBA framework. The address of this
function (0x4B4) is stored in PSLT entries as a secondary slot reference.
@param[in] Value First parameter (unused).
@param[in] Value2 Second parameter to pass through.
@return The value of the second parameter.
**/
UINT8
SlotDataAccessor2 (
UINT64 Value,
UINT8 Value2
)
{
return Value2;
}
//
// ---------------------------------------------------------------------------
// Helper Functions
// ---------------------------------------------------------------------------
//
/**
Reads a UINT64 value from the specified buffer with a NULL pointer check.
Performs an ASSERT that the buffer is not NULL before reading.
@param[in] Buffer Pointer to the memory to read. Must not be NULL.
@return The UINT64 value at the specified memory location.
**/
UINT64
ReadUnaligned64 (
UINT64 *Buffer
)
{
ASSERT (Buffer != NULL);
return *Buffer;
}
/**
Compares two 16-byte GUID values by comparing them as two 8-byte halves.
This avoids potential alignment faults when GUIDs are not naturally aligned.
@param[in] Guid1 Pointer to first 16-byte GUID.
@param[in] Guid2 Pointer to second 16-byte GUID.
@retval TRUE Both halves match.
@retval FALSE GUIDs differ.
**/
BOOLEAN
CompareGuidValue (
UINT64 *Guid1,
UINT64 *Guid2
)
{
return (ReadUnaligned64 (Guid1) == ReadUnaligned64 (Guid2)) &&
(ReadUnaligned64 (Guid1 + 1) == ReadUnaligned64 (Guid2 + 1));
}
//
// ---------------------------------------------------------------------------
// UBA Debug Protocol Support
// ---------------------------------------------------------------------------
//
/**
Locates and caches the UBA debug protocol.
This function is called by the assertion handler to find the
UBA debug protocol (identified by gUbaDebugProtocolGuid).
The protocol is cached after first lookup.
Uses an interrupt-safe pattern: temporarily raises TPL to HIGH_LEVEL
and restores to serialize initialization.
@return Pointer to the UBA debug protocol, or NULL if not found.
**/
VOID *
LocateDebugProtocol (
VOID
)
{
EFI_STATUS Status;
EFI_TPL OldTpl;
if (gUbaDebugProtocol == NULL) {
//
// Raise TPL to HIGH_LEVEL to serialize access during protocol lookup.
//
OldTpl = gBootServices->RaiseTPL (TPL_HIGH_LEVEL);
gBootServices->RestoreTPL (OldTpl);
if (OldTpl <= TPL_NOTIFY) {
//
// Locate the UBA debug protocol for assertion output.
//
Status = gBootServices->LocateProtocol (
&gUbaDebugProtocolGuid,
NULL,
&gUbaDebugProtocol
);
if (EFI_ERROR (Status)) {
gUbaDebugProtocol = NULL;
}
}
}
return gUbaDebugProtocol;
}
/**
Debug assertion handler using the UBA debug protocol.
Reports assertion failures through the UBA debug protocol if available.
Falls back silently if the protocol has not been located.
@param[in] FileName Source file name where the assertion occurred.
@param[in] LineNumber Line number of the assertion.
@param[in] Description Assertion failure description string.
**/
VOID
UbaDebugAssert (
CHAR8 *FileName,
UINTN LineNumber,
CHAR8 *Description
)
{
VOID *Protocol;
Protocol = LocateDebugProtocol ();
if (Protocol != NULL) {
//
// Call the assertion function at protocol + 0x08 (index 1).
//
((VOID (EFIAPI *)(CHAR8 *, UINTN, CHAR8 *))(*(UINT64 **)Protocol)[1])(
FileName,
LineNumber,
Description
);
}
}
//
// ---------------------------------------------------------------------------
// HOB List Support
// ---------------------------------------------------------------------------
//
/**
Locates the HOB (Hand-Off Block) list from the UEFI System Table.
Searches through the system table's configuration table entries for
gEfiHobListGuid. The HOB list is created during PEI and consumed in DXE.
@return Pointer to the HOB list, or NULL if not found.
**/
VOID *
LocateHobList (
VOID
)
{
UINTN Index;
EFI_CONFIGURATION_TABLE *Entry;
//
// Return early if there are no configuration table entries.
//
if (gSystemTable->NumberOfTableEntries == 0) {
DEBUG ((DEBUG_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", EFI_NOT_FOUND));
ASSERT (!EFI_ERROR (EFI_NOT_FOUND));
return NULL;
}
//
// Iterate through configuration table entries to find gEfiHobListGuid.
//
for (Index = 0; Index < gSystemTable->NumberOfTableEntries; Index++) {
Entry = &gSystemTable->ConfigurationTable[Index];
if (CompareGuidValue (
(UINT64 *)&Entry->VendorGuid,
(UINT64 *)&gEfiHobListGuid
)) {
//
// Found the HOB list - cache and return the pointer.
//
gHobList = Entry->VendorTable;
return gHobList;
}
}
//
// HOB list not found - report error and assert.
//
DEBUG ((DEBUG_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", EFI_NOT_FOUND));
ASSERT (!EFI_ERROR (EFI_NOT_FOUND));
ASSERT (gHobList != NULL);
return NULL;
}
//
// ---------------------------------------------------------------------------
// Platform Detection & Slot Data Initialization
// ---------------------------------------------------------------------------
//
/**
Platform-specific slot data initialization.
Reads CMOS register 0x4B to determine the platform configuration type,
then dispatches the slot data update to the UBA protocol handler.
CMOS register 0x4B encoding:
Bits[1:0]:
0 = Default/uninitialized - reads HW strap from MMIO 0xFDAF0490
1 = Platform type A (maps to flag 0x80000004)
2 = Platform type B (maps to flag 0x80000006)
3+ = Platform type C (maps to flag 0x80000006)
@param[in] Flag Platform type filter (e.g., 0x80000000).
@param[in] PlatformString Platform identifier string for debug output.
@retval Non-zero if slot data was initialized by the UBA protocol.
@retval 0 if the platform type does not match or protocol unavailable.
**/
UINTN
PlatformSlotDataInit (
UINTN Flag,
CHAR8 *PlatformString
)
{
UINTN Result;
UINT8 NmiSave;
UINT8 CmosValue;
Result = 0;
if (LocateDebugProtocol () != NULL) {
//
// Save current NMI state and select CMOS register 0x4B.
// Register 0x4B holds platform configuration data.
//
NmiSave = IoRead8 (0x70);
IoWrite8 (0x70, (NmiSave & 0x80) | 0x4B);
CmosValue = IoRead8 (0x71);
//
// Handle special CMOS values:
// > 3 and != 0: treat as extended type
// = 0: read hardware strap register for actual type
//
if (CmosValue > 3) {
if (CmosValue == 0) {
//
// Read hardware strap from fixed MMIO address.
// Bit 1 selects platform type; OR with 1 for default.
//
CmosValue = (MmioRead32 (0xFDAF0490) & 2) | 1;
}
}
//
// Map CMOS value to platform type flags:
// Value 1 -> Flag | 0x00000004
// Other -> Flag | 0x00000006
//
if ((CmosValue - 1) <= 0xFD) {
if (CmosValue == 1) {
Flag |= 0x00000004;
} else {
Flag |= 0x00000006;
}
//
// Dispatch to UBA protocol slot data init handler (index 0 = first function pointer).
// The protocol's first function handles platform-specific init dispatch:
// (*Protocol)(Flag, PlatformString, VarArgs)
//
Result = ((UINTN (EFIAPI *)(UINTN, CHAR8 *, UINTN))(
*(UINT64 **)LocateDebugProtocol ())[0])(
Flag,
PlatformString,
Flag
);
}
}
return Result;
}
//
// ---------------------------------------------------------------------------
// Entry Point
// ---------------------------------------------------------------------------
//
/**
Entry point for the SlotDataUpdate DXE driver for NeonCity EP EC B.
Initializes UEFI boot/runtime service table pointer globals, locates the
HOB list, performs CMOS-based platform detection, and registers PCIe slot
configuration data via the UBA slot data update protocol.
Two PSLT entries are registered:
1. 32-byte entry (GUID {B93613E1-48F0-4B32-B3A8-4FEDFC7C1365}) -
Single-slot configuration referencing SlotDataAccessor1 (0x4B0).
2. 40-byte entry (GUID {226763AE-972C-4E3C-80D1-73B25E8CBBA3}) -
Dual-slot configuration referencing both SlotDataAccessor1 (0x4B0)
and SlotDataAccessor2 (0x4B4).
@param[in] ImageHandle The firmware allocated handle for the EFI image.
@param[in] SystemTable A pointer to the EFI System Table.
@retval EFI_SUCCESS Slot data was successfully registered.
@retval EFI_INVALID_PARAMETER ImageHandle or SystemTable was NULL.
@retval EFI_NOT_FOUND The UBA protocol or HOB list was not found.
**/
EFI_STATUS
EFIAPI
SlotDataUpdateEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
VOID *Protocol;
UINT64 LocalVariable;
//
// Store global EFI handles.
//
gImageHandle = ImageHandle;
gSystemTable = SystemTable;
gBootServices = SystemTable->BootServices;
gRuntimeServices = SystemTable->RuntimeServices;
//
// Validate input parameters (uses UBA assert for output).
//
ASSERT (ImageHandle != NULL);
ASSERT (SystemTable != NULL);
ASSERT (gBootServices != NULL);
ASSERT (gRuntimeServices != NULL);
//
// Locate the HOB list from the system table configuration table.
//
LocateHobList ();
//
// Perform platform detection and slot data initialization
// via the UBA framework. This checks CMOS register 0x4B
// and dispatches to the appropriate platform type handler.
//
PlatformSlotDataInit (0x80000000, "UBA:SlotDataUpdate-TypeNeonCityEPECB\n");
//
// Locate the UBA slot data update protocol.
//
LocalVariable = 0;
Status = gBootServices->LocateProtocol (
&gUbaSlotDataUpdateProtocolGuid,
NULL,
(VOID **)&LocalVariable
);
if (!EFI_ERROR (Status)) {
Protocol = (VOID *)(UINTN)LocalVariable;
//
// Register 32-byte slot configuration (single slot, referenced by gSlotConfigGuid32).
//
Status = ((EFI_STATUS (EFIAPI *)(VOID *, EFI_GUID *, VOID *, UINTN))(
*(UINT64 **)Protocol)[2])(
Protocol,
&gSlotConfigGuid32,
&mSlotConfig32,
sizeof (mSlotConfig32)
);
if (!EFI_ERROR (Status)) {
//
// Register 40-byte slot configuration (dual slot, referenced by gSlotConfigGuid40).
//
Status = ((EFI_STATUS (EFIAPI *)(VOID *, EFI_GUID *, VOID *, UINTN))(
*(UINT64 **)Protocol)[2])(
Protocol,
&gSlotConfigGuid40,
&mSlotConfig40,
sizeof (mSlotConfig40)
);
}
}
return Status;
}