Newer
Older
AMI-Aptio-BIOS-Reversed / AmiIpmiPkg / Ipmi / BmcElog / BmcElog / BmcElog.c
/** @file BmcElog.c - recovered event-log runtime driver layout Source: e:\hs\AmiIpmiPkg\Ipmi\BmcElog\BmcElog.c MD5: fef617a6fb305102b78e179f1da789a4 SHA256: c04adaed32a1244467eb732b935b1fd51365e255b001e01654b65c83c9748aa0 Image Size: 0x6000 Architecture: x86_64 (UEFI)
 Toolchain: EDK II Decompiled from HR650X BIOS (PE0118)
**/

#include "BmcElog.h"

//
// Module globals
//
EFI_HANDLE gImageHandle = NULL;
EFI_SYSTEM_TABLE *gST = NULL;
EFI_BOOT_SERVICES *gBS = NULL;
EFI_RUNTIME_SERVICES *gRT = NULL;

UINT64 mBootServices = 0;
UINT64 mRuntimeServices = 0;
UINT64 mHobList = 0;

//
// IPMI transport protocol handle (opaque)
//
VOID *mIpmiTransport = NULL;

//
// ASSERT reporting function
//
VOID
EFIAPI
DebugAssert (
  CONST CHAR8  *FileName,
  UINTN        LineNumber,
  CONST CHAR8  *Description
  )
{
  if (mBootServices != 0) {
    ((VOID ( *)(CONST CHAR8 *, UINTN, CONST CHAR8 *))(mBootServices + 8)) (
      FileName,
      LineNumber,
      Description
      );
  }
}

//
// Internal memory operations
//

/**Copies a buffer. Source: BaseMemoryLibRepStr / CopyMemWrapper.c

 @param Destination Pointer to the destination buffer.
 @param Source Pointer to the source buffer.
 @param Length Number of bytes to copy.

 @return Destination.
**/
VOID *EFIAPI BmcElogCopyMem (
 VOID *Destination,
 const VOID *Source,
 UINTN Length
 )
{
 //
 // Copy with overlap handling.
 //
 if ((UINTN)Source < (UINTN)Destination &&
 (UINTN)Source + Length - 1 >= (UINTN)Destination)
 {
 DebugAssert (
 __FILE__,
 56,
 "(Length - 1) <= (0xFFFFFFFFFFFFFFFFULL - (UINTN)DestinationBuffer)"
 );
 }
 if ((UINTN)Destination + Length - 1 < (UINTN)Destination) {
 DebugAssert (
 __FILE__,
 57,
 "(Length - 1) <= (0xFFFFFFFFFFFFFFFFULL - (UINTN)SourceBuffer)"
 );
 }
 if (Destination == Source) {
 return Destination;
 }

 //
 {
 UINT8 *Dst8 = (UINT8 *)Destination;
 UINT8 *Src8 = (UINT8 *)Source;
 UINTN Count;

 if (Src8 < Dst8 && &Src8[Length - 1] >= Dst8) {
 //
 // Overlap: copy backwards
 //
 Src8 = &Src8[Length - 1];
 Dst8 = &Dst8[Length - 1];
 for (Count = Length; Count > 0; Count--) {
 *Dst8-- = *Src8--;
 }
 } else {
 //
 // No overlap: copy 8 bytes at a time
 //
 Count = Length >> 3;
 if (Count > 0) {
 CopyMem (Dst8, Src8, Count << 3);
 Dst8 += Count << 3;
 Src8 += Count << 3;
 }
 Count = Length & 7;
 if (Count > 0) {
 CopyMem (Dst8, Src8, Count);
 }
 }
 }

 return Destination;
}

/**Fills a buffer with zeros.

 @param Buffer Pointer to the buffer to zero.
 @param Length Number of bytes to zero.

 @return Buffer.
**/
VOID *EFIAPI BmcElogZeroMem (
 VOID *Buffer,
 UINTN Length
 )
{
 //
 // Zero in aligned chunks, then finish the tail.
 //
 if (Length >= 8) {
 ZeroMem (Buffer, Length & ~(UINTN)7);
 }
 if ((Length & 7) != 0) {
 ZeroMem ((UINT8 *)Buffer + (Length & ~(UINTN)7), Length & 7);
 }
 return Buffer;
}

//
// IPMI transport helper
//

/**Gets the IPMI transport protocol interface.
 Uses the handle database to locate the IPMI transport protocol.
 The handle is cached in mIpmiTransport.

 @return Pointer to the IPMI transport protocol, or NULL.
**/
VOID *GetCachedIpmiTransport (
 VOID
 )
{
 //
 // Return the cached IPMI transport protocol, or locate it once and cache it.
 //
 if (mIpmiTransport != NULL) {
 return mIpmiTransport;
 }

 if (mBootServices != 0) {
 UINTN BufferSize;
 EFI_STATUS Status;

 BufferSize = 0x10;
 Status = gBS->LocateProtocol (
 &gEfiIpmiTransportProtocolGuid,
 NULL,
 &mIpmiTransport
 );
 if (EFI_ERROR (Status)) {
 mIpmiTransport = NULL;
 }
 }

 return mIpmiTransport;
}

/**IPMI command: detect platform type via CMOS and dispatch to the correct transport (KCS vs. BT vs. SMBUS).

 Uses CMOS register 0x4B to determine BMC presence and interface type.

 @param[in] IoOperation Operation flags.
 @param[in] Param2 Secondary parameter.

 @retval EFI_SUCCESS on matching interface.
**/
EFI_STATUS BmcElogDispatchIpmiCommand (
 UINT64 IoOperation,
 UINT64 Param2,
 ...
 )
{
 //
 // Resolve the active BMC interface from CMOS and dispatch to the transport
 // when the requested interface mask matches.
 //
 VA_LIST Args;
 UINT8 BmcIfType;
 UINT64 InterfaceId;
 VOID *Transport;

 VA_START (Args, Param2);

 Transport = GetCachedIpmiTransport ();
 if (Transport == NULL) {
 return EFI_NOT_FOUND;
 }

 //
 // Read CMOS register 0x4B
 //
 BmcIfType = IoRead8 (0x70);
 IoWrite8 (0x70, BmcIfType & 0x80 | 0x4B);
 BmcIfType = IoRead8 (0x71);

 if (BmcIfType > 3) {
 BmcIfType = 3;
 }

 if (BmcIfType == 0) {
 return EFI_UNSUPPORTED;
 }

 switch (BmcIfType) {
 case 1:
 InterfaceId = IPMI_INTERFACE_KCS;
 break;
 case 2:
 InterfaceId = IPMI_INTERFACE_SMBUS;
 break;
 case 3:
 InterfaceId = IPMI_INTERFACE_BT;
 break;
 default:
 return EFI_UNSUPPORTED;
 }

 if ((InterfaceId & IoOperation) != 0) {
 //
 // Call the transport function through the protocol
 //
 return ((EFI_STATUS ( *)(
 UINT64, UINT64, VA_LIST
 ))Transport)(IoOperation, Param2, Args);
 }

 VA_END (Args);
 return EFI_UNSUPPORTED;
}

/**Sends an IPMI command over the appropriate transport.

 @param[in] NetFunction IPMI net function.
 @param[in] Command IPMI command.
 @param[in] CommandData Pointer to command data.
 @param[in] CommandDataSize Size of command data.
 @param[out] ResponseData Pointer to response data buffer.
 @param[out] ResponseDataSize Pointer to response data size.

 @retval EFI_SUCCESS Command was sent successfully.
 @retval others Command failed.
**/
EFI_STATUS BmcElogSendIpmiCommand (
 UINT8 NetFunction,
 UINT8 Command,
 VOID *CommandData,
 UINT32 CommandDataSize,
 VOID *ResponseData,
 UINT32 *ResponseDataSize
 )
{
 //
 // Send an IPMI command through the cached transport with limited retries.
 // Retry through the alternate write path when the completion code says the
 // command cannot be retried.
 //
 VOID *Transport;
 UINTN Retries;
 UINT8 WriteData[4];
 UINT8 Response1;
 EFI_STATUS Status;

 Transport = *(VOID **)((UINT8 *)mHobList + 0x3040); // IPMI transport protocol Retries = 512;

 do {
 //
 // Prepare command packet
 //
 WriteData[0] = *((UINT8 *)CommandData + 0);
 WriteData[1] = *((UINT8 *)CommandData + 1);
 WriteData[2] = 0x525353; // Protocol-specific command payload marker.

 //
 // Send command via transport->SendCommand (offset +16)
 //
 Status = ((EFI_STATUS ( *)(
 VOID *, UINT8, UINT64, UINT8,
 UINT8 *, UINT32, UINT8 *, UINT8 *
 ))Transport)(
 Transport,
 10, // NetFunction 0, // Lun 71, // Command WriteData,
 6,
 &Response1,
 NULL
 );

 if (!EFI_ERROR (Status)) {
 if (Response1 == 1) {
 return EFI_SUCCESS;
 }
 } else {
 //
 // Check for retryable error (0xC5 = CC_CANNOT_RETRY)
 //
 if (*(UINT8 *)((UINT8 *)Transport + 8) == 0xC5) {
 UINT8 RetryCmd = 2;
 Status = ((EFI_STATUS ( *)(
 VOID *, UINT8, UINT64, UINT8,
 UINT64, UINT32, UINT8 *, UINT8 *
 ))Transport)(
 Transport,
 10, // NetFunction 0,
 66, // Write sub-command 0,
 0,
 CommandData,
 &RetryCmd
 );
 if (EFI_ERROR (Status)) {
 return Status;
 }
 }
 }

 Retries--;
 } while (Retries > 0);

 return EFI_TIMEOUT;
}

/**Gets an IPMI response (reads data from BMC).

 @param[in] NetFunction IPMI net function.
 @param[in] Command IPMI command.
 @param[out] ResponseData Pointer to response data buffer.
 @param[in,out] ResponseDataSize Pointer to response data size.

 @retval EFI_SUCCESS Response was read successfully.
 @retval others Failed to read response.
**/
EFI_STATUS BmcElogGetIpmiResponse (
 UINT8 NetFunction,
 UINT8 Command,
 VOID *ResponseData,
 UINT32 *ResponseDataSize
 )
{
 //
 // Fetch a response block from the transport and copy it out.
 //
 UINT8 Buffer[16];
 UINTN BufferSize;
 UINT16 RecordId;
 UINT8 Response;
 EFI_STATUS Status;

 BufferSize = 16;

 Status = BmcElogSendIpmiCommand (
 NetFunction,
 Command,
 NULL,
 0,
 Buffer,
 &BufferSize
 );
 if (EFI_ERROR (Status)) {
 return Status;
 }

 if (BufferSize > 16) {
 return EFI_INVALID_PARAMETER;
 }

 BmcElogCopyMem (ResponseData, Buffer, 16);

 return Status;
}

/**Reads BMC event log records.

 @param[in] This Protocol instance pointer.
 @param[out] Data Pointer to buffer for reading data.
 @param[in] Revision Protocol revision.
 @param[in,out] RecordCount On input, max records to read; on output,
 number of records read.
 @param[in,out] RecordId On input, starting record ID; on output,
 next record ID.

 @retval EFI_SUCCESS Read succeeded.
 @retval EFI_INVALID_PARAMETER Bad signature or revision mismatch.
 @retval EFI_BUFFER_TOO_SMALL Buffer too small.
 @retval EFI_NOT_FOUND No records found.
**/
EFI_STATUS BmcElogRead (
 VOID *This,
 UINT8 *Data,
 UINT32 Revision,
 UINT64 *RecordCount,
 UINT16 *RecordId
 )
{
 //
 // Validate the protocol instance and fetch one SEL record entry.
 //
 BMC_ELOG_REFROTOCOL *Ref;
 UINT8 Request[6];
 UINT16 ResponseRecordId;
 UINT16 ReadData[4];
 EFI_STATUS Status;

 BmcElogZeroMem (ReadData, sizeof (ReadData));

 //
 // Validate signature
 //
 Ref = (BMC_ELOG_REFROTOCOL *)((UINT8 *)This - 113);
 if (Ref->Signature != 0x66657252) { // "Refr"
 DebugAssert (
 "e:\\hs\\AmiIpmiPkg\\Ipmi\\BmcElog\\BmcElog.c",
 297,
 "CR has Bad Signature"
 );
 return EFI_INVALID_PARAMETER;
 }

 if (Ref->Revision != Revision) {
 return EFI_INVALID_PARAMETER;
 }

 if (*RecordCount < 16) {
 return EFI_BUFFER_TOO_SMALL;
 }

 //
 // Build the IPMI Get SEL Entry command
 //
 ReadData[0] = 0; // Reserved ReadData[2] = 0xFF00; // Record type mask / last entry ReadData[1] = *RecordId; // Starting record ID Status = ((EFI_STATUS ( *)(
 VOID *, UINT8, UINT64, UINT64 *,
 UINT16 *, UINT32, UINT16 *, UINT8 *
 )) (*(VOID **)((UINT8 *)mHobList + 0x3040) + 16))(
 *(VOID **)((UINT8 *)mHobList + 0x3040),
 10, // NetFn 0,
 (UINT64 *)67, // Get SEL Entry ReadData,
 6,
 &ResponseRecordId,
 NULL
 );

 if (EFI_ERROR (Status)) {
 return Status;
 }

 *RecordId = ResponseRecordId;
 BmcElogCopyMem (Data, Request, 16);

 return EFI_SUCCESS;
}

/**Clears BMC event log.

 @param[in] This Protocol instance pointer.
 @param[in] Data Pointer to data for erase command.
 @param[in] Revision Protocol revision.

 @retval EFI_SUCCESS Clear succeeded.
 @retval EFI_INVALID_PARAMETER Bad signature or revision mismatch.
 @retval EFI_UNSUPPORTED Operation not supported for this revision.
 @retval EFI_DEVICE_ERROR Device error.
**/
EFI_STATUS BmcElogClear (
 VOID *This,
 UINT8 *Data,
 UINT32 Revision
 )
{
 //
 // Validate the protocol instance and clear or reserve SEL state as needed.
 //
 BMC_ELOG_REFROTOCOL *Ref;
 UINT8 Response[16];
 UINT8 Request[4];
 UINT8 ResponseSize;
 UINT8 Reservation[2];
 UINT8 ClearData[2];
 UINT16 ReservedId;
 EFI_STATUS Status;
 UINT8 CmdResult;

 //
 // Validate signature
 //
 Ref = (BMC_ELOG_REFROTOCOL *)((UINT8 *)This - 113);
 if (Ref->Signature != 0x66657252) {
 DebugAssert (
 "e:\\hs\\AmiIpmiPkg\\Ipmi\\BmcElog\\BmcElog.c",
 382,
 "CR has Bad Signature"
 );
 return EFI_INVALID_PARAMETER;
 }

 if (Ref->Revision != Revision) {
 return EFI_INVALID_PARAMETER;
 }

 //
 // Get SEL Info
 //
 ResponseSize = 14;
 Status = ((EFI_STATUS ( *)(
 VOID *, UINT8, UINT64, UINT64,
 UINT64, UINT8, UINT8 *, UINT8 *, UINT32
 )) (*(VOID **)((UINT8 *)mHobList + 0x3040) + 16))(
 *(VOID **)((UINT8 *)mHobList + 0x3040),
 10,
 0,
 64, // Get SEL Info 0,
 0,
 Response,
 &ResponseSize,
 0x0201 // Reserved
 );

 if (EFI_ERROR (Status)) {
 return Status;
 }

 if ((Data != NULL) && ((Response[5] & 8) == 0)) {
 //
 // Erase is not supported
 //
 return EFI_UNSUPPORTED;
 }

 if ((Response[5] & 2) != 0) {
 //
 // Reserve SEL first.
 //
 EFI_STATUS ReserveStatus;

 ReserveStatus = ((EFI_STATUS ( *)(
 VOID *, UINT8, UINT64, UINT64,
 UINT64, UINT32, UINT8 *, UINT8 *
 )) (*(VOID **)((UINT8 *)mHobList + 0x3040) + 16))(
 *(VOID **)((UINT8 *)mHobList + 0x3040),
 10,
 0,
 66, // Reserve SEL 0,
 0,
 Reservation,
 NULL
 );
 if (EFI_ERROR (ReserveStatus)) {
 return ReserveStatus;
 }

 ClearData[1] = Reservation[1];
 ClearData[0] = Reservation[0];
 } else {
 ClearData[0] = 0;
 ClearData[1] = 0;
 }

 if (Data != NULL) {
 //
 // Erase SEL with reservation.
 //
 UINT8 EraseData[4];

 EraseData[1] = ClearData[1]; // Reservation byte 1
 EraseData[0] = ClearData[0]; // Reservation byte 0
 EraseData[3] = *Data; // User-provided data bytes EraseData[2] = Data[1];
 EraseData[0] = ClearData[0];

 Status = ((EFI_STATUS ( *)(
 VOID *, UINT8, UINT64, UINT64,
 UINT8 *, UINT32, UINT16 *, UINT8 *
 )) (*(VOID **)((UINT8 *)mHobList + 0x3040) + 16))(
 *(VOID **)((UINT8 *)mHobList + 0x3040),
 10,
 0,
 70, // Clear SEL EraseData,
 4,
 &ReservedId,
 (UINT8 *)&CmdResult + 1
 );

 if (EFI_ERROR (Status)) {
 return Status;
 }

 //
 // Check completion code
 //
 if (*(UINT8 *)((UINT8 *)(*(VOID **)((UINT8 *)mHobList + 0x3040)) + 8) == 0x80) {
 return EFI_UNSUPPORTED;
 }
 if (*(UINT8 *)((UINT8 *)(*(VOID **)((UINT8 *)mHobList + 0x3040)) + 8) == 0x81) {
 return EFI_DEVICE_ERROR;
 }

 * (UINT64 *) Data = ReservedId;
 return Status;
 } else {
 //
 // Clear via Set SEL Time / erase mechanism.
 //
 UINT8 ClearBuf[6];
 UINT32 ClearResult;

 ClearBuf[1] = ClearData[1];
 ClearBuf[0] = ClearData[0];
 ClearBuf[5] = 0xAA;
 ClearBuf[4] = 0xAA;
 ClearBuf[2] = 0xAA;
 ClearBuf[3] = 0xAA;

 ((EFI_STATUS ( *)(
 VOID *, UINT8, UINT64, UINT64,
 UINT8 *, UINT32, UINT8 *, UINT32 *
 )) (*(VOID **)((UINT8 *)mHobList + 0x3040) + 16))(
 *(VOID **)((UINT8 *)mHobList + 0x3040),
 10,
 0,
 71, // Set SEL Time ClearBuf,
 6,
 (UINT8 *)&CmdResult + 2,
 &ClearResult
 );

 return BmcElogSendIpmiCommand (0, 0, Reservation, 0, NULL, NULL);
 }
}

/**Sets the BMC event log enable/disable status.

 @param[in] This Protocol instance pointer.
 @param[in] Revision Protocol revision.
 @param[in] Status Pointer to enable/disable status byte.
 @param[out] Enabled If Status is NULL, returns current enabled state.

 @retval EFI_SUCCESS Status was set or read successfully.
 @retval EFI_INVALID_PARAMETER Bad signature or revision mismatch.
**/
EFI_STATUS BmcElogSetStatus (
 VOID *This,
 UINT64 Revision,
 UINT8 *Status,
 BOOLEAN *Enabled
 )
{
 //
 // Read or update the SEL enable flag via the SEL Info response.
 //
 BMC_ELOG_REFROTOCOL *Ref;
 UINT8 Response;
 UINT8 ResponseSize;
 UINT32 Result;
 UINT32 Parameter;
 EFI_STATUS ReturnStatus;

 Parameter = 1; // Get SEL Info parameter Result = 1;

 //
 // Validate signature
 //
 Ref = (BMC_ELOG_REFROTOCOL *)((UINT8 *)This - 113);
 if (Ref->Signature != 0x66657252) {
 DebugAssert (
 "e:\\hs\\AmiIpmiPkg\\Ipmi\\BmcElog\\BmcElog.c",
 524,
 "CR has Bad Signature"
 );
 return EFI_INVALID_PARAMETER;
 }

 if (Ref->Revision != Revision) {
 return EFI_INVALID_PARAMETER;
 }

 //
 // IPMI Get SEL Info command (NetFn=6, Cmd=0x47)
 //
 ReturnStatus = ((EFI_STATUS ( *)(
 VOID *, UINT8, UINT64, BOOLEAN *,
 UINT64, UINT8, UINT8 *, UINT8 *, UINT32
 )) (*(VOID **)((UINT8 *)mHobList + 0x3040) + 16))(
 *(VOID **)((UINT8 *)mHobList + 0x3040),
 6, // Storage NetFn 0,
 (BOOLEAN *)47, // Get SEL Info 0,
 0,
 &Response,
 (UINT8 *)&Result,
 Parameter
 );

 if (EFI_ERROR (ReturnStatus)) {
 return ReturnStatus;
 }

 if (Status != NULL) {
 //
 // Set the enable/disable bit
 //
 UINT8 NewResponse = Response;
 UINT8 SetBit = *Status;

 if (((Response & 8) != 0) != SetBit) {
 NewResponse ^= (NewResponse ^ (SetBit *8)) & 8;

 ReturnStatus = ((EFI_STATUS ( *)(
 VOID *, UINT8, UINT64, UINT64,
 UINT8 *, UINT32, UINT32 *, UINT8 *
 )) (*(VOID **)((UINT8 *)mHobList + 0x3040) + 16))(
 *(VOID **)((UINT8 *)mHobList + 0x3040),
 6, // Storage NetFn 0,
 (UINT64)46, // Set SEL Time (to change enable)
 &NewResponse,
 1,
 &Result,
 (UINT8 *)&Result
 );
 return ReturnStatus;
 }
 } else {
 *Enabled = (Response & 8) != 0;
 }

 return ReturnStatus;
}

/**Writes a BMC event log record.

 @param[in] This Protocol instance pointer.
 @param[in] Data Pointer to event log data.
 @param[in] Revision Protocol revision.
 @param[out] Key Reserved, unused.
 @param[in] DataSize Size of the event data.
 @param[out] RecordId Returned record ID.

 @retval EFI_SUCCESS Write succeeded.
 @retval EFI_INVALID_PARAMETER Bad signature or revision mismatch.
 @retval EFI_OUT_OF_RESOURCES Out of resources.
**/
EFI_STATUS BmcElogWrite (
 VOID *This,
 UINT8 *Data,
 UINT32 Revision,
 UINT64 *Key,
 UINTN DataSize,
 UINT16 *RecordId
 )
{
 //
 // Validate the protocol instance, send the record, and return the SEL ID.
 //
 BMC_ELOG_REFROTOCOL *Ref;
 UINT8 Buffer[16];
 UINT8 Response;
 UINT16 ReturnedRecordId;
 UINT8 ResponseSize;
 EFI_STATUS Status;

 //
 // Validate signature
 //
 Ref = (BMC_ELOG_REFROTOCOL *)((UINT8 *)This - 113);
 if (Ref->Signature != 0x66657252) {
 DebugAssert (
 "e:\\hs\\AmiIpmiPkg\\Ipmi\\BmcElog\\BmcElog.c",
 182,
 "CR has Bad Signature"
 );
 return EFI_INVALID_PARAMETER;
 }

 if (Ref->Revision != Revision) {
 return EFI_INVALID_PARAMETER;
 }

 //
 // Prepare buffer and open IPMI session
 //
 BmcElogZeroMem (Buffer, sizeof (Buffer));

 ResponseSize = 14;
 Status = ((EFI_STATUS ( *)(
 VOID *, UINT8, UINT64, UINT64,
 UINT64, UINT8, UINT8 *, UINT8 *
 )) (*(VOID **)((UINT8 *)mHobList + 0x3040) + 16))(
 *(VOID **)((UINT8 *)mHobList + 0x3040),
 10, // NetFn 0,
 64, // Get SEL Info 0,
 0,
 Buffer,
 &ResponseSize
 );

 if (EFI_ERROR (Status)) {
 return Status;
 }

 if (Buffer[5] & 0x80) {
 //
 // Check overflow bit; if set return EFI_OUT_OF_RESOURCES
 //
 return EFI_OUT_OF_RESOURCES;
 }

 //
 // DataSize must be <= 0x10 (max SEL record size)
 //
 if (DataSize > 16) {
 return EFI_INVALID_PARAMETER;
 }

 BmcElogCopyMem (Buffer, Data, 16);

 //
 // Send the Add SEL Entry command
 //
 {
 UINT8 AddEntryData[4];

 AddEntryData[0] = 2;
 Status = ((EFI_STATUS ( *)(
 VOID *, UINT8, UINT64, UINT64,
 UINT8 *, UINT32, UINT16 *, UINT8 *
 )) (*(VOID **)((UINT8 *)mHobList + 0x3040) + 16))(
 *(VOID **)((UINT8 *)mHobList + 0x3040),
 10,
 0,
 68, // Add SEL Entry Buffer,
 16,
 &ReturnedRecordId,
 AddEntryData
 );
 }

 if (!EFI_ERROR (Status)) {
 *RecordId = ReturnedRecordId;
 }

 return Status;
}

//
// HOB library support
//

/**Compares a GUID to a HOB header GUID.

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

 @retval TRUE GUIDs are equal.
 @retval FALSE GUIDs are not equal.
**/
BOOLEAN EFIAPI CompareGuid (
 CONST EFI_GUID *Guid1,
 CONST EFI_GUID *Guid2
 )
{
 //
 // Compare two GUIDs as two 64-bit values.
 //
 UINT64 *Data1 = (UINT64 *)Guid1;
 UINT64 *Data2 = (UINT64 *)Guid2;

 return (Data1[0] == Data2[0] && Data1[1] == Data2[1]);
}

/**Returns the first HOB matching a predefined GUID.

 @return Pointer to the HOB, or NULL if not found.
**/
VOID *EFIAPI GetFirstHob (
 VOID
 )
{
 //
 // Scan the configuration table for the HOB list and cache it.
 //
 EFI_PEI_HOB_POINTERS Hob;
 UINTN Index;
 UINTN HobCount;
 EFI_GUID *HobArray;

 if (mHobList != 0) {
 return (VOID *)mHobList;
 }

 mHobList = 0;

 HobCount = gST->NumberOfTableEntries;
 HobArray = (EFI_GUID *)gST->ConfigurationTable;

 for (Index = 0; Index < HobCount; Index++) {
 if (CompareGuid (&HobArray[Index], &gEfiHobListGuid)) {
 mHobList = (UINT64)&HobArray[Index];
 return (VOID *)mHobList;
 }
 }

 //
 // HOB list not found - assertion
 //
 DebugAssert (
 "e:\\hs\\MdePkg\\Library\\DxeHobLib\\HobLib.c",
 54,
 "!EFI_ERROR (Status)"
 );
 if (mHobList == 0) {
 DebugAssert (
 "e:\\hs\\MdePkg\\Library\\DxeHobLib\\HobLib.c",
 55,
 "mHobList != ((void *) 0)"
 );
 }

 return (VOID *)mHobList;
}

//
// Event notification handlers
//

/**Notification function for the UEFI Boot Services event.
 Clears the BootServices cache on exit boot services.
**/
VOID EFIAPI OnExitBootServices (
 IN EFI_EVENT Event,
 IN VOID *Context
 )
{
 //
 // Clear the cached BootServices pointer.
 //
 mBootServices = 0;
}

/**Notification function for virtual address change event.
 Re-locates the IPMI transport protocol.

 @param[in] Event The event that triggered this notification.
 @param[in] Context Not used.

 @retval EFI_SUCCESS Always returns success.
**/
EFI_STATUS EFIAPI OnVirtualAddressChange (
 IN EFI_EVENT Event,
 IN VOID *Context
 )
{
 //
 // Convert the cached transport pointer for the new virtual mapping.
 //
 if (mIpmiTransport != NULL) {
 return gRT->ConvertPointer (0, (VOID **)&mIpmiTransport);
 }
 return EFI_SUCCESS;
}

//
// Memory allocation helpers
//

/**Allocates a zeroed buffer of 145 bytes (size of REFROTOCOL structure).

 @param[in] AllocType Type of allocation.

 @return Pointer to allocated zeroed buffer, or NULL.
**/
VOID *AllocateZeroedRefProtocol (
 UINTN AllocType
 )
{
 //
 // Allocate zeroed storage for the protocol instance.
 //
 VOID *Buffer;
 EFI_STATUS Status;

 Status = gBS->AllocatePool (AllocType, 145, &Buffer);
 if (EFI_ERROR (Status)) {
 return NULL;
 }

 return Buffer;
}

/**Frees the allocated protocol buffer on error.

 Frees the buffer (buf) and asserts if the free operation fails.
**/
VOID FreeBmcElogProtocolBuffer (
 VOID
 )
{
 //
 // Free the protocol buffer allocated during initialization.
 //
 EFI_STATUS Status;

 Status = gBS->FreePool ((VOID *)mIpmiTransport);
 if (EFI_ERROR (Status)) {
 BmcElogDispatchIpmiCommand (0x80000000, (UINT64)"\nASSERT_EFI_ERROR (Status = %r)\n");
 DebugAssert (
 "e:\\hs\\MdePkg\\Library\\UefiMemoryAllocationLib\\MemoryAllocationLib.c",
 819,
 "!EFI_ERROR (Status)"
 );
 }
}

/**Entry point for BmcElog DXE driver.

 Initializes global state, locates the IPMI transport protocol,
 allocates and initializes the REFROTOCOL structure, then registers the protocol interface with the UEFI Boot Services.

 @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 entry point is executed successfully.
 @retval EFI_OUT_OF_RESOURCES Memory allocation failed.
 @retval EFI_UNSUPPORTED IPMI transport not found or unsupported.
 @retval other Some error occurs during registration.
**/
EFI_STATUS EFIAPI BmcElogEntryPoint (
 IN EFI_HANDLE ImageHandle,
 IN EFI_SYSTEM_TABLE *SystemTable
 )
{
 EFI_STATUS Status;
 BOOLEAN StatusEnabled;
 UINT8 EnableStatus;
 UINT64 Key;
 UINT16 RecordId;
 BMC_ELOG_REFROTOCOL *RefProtocol;

 //
 // Initialize module globals
 //
 gST = SystemTable;
 gBS = SystemTable->BootServices;
 gRT = SystemTable->RuntimeServices;
 gImageHandle = ImageHandle;

 //
 // Validate pointers
 //
 if (ImageHandle == NULL) {
 DebugAssert (
 "e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
 51,
 "gImageHandle != ((void *) 0)"
 );
 }
 if (SystemTable == NULL) {
 DebugAssert (
 "e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
 57,
 "gST != ((void *) 0)"
 );
 }
 if (gBS == NULL) {
 DebugAssert (
 "e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
 63,
 "gBS != ((void *) 0)"
 );
 }
 if (gRT == NULL) {
 DebugAssert (
 "e:\\hs\\MdePkg\\Library\\UefiRuntimeServicesTableLib\\UefiRuntimeServicesTableLib.c",
 47,
 "gRT != ((void *) 0)"
 );
 }

 //
 // Cache boot services pointer for notification cleanup.
 //
 mBootServices = (UINT64)gBS;
 mRuntimeServices = (UINT64)gRT;

 //
 // Register ExitBootServices event to clear BS pointer
 //
 gBS->CreateEvent (
 EVT_SIGNAL_EXIT_BOOT_SERVICES,
 TPL_NOTIFY,
 OnExitBootServices,
 NULL,
 NULL
 );

 //
 // Register VirtualAddressChange event
 //
 gBS->CreateEvent (
 EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE,
 TPL_NOTIFY,
 OnVirtualAddressChange,
 NULL,
 &gRT
 );

 //
 // Get the HOB list
 //
 GetFirstHob ();

 //
 // Get the cached transport pointer
 // Locate the IPMI transport protocol and register BmcElog.
 //
 {
 UINT8 InitData[16];
 UINT8 Response;
 UINT64 TransportHandle;
 EFI_GUID *ProtocolGuid;

 ZeroMem (InitData, sizeof (InitData));

 //
 // Locate IPMI transport protocol handle
 //
 Status = gBS->LocateProtocol (
 &gEfiIpmiTransportProtocolGuid,
 NULL,
 &TransportHandle
 );
 if (EFI_ERROR (Status)) {
 return Status;
 }

 //
 // Reserve SEL (for subsequent clear operations)
 //
 ZeroMem (InitData, sizeof (InitData));
 InitData[0] = 1; // Reserve SEL command data Status = ((EFI_STATUS ( *)(
 UINT64, UINT8, UINT64, UINT8, UINT8,
 UINT8, UINT8 *, UINT64 *
 )) (*(UINT64 *)((UINT8 *)TransportHandle + 16) + 16))(
 TransportHandle,
 6, // Storage NetFn 0,
 15, // Reserve SEL 0,
 InitData,
 &Response,
 &TransportHandle
 );

 if (EFI_ERROR (Status)) {
 return Status;
 }

 //
 // Check response for SEL support
 //
 if (InitData[1] & 0x04) {
 UINT8 SelVersion = InitData[3];

 if (SelVersion == 0x51 || SelVersion == 0x02) {
 //
 // Allocate and initialize the REFROTOCOL structure
 //
 RefProtocol = (BMC_ELOG_REFROTOCOL *)AllocateZeroedRefProtocol (EfiBootServicesData);
 if (RefProtocol == NULL) {
 return EFI_OUT_OF_RESOURCES;
 }

 //
 // Initialize the protocol structure
 //
 RefProtocol->Signature = 0x66657252; // "Refr"
 RefProtocol->Revision = 1;

 //
 // Register function pointers into the REFROTOCOL at known offsets
 //
 *(UINT64 *)((UINT8 *)RefProtocol + 113) = (UINT64)BmcElogWrite;
 *(UINT64 *)((UINT8 *)RefProtocol + 121) = (UINT64)BmcElogRead;
 *(UINT64 *)((UINT8 *)RefProtocol + 129) = (UINT64)BmcElogClear;
 *(UINT64 *)((UINT8 *)RefProtocol + 137) = (UINT64)BmcElogSetStatus;

 //
 // Install the protocol
 //
 Status = gBS->InstallProtocolInterface (
 &gImageHandle,
 &gEfiBmcElogProtocolGuid,
 EFI_NATIVE_INTERFACE,
 NULL
 );
 if (EFI_ERROR (Status)) {
 FreeBmcElogProtocolBuffer ();
 return Status;
 }

 //
 // Enable SEL logging (if already enabled, ensure it stays)
 //
 EnableStatus = 1;
 BmcElogSetStatus (
 (UINT8 *)RefProtocol + 113,
 1,
 &EnableStatus,
 &StatusEnabled
 );

 return EFI_SUCCESS;
 }
 }

 return EFI_UNSUPPORTED;
 }
}