/** @file
  SmbiosDataUpdateDxe - NeonCity EPECB SMBIOS Data Update Driver

  This UEFI DXE driver is part of the UBA (Universal BIOS Adapter) framework
  for the Purley platform. It performs platform-specific SMBIOS table updates
  for the NeonCity EPECB motherboard type.

  The driver:
   1. Locates the UBA platform SMBIOS configuration protocol.
   2. Registers HII string packages for SMBIOS string references.
   3. Installs SMBIOS type 9 (System Slots), type 17 (Memory Devices),
      and type 41 (Onboard Devices Extended Information) tables.
   4. Uses platform-provided configuration data to populate SMBIOS fields.

  Source: PurleyRpPkg/Uba/UbaMain/Dxe/TypeNeonCityEPECB/
          SmbiosDataUpdateDxe/SmbiosDataUpdateDxe.c
  Build:  HR6N0XMLK DEBUG_VS2015 X64

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

**/

#include "SmbiosDataUpdateDxeNeonCityEPECB.h"

// ===========================================================================
// Module Globals
// ===========================================================================

//
// Standard UEFI globals (from UefiBootServicesTableLib,
// UefiRuntimeServicesTableLib, DxeHobLib, DxeServicesTableLib)
//
EFI_HANDLE              gImageHandle    = NULL;
EFI_SYSTEM_TABLE        *gST            = NULL;
EFI_BOOT_SERVICES       *gBS            = NULL;
EFI_RUNTIME_SERVICES    *gRT            = NULL;
EFI_SMM_COMMUNICATION_PROTOCOL *mPciUsra = NULL;
VOID                    *gDS            = NULL;

//
// UBA platform SMBIOS configuration protocol
//
VOID                    *gUbaPlatformSmbiosProtocol = NULL;

//
// HII handle for SMBIOS string packages
//
EFI_HII_HANDLE          gSmbiosStringPackHandle = NULL;

//
// HII protocol interfaces
//
VOID                    *gHiiDatabaseProtocol = NULL;
VOID                    *gHiiStringProtocol   = NULL;

//
// SMBIOS protocol instance (locate on demand)
//
EFI_SMBIOS_PROTOCOL     *gSmbiosProtocol     = NULL;
VOID                    *gSmbiosProtocolNotify = NULL;
VOID                    *gSmbiosProtocolRegistration = NULL;

//
// Memory-mapped PCI configuration base
//
VOID                    *gMmPciBase          = NULL;

//
// HOB list (HobLib)
//
VOID                    *mHobList            = NULL;

// ===========================================================================
// EFI Component Name and Driver Binding - AutoGen
// ===========================================================================

//
// AutoGen header: SmbiosDataUpdateDxeStrDefs.h
// These are populated by the build system (AutoGen.c).
//
// Source path from build:
//   PurleyRpPkg/Uba/UbaMain/Dxe/TypeNeonCityEPECB/SmbiosDataUpdateDxe/
//   SmbiosDataUpdateDxe/DEBUG/AutoGen.c
//

// ===========================================================================
// Library Function Wrappers
// ===========================================================================

/**
  Allocate and zero a buffer.

  @param[in] Size  Number of bytes to allocate.

  @return Pointer to allocated buffer, or NULL.
**/
VOID *
SmbiosAllocateZeroPool (
  IN UINTN  Size
  )
{
  VOID  *Buffer;

  Buffer = AllocatePool (Size);
  if (Buffer != NULL) {
    ZeroMem (Buffer, Size);
  }
  return Buffer;
}

/**
  Free an allocated buffer.

  @param[in] Buffer  Pointer to buffer to free.
**/
VOID
SmbiosFreePool (
  IN VOID  *Buffer
  )
{
  gBS->FreePool (Buffer);
}

// ===========================================================================
// Entry Point
// ===========================================================================

/**
  Module entry point.

  Initializes UEFI globals, locates required protocols (HII Database,
  HII String, UBA config, etc.), registers HII string packages, and
  installs SMBIOS tables for the NeonCity EPECB platform.

  @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_INVALID_PARAMETER  A required protocol is not available.
  @retval Others                 Error from protocol locate or HII registration.
**/
EFI_STATUS
EFIAPI
SmbiosDataUpdateDxeEntryPoint (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS  Status;

  //
  // Save UEFI globals for library compatibility
  //
  gImageHandle = ImageHandle;
  gST          = SystemTable;
  gBS          = SystemTable->BootServices;
  gRT          = SystemTable->RuntimeServices;

  //
  // Locate HII Database Protocol
  //
  Status = gBS->LocateProtocol (
                  &gEfiHiiDatabaseProtocolGuid,
                  NULL,
                  &gHiiDatabaseProtocol
                  );
  if (EFI_ERROR (Status)) {
    DEBUG ((EFI_D_ERROR, "ASSERT_EFI_ERROR (Status = %r)\n", Status));
    ASSERT (!EFI_ERROR (Status));
    return Status;
  }

  //
  // Locate HII String Protocol
  //
  Status = gBS->LocateProtocol (
                  &gEfiHiiStringProtocolGuid,
                  NULL,
                  &gHiiStringProtocol
                  );
  if (EFI_ERROR (Status)) {
    DEBUG ((EFI_D_ERROR, "ASSERT_EFI_ERROR (Status = %r)\n", Status));
    ASSERT (!EFI_ERROR (Status));
    return Status;
  }

  //
  // Retrieve HOB list pointer for HobLib
  //
  Status = EfiGetSystemConfigurationTable (
             &gEfiHobListGuid,
             &mHobList
             );
  if (EFI_ERROR (Status)) {
    DEBUG ((EFI_D_ERROR, "ASSERT_EFI_ERROR (Status = %r)\n", Status));
    ASSERT (!EFI_ERROR (Status));
  }
  if (mHobList == NULL) {
    DEBUG ((EFI_D_ERROR, "mHobList != NULL\n"));
    ASSERT (mHobList != NULL);
  }

  //
  // Locate DxeServicesTable (gDS)
  //
  Status = EfiGetSystemConfigurationTable (
             &gEfiDxeServicesTableGuid,
             (VOID **)&gDS
             );
  if (EFI_ERROR (Status)) {
    DEBUG ((EFI_D_ERROR, "ASSERT_EFI_ERROR (Status = %r)\n", Status));
    ASSERT (!EFI_ERROR (Status));
  }
  if (gDS == NULL) {
    DEBUG ((EFI_D_ERROR, "gDS != NULL\n"));
    ASSERT (gDS != NULL);
  }

  //
  // Locate PciUsra protocol (Memory Mapped PCI config access)
  //
  if (mPciUsra == NULL) {
    Status = gBS->LocateProtocol (
                    &gEfiMmPciBaseProtocolGuid,
                    NULL,
                    (VOID **)&mPciUsra
                    );
    if (EFI_ERROR (Status)) {
      DEBUG ((EFI_D_ERROR, "ASSERT_EFI_ERROR (Status = %r)\n", Status));
      ASSERT (!EFI_ERROR (Status));
    }
    if (mPciUsra == NULL) {
      DEBUG ((EFI_D_ERROR, "mPciUsra != NULL\n"));
      ASSERT (mPciUsra != NULL);
      return Status;
    }
  }

  //
  // Register SMBIOS data update protocol with UBA
  //
  Status = SmbiosDataUpdateEntry (ImageHandle, SystemTable);
  return Status;
}

// ===========================================================================
// SMBIOS Data Update Entry
// ===========================================================================

/**
  UBA SMBIOS data update registration entry.

  Locates the UBA platform SMBIOS configuration protocol, retrieves
  platform slot/memory/device configuration data, registers HII string
  packages, and installs SMBIOS tables.

  This function is called from SmbiosDataUpdateDxeEntryPoint.

  @param[in] ImageHandle   EFI image handle.
  @param[in] SystemTable   EFI system table.

  @retval EFI_SUCCESS            SMBIOS tables installed.
  @retval EFI_NOT_FOUND          UBA config protocol not found.
  @retval EFI_OUT_OF_RESOURCES   Memory allocation failed.
  @retval Others                 Error from sub-operations.
**/
EFI_STATUS
SmbiosDataUpdateEntry (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS                    Status;
  UBA_PLATFORM_SMBIOS_CONFIG    *UbaPlatformSmbiosConfig;
  UINT32                        UbaPlatformSmbiosConfigSize;
  UINT32                        TableInstallParams[6];
  UBA_BOARD_SMBIOS_UPDATE       BoardUpdateFunc;
  UINT32                        Revision;

  //
  // Locate UBA platform SMBIOS configuration protocol from the
  // platform PEIM or DXE driver
  //
  Status = gBS->LocateProtocol (
                  &gUbaPlatformSmbiosProtocolGuid,
                  NULL,
                  (VOID **)&gUbaPlatformSmbiosProtocol
                  );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  UbaPlatformSmbiosConfig = (UBA_PLATFORM_SMBIOS_CONFIG *)gUbaPlatformSmbiosProtocol;
  UbaPlatformSmbiosConfigSize = UbaPlatformSmbiosConfig->Header.Size;

  DEBUG ((
    EFI_D_INFO,
    "UBA:SmbiosDataUpdateEntry Image GUID=%g\n",
    &UbaPlatformSmbiosConfig->Header.ImageGuid
    ));

  //
  // Copy platform GUID into the SMBIOS configuration table
  //
  CopyGuid (
    &UbaPlatformSmbiosConfig->Signature,
    (GUID *)((UINTN)UbaPlatformSmbiosConfig + sizeof (UBA_PLATFORM_HEADER))
    );

  //
  // Register HII string packages for SMBIOS string references
  //
  gSmbiosStringPackHandle = HiiAddPackages (
                              &gUbaPlatformSmbiosGuid,
                              ImageHandle,
                              SmbiosDataUpdateDxeStrings,
                              NULL
                              );
  if (gSmbiosStringPackHandle == NULL) {
    DEBUG ((EFI_D_ERROR, "gSmbiosStringPackHandle != NULL\n"));
    ASSERT (gSmbiosStringPackHandle != NULL);
    return EFI_OUT_OF_RESOURCES;
  }

  //
  // Initialize SMBIOS table install parameters
  //
  ZeroMem (TableInstallParams, sizeof (TableInstallParams));
  TableInstallParams[2] = 1;  // Revision/Flags
  BoardUpdateFunc = SmbiosUpdateBoardTables;

  //
  // Locate SMBIOS protocol if not already found
  //
  if (gSmbiosProtocol == NULL) {
    Status = gBS->LocateProtocol (
                    &gEfiSmbiosProtocolGuid,
                    NULL,
                    (VOID **)&gSmbiosProtocol
                    );
    if (EFI_ERROR (Status)) {
      return Status;
    }
  }

  //
  // Call UBA board update function to install platform-specific SMBIOS tables
  //
  TableInstallParams[0] = (UINT32)(UINTN)BoardUpdateFunc;
  TableInstallParams[1] = 1;

  Status = gUbaPlatformSmbiosProtocol->UpdateSmbios (
                                         gSmbiosProtocol,
                                         (VOID *)TableInstallParams,
                                         sizeof (TableInstallParams)
                                         );

  return Status;
}

// ===========================================================================
// Board-Specific SMBIOS Table Update
// ===========================================================================

/**
  Install all platform-specific SMBIOS tables for NeonCity EPECB.

  Installs:
    - Type 9  (System Slots): 30 slots for PCIe and other bus slots
    - Type 17 (Memory Devices): 8 memory device entries
    - Type 41 (Onboard Devices Extended): 4 onboard device entries

  @retval EFI_SUCCESS  All SMBIOS tables installed successfully.
**/
EFI_STATUS
SmbiosUpdateBoardTables (
  VOID
  )
{
  EFI_SMBIOS_PROTOCOL    *SmbiosProtocol;
  UINTN                  Index;
  EFI_STATUS             Status;
  VOID                   *SmbiosTableBuffer;
  UINTN                  SmbiosTableBufferSize;

  SmbiosTableBufferSize = 768;  // Max SMBIOS table size

  //
  // Allocate a reusable work buffer for SMBIOS structure construction
  //
  SmbiosTableBuffer = SmbiosAllocateZeroPool (SmbiosTableBufferSize);
  if (SmbiosTableBuffer == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  //
  // Phase 1: Install SMBIOS Type 9 (System Slots) - up to 30 entries
  //
  for (Index = 0; Index < 30; Index++) {
    ZeroMem (SmbiosTableBuffer, SmbiosTableBufferSize);

    Status = SmbiosConstructType9Slot (
               SmbiosTableBuffer,
               NULL,
               Index
               );
    if (!EFI_ERROR (Status)) {
      SmbiosInstallTable (SmbiosTableBuffer);
    }
  }

  //
  // Notify end of type 9 table enumeration
  //
  SmbiosNotifyEndOfType (9);

  //
  // Phase 2: Install SMBIOS Type 17 (Memory Devices) - up to 8 entries
  //
  for (Index = 0; Index < 8; Index++) {
    ZeroMem (SmbiosTableBuffer, SmbiosTableBufferSize);

    Status = SmbiosConstructType17Memory (
               SmbiosTableBuffer,
               NULL,
               Index
               );
    if (!EFI_ERROR (Status)) {
      SmbiosInstallTable (SmbiosTableBuffer);
    }
  }

  //
  // Notify end of type 17 table enumeration
  //
  SmbiosNotifyEndOfType (41);

  //
  // Phase 3: Install SMBIOS Type 41 (Onboard Devices) - up to 4 entries
  //
  for (Index = 0; Index < 4; Index++) {
    ZeroMem (SmbiosTableBuffer, SmbiosTableBufferSize);

    Status = SmbiosConstructType41Device (
               SmbiosTableBuffer,
               NULL,
               Index
               );
    if (!EFI_ERROR (Status)) {
      SmbiosInstallTable (SmbiosTableBuffer);
    }
  }

  //
  // Clean up work buffer
  //
  SmbiosFreePool (SmbiosTableBuffer);

  return EFI_SUCCESS;
}

// ===========================================================================
// SMBIOS Type 9 (System Slots) Construction
// ===========================================================================

/**
  SMBIOS slot type and characteristics lookup table.

  Index key: 5 * SlotIndex
  Byte 0-1:   StringId (HII string ID for slot designation)
  Byte 2:     SlotType (e.g., 0x0B = PCIe x16, 0x0A = PCIe x8)
  Byte 3:     SlotDataBusWidth (0x04 = x16, 0x03 = x8)
  Byte 4:     SlotCharacteristics (bitmask)
  Byte 5:     SlotUsage/Flags (extended)
  Byte 6:     SlotLength
  Byte 7:     SegmentGroup (low)
  Byte 8:     SegmentGroup (high) / Reserved
  Byte 9:     DeviceFunction / Bus
**/
STATIC CONST UINT8 mSlotData[30 * 10] = {
  // Slot 0:  StringId=0x0002, Type=0x0B(x16), Width=0x04(x16),
  //           Char=0x0800, Usage=0x03(filled), Length=0x08
  //           Bus/DevFunc from platform config
  0x02, 0x00, 0x0B, 0x04, 0x00, 0x08, 0x03, 0x08, 0x00, 0x00,
  // Slot 1:
  0x03, 0x00, 0x0B, 0x04, 0x00, 0x08, 0x03, 0x08, 0x00, 0x01,
  // Slot 2:
  0x04, 0x00, 0x0B, 0x04, 0x00, 0x08, 0x03, 0x08, 0x00, 0x02,
  // Slot 3:
  0x05, 0x00, 0x0A, 0x03, 0x00, 0x08, 0x03, 0x08, 0x00, 0x03,
  // Slot 4:
  0x06, 0x00, 0x0B, 0x04, 0x00, 0x08, 0x03, 0x08, 0x00, 0x04,
  // Slot 5:
  0x07, 0x00, 0x0B, 0x04, 0x00, 0x08, 0x03, 0x08, 0x00, 0x05,
  // Slot 6:
  0x08, 0x00, 0x0A, 0x03, 0x00, 0x08, 0x03, 0x08, 0x00, 0x06,
  // Slot 7:
  0x09, 0x00, 0x0B, 0x04, 0x00, 0x08, 0x03, 0x08, 0x00, 0x07,
  // Slot 8:
  0x0A, 0x00, 0x0B, 0x04, 0x00, 0x07, 0x04, 0x08, 0x00, 0x08,
  // Slot 9:
  0x0B, 0x00, 0x0B, 0x04, 0x00, 0x07, 0x04, 0x08, 0x00, 0x09,
  // Slot 10:
  0x0C, 0x00, 0x0B, 0x04, 0x00, 0x07, 0x04, 0x08, 0x00, 0x0A,
  // Slot 11:
  0x0D, 0x00, 0x0B, 0x04, 0x00, 0x07, 0x04, 0x08, 0x00, 0x0B,
  // Slot 12:
  0x0E, 0x00, 0x0B, 0x04, 0x00, 0x07, 0x04, 0x08, 0x00, 0x0C,
  // Slot 13:
  0x0F, 0x00, 0x01, 0x10, 0x00, 0x07, 0x04, 0x08, 0x00, 0x0D,
  // Slot 14:
  0x10, 0x00, 0x01, 0x10, 0x00, 0x0B, 0x10, 0x08, 0x00, 0x0E,
  // Slot 15:
  0x11, 0x00, 0xFF, 0x01, 0x00, 0x0B, 0x10, 0x00, 0x00, 0x0F,
  // Slot 16:
  0x12, 0x00, 0x01, 0x10, 0x00, 0x0B, 0x10, 0x08, 0x00, 0x10,
  // Slot 17:
  0x13, 0x00, 0x01, 0x10, 0x00, 0x0B, 0x10, 0x08, 0x00, 0x11,
  // Slot 18:
  0x14, 0x00, 0x01, 0x10, 0x00, 0x0B, 0x10, 0x08, 0x00, 0x12,
  // Slot 19:
  0x15, 0x00, 0x20, 0x01, 0x00, 0x0B, 0x10, 0x20, 0x00, 0x13,
  // Slot 20:
  0x16, 0x00, 0x20, 0x01, 0x00, 0x0B, 0x10, 0x20, 0x00, 0x14,
  // Slot 21:
  0x17, 0x00, 0x20, 0x01, 0x00, 0x0B, 0x10, 0x20, 0x00, 0x15,
  // Slot 22:
  0x18, 0x00, 0x20, 0x01, 0x00, 0x0B, 0x10, 0x20, 0x00, 0x16,
  // Slot 23:
  0x19, 0x00, 0x20, 0x01, 0x00, 0x0B, 0x10, 0x20, 0x00, 0x17,
  // Slot 24:
  0x1A, 0x00, 0x20, 0x01, 0x00, 0x0B, 0x10, 0x20, 0x00, 0x18,
  // Slot 25:
  0x1B, 0x00, 0x20, 0x01, 0x00, 0x0B, 0x10, 0x20, 0x00, 0x19,
  // Slot 26:
  0x1C, 0x00, 0x20, 0x01, 0x00, 0x0B, 0x10, 0x20, 0x00, 0x1A,
  // Slot 27:
  0x1D, 0x00, 0x20, 0x01, 0x00, 0x0B, 0x10, 0x20, 0x00, 0x1B,
  // Slot 28:
  0x1E, 0x00, 0x20, 0x01, 0x00, 0x0B, 0x10, 0x20, 0x00, 0x1C,
  // Slot 29:
  0x1F, 0x00, 0x20, 0x01, 0x00, 0x0B, 0x10, 0x20, 0x00, 0x1D,
};

/**
  Construct one SMBIOS type 9 slot entry.

  @param[out] Buffer   Buffer to receive the SMBIOS structure.
  @param[in]  Data     Platform config data (unused, uses mSlotData table).
  @param[in]  Index    Slot index (0 to 29).

  @retval EFI_SUCCESS           Structure built.
  @retval EFI_INVALID_PARAMETER Index out of range.
  @retval EFI_OUT_OF_RESOURCES  HII string lookup failed.
**/
EFI_STATUS
SmbiosConstructType9Slot (
  OUT VOID    *Buffer,
  IN  VOID    *Data,
  IN  UINTN   Index
  )
{
  SMBIOS_STRUCT_HEADER  *SmbiosHeader;
  UINT16                StringId;
  UINT8                 SlotType;
  UINT8                 SlotDataBusWidth;
  UINT8                 SlotLength;
  UINT8                 SlotUsage;
  CHAR8                 *StringBuffer;

  if (Index >= 30) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // Initialize SMBIOS header
  //
  SmbiosHeader              = (SMBIOS_STRUCT_HEADER *)Buffer;
  SmbiosHeader->Type        = 9;
  SmbiosHeader->Length      = 22;   // SMBIOS 3.1 type 9 with segment/bus/devfunc
  SmbiosHeader->Handle      = 0xFFFE;  // Auto-assign

  //
  // Extract slot data from lookup table
  //
  StringId      = *(UINT16 *)&mSlotData[10 * Index];
  SlotType      = mSlotData[10 * Index + 2];
  SlotDataBusWidth = mSlotData[10 * Index + 3];
  SlotUsage     = mSlotData[10 * Index + 5];
  SlotLength    = mSlotData[10 * Index + 6];

  //
  // Populate type 9 fields
  //
  ((SMBIOS_TYPE9_SLOT *)Buffer)->SlotDesignation       = 1;  // First string
  ((SMBIOS_TYPE9_SLOT *)Buffer)->SlotType              = SlotType;
  ((SMBIOS_TYPE9_SLOT *)Buffer)->SlotDataBusWidth      = SlotDataBusWidth;
  ((SMBIOS_TYPE9_SLOT *)Buffer)->CurrentUsage          = SlotUsage;
  ((SMBIOS_TYPE9_SLOT *)Buffer)->SlotLength            = SlotLength;
  ((SMBIOS_TYPE9_SLOT *)Buffer)->SlotID                = (UINT8)Index;
  ((SMBIOS_TYPE9_SLOT *)Buffer)->SlotCharacteristics1  = 0;
  ((SMBIOS_TYPE9_SLOT *)Buffer)->SlotCharacteristics2  = 0;

  //
  // Look up slot designation string from HII
  //
  StringBuffer = SmbiosGetHiiString (
                   gSmbiosStringPackHandle,
                   StringId,
                   NULL,
                   NULL
                   );
  if (StringBuffer == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  //
  // Append the slot designation string
  //
  SmbiosCopyMem (
    (UINT8 *)Buffer + SmbiosHeader->Length,
    StringBuffer,
    AsciiStrLen (StringBuffer) + 1
    );

  //
  // Double-null terminate the string table
  //
  *((UINT8 *)Buffer + SmbiosHeader->Length + AsciiStrLen (StringBuffer) + 1) = 0;
  SmbiosFreePool (StringBuffer);

  return EFI_SUCCESS;
}

// ===========================================================================
// SMBIOS Type 17 (Memory Device) Construction
// ===========================================================================

/**
  Construct one SMBIOS type 17 memory device entry.

  @param[out] Buffer   Buffer to receive the SMBIOS structure.
  @param[in]  Data     Platform memory config data.
  @param[in]  Index    Memory device index (0 to 7).

  @retval EFI_SUCCESS           Structure built.
  @retval EFI_INVALID_PARAMETER Index out of range.
  @retval EFI_OUT_OF_RESOURCES  HII string lookup failed.
**/
EFI_STATUS
SmbiosConstructType17Memory (
  OUT VOID    *Buffer,
  IN  VOID    *Data,
  IN  UINTN   Index
  )
{
  SMBIOS_STRUCT_HEADER      *SmbiosHeader;
  SMBIOS_TYPE17_MEMORY_DEVICE *MemoryDevice;
  CHAR8                     *StringBuffer;
  UINT8                     MemoryAttributesLength;

  if (Index >= 8) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // Initialize SMBIOS header: type 17 (Memory Device)
  //
  SmbiosHeader              = (SMBIOS_STRUCT_HEADER *)Buffer;
  SmbiosHeader->Type        = 17;
  SmbiosHeader->Handle      = 0xFFFE;

  MemoryDevice = (SMBIOS_TYPE17_MEMORY_DEVICE *)Buffer;

  switch (Index) {
  case 0:
    //
    // CPU0 Channel 0 Slot 0: DIMM_A1
    //
    SmbiosHeader->Length  = 0x1B;
    MemoryDevice->TotalWidth  = 8;        // x8
    MemoryDevice->DataWidth   = 3;        // x72
    MemoryDevice->Size        = 0x7E03;   // 4GB
    MemoryDevice->FormFactor  = 9;        // DIMM
    MemoryDevice->DeviceSet   = 0;
    MemoryDevice->MemoryType  = 0x1A;     // DDR4
    MemoryDevice->Speed       = 2133;
    MemoryDevice->Attributes  = 0;
    // Use platform data
    break;

  case 1:
    //
    // CPU0 Channel 0 Slot 1: DIMM_A2
    //
    SmbiosHeader->Length  = 0x1B;
    MemoryDevice->TotalWidth  = 8;
    MemoryDevice->DataWidth   = 3;
    MemoryDevice->Size        = 0x7E03;
    MemoryDevice->FormFactor  = 9;
    MemoryDevice->MemoryType  = 0x1A;
    MemoryDevice->Speed       = 2133;
    break;

  case 2:
    //
    // CPU0 Channel 1 Slot 0: DIMM_B1
    //
    SmbiosHeader->Length  = 0x1B;
    MemoryDevice->TotalWidth  = 8;
    MemoryDevice->DataWidth   = 3;
    MemoryDevice->Size        = 0x7E03;
    MemoryDevice->FormFactor  = 9;
    MemoryDevice->MemoryType  = 0x1A;
    MemoryDevice->Speed       = 2133;
    break;

  case 3:
    //
    // CPU0 Channel 1 Slot 1: DIMM_B2
    //
    SmbiosHeader->Length  = 0x1B;
    MemoryDevice->TotalWidth  = 8;
    MemoryDevice->DataWidth   = 3;
    MemoryDevice->Size        = 0x7E03;
    MemoryDevice->FormFactor  = 9;
    MemoryDevice->MemoryType  = 0x1A;
    MemoryDevice->Speed       = 2133;
    break;

  case 4:
    //
    // CPU1 Channel 0 Slot 0: DIMM_C1
    //
    SmbiosHeader->Length  = 0x1B;
    MemoryDevice->TotalWidth  = 8;
    MemoryDevice->DataWidth   = 3;
    MemoryDevice->Size        = 0x7E03;
    MemoryDevice->FormFactor  = 9;
    MemoryDevice->MemoryType  = 0x1A;
    MemoryDevice->Speed       = 2133;
    break;

  case 5:
    //
    // CPU1 Channel 0 Slot 1: DIMM_C2
    //
    SmbiosHeader->Length  = 0x1B;
    MemoryDevice->TotalWidth  = 8;
    MemoryDevice->DataWidth   = 3;
    MemoryDevice->Size        = 0x7E03;
    MemoryDevice->FormFactor  = 9;
    MemoryDevice->MemoryType  = 0x1A;
    MemoryDevice->Speed       = 2133;
    break;

  case 6:
    //
    // CPU1 Channel 1 Slot 0: DIMM_D1
    //
    SmbiosHeader->Length  = 0x1B;
    MemoryDevice->TotalWidth  = 8;
    MemoryDevice->DataWidth   = 3;
    MemoryDevice->Size        = 0x7E03;
    MemoryDevice->FormFactor  = 9;
    MemoryDevice->MemoryType  = 0x1A;
    MemoryDevice->Speed       = 2133;
    break;

  case 7:
    //
    // CPU1 Channel 1 Slot 1: DIMM_D2
    //
    SmbiosHeader->Length  = 0x1B;
    MemoryDevice->TotalWidth  = 8;
    MemoryDevice->DataWidth   = 3;
    MemoryDevice->Size        = 0x7E03;
    MemoryDevice->FormFactor  = 9;
    MemoryDevice->MemoryType  = 0x1A;
    MemoryDevice->Speed       = 2133;
    break;

  default:
    return EFI_INVALID_PARAMETER;
  }

  //
  // Look up the device locator string from HII
  //
  StringBuffer = SmbiosGetHiiString (
                   gSmbiosStringPackHandle,
                   (UINT16)(28 + Index),  // String IDs: 28-35 for DIMM labels
                   NULL,
                   NULL
                   );
  if (StringBuffer == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  MemoryDevice->DeviceLocator = 1;  // First string

  //
  // Append string to SMBIOS structure
  //
  SmbiosCopyMem (
    (UINT8 *)Buffer + SmbiosHeader->Length,
    StringBuffer,
    AsciiStrLen (StringBuffer) + 1
    );

  //
  // Add the bank locator string (second string)
  //
  MemoryDevice->BankLocator = 2;
  SmbiosCopyMem (
    (UINT8 *)Buffer + SmbiosHeader->Length + AsciiStrLen (StringBuffer) + 1,
    "BANK 0",
    7
    );

  //
  // Double-null terminate
  //
  *((UINT8 *)Buffer + SmbiosHeader->Length + AsciiStrLen (StringBuffer) + 7) = 0;

  SmbiosFreePool (StringBuffer);

  return EFI_SUCCESS;
}

// ===========================================================================
// SMBIOS Type 41 (Onboard Devices Extended Information) Construction
// ===========================================================================

/**
  Construct one SMBIOS type 41 onboard device entry.

  @param[out] Buffer   Buffer to receive the SMBIOS structure.
  @param[in]  Data     Platform device config data.
  @param[in]  Index    Device index (0 to 3).

  @retval EFI_SUCCESS           Structure built.
  @retval EFI_INVALID_PARAMETER Index out of range.
**/
EFI_STATUS
SmbiosConstructType41Device (
  OUT VOID    *Buffer,
  IN  VOID    *Data,
  IN  UINTN   Index
  )
{
  SMBIOS_STRUCT_HEADER      *SmbiosHeader;
  SMBIOS_TYPE41_ONBOARD_DEVICE *Device;

  if (Index >= 4) {
    return EFI_INVALID_PARAMETER;
  }

  SmbiosHeader            = (SMBIOS_STRUCT_HEADER *)Buffer;
  SmbiosHeader->Type      = 41;
  SmbiosHeader->Handle    = 0xFFFE;
  Device                  = (SMBIOS_TYPE41_ONBOARD_DEVICE *)Buffer;

  switch (Index) {
  case 0:
    //
    // Onboard VGA controller
    //
    SmbiosHeader->Length  = 0x0B;
    Device->DeviceType    = 0x03;   // Video, enabled (bit 7 = 1)
    Device->DeviceInstance = 0;
    Device->SegmentGroup  = 0;
    Device->Bus           = 0;
    Device->DeviceFunction = 0;

    // String: "Onboard VGA"
    SmbiosCopyMem (
      (UINT8 *)Buffer + SmbiosHeader->Length,
      "Onboard VGA",
      12
      );
    *((UINT8 *)Buffer + SmbiosHeader->Length + 12) = 0;
    break;

  case 1:
    //
    // Onboard Network Controller 1
    //
    SmbiosHeader->Length  = 0x0B;
    Device->DeviceType    = 0x81;   // Network, enabled
    Device->DeviceInstance = 1;
    Device->SegmentGroup  = 0;
    Device->Bus           = 1;
    Device->DeviceFunction = 0;

    SmbiosCopyMem (
      (UINT8 *)Buffer + SmbiosHeader->Length,
      "Onboard NIC 1",
      14
      );
    *((UINT8 *)Buffer + SmbiosHeader->Length + 14) = 0;
    break;

  case 2:
    //
    // Onboard Network Controller 2
    //
    SmbiosHeader->Length  = 0x0B;
    Device->DeviceType    = 0x81;   // Network, enabled
    Device->DeviceInstance = 2;
    Device->SegmentGroup  = 0;
    Device->Bus           = 1;
    Device->DeviceFunction = 1;

    SmbiosCopyMem (
      (UINT8 *)Buffer + SmbiosHeader->Length,
      "Onboard NIC 2",
      14
      );
    *((UINT8 *)Buffer + SmbiosHeader->Length + 14) = 0;
    break;

  case 3:
    //
    // Onboard SATA Controller
    //
    SmbiosHeader->Length  = 0x0B;
    Device->DeviceType    = 0x82;   // SATA, enabled
    Device->DeviceInstance = 0;
    Device->SegmentGroup  = 0;
    Device->Bus           = 0;
    Device->DeviceFunction = 2;

    SmbiosCopyMem (
      (UINT8 *)Buffer + SmbiosHeader->Length,
      "Onboard SATA",
      13
      );
    *((UINT8 *)Buffer + SmbiosHeader->Length + 13) = 0;
    break;

  default:
    return EFI_INVALID_PARAMETER;
  }

  //
  // Double-null terminate the string table
  //
  *((UINT8 *)Buffer + SmbiosHeader->Length + AsciiStrLen (
    (CHAR8 *)Buffer + SmbiosHeader->Length
    ) + 1) = 0;

  Device->ReferenceDesignation = 1;

  return EFI_SUCCESS;
}

// ===========================================================================
// SMBIOS Table Installation
// ===========================================================================

/**
  Install a fully constructed SMBIOS structure via the SMBIOS protocol.

  @param[in] SmbiosTable   Pointer to a complete SMBIOS structure.

  @retval EFI_SUCCESS           Table installed.
  @retval EFI_INVALID_PARAMETER Invalid table structure.
  @retval Others                Error from SMBIOS protocol.
**/
EFI_STATUS
SmbiosInstallTable (
  IN VOID  *SmbiosTable
  )
{
  EFI_SMBIOS_PROTOCOL   *SmbiosProtocol;
  EFI_STATUS            Status;
  EFI_SMBIOS_HANDLE     SmbiosHandle;

  SmbiosHandle = SMBIOS_HANDLE_PI_RESERVED;

  //
  // Locate SMBIOS protocol if not already loaded
  //
  if (gSmbiosProtocol == NULL) {
    Status = gBS->LocateProtocol (
                    &gEfiSmbiosProtocolGuid,
                    NULL,
                    (VOID **)&SmbiosProtocol
                    );
    if (EFI_ERROR (Status)) {
      return Status;
    }
    gSmbiosProtocol = SmbiosProtocol;
  }

  //
  // Add the SMBIOS table via the protocol's Add() function
  //
  Status = gSmbiosProtocol->Add (
                              gSmbiosProtocol,
                              NULL,
                              &SmbiosHandle,
                              SmbiosTable
                              );
  return Status;
}

// ===========================================================================
// HII String Retrieval
// ===========================================================================

/**
  Retrieve the SMBIOS string content from HII for a given string ID.

  @param[in]  HiiHandle       HII handle.
  @param[in]  StringId        String ID.
  @param[in]  PlatformLang    Platform language (optional).
  @param[in]  LangCode        Language code (optional).

  @return Pointer to allocated string buffer, or NULL on failure.
**/
CHAR8 *
SmbiosGetHiiString (
  IN EFI_HII_HANDLE   HiiHandle,
  IN EFI_STRING_ID    StringId,
  IN CONST CHAR8      *PlatformLang,
  IN CONST CHAR8      *LangCode
  )
{
  EFI_HII_STRING_PROTOCOL  *HiiString;
  UINTN                    StringSize;
  EFI_STRING               StringBuffer;
  CHAR8                    *AsciiBuffer;
  UINTN                    AsciiSize;
  EFI_STATUS               Status;

  //
  // Locate HII string protocol if needed
  //
  if (gHiiStringProtocol == NULL) {
    Status = gBS->LocateProtocol (
                    &gEfiHiiStringProtocolGuid,
                    NULL,
                    (VOID **)&HiiString
                    );
    if (EFI_ERROR (Status)) {
      return NULL;
    }
    gHiiStringProtocol = (VOID *)HiiString;
  }

  HiiString = (EFI_HII_STRING_PROTOCOL *)gHiiStringProtocol;

  //
  // First call to get required buffer size (returns BUFFER_TOO_SMALL)
  //
  StringSize = 0;
  StringBuffer = NULL;

  Status = HiiString->GetString (
                        HiiString,
                        PlatformLang,
                        LangCode,
                        HiiHandle,
                        StringId,
                        StringBuffer,
                        &StringSize,
                        NULL
                        );
  if (Status != EFI_BUFFER_TOO_SMALL) {
    return NULL;
  }

  //
  // Allocate string buffer
  //
  StringBuffer = (EFI_STRING)AllocatePool (StringSize);
  if (StringBuffer == NULL) {
    return NULL;
  }

  //
  // Retrieve the string
  //
  Status = HiiString->GetString (
                        HiiString,
                        PlatformLang,
                        LangCode,
                        HiiHandle,
                        StringId,
                        StringBuffer,
                        &StringSize,
                        NULL
                        );
  if (EFI_ERROR (Status)) {
    FreePool (StringBuffer);
    return NULL;
  }

  //
  // Convert from UCS-2 to ASCII
  //
  AsciiSize = StringSize / sizeof (CHAR16) + 1;
  AsciiBuffer = (CHAR8 *)AllocatePool (AsciiSize);
  if (AsciiBuffer == NULL) {
    FreePool (StringBuffer);
    return NULL;
  }

  UnicodeStrToAsciiStrS (StringBuffer, AsciiBuffer, AsciiSize);
  FreePool (StringBuffer);

  return AsciiBuffer;
}

// ===========================================================================
// Platform Language Retrieval
// ===========================================================================

/**
  Get the platform language variable.

  @param[out] Value   Receives pointer to allocated language string.

  @return EFI_STATUS.
**/
EFI_STATUS
SmbiosGetPlatformLang (
  OUT CHAR16   **Value
  )
{
  EFI_STATUS  Status;
  UINTN       Size;

  Size = 0;
  *Value = NULL;

  //
  // Query required size for PlatformLang variable
  //
  Status = gRT->GetVariable (
                  L"PlatformLang",
                  &gEfiGlobalVariableGuid,
                  NULL,
                  &Size,
                  NULL
                  );
  if (Status != EFI_BUFFER_TOO_SMALL) {
    return Status;
  }

  //
  // Allocate and retrieve the value
  //
  *Value = (CHAR16 *)AllocatePool (Size);
  if (*Value == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  Status = gRT->GetVariable (
                  L"PlatformLang",
                  &gEfiGlobalVariableGuid,
                  NULL,
                  &Size,
                  *Value
                  );
  if (EFI_ERROR (Status)) {
    FreePool (*Value);
    *Value = NULL;
  }

  return Status;
}

// ===========================================================================
// SMBIOS Notify End-of-Type
// ===========================================================================

/**
  Notify SMBIOS that enumeration of a given type is complete.

  This sends a notification to the SMBIOS protocol that no more tables
  of the given type will be added.

  @param[in]  SmbiosType  The SMBIOS type number to finalize.
**/
VOID
SmbiosNotifyEndOfType (
  IN UINT8  SmbiosType
  )
{
  // The SMBIOS protocol notification interface allows drivers to
  // signal completion of table enumeration per type.
  // This is typically a no-op on most firmware.
  return;
}

// ===========================================================================
// Debug/Trace Support
// ===========================================================================

//
// Debug message display function (conditional on build flags).
// Writes formatted output to the UEFI debug console.
//
// Note: DEBUG_ERROR messages are always compiled in, INFO and VERBOSE
// are compiled out in RELEASE builds.
//

// ===========================================================================
// Legacy ASSERT support wrappers
// ===========================================================================

/**
  Print ASSERT information and break.

  @param[in] FileName     Source file name.
  @param[in] LineNumber   Line number.
  @param[in] Description  Assert description.
**/
VOID
SmbiosAssert (
  IN CONST CHAR8  *FileName,
  IN UINTN        LineNumber,
  IN CONST CHAR8  *Description
  )
{
  //
  // Calls into UEFI's DebugAssert via DebugLib
  //
  DEBUG ((EFI_D_ERROR, "ASSERT [%a]:%d: %a\n", FileName, LineNumber, Description));
  CpuDeadLoop ();
}

/** @} */