/** @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;
}
}