Newer
Older
AMI-Aptio-BIOS-Reversed / BmcElog / BmcElog.c
@Ajax Dong Ajax Dong 2 days ago 26 KB Init
/** @file BmcElog.c - Decompilation of BmcElog.efi 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
//
/**Stack misuse: the debug/assert library uses the IPMI transport sub_1B68() -> sub_1A60() to obtain a reporting handle, then calls through offset +8 to deliver the assert message.

 In the original source these are DebugAssert() calls.
**/
VOID EFIAPI DebugAssert (
 CONST CHAR8 *FileName,
 UINTN LineNumber,
 CONST CHAR8 *Description
 )
{
 //
 // sub_1B68:
 // result = sub_1A60();
 // if (result)
 // return (*(UINT64 ( **)(UINT64, UINT64, UINT64))(result + 8))
 // (FileName, LineNumber, Description);
 //
 // This is the DebugAssert() routine from DebugLib.
 //
 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 InternalCopyMem (
 VOID *Destination,
 const VOID *Source,
 UINTN Length
 )
{
 //
 // sub_19C0: validates overlap, then calls sub_1000
 // sub_1000: qmemcpy with 8-byte-aligned bulk then remainder
 //
 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;
 }

 //
 // sub_1000: actual copy with overlap handling
 //
 {
 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 InternalZeroMem (
 VOID *Buffer,
 UINTN Length
 )
{
 //
 // sub_1070: ZeroMem implementation
 //
 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 *GetIpmiTransport (
 VOID
 )
{
 //
 // sub_1A60:
 // If mIpmiTransport cached, return it.
 // Otherwise:
 // If mBootServices exists:
 // Check if buffer size needed <= 0x10
 // Allocate pool
 // Locate protocol handle for the IPMI transport GUID (unk_3000)
 // Cache in mIpmiTransport
 //
 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 IpmiDetectInterface (
 UINT64 IoOperation,
 UINT64 Param2,
 ...
 )
{
 //
 // sub_1AE8:
 // Get transport via GetIpmiTransport()
 // Read CMOS 0x70/0x71 register 0x4B to get BMC interface type
 // Map to transport interface ID:
 // 1 -> 0x80000004 (KCS)
 // 2 -> 0x80000006 (SMBUS) [value 3 would be BT]
 // 3 -> BT
 // If IoOperation matches the interface, call transport.
 //
 VA_LIST Args;
 UINT8 BmcIfType;
 UINT64 InterfaceId;
 VOID *Transport;

 VA_START (Args, Param2);

 Transport = GetIpmiTransport ();
 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 IpmiSendCommand (
 UINT8 NetFunction,
 UINT8 Command,
 VOID *CommandData,
 UINT32 CommandDataSize,
 VOID *ResponseData,
 UINT32 *ResponseDataSize
 )
{
 //
 // sub_138C:
 // Loop up to 512 iterations sending the IPMI command via
 // the transport protocol (qword_3040).
 // If transport returns CC_CANNOT_RETRY (0xC5), retry with
 // a "Write" sub-command (0x42 / 0x42 = Write).
 // Otherwise return success or timeout.
 //
 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; // "RSS" constant from sub_138C

 //
 // 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 IpmiGetResponse (
 UINT8 NetFunction,
 UINT8 Command,
 VOID *ResponseData,
 UINT32 *ResponseDataSize
 )
{
 //
 // sub_145C:
 // Opens GetResponse command to the IPMI transport.
 // Uses the BMC_ELOG_REFROTOCOL signature validation.
 // Copies data from transport buffer to output.
 //
 UINT8 Buffer[16];
 UINTN BufferSize;
 UINT16 RecordId;
 UINT8 Response;
 EFI_STATUS Status;

 BufferSize = 16;

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

 if (BufferSize > 16) {
 return EFI_INVALID_PARAMETER;
 }

 InternalCopyMem (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
 )
{
 //
 // sub_15A0:
 // Validates REFROTOCOL signature at This-113,
 // checks revision, builds read request via transport.
 // Copies 16 bytes of event data to output.
 //
 BMC_ELOG_REFROTOCOL *Ref;
 UINT8 Request[6];
 UINT16 ResponseRecordId;
 UINT16 ReadData[4];
 EFI_STATUS Status;

 ZeroMem (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;
 InternalCopyMem (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
 )
{
 //
 // sub_16A4:
 // Validates signature, performs clear/reserve SEL operation.
 // If a3 (Data) is non-NULL, does a full SEL erase with record ID reservation.
 // If a3 is NULL, does a "Get SEL Info" then "Clear SEL" via sub_138C.
 //
 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 (full clear)
 //
 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 using sub_138C
 //
 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 IpmiSendCommand (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
 )
{
 //
 // sub_188C:
 // Validates signature and revision.
 // Sends or gets SEL info to read/write bit 3 of the SEL operation
 // support flags (bit 3 = SEL enable/disable).
 //
 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
 )
{
 //
 // sub_145C:
 // Validates REFROTOCOL signature, checks revision,
 // opens an IPMI session, sends command data,
 // and returns the record 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
 //
 ZeroMem (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;
 }

 InternalCopyMem (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
 )
{
 //
 // sub_1D94 / sub_1E04:
 // Compare two GUIDs as two 64-bit values.
 // sub_1E04 reads a UINT64 from a potentially unaligned address.
 //
 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
 )
{
 //
 // sub_1C60:
 // Scans the HOB list (from SystemTable + 104/112) looking for
 // a HOB with a specific GUID (unk_3020, unk_3028).
 // Caches result in mHobList.
 // If no matching HOB, asserts.
 //
 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
 )
{
 //
 // sub_1BA8:
 // Clears 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
 )
{
 //
 // sub_1BB4:
 // If mIpmiTransport is set, calls RuntimeServices->ConvertPointer
 // to convert the pointer for the new virtual address 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
 )
{
 //
 // sub_1BDC:
 // AllocatePool and ZeroMem for the BmcElog protocol structure.
 //
 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
 )
{
 //
 // sub_1C14:
 // Frees the BmcElog protocol buffer allocated during initialization.
 //
 EFI_STATUS Status;

 Status = gBS->FreePool ((VOID *)mIpmiTransport);
 if (EFI_ERROR (Status)) {
 IpmiDetectInterface (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 (used in sub_1BA8-style notification cleanups)
 //
 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
 // sub_1210: 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;
 }
}