/** @file
  SmbiosDataUpdateDxeLightningRidgeEXECB2 - SMBIOS Data Update DXE Driver

  This DXE driver updates SMBIOS data tables for the Lightning Ridge EXECB2
  platform variant. It reads platform-specific board and slot configuration from
  MM PCI registers and patches SMBIOS Type 9 (System Slots), Type 10 (On-Board
  Devices), and Type 41 (Onboard Devices Extended) structures accordingly.

  The driver obtains its update parameters via the UBA (Universal BIOS Architecture)
  protocol, reading HII string packages to get slot/device name strings, and using
  MM PCI register reads (via DxeMmPciBaseLib) to determine actual hardware configuration.

  Build path:
    PurleyRpPkg\Uba\UbaMain\Dxe\TypeLightningRidgeEXECB2\SmbiosDataUpdateDxe

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

**/

#include "SmbiosDataUpdateDxeLightningRidgeEXECB2.h"

//
// Global UEFI protocol pointers
//
EFI_SYSTEM_TABLE         *gSystemTable    = NULL;
EFI_BOOT_SERVICES        *gBootServices   = NULL;
EFI_RUNTIME_SERVICES     *gRuntimeServices = NULL;
EFI_HANDLE                gImageHandle    = NULL;
EFI_HII_DATABASE_PROTOCOL *gHiiDatabase   = NULL;
EFI_HII_STRING_PROTOCOL  *gHiiString      = NULL;
EFI_HANDLE                gHiiPackageListHandle = NULL;
EFI_SMM_PCI_BASE_PROTOCOL *gMmPciBase     = NULL;

//
// Global SMBIOS string pack handle from UBA
//
EFI_HII_HANDLE            gSmbiosStringPackHandle = NULL;

//
// HOB list pointer
//
VOID                      *gHobList        = NULL;

//
// DXE Services Table pointer
//
EFI_DXE_SERVICES          *gDxeServices    = NULL;

//
// UBA SMBIOS data update protocol
//
VOID                      *gUbaSmbiosUpdateProtocol = NULL;

//
// HII Config Routing Protocol (for SMBIOS string updates)
//
EFI_HII_CONFIG_ROUTING_PROTOCOL *gHiiConfigRouting = NULL;

//
// HII Config Access Protocol (driver handle)
//
EFI_HII_CONFIG_ACCESS_PROTOCOL  *gHiiConfigAccess  = NULL;

//
// HII Package List handle
//
EFI_HII_HANDLE            gHiiPackageList   = NULL;

//
// SMBIOS string pack configuration port handle
//
VOID                      *gSmbiosStringPackConfigPort = NULL;

//
// SMBIOS string pack data port (used in string enumeration)
//
VOID                      *gSmbiosStringPackDataPort = NULL;

//
// Forward declarations of internal helper functions
//
STATIC
EFI_STATUS
GetPlatformLang (
  OUT CHAR16  **PlatformLang
  );

STATIC
CHAR8 *
GetSupportedLanguage (
  IN  CHAR8   *SupportedLanguages,
  IN  CHAR8   *TargetLanguage,
  ...
  );

STATIC
CHAR16 *
HiiGetString (
  IN EFI_HII_HANDLE  HiiHandle,
  IN UINT16          StringId
  );

STATIC
VOID *
InternalAllocateCopyPool (
  IN UINTN  AllocationSize,
  IN VOID   *Buffer
  );

STATIC
UINTN
InternalAsciiStrLen (
  IN CHAR8  *String
  );

STATIC
UINTN
InternalAsciiStrSize (
  IN CHAR8  *String
  );

STATIC
INTN
InternalAsciiStrnCmp (
  IN CHAR8   *FirstString,
  IN CHAR8   *SecondString,
  IN UINTN   Length
  );

STATIC
CHAR8 *
InternalAsciiStrnCpyS (
  OUT CHAR8        *Destination,
  IN  CHAR8        *Source,
  IN  UINTN        Count
  );

STATIC
UINTN
InternalStrLen (
  IN CHAR16  *String
  );

STATIC
UINTN
InternalStrSize (
  IN CHAR16  *String
  );

STATIC
CHAR8 *
InternalUnicodeStrToAscii (
  IN CHAR16  *Source,
  IN UINTN   DestMax
  );

STATIC
EFI_STATUS
InternalSmbiosStringPackFindByType (
  IN  UINT8   Type,
  OUT UINT16  *Handle
  );

//
// SMBIOS Type 9 slot configuration table for Lightning Ridge EXECB2.
// Each entry is a 10-byte record: { Type, BusWidth, Characteristics1, Characteristics2,
//                                    Segment, Bus, Device/Function, DataBusWidth, Pad1, Pad2 }
// Organized as an array of 30 (0x1E) entries.
//
// NOTE: These values are encoded from the hardware-specific slot layout for this
//       platform. The actual register-read based detection happens at runtime.
//
STATIC CONST UINT8  mType9SlotConfig[] = {
  // Slot 0:  PCH PCIe x16, width x16 (0x0B), x1 link (0x08)
  0x02, 0x80, 0x10, 0x00, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00,
  // Slot 1:  PCH PCIe x8
  0x03, 0x80, 0x10, 0x00, 0x00, 0x00, 0x08, 0x04, 0x00, 0x00,
  // ... (additional entries generated at runtime from register data)
};

/**
  Reads a 32-bit value from an MM PCI register.

  Computes a PCI CF8 address from bus/device/function/register and reads
  via the MM PCI protocol.

  @param[in]  Bus       PCI bus number.
  @param[in]  Device    PCI device number (bits [4:0] of devfn).
  @param[in]  Function  PCI function number (bits [2:0] of devfn).
  @param[in]  Register  PCI configuration register offset.

  @return The 32-bit value read from the configuration register.
          Returns 0xFFFFFFFF on error (no device present).
**/
UINT32
MmPciRead32 (
  IN UINT8   Bus,
  IN UINT8   Device,
  IN UINT8   Function,
  IN UINT32  Register
  )
{
  UINT32  Address;

  //
  // Build PCI CF8 address from bus/dev/func/reg
  // CF8 = (EN | Bus << 16 | Device << 11 | Function << 8 | Register)
  //
  Address = (UINT32)((Function & 0x7) |
                     ((Device & 0x1F) << 3) |
                     ((Bus & 0xFF) << 8)) << 12;
  Address |= (UINT32)(Register & 0xFFF);

  //
  // If MM PCI protocol is not available, return error
  //
  if (gMmPciBase == NULL) {
    return 0xFFFFFFFF;
  }

  //
  // Read via MM PCI base protocol function 24 (Read32)
  //
  return gMmPciBase->Read32 (Address);
}

//
// ----- Boot Services Library helpers (linked from DXE) -----
//

/**
  Calls gBS->LocateProtocolBuffer to locate a protocol by GUID.

  @param[in]   ProtocolGuid  The protocol GUID.
  @param[out]  Protocol      Pointer to receive the protocol interface.

  @return EFI_STATUS.
**/
EFI_STATUS
LocateProtocol (
  IN  EFI_GUID  *ProtocolGuid,
  OUT VOID      **Protocol
  )
{
  return gBootServices->LocateProtocol (ProtocolGuid, NULL, Protocol);
}

/**
  Allocates a buffer of the given size from EFI boot services data pool.

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

  @return Pointer to allocated buffer, or NULL on failure.
**/
VOID *
AllocatePool (
  IN UINTN  Size
  )
{
  EFI_STATUS  Status;
  VOID        *Buffer;

  Status = gBootServices->AllocatePool (EfiBootServicesData, Size, &Buffer);
  if (EFI_ERROR (Status)) {
    return NULL;
  }

  return Buffer;
}

//
// ----- ZeroMem / CopyMem wrappers -----
//

/**
  Fills a buffer with zeros.

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

  @return Buffer.
**/
VOID *
InternalZeroMem (
  IN VOID   *Buffer,
  IN UINTN  Size
  )
{
  volatile UINT8  *Ptr;

  Ptr = (volatile UINT8 *)Buffer;
  while (Size--) {
    *Ptr++ = 0;
  }

  return Buffer;
}

/**
  Copies a memory buffer.

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

  @return Destination.
**/
VOID *
InternalCopyMem (
  OUT VOID        *Destination,
  IN  CONST VOID  *Source,
  IN  UINTN       Count
  )
{
  CHAR8  *Dst8;
  CONST CHAR8  *Src8;

  Dst8 = (CHAR8 *)Destination;
  Src8 = (CONST CHAR8 *)Source;

  if (Count == 0) {
    return Destination;
  }

  if (Dst8 == Src8) {
    return Destination;
  }

  //
  // Use forward copy if source and destination don't overlap,
  // or source is before destination (non-an overlapping case)
  //
  if (Src8 > Dst8 || Src8 + Count <= Dst8) {
    while (Count--) {
      *Dst8++ = *Src8++;
    }
  } else {
    //
    // Backward copy for overlapping regions where src < dst
    //
    Dst8 += Count;
    Src8 += Count;
    while (Count--) {
      *--Dst8 = *--Src8;
    }
  }

  return Destination;
}

//
// ----- String utility helpers -----
//

/**
  Returns the length of a null-terminated ASCII string.

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

  @return Length of the string in bytes (excluding null terminator).
**/
UINTN
InternalAsciiStrLen (
  IN CHAR8  *String
  )
{
  const CHAR8  *Sc;

  ASSERT (String != NULL);

  for (Sc = String; *Sc != '\0'; Sc++) {
    ASSERT (Sc - String < PCD_GET_FIXED (PcdMaximumAsciiStringLength));
  }

  return Sc - String;
}

/**
  Returns the size of a null-terminated ASCII string, including terminator.

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

  @return Size of the string in bytes including null terminator.
**/
UINTN
InternalAsciiStrSize (
  IN CHAR8  *String
  )
{
  return InternalAsciiStrLen (String) + 1;
}

/**
  Compares two ASCII strings up to a specified length.

  @param[in]  FirstString    First string to compare.
  @param[in]  SecondString   Second string to compare.
  @param[in]  Length         Maximum number of characters to compare.

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

  while (*FirstString && *SecondString &&
         *FirstString == *SecondString &&
         Length > 1)
  {
    FirstString++;
    SecondString++;
    Length--;
  }

  return (INTN)(CHAR8)(*FirstString) - (INTN)(CHAR8)(*SecondString);
}

/**
  Copies an ASCII string with a maximum length.

  @param[out] Destination  Destination buffer.
  @param[in]  Source       Source string.
  @param[in]  Count        Maximum number of bytes to copy (including terminator).

  @return Destination.
**/
CHAR8 *
InternalAsciiStrnCpyS (
  OUT CHAR8  *Destination,
  IN  CHAR8  *Source,
  IN  UINTN  Count
  )
{
  UINTN  SourceLen;

  if (Count == 0) {
    return Destination;
  }

  SourceLen = InternalAsciiStrLen (Source);
  if (SourceLen >= Count) {
    SourceLen = Count - 1;
  }

  InternalCopyMem (Destination, Source, SourceLen);
  Destination[SourceLen] = '\0';

  return Destination;
}

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

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

  @return Length of the string in characters.
**/
UINTN
InternalStrLen (
  IN CHAR16  *String
  )
{
  UINTN  Length;

  ASSERT (String != NULL);

  Length = 0;
  while (String[Length] != L'\0') {
    if (Length >= PCD_GET_FIXED (PcdMaximumUnicodeStringLength)) {
      break;
    }
    Length++;
  }

  return Length;
}

/**
  Returns the size of a null-terminated Unicode string, including terminator.

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

  @return Size in bytes including null terminator.
**/
UINTN
InternalStrSize (
  IN CHAR16  *String
  )
{
  return (InternalStrLen (String) + 1) * sizeof (CHAR16);
}

/**
  Converts a UCS-2 Unicode string to a narrow ASCII string.

  @param[in]  Source    The UCS-2 string to convert.
  @param[in]  DestMax   Maximum size of the destination buffer in bytes.

  @return A newly-allocated ASCII string, or NULL on failure.
**/
CHAR8 *
InternalUnicodeStrToAscii (
  IN CHAR16  *Source,
  IN UINTN   DestMax
  )
{
  CHAR8    *Destination;
  CHAR8    *Dst;
  CHAR16   Src;

  Destination = (CHAR8 *)Source;  // in-place when called from HII string path

  //
  // Convert in-place: each CHAR16 narrows to one CHAR8
  //
  Dst = (CHAR8 *)Source;
  while ((Src = *Source) != 0) {
    if ((UINTN)(Dst - (CHAR8 *)Source) >= DestMax) {
      break;
    }
    if (Src >= 0x100) {
      DEBUG ((EFI_D_ERROR, "UnicodeStrToAscii: *Source < 0x100 failed\n"));
      break;
    }
    *Dst++ = (CHAR8)Src;
    Source++;
  }
  *Dst = 0;

  return (CHAR8 *)Destination;
}

//
// ----- HII Language helpers -----
//

/**
  Retrieves the current platform language (from UEFI variable "PlatformLang").

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

  @return EFI_STATUS.
**/
EFI_STATUS
GetPlatformLang (
  OUT CHAR16  **PlatformLang
  )
{
  EFI_STATUS  Status;
  UINTN       BufferSize;
  CHAR16      *Lang;

  *PlatformLang = NULL;

  //
  // First call to get the required buffer size
  //
  BufferSize = 0;
  Status = gRuntimeServices->GetVariable (
                               L"PlatformLang",
                               &gEfiGlobalVariableGuid,
                               NULL,
                               &BufferSize,
                               NULL
                               );
  if (Status == EFI_BUFFER_TOO_SMALL) {
    //
    // Allocate the buffer
    //
    Lang = AllocatePool (BufferSize);
    if (Lang == NULL) {
      return EFI_OUT_OF_RESOURCES;
    }

    *PlatformLang = Lang;

    //
    // Second call to get the actual data
    //
    Status = gRuntimeServices->GetVariable (
                                 L"PlatformLang",
                                 &gEfiGlobalVariableGuid,
                                 NULL,
                                 &BufferSize,
                                 Lang
                                 );
    if (EFI_ERROR (Status)) {
      FreePool (Lang);
      *PlatformLang = NULL;
    }
  }

  return Status;
}

/**
  Matches a target language against a semicolon-separated list of supported
  languages (RFC 4646 format), accounting for partial matches with primary
  tags and secondary subtags.

  @param[in]  SupportedLanguages  RFC 4646 language list.
  @param[in]  TargetLanguage      The language to find.
  @param[in]  ...                 Optional additional language lists to search.

  @return Allocated matching language string, or NULL if no match found.
**/
CHAR8 *
GetSupportedLanguage (
  IN CHAR8  *SupportedLanguages,
  IN CHAR8  *TargetLanguage,
  ...
  )
{
  va_list   Args;
  CHAR8     *Lang;
  CHAR8     *Match;
  UINTN     PrimaryLen;
  UINTN     SecondaryLen;

  Match = NULL;

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

  //
  // Determine the length of the primary language subtag
  //
  PrimaryLen = InternalAsciiStrLen (TargetLanguage);
  if (PrimaryLen > 3) {
    PrimaryLen = 3;
  }

  //
  // Walk through the comma/semicolon-separated language list
  //
  Lang = SupportedLanguages;
  while (*Lang) {
    UINTN  LangLen;

    //
    // Skip separators
    //
    while (*Lang == ';' || *Lang == ',') {
      Lang++;
    }

    //
    // Find the end of this language entry
    //
    LangLen = 0;
    while (Lang[LangLen] && Lang[LangLen] != ';') {
      LangLen++;
    }

    //
    // Compare the primary subtag
    //
    if (LangLen >= PrimaryLen) {
      //
      // Check if the secondary subtag (after '-') matches
      //
      if (InternalAsciiStrnCmp (Lang, TargetLanguage, PrimaryLen) == 0) {
        //
        // Found a match - allocate and copy
        //
        Match = AllocatePool (LangLen + 1);
        if (Match != NULL) {
          InternalCopyMem (Match, Lang, LangLen);
          Match[LangLen] = '\0';
        }
        break;
      }
    }

    Lang += LangLen;
  }

  //
  // If we didn't find a match via primary language and there are no
  // additional arguments, try the other language list from the va_list
  //
  if (Match == NULL) {
    va_start (Args, TargetLanguage);

    while ((Lang = va_arg (Args, CHAR8 *)) != NULL) {
      //
      // Recurse through additional language lists
      //
      Match = GetSupportedLanguage (Lang, TargetLanguage);
      if (Match != NULL) {
        break;
      }
    }

    va_end (Args);
  }

  return Match;
}

//
// ----- HII String Protocol wrappers -----
//

/**
  Retrieves the size of an HII string, then allocates and retrieves the string data.

  @param[in]  HiiHandle   The HII handle for the string package.
  @param[in]  StringId    The string ID to retrieve.

  @return Pointer to the retrieved string, or NULL on failure.
**/
CHAR16 *
HiiGetString (
  IN EFI_HII_HANDLE  HiiHandle,
  IN UINT16          StringId
  )
{
  EFI_STATUS  Status;
  UINTN       StringSize;
  CHAR16      *String;

  ASSERT (HiiHandle != NULL);
  ASSERT (StringId != 0);

  if (!HiiHandle) {
    return NULL;
  }

  if (gHiiString == NULL) {
    return NULL;
  }

  //
  // First call to get the string size
  //
  StringSize = 0;
  Status = gHiiString->GetString (
                         gHiiString,
                         NULL,       // Language not needed for size query
                         HiiHandle,
                         StringId,
                         &String,
                         &StringSize,
                         NULL
                         );
  if (Status != EFI_BUFFER_TOO_SMALL) {
    return NULL;
  }

  //
  // Allocate the string buffer
  //
  String = AllocatePool (StringSize);
  if (String == NULL) {
    return NULL;
  }

  //
  // Second call to get the actual string
  //
  Status = gHiiString->GetString (
                         gHiiString,
                         NULL,
                         HiiHandle,
                         StringId,
                         &String,
                         &StringSize,
                         NULL
                         );
  if (EFI_ERROR (Status)) {
    FreePool (String);
    return NULL;
  }

  return String;
}

/**
  Allocates a copy of a buffer using AllocatePool.

  @param[in]  AllocationSize  Size of the buffer to allocate.
  @param[in]  Buffer          Source buffer to copy from.

  @return Pointer to the newly allocated copy, or NULL on failure.
**/
VOID *
InternalAllocateCopyPool (
  IN UINTN  AllocationSize,
  IN VOID   *Buffer
  )
{
  VOID  *NewBuffer;

  NewBuffer = AllocatePool (AllocationSize);
  if (NewBuffer != NULL) {
    InternalCopyMem (NewBuffer, Buffer, AllocationSize);
  }

  return NewBuffer;
}

/**
  Converts SMBIOS HII handle ID to a string and extracts the slot type.

  Translates from UBA SMBIOS string pack to actual SMBIOS table strings
  that reflect the slot naming on this platform.

  @param[in]  SlotHandle      Handle containing slot configuration data.
  @param[in]  StringId        The SMBIOS string ID for this slot.
  @param[in]  SlotIndex       The index of the slot being processed.
  @param[in]  SecondaryFlags  Secondary slot characteristics flags.

  @return Pointer to the ASCII string for this slot entry, or NULL on error.
**/
CHAR8 *
GetSmbiosSlotString (
  IN  UINTN   SlotHandle,
  IN  UINT16  StringId,
  IN  UINTN   SlotIndex,
  IN  UINTN   SecondaryFlags
  )
{
  CHAR16  *HiiString;
  CHAR8   *AsciiString;

  //
  // Get the HII string for this slot
  //
  HiiString = HiiGetString ((EFI_HII_HANDLE)SlotHandle, StringId);
  if (HiiString == NULL) {
    return NULL;
  }

  //
  // Convert to ASCII (narrow each CHAR16)
  //
  AsciiString = AllocatePool (InternalStrSize (HiiString));
  if (AsciiString == NULL) {
    FreePool (HiiString);
    return NULL;
  }

  InternalUnicodeStrToAscii (HiiString, InternalStrSize (HiiString));

  FreePool (HiiString);

  return AsciiString;
}

//
// ----- SMBIOS Data Update Core Functions -----
//

/**
  Builds SMBIOS Type 9 (System Slots) structure.

  Handles 30 system slots and populates slot type, length, bus width, ID,
  characteristics, segment, bus, device/function, and data bus width from
  platform-specific configuration tables (derived from MM PCI register reads).

  @param[in,out]  Buffer      Pointer to the SMBIOS Type 9 structure buffer.
  @param[in]      SlotIndex   Index of the slot to update (0-based).

  @retval EFI_SUCCESS           Slot updated successfully.
  @retval EFI_BAD_BUFFER_SIZE   Slot index is out of range.
  @retval EFI_OUT_OF_RESOURCES  Failed to allocate slot string buffers.
**/
EFI_STATUS
UpdateSmbiosType9Slots (
  IN OUT UINT8        *Buffer,
  IN     UINTN        SlotIndex
  )
{
  EFI_STATUS  Status;
  UINT8       SlotType;
  UINT8       SlotBusWidth;
  UINT8       CurrentUsage;
  UINT8       SlotLength;
  UINT16      SlotId;
  UINT8       SlotCharacteristics1;
  UINT8       SlotCharacteristics2;
  UINT16      SegmentGroupNum;
  UINT8       BusNum;
  UINT8       DeviceFuncNum;
  UINT8       DataBusWidth;
  UINT16      StringNumber;
  UINT16      SlotStringId;
  CHAR8       *SlotString;

  //
  // SMBIOS type 9 structure:
  //   [0] Type = 9
  //   [1] Length
  //   [2-3] Handle
  //   [4] Slot type
  //   [5] Slot bus width
  //   [6] Current usage
  //   [7] Slot length
  //   [8-9] Slot ID
  //   [10] Characteristics1
  //   [11] Characteristics2
  //   [12-13] Segment group number
  //   [14] Bus number
  //   [15] Device/function number
  //   [16] Data bus width
  //
  // The first field [0] must be the SMBIOS type (set by caller)
  // Field [1] is the structure length (set by caller = -2 for variable)
  // Field [2-3] handle (initialized by SMBIOS stack)

  Status = EFI_SUCCESS;

  //
  // Validate slot index (0 to 29)
  //
  if (SlotIndex >= SMBIOS_TYPE9_SLOT_COUNT) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // Set SMBIOS type 9 header fields
  //
  WRITE_UINT8 (Buffer, 9);                                // Type
  WRITE_UINT8 (Buffer + 1, (UINT8)(sizeof (SMBIOS_TABLE_TYPE9) + 2));  // Length (variable)
  // Handle set by caller

  switch (SlotIndex) {
  case 0:
    //
    // Slot 0: PCIe x16 (PCH root port)
    //
    SlotType = SLOT_TYPE_PCIE_X16;
    SlotBusWidth = SLOT_BUS_WIDTH_X16;
    SlotStringId = STR_TYPE9_SLOT1;       // "SLOT1"
    DataBusWidth = SLOT_BUS_WIDTH_X1;     // x1 link (x16 physical)
    break;

  case 1:
    //
    // Slot 1: PCIe x8 (PCH root port)
    //
    SlotType = SLOT_TYPE_PCIE_X8;
    SlotBusWidth = SLOT_BUS_WIDTH_X8;
    SlotStringId = STR_TYPE9_SLOT2;
    DataBusWidth = SLOT_BUS_WIDTH_X1;
    break;

  case 2:
    //
    // Slot 2: PCIe x8 (PCH root port)
    //
    SlotType = SLOT_TYPE_PCIE_X8;
    SlotBusWidth = SLOT_BUS_WIDTH_X8;
    SlotStringId = STR_TYPE9_SLOT4;
    DataBusWidth = SLOT_BUS_WIDTH_X1;
    break;

  case 3:
    //
    // Slot 3: PCIe x16 (PCH root port, x8 electrically)
    //
    SlotType = SLOT_TYPE_PCIE_X8;
    SlotBusWidth = SLOT_BUS_WIDTH_X8;
    SlotStringId = STR_TYPE9_SLOT3;
    DataBusWidth = SLOT_BUS_WIDTH_X1;
    break;

  case 4:
    //
    // Slot 4: PCIe x8 (PCH)
    //
    SlotType = SLOT_TYPE_PCIE_X8;
    SlotBusWidth = SLOT_BUS_WIDTH_X8;
    SlotStringId = STR_TYPE9_SLOT5;
    DataBusWidth = SLOT_BUS_WIDTH_X1;
    break;

  case 5:
    //
    // Slot 5: PCIe x8 (PCH)
    //
    SlotType = SLOT_TYPE_PCIE_X8;
    SlotBusWidth = SLOT_BUS_WIDTH_X8;
    SlotStringId = STR_TYPE9_SLOT6;
    DataBusWidth = SLOT_BUS_WIDTH_X1;
    break;

  case 6:
  case 7:
  case 8:
    //
    // Slots on CPU1 (PCIe root ports)
    //
    SlotType = SLOT_TYPE_PCIE_X16;
    SlotBusWidth = SLOT_BUS_WIDTH_X16;

    if (SlotIndex == 6) {
      SlotStringId = STR_TYPE9_CPU1_SLOT1;
    } else if (SlotIndex == 7) {
      SlotStringId = STR_TYPE9_CPU1_SLOT2;
    } else {
      SlotStringId = STR_TYPE9_CPU1_SLOT3;
    }
    DataBusWidth = SLOT_BUS_WIDTH_X16;
    break;

  case 9:
  case 10:
  case 11:
    //
    // Slots on CPU2 (PCIe root port)
    //
    SlotType = SLOT_TYPE_PCIE_X16;
    SlotBusWidth = SLOT_BUS_WIDTH_X16;

    if (SlotIndex == 9) {
      SlotStringId = STR_TYPE9_CPU2_SLOT1;
    } else if (SlotIndex == 10) {
      SlotStringId = STR_TYPE9_CPU2_SLOT2;
    } else {
      SlotStringId = STR_TYPE9_CPU2_SLOT3;
    }
    DataBusWidth = SLOT_BUS_WIDTH_X16;
    break;

  default:
    //
    // All other slots use PCIe x8 width
    //
    SlotType = SLOT_TYPE_PCIE_X8;
    SlotBusWidth = SLOT_BUS_WIDTH_X8;
    SlotStringId = (UINT16)(STR_TYPE9_SLOT1 + SlotIndex);
    DataBusWidth = SLOT_BUS_WIDTH_X4;
    break;
  }

  //
  // Write slot descriptor fields
  //
  WRITE_UINT8 (Buffer + 4, SlotType);
  WRITE_UINT8 (Buffer + 5, SlotBusWidth);

  //
  // CurrentUsage: 0xFF = available, 0x00 = unknown, other = in use
  //
  CurrentUsage = SLOT_USAGE_AVAILABLE;
  WRITE_UINT8 (Buffer + 6, CurrentUsage);

  //
  // SlotLength: 0x00 = unknown, 0x01 = long (full height), 0x02 = short
  //
  SlotLength = SLOT_LENGTH_LONG;
  WRITE_UINT8 (Buffer + 7, SlotLength);

  //
  // Slot ID / String number: 1-based offset into string table
  //
  StringNumber = (UINT16)(SlotIndex + 1);
  WRITE_UINT16 (Buffer + 8, StringNumber);

  //
  // Characteristics1: 3.3V, PME, etc.
  //
  SlotCharacteristics1 = SLOT_CHAR1_3_3V | SLOT_CHAR1_PME;
  WRITE_UINT8 (Buffer + 10, SlotCharacteristics1);

  //
  // Characteristics2
  //
  SlotCharacteristics2 = 0;
  WRITE_UINT8 (Buffer + 11, SlotCharacteristics2);

  //
  // Segment group number (default 0)
  //
  SegmentGroupNum = 0;
  WRITE_UINT16 (Buffer + 12, SegmentGroupNum);

  //
  // Bus number, Device/Function number, Data bus width
  // These are populated from the slot configuration table
  //
  BusNum        = mSlotConfig[SlotIndex].BusNum;
  DeviceFuncNum = mSlotConfig[SlotIndex].DeviceFuncNum;
  DataBusWidth  = mSlotConfig[SlotIndex].DataBusWidth;

  WRITE_UINT8 (Buffer + 14, BusNum);
  WRITE_UINT8 (Buffer + 15, DeviceFuncNum);
  WRITE_UINT8 (Buffer + 16, DataBusWidth);

  return Status;
}

/**
  Builds SMBIOS Type 10 (On-Board Devices) structure.

  Type 10 is a list of on-board device descriptors with device type and
  string number for the device description.

  @param[in,out]  Buffer       Pointer to the SMBIOS Type 10 structure buffer.
  @param[in]      DeviceIndex  Index of the device to update (0-based).

  @retval EFI_SUCCESS           Device entry updated.
  @retval EFI_INVALID_PARAMETER DeviceIndex is out of range.
**/
EFI_STATUS
UpdateSmbiosType10Devices (
  IN OUT UINT8        *Buffer,
  IN     UINTN        DeviceIndex
  )
{
  UINT8   DeviceType;
  UINT8   DeviceStringNum;
  UINT8   DeviceStatus;

  //
  // SMBIOS type 10 structure:
  //   [0] Type = 10
  //   [1] Length (variable)
  //   [2-3] Handle (initialized by SMBIOS)
  //   [4] Device count ( = number of descriptors = Type10Count )
  //   [5] Device type / string number pairs follow
  //       Each pair: [0] = Device Type, [1] = String Number (for description)
  //

  WRITE_UINT8 (Buffer, 10);               // Type
  WRITE_UINT16 (Buffer + 2, -2);           // Handle placeholder

  DeviceStatus = TYPE10_DEVICE_ENABLED;

  switch (DeviceIndex) {
  case 0:
    //
    // BMC / AST2500 video controller
    //
    DeviceType = TYPE10_DEVICE_TYPE_VIDEO;
    DeviceStringNum = 1;
    break;

  case 1:
    //
    // On-board NIC 1 (I350)
    //
    DeviceType = TYPE10_DEVICE_TYPE_ETHERNET;
    DeviceStringNum = 3;
    break;

  case 2:
    //
    // On-board NIC 2 (I350)
    //
    DeviceType = TYPE10_DEVICE_TYPE_ETHERNET;
    DeviceStringNum = 4;
    break;

  case 3:
    //
    // SATA controller
    //
    DeviceType = TYPE10_DEVICE_TYPE_SATA;
    DeviceStringNum = 6;
    break;

  case 4:
    //
    // On-board NIC 3
    //
    DeviceType = TYPE10_DEVICE_TYPE_ETHERNET;
    DeviceStringNum = 7;
    break;

  case 5:
    //
    // On-board NIC 4
    //
    DeviceType = TYPE10_DEVICE_TYPE_ETHERNET;
    DeviceStringNum = 8;
    break;

  case 6:
    //
    // USB controller
    //
    DeviceType = TYPE10_DEVICE_TYPE_UNKNOWN;
    DeviceStringNum = 0;   // No string
    break;

  case 7:
    //
    // SAS / SATA controller (PCH)
    //
    DeviceType = TYPE10_DEVICE_TYPE_SAS;
    DeviceStringNum = 5;
    break;

  default:
    return EFI_INVALID_PARAMETER;
  }

  //
  // Check if the device is actually present via MM PCI register read
  //
  if (DeviceIndex <= 5) {
    UINT32  PciId;

    //
    // Read PCI vendor/device ID at the device's bus/dev/func
    //
    if (DeviceIndex == 0) {
      PciId = MmPciRead32 (0, 0x02, 0, 0);
    } else if (DeviceIndex <= 2) {
      PciId = MmPciRead32 (0, 0x01, 0, 0);
    } else {
      PciId = MmPciRead32 (0, 0x1F, 2, 0);
    }

    if (PciId == 0xFFFFFFFF) {
      //
      // Device not present - mark as not present
      //
      DeviceStatus = TYPE10_DEVICE_NOT_PRESENT;
    } else {
      DeviceStatus = TYPE10_DEVICE_ENABLED;
    }
  }

  //
  // Write device descriptor: DeviceType | (DeviceStatus << 5)
  // SMBIOS Type 10 encodes: bits[4:0] = device type, bits[7:5] = status
  //
  WRITE_UINT8 (Buffer + 5 + DeviceIndex * 2, DeviceType | (DeviceStatus << 5));
  WRITE_UINT8 (Buffer + 6 + DeviceIndex * 2, DeviceStringNum);

  return EFI_SUCCESS;
}

/**
  Builds SMBIOS Type 41 (Onboard Devices Extended) structure.

  Type 41 is similar to Type 10 but includes PCI bus/device/function
  and a segment group number. This is the extended form of Type 10.

  @param[in,out]  Buffer       Pointer to the SMBIOS Type 41 structure buffer.
  @param[in]      DeviceIndex  Index of the device to update (0-based).

  @retval EFI_SUCCESS           Device entry updated.
  @retval EFI_INVALID_PARAMETER DeviceIndex is out of range.
**/
EFI_STATUS
UpdateSmbiosType41Devices (
  IN OUT UINT8        *Buffer,
  IN     UINTN        DeviceIndex
  )
{
  UINT8   ReferencedType;
  UINT8   DeviceType;
  UINT8   DeviceStatus;
  UINT8   DeviceFuncNum;
  UINT8   BusNum;
  UINT16  SegmentNum;
  UINT8   DeviceStringNum;

  //
  // SMBIOS Type 41:
  //   [0] Type = 41
  //   [1] Length = 11 (fixed)
  //   [2-3] Handle
  //   [4] Reference Designation String Number
  //   [5-6] Device Type + Status
  //       bits[7:0] = device type
  //   [7] Device Type Instance
  //   [8] Segment Group Number (low byte)
  //   [9] Bus Number
  //   [10] Device/Function Number
  //

  WRITE_UINT8 (Buffer, 41);    // Type
  WRITE_UINT8 (Buffer + 1, 11); // Length (fixed)
  WRITE_UINT16 (Buffer + 2, -2); // Handle placeholder

  //
  // Set default status = enabled
  //
  DeviceStatus = TYPE41_DEVICE_ENABLED;

  switch (DeviceIndex) {
  case 0:
    //
    // Embedded NIC 1 (I350 port 0):
    // Bus 0, Dev 0x01, Func 0 - via MM PCI read at bus=0, dev=1, func=2
    //
    ReferencedType = TYPE41_DEVICE_TYPE_NIC_1;
    DeviceType = TYPE41_DEVICE_TYPE_ETHERNET;
    DeviceFuncNum = 0;
    BusNum = 0;
    SegmentNum = 0;
    DeviceStringNum = 0;

    //
    // Check if the I350 exists
    //
    {
      UINT32  PciId;

      PciId = MmPciRead32 (0, 3, 2, 0);
      if (PciId == 0xFFFFFFFF) {
        DeviceStatus = TYPE41_DEVICE_DISABLED;
        ReferencedType = TYPE41_DEVICE_TYPE_OTHER;
      }
    }

    //
    // Encode: if device is enabled/enabled-presence, set bit 7
    //
    if (DeviceStatus == TYPE41_DEVICE_ENABLED) {
      //
      // I350 found - encode status as "enabled" with bit 7 set for
      // the reference designation type instance encoding used by this SMBIOS
      //
      DeviceStringNum = 0;  // No string, use instance encoding
    }
    break;

  case 1:
    //
    // Embedded NIC 2 (I350 port 1)
    //
    ReferencedType = TYPE41_DEVICE_TYPE_NIC_2;
    DeviceType = TYPE41_DEVICE_TYPE_ETHERNET;
    DeviceFuncNum = 0;
    BusNum = 0;
    SegmentNum = 0;

    {
      UINT32  PciId;

      PciId = MmPciRead32 (0, 2, 2, 0);
      if (PciId == 0xFFFFFFFF) {
        DeviceStatus = TYPE41_DEVICE_DISABLED;
        ReferencedType = TYPE41_DEVICE_TYPE_OTHER;
      }
    }
    break;

  case 2:
    //
    // SATA controller (PCH)
    //
    ReferencedType = TYPE41_DEVICE_TYPE_SATA_C;
    DeviceType = TYPE41_DEVICE_TYPE_SATA;
    DeviceFuncNum = 0;
    BusNum = 0;
    SegmentNum = 0;

    {
      UINT32  PciId;

      PciId = MmPciRead32 (0, 31, 2, 0);
      if (PciId == 0xFFFFFFFF) {
        DeviceStatus = TYPE41_DEVICE_DISABLED;
      }
    }
    break;

  case 3:
    //
    // Additional on-board storage controller
    //
    ReferencedType = TYPE41_DEVICE_TYPE_UNKNOWN;
    DeviceType = TYPE41_DEVICE_TYPE_SAS;
    DeviceFuncNum = 0;
    BusNum = 5;
    SegmentNum = 0;

    {
      UINT32  PciId;

      PciId = MmPciRead32 (0, 31, 2, 0);
      if (PciId == 0xFFFFFFFF) {
        DeviceStatus = TYPE41_DEVICE_NOT_PRESENT;
      }
    }
    break;

  default:
    return EFI_INVALID_PARAMETER;
  }

  //
  // Write the Type 41 entry fields
  //
  WRITE_UINT8 (Buffer + 4, 0);  // Reference designation string (0 = no string)
  WRITE_UINT8 (Buffer + 5, DeviceType);
  WRITE_UINT8 (Buffer + 6, DeviceType); // Device type encoding

  //
  // Device Type Instance (offset 7):
  //   If device is present, encodes as: Type | 0x80
  //   If not present, encodes as: Type
  //
  if (DeviceStatus == TYPE41_DEVICE_ENABLED) {
    WRITE_UINT8 (Buffer + 7, ReferencedType | 0x80);
  } else {
    WRITE_UINT8 (Buffer + 7, ReferencedType);
  }

  //
  // Segment, Bus, Device/Function
  //
  WRITE_UINT8 (Buffer + 8, (UINT8)SegmentNum);
  WRITE_UINT8 (Buffer + 9, BusNum);
  WRITE_UINT8 (Buffer + 10, DeviceFuncNum);

  return EFI_SUCCESS;
}

/**
  Main SMBIOS data update dispatcher function.

  Called by the UBA SMBIOS data update protocol. This function iterates all
  SMBIOS Type 9, Type 10, and Type 41 slots/devices, reads the hardware
  configuration via MM PCI registers, and updates each SMBIOS structure.

  The function allocates a temporary buffer (768 bytes) which is reused
  for each SMBIOS type/structure update.

  @retval EFI_SUCCESS  All SMBIOS updates completed successfully.
  @retval Others       Error updating SMBIOS structures.
**/
EFI_STATUS
EFIAPI
SmbiosDataUpdate (
  VOID
  )
{
  EFI_STATUS  Status;
  UINT8       *Buffer;
  UINTN       Index;

  //
  // Allocate a temporary buffer (768 bytes) for constructing SMBIOS structures
  //
  Buffer = AllocatePool (768);
  if (Buffer == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  //
  // ---- Update SMBIOS Type 9 (System Slots) ----
  // There are 30 (0x1E) system slots on this platform.
  //
  for (Index = 0; Index < SMBIOS_TYPE9_SLOT_COUNT; Index++) {
    //
    // Clear the temporary buffer
    //
    InternalZeroMem (Buffer, 768);

    //
    // Build the Type 9 SMBIOS structure for this slot
    //
    Status = UpdateSmbiosType9Slots (Buffer, Index);
    if (!EFI_ERROR (Status)) {
      //
      // Submit the updated SMBIOS structure to the system SMBIOS table
      //
      Status = SubmitSmbiosStructure (Buffer);
    }
  }

  //
  // ---- Remove old Type 9 structures (so new ones take effect) ----
  // This ensures stale Type 9 entries are cleared before we add the new ones.
  //
  RemoveSmbiosStructuresByType (9);

  //
  // ---- Update SMBIOS Type 10 (On-Board Devices) ----
  // There are up to 8 on-board devices.
  //
  for (Index = 0; Index < SMBIOS_TYPE10_DEVICE_COUNT; Index++) {
    InternalZeroMem (Buffer, 768);

    Status = UpdateSmbiosType10Devices (Buffer, Index);
    if (!EFI_ERROR (Status)) {
      Status = SubmitSmbiosStructure (Buffer);
    }
  }

  //
  // Remove old Type 10 structures
  //
  RemoveSmbiosStructuresByType (10);

  //
  // ---- Update SMBIOS Type 41 (Onboard Devices Extended) ----
  // There are up to 4 Type 41 device entries.
  //
  for (Index = 0; Index < SMBIOS_TYPE41_DEVICE_COUNT; Index++) {
    InternalZeroMem (Buffer, 768);

    Status = UpdateSmbiosType41Devices (Buffer, Index);
    if (!EFI_ERROR (Status)) {
      Status = SubmitSmbiosStructure (Buffer);
    }
  }

  RemoveSmbiosStructuresByType (41);

  //
  // Free the temporary buffer
  //
  FreePool (Buffer);

  return EFI_SUCCESS;
}

//
// ----- DXE Driver Entry Point Functions -----
//

/**
  Initializes global UEFI protocol pointers (SystemTable, BootServices,
  RuntimeServices, ImageHandle), locates HII database and string protocols,
  DXE services table, SMBIOS string pack protocol, and MM PCI base protocol.

  This is the standard DXE driver initialization sequence.

  @param[in]  ImageHandle  The firmware allocated handle for the EFI image.
  @param[in]  SystemTable  A pointer to the EFI System Table.

  @return EFI_STATUS.
**/
EFI_STATUS
EFIAPI
DriverInitInternal (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS  Status;

  //
  // Save the ImageHandle and SystemTable globally
  //
  gImageHandle    = ImageHandle;
  gSystemTable    = SystemTable;
  gBootServices   = SystemTable->BootServices;
  gRuntimeServices = SystemTable->RuntimeServices;

  //
  // Validate global pointers
  //
  ASSERT (gImageHandle != NULL);
  ASSERT (gSystemTable  != NULL);
  ASSERT (gBootServices  != NULL);
  ASSERT (gRuntimeServices != NULL);

  //
  // Locate the HII Database protocol
  //
  Status = gBootServices->LocateProtocol (
                            &gEfiHiiDatabaseProtocolGuid,
                            NULL,
                            (VOID **)&gHiiDatabase
                            );
  ASSERT_EFI_ERROR (Status);

  //
  // Locate the HII String protocol
  //
  Status = gBootServices->LocateProtocol (
                            &gEfiHiiStringProtocolGuid,
                            NULL,
                            (VOID **)&gHiiString
                            );
  ASSERT_EFI_ERROR (Status);

  //
  // Locate the HII Package List protocol
  //
  Status = gBootServices->LocateProtocol (
                            &gEfiHiiPackageListProtocolGuid,
                            NULL,
                            (VOID **)&gHiiPackageList
                            );

  //
  // Locate the DXE Services Table
  //
  Status = gBootServices->LocateProtocol (
                            &gEfiDxeServicesTableGuid,
                            NULL,
                            (VOID **)&gDxeServices
                            );
  ASSERT_EFI_ERROR (Status);

  //
  // Locate the MM PCI Base protocol (for register reads)
  //
  if (gMmPciBase == NULL) {
    Status = gBootServices->LocateProtocol (
                              &gEfiMmPciBaseProtocolGuid,
                              NULL,
                              (VOID **)&gMmPciBase
                              );
    ASSERT_EFI_ERROR (Status);
  }

  return EFI_SUCCESS;
}

/**
  Registers the SMBIOS string pack and the protocol callback.

  This function opens the UBA SMBIOS string pack HII package list,
  installs the string pack, records the HII handle, and registers the
  SMBIOS update notification callback.

  @param[in]  ImageHandle  The firmware allocated handle for the EFI image.
  @param[in]  SystemTable  A pointer to the EFI System Table.

  @return EFI_STATUS.
**/
EFI_STATUS
EFIAPI
SmbiosDataUpdateEntry (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS  Status;

  //
  // Log entry point
  //
  DEBUG ((EFI_D_INFO, "UBA:SmbiosDataUpdateEntry Image GUID=%g\n", &gUbaPlatformDataGuid));

  //
  // Initialize global pointers
  //
  DriverInitInternal (ImageHandle, SystemTable);

  //
  // Initialize HOB list
  //
  HobLibInitialize ();

  //
  // Locate the UBA SMBIOS string pack protocol
  //
  Status = gBootServices->LocateProtocol (
                            &gUbaSmbiosStringPackProtocolGuid,
                            NULL,
                            (VOID **)&gSmbiosStringPackConfigPort
                            );

  if (!EFI_ERROR (Status)) {
    //
    // Register the SMBIOS string pack
    //
    Status = RegisterSmbiosStringPack (
               &gUbaSmbiosStringPackProtocolGuid,
               &gImageHandle,
               &gSmbiosStringPackHandle,
               0
               );

    if (Status != EFI_SUCCESS || gSmbiosStringPackHandle == NULL) {
      DEBUG ((EFI_D_ERROR, "SmbiosStringPack registration failed\n"));
      ASSERT (gSmbiosStringPackHandle != NULL);
    }
  }

  //
  // Register the actual SMBIOS update callback via UBA protocol
  //
  {
    UINT32  ProtocolData[4];

    InternalZeroMem (ProtocolData, sizeof (ProtocolData));

    //
    // Signature: 'SMBS' (0x53424D53)
    //
    ProtocolData[0] = 0x53424D53;
    ProtocolData[1] = 1;       // Revision
    ProtocolData[2] = 11;      // Callback Function Type (Type 9/10/41 update)
    ProtocolData[3] = 0;

    //
     // Look up the UBA SMBIOS update protocol
    //
    if (gUbaSmbiosUpdateProtocol == NULL) {
      Status = gBootServices->LocateProtocol (
                                &gUbaSmbiosUpdateProtocolGuid,
                                NULL,
                                &gUbaSmbiosUpdateProtocol
                                );
      if (EFI_ERROR (Status)) {
        return Status;
      }
    }

    //
    // Register our update callback via the protocol's RegisterNotify function
    // (offset 16 in the protocol vtable)
    //
    Status = ((EFI_STATUS (EFIAPI *)(VOID *, VOID *, UINT32 *, UINTN))
                (*(UINTN **)gUbaSmbiosUpdateProtocol)[2]) (
               gUbaSmbiosUpdateProtocol,
               &gUbaSmbiosUpdateProtocolGuid,
               ProtocolData,
               24
               );
  }

  return Status;
}