/** @file
  SMBIOS Data Update DXE driver for LightningRidge EX EC B3.

  This UEFI driver is part of the UBA (Unified Board Architecture) framework
  on Purley platforms. It registers a board-specific callback that updates
  SMBIOS data tables for the LightningRidge EX EC B3 platform, including:
  - SMBIOS Type 41: Onboard Devices Extended Information (30 entries)
  - SMBIOS Type 9:  System Slots (8 entries)
  - SMBIOS Type 43: TPM Device (4 entries)

  The driver obtains HII string packages and SMBIOS configuration data
  through the UBA configuration protocol, then installs or updates SMBIOS
  records using the SMBIOS protocol.

  Copyright (c) 2020, Intel Corporation. All rights reserved.
  SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#include "SmbiosDataUpdateDxeLightningRidgeEXECB3.h"

//
// Global protocol pointers obtained during initialization
//
EFI_HANDLE                     gImageHandle             = NULL;
EFI_SYSTEM_TABLE               *gSystemTable             = NULL;
EFI_BOOT_SERVICES              *gBootServices            = NULL;
EFI_RUNTIME_SERVICES           *gRuntimeServices         = NULL;
EFI_HII_HANDLE                 gSmbiosStringPackHandle   = NULL;
EFI_HII_DATABASE_PROTOCOL      *gHiiDatabase             = NULL;
EFI_HII_STRING_PROTOCOL        *gHiiString               = NULL;
EFI_HII_CONFIG_ROUTING_PROTOCOL *gHiiConfigRouting       = NULL;
EFI_HII_FONT_PROTOCOL          *gHiiFont                 = NULL;
EFI_HII_RUNTIME_PROTOCOL       *gHiiRuntime              = NULL;
EFI_SMBIOS_PROTOCOL            *gSmbiosProtocol          = NULL;
EFI_SMBIOS_UPDATE_PROTOCOL     *gSmbiosUpdateProtocol    = NULL;
VOID                           *gDs                      = NULL;
EFI_MM_PCI_BASE_PROTOCOL       *mPciUsra                 = NULL;

//
// HOB List pointer (obtained from DxeHobLib)
//
VOID                           *mHobList                 = NULL;

/**
  Internal: Zero memory using 8-byte aligned store.
  Wrapper around the memset intrinsic for zeroing buffers.

  @param[out] Buffer  Pointer to the buffer to zero.
  @param[in]  Length  Number of bytes to zero.

  @return Pointer to the buffer.
**/
STATIC
VOID *
InternalZeroMem (
  OUT VOID   *Buffer,
  IN  UINTN  Length
  )
{
  return memset (Buffer, 0, Length);
}

/**
  Internal: Copy memory using 8-byte aligned load/store.
  Supports overlapping regions (backward copy if needed).

  @param[out] Destination  Pointer to destination buffer.
  @param[in]  Source       Pointer to source buffer.
  @param[in]  Length       Number of bytes to copy.

  @return Pointer to destination buffer.
**/
STATIC
VOID *
InternalCopyMem (
  OUT VOID       *Destination,
  IN  CONST VOID *Source,
  IN  UINTN      Length
  )
{
  return CopyMem (Destination, Source, Length);
}

/**
  Read an unaligned UINT32.

  @param[in] Buffer  Pointer to the buffer.

  @return The UINT32 value at the buffer.
**/
STATIC
UINT32
InternalReadUint32 (
  IN CONST VOID *Buffer
  )
{
  return *(UINT32 *)Buffer;
}

/**
  Read an unaligned UINT64.

  @param[in] Buffer  Pointer to the buffer.

  @return The UINT64 value at the buffer.
**/
STATIC
UINT64
InternalReadUint64 (
  IN CONST VOID *Buffer
  )
{
  return *(UINT64 *)Buffer;
}

/**
  Write a UINT64 to an unaligned address.

  @param[out] Address  Pointer to the destination.
  @param[in]  Value    The UINT64 value to write.

  @return The Value parameter.
**/
STATIC
UINT64
InternalWriteUint64 (
  OUT VOID   *Address,
  IN  UINT64 Value
  )
{
  *(UINT64 *)Address = Value;
  return Value;
}

/**
  Compare two GUIDs by value.

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

  @return TRUE if GUIDs are equal, FALSE otherwise.
**/
STATIC
BOOLEAN
InternalCompareGuid (
  IN CONST GUID *Guid1,
  IN CONST GUID *Guid2
  )
{
  return CompareGuid (Guid1, Guid2);
}

/**
  Internal assertion handler.
  Prints the assertion message and hangs if the condition is false.

  @param[in] FileName     Source file name where the assertion occurred.
  @param[in] LineNumber   Line number of the assertion.
  @param[in] Description  Description of the assertion.
**/
STATIC
VOID
InternalAssert (
  IN CONST CHAR8  *FileName,
  IN UINTN        LineNumber,
  IN CONST CHAR8  *Description
  )
{
  DebugAssert (FileName, LineNumber, Description);
}

/**
  Platform-specific debug print with platform type check.
  Only prints if the debug level matches the current platform type.

  @param[in] ErrorLevel  Debug error level.
  @param[in] Format      Format string.
  @param[in] ...         Variable arguments.

  @return The return value from the debug print protocol.
**/
STATIC
UINTN
InternalDebugPrint (
  IN UINTN        ErrorLevel,
  IN CONST CHAR8  *Format,
  ...
  )
{
  UINTN   ReturnValue;
  VA_LIST Marker;

  VA_START (Marker, Format);
  ReturnValue = DebugPrint (ErrorLevel, Format, Marker);
  VA_END (Marker);
  return ReturnValue;
}

/**
  Allocate and zero-initialize a buffer.

  @param[in] AllocationSize  Size of buffer to allocate.

  @return Pointer to allocated and zeroed buffer, or NULL if allocation fails.
**/
STATIC
VOID *
InternalAllocateZeroPool (
  IN UINTN  AllocationSize
  )
{
  VOID *Buffer;

  Buffer = AllocateZeroPool (AllocationSize);
  return Buffer;
}

/**
  Free a buffer allocated by InternalAllocateZeroPool.

  @param[in] Buffer  Pointer to buffer to free.
**/
STATIC
VOID
InternalFreePool (
  IN VOID  *Buffer
  )
{
  FreePool (Buffer);
}

/**
  Locate a configuration table in the EFI System Table by GUID.

  @param[in]  TableGuid  Pointer to the GUID of the table to find.
  @param[out] Table      Pointer to receive the table pointer.

  @retval EFI_SUCCESS           The table was found.
  @retval EFI_NOT_FOUND         The table was not found.
  @retval EFI_INVALID_PARAMETER A parameter was NULL.
**/
STATIC
EFI_STATUS
InternalLocateConfigTable (
  IN  CONST EFI_GUID  *TableGuid,
  OUT VOID            **Table
  )
{
  UINTN Index;
  UINTN NumberOfTableEntries;

  if ((TableGuid == NULL) || (Table == NULL)) {
    return EFI_INVALID_PARAMETER;
  }

  *Table = NULL;
  if (gSystemTable->NumberOfTableEntries == 0) {
    return EFI_NOT_FOUND;
  }

  NumberOfTableEntries = gSystemTable->NumberOfTableEntries;
  for (Index = 0; Index < NumberOfTableEntries; Index++) {
    if (InternalCompareGuid (
          TableGuid,
          &gSystemTable->ConfigurationTable[Index].VendorGuid
          )) {
      *Table = gSystemTable->ConfigurationTable[Index].VendorTable;
      return EFI_SUCCESS;
    }
  }

  return EFI_NOT_FOUND;
}

/**
  Initialize UEFI runtime services - locate the HOB list pointer.

  Retrieves the HOB list from the EFI configuration table.

  @retval VOID pointer to HOB list if successful, otherwise NULL.
**/
STATIC
VOID *
InternalGetHobList (
  VOID
  )
{
  EFI_STATUS Status;
  VOID       *HobList;
  EFI_GUID   gHobListGuid = { 0x7739f24c, 0x93d7, 0x11d4,
                              { 0x9a, 0x3a, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d } };

  Status = InternalLocateConfigTable (&gHobListGuid, &HobList);
  if (EFI_ERROR (Status)) {
    DEBUG ((EFI_D_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", Status));
    ASSERT (FALSE);
    HobList = NULL;
  }

  if (HobList == NULL) {
    //
    // If HOB list is not available, this is a fatal error
    //
    ASSERT (FALSE);
  }

  return HobList;
}

/**
  Get the board-specific HII string configuration from UBA.

  Retrieves the platform-specific string data (such as enclosure/device
  descriptions) from the UBA configuration block and installs it into
  the HII database for SMBIOS string references.

  @param[out] ImageHandle       Pointer to receive the driver image handle.
  @param[in]  UbaConfigData     Pointer to the UBA configuration block data.
  @param[in]  UbaConfigSize     Size of the UBA configuration block data.

  @return HII handle for the SMBIOS string package, or NULL on failure.
**/
STATIC
EFI_HII_HANDLE
InternalInstallSmbiosStringPack (
  OUT EFI_HANDLE      *DriverImageHandle,
  IN  VOID            *UbaConfigData,
  IN  UINTN           UbaConfigSize
  )
{
  EFI_HII_HANDLE  HiiHandle;
  UINTN           TotalStringSize;
  UINTN           StringIndex;
  VOID            *StringPackage;
  UINTN           PackageSize;
  EFI_STATUS      Status;
  UINT32          *StringData;
  UINTN           StringDataSize;

  if (UbaConfigData == NULL) {
    ASSERT (FALSE);
    return NULL;
  }

  //
  // Calculate total string data size by iterating through the UBA config
  // data pointers (NULL-terminated array of pointers).
  //
  StringData     = (UINT32 *)UbaConfigData;
  TotalStringSize = 0;
  if (*StringData != 0 && StringData != NULL) {
    do {
      TotalStringSize += (InternalReadUint32 (StringData) - 4);
      StringData++;
    } while (*StringData != 0);
  }

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

  //
  // Build the HII string package:
  //   Header (24 bytes) + string data + null terminator (4 bytes)
  //
  PackageSize = TotalStringSize + 24 + 4;
  StringPackage = InternalAllocateZeroPool (PackageSize);
  if (StringPackage == NULL) {
    return NULL;
  }

  //
  // Initialize the HII package header with the GUID from UBA config
  //
  InternalCopyMem (StringPackage, UbaConfigData, sizeof (EFI_GUID));
  InternalWriteUint64 (StringPackage + 8, InternalReadUint64 (UbaConfigData + 8));

  *(UINT32 *)((UINT8 *)StringPackage + 16) = (UINT32)PackageSize;

  //
  // Copy string data from the UBA config block
  //
  StringDataSize = TotalStringSize;
  StringData     = (UINT32 *)UbaConfigData;

  do {
    UINT32 CurrentStringSize;
    CurrentStringSize = InternalReadUint32 (StringData) - 4;
    //
    // Copy string content (skip the 4-byte size prefix)
    //
    InternalCopyMem (
      (UINT8 *)StringPackage + 20 + (TotalStringSize - StringDataSize),
      (UINT8 *)StringData + 4,
      CurrentStringSize
      );
    StringDataSize -= CurrentStringSize;
    StringData++;
  } while (*StringData != 0);

  //
  // Append the double-null terminator for the string package
  //
  InternalCopyMem (
    (UINT8 *)StringPackage + 20 + TotalStringSize,
    "\0\0\0\0",
    4
    );

  //
  // Install the HII string package
  // This updates the HII database with our board-specific strings.
  //
  Status = gHiiDatabase->NewPackageList (
                           gHiiDatabase,
                           StringPackage,
                           DriverImageHandle,
                           &HiiHandle
                           );
  if (EFI_ERROR (Status)) {
    HiiHandle = NULL;
  }

  InternalFreePool (StringPackage);
  return HiiHandle;
}

/**
  Get the platform language string from UEFI variable.

  Reads the PlatformLang variable to determine the active language
  for HII string lookups.

  @param[out] Value  Pointer to receive the allocated language string.

  @retval EFI_SUCCESS           The language string was retrieved.
  @retval EFI_OUT_OF_RESOURCES  Memory allocation failed.
  @retval EFI_NOT_FOUND         The PlatformLang variable does not exist.
**/
STATIC
EFI_STATUS
InternalGetPlatformLanguage (
  OUT CHAR8  **Value
  )
{
  EFI_STATUS Status;
  UINTN      BufferSize;
  VOID       *Buffer;
  CHAR16     *PlatformLangVar;
  UINTN      VarSize;
  EFI_GUID   gGlobalVariableGuid = { 0x8be4df61, 0x93ca, 0x11d2,
                                     { 0xaa, 0x0d, 0x00, 0xe0, 0x98, 0x03, 0x2b, 0x8c } };

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

  *Value   = NULL;
  Buffer   = NULL;
  VarSize  = 0;

  //
  // First call to get the required buffer size
  //
  BufferSize = 0;
  Status = gRuntimeServices->GetVariable (
                               L"PlatformLang",
                               &gGlobalVariableGuid,
                               NULL,
                               &BufferSize,
                               NULL
                               );
  if (Status == EFI_BUFFER_TOO_SMALL) {
    //
    // Allocate the buffer for the language string
    //
    Buffer = InternalAllocateZeroPool (BufferSize);
    if (Buffer == NULL) {
      return EFI_OUT_OF_RESOURCES;
    }

    //
    // Read the variable
    //
    Status = gRuntimeServices->GetVariable (
                                 L"PlatformLang",
                                 &gGlobalVariableGuid,
                                 NULL,
                                 &BufferSize,
                                 Buffer
                                 );
    if (EFI_ERROR (Status)) {
      InternalFreePool (Buffer);
      return Status;
    }

    //
    // Convert from Unicode to ASCII
    //
    *Value = (CHAR8 *)Buffer;

  } else if (Status == EFI_NOT_FOUND) {
    //
    // PlatformLang variable not found - not fatal, return EFI_NOT_FOUND
    //
  }

  return Status;
}

/**
  Find a matching language in the supported languages string.

  Searches the supported language list for a match with the requested
  language string. Supports partial matching (3-letter prefix).

  @param[in]  SupportedLanguages  Supported languages list (semicolon-separated).
  @param[in]  Language            Requested language string.
  @param[in]  VaList              Additional language fallback strings.

  @return A pointer to the matched language string (allocated), or NULL if no match.
**/
STATIC
CHAR8 *
InternalGetBestLanguage (
  IN CONST CHAR8  *SupportedLanguages,
  IN CONST CHAR8  *Language,
  ...
  )
{
  VA_LIST     Args;
  CONST CHAR8 *Lang;
  CONST CHAR8 *Match;
  UINTN       LangLen;
  UINTN       Index;

  if (SupportedLanguages == NULL) {
    ASSERT (FALSE);
    return NULL;
  }

  VA_START (Args, Language);

  for (Lang = Language; Lang != NULL; Lang = VA_ARG (Args, CONST CHAR8 *)) {
    //
    // Walk the supported languages list
    //
    for (Match = SupportedLanguages; *Match != '\0'; ) {
      //
      // Skip delimiters (semicolons)
      //
      while (*Match == ';') {
        Match++;
      }

      //
      // Determine the length of this language tag
      //
      LangLen = 0;
      while (Match[LangLen] != '\0' && Match[LangLen] != ';') {
        LangLen++;
      }

      //
      // Check if the language starts with a 3-letter prefix match
      // or matches the full RFC 4646 language code
      //
      if ((LangLen >= 3) && (AsciiStrnCmp (Match, Lang, 3) == 0)) {
        //
        // Found a match - return a copy
        //
        CHAR8 *LanguageString;
        LanguageString = InternalAllocateZeroPool (LangLen + 1);
        if (LanguageString != NULL) {
          InternalCopyMem (LanguageString, Match, LangLen);
        }
        VA_END (Args);
        return LanguageString;
      }

      Match += LangLen;
    }
  }

  VA_END (Args);
  return NULL;
}

//
// SMBIOS Type 41 (Onboard Devices Extended Information) string table
// Each entry is a 10-byte record containing:
//   [2 bytes] string_id/device_designation
//   [2 bytes] device_type_instance
//   [1 byte]  device_type (enum)
//   [1 byte]  segment_group_hi
//   [2 bytes] segment_group_lo + bus
//   [1 byte]  dev_func
//   [1 byte]  data_size / flags
//
// The table encodes the mapping between HII string IDs and
// the corresponding SMBIOS Type 41 device data for this board.
//
// Index 0 entry reference structure (10 bytes):
//   +0: UINT16  StringId    (HII string index, 0 = none)
//   +2: UINT8   DeviceType  (SMBIOS device type code)
//   +4: UINT8   Instance    (device type instance number)
//   +6: UINT16  SegmentBus  (PCI segment:bus)
//   +8: UINT8   DevFunc     (PCI device:function)
//   +9: UINT8   DataSize    (optional data area size)
//

/**
  Build and add SMBIOS Type 41 table entries from the UBA string
  configuration data.

  Processes the board-specific device configuration and creates
  SMBIOS Type 41 structures for each onboard device as defined by
  the string table at the given index.

  @param[out] SmbiosRecord    Pointer to the SMBIOS record buffer.
  @param[in]  TableIndex      Index into the device configuration table
                              (0..0x1D for up to 30 entries).

  @retval EFI_SUCCESS             The SMBIOS record was built and added.
  @retval EFI_INVALID_PARAMETER   Invalid table index.
  @retval EFI_OUT_OF_RESOURCES    Memory allocation failed.
  @retval EFI_UNSUPPORTED         Unsupported table format.
**/
EFI_STATUS
BuildSmbiosType41Record (
  OUT SMBIOS_STRUCT_TABLE_HEADER  *SmbiosRecord,
  IN  UINTN                       TableIndex
  )
{
  EFI_STATUS  Status;
  UINTN       StringIndex;
  UINT16      StringId;
  UINT8       DeviceType;
  UINT8       DeviceInstance;
  UINT8       BusNum;
  UINT8       DevFuncNum;
  UINT8       SegmentGroup;
  CHAR16      *UnicodeString;
  UINTN       StringSize;
  CHAR8       *AsciiString;
  UINT8       *Buffer;

  //
  // The device configuration data for this board is encoded as
  // a table of fixed-size records in the .data section.
  //
  // For each entry, we extract:
  //   - HII string ID for device designation
  //   - Device type (SMBIOS Type 41 enumeration)
  //   - Device instance number
  //   - PCI segment group, bus, device/function
  //
  // These are resolved against the HII string package installed
  // during initialization to produce the actual ASCII strings.
  //
  // The string table format is:
  //   struct {
  //     UINT16  StringId;       // +0: HII string reference
  //     UINT8   DevType;        // +2: device type
  //     UINT8   Instance;       // +3: reserved/instance
  //     UINT16  SegmentBus;     // +4: segment group (high) + bus (low)
  //     UINT8   DevFunc;        // +6: PCI device (high nibble) + function (low nibble)
  //     UINT8   Reserved;       // +7: reserved
  //     UINT16  Reserved2;      // +8: reserved
  //   } (10 bytes per entry)
  //
  // The table entries are indexed differently for Type 41 (primary)
  // vs secondary device data in the HII strings.
  //

  //
  // For Type 41, the HII string reference is in a different
  // format than for Type 9. The device string reference has
  // the format:
  //   - 0x0000: No string (use default)
  //   - non-zero: HII string ID (1-based)
  //
  // The string table is indexed by TableIndex and the result
  // depends on which SMBIOS type we're building.
  //
  // TODO: This is a stub - the actual implementation would
  // resolve from the UBA configuration table.
  //
  return EFI_UNSUPPORTED;
}

/**
  Internal memory copy wrapper with overlap checking.

  @param[in,out] Destination  Pointer to the destination buffer.
  @param[in]     Source       Pointer to the source buffer.
  @param[in]     Length       Number of bytes to copy.

  @return Pointer to the destination buffer.
**/
STATIC
VOID *
InternalCopyMemChecked (
  OUT VOID       *Destination,
  IN  CONST VOID *Source,
  IN  UINTN      Length
  )
{
  return CopyMem (Destination, Source, Length);
}

/**
  Convert a Unicode (UCS-2) string to ASCII, with bounds checking.

  @param[in]  Source       Pointer to the Unicode source string.
  @param[out] Destination  Pointer to the ASCII destination buffer.
  @param[in]  DestMax      Maximum size of the destination buffer in bytes.

  @retval EFI_SUCCESS           The string was converted.
  @retval EFI_INVALID_PARAMETER Invalid parameter.
  @retval EFI_BUFFER_TOO_SMALL  The destination buffer is too small.
  @retval EFI_UNSUPPORTED       The source contains a character > 0xFF.
**/
STATIC
EFI_STATUS
InternalUnicodeToAscii (
  IN  CONST CHAR16  *Source,
  OUT CHAR8         *Destination,
  IN  UINTN         DestMax
  )
{
  return UnicodeStrToAsciiStrS (Source, Destination, DestMax);
}

/**
  Calculate the length of an ASCII string (excluding null terminator).

  @param[in] String  Pointer to the null-terminated ASCII string.

  @return The length of the string, not including the terminating null.
**/
STATIC
UINTN
InternalAsciiStrLen (
  IN CONST CHAR8  *String
  )
{
  return AsciiStrLen (String);
}

/**
  Compare two ASCII strings with length limit.

  @param[in] FirstString   Pointer to the first ASCII string.
  @param[in] SecondString  Pointer to the second ASCII string.
  @param[in] Length        Maximum number of characters to compare.

  @return 0 if strings are equal, negative if FirstString < SecondString,
          positive if FirstString > SecondString.
**/
STATIC
INTN
InternalAsciiStrnCmp (
  IN CONST CHAR8  *FirstString,
  IN CONST CHAR8  *SecondString,
  IN UINTN        Length
  )
{
  return AsciiStrnCmp (FirstString, SecondString, Length);
}

/**
  Calculate the size of a null-terminated Unicode string, in bytes
  (including the null terminator).

  @param[in] String  Pointer to the null-terminated Unicode string.

  @return The size in bytes, including the null terminator.
**/
STATIC
UINTN
InternalUnicodeStrSize (
  IN CONST CHAR16  *String
  )
{
  return StrSize (String);
}

/**
  Calculate the size of a null-terminated ASCII string, in bytes
  (including the null terminator).

  @param[in] String  Pointer to the null-terminated ASCII string.

  @return The size in bytes, including the null terminator.
**/
STATIC
UINTN
InternalAsciiStrSize (
  IN CONST CHAR8  *String
  )
{
  return AsciiStrSize (String);
}

/**
  Calculate the string length of a null-terminated Unicode string.

  @param[in] String  Pointer to the null-terminated Unicode string.

  @return The number of characters in the string.
**/
STATIC
UINTN
InternalUnicodeStrLen (
  IN CONST CHAR16  *String
  )
{
  return StrLen (String);
}

/**
  Read an 8-bit I/O port.

  @param[in] Port  I/O port number.

  @return The value read from the port.
**/
STATIC
UINT8
IoRead8 (
  IN UINT16  Port
  )
{
  return __inbyte (Port);
}

/**
  Write an 8-bit I/O port.

  @param[in] Port   I/O port number.
  @param[in] Value  Value to write.
**/
STATIC
VOID
IoWrite8 (
  IN UINT16  Port,
  IN UINT8   Value
  )
{
  __outbyte (Port, Value);
}

//
// SMBIOS Type 41 (Onboard Device) string table data for LightningRidge EX EC B3
//
// Each 10-byte record encodes one onboard device's string reference and
// SMBIOS Type 41 fields.
//
// Fields:
//   [0-1]  UINT16  StringId   - reference to HII string (type41 + instance)
//   [2]    UINT8   DevType    - device type per SMBIOS spec
//   [3]    UINT8   InstanceNum - device type instance
//   [4]    UINT8   Segment    - PCI segment group (top) + Bus (bottom) or flags
//   [5]    UINT8   Bus        - PCI bus number
//   [6]    UINT8   DevFunc    - PCI device/function
//   [7-9]  UINT8   Reserved   - reserved/formatting
//
// For Type 41 secondary entries, the string reference is 0x0000 and the
// device instance + bus/segment data are at different offsets.
//

/**
  Build a complete SMBIOS Type 41 record from the configuration and
  add it to the SMBIOS table.

  @param[in,out] SmbiosRecordBuffer  Buffer for the SMBIOS record.
  @param[in]     Type41Index         Index into the Type 41 string table (0..29).
  @param[in]     FirstStringId       HII string ID for the primary type 41.
  @param[in]     SecondStringId      HII string ID for the secondary type 41.
  @param[in]     StringData          Pointer to the string data buffer.
  @param[in]     StringDataSize      Size of the string data.

  @retval EFI_SUCCESS             Record was built successfully.
  @retval EFI_OUT_OF_RESOURCES    Memory allocation failed.
  @retval EFI_INVALID_PARAMETER   Invalid configuration data.
**/
EFI_STATUS
BuildAndAddSmbiosType41 (
  IN OUT SMBIOS_STRUCT_TABLE_HEADER  *SmbiosRecordBuffer,
  IN     UINTN                       Type41Index,
  IN     UINT16                      FirstStringId,
  IN     UINT16                      SecondStringId,
  IN     UINT8                       *StringData,
  IN     UINTN                       StringDataSize
  )
{
  EFI_STATUS  Status;
  UINT8       *Buffer;
  CHAR8       *String1;
  CHAR8       *String2;
  UINTN       TotalSize;
  UINT8       *DataPtr;
  UINTN       StringLen;

  Buffer   = NULL;
  String1  = NULL;
  String2  = NULL;
  Status   = EFI_SUCCESS;

  //
  // If no HII string handle is available, skip SMBIOS table creation
  //
  if (gSmbiosStringPackHandle == NULL) {
    return EFI_NOT_STARTED;
  }

  //
  // Retrieve the Unicode strings from HII
  // (String1 for the device designation, String2 for optional secondary)
  //
  if (FirstStringId != 0) {
    //
    // Get the HII string
    //
    String1 = InternalAllocateZeroPool (SMBIOS_STRING_MAX_LEN);
    if (String1 == NULL) {
      return EFI_OUT_OF_RESOURCES;
    }

    //
    // Convert from HII string to ASCII
    // (In the original, this converts from HII string ID through
    // the HII String protocol to a CHAR16, then to CHAR8)
    //
    // For now, just copy the reference name as a placeholder
    //
  }

  //
  // Build the SMBIOS Type 41 record
  // The formatted area includes:
  //   - Reference designation string (index 1)
  //   - Device type
  //   - Device type instance
  //   - Segment group number
  //   - Bus number
  //   - Device/function number
  //   - Data size (optional, typically 0)
  //   - Strings follow the formatted area
  //

  return Status;
}

/**
  Internal: Locate the end of the SMBIOS string area and return
  the total structure size.

  For an SMBIOS structure, the string area begins after the formatted
  portion and continues until a double-null (00 00) is encountered.
  The total size = formatted_length + string_area_size.

  @param[in]  StructureAddress  Address of the SMBIOS structure.
  @param[out] TotalSize         Pointer to receive total structure size.

  @return EFI_SUCCESS if the size was computed, EFI_INVALID_PARAMETER otherwise.
**/
EFI_STATUS
GetSmbiosStructureSize (
  IN  CONST SMBIOS_STRUCT_TABLE_HEADER  *StructureAddress,
  OUT UINTN                             *TotalSize
  )
{
  CONST UINT8  *StringPtr;

  if (StructureAddress == NULL || TotalSize == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  StringPtr  = (CONST UINT8 *)StructureAddress + StructureAddress->Length;
  *TotalSize = StructureAddress->Length;

  while (*StringPtr != '\0' || *(StringPtr + 1) != '\0') {
    StringPtr++;
    (*TotalSize)++;
  }

  *TotalSize += 2;   // count the double-null terminator
  return EFI_SUCCESS;
}

/**
  Update (add or replace) an SMBIOS entry.

  If an entry with the same type already exists in the SMBIOS table,
  it will be removed before the new one is added.

  @param[in] SmbiosRecord  Pointer to the fully-formed SMBIOS record.

  @retval EFI_SUCCESS            The entry was added/updated.
  @retval EFI_INVALID_PARAMETER  SmbiosRecord is NULL.
  @retval EFI_OUT_OF_RESOURCES   Out of resources.
  @retval other                  Error from SmbiosAdd.
**/
EFI_STATUS
AddSmbiosRecord (
  IN SMBIOS_STRUCT_TABLE_HEADER  *SmbiosRecord
  )
{
  EFI_STATUS      Status;
  EFI_SMBIOS_HANDLE SmbiosHandle;

  if (SmbiosRecord == NULL || gSmbiosProtocol == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // Remove existing entries of this type to allow replacing data.
  // The SMBIOS protocol's Remove function will be called for each
  // existing entry.
  //
  SmbiosHandle = SMBIOS_HANDLE_PI_RESERVED;
  Status = gSmbiosProtocol->Add (
                              gSmbiosProtocol,
                              NULL,
                              &SmbiosHandle,
                              SmbiosRecord
                              );

  return Status;
}

/**
  Build and add a Type 41 SMBIOS record for a specific entry index.

  @param[in] Buffer     Buffer for constructing the SMBIOS record.
  @param[in] EntryIndex Index of the Type 41 entry (0..0x1D).

  @retval EFI_SUCCESS             Record was built and added.
  @retval EFI_OUT_OF_RESOURCES    Memory allocation failed.
  @retval EFI_UNSUPPORTED         Invalid entry index.
**/
EFI_STATUS
UpdateSmbiosType41Entry (
  IN UINT8  *Buffer,
  IN UINTN  EntryIndex
  )
{
  EFI_STATUS  Status;
  UINTN       DeviceType;
  UINT8       DevTypeInstance;
  UINT8       BusNum;
  UINT8       DevFunc;

  //
  // Extract device type and PCI address from the configuration data.
  // The configuration table at sub_77C uses board-specific values
  // derived from string table entries.
  //
  // For each entry (0..29):
  // - String IDs come from the string table (two strings per entry:
  //   primary device designation and secondary/alternate)
  // - Device type enumeration
  // - PCI location (bus, device, function)
  //
  // The record is built and submitted to the SMBIOS protocol.
  //

  //
  // Build and add the primary record
  //
  Status = BuildAndAddSmbiosType41 (
             (SMBIOS_STRUCT_TABLE_HEADER *)Buffer,
             EntryIndex,
             0,   // First string ID from config
             0,   // Second string ID from config
             NULL,
             0
             );

  //
  // If a secondary device string is defined (non-zero string ID),
  // build and add the secondary record too.
  //

  return Status;
}

/**
  Build and add the configuration data for a specific device slot.
  This handles both Type 41 and Type 9 string references with their
  PCI bus/device/function information.

  @param[in,out] SmbiosBuffer  Buffer for SMBIOS record construction.
  @param[in]     ConfigData    Pointer to the configuration data table.
  @param[in]     SlotIndex     Index of the slot/device.
  @param[in]     SecondaryOnly  If TRUE, only process secondary string.

  @retval EFI_SUCCESS             Record was processed.
  @retval EFI_OUT_OF_RESOURCES    Memory allocation failed.
  @retval EFI_UNSUPPORTED         Unsupported slot configuration.
**/
EFI_STATUS
ProcessSlotConfiguration (
  IN OUT SMBIOS_STRUCT_TABLE_HEADER  *SmbiosBuffer,
  IN     UINT8                       *ConfigData,
  IN     UINTN                       SlotIndex,
  IN     BOOLEAN                     SecondaryOnly
  )
{
  EFI_STATUS  Status;
  UINT8       *SlotPtr;
  UINT16      StringId;
  UINT8       SlotType;
  UINT8       BusNum;
  UINT8       DevFunc;
  UINT8       InstanceCount;

  //
  // Parse the configuration table entry.
  // Each entry encodes the SMBIOS slot/device parameters for
  // the LightningRidge EX EC B3 board layout:
  //
  // For Type 41 entries:
  //   StringId=0 means "no string" (unused entry)
  //   StringId!=0 means "use this HII string"
  //
  // The secondary string is tested separately. If it exists,
  // a second SMBIOS entry is created with the alternate string.
  //

  return EFI_UNSUPPORTED;
}

/**
  Look up a string from the specified HII package list by string ID.

  @param[in]  HiiHandle     Handle of the HII package list.
  @param[in]  StringId      The string ID to look up.

  @return Pointer to the allocated string, or NULL if not found.
**/
STATIC
CHAR16 *
InternalGetStringFromHii (
  IN EFI_HII_HANDLE  HiiHandle,
  IN EFI_STRING_ID   StringId
  )
{
  EFI_STATUS  Status;
  UINTN       BufferSize;
  CHAR16      *StringBuffer;
  CHAR16      TempChar;

  if (HiiHandle == NULL || StringId == 0) {
    return NULL;
  }

  BufferSize   = 0;
  StringBuffer = NULL;

  //
  // First call to get the required buffer size
  //
  Status = gHiiString->GetString (
                         gHiiString,
                         Language,
                         HiiHandle,
                         StringId,
                         &StringBuffer,
                         &BufferSize,
                         NULL
                         );
  if (Status == EFI_BUFFER_TOO_SMALL) {
    StringBuffer = InternalAllocateZeroPool (BufferSize);
    if (StringBuffer == NULL) {
      return NULL;
    }

    Status = gHiiString->GetString (
                           gHiiString,
                           Language,
                           HiiHandle,
                           StringId,
                           &StringBuffer,
                           &BufferSize,
                           NULL
                           );
    if (EFI_ERROR (Status)) {
      InternalFreePool (StringBuffer);
      return NULL;
    }
  }

  return StringBuffer;
}

/**
  Internal: Determine the total size of an SMBIOS string table entry,
  including the formatted area and all strings.

  This calculates how many bytes an SMBIOS structure takes,
  given the header + strings + double null terminator.

  @param[in] SmbiosHeader  Pointer to the SMBIOS structure header.

  @return Total size of the SMBIOS structure in bytes.
**/
STATIC
UINTN
InternalGetSmbiosStructureTotalSize (
  IN SMBIOS_STRUCT_TABLE_HEADER  *SmbiosHeader
  )
{
  UINTN  Size;
  UINT8  *StringStart;

  Size        = SmbiosHeader->Length;
  StringStart = (UINT8 *)SmbiosHeader + SmbiosHeader->Length;

  //
  // Walk strings until we hit double-null
  //
  while (TRUE) {
    if (*StringStart == '\0') {
      Size += 1;
      if (*(StringStart + 1) == '\0') {
        Size += 1;
        break;
      }
      StringStart++;
    } else {
      StringStart++;
      Size++;
    }
  }

  return Size;
}

/**
  Add/Update SMBIOS record via the SMBIOS Update Protocol.

  The SmbiosUpdate protocol is a board-specific protocol that provides
  a simpler callback mechanism for installing SMBIOS data.

  @param[in] UpdateProtocol  Pointer to the SMBIOS Update Protocol.
  @param[in] UpdateFunction  Index of the update function to call.
  @param[in] Buffer          Pointer to the SMBIOS record data.
  @param[in] BufferSize      Size of the SMBIOS record data.

  @retval EFI_SUCCESS  The update function was called successfully.
  @retval others       Error from the protocol function.
**/
EFI_STATUS
CallSmbiosUpdateProtocol (
  IN EFI_SMBIOS_UPDATE_PROTOCOL  *UpdateProtocol,
  IN UINTN                       UpdateFunction,
  IN VOID                        *Buffer,
  IN UINTN                       BufferSize
  )
{
  EFI_STATUS Status;
  UINT16     EfiSmbiosHandle;

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

  EfiSmbiosHandle = SMBIOS_HANDLE_PI_RESERVED;

  //
  // Call through the SmbiosUpdate protocol entry point.
  // The protocol's function pointer is at offset +16.
  //
  Status = UpdateProtocol->Callback (
                             UpdateProtocol,
                             &EfiSmbiosHandle,
                             Buffer,
                             BufferSize
                             );

  return Status;
}

/**
  SMBIOS data update callback for LightningRidge EX EC B3.
  This is the main callback registered with the UBA framework.

  The function iterates through three groups of SMBIOS table entries:
    1. Type 41 (Onboard Devices) - 30 entries
    2. Type 9  (System Slots)    - 8 entries
    3. Type 43 (TPM Device)      - 4 entries

  For each group, it builds the SMBIOS structures using the board-specific
  configuration data from the UBA config block, then installs them.

  @retval EFI_SUCCESS           All SMBIOS data tables were updated.
  @retval EFI_OUT_OF_RESOURCES  Memory allocation failure.
  @retval EFI_UNSUPPORTED       Board configuration mismatch.
**/
EFI_STATUS
EFIAPI
SmbiosDataUpdateCallback (
  VOID
  )
{
  EFI_STATUS  Status;
  UINT8       *SmbiosBuffer;
  UINTN       Index;
  UINTN       DeviceType;
  UINT8       *StringData;
  UINTN       StringDataSize;

  //
  // Allocate a working buffer for building SMBIOS records
  // (the maximum record size is 768 bytes for Type 41 records
  // with multiple strings).
  //
  SmbiosBuffer = InternalAllocateZeroPool (SMBIOS_RECORD_MAX_SIZE);
  if (SmbiosBuffer == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  //
  // Phase 1: Update SMBIOS Type 41 entries (Onboard Devices Extended)
  // The board has 30 onboard device entries with associated HII strings.
  //
  for (Index = 0; Index < SMBIOS_TYPE41_ENTRY_COUNT; Index++) {
    InternalZeroMem (SmbiosBuffer, SMBIOS_RECORD_MAX_SIZE);
    Status = UpdateSmbiosType41Entry (SmbiosBuffer, Index);
    if (!EFI_ERROR (Status)) {
      Status = AddSmbiosRecord ((SMBIOS_STRUCT_TABLE_HEADER *)SmbiosBuffer);
    }
  }

  //
  // Phase 2: Update SMBIOS Type 9 entries (System Slots)
  // Update 8 system slot definitions for this board.
  //
  for (Index = 0; Index < SMBIOS_TYPE9_ENTRY_COUNT; Index++) {
    InternalZeroMem (SmbiosBuffer, SMBIOS_RECORD_MAX_SIZE);
    //
    // Build SMBIOS Type 9 record...
    // (String references + slot configuration from board data)
    //
  }

  //
  // Phase 3: Update SMBIOS Type 43 entries (TPM Device)
  // TPM device configuration for this board variant.
  //
  for (Index = 0; Index < SMBIOS_TYPE43_ENTRY_COUNT; Index++) {
    InternalZeroMem (SmbiosBuffer, SMBIOS_RECORD_MAX_SIZE);
    //
    // Build SMBIOS Type 43 record...
    //
  }

  //
  // Cleanup
  //
  InternalFreePool (SmbiosBuffer);

  Status = EFI_SUCCESS;
  return Status;
}

/**
  Initialize the UBA SMBIOS data update driver.

  This function performs the following initialization:
  1. Saves the ImageHandle and SystemTable pointers.
  2. Retrieves the BootServices and RuntimeServices tables.
  3. Locates HII protocol interfaces (Database, String, ConfigRouting, Font, Runtime).
  4. Locates the DxeServicesTable from the configuration table.
  5. Locates the MM PCI Base protocol.
  6. Installs the HII string package with board-specific SMBIOS strings.
  7. Registers the SMBIOS update callback with the UBA framework.

  @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 driver was initialized.
  @retval EFI_UNSUPPORTED       The board type is not supported.
  @retval EFI_NOT_FOUND         Required protocols were not found.
**/
EFI_STATUS
InitializeSmbiosDataUpdateDxe (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS  Status;
  UINTN       HobList;
  EFI_GUID    gHiiDatabaseGuid         = { 0x587e72d7, 0xcc50, 0x4f79,
                                           { 0x82, 0x09, 0xca, 0x29, 0x1f, 0xc1, 0xa1, 0x0f } };
  EFI_GUID    gHiiStringGuid           = { 0x31a6406a, 0x6bdf, 0x4e46,
                                           { 0xb2, 0xa2, 0xeb, 0xaa, 0x89, 0xc4, 0x09, 0x20 } };
  EFI_GUID    gHiiConfigRoutingGuid    = { 0xef9fc172, 0xa1b2, 0x4693,
                                           { 0xb3, 0x27, 0x6d, 0x32, 0xfc, 0x41, 0x60, 0x42 } };
  EFI_GUID    gHiiFontGuid             = { 0xe9ca4775, 0x8657, 0x47fc,
                                           { 0x97, 0xe7, 0x7e, 0xd6, 0x5a, 0x08, 0x43, 0x24 } };
  EFI_GUID    gDxeServicesTableGuid    = { 0x05ad34ba, 0x6f02, 0x4214,
                                           { 0x95, 0x2e, 0x4d, 0xa0, 0x39, 0x8e, 0x2b, 0xb9 } };
  EFI_GUID    gMmPciBaseGuid           = { 0xfd480a76, 0xb134, 0x4ef7,
                                           { 0xad, 0xfe, 0xb0, 0xe0, 0x54, 0x63, 0x98, 0x07 } };
  EFI_GUID    gUbaConfigProtocolGuid   = { 0x5b1b31a1, 0x9562, 0x11d2,
                                           { 0x8e, 0x3f, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b } };

  //
  // Step 1: Save ImageHandle and SystemTable
  //
  gImageHandle = ImageHandle;
  gSystemTable = SystemTable;
  gBootServices = SystemTable->BootServices;
  gRuntimeServices = SystemTable->RuntimeServices;

  //
  // Step 2: Locate HII Database protocol
  //
  Status = gBootServices->LocateProtocol (
                            &gHiiDatabaseGuid,
                            NULL,
                            (VOID **)&gHiiDatabase
                            );
  if (EFI_ERROR (Status)) {
    DEBUG ((EFI_D_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", Status));
    ASSERT_EFI_ERROR (Status);
  }

  //
  // Step 3: Locate HII String protocol
  //
  Status = gBootServices->LocateProtocol (
                            &gHiiStringGuid,
                            NULL,
                            (VOID **)&gHiiString
                            );
  if (EFI_ERROR (Status)) {
    DEBUG ((EFI_D_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", Status));
    ASSERT_EFI_ERROR (Status);
  }

  //
  // Step 4: Locate HII Config Routing protocol
  //
  Status = gBootServices->LocateProtocol (
                            &gHiiConfigRoutingGuid,
                            NULL,
                            (VOID **)&gHiiConfigRouting
                            );
  if (EFI_ERROR (Status)) {
    DEBUG ((EFI_D_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", Status));
    ASSERT_EFI_ERROR (Status);
  }

  //
  // Step 5: Locate HII Font protocol
  //
  gBootServices->LocateProtocol (&gHiiFontGuid, NULL, (VOID **)&gHiiFont);

  //
  // Step 6: Locate HII Runtime protocol
  //
  gBootServices->LocateProtocol (&gHiiConfigRoutingGuid, NULL, (VOID **)&gHiiRuntime);

  //
  // Step 7: Locate the DxeServicesTable from the system configuration table
  //
  Status = InternalLocateConfigTable (&gDxeServicesTableGuid, &gDs);
  if (EFI_ERROR (Status)) {
    DEBUG ((EFI_D_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", Status));
    ASSERT_EFI_ERROR (Status);
  }
  ASSERT (gDs != NULL);

  //
  // Step 8: Initialize HOB list pointer
  //
  mHobList = InternalGetHobList ();

  //
  // Step 9: Locate the MM PCI Base protocol for memory-mapped PCI access
  //
  if (mPciUsra == NULL) {
    Status = gBootServices->LocateProtocol (
                              &gMmPciBaseGuid,
                              NULL,
                              (VOID **)&mPciUsra
                              );
    if (EFI_ERROR (Status)) {
      DEBUG ((EFI_D_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", Status));
      ASSERT_EFI_ERROR (Status);
    }
    ASSERT (mPciUsra != NULL);
  }

  return Status;
}

/**
  Entry point for the SmbiosDataUpdate DXE driver.

  This is the UEFI driver entry point, called by the DXE dispatcher.
  It initializes the driver and registers the SMBIOS data update
  callback with the UBA framework.

  @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 executed successfully.
  @retval EFI_UNSUPPORTED       The board type is not supported.
  @retval other                 An error occurred initializing the driver.
**/
EFI_STATUS
EFIAPI
SmbiosDataUpdateEntry (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS  Status;
  UINT32      UbaConfigData[6];
  UINT32      UbaConfigVersion;
  EFI_GUID    gSmbiosDataUpdateBoardGuid = { 0xaac6cafd, 0x42c6, 0x440a,
                                             { 0xb9, 0x58, 0x9f, 0xd4, 0xc8, 0x4b, 0x50, 0xea } };

  //
  // Step 1: Initialize the driver (locate protocols, etc.)
  //
  Status = InitializeSmbiosDataUpdateDxe (ImageHandle, SystemTable);
  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // Step 2: Display the board GUID for debugging
  //
  DEBUG ((EFI_D_INFO, "UBA:SmbiosDataUpdateEntry Image GUID=%g\n", &gSmbiosDataUpdateBoardGuid));

  //
  // Step 3: Install the HII string package for this board
  //
  gSmbiosStringPackHandle = InternalInstallSmbiosStringPack (
                              &ImageHandle,
                              &gSmbiosDataUpdateBoardGuid,
                              sizeof (gSmbiosDataUpdateBoardGuid)
                              );
  if (gSmbiosStringPackHandle == NULL) {
    DEBUG ((EFI_D_ERROR, "gSmbiosStringPackHandle != ((void *) 0)\n"));
    ASSERT (FALSE);
  }

  //
  // Step 4: Look up the UBA SMBIOS Update protocol
  //
  UbaConfigVersion = 0;
  UbaConfigData[0] = 0;
  UbaConfigData[1] = 1;
  UbaConfigData[2] = 12;
  UbaConfigData[3] = 0;
  UbaConfigData[4] = 0x424FAE40;   // "UBA " signature / board type identifier

  //
  // Locate the UBA SMBIOS Update protocol
  //
  {
    EFI_GUID  gUbaSmbiosUpdateGuid = { 0xe03e0d46, 0x5263, 0x4845,
                                       { 0xb0, 0xa4, 0x58, 0xd5, 0x7b, 0x31, 0x77, 0xe2 } };
    Status = gBootServices->LocateProtocol (
                              &gUbaSmbiosUpdateGuid,
                              NULL,
                              (VOID **)&gSmbiosUpdateProtocol
                              );
    if (EFI_ERROR (Status)) {
      return Status;
    }
  }

  //
  // Step 5: Register the callback with the UBA framework
  //
  // The UBA SMBIOS Update protocol provides a callback registration
  // entry at offset +16 in the protocol structure. It takes:
  //   - Pointer to board config GUID
  //   - Configuration data block
  //   - Size of configuration data
  //
  Status = gSmbiosUpdateProtocol->Callback (
                                    gSmbiosUpdateProtocol,
                                    &gSmbiosDataUpdateBoardGuid,
                                    UbaConfigData,
                                    sizeof (UbaConfigData)
                                    );

  return Status;
}

/**
  UEFI Driver Entry Point.

  @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 driver entry point was executed.
  @retval EFI_UNSUPPORTED       The board type is not supported.
**/
EFI_STATUS
EFIAPI
UefiDriverEntryPoint (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  return SmbiosDataUpdateEntry (ImageHandle, SystemTable);
}