/** @file
IioCfgUpdateDxeCLX64L - IIO Configuration Update DXE driver for CLX64L (Cascade Lake)
This DXE driver is responsible for publishing IIO (Integrated I/O) configuration
data for the Cascade Lake (CLX64L) platform via the UBA (Unified BIOS Architecture)
protocol. It reads CMOS register 0x4B to determine the socket configuration type
(single-socket vs. multi-socket) and registers the IIO platform configuration block
under four distinct sub-system GUIDs.
The driver follows a standard UEFI DXE driver pattern:
1. Entry point caches UEFI Boot/Runtime Services pointers (via BootServicesTableLib).
2. Retrieves the HOB (Hand-Off Block) list for system configuration data.
3. Locates the UBA IIO Configuration Database protocol.
4. Registers the CLX64L-specific IIO configuration data block four times,
each under a different sub-system GUID.
Copyright (c) Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "IioCfgUpdateDxeCLX64L.h"
//
// Global caches for UEFI Boot/Runtime services and protocol pointers.
// These are populated by the entry point and used by helper functions.
//
EFI_BOOT_SERVICES *gBS;
EFI_RUNTIME_SERVICES *gRT;
EFI_SYSTEM_TABLE *gST;
EFI_HANDLE gImageHandle;
//
// Cached protocol pointers for UBA debug and UBA IIO configuration.
// Initialized on first use, then reused for subsequent calls.
//
UBA_DEBUG_PROTOCOL *mUbaDebugProtocol = NULL; ///< Located by GetUbaDebugProtocol()
VOID *mHobEntry = NULL; ///< Located by GetHobList()
//
// CMOS register 0x4B stores the socket configuration type.
//
volatile UINT8 mSocketConfig;
///
/// IIO Platform Configuration Block for CLX64L (Cascade Lake)
///
/// This data block is registered under each of the four sub-system GUIDs.
/// The values represent platform-specific IIO configuration parameters.
///
/// Structure layout:
/// +0x00: Signature "IIOP" (0x4F494950)
/// +0x04: Version 1
/// +0x08: TotalSize 0xD18 (3352 bytes)
/// +0x10: EntryCount 27
/// +0x18: Field3 0x4F8 (1272) - IIO topology or routing data size
/// +0x20: Field4 0xC00 (3072) - IIO register/data block size
/// +0x28: Field5 0x114 (276) - IIO configuration table count
///
IIO_PLATFORM_CONFIG_BLOCK mIioPlatformConfig = {
.Signature = IIO_PLATFORM_CONFIG_SIGNATURE,
.Version = IIO_PLATFORM_CONFIG_VERSION,
.TotalSize = 0xD18,
.EntryCount = 27,
.UnkField18 = 0x4F8,
.UnkField20 = 0xC00,
.UnkField28 = 0x114
};
/**
Locates and caches the UBA debug protocol.
Uses BootServices->LocateProtocol to find the UBA debug protocol interface.
The protocol pointer is cached in mUbaDebugProtocol after the first successful
lookup. Also checks that the HOB list size does not exceed the maximum expected
size (0x10 entries) as a sanity check before proceeding.
@return Pointer to the cached UBA debug protocol, or NULL if:
- The HOB list has more than 16 entries (unexpected platform state)
- The protocol could not be located
- LocateProtocol returned an error
**/
UBA_DEBUG_PROTOCOL *
GetUbaDebugProtocol (
VOID
)
{
UBA_DEBUG_PROTOCOL *Protocol;
EFI_HANDLE HobList;
EFI_STATUS Status;
//
// Return cached protocol if already located.
//
if (mUbaDebugProtocol != NULL) {
return mUbaDebugProtocol;
}
//
// Check the HOB list size as a platform sanity check.
// The number of HOB entries should be <= 0x10 for this platform.
//
HobList = (EFI_HANDLE)gBS->GetHobList ();
if ((UINTN)HobList > 0x10) {
//
// HOB list exceeds expected size; protocol lookup is skipped.
//
return NULL;
}
//
// Locate the UBA debug protocol.
//
Status = gBS->LocateProtocol (
&gUbaDebugProtocolGuid,
NULL,
(VOID **)&Protocol
);
if (EFI_ERROR (Status)) {
//
// Protocol not available; cache NULL to avoid repeated lookups.
//
mUbaDebugProtocol = NULL;
return NULL;
}
//
// Cache the protocol pointer for subsequent calls.
//
mUbaDebugProtocol = Protocol;
return Protocol;
}
//
// GUID definitions for the UBA debug protocol.
// {36232936-0E76-31C8-A13A-3AF2FC1C3932}
//
EFI_GUID gUbaDebugProtocolGuid = UBA_DEBUG_PROTOCOL_GUID;
/**
Reads CMOS register 0x4B to determine the IIO socket configuration type.
Reads the current value from CMOS index 0x4B. The value indicates whether the
platform is configured as single-socket or multi-socket. If the value read is
greater than 3 and non-zero, the function checks the chipset configuration
register at memory-mapped address 0xFDAF0490 to derive the socket type.
@return Socket configuration type:
1 = Single-socket configuration
4 = Multi-socket configuration (default, or when value > 3)
**/
UINT8
ReadIioSocketConfig (
VOID
)
{
UINT8 CmosValue;
UINT8 SocketType;
//
// Read CMOS register 0x4B with NMI mask preserved.
//
CmosValue = IoRead8 (RTC_ADDRESS_REGISTER);
IoWrite8 (RTC_ADDRESS_REGISTER, (CmosValue & CMOS_NMI_MASK) | CMOS_IIO_SOCKET_CONFIG);
SocketType = IoRead8 (RTC_DATA_REGISTER);
//
// Decode the socket configuration.
//
if (SocketType > 3) {
//
// If CMOS value is out of expected range, check hardware register.
// If SocketType is 0, fall through to hardware register check.
//
if (SocketType == 0) {
//
// Read chipset configuration at fixed MMIO address 0xFDAF0490.
// Bit 1 indicates socket topology (0 = dual-socket, 1 = single-socket).
//
SocketType = (MmioRead8 (0xFDAF0490) & 0x02) | 0x01;
}
}
return SocketType;
}
/**
Prints a debug/trace message through the UBA debug protocol.
This function formats and dispatches a debug message at the specified severity
level via the UBA debug protocol's print function (vtable offset +8, function
index 1). The socket configuration type is read from CMOS to determine the
appropriate debug mask value.
@param[in] DebugLevel Debug severity mask. Expected values include:
- 0x80000000 (EFI_D_ERROR / DEBUG level)
- 0x00400000 (EFI_D_VERBOSE)
@param[in] Format A pointer to a Null-terminated Unicode format string.
@param[in] ... Variable arguments to be formatted.
@return The return value from the protocol print function, or 0 if the
UBA debug protocol is not available.
**/
UINT8
UbaDebugPrint (
IN UINTN DebugLevel,
IN CONST CHAR8 *Format,
...
)
{
UBA_DEBUG_PROTOCOL *Protocol;
UINT64 DebugMask;
UINT8 SocketType;
VA_LIST VaList;
//
// Get the cached UBA debug protocol.
//
Protocol = GetUbaDebugProtocol ();
if (Protocol == NULL) {
return 0;
}
//
// Read the socket configuration to select the appropriate debug mask.
//
SocketType = ReadIioSocketConfig ();
DebugMask = 0;
if ((SocketType - 1) <= 0xFD) {
//
// Valid socket type: set default multi-socket mask.
//
DebugMask = 0x80000006; // DEBUG_ERROR | 0x6 (platform-specific mask)
if (SocketType == SOCKET_CONFIG_SINGLE) {
DebugMask = 0x80000004; // Single-socket mask
}
}
//
// Only print if the requested debug level matches the current mask.
//
if ((DebugMask & DebugLevel) != 0) {
VA_START (VaList, Format);
Protocol->DebugPrint (DebugLevel, Format, VaList);
VA_END (VaList);
}
return (UINT8)(UINTN)Protocol;
}
/**
Compares two GUIDs by their first 8 bytes (two consecutive UINT64 values).
This is an optimized GUID comparison that reads both halves of the GUID as
QWORD values and compares them. Used primarily for matching HOB entry GUIDs.
@param[in] Guid1 Pointer to the first GUID.
@param[in] Guid2 Pointer to the second GUID.
@retval TRUE Both GUIDs are identical.
@retval FALSE GUIDs differ.
**/
BOOLEAN
CompareGuid (
IN EFI_GUID *Guid1,
IN EFI_GUID *Guid2
)
{
//
// Read both QWORD halves of each GUID for comparison.
// Use unaligned reads since GUID pointers in HOB entries may not be aligned.
//
return (ReadUnaligned64 ((UINT64 *)Guid1) == ReadUnaligned64 ((UINT64 *)Guid2)) &&
(ReadUnaligned64 ((UINT64 *)((UINT8 *)Guid1 + 8)) == ReadUnaligned64 ((UINT64 *)((UINT8 *)Guid2 + 8)));
}
//
// GUID for the HOB Memory Allocation Module entry.
// {7739F24C-93D7-11D4-9A3A-0090273FC14D}
//
EFI_GUID gHobMemoryAllocModuleGuid = MEMORY_ALLOC_MODULE_HOB_GUID;
/**
Retrieves the HOB list pointer for this module.
Walks the HOB (Hand-Off Block) list from the System Table to find an entry
whose GUID matches gEfiHobMemoryAllocModuleGuid. The HOB list pointer is
cached in mHobEntry after the first successful lookup.
@param[in] ImageHandle Unused; provided for API compatibility with the
standard UEFI driver entry point signature.
@return Pointer to the matching HOB entry, or NULL if:
- The HOB list is empty (SystemTable->HobListSize == 0)
- No matching GUID entry was found
- The ASSERT on !EFI_ERROR(Status) would fire
**/
VOID *
GetHobList (
IN EFI_HANDLE ImageHandle
)
{
EFI_HOB_GUID_TYPE *Hob;
UINTN HobListSize;
EFI_PHYSICAL_ADDRESS HobListStart;
UINTN Index;
//
// Return cached HOB entry if already located.
//
if (mHobEntry != NULL) {
return mHobEntry;
}
//
// Initialize cache to NULL to detect lookup failures.
//
mHobEntry = NULL;
//
// Get HOB list information from the System Table.
// SystemTable + 104 = HobListSize (UINTN)
// SystemTable + 112 = HobListStart (EFI_PHYSICAL_ADDRESS *)
//
HobListSize = *(UINTN *)((UINT8 *)gST + 104);
if (HobListSize == 0) {
//
// HOB list is empty. This indicates a system configuration error.
//
DEBUG ((DEBUG_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", EFI_INVALID_PARAMETER));
ASSERT (!EFI_ERROR (EFI_INVALID_PARAMETER));
}
//
// Walk the HOB list to find a matching GUID entry.
//
HobListStart = *(EFI_PHYSICAL_ADDRESS *)((UINT8 *)gST + 112);
for (Index = 0; Index < HobListSize; Index++) {
Hob = (EFI_HOB_GUID_TYPE *)(HobListStart + (Index * HOB_ENTRY_SIZE));
if (CompareGuid (&Hob->Name, &gHobMemoryAllocModuleGuid)) {
//
// Found the matching HOB entry; cache and return it.
// The entry data starts at Hob + 16 (after GUID and header).
//
mHobEntry = (VOID *)((UINT8 *)Hob + 16);
return mHobEntry;
}
}
//
// No matching HOB entry found.
//
ASSERT (mHobEntry != NULL);
return mHobEntry;
}
//
// GUIDs for the UBA IIO Configuration Database protocol and sub-system blocks.
//
// UBA IIO Config Protocol: {E03E0D46-5263-4845-B0A4-58D57B3177E2}
//
EFI_GUID gUbaIioConfigProtocolGuid = UBA_IIO_CONFIG_PROTOCOL_GUID;
//
// IIO sub-system configuration GUIDs
//
EFI_GUID gIioConfigSubGuid1 = IIO_CONFIG_SUBGUID_1; // {6FE6C559-4F35-4111-98E1-332A251512F3}
EFI_GUID gIioConfigSubGuid2 = IIO_CONFIG_SUBGUID_2; // {0F722F2A-650F-448A-ABB7-04EECD75BB30}
EFI_GUID gIioConfigSubGuid3 = IIO_CONFIG_SUBGUID_3; // {EBD11A00-8C5C-4F71-BB9E-5394032B01F4}
EFI_GUID gIioConfigSubGuid4 = IIO_CONFIG_SUBGUID_4; // {123BD082-3201-465C-B139-0CB8C77208F8}
/**
Registers the IIO platform configuration blocks via the UBA protocol.
Locates the UBA IIO Configuration Database protocol and calls its SetData
function (vtable offset +16, function index 2) four times to register the
same IIO_PLATFORM_CONFIG_BLOCK under four different sub-system GUIDs.
The four GUIDs correspond to different IIO sub-systems within the CLX64L
platform (e.g., PCIe root ports, UPI links, memory controllers).
@retval EFI_SUCCESS All four IIO configuration blocks were
successfully registered.
@retval EFI_UNSUPPORTED The UBA IIO configuration protocol could not
be located on this platform.
@retval EFI_INVALID_PARAMETER One or more parameters were invalid.
@retval other Error status returned by LocateProtocol.
**/
EFI_STATUS
RegisterIioPlatformConfig (
VOID
)
{
EFI_STATUS Status;
UBA_IIO_CONFIG_DB *UbaIioConfigDb;
//
// Print trace message indicating IIO config update for CLX64L.
//
UbaDebugPrint (DEBUG_VERBOSE, "UBA:IioCfgUpdate-TypeClx64L\n");
//
// Locate the UBA IIO Configuration Database protocol.
//
Status = gBS->LocateProtocol (
&gUbaIioConfigProtocolGuid,
NULL,
(VOID **)&UbaIioConfigDb
);
if (EFI_ERROR (Status)) {
//
// Protocol not available on this platform.
//
return Status;
}
//
// Register the IIO platform configuration block under each sub-system GUID.
// The same configuration block is registered four times, once per sub-system.
// Each call uses the protocol's SetData function with size 48 bytes.
//
Status = UbaIioConfigDb->SetData (&gIioConfigSubGuid1, &mIioPlatformConfig, sizeof (mIioPlatformConfig));
Status |= UbaIioConfigDb->SetData (&gIioConfigSubGuid2, &mIioPlatformConfig, sizeof (mIioPlatformConfig));
Status |= UbaIioConfigDb->SetData (&gIioConfigSubGuid3, &mIioPlatformConfig, sizeof (mIioPlatformConfig));
Status |= UbaIioConfigDb->SetData (&gIioConfigSubGuid4, &mIioPlatformConfig, sizeof (mIioPlatformConfig));
return Status;
}
/**
DXE Driver Entry Point for IioCfgUpdateDxeCLX64L.
This is the main entry point called by the UEFI DXE core. It initializes the
UEFI system table pointers via gBS, gRT, gST, and gImageHandle (provided by
the UefiBootServicesTableLib and UefiRuntimeServicesTableLib libraries), then
retrieves the HOB list and registers IIO platform configuration data.
Execution flow:
1. Validate and cache ImageHandle and SystemTable.
2. Validate and cache gBS (BootServices) and gRT (RuntimeServices).
3. Call GetHobList() to retrieve the HOB list for memory allocation data.
4. Call RegisterIioPlatformConfig() to publish IIO config blocks.
@param[in] ImageHandle The firmware allocated handle for the EFI image.
@param[in] SystemTable A pointer to the EFI System Table.
@retval EFI_SUCCESS The driver entry point completed successfully.
@retval EFI_INVALID_PARAMETER ImageHandle or SystemTable was NULL.
@retval other Error status from RegisterIioPlatformConfig().
**/
EFI_STATUS
EFIAPI
IioCfgUpdateDxeEntry (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
VOID *HobList;
//
// === Phase 1: Initialize UEFI Service Pointers ===
//
// The UefiBootServicesTableLib constructor sets:
// gImageHandle = ImageHandle
// gST = SystemTable
// gBS = SystemTable->BootServices
// The UefiRuntimeServicesTableLib constructor sets:
// gRT = SystemTable->RuntimeServices
//
// Each pointer is validated against NULL with ASSERT.
//
//
// === Phase 2: Retrieve HOB List ===
//
// Walk the System Table's HOB database to locate the memory allocation
// module HOB entry (gEfiHobMemoryAllocModuleGuid).
//
HobList = GetHobList (ImageHandle);
if (HobList == NULL) {
//
// HOB list not found - system configuration error.
// An ASSERT will have already been triggered inside GetHobList().
//
return EFI_NOT_FOUND;
}
//
// === Phase 3: Register IIO Configuration Blocks ===
//
// Locate the UBA IIO Configuration Database protocol and register the
// CLX64L-specific IIO platform configuration data under four GUIDs.
//
Status = RegisterIioPlatformConfig ();
return Status;
}