/** @file
OemErrorLogDxe - HR650X BIOS OEM Error Log DXE Driver
This DXE driver registers to check CPU microcode version, stepping, and
frequency consistency across all Application Processors (APs) vs. the
Boot Strap Processor (BSP). When mismatches are detected, it sends an
IPMI SEL (System Event Log) entry via the IPMI transport protocol.
Copyright (C) Lenovo
SPDX-License-Identifier: BSD-2-Clause-Patent
File: OemErrorLogDxe.c
Binary: OemErrorLogDxe.efi (Index 0090)
**/
#include "OemErrorLogDxe.h"
//
// EFI global protocol/table pointers
//
EFI_HANDLE gImageHandle = NULL;
EFI_SYSTEM_TABLE *gSystemTable = NULL;
EFI_BOOT_SERVICES *gBootServices = NULL;
EFI_RUNTIME_SERVICES *gRuntimeServices = NULL;
//
// Module globals
//
EFI_MP_SERVICES_PROTOCOL *gMpService = NULL; // qword_1698
UINT32 *gMicrocodeVersions = NULL; // qword_1690
UINT32 *gSteppingValues = NULL; // qword_1688
UINT32 *gFrequencies = NULL; // qword_1680
VOID *gHobList = NULL; // qword_16C8
VOID *gIpmiTransport = NULL; // qword_16D0
VOID *gDebugPrintProtocol = NULL; // qword_16C0
UINT8 gErrorLogBMCBus = 0; // n3 at 0x16D8
//
// GUID definitions (from .data section at 0x1640)
//
// gEfiMpServiceProtocolGuid = { 3FDDA605-A76E-4F46-AD29-12F4531B3D08 }
// GUID at 0x1650 = { 36232936-0E76-31C8-A13A-3AF2FC1C3932 } (EFI_HOB_LIST_GUID)
// GUID at 0x1660 (unk_1660) = { 4A1D0E66-5271-4E22-83FE-90921B748213 } (gEfiIpmiTransportGuid)
//
/**
CPUID wrapper.
Executes the CPUID instruction with the given leaf and optionally stores
the resulting register values.
@param[in] Leaf CPUID leaf index.
@param[out] Eax Optional pointer to store EAX.
@param[out] Ebx Optional pointer to store EBX.
@param[out] Ecx Optional pointer to store ECX.
@param[out] Edx Optional pointer to store EDX.
@return The leaf value.
**/
UINT64
EFIAPI
CpuId (
IN UINT32 Leaf,
OUT UINT32 *Eax OPTIONAL,
OUT UINT32 *Ebx OPTIONAL,
OUT UINT32 *Ecx OPTIONAL,
OUT UINT32 *Edx OPTIONAL
)
{
UINT32 LocalEax;
UINT32 LocalEbx;
UINT32 LocalEcx;
UINT32 LocalEdx;
AsmCpuid (Leaf, &LocalEax, &LocalEbx, &LocalEcx, &LocalEdx);
if (Eax != NULL) {
*Eax = LocalEax;
}
if (Ebx != NULL) {
*Ebx = LocalEbx;
}
if (Ecx != NULL) {
*Ecx = LocalEcx;
}
if (Edx != NULL) {
*Edx = LocalEdx;
}
return Leaf;
}
/**
Zero memory (replacement for ZeroMem with additional safety checks).
@param[in] Buffer Pointer to buffer to zero.
@param[in] Size Size in bytes.
@return Pointer to the buffer.
**/
VOID *
EFIAPI
ZeroMemOrNull (
OUT VOID *Buffer,
IN UINTN Size
)
{
ASSERT (Buffer != NULL);
ASSERT (Size <= (MAX_UINTN - (UINTN)Buffer + 1));
ZeroMem (Buffer, Size);
return Buffer;
}
/**
Read a 64-bit value from an unaligned pointer.
@param[in] Buffer Pointer to read from.
@return The 64-bit value read.
**/
UINT64
EFIAPI
ReadUnaligned64Wrapper (
IN VOID *Buffer
)
{
ASSERT (Buffer != NULL);
return ReadUnaligned64 (Buffer);
}
/**
Compare two GUIDs by reading their first QWORD pair.
@param[in] Guid1 First GUID.
@param[in] Guid2 Second GUID.
@retval TRUE GUIDs match.
@retval FALSE GUIDs do not match.
**/
BOOLEAN
EFIAPI
CompareGuidByQwords (
IN EFI_GUID *Guid1,
IN EFI_GUID *Guid2
)
{
UINT64 Qword1a;
UINT64 Qword1b;
UINT64 Qword2a;
UINT64 Qword2b;
Qword1a = ReadUnaligned64Wrapper (Guid1);
Qword1b = ReadUnaligned64Wrapper ((UINT8 *)Guid1 + 8);
Qword2a = ReadUnaligned64Wrapper (Guid2);
Qword2b = ReadUnaligned64Wrapper ((UINT8 *)Guid2 + 8);
return (BOOLEAN)(Qword1a == Qword2a && Qword1b == Qword2b);
}
/**
Allocate pool wrapper. Returns NULL on allocation failure.
@param[in] AllocationSize Size to allocate in bytes.
@return Pointer to allocated buffer, or NULL on failure.
**/
VOID *
EFIAPI
AllocatePoolOrNull (
IN UINTN AllocationSize
)
{
VOID *Buffer;
EFI_STATUS Status;
Status = gBootServices->AllocatePool (EfiBootServicesData, AllocationSize, &Buffer);
if (EFI_ERROR (Status)) {
return NULL;
}
return Buffer;
}
/**
Free pool with debug assertion.
@param[in] Buffer Pointer to buffer to free.
@retval EFI_SUCCESS Buffer was freed.
**/
EFI_STATUS
EFIAPI
FreePoolChecked (
IN VOID *Buffer
)
{
EFI_STATUS Status;
Status = gBootServices->FreePool (Buffer);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", Status));
ASSERT (!EFI_ERROR (Status));
}
return Status;
}
/**
Assertion failure handler (debug print).
Prints the filename, line number, and assertion description via the
debug print protocol.
@param[in] FileName Source file name.
@param[in] LineNumber Line number of the assertion.
@param[in] Description Assertion description string.
**/
VOID
EFIAPI
DebugAssertWorker (
IN CONST CHAR8 *FileName,
IN UINTN LineNumber,
IN CONST CHAR8 *Description
)
{
if (gDebugPrintProtocol != NULL) {
((DEBUG_PRINT_PROTOCOL *)gDebugPrintProtocol)->DebugAssert (FileName, LineNumber, Description);
}
}
/**
Initialize debug print protocol (locate gEfiDebugPrintProtocolGuid on demand).
@retval Pointer to debug print protocol, or NULL if not found.
**/
VOID *
EFIAPI
GetDebugPrintProtocol (
VOID
)
{
EFI_STATUS Status;
if (gDebugPrintProtocol == NULL) {
//
// Check if running in SMM or similar restricted context (< 0x10 pages free)
//
if (gBootServices->GetBootMode () == BOOT_ON_S3_RESUME) {
return NULL;
}
Status = gBootServices->LocateProtocol (
&gEfiDebugPrintProtocolGuid,
NULL,
&gDebugPrintProtocol
);
if (EFI_ERROR (Status)) {
gDebugPrintProtocol = NULL;
}
}
return gDebugPrintProtocol;
}
/**
Debug print wrapper using port 0x70/0x71 and serial.
This function checks the error level against the platform's current
debug level (read via CMOS ports 0x70/0x71 or a fixed memory address)
and prints the debug message via the debug print protocol if the level
is enabled.
@param[in] ErrorLevel Debug error level.
@param[in] Format Print format string.
@param[in] ... Variable arguments for format string.
@return 0 if message was not printed, non-zero if it was.
**/
UINT8
EFIAPI
DebugPrintWrapper (
IN UINTN ErrorLevel,
IN CONST CHAR8 *Format,
...
)
{
UINT8 DebugLevel;
UINTN EnabledMask;
VOID *DebugProtocol;
VA_LIST VaList;
UINT8 Result;
DebugProtocol = GetDebugPrintProtocol ();
if (DebugProtocol == NULL) {
return 0;
}
//
// Read current debug level from CMOS
//
IoWrite8 (0x70, (IoRead8 (0x70) & 0x80) | 0x4B);
DebugLevel = IoRead8 (0x71);
if (DebugLevel > 3) {
if (DebugLevel == 0) {
//
// Fallback: read debug mask from fixed memory location
//
DebugLevel = (*(volatile UINT8 *)(UINTN)0xFDAF0490) & 2 | 1;
}
}
EnabledMask = 0;
if ((DebugLevel - 1) <= 0xFD) {
EnabledMask = 0x80000006;
if (DebugLevel == 1) {
EnabledMask = 0x80000004;
}
}
if ((EnabledMask & ErrorLevel) != 0) {
VA_START (VaList, Format);
Result = ((DEBUG_PRINT_PROTOCOL *)DebugProtocol)->DebugPrint (
ErrorLevel,
Format,
VaList
);
VA_END (VaList);
return Result;
}
return 0;
}
/**
Override for DEBUG() macro via DebugPrintWrapper.
We redefine the macro in the build system; this function exists so
the original ASSERT_EFI_ERROR strings are redirected through the
CMOS-aware print wrapper.
**/
VOID
EFIAPI
DebugPrintCmosAware (
IN UINTN ErrorLevel,
IN CONST CHAR8 *Format,
...
)
{
VA_LIST VaList;
VA_START (VaList, Format);
DebugPrintWrapper (ErrorLevel, Format, VaList);
VA_END (VaList);
}
/**
Get HOB list pointer (equivalent of GetHobList from DxeHobLib).
Scans the system table's HOB list entries to find the HOB list pointer
by matching the HOB list GUID.
@return Pointer to the HOB list, or NULL if not found.
**/
VOID *
EFIAPI
GetHobList (
VOID
)
{
UINTN Index;
UINTN NumberOfTableEntries;
EFI_CONFIGURATION_TABLE *ConfigTable;
UINT64 *HobGuid;
if (gHobList != NULL) {
return gHobList;
}
gHobList = NULL;
NumberOfTableEntries = gSystemTable->NumberOfTableEntries;
ConfigTable = gSystemTable->ConfigurationTable;
for (Index = 0; Index < NumberOfTableEntries; Index++) {
if (CompareGuidByQwords (
&ConfigTable[Index].VendorGuid,
(EFI_GUID *)(UINTN)0x1670 // gEfiHobListGuid
))
{
gHobList = ConfigTable[Index].VendorTable;
break;
}
}
if (gHobList == NULL) {
DEBUG ((DEBUG_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", EFI_NOT_FOUND));
ASSERT (!EFI_ERROR (EFI_NOT_FOUND));
ASSERT (gHobList != NULL);
}
return gHobList;
}
/**
Locate IPMI transport protocol and store in gIpmiTransport.
@retval EFI_SUCCESS IPMI transport protocol located.
@retval other LocateProtocol failed status.
**/
EFI_STATUS
EFIAPI
LocateIpmiTransport (
VOID
)
{
EFI_STATUS Status;
Status = gBootServices->LocateProtocol (
(EFI_GUID *)(UINTN)0x1660, // gEfiIpmiTransportProtocolGuid
NULL,
&gIpmiTransport
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", Status));
ASSERT (!EFI_ERROR (Status));
}
return Status;
}
//
// IPMI command data structures for CPU mismatch SEL
//
#pragma pack(1)
typedef struct {
UINT8 NetFn;
UINT8 Cmd;
UINT8 CompletionCode;
} IPMI_RESPONSE_HEADER;
typedef struct {
UINT8 GeneratorId; // 0x20 = BIOS
UINT8 EvMRevision; // 0x04
UINT8 SensorType; // 0x02 = Processor
UINT8 SensorNumber; // 0x72
UINT8 EventDirType; // Event Dir (bit 7) | Event Type
UINT8 EventData1; // 0x6F = OEM specific / Sensor-specific offset
UINT8 EventData2; // Flags: bit 4=microcode, bit 0=freq, bit 3=stepping
UINT8 EventData3; // 0x44 = OEM byte
} IPMI_CPU_MISMATCH_SEL_RECORD;
#pragma pack()
/**
Check BMC bus number and determine whether to log the error.
Sends a IPMI command to check the platform's current BMC channel
or reads the "Setup" UEFI variable.
@param[out] Bus Pointer to receive the bus number.
@retval EFI_SUCCESS Bus number obtained.
@retval EFI_NOT_FOUND IPMI protocol not available.
@retval EFI_UNSUPPORTED Not supported.
**/
EFI_STATUS
EFIAPI
GetBmcLogBus (
OUT UINT8 *Bus
)
{
EFI_STATUS Status;
IPMI_CPU_MISMATCH_SEL_RECORD *IpmiCmd;
UINT8 ResponseData;
UINT32 ResponseSize;
UINT8 *VarData;
UINTN VarDataSize;
UINT8 BusValue;
*Bus = 0xFF;
if (gIpmiTransport != NULL) {
//
// Send IPMI command 0x2E (0x46, 0x01) to IPMI transport
//
IpmiCmd = NULL; // command data: {0x02}, length 1
ResponseSize = sizeof (ResponseData);
Status = ((IPMI_TRANSPORT_PROTOCOL *)gIpmiTransport)->SendIpmiCommand (
(IPMI_TRANSPORT_PROTOCOL *)gIpmiTransport,
0x2E, // NetFn
0, // LUN
0x01, // Command
(UINT8 *)IpmiCmd,
1, // Request length
&ResponseData,
&ResponseSize
);
if (!EFI_ERROR (Status)) {
BusValue = ResponseData;
goto Done;
}
}
//
// Fallback: read "Setup" UEFI variable via RuntimeServices
//
VarData = NULL;
VarDataSize = 0;
Status = gRuntimeServices->GetVariable (
L"Setup",
(EFI_GUID *)(UINTN)0x1678, // Setup variable GUID
NULL,
&VarDataSize,
NULL
);
if (Status == EFI_BUFFER_TOO_SMALL) {
VarData = (UINT8 *)AllocatePoolOrNull (VarDataSize);
if (VarData != NULL) {
Status = gRuntimeServices->GetVariable (
L"Setup",
(EFI_GUID *)(UINTN)0x1678,
NULL,
&VarDataSize,
VarData
);
if (!EFI_ERROR (Status)) {
BusValue = VarData[284]; // Offset 284 in Setup variable = BMC bus
}
FreePool (VarData);
}
}
if (EFI_ERROR (Status)) {
*Bus = 0xFF;
return EFI_UNSUPPORTED;
}
Done:
*Bus = BusValue;
return EFI_SUCCESS;
}
/**
AP worker function that collects CPU information.
Called on each AP via EFI_MP_SERVICES_PROTOCOL.StartupAllAPs.
Reads the microcode version (MSR 0x8B), stepping (CPUID), and
frequency (MSR 0xCE) and stores them in the global arrays.
@param[in] Buffer Not used (required by MP Services API).
**/
VOID
EFIAPI
ApCollectCpuInfo (
IN VOID *Buffer
)
{
UINT32 ProcessorIndex;
UINT32 Eax;
UINT32 Ebx;
UINT32 Ecx;
UINT32 Edx;
UINT64 MicrocodeMsr;
UINT64 FreqMsr;
//
// Get current processor number from MP services
//
gMpService->WhoAmI (gMpService, &ProcessorIndex);
//
// Read microcode version from MSR 0x8B
//
AsmWriteMsr64 (0x8B, 0);
CpuId (1, (UINT32 *)&Eax, (UINT32 *)&Ebx, (UINT32 *)&Ecx, (UINT32 *)&Edx);
MicrocodeMsr = AsmReadMsr64 (0x8B);
if (gMicrocodeVersions != NULL) {
gMicrocodeVersions[ProcessorIndex] = (UINT32)(MicrocodeMsr >> 32);
}
//
// Read stepping from CPUID (EAX[3:0] after CPUID leaf 1)
//
if (gSteppingValues != NULL) {
CpuId (1, &Eax, NULL, NULL, NULL);
gSteppingValues[ProcessorIndex] = Eax & 0xF;
}
//
// Read frequency from MSR 0xCE (Platform Info MSR)
//
if (gFrequencies != NULL) {
FreqMsr = AsmReadMsr64 (0xCE);
//
// Byte 1 of MSR 0xCE contains the max non-turbo ratio in 100 MHz units
//
gFrequencies[ProcessorIndex] = (UINT32)((10000 * (UINT8)((FreqMsr >> 8) & 0xFF)) / 100);
}
}
/**
Main CPU information check and notification handler.
This function:
1. Locates the MP Services protocol.
2. Allocates arrays for microcode, stepping, and frequency data.
3. Collects BSP data by calling ApCollectCpuInfo once.
4. Starts APs via StartupAllAPs to collect their data.
5. Compares all AP data against BSP.
6. If mismatches found, sends an IPMI SEL.
@param[in] Event Event that triggered the notification.
@param[in] Context Context passed to the callback.
@retval EFI_SUCCESS Check completed.
@retval other Error from protocol or allocation.
**/
EFI_STATUS
EFIAPI
OemCheckCpuInfoNotify (
IN EFI_EVENT Event,
IN VOID *Context
)
{
EFI_STATUS Status;
EFI_MP_SERVICES_PROTOCOL *MpService;
UINTN NumberOfProcessors;
UINTN NumberOfEnabledProcessors;
UINTN Index;
UINT32 BspMicrocode;
UINT32 BspStepping;
UINT32 BspFrequency;
BOOLEAN MicrocodeMismatch;
BOOLEAN FrequencyMismatch;
BOOLEAN SteppingMismatch;
UINTN BufferSize;
IPMI_CPU_MISMATCH_SEL_RECORD SelRecord;
IPMI_TRANSPORT_PROTOCOL *IpmiTransport;
UINT8 ResponseData;
UINT32 ResponseSize;
UINT8 BusValue;
DEBUG_PRINT_CMOS (DEBUG_INFO, "OemCheckCpuInfoNotify start ...\n");
//
// 1. Locate MP Services Protocol
//
Status = gBootServices->LocateProtocol (
&gEfiMpServiceProtocolGuid,
NULL,
(VOID **)&MpService
);
if (EFI_ERROR (Status)) {
DEBUG_PRINT_CMOS (DEBUG_INFO,
"OemCheckCpuInfoNotify: locate gEfiMpServiceProtocolGuid fail Status: %r\n",
Status);
return Status;
}
gMpService = MpService;
//
// 2. Enable watchdog (optional)
//
gBootServices->Stall (1000); // placeholder for watchdog timer
//
// 3. Get processor count
//
MpService->GetNumberOfProcessors (MpService, &NumberOfProcessors, &NumberOfEnabledProcessors);
if (NumberOfProcessors <= 1) {
return EFI_SUCCESS;
}
BufferSize = sizeof (UINT32) * NumberOfProcessors;
//
// 4. Allocate arrays
//
gMicrocodeVersions = AllocatePoolOrNull (BufferSize);
if (gMicrocodeVersions != NULL) {
ZeroMemOrNull (gMicrocodeVersions, BufferSize);
}
gSteppingValues = AllocatePoolOrNull (BufferSize);
if (gSteppingValues != NULL) {
ZeroMemOrNull (gSteppingValues, BufferSize);
}
gFrequencies = AllocatePoolOrNull (BufferSize);
if (gFrequencies != NULL) {
ZeroMemOrNull (gFrequencies, BufferSize);
}
//
// 5. Collect BSP data first, then all APs
//
ApCollectCpuInfo (NULL);
Status = MpService->StartupAllAPs (
MpService,
ApCollectCpuInfo,
FALSE, // SingleThread
NULL, // WaitEvent
0, // TimeoutInMicroseconds (infinite)
NULL, // ProcedureArgument
NULL // FailedCpuList
);
if (EFI_ERROR (Status)) {
DEBUG_PRINT_CMOS (DEBUG_INFO,
"OemCheckCpuInfoNotify: StartupAllAPs fail Status: %r\n",
Status);
return Status;
}
//
// 6. Compare each AP against BSP
//
MicrocodeMismatch = FALSE;
FrequencyMismatch = FALSE;
SteppingMismatch = FALSE;
DEBUG_PRINT_CMOS (DEBUG_INFO, "BSP Information\n");
DEBUG_PRINT_CMOS (DEBUG_INFO, "Microcode version: 0x%x\n", gMicrocodeVersions[0]);
DEBUG_PRINT_CMOS (DEBUG_INFO, "Stepping: 0x%x\n", gSteppingValues[0]);
DEBUG_PRINT_CMOS (DEBUG_INFO, "Frequency(MHz): %d\n", gFrequencies[0]);
for (Index = 0; Index < NumberOfProcessors; Index++) {
if (gMicrocodeVersions[0] != gMicrocodeVersions[Index]) {
DEBUG_PRINT_CMOS (DEBUG_INFO,
"AP%d microcode version (0x%x) is different from BSP\n",
Index,
gMicrocodeVersions[Index]);
MicrocodeMismatch = TRUE;
}
if (gFrequencies[0] != gFrequencies[Index]) {
DEBUG_PRINT_CMOS (DEBUG_INFO,
"AP%d frequency (%d MHz) is different from BSP\n",
Index,
gFrequencies[Index]);
FrequencyMismatch = TRUE;
}
if (gSteppingValues[0] != gSteppingValues[Index]) {
DEBUG_PRINT_CMOS (DEBUG_INFO,
"AP%d stepping (0x%x) is different from BSP\n",
Index,
gSteppingValues[Index]);
SteppingMismatch = TRUE;
}
}
//
// 7. Free allocated arrays
//
if (gMicrocodeVersions != NULL) {
FreePoolChecked (gMicrocodeVersions);
gMicrocodeVersions = NULL;
}
if (gFrequencies != NULL) {
FreePoolChecked (gFrequencies);
gFrequencies = NULL;
}
if (gSteppingValues != NULL) {
FreePoolChecked (gSteppingValues);
gSteppingValues = NULL;
}
//
// 8. If any mismatch found, send IPMI SEL
//
if (MicrocodeMismatch || FrequencyMismatch || SteppingMismatch) {
DEBUG_PRINT_CMOS (DEBUG_INFO, "OemCheckCpuInfoNotify: CPU Mismatch\n");
Status = gBootServices->LocateProtocol (
(EFI_GUID *)(UINTN)0x1660, // gEfiIpmiTransportProtocolGuid
NULL,
(VOID **)&IpmiTransport
);
if (EFI_ERROR (Status)) {
DEBUG_PRINT_CMOS (DEBUG_INFO,
"OemCheckCpuInfoNotify: LocateProtocol fail (%r)\n",
Status);
return Status;
}
//
// Determine BMC bus for SEL
//
Status = GetBmcLogBus (&BusValue);
if (EFI_ERROR (Status)) {
BusValue = 0xFF;
}
//
// Build IPMI SEL record
//
SelRecord.GeneratorId = 0x20; // BIOS
SelRecord.EvMRevision = 0x04;
SelRecord.SensorType = 0x02; // Processor
SelRecord.SensorNumber = 0x72;
SelRecord.EventDirType = (BusValue <= 1) ? 0x70 : 0x95; // Event Dir | Event Type
SelRecord.EventData1 = 0x6F; // OEM specific
SelRecord.EventData2 = 0;
if (MicrocodeMismatch) {
SelRecord.EventData2 |= 0x10; // bit 4: microcode
}
if (FrequencyMismatch) {
SelRecord.EventData2 |= 0x01; // bit 0: frequency
}
if (SteppingMismatch) {
SelRecord.EventData2 |= 0x08; // bit 3: stepping
}
SelRecord.EventData3 = 0x44; // OEM byte
//
// Send IPMI command (NetFn=0x0A, Cmd=0x44) to add SEL entry
//
ResponseSize = sizeof (ResponseData);
Status = IpmiTransport->SendIpmiCommand (
IpmiTransport,
0x0A, // NetFn: Application
0, // LUN
0x44, // Command: Add SEL Entry
(UINT8 *)&SelRecord,
sizeof (SelRecord),
&ResponseData,
&ResponseSize
);
if (EFI_ERROR (Status)) {
DEBUG_PRINT_CMOS (DEBUG_INFO,
"OemCheckCpuInfoNotify: IpmiTransportDxe->SendIpmiCommand fail Status: %r\n",
Status);
return Status;
}
DEBUG_PRINT_CMOS (DEBUG_INFO, "OemCheckCpuInfoNotify end ...\n");
}
return EFI_SUCCESS;
}
/**
Library constructor that saves UEFI global pointers and locates
the IPMI transport protocol.
@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 constructor always returns successfully.
**/
EFI_STATUS
EFIAPI
OemErrorLogDxeLibConstructor (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
//
// Save global pointers
//
gImageHandle = ImageHandle;
gSystemTable = SystemTable;
gBootServices = SystemTable->BootServices;
gRuntimeServices = SystemTable->RuntimeServices;
ASSERT (gImageHandle != NULL);
ASSERT (gSystemTable != NULL);
ASSERT (gBootServices != NULL);
ASSERT (gRuntimeServices != NULL);
//
// Initialize HOB list
//
GetHobList ();
//
// Locate IPMI transport protocol to prepare for SEL logging
//
Status = gBootServices->LocateProtocol (
(EFI_GUID *)(UINTN)0x1660, // gEfiIpmiTransportProtocolGuid
NULL,
&gIpmiTransport
);
if (EFI_ERROR (Status)) {
//
// IPMI not available yet; register for protocol notification
//
Status = gBootServices->RegisterProtocolNotify (
(EFI_GUID *)(UINTN)0x1660, // gEfiIpmiTransportProtocolGuid
(EFI_EVENT)(UINTN)LocateIpmiTransport,
NULL
);
if (EFI_ERROR (Status)) {
DEBUG_PRINT_CMOS (DEBUG_ERROR,
"DxeLnvSendIpmiCmdLibConstructor Status = %r \n",
Status);
}
}
return EFI_SUCCESS;
}
/**
Driver entry point. Initializes globals, prints start/end messages,
and registers the CPU info check notification.
@param[in] ImageHandle The firmware allocated handle for the EFI image.
@param[in] SystemTable A pointer to the EFI System Table.
@return EFI status code.
**/
EFI_STATUS
EFIAPI
OemErrorLogDxeEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
//
// Initialize UEFI globals and construct library
//
Status = OemErrorLogDxeLibConstructor (ImageHandle, SystemTable);
if (EFI_ERROR (Status)) {
return Status;
}
DEBUG_PRINT_CMOS (DEBUG_INFO, "OemErrorLogDxeEntry\tstart ...\n");
//
// Register the CPU check notification via MP services protocol notify
//
Status = gBootServices->RegisterProtocolNotify (
&gEfiMpServiceProtocolGuid,
(EFI_EVENT)(UINTN)OemCheckCpuInfoNotify,
NULL
);
if (!EFI_ERROR (Status)) {
//
// Check immediately after registration for already-present protocol
//
OemCheckCpuInfoNotify (NULL, NULL);
}
DEBUG_PRINT_CMOS (DEBUG_INFO, "OemErrorLogDxeEntry exit ...\n");
return Status;
}