Newer
Older
AMI-Aptio-BIOS-Reversed / DxeSelStatusCode / DxeSelStatusCode.c
@Ajax Dong Ajax Dong 2 days ago 34 KB Init
/** @file
  DxeSelStatusCode - AMI IPMI SEL Status Code DXE Driver

  This DXE driver maps UEFI status codes to IPMI SEL (System Event Log)
  records. It reads configuration from the "ServerSetup" UEFI variable,
  locates the IPMI transport protocol, and registers event notification
  callbacks to write SEL records when status codes are reported during
  boot and runtime.

  Copyright (c) AMI. All rights reserved.
**/

#include <Uefi.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiRuntimeServicesTableLib.h>
#include <Library/UefiRuntimeLib.h>
#include <Library/DxeHobLib.h>
#include <Library/BaseLib.h>
#include "DxeSelStatusCode.h"

//
// Global Variables
//
EFI_HANDLE              gImageHandle       = NULL;
EFI_SYSTEM_TABLE        *gSystemTable      = NULL;
EFI_BOOT_SERVICES       *gBootServices     = NULL;
EFI_RUNTIME_SERVICES    *gRuntimeServices  = NULL;
EFI_RUNTIME_SERVICES    *gRuntimeServicesCopy = NULL;  // RuntimeServices_0
EFI_RUNTIME_SERVICES    *gRuntimeServices2 = NULL;      // RuntimeServices_1

VOID                    *gIpmiTransport    = NULL;       // qword_4418

// SEL configuration state
UINT8                   gSelRedundancy     = 0;          // byte_4370
UINT8                   gEnableRedundancy  = 0;          // byte_4410
UINT8                   gSelRedundancyMode = 0;          // byte_4421
UINT8                   gSelOperationMode  = 0;          // n2 (0x4420)

VOID                    *gDebugPrintProtocol = NULL;     // qword_43C0
VOID                    *gHobList           = NULL;      // qword_43C8
VOID                    *gSmbiosProtocol    = NULL;      // qword_43D8
UINT8                   gSmbiosInitialized  = 0;         // byte_43D0

EFI_EVENT               gVirtualAddressChangeEvent = NULL;   // qword_4408
EFI_EVENT               gBootServicesCleanupEvent  = NULL;   // qword_43B0
EFI_EVENT               gRuntimeCleanupEvent       = NULL;   // qword_43F0
EFI_EVENT               gSmbiosCleanupEvent        = NULL;   // qword_43E8
EFI_EVENT               gSmmCommEvent              = NULL;   // qword_43F8
EFI_EVENT               gRuntimeProtocolCleanupEvent = NULL; // qword_43A8

VOID                    *gBootServicesCopy = NULL;           // BootServices_0 at qword_43B8

//
// SEL Event Record Mapping Table
//
// 61 entries x 12 bytes mapping UEFI status code classes to IPMI
// sensor number, sensor type, event direction, and event data.
//
SEL_EVENT_MAP_ENTRY gSelEventMapTable[] = {
  // {StatusCodeValue, StatusCodeInstance, SensorNum, SensorType, EvDir, EvData2}
  {0x00051005, 0x00000001, 0x0F, 0xC2, 0x01, 0xFF},
  {0x02080000, 0x00000001, 0x0F, 0xC2, 0x02, 0xFF},
  {0x00011007, 0x00000001, 0x0F, 0xC2, 0x03, 0xFF},
  {0x03051006, 0x00000001, 0x0F, 0xC2, 0x04, 0xFF},
  {0x03050007, 0x00000001, 0x0F, 0xC2, 0x05, 0xFF},
  {0x02020000, 0x00000001, 0x0F, 0xC2, 0x06, 0xFF},
  {0x02011001, 0x00000001, 0x0F, 0xC2, 0x07, 0xFF},
  {0x03051000, 0x00000001, 0x0F, 0xC2, 0x08, 0xFF},
  {0x01030000, 0x00000001, 0x0F, 0xC2, 0x09, 0xFF},
  {0x00011001, 0x00000001, 0x0F, 0xC2, 0x0A, 0xFF},
  {0x020B0000, 0x00000001, 0x0F, 0xC2, 0x0B, 0xFF},
  {0x01010000, 0x00000001, 0x0F, 0xC2, 0x0C, 0xFF},
  {0x00020000, 0x00000001, 0x0F, 0xC2, 0x0D, 0xFF},
  {0x03031006, 0x00000001, 0x0F, 0xC2, 0x12, 0xFF},
  {0x03051002, 0x00000001, 0x0F, 0xC2, 0x13, 0xFF},
  {0x01011001, 0x00000001, 0x0F, 0xC2, 0x17, 0xFF},
  {0x01021000, 0x00000001, 0x0F, 0xC2, 0x18, 0xFF},
  {0x00011000, 0x00000001, 0x0F, 0xC2, 0x19, 0xFF},
  {0x03101019, 0x00000001, 0x0F, 0xC2, 0x13, 0xFF},
  {0x00000000, 0x00000002, 0x0F, 0xC0, 0x00, 0xFF},
  {0x00051009, 0x00000002, 0x0F, 0xC0, 0x01, 0xFF},
  {0x0005100A, 0x00000002, 0x0F, 0xC0, 0x02, 0xFF},
  {0x02081002, 0x00000002, 0x0D, 0x02, 0xFF, 0xFF},
  {0x02080005, 0x00000002, 0x0D, 0x01, 0xFF, 0xFF},
  {0x90000000, 0x00000002, 0x0F, 0xC0, 0x04, 0xFF},
  {0x02080006, 0x00000002, 0x0F, 0xC0, 0x06, 0xFF},
  {0x01010006, 0x00000002, 0x0F, 0xC0, 0x07, 0xFF},
  {0x01080006, 0x00000002, 0x0F, 0xC0, 0x08, 0xFF},
  {0x01030006, 0x00000002, 0x0F, 0xC0, 0x09, 0xFF},
  {0x01030003, 0x00000002, 0x0F, 0xC0, 0x0A, 0xFF},
  {0x00020006, 0x00000002, 0x0F, 0xC0, 0x0B, 0xFF},
  {0x00011007, 0x00000002, 0x0F, 0xC0, 0x0C, 0xFF},
  {0x00010001, 0x00000002, 0x0F, 0xC0, 0x0D, 0xFF},
  {0x00010001, 0x00000002, 0x07, 0x08, 0xFF, 0xFF},
  {0x00011002, 0x00000002, 0x07, 0x05, 0xFF, 0xFF},
  {0x00011006, 0x00000002, 0x07, 0x01, 0xFF, 0xFF},
  {0x00011004, 0x00000002, 0x07, 0x02, 0xFF, 0xFF},
  {0x00011005, 0x00000002, 0x07, 0x00, 0xFF, 0xFF},
  {0x03150011, 0x00000002, 0x10, 0x04, 0xFF, 0xFF},
  {0x02011000, 0x00000002, 0x13, 0x04, 0xFF, 0xFF},
  {0x02011001, 0x00000002, 0x13, 0x05, 0xFF, 0xFF},
  {0x00050004, 0x00000002, 0x0C, 0x07, 0xFF, 0xFF},
  {0x00050001, 0x00000002, 0x0C, 0x24, 0xFF, 0x00},
  {0x00050011, 0x00000002, 0x0C, 0x24, 0xFF, 0x01},
  {0x00050021, 0x00000002, 0x0C, 0x24, 0xFF, 0x02},
  {0x00050031, 0x00000002, 0x0C, 0x24, 0xFF, 0x03},
  {0x00050041, 0x00000002, 0x0C, 0x24, 0xFF, 0x04},
  {0x00050051, 0x00000002, 0x0C, 0x24, 0xFF, 0x05},
  {0x00050061, 0x00000002, 0x0C, 0x24, 0xFF, 0x06},
  {0x00050071, 0x00000002, 0x0C, 0x24, 0xFF, 0x07},
  {0x00051002, 0x00000002, 0x0C, 0x00, 0xFF, 0xFF},
  {0x00051003, 0x00000002, 0x0C, 0x01, 0xFF, 0xFF},
  {0x00050005, 0x00000001, 0x0C, 0x28, 0xFF, 0x00},
  {0x00050015, 0x00000001, 0x0C, 0x28, 0xFF, 0x01},
  {0x00050025, 0x00000001, 0x0C, 0x28, 0xFF, 0x02},
  {0x00050035, 0x00000001, 0x0C, 0x28, 0xFF, 0x03},
  {0x00050045, 0x00000001, 0x0C, 0x28, 0xFF, 0x04},
  {0x00050055, 0x00000001, 0x0C, 0x28, 0xFF, 0x05},
  {0x00050065, 0x00000001, 0x0C, 0x28, 0xFF, 0x06},
  {0x00050075, 0x00000001, 0x0C, 0x28, 0xFF, 0x07},
  {0x00051001, 0x00000001, 0x0C, 0x06, 0xFF, 0xFF},
};

//
// Function Prototypes
//
EFI_STATUS
EFIAPI
UefiBootServicesTableLibConstructor (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  );

EFI_STATUS
ReportStatusCodeLibDestructor (
  VOID
  );

VOID
EFIAPI
BootServicesCleanup (
  VOID
  );

VOID
EFIAPI
RuntimeProtocolCleanup (
  VOID
  );

//----------------------------------------------------------------------
// Module Entry Point
//----------------------------------------------------------------------

EFI_STATUS
EFIAPI
ModuleEntryPoint (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS  Status;

  UefiBootServicesTableLibConstructor (ImageHandle, SystemTable);
  Status = SelStatusCodeEntryInit ();
  if (EFI_ERROR (Status)) {
    ReportStatusCodeLibDestructor ();
  }
  return Status;
}

//----------------------------------------------------------------------
// UefiBootServicesTableLibConstructor (formerly sub_10F8)
//
// Initializes UEFI boot services table library state. Registers
// exit boot services and set virtual address map events. Locates
// runtime protocol and debug print protocol instances.
//----------------------------------------------------------------------

EFI_STATUS
EFIAPI
UefiBootServicesTableLibConstructor (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS  Status;

  // Cache global pointers
  gImageHandle = ImageHandle;
  ASSERT (gImageHandle != NULL);

  gSystemTable = SystemTable;
  ASSERT (gSystemTable != NULL);

  gBootServices = SystemTable->BootServices;
  ASSERT (gBootServices != NULL);

  gRuntimeServices = SystemTable->RuntimeServices;
  ASSERT (gRuntimeServices != NULL);

  gRuntimeServicesCopy = gRuntimeServices;

  // Register BootServices cleanup event
  Status = gBootServices->CreateEvent (
                            EVT_SIGNAL_EXIT_BOOT_SERVICES,
                            TPL_CALLBACK,
                            BootServicesCleanup,
                            NULL,
                            &gBootServicesCleanupEvent
                            );
  ASSERT_EFI_ERROR (Status);

  // Register runtime protocol cleanup event
  Status = gBootServices->CreateEvent (
                            EVT_SIGNAL_EXIT_BOOT_SERVICES,
                            TPL_CALLBACK,
                            RuntimeProtocolCleanup,
                            NULL,
                            &gBootServicesCleanupEvent
                            );
  ASSERT_EFI_ERROR (Status);

  HobGetGuidEntry ();

  // Register SetVirtualAddressMap notify for runtime library
  Status = EfiCreateEventVirtualAddressMap (
             TPL_NOTIFY,
             SetVirtualAddressMapHandler
             );
  ASSERT_EFI_ERROR (Status);

  // Register runtime protocol notify
  Status = gBootServices->CreateEvent (
                            EVT_NOTIFY_SIGNAL,
                            TPL_NOTIFY,
                            nullsub_1,
                            NULL,
                            &gRuntimeProtocolCleanupEvent
                            );
  ASSERT_EFI_ERROR (Status);

  SmbiosProtocolInit ();

  // Register SMBIOS cleanup
  Status = gBootServices->CreateEvent (
                            EVT_NOTIFY_SIGNAL,
                            TPL_NOTIFY,
                            SmbiosProtocolCleanup,
                            NULL,
                            &gSmbiosCleanupEvent
                            );
  ASSERT_EFI_ERROR (Status);

  // Register ReportStatusCode shutdown
  Status = gBootServices->CreateEvent (
                            EVT_NOTIFY_SIGNAL,
                            TPL_NOTIFY,
                            ReportStatusCodeNotifyShutdown,
                            NULL,
                            &gSmbiosCleanupEvent
                            );
  ASSERT_EFI_ERROR (Status);

  return Status;
}

//----------------------------------------------------------------------
// ReportStatusCodeLibDestructor (formerly sub_1424)
//
// Closes all registered events and frees protocol resources.
// Called on error unwind or module unload.
//----------------------------------------------------------------------

EFI_STATUS
ReportStatusCodeLibDestructor (
  VOID
  )
{
  ASSERT (gBootServices != NULL);

  if (gSmbiosCleanupEvent != NULL) {
    gBootServices->CloseEvent (gSmbiosCleanupEvent);
    ASSERT_EFI_ERROR (Status);
  }
  if (gRuntimeCleanupEvent != NULL) {
    gBootServices->CloseEvent (gRuntimeCleanupEvent);
    ASSERT_EFI_ERROR (Status);
  }
  if (gSmmCommEvent != NULL) {
    gBootServices->CloseEvent (gSmmCommEvent);
    ASSERT_EFI_ERROR (Status);
  }
  if (gVirtualAddressChangeEvent != NULL) {
    gBootServices->CloseEvent (gVirtualAddressChangeEvent);
    ASSERT_EFI_ERROR (Status);
  }

  gBootServices->CloseEvent (gRuntimeProtocolCleanupEvent);
  gBootServices->CloseEvent (gBootServicesCleanupEvent);

  return EFI_SUCCESS;
}

//----------------------------------------------------------------------
// SetVirtualAddressMapHandler (formerly sub_15CC)
//
// Converts gRuntimeServicesCopy and gIpmiTransport for virtual
// addressing after SetVirtualAddressMap.
//----------------------------------------------------------------------

VOID
EFIAPI
SetVirtualAddressMapHandler (
  VOID
  )
{
  EfiConvertPointer (0, (VOID **)&gRuntimeServicesCopy);
  EfiConvertPointer (0, (VOID **)&gIpmiTransport);
}

//----------------------------------------------------------------------
// SelMapStatusCodeToEventData (formerly sub_15FC)
//
// Searches the SEL event mapping table for an entry matching the
// given status code value and instance. If found, builds a 16-byte
// IPMI SEL event record data buffer.
//
// @param  StatusCodeValue     UEFI status code value
// @param  StatusCodeInstance  Instance or sub-classifier
// @param  RecordData          Output buffer for record data
//
// @retval EFI_SUCCESS         Record data constructed
// @retval EFI_NOT_FOUND       No matching mapping
//----------------------------------------------------------------------

EFI_STATUS
SelMapStatusCodeToEventData (
  IN  UINT32  StatusCodeValue,
  IN  UINT32  StatusCodeInstance,
  OUT UINT8   *RecordData
  )
{
  UINTN                 Index;
  SEL_EVENT_MAP_ENTRY   *Entry;

  Index = 0;
  for (Index = 0; Index < ARRAY_SIZE (gSelEventMapTable); Index++) {
    if (gSelEventMapTable[Index].StatusCodeValue == StatusCodeValue &&
        gSelEventMapTable[Index].StatusCodeInstance == StatusCodeInstance) {
      Entry = &gSelEventMapTable[Index];
      break;
    }
  }

  if (Entry == NULL) {
    return EFI_NOT_FOUND;
  }

  // Check redundancy status
  if (SelCheckRedundancyStatus (StatusCodeValue, StatusCodeInstance, Entry) == 1) {
    if (gSelOperationMode == 1) {
      // Clear the record first
      SelManageEventLog (StatusCodeValue, 1);
    } else if (gSelRedundancy && gSelOperationMode == 2) {
      // Add with redundancy
      SelManageEventLog (StatusCodeValue, 2);
    }
  }

  // Build record data structure
  *(UINT16 *)(RecordData + 0)  = 0;          // Record ID = 0
  *(UINT8  *)(RecordData + 2)  = 2;           // Record Type = 2 (SEL)
  *(UINT32 *)(RecordData + 3)  = 0;           // Timestamp = 0
  *(UINT16 *)(RecordData + 7)  = 1;           // Generator ID
  *(UINT8  *)(RecordData + 9)  = 4;           // Event Message Format Version
  *(UINT8  *)(RecordData + 10) = Entry->SensorNumber;
  *(UINT16 *)(RecordData + 11) = 0x6F00;      // Event Type + Event Direction
  *(UINT8  *)(RecordData + 13) = Entry->SensorType;
  *(UINT8  *)(RecordData + 14) = Entry->EventDirType;
  *(UINT8  *)(RecordData + 15) = Entry->EventData2;

  return EFI_SUCCESS;
}

//----------------------------------------------------------------------
// SelWriteEventToRecord (formerly sub_16D4)
//
// Writes an IPMI SEL event record using the transport protocol.
// Handles reserve and append operations.
//----------------------------------------------------------------------

EFI_STATUS
SelWriteEventToRecord (
  IN  VOID    *Transport,
  IN  UINT8   OperationMode,
  IN  UINT32  StatusCodeValue,
  IN  UINT32  StatusCodeInstance
  )
{
  EFI_STATUS  Status;
  UINT8       RecordData[16];
  UINT16      RecordId;
  INTN        N16;
  UINT8       ResponseSize;
  UINT8       CompletionCode;

  RecordId = 0;

  // Skip if redundancy not needed
  if (OperationMode == 1 && !gSelRedundancyMode) return EFI_SUCCESS;
  if (OperationMode == 2 && !gEnableRedundancy) return EFI_SUCCESS;

  // Map status code to SEL event data
  Status = SelMapStatusCodeToEventData (StatusCodeValue, StatusCodeInstance, RecordData);
  if (EFI_ERROR (Status)) {
    return EFI_SUCCESS;
  }

  ResponseSize = 2;
  N16 = 16;
  // IPMI transport write command
  return gIpmiTransport->Write (
                           gIpmiTransport,
                           IPMI_SEL_CMD_RESERVE,            // NetFn 0x0A
                           0,
                           IPMI_SEL_CMD_ADD,                 // Command 0x44
                           RecordData,
                           N16,
                           &RecordId,
                           &ResponseSize
                           );
}

//----------------------------------------------------------------------
// SelClearEventRecord (formerly sub_175C)
//
// Clears an IPMI SEL event record. Validates sensor type.
//----------------------------------------------------------------------

EFI_STATUS
SelClearEventRecord (
  IN  UINT32  StatusCodeType,
  IN  UINT32  StatusCodeValue
  )
{
  UINTN     Index;
  SEL_EVENT_MAP_ENTRY *Entry;

  // Validate type
  if ((UINT8)StatusCodeType < 1 || (UINT8)StatusCodeType > 2) {
    return EFI_SUCCESS;
  }

  Index = 0;
  Entry = NULL;
  for (Index = 0; Index < ARRAY_SIZE (gSelEventMapTable); Index++) {
    if (gSelEventMapTable[Index].StatusCodeValue == StatusCodeValue &&
        (UINT8)StatusCodeType == gSelEventMapTable[Index].EventDirType) {
      Entry = &gSelEventMapTable[Index];
      break;
    }
  }

  if (Entry == NULL) {
    return EFI_NOT_FOUND;
  }

  // Call IPMI transport to clear SEL
  return gRuntimeServicesCopy->SetVariable (
                                 L"ServerSetup",
                                 &gServerSetupVariableGuid,
                                 (UINTN)StatusCodeValue,
                                 sizeof(SERVER_SETUP_VARIABLE),
                                 &gServerSetupVariable
                                 );
}

//----------------------------------------------------------------------
// SelStatusCodeEntryInit (formerly sub_17E0)
//
// Main initialization for the SEL Status Code driver. Reads platform
// configuration from the "ServerSetup" UEFI variable, locates the IPMI
// transport protocol, registers SetVirtualAddressMap notification, and
// installs the SEL event clearing/smm notification protocol.
//----------------------------------------------------------------------

EFI_STATUS
SelStatusCodeEntryInit (
  VOID
  )
{
  EFI_STATUS          Status;
  SERVER_SETUP_VARIABLE ServerSetup;
  UINTN               DataSize;
  IPMI_Transport      *Transport;
  VOID                *Interface;

  ServerSetup.Value = 33554433;  // Default: 0x2000001
  DataSize = 1072;
  Interface = NULL;

  // Get IPMI transport protocol
  Status = gBootServices->LocateProtocol (
                            &gIpmiTransportProtocolGuid,
                            NULL,
                            &gIpmiTransport
                            );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  // Read ServerSetup variable
  Status = gRuntimeServices->GetVariable (
                               L"ServerSetup",
                               &gServerSetupVariableGuid,
                               &ServerSetup.Attributes,
                               &DataSize,
                               &ServerSetup
                               );
  if (EFI_ERROR (Status)) {
    ServerSetup.Value = 33554433;
    ServerSetup.Bits.SelEnabled = 0;
    ServerSetup.Bits.SelOperation = 1;
  }

  if (!ServerSetup.Bits.SelEnabled) {
    return EFI_UNSUPPORTED;
  }

  // Handle SEL clear operation
  if (ServerSetup.Bits.SelOperation == 1 || ServerSetup.Bits.SelOperation == 2) {
    SelManageEventLog (ServerSetup.Value, 1);
    if (ServerSetup.Bits.SelOperation == 1) {
      ServerSetup.Bits.SelOperation = 0;
      Status = gRuntimeServices->SetVariable (
                                   L"ServerSetup",
                                   &gServerSetupVariableGuid,
                                   ServerSetup.Attributes,
                                   DataSize,
                                   &ServerSetup
                                   );
      ASSERT_EFI_ERROR (Status);
    }
  }

  gSelOperationMode = ServerSetup.Bits.ReservePolicy;

  // Check redundancy
  if (SelCheckRedundancyStatus (0, 0, 0) == 1 && gSelOperationMode == 1) {
    SelManageEventLog (0, 1);
  }

  // Configure SEL event handling
  if (ServerSetup.Bits.EventHandling != 0) {
    gSelRedundancyMode = (ServerSetup.Bits.EventHandling == 1 || ServerSetup.Bits.EventHandling == 3);
    gEnableRedundancy = (ServerSetup.Bits.EventHandling <= 2);

    // Register SMM communication protocol
    Status = gBootServices->InstallProtocolInterface (
                              &Interface,
                              &gSmmCommunicationProtocolGuid,
                              EFI_NATIVE_INTERFACE,
                              SelWriteEventToRecord
                              );
    if (!EFI_ERROR (Status)) {
      // Register SetVirtualAddressMap event
      Status = gBootServices->CreateEvent (
                                EVT_NOTIFY_SIGNAL,
                                TPL_NOTIFY,
                                SetVirtualAddressMapHandler
                                );
      if (!EFI_ERROR (Status)) {
        // Locate SMM communication/SMBIOS dispatch protocol
        Status = gBootServices->LocateProtocol (
                                  &gSmbiosDispatchProtocolGuid,
                                  NULL,
                                  &v16
                                  );
        if (!EFI_ERROR (Status)) {
          // Register clear handler
          (*v16) (SelClearEventRecord, 31);
        }
        ASSERT_EFI_ERROR (Status);
      }
      return EFI_SUCCESS;
    }
  }

  return EFI_UNSUPPORTED;
}

//----------------------------------------------------------------------
// DebugPrintProtocolInit (formerly sub_1A48)
//
// Allocates memory and locates the debug print protocol. Used for
// ASSERT and debug output.
//----------------------------------------------------------------------

VOID *
DebugPrintProtocolInit (
  VOID
  )
{
  VOID      *Protocol;
  UINTN     PoolSize;

  if (gDebugPrintProtocol != NULL) {
    return gDebugPrintProtocol;
  }

  if (gBootServicesCopy != NULL) {
    PoolSize = gBootServicesCopy->GetPoolSize (NULL);
    if (PoolSize <= 16) {
      Status = gBootServicesCopy->LocateProtocol (
                                    &gDebugPortProtocolGuid,
                                    NULL,
                                    &gDebugPrintProtocol
                                    );
      if (EFI_ERROR (Status)) {
        gDebugPrintProtocol = NULL;
      }
    }
  }

  return gDebugPrintProtocol;
}

//----------------------------------------------------------------------
// DebugPrintWithLevel (formerly sub_1AD0)
//
// Checks current debug verbosity level (via CMOS) and prints message
// if level matches. Used by ASSERT_EFI_ERROR macro output.
//----------------------------------------------------------------------

EFI_STATUS
DebugPrintWithLevel (
  IN  UINTN       ErrorLevel,
  IN  CONST CHAR8 *Format,
  ...
  )
{
  VA_LIST           Marker;
  EFI_STATUS        Status;
  DEBUG_PRINT_PROTOCOL *Protocol;
  UINT8             DebugLevel;
  UINT8             CmosValue;
  UINT8             EffectiveLevel;

  Protocol = DebugPrintProtocolInit ();
  if (Protocol != NULL) {
    // Read debug level from CMOS (offset 0x4B)
    CmosValue = IoRead8 (0x70);
    CmosValue = (CmosValue & 0x80) | 0x4B;
    IoWrite8 (0x70, CmosValue);
    DebugLevel = IoRead8 (0x71);

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

    EffectiveLevel = DebugLevel - 1;
    if (EffectiveLevel <= 0xFD) {
      Status = 0x7FFFFFF4;
      if (DebugLevel == 1) {
        Status = 0x7FFFFFF4;
      }
    }

    if (Status & ErrorLevel) {
      VA_START (Marker, Format);
      Status = Protocol->Print (ErrorLevel, Format, &Marker);
      VA_END (Marker);
    }
  }

  return Status;
}

//----------------------------------------------------------------------
// DebugAssert (formerly sub_1B50)
//
// Standard EDK2 ASSERT handler. Prints file/line/expression and
// enters dead loop.
//----------------------------------------------------------------------

VOID
DebugAssert (
  IN  CONST CHAR8  *FileName,
  IN  UINTN        LineNumber,
  IN  CONST CHAR8  *Description
  )
{
  DEBUG_PRINT_PROTOCOL *Protocol;

  Protocol = DebugPrintProtocolInit ();
  if (Protocol != NULL) {
    Protocol->Assert (FileName, LineNumber, Description);
  }
}

//----------------------------------------------------------------------
// BootServicesCleanup (formerly sub_1B90)
//
// ExitBootServices callback. Nullifies the BootServices_0 pointer
// to prevent use after boot services exit.
//----------------------------------------------------------------------

VOID
EFIAPI
BootServicesCleanup (
  VOID
  )
{
  gBootServicesCopy = NULL;
}

//----------------------------------------------------------------------
// RuntimeProtocolCleanup (formerly sub_1B9C)
//
// SetVirtualAddressMap callback. Converts the debug print protocol
// pointer for runtime virtual addressing.
//----------------------------------------------------------------------

EFI_STATUS
EFIAPI
RuntimeProtocolCleanup (
  VOID
  )
{
  if (gDebugPrintProtocol != NULL) {
    return EfiConvertPointer (0, &gDebugPrintProtocol);
  }
  return EFI_SUCCESS;
}

//----------------------------------------------------------------------
// HobGetGuidEntry (formerly sub_1BC4)
//
// Locates the GUIDed HOB entry in the system HOB list. Used to find
// the SMBIOS table or other platform configuration tables.
//----------------------------------------------------------------------

VOID *
HobGetGuidEntry (
  VOID
  )
{
  EFI_STATUS        Status;
  EFI_HOB_HANDOFF_INFO_TABLE *HobList;
  EFI_GUID          *Guid;
  UINTN             HobCount;

  if (gHobList != NULL) {
    return gHobList;
  }

  // Get HOB list from SystemTable
  HobList = (EFI_HOB_HANDOFF_INFO_TABLE *)gSystemTable->BootServices->GetHobList ();

  Status = EFI_NOT_FOUND;
  if (HobList->EfiMemoryTop != NULL) {
    for (HobCount = 0; HobCount < HobList->EfiFreeMemoryBottom; HobCount++) {
      Guid = (EFI_GUID *)((UINT8 *)HobList + HobCount * sizeof(EFI_HOB_GUID_TYPE));
      if (HobGuidCompare (Guid, &gSmbiosTableGuid)) {
        gHobList = *(VOID **)((UINT8 *)Guid + 16);
        break;
      }
    }
  }

  if (gHobList == NULL) {
    ASSSERT_EFI_ERROR (EFI_NOT_FOUND);
    ASSERT (gHobList != NULL);
  }

  return gHobList;
}

//----------------------------------------------------------------------
// nullsub_1 (formerly nullsub_1)
//
// No-op callback. Used as placeholder for events that don't require
// action but need a registered callback.
//----------------------------------------------------------------------

VOID
nullsub_1 (
  VOID
  )
{
  return;
}

//----------------------------------------------------------------------
// RuntimeProtocolNotifyCleanup (formerly sub_1CA0)
//
// SetVirtualAddressMap notification to convert the gRuntimeServices2
// pointer to virtual addressing.
//----------------------------------------------------------------------

VOID
EFIAPI
RuntimeProtocolNotifyCleanup (
  VOID
  )
{
  return EfiConvertPointer (0, &gRuntimeServices2);
}

//----------------------------------------------------------------------
// SmbiosProtocolInit (formerly sub_1CB4)
//
// Locates the SMBIOS protocol instance. Used for SMBIOS table access
// during SEL event logging.
//----------------------------------------------------------------------

VOID
SmbiosProtocolInit (
  VOID
  )
{
  EFI_STATUS  Status;

  if (gSmbiosProtocol != NULL || gSmbiosInitialized) {
    return;
  }

  if (gBootServices != NULL) {
    Status = gBootServices->LocateProtocol (
                              &gSmbiosProtocolGuid,
                              NULL,
                              &gSmbiosProtocol
                              );
    if (EFI_ERROR (Status)) {
      gSmbiosProtocol = NULL;
    }
  }
}

//----------------------------------------------------------------------
// SmbiosProtocolCleanup (formerly sub_1D14)
//
// SetVirtualAddressMap callback to convert the SMBIOS protocol
// pointer for runtime virtual addressing.
//----------------------------------------------------------------------

VOID
EFIAPI
SmbiosProtocolCleanup (
  VOID
  )
{
  if (gSmbiosProtocol != NULL) {
    return EfiConvertPointer (0, &gSmbiosProtocol);
  }
  return EFI_SUCCESS;
}

//----------------------------------------------------------------------
// ReportStatusCodeNotifyShutdown (formerly sub_1D3C)
//
// ExitBootServices notification. Initializes SMBIOS protocol for
// runtime and marks initialization complete.
//----------------------------------------------------------------------

VOID
EFIAPI
ReportStatusCodeNotifyShutdown (
  VOID
  )
{
  SmbiosProtocolInit ();
  gSmbiosInitialized = 1;
}

//----------------------------------------------------------------------
// SelAddEventRecord (formerly sub_1D54)
//
// Adds a SEL event record via the IPMI transport. Retries up to
// 512 times to handle the reserve-then-add race condition with
// the BMC.
//
// @param  Transport   IPMI transport protocol
// @param  RecordId    Record ID bytes
//
// @retval EFI_SUCCESS           Record added
// @retval EFI_INVALID_PARAMETER Invalid input
// @retval EFI_TIMEOUT           Max retries exceeded
//----------------------------------------------------------------------

EFI_STATUS
SelAddEventRecord (
  IN  VOID    *Transport,
  IN  UINT8   *RecordId,
  ...
  )
{
  UINTN       Retries;
  EFI_STATUS  Status;
  UINT8       ResponseCode;
  UINT8       RequestData[6];
  UINT8       ResponseData[2];
  UINT8       CompletionCode;

  if (RecordId == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  Retries = 512;
  do {
    // Send reserve SEL command
    RequestData[0] = RecordId[0];
    RequestData[1] = RecordId[1];
    RequestData[2] = 0x34;  // IPMI SEL reserve
    RequestData[3] = 0x52;  // IPMI SEL add
    RequestData[4] = 6;     // Data length
    RequestData[5] = 1;     // Set reserve flag

    Status = ((IPMI_TRANSPORT_PROTOCOL *)Transport)->SendCommand (
      Transport,
      IPMI_NETFN_STORAGE,
      0,
      IPMI_SEL_CMD_RESERVE,
      RequestData,
      sizeof(RequestData),
      &ResponseData,
      &ResponseCode
    );

    if (EFI_ERROR (Status)) {
      if (ResponseCode == 0xC5) {
        // Reservation lost/gained - need to retry
        Status = ((IPMI_TRANSPORT_PROTOCOL *)Transport)->SendCommand (
          Transport,
          IPMI_NETFN_STORAGE,
          0,
          IPMI_SEL_CMD_RESERVE,
          NULL,
          0,
          RecordId,
          &ResponseCode
        );
        if (EFI_ERROR (Status)) {
          return Status;
        }
      }
    } else {
      if ((ResponseData[0] & 0x0F) == 1) {
        return EFI_SUCCESS;
      }
    }

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

  return EFI_TIMEOUT;
}

//----------------------------------------------------------------------
// SelCheckRedundancyStatus (formerly sub_1E30)
//
// Checks the IPMI SEL status via the transport protocol to determine
// if the SEL is operational, has available space, and redundancy is
// active.
//
// @retval 1   SEL is operational with space and redundancy
// @retval 0   SEL not available or no redundancy
//----------------------------------------------------------------------

UINT8
SelCheckRedundancyStatus (
  VOID
  )
{
  EFI_STATUS  Status;
  UINT8       SelStatus[13];
  UINT8       DataSize;
  UINT64      CommandData;

  if (gIpmiTransport == NULL) {
    return 0;
  }

  CommandData = 14;  // NetFn 0x0A, Cmd 0x40 (Get SEL Info)
  DataSize = 0;

  Status = gIpmiTransport->Write (
                             gIpmiTransport,
                             0x0A,       // IPMI NetFn Storage
                             0,
                             0x40,       // Get SEL Info
                             0,
                             DataSize,
                             SelStatus,
                             &CommandData
                             );
  if (EFI_ERROR (Status)) {
    return 0;
  }

  // Byte 7: SEL State (bit 3 = Redundancy, bit 7 = Operational)
  gSelRedundancy = SelStatus[7] & 8;
  return SelStatus[7] >> 7;
}

//----------------------------------------------------------------------
// SelManageEventLog (formerly sub_1E98)
//
// Manages the IPMI SEL event log. Supports two operations:
//   Mode 1: Clear SEL entries matching the status code
//   Mode 2: Add SEL entries for status codes
//
// For mode 2, performs a reserve + add sequence using the IPMI
// transport protocol.
//----------------------------------------------------------------------

EFI_STATUS
SelManageEventLog (
  IN  UINT64  ServerSetupData,
  IN  UINT8   OperationMode
  )
{
  EFI_STATUS         Status;
  IPMI_TRANSPORT     *Transport;
  UINT8              RecordId[2];
  UINT8              ResponseData[4];
  UINT8              SelStatus[13];
  UINT8              DataSize;
  UINT8              ResponseSize;
  UINTN              EventType;

  Transport = gIpmiTransport;
  RecordId[0] = 0;
  RecordId[1] = 0;
  ResponseSize = 1;
  DataSize = 0;

  if (gIpmiTransport == NULL || OperationMode > 2) {
    return EFI_INVALID_PARAMETER;
  }

  // Get SEL info to check state
  Status = gIpmiTransport->Write (
                             gIpmiTransport,
                             0x0A,       // IPMI NetFn Storage
                             0,
                             0x40,       // Get SEL Info
                             0,
                             0,
                             SelStatus,
                             (UINT8 *)&DataSize
                             );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  if ((SelStatus[13] & 2) != 0) {
    // SEL has entries - clear or process
    if (OperationMode == 1) {
      // Clear: reserve + clear
      Status = gIpmiTransport->Write (
                                 gIpmiTransport,
                                 0x0A,
                                 0,
                                 0x44,   // Reserve SEL
                                 0,
                                 0,
                                 RecordId,
                                 &ResponseSize
                                 );
      if (EFI_ERROR (Status)) {
        return Status;
      }

      EventType = HIBYTE(RecordId[0]);
      // Clear SEL entries
      Status = gIpmiTransport->Write (
                                 gIpmiTransport,
                                 0x0A,
                                 0,
                                 0x47,   // Clear SEL
                                 &EventType,
                                 6,
                                 ResponseData,
                                 &ResponseSize
                                 );
      if (EFI_ERROR (Status)) {
        return Status;
      }

      if ((ResponseData[0] & 0x0F) == 1) {
        return EFI_SUCCESS;
      }

      return SelAddEventRecord (Transport, RecordId);
    } else if ((SelStatus[13] & 8) == 0) {
      // Deleting single record not supported
      DebugPrintWithLevel (EFI_D_ERROR, "Deleting Single record is not supported. Status: EFI_UNSUPPORTED\n");
      return EFI_UNSUPPORTED;
    }

    // Add entry
    ResponseSize = 4;
    Status = gIpmiTransport->Write (
                               gIpmiTransport,
                               0x0A,
                               0,
                               0x44,   // Reserve + Add
                               ResponseData,
                               4,
                               StatusData,
                               &ResponseSize
                               );
    if (EFI_ERROR (Status)) {
      return Status;
    }

    // Check response byte 8 for status
    if (Transport->CompletionCode == 0x80) {
      return EFI_UNSUPPORTED;
    }
    if (Transport->CompletionCode == 0x81) {
      return EFI_UNSUPPORTED;
    }
  }

  return Status;
}

//----------------------------------------------------------------------
// HobGuidCompare (formerly sub_2048)
//
// Compares two GUIDs by reading their QWORD pairs via unaligned
// access. Compares GUID at unk_4060 (SMBIOS_TABLE_GUID).
//----------------------------------------------------------------------

BOOLEAN
HobGuidCompare (
  IN  CONST EFI_GUID  *Guid1,
  IN  CONST EFI_GUID  *Guid2
  )
{
  EFI_GUID  TargetGuid;

  CopyMem (&TargetGuid, &gSmbiosTableGuid, sizeof(EFI_GUID));
  return (ReadUnaligned64 (&TargetGuid) == ReadUnaligned64 (Guid1) &&
          ReadUnaligned64 ((UINT8 *)&TargetGuid + 8) == ReadUnaligned64 (((UINT8 *)Guid2 + 8)));
}

//----------------------------------------------------------------------
// ReadUnaligned64 (formerly sub_20B8)
//
// Reads a 64-bit value from potentially unaligned memory.
//
// @param  Buffer   Pointer to data (may be unaligned)
//
// @return The 64-bit value read.
//----------------------------------------------------------------------

UINT64
EFIAPI
ReadUnaligned64 (
  IN  CONST VOID  *Buffer
  )
{
  ASSERT (Buffer != NULL);
  return *(UINT64 *)Buffer;
}