/** @file
  StaticSkuDataDxeCLX64L - CLX64L Platform SKU Configuration Driver.

  This DXE driver provides static SKU configuration data for the CLX64L
  (Cooper Lake 64-Lane) platform. It installs a protocol interface that
  exposes platform SKU identification and configuration tables.

  The driver operates as follows:
  1. Initializes UEFI boot service and runtime service table pointers
     (gImageHandle, gST, gBS, gRT).
  2. Retrieves the HOB (Hand-Off Block) list from the System Table.
  3. Installs the SKU Data Protocol into the UEFI protocol database.
  4. Registers protocol notify for SKU data consumers.

  Copyright (C) 2022-2026. All Rights Reserved.

  SPDX-License-Identifier: BSD-2-Clause-Patent
**/

#include "StaticSkuDataDxeCLX64L.h"

//
// ---------------------------------------------------------------------------
// Global Variables
// ---------------------------------------------------------------------------

//
// UEFI handle for this driver image (set by ModuleEntryPoint)
//
EFI_HANDLE  gImageHandle = NULL;

//
// Pointer to the UEFI System Table (set by ModuleEntryPoint)
//
EFI_SYSTEM_TABLE  *gSystemTable = NULL;

//
// Pointer to the UEFI Boot Services Table (set by ModuleEntryPoint)
//
EFI_BOOT_SERVICES  *gBootServices = NULL;

//
// Pointer to the UEFI Runtime Services Table (set by ModuleEntryPoint)
//
EFI_RUNTIME_SERVICES  *gRuntimeServices = NULL;

//
// Protocol notification handle for the SKU Data Protocol
//
VOID  *mSkuDataProtocolNotifyHandle = NULL;

//
// HOB (Hand-Off Block) list pointer retrieved from SystemTable
//
VOID  *mHobList = NULL;

//
// ---------------------------------------------------------------------------
// ACPI Method Name Strings for CCT (CPU C-State Control Table) Entries
// ---------------------------------------------------------------------------
// These ACPI method names are used by platform AML code to query per-core
// C-state configuration for each of the 9 entries (CCT0-CCT8).
// The CLX64L platform provides individual C-state control tables for
// each logical processor grouping.
//

CONST CHAR8  *mCctMethodNames[MAX_CCT_ENTRIES] = {
  "_SB_.CCT0",  // CPU C-State Control Table Entry 0
  "_SB_.CCT1",  // CPU C-State Control Table Entry 1
  "_SB_.CCT2",  // CPU C-State Control Table Entry 2
  "_SB_.CCT3",  // CPU C-State Control Table Entry 3
  "_SB_.CCT4",  // CPU C-State Control Table Entry 4
  "_SB_.CCT5",  // CPU C-State Control Table Entry 5
  "_SB_.CCT6",  // CPU C-State Control Table Entry 6
  "_SB_.CCT7",  // CPU C-State Control Table Entry 7
  "_SB_.CCT8"   // CPU C-State Control Table Entry 8
};

//
// ACPI method name for CPU Fan Health monitoring
//
CONST CHAR8  *mCfh0MethodName = "_SB_.CFH0";

//
// ---------------------------------------------------------------------------
// Static SKU Configuration Data Tables
// ---------------------------------------------------------------------------
// The following data occupies the .data section from 0x4060 through ~0xF560
// (~46KB). This includes:
//   - Protocol GUID instance structures
//   - SKU-specific configuration tables
//   - DMI/SMBIOS data for platform identification
//   - CPU and memory topology configuration
//
// The exact structure of these tables is generated by the Intel/AMI
// SKU generation tools and is platform-specific to CLX64L.
//
// NOTE: The large static data tables at offset 0x4060-0xF500 contain the
//       full SKU configuration payload. This includes per-SKU entries for
//       power management, memory map, PCIe lane configuration, and
//       thermal management settings. The data layout follows the Intel
//       Firmware Support Package (FSP) SKU data format.
//
// ---------------------------------------------------------------------------

//
// ---------------------------------------------------------------------------
// Function Implementations
// ---------------------------------------------------------------------------

/**
  Locates the SKU Data Protocol by GUID and returns its handle.

  This function allocates a small pool buffer (required by the UEFI
  LocateProtocol API on some firmware implementations), then searches
  for the SKU Data Protocol GUID. If the protocol is not yet installed,
  it returns NULL.

  @return Pointer to the protocol interface, or NULL on failure.
**/
VOID *
LocateSkuDataProtocol (
  VOID
  )
{
  EFI_STATUS  Status;
  VOID       *ProtocolHandle;
  VOID       *PoolBuffer;

  //
  // Allocate a small pool buffer (31 bytes) to satisfy LocateProtocol
  // requirements on some firmware implementations. Free it immediately
  // after the call since we only need the protocol interface pointer.
  //
  PoolBuffer = gBootServices->AllocatePool (31);
  gBootServices->FreePool (PoolBuffer);

  //
  // Verify that the allocated pool pointer is valid (at least 16 bytes
  // of addressable space). If the allocation was too small or the system
  // is in a constrained state, bail out.
  //
  if ((UINTN)PoolBuffer <= 0x10) {
    return NULL;
  }

  //
  // Locate the SKU Data Protocol interface
  //
  Status = gBootServices->LocateProtocol (
                            &gSkuDataProtocolGuid,
                            NULL,
                            &ProtocolHandle
                            );
  if (EFI_ERROR (Status)) {
    ProtocolHandle = NULL;
  }

  return ProtocolHandle;
}

/**
  Reads the SKU type from CMOS/NVRAM register 0x4B.

  This function accesses the platform CMOS/NVRAM via I/O ports 0x70/0x71
  to read the SKU identification register. It preserves the NMI (Non-Maskable
  Interrupt) enable bit (bit 7) in register 0x70.

  The SKU classification logic:
  - If CCT version > 3: returns the CMOS value directly
  - If CCT version == 0: reads hardware strap from fixed address 0xFDAF0490,
    extracts bit 1, ORs with 1, subtracts 1 (yields 0, 1, or 2)
  - If CCT version == 1: returns 0x80000004 (SKU type 4, error path)
  - If CCT version == 2: returns 0x80000006 (SKU type 2, specific platform)
  - Default (CCT version >= 3): returns SKU type 4

  @param[in] CpuCStateVersion  The CCT (CPU C-State Control Table) version
                                read from CMOS register 0x4B.

  @return The SKU type identifier.
          Returns 4 (SKU_TYPE_4) for unknown/default configurations.
**/
UINT8
GetSkuTypeFromCmos (
  UINT64  CpuCStateVersion
  )
{
  UINT8   CmosValue;
  UINT8   SkuType;
  UINT64  PlatformFlags;

  //
  // Initialize the SKU type to unknown
  //
  SkuType = SKU_TYPE_UNKNOWN;

  LocateSkuDataProtocol ();

  //
  // Read CMOS register 0x4B while preserving NMI enable bit
  //
  CmosValue = IoRead8 (0x70);
  IoWrite8 (0x70, CmosValue & CMOS_SKU_REGISTER_MASK | CMOS_SKU_REGISTER);
  CmosValue = IoRead8 (0x71);

  //
  // CCT (CPU C-State Control Table) version interpretation
  //
  if (CmosValue > 3) {
    //
    // Valid CCT version > 3: use the CMOS register value directly
    //
    SkuType = CmosValue;
  } else if (CmosValue == 0) {
    //
    // CCT version 0 (uninitialized): determine SKU from hardware strap
    // Read platform strap at fixed address 0xFDAF0490 (PCH/CPU register),
    // extract bit 1 to determine SKU variant.
    //
    PlatformFlags  = *(UINT64 *)0xFDAF0490;
    SkuType        = (UINT8)((PlatformFlags & 2) | 1);
  }

  //
  // Map the raw CCT value to the platform SKU type:
  //   CCT == 1 -> SKU type 2 (0x80000004 >> 24)
  //   CCT == 2 -> SKU type 2 (0x80000006 >> 24, same effective type)
  //   other    -> SKU type 4 (default)
  //
  if ((UINT8)(SkuType - 1) <= 0xFD) {
    if (CpuCStateVersion == 1) {
      SkuType = SKU_TYPE_4;
    } else {
      SkuType = SKU_TYPE_2;
    }
  }

  if (CpuCStateVersion == 1) {
    SkuType = SKU_TYPE_4;
  } else {
    SkuType = SKU_TYPE_2;
  }

  return SkuType;
}

/**
  Debug print routine.

  Sends a formatted debug message through the UEFI debug protocol.
  Uses the protocol interface obtained via LocateSkuDataProtocol().

  @param[in] FileName     The source file name string.
  @param[in] LineNumber   The line number in the source file.
  @param[in] Message      The debug message string.
**/
VOID
DebugPrint (
  IN CONST CHAR8  *FileName,
  IN UINTN         LineNumber,
  IN CONST CHAR8  *Message
  )
{
  EFI_STATUS                    Status;
  VOID                         *DebugProtocol;
  EFI_DEBUG_PROTOCOL           *Debug;

  //
  // Locate the debug protocol
  //
  DebugProtocol = LocateSkuDataProtocol ();
  if (DebugProtocol != NULL) {
    //
    // Use the debug protocol's output function (offset +8 from interface)
    //
    Debug = (EFI_DEBUG_PROTOCOL *)DebugProtocol;
    Debug->DebugPrint (FileName, LineNumber, Message);
  }
}

/**
  Retrieves the HOB list from the UEFI System Table.

  This function walks the HOB (Hand-Off Block) list stored in the
  UEFI System Table to find a specific HOB matching the given GUID.
  The HOB list is a linked list of data structures passed from the
  PEI phase to the DXE phase.

  @param[in] HobGuid  Pointer to the GUID to search for.

  @return Pointer to the HOB if found, NULL otherwise.
**/
VOID *
GetHobList (
  IN EFI_GUID  *HobGuid
  )
{
  EFI_PEI_HOB_POINTERS  Hob;
  VOID                  *HobStart;
  UINTN                 HobCount;
  UINTN                 Index;

  //
  // On first call, locate the HOB list from the System Table
  //
  if (mHobList == NULL) {
    //
    // The HOB list pointer is stored at offset +104 from the System Table
    // (HobList field in EFI_SYSTEM_TABLE). The entries are at offset +112.
    //
    mHobList = NULL;

    if (gSystemTable->HobList != NULL) {
      HobStart = gSystemTable->HobList;
      HobCount = *(UINTN *)((UINT8 *)gSystemTable + 104);

      //
      // Walk the HOB list to find a HOB whose GUID matches
      //
      for (Index = 0; Index < HobCount; Index++) {
        Hob.Raw = (UINT8 *)HobStart + (Index * sizeof (EFI_HOB_GUID_TYPE));

        if (CompareGuidField (
              &Hob.Guid->Name,
              HobGuid
              ))
        {
          //
          // Found the matching HOB, store its GUID-specific field (+16)
          //
          mHobList = *(VOID **)((UINT8 *)Hob.Raw + 16);
          break;
        }
      }

      //
      // If no matching HOB found, assert and leave mHobList as NULL
      //
      if (mHobList == NULL) {
        DEBUG_ASSERT (
          !EFI_ERROR (EFI_NOT_FOUND),
          "!EFI_ERROR (Status)"
          );
        DEBUG_ASSERT (
          mHobList != NULL,
          "mHobList != ((void *) 0)"
          );
      }
    }
  }

  return mHobList;
}

/**
  Compares two 8-byte aligned values for equality.

  This function compares two 64-bit values extracted from HOB GUID
  entries. It is used to match GUID fields during HOB list traversal.

  @param[in] Buffer1  Pointer to the first value.
  @param[in] Buffer2  Pointer to the second value.

  @retval TRUE   The two values are equal.
  @retval FALSE  The two values are not equal.
**/
BOOLEAN
CompareGuidField (
  IN CONST VOID  *Buffer1,
  IN CONST VOID  *Buffer2
  )
{
  UINT64  Value1;
  UINT64  Value2;

  Value1 = ReadUnaligned64 (Buffer1);
  Value2 = ReadUnaligned64 (Buffer2);

  //
  // Also compare the second qword (GUID.Data4 field range)
  //
  Value1 = ReadUnaligned64 ((UINT8 *)Buffer1 + 8);
  Value2 = ReadUnaligned64 ((UINT8 *)Buffer2 + 8);

  return (BOOLEAN)(Value1 == Value2);
}

/**
  Reads an unaligned 8-byte value from memory.

  This function reads a UINT64 value from a potentially unaligned
  memory address. It is used to safely access GUID fields within
  HOB entries that may not be aligned on an 8-byte boundary.

  @param[in] Buffer  Pointer to the buffer to read from.

  @return The UINT64 value read from the buffer.
**/
UINT64
ReadUnaligned64 (
  IN CONST VOID  *Buffer
  )
{
  //
  // Assert that the buffer pointer is valid
  //
  ASSERT (Buffer != NULL);

  //
  // Read the unaligned 64-bit value directly
  // The platform must support unaligned access (x64 does)
  //
  return *(UINT64 *)Buffer;
}

/**
  Entry point for StaticSkuDataDxeCLX64L.

  Initializes global table pointers from the UEFI System Table and
  Boot Services, then installs the SKU Data Protocol interface.

  The protocol registration sequence:
  1. Verify ImageHandle and SystemTable are valid (assert on NULL)
  2. Save gBS, gRT, gImageHandle, gST as globals
  3. Call GetHobList() to build the HOB list cache
  4. Install SKU Data Protocol via BootServices->InstallProtocolInterface()
  5. Register protocol notify via BootServices->RegisterProtocolNotify()

  @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_NOT_FOUND         Required protocol or HOB not found.
  @retval EFI_OUT_OF_RESOURCES  Out of resources.
**/
EFI_STATUS
EFIAPI
StaticSkuDataDxeEntryPoint (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE *SystemTable
  )
{
  EFI_STATUS  Status;
  VOID       *Registration;

  //
  // Store ImageHandle globally for later use
  //
  gImageHandle = ImageHandle;
  ASSERT (gImageHandle != NULL);

  //
  // Store SystemTable pointer globally
  //
  gSystemTable = SystemTable;
  ASSERT (gSystemTable != NULL);

  //
  // Store Boot Services Table pointer globally
  //
  gBootServices = SystemTable->BootServices;
  ASSERT (gBootServices != NULL);

  //
  // Store Runtime Services Table pointer globally
  //
  gRuntimeServices = SystemTable->RuntimeServices;
  ASSERT (gRuntimeServices != NULL);

  //
  // Initialize the HOB list cache for SKU data lookup
  //
  GetHobList (NULL);

  //
  // Install the SKU Data Protocol interface
  // The protocol instance data (gSkuDataProtocolInstance) contains the
  // static SKU configuration tables for the CLX64L platform.
  //
  Registration = NULL;
  Status = gBootServices->InstallProtocolInterface (
                            &gSkuDataProtocolNotifyHandle,
                            &gSkuDataProtocolGuid,
                            EFI_NATIVE_INTERFACE,
                            NULL
                            );
  if (!EFI_ERROR (Status)) {
    //
    // Register protocol notify to allow SKU data consumers to connect
    //
    Status = gBootServices->RegisterProtocolNotify (
                              &gSkuDataProtocolGuid,
                              &Registration,
                              gSkuDataProtocolNotifyHandle
                              );
    if (!EFI_ERROR (Status)) {
      //
      // Install the full SKU configuration interface
      //
      Status = gBootServices->InstallProtocolInterface (
                                &gSkuDataProtocolNotifyHandle,
                                &gSkuDataProtocolGuid,
                                EFI_NATIVE_INTERFACE,
                                &gSkuConfigurationData
                                );
    }
  }

  return Status;
}