Newer
Older
AMI-Aptio-BIOS-Reversed / SmbiosRpTable / SmbiosRpTable.c
@Ajax Dong Ajax Dong 2 days ago 14 KB Init
/** @file
  SmbiosRpTable - SMBIOS Type 133 Record Installation Driver

  This UEFI DXE driver allocates an ACPI NVS communication buffer and installs
  a SMBIOS Type 133 record pointing to that buffer for BIOS-to-OS communication.

  Copyright (c) 2025, Insyde Software Corp. All rights reserved.

  Module: SmbiosRpTable.efi
  MD5:    5e6058cf09eb35a44a33a7c77bcb10b9
  SHA256: ec570f2fde3423e2900bfd8049ec63748136489363612e44afa49364fcb6b8f6
**/

#include "SmbiosRpTable.h"

//
// Global variables
//
EFI_HANDLE              ImageHandle    = NULL;
EFI_SYSTEM_TABLE        *SystemTable   = NULL;
EFI_BOOT_SERVICES       *BootServices  = NULL;
EFI_RUNTIME_SERVICES    *RuntimeServices = NULL;
VOID                    *mHobList      = NULL;
EFI_DEBUG_PRINT_PROTOCOL *mDebugPrintProtocol = NULL;

/**
  Zero fill a buffer by processing 8-byte aligned chunks.

  @param[in] Buffer  Pointer to buffer to zero fill.
  @param[in] Length  Length of buffer in bytes.

  @return Pointer to the buffer.
**/
VOID *
EFIAPI
ZeroMem (
  VOID   *Buffer,
  UINTN  Length
  )
{
  ZeroMemAligned64 (Buffer, Length);

  return Buffer;
}

/**
  Internal helper to zero memory using 8-byte aligned writes.

  @param[in] Buffer  Pointer to the buffer.
  @param[in] Length  Length of the buffer in bytes.
**/
STATIC
VOID
ZeroMemAligned64 (
  VOID   *Buffer,
  UINTN  Length
  )
{
  //
  // Zero 8-byte aligned chunks first.
  //
  SetMem32 ((UINT32 *)Buffer, 0, (UINT32)(Length >> 3));

  //
  // Zero the remaining bytes (0-7).
  //
  SetMem8 (
    (UINT8 *)Buffer + (Length & ~7),
    (UINT8)0,
    (UINT8)(Length & 7)
    );
}

/**
  The module entry point.

  Initializes UEFI boot/runtime services globals, locates HII and other
  required protocols, then locates the SMBIOS protocol and installs a
  Type 133 SMBIOS record pointing to an ACPI NVS communication buffer.

  @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 SMBIOS Type 133 record was installed.
  @retval EFI_NOT_FOUND         The SMBIOS protocol was not found.
  @retval EFI_OUT_OF_RESOURCES  Memory allocation failed.
  @return Status codes from gBS->LocateProtocol() or SMBIOS Add().
**/
EFI_STATUS
EFIAPI
ModuleEntryPoint (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS                    Status;
  EFI_SMBIOS_PROTOCOL           *SmbiosProtocol;

  //
  // Initialize the DXE global variables (ImageHandle, SystemTable,
  // BootServices, RuntimeServices) and locate HII/HOB protocols.
  //
  UefiMainEntry (ImageHandle, SystemTable);

  //
  // Locate the SMBIOS protocol.
  //
  Status = gBS->LocateProtocol (
                  &gEfiSmbiosProtocolGuid,
                  NULL,
                  (VOID **)&SmbiosProtocol
                  );
  if (EFI_ERROR (Status)) {
    DEBUG ((EFI_D_ERROR, "SmbiosRpTable Could not locate SMBIOS protocol.%r\n", Status));
    return Status;
  }

  //
  // Install a Type 133 SMBIOS record with the communication buffer address.
  //
  return SmbiosType133RecordInstall (SmbiosProtocol);
}

/**
  UEFI main initialization routine.

  Initializes global ImageHandle, SystemTable, BootServices, RuntimeServices,
  locates HOB list, and resolves HII protocol GUIDs.

  @param[in] ImageHandle  The image handle.
  @param[in] SystemTable  The EFI system table.
**/
VOID
UefiMainEntry (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS  Status;

  gImageHandle = ImageHandle;
  ASSERT (gImageHandle != NULL);

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

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

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

  //
  // Get the HOB list from the system configuration table.
  //
  GetHobList ();

  //
  // Resolve HII protocol GUIDs.
  //
  Status = gBS->LocateProtocol (
                  &gEfiHiiDatabaseProtocolGuid,
                  NULL,
                  (VOID **)&gHiiDatabase
                  );
  ASSERT_EFI_ERROR (Status);

  Status = gBS->LocateProtocol (
                  &gEfiHiiStringProtocolGuid,
                  NULL,
                  (VOID **)&gHiiString
                  );
  ASSERT_EFI_ERROR (Status);

  Status = gBS->LocateProtocol (
                  &gEfiHiiConfigRoutingProtocolGuid,
                  NULL,
                  (VOID **)&gHiiConfigRouting
                  );
  ASSERT_EFI_ERROR (Status);

  Status = gBS->LocateProtocol (
                  &gEfiHiiFontProtocolGuid,
                  NULL,
                  (VOID **)&gHiiFont
                  );
  ASSERT_EFI_ERROR (Status);

  Status = gBS->LocateProtocol (
                  &gEfiHiiImageProtocolGuid,
                  NULL,
                  (VOID **)&gHiiImage
                  );
  ASSERT_EFI_ERROR (Status);
}

/**
  Installs a SMBIOS Type 133 record.

  Allocates ACPI NVS memory as a BIOS utility communication buffer of
  0x4000 bytes, then creates a Type 133 SMBIOS record pointing to that
  buffer.

  @param[in] SmbiosProtocol  A pointer to the SMBIOS protocol.

  @retval EFI_SUCCESS           The Type 133 record was added.
  @retval EFI_OUT_OF_RESOURCES  Memory allocation failed.
  @return Status from SmbiosProtocol->Add().
**/
EFI_STATUS
SmbiosType133RecordInstall (
  IN EFI_SMBIOS_PROTOCOL  *SmbiosProtocol
  )
{
  EFI_STATUS            Status;
  UINTN                 BufferAddr;
  SMBIOS_STRUCTURE      *SmbiosRecord;
  EFI_SMBIOS_TABLE_HEADER *RecordHeader;
  UINT8                 *StringPtr;

  //
  // Allocate ACPI NVS memory for BIOS utility communication buffer (16KB).
  //
  BufferAddr = 0xFFFFFFFFULL;
  Status = gBS->AllocatePages (
                  AllocateAnyPages,
                  EfiACPIMemoryNVS,
                  EFI_SIZE_TO_PAGES (0x4000),
                  &BufferAddr
                  );
  if (EFI_ERROR (Status)) {
    DEBUG ((EFI_D_ERROR, "Failed to allocate ACPI NVS memory as BIOS utility communication buffer, Status %r\n", Status));
    return Status;
  }

  DEBUG ((EFI_D_INFO, "Allocated Communication Buffer address = %x\n", BufferAddr));

  //
  // Allocate the SMBIOS record structure.
  //
  SmbiosRecord = (SMBIOS_STRUCTURE *)AllocateACPINvsBuffer ();
  if (SmbiosRecord == NULL) {
    DEBUG ((EFI_D_ERROR, "Failed to allocate smbios record\n"));
    return EFI_OUT_OF_RESOURCES;
  }

  //
  // Zero the record buffer (14 bytes for type 133 header + 2 handle).
  //
  ZeroMem14 ((UINTN)SmbiosRecord);
  if (SmbiosRecord == NULL) {
    DEBUG ((EFI_D_ERROR, "Failed to allocate smbios record\n"));
    return EFI_OUT_OF_RESOURCES;
  }

  //
  // Fill in Type 133 SMBIOS record:
  //   Type    = SMBIOS_TYPE_BIOS_UTILITY_COMMUNICATION (133)
  //   Handle  = 0x0C85 (3205)
  //   Buffer  = ACPI NVS physical address
  //   Size    = 0x4000 (16KB)
  //
  RecordHeader = (EFI_SMBIOS_TABLE_HEADER *)SmbiosRecord;
  RecordHeader->Type    = SMBIOS_TYPE_BIOS_UTILITY_COMMUNICATION;
  RecordHeader->Length  = SMBIOS_TYPE_133_RECORD_LENGTH;
  RecordHeader->Handle  = SMBIOS_HANDLE_BIOS_UTILITY_COMM;
  //
  // The record payload contains:
  //   DWORD: Buffer physical address (low 32 bits)
  //   DWORD: Buffer size in bytes    (0x4000)
  //
  *((UINT32 *)&SmbiosRecord->BufferAddress) = (UINT32)BufferAddr;
  *((UINT32 *)&SmbiosRecord->BufferSize)    = 0x4000;

  //
  // Add the SMBIOS record.
  //
  Status = SmbiosProtocol->Add (
                             SmbiosProtocol,
                             0,
                             (EFI_SMBIOS_TABLE_HEADER *)SmbiosRecord,
                             (UINT8 *)StringPtr
                             );
  DEBUG ((EFI_D_INFO, "Smbios protocol addition (Type 133) returns %r\n", Status));

  return Status;
}

/**
  Locates and caches the EFI_DEBUG_PRINT_PROTOCOL.

  @return Pointer to EFI_DEBUG_PRINT_PROTOCOL, or NULL if not found.
**/
EFI_DEBUG_PRINT_PROTOCOL *
GetDebugPrintProtocol (
  VOID
  )
{
  EFI_STATUS  Status;
  UINTN       CpuCount;

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

  CpuCount = gBS->GetNumberOfCpuCores ();
  if (CpuCount == 0) {
    //
    // Only attempt to locate protocol if we have a sane number of CPUs.
    //
    return NULL;
  }

  gBS->CheckCpuCores (CpuCount);

  if (CpuCount > 16) {
    return NULL;
  }

  Status = gBS->LocateProtocol (
                  &gEfiDebugPrintProtocolGuid,
                  NULL,
                  (VOID **)&mDebugPrintProtocol
                  );
  if (EFI_ERROR (Status)) {
    mDebugPrintProtocol = NULL;
  }

  return mDebugPrintProtocol;
}

/**
  Prints a debug message to the debug output device.

  Only prints if the DebugPrint protocol is available and the error level
  matches the current platform debug level.

  @param[in] ErrorLevel  The error level of the debug message.
  @param[in] Format      Format string for the debug message.
  @param[in] ...         Variable argument list for the format.
**/
VOID
EFIAPI
DebugPrint (
  IN UINTN        ErrorLevel,
  IN CONST CHAR8  *Format,
  ...
  )
{
  EFI_DEBUG_PRINT_PROTOCOL  *DebugPrintProtocol;
  UINTN                     CurrentDebugLevel;
  UINTN                     AllowedMask;
  VA_LIST                   Marker;

  VA_START (Marker, Format);

  DebugPrintProtocol = GetDebugPrintProtocol ();
  if (DebugPrintProtocol == NULL) {
    return;
  }

  //
  // Check the platform debug level via CMOS index 0x4B.
  //
  CurrentDebugLevel = GetPlatformDebugLevel ();

  AllowedMask = 0;
  if (CurrentDebugLevel == 1) {
    //
    // Error level only: 0x80000004 (EFI_D_ERROR)
    //
    AllowedMask = DEBUG_ERROR;
  } else if (CurrentDebugLevel > 0 && CurrentDebugLevel <= 0xFE) {
    //
    // Allow all except DEBUG_ERROR when in verbose mode.
    //
    AllowedMask = DEBUG_VERBOSE;
  }

  if ((AllowedMask & ErrorLevel) != 0) {
    DebugPrintProtocol->DebugPrint (
                          DebugPrintProtocol,
                          ErrorLevel,
                          Format,
                          Marker
                          );
  }

  VA_END (Marker);
}

/**
  Reads the platform debug level from CMOS.
  Handles platform-specific debug level registers.

  @return The current debug level index.
**/
UINTN
GetPlatformDebugLevel (
  VOID
  )
{
  UINT8   DebugRegister;
  UINT8   DebugLevel;

  //
  // Read CMOS index 0x4B (debug level register).
  //
  DebugRegister = IoRead8 (CMOS_INDEX_PORT);
  IoWrite8 (CMOS_INDEX_PORT, DebugRegister & 0x80 | 0x4B);

  DebugLevel = IoRead8 (CMOS_DATA_PORT);
  if (DebugLevel > 3) {
    //
    // Check extended memory for debug settings on certain platforms.
    //
    if (DebugLevel == 0) {
      DebugLevel = *(volatile UINT8 *)(UINTN)0xFDAF0490 & 2 | 1;
    }
  }

  return DebugLevel;
}

/**
  ASSERT macro support.

  Prints the assertion failure information using the DebugAssert protocol.

  @param[in] FileName      The file name where the assertion occurred.
  @param[in] LineNumber    The line number where the assertion occurred.
  @param[in] Description   The description of the assertion failure.
**/
VOID
EFIAPI
DebugAssert (
  IN CONST CHAR8  *FileName,
  IN UINTN        LineNumber,
  IN CONST CHAR8  *Description
  )
{
  EFI_DEBUG_PRINT_PROTOCOL  *DebugPrintProtocol;

  DebugPrintProtocol = GetDebugPrintProtocol ();
  if (DebugPrintProtocol != NULL) {
    DebugPrintProtocol->DebugAssert (
                          DebugPrintProtocol,
                          FileName,
                          LineNumber,
                          Description
                          );
  }
}

/**
  Allocates ACPI NVS memory for the SMBIOS record buffer.

  Calls gBS->AllocatePool() with EfiACPIMemoryNVS memory type.

  @param[in] a1  Unused parameter (provided for compatibility).
  @param[in] a2  Unused parameter (provided for compatibility).

  @return Pointer to allocated ACPI NVS buffer, or NULL if allocation failed.
**/
VOID *
AllocateACPINvsBuffer (
  VOID
  )
{
  EFI_STATUS  Status;
  VOID        *Buffer;

  Buffer = NULL;
  Status = gBS->AllocatePool (
                  EfiACPIMemoryNVS,
                  sizeof (SMBIOS_STRUCTURE),
                  &Buffer
                  );
  if (EFI_ERROR (Status)) {
    return NULL;
  }

  return Buffer;
}

/**
  Returns the HOB list pointer from the system configuration table.

  Searches the configuration table for the HOB list GUID and stores the
  pointer in the mHobList global.

  @return Pointer to the HOB list, or NULL if not found.
**/
VOID *
EFIAPI
GetHobList (
  VOID
  )
{
  UINTN   Index;
  UINTN   TableEntry;
  GUID    *Guid1;

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

  mHobList = NULL;

  if (gST->NumberOfTableEntries > 0) {
    TableEntry = (UINTN)gST->ConfigurationTable;

    for (Index = 0; Index < gST->NumberOfTableEntries; Index++) {
      Guid1 = (GUID *)(TableEntry + (Index * sizeof (EFI_CONFIGURATION_TABLE)));

      //
      // Check for gEfiHobListGuid match.
      //
      if (CompareGuid (&gEfiHobListGuid, Guid1)) {
        mHobList = *(VOID **)(TableEntry + (Index * sizeof (EFI_CONFIGURATION_TABLE)) + OFFSET_OF (EFI_CONFIGURATION_TABLE, VendorTable));
        return mHobList;
      }
    }
  }

  //
  // If we get here, the HOB list was not found.
  //
  DEBUG ((EFI_D_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", EFI_NOT_FOUND));
  ASSERT (EFI_ERROR (EFI_NOT_FOUND));

  return mHobList;
}

/**
  Reads a 64-bit unaligned value.

  @param[in] Buffer  Pointer to the unaligned buffer.

  @return The 64-bit value read from the buffer.
**/
UINT64
EFIAPI
ReadUnaligned64 (
  IN CONST UINT64  *Buffer
  )
{
  ASSERT (Buffer != NULL);
  return *Buffer;
}

/**
  Zeros 14 bytes of memory at the given address.

  @param[in] Buffer  Address of the buffer to zero.

  @return Pointer to the zeroed buffer.
**/
VOID *
EFIAPI
ZeroMem14 (
  IN UINTN  Buffer
  )
{
  ASSERT (Buffer != 0);
  ASSERT ((UINTN)(-(INTN)Buffer) >= 14);

  return ZeroMem ((VOID *)Buffer, 14);
}

/**
  Compares two GUIDs.

  Uses unaligned 64-bit reads for efficiency.

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

  @retval TRUE    Guid1 equals Guid2.
  @retval FALSE   Guid1 does not equal Guid2.
**/
BOOLEAN
EFIAPI
CompareGuid (
  IN CONST GUID  *Guid1,
  IN CONST GUID  *Guid2
  )
{
  //
  // Compare the first 8 bytes (Data1 + Data2 + Data3).
  //
  if (ReadUnaligned64 ((UINT64 *)Guid1) != ReadUnaligned64 ((UINT64 *)Guid2)) {
    return FALSE;
  }

  //
  // Compare last 8 bytes (Data4).
  //
  return ReadUnaligned64 ((UINT64 *)Guid1->Data4) == ReadUnaligned64 ((UINT64 *)Guid2->Data4);
}