/** @file
  StaticSkuDataDxeNeonCityEPECB - Static SKU Data for Neon City EPECB (Purley Platform)

  This UEFI DXE driver provides board-specific static SKU data tables for the
  Neon City EPECB UBA variant. It registers ACPI name-space paths for core count
  tables (CCT), core frequency headers (CFH), NVDIMM driver entries (NVDR), and
  PCIe slot FIX methods (FIX0-X, FIXV, FIXY, FIXW) via UBA protocol GUIDs.

  Driver entry point (ModuleEntryPoint):
    1. Initializes global UEFI table pointers (ImageHandle, SystemTable, etc.)
    2. Locates the HOB list via DxeHobLib
    3. Locates the UMPT, PIRQ, and FIX protocol interfaces
    4. Registers the ACPI path format table for downstream UBA dispatch code

  Build:
    VS2015, X64, DEBUG
    Source: PurleyRpPkg\Uba\UbaMain\Dxe\TypeNeonCityEPECB\StaticSkuDataDxe\

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

#include "StaticSkuDataDxeNeonCityEPECB.h"

//
// ---------------------------------------------------------------------------
// Global UEFI Table Pointers
// ---------------------------------------------------------------------------
//

///
/// The image handle of this driver (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;

///
/// Pointer to the HOB (Hand-Off Block) list, initialized via DxeHobLib.GetBootModeHobList().
///
VOID *mHobList = NULL;

//
// ---------------------------------------------------------------------------
// Protocol GUID definitions (stored in .data section at 0x4060-0x40E0)
// ---------------------------------------------------------------------------
//

///
/// OEM-defined GUID for UMPT protocol (untyped).
/// Bytes at 0x4060-0x407F (32 bytes, first 16 = GUID, next 8 = name, next 8 = reserved/ptr)
///
/// GUID: 36292936-0E76-31C8-A13A-3AF2FC1C3932
/// This is a UEFI configuration table GUID used to locate platform-specific data.
///
GUID mUbaOemGuid1 = { 0x36292936, 0x0E76, 0x31C8, { 0xA1, 0x3A, 0x3A, 0xF2, 0xFC, 0x1C, 0x39, 0x32 } };

///
/// OEM-defined GUID for the second protocol table.
/// Identifies the UMPT protocol handler GUID.
///
GUID mUbaOemGuid2 = { 0x460D3EE0, 0x6352, 0x4548, { 0xB0, 0xA4, 0x58, 0xD5, 0x7B, 0x31, 0x77, 0xE2 } };

///
/// UMPT (UEFI Memory Protocol Table) GUID.
/// { 0x7739F24C, 0x93D7, 0x11D4, { 0x9A, 0x3A, 0x00, 0x90, 0x27, 0x3F, 0xC1, 0x4D } }
///
/// This GUID identifies the UMPT protocol. The protocol provides memory topology
/// and configuration data used by the board-specific SKU dispatch code.
///
EFI_GUID gUmpProtocolGuid = UMPT_PROTOCOL_GUID;

///
/// Name string "UMPT" stored alongside the GUID in the protocol registration entry.
///
CHAR8 mUmpProtocolName[4] = "UMPT";

///
/// PIRQ (PCI Interrupt Routing) protocol GUID.
/// { 0xCFA1F80F, 0xABA0, 0xC04A, { 0xBF, 0xC9, 0x34, 0xA7, 0x8F, 0x68, 0xDD, 0x8A } }
///
/// This GUID identifies the PIRQ protocol, which provides PCI interrupt routing
/// configuration for the board.
///
EFI_GUID gPirqProtocolGuid = PIRQ_PROTOCOL_GUID;

///
/// Name string "PIRQ" stored alongside the GUID in the protocol registration entry.
///
CHAR8 mPirqProtocolName[4] = "PIRQ";

///
/// FIX protocol GUID.
/// { 0xA5481F4C, 0x76C9, 0x904D, { 0x9F, 0x03, 0x8E, 0x9B, 0x1C, 0x32, 0x7F, 0xCF } }
///
/// This GUID identifies the FIX protocol, which provides board-specific fixup
/// configuration methods (FIX0-X, FIXV, FIXY, FIXW).
///
EFI_GUID gFixProtocolGuid = FIX_PROTOCOL_GUID;

///
/// ACPF (ACPI Path Format) protocol GUID.
/// { 0xF89E1281, 0x1D39, 0x4F63, { 0xAE, 0x99, 0x58, 0x51, 0x7E, 0xC0, 0x77, 0xE3 } }
///
/// This GUID identifies the ACPF protocol, which exposes the ACPI path format
/// table to UBA dispatch code.
///
EFI_GUID gAcpfProtocolGuid = ACPF_PROTOCOL_GUID;

//
// ---------------------------------------------------------------------------
// NVDIMM ACPI Path Name Strings (stored in .rdata section)
// ---------------------------------------------------------------------------
//
// The .rdata section contains ACPI path strings organized as:
//   _SB_.CCT0..CCT8     - Core Count Tables (9 entries)
//   _SB_.CFH0..CFH8     - Core Frequency Headers (9 entries)
//   _SB_.NVDR.DRVT      - NVDIMM Driver root
//   _SB_.NVDR.Nnnn.FXxx - NVDIMM function methods (FXCD, FXST, FXIN, FXOU, FXBS, FXFH, CENA, CFIS)
//   _SB_.PCnn.FIXx       - PCIe Root Port FIX methods
//   _SB_.PCnn.MCPx.MCTL  - PCIe MCTL method
//   _SB_.PCnn.SRxx.MCTL  - PCIe SR MCTL method
//   _SB_.PCnn.RRxx.MCTL  - PCIe RR MCTL method
//

//
// CCT (Core Count Table) path strings: _SB_.CCT0 through _SB_.CCT8
// These map CPU core count configurations per socket.
//
// CFH (Core Frequency Header) path strings: _SB_.CFH0 through _SB_.CFH8
// These map CPU frequency configuration headers per socket.
//

//
// NVDIMM namespace: _SB_.NVDR
// NVDIMM sub-namespace devices (N000 through N151, organized by socket/channel)
//
// Each NVDIMM entry has ACPI methods:
//   FXCD - Function Code
//   FXST - Function Status
//   FXIN - Function Input
//   FXOU - Function Output
//   FXBS - Function Buffer Size
//   FXFH - Function File Handle
//   CENA - Command Enable
//   CFIS - Command Finish Status
//

//
// PCIe Root Port FIX methods (per port):
//   FIX1-FIX4 - Fixed method entries
//   FIX5-FIX7 - Variable method entries
//   MCTL      - Management Control
//
// FIX types:
//   FIX0  - Base FIX method (single entry)
//   FIXX  - FIX Extension (0..8 per device) with type=0x5B80
//   FIXV  - FIX Value (0..8 per device) with type=0x5B80
//   FIXY  - NVDIMM FIX Y (per sub-instance) with type=0x0008
//   FIXW  - NVDIMM FIX W (per sub-instance) with type=0x0008
//

//
// ---------------------------------------------------------------------------
// ACPI Path Entry Table (stored in .data section at 0x40E0+)
// ---------------------------------------------------------------------------
//
// This table is organized as arrays of 32-byte ACPI_PATH_ENTRY structures.
// The table is anchored by the ACPI_PATH_FORMAT_TABLE header at 0x9000.
//
// Entry sections:
//
//   1. PSYS-rooted entries (type 0x5B80):
//      - FIX0 base entry (index 0)
//      - FIXX entries (indices 0..8) for PCIe slot configuration
//      - FIXV entries (indices 0..8) for PCIe slot value configuration
//
//   2. NVDR-rooted entries (type 0x0008):
//      - NVDIMM sub-instance entries organized in groups of 8 per NVDR instance
//      - Each group contains: 6 data entries + 1 FIXY + 1 FIXW
//      - Groups correspond to: N000, N001, N010, N011, N020, N021, ..., N151
//      - NVDIMM instances span sockets (0-1), channels (0-5 per socket), slots (0-1)
//
// Total entries: ~200+ entries covering all board-configurable ACPI paths
//

//
// ACPI Path Format Table header at 0x9000
//
// Signature: "ACPF" (0x46504341)
// Version: 1
// EntryTable pointer: 0x40E0 (gAcpiPathEntries)
//
ACPI_PATH_FORMAT_TABLE mAcpiPathFormatTable = {
  .Signature    = 0x46504341,   ///< "ACPF"
  .Version      = 1,
  .EntryTable   = (ACPI_PATH_ENTRY *) 0x40E0,
};

//
// Protocol registration handles (initialized by entry point)
//
UINT64 gUmpProtocolHandle  = 0;
UINT64 gPirqProtocolHandle = 0;
UINT64 gFixProtocolHandle  = 0;

//
// ---------------------------------------------------------------------------
// Forward Function Declarations
// ---------------------------------------------------------------------------
//

/**
  Allocate and initialize the protocol handle cache.
  Uses BootServices->AllocatePool to allocate memory and then maps the
  UMPT protocol from the configuration table.
**/
UINT64
InitializeUmpProtocol (
  VOID
  );

/**
  Locate the HOB list from SystemTable and validate it.
  Uses HOB list pointer from DXE HOB Library.
**/
VOID *
GetBootModeHobList (
  VOID
  );

/**
  Compare two 8-byte values for equality (unaligned read).

  @param[in] Buffer  Pointer to the data to read.

  @return The 64-bit value read from the buffer.
**/
UINT64
ReadUnaligned64 (
  IN CONST VOID *Buffer
  );

/**
  Check if a HOB entry matches the expected GUID.

  @param[in]  Entry       Pointer to the HOB entry.
  @param[in]  Expected    Pointer to the expected GUID structure.

  @retval TRUE   The HOB entry matches.
  @retval FALSE  The HOB entry does not match.
**/
BOOLEAN
IsHobMatchingGuid (
  IN VOID   *Entry,
  IN VOID   *Expected
  );

/**
  Output ASSERT message via protocol.

  @param[in]  FileName    Source file name string.
  @param[in]  LineNumber  Line number of the assertion.
  @param[in]  Description Assertion description string.
**/
VOID
AssertPrint (
  IN CONST CHAR8 *FileName,
  IN UINTN       LineNumber,
  IN CONST CHAR8 *Description
  );

/**
  Check an EFI status and assert on error.

  @param[in]  Status  The EFI_STATUS value to check.
**/
VOID
CheckEfiStatus (
  IN EFI_STATUS Status
  );

/**
  Read CMOS index register 0x70 and return a board-type dependent value.

  @param[in]  InputValue  Input parameter (unused in current implementation).

  @return Board-type specific value derived from CMOS index 0x4B.
**/
UINT8
GetBoardTypeFromCmos (
  IN UINT64 InputValue
  );

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

/**
  Allocate and initialize the UMP protocol handle cache.

  Allocates a pool buffer, checks system memory size (must be <= 16),
  then locates the UMPT configuration table entry and caches the protocol handle.

  @return The protocol handle pointer, or NULL on failure.
**/
UINT64
InitializeUmpProtocol (
  VOID
  )
{
  UINT64                Handle;
  UINT64                PoolSize;
  UINT64                Result;
  UINT64                CachedHandle;

  Handle = gUmpProtocolHandle;
  if (Handle != 0) {
    return Handle;
  }

  //
  // Allocate a pool buffer using BootServices->AllocatePool (index 24 = 3 entries).
  //
  PoolSize = gBootServices->AllocatePool (31);
  gBootServices->FreePool (PoolSize);

  if (PoolSize <= 0x10) {
    //
    // Locate the UMPT protocol configuration table entry.
    //
    Result = gBootServices->LocateProtocol (&gUmpOemGuid2, NULL, &CachedHandle);
    if (Result < 0) {
      CachedHandle = 0;
    }
    gUmpProtocolHandle = CachedHandle;
    return CachedHandle;
  }

  return 0;
}

/**
  Locate the HOB list from the UEFI System Table via the DXE HOB Library.

  Searches the HOB list for an entry matching the expected GUID and caches it.
  If no matching HOB is found, triggers an ASSERT.

  @param[in]  A1  Parameter (currently unused, retained for compatibility).

  @return The cached HOB list pointer.
**/
VOID *
GetBootModeHobList (
  VOID
  )
{
  VOID   *HobList;
  UINT64  EntryCount;
  UINT64  EntryBase;
  UINT64  CurrentEntry;

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

  //
  // Initialize search state.
  //
  EntryCount = 0;
  mHobList   = NULL;

  //
  // Check if the SystemTable has a HOB list pointer at offset 104.
  // (Offset 104 in EFI_SYSTEM_TABLE is the HOB list pointer in PI-spec DXE phase.)
  //
  if (*(UINT64 *)((UINT8 *)gSystemTable + 104) != 0) {
    EntryBase = *(UINT64 *)((UINT8 *)gSystemTable + 112);

    //
    // Linear search through the HOB list.
    //
    for (CurrentEntry = EntryBase; ; CurrentEntry += 24) {
      if (!IsHobMatchingGuid ((VOID *)CurrentEntry, (VOID *)CurrentEntry)) {
        break;
      }
      EntryCount++;
      if (EntryCount >= *(UINT64 *)((UINT8 *)gSystemTable + 104)) {
        goto FoundEntries;
      }
    }

    //
    // Found matching entry: extract the HOB pointer at offset +16.
    //
    HobList = *(VOID **)(CurrentEntry + 16);
    mHobList = HobList;
    return HobList;

FoundEntries:
    //
    // No matching HOB entry found; trigger ASSERT.
    //
    CheckEfiStatus ((EFI_STATUS)(UINTN)0x800000000000000EULL);
    return mHobList;
  }

  //
  // SystemTable has no HOB list; trigger ASSERT.
  //
  CheckEfiStatus ((EFI_STATUS)(UINTN)0x800000000000000EULL);
  return mHobList;
}

/**
  Read an unaligned 64-bit value from the specified buffer.

  @param[in]  Buffer  Pointer to the data to read (need not be aligned).

  @return The 64-bit value at the buffer address.
**/
UINT64
ReadUnaligned64 (
  IN CONST VOID *Buffer
  )
{
  if (Buffer == NULL) {
    AssertPrint (
      "e:\\hs\\MdePkg\\Library\\BaseLib\\Unaligned.c",
      192,
      "Buffer != ((void *) 0)"
      );
  }
  return *(const UINT64 *)Buffer;
}

/**
  Compare two GUID entries in the HOB list by matching their first and second
  64-bit fields.

  @param[in]  Entry     Pointer to the HOB entry GUID.
  @param[in]  Expected  Pointer to the expected GUID structure.

  @retval TRUE   The two GUIDs match.
  @retval FALSE  The two GUIDs do not match (or NULL parameters).
**/
BOOLEAN
IsHobMatchingGuid (
  IN VOID   *Entry,
  IN VOID   *Expected
  )
{
  UINT64 GuidFirst;
  UINT64 GuidSecond;
  UINT64 ExpectedFirst;
  UINT64 ExpectedSecond;

  //
  // Read both GUIDs via unaligned 64-bit reads.
  //
  GuidFirst     = ReadUnaligned64 (Entry);
  GuidSecond    = ReadUnaligned64 ((UINT8 *)Entry + 8);
  ExpectedFirst = ReadUnaligned64 (Expected);
  ExpectedSecond= ReadUnaligned64 ((UINT8 *)Expected + 8);

  return (BOOLEAN)(GuidFirst == ExpectedFirst && GuidSecond == ExpectedSecond);
}

/**
  Output an assertion message using the registered debug/report protocol.

  If the assertion protocol is available, it formats and dispatches the
  assertion string. Otherwise, falls back to a simple ASSERT output.

  @param[in]  FileName     Source file name string.
  @param[in]  LineNumber   Line number of the assertion.
  @param[in]  Description  Assertion description string.
**/
VOID
AssertPrint (
  IN CONST CHAR8 *FileName,
  IN UINTN       LineNumber,
  IN CONST CHAR8 *Description
  )
{
  UINT64 ProtocolHandle;

  ProtocolHandle = InitializeUmpProtocol ();
  if (ProtocolHandle != 0) {
    //
    // Dispatch assertion via the protocol's report function (at offset +8 in vtable).
    //
    ((VOID (*)(CONST CHAR8 *, UINTN, CONST CHAR8 *)) \
      (*(UINT64 **)ProtocolHandle)[1]) (FileName, LineNumber, Description);
  }
}

/**
  Check an EFI status value and assert on error.

  If the status has the high error bit set, triggers a debug assertion
  with the status code formatted as %r.

  @param[in]  Status  The EFI_STATUS value to check.
**/
VOID
CheckEfiStatus (
  IN EFI_STATUS Status
  )
{
  //
  // Check if this is a HOB list pointer validation by examining
  // the SystemTable HOB count entry.
  //
  GetBootModeHobList ((UINT64)Status);
}

/**
  Read the CMOS configuration register and return a board-type dependent value
  used for SKU data dispatch.

  Reads CMOS index 0x4B (preserving bit 7 of index 0x70), interprets the value,
  and maps it to a protocol call type.

  @param[in]  InputValue  Input parameter (used as a bitmask for protocol dispatch).

  @return Board-type dependent value, or 4 if the CMOS value is out of expected range.
**/
UINT8
GetBoardTypeFromCmos (
  IN UINT64 InputValue
  )
{
  UINT64                ProtocolHandle;
  UINT64                DispatchType;
  UINT64                (*ProtocolFunc)(UINT64, UINT64, UINT64 *);
  UINT8                 CmosValue;
  UINT8                 RawCmosResult;
  UINT8                 Result;

  //
  // Get the UMP protocol handle for dispatch.
  //
  ProtocolHandle = InitializeUmpProtocol ();
  DispatchType   = 0;
  ProtocolFunc   = (VOID *)ProtocolHandle;

  if (ProtocolHandle != 0) {
    //
    // Read CMOS index register 0x70, set index to 0x4B (preserving NMI enable bit).
    //
    CmosValue = IoRead8 (0x70);
    IoWrite8 (0x70, CmosValue & 0x80 | 0x4B);

    //
    // Read the CMOS data register 0x71.
    //
    RawCmosResult = IoRead8 (0x71);
    Result = RawCmosResult;

    //
    // If the value is > 3, it may indicate a special configuration.
    // However, the inner check for value == 0 is unreachable in this path
    // (0 <= 3, so it never enters this block). This is compiler-intrinsic
    // behavior preserved from the original binary.
    //
    if (RawCmosResult > 3) {
      if (RawCmosResult == 0) {
        Result = (*(volatile UINT8 *)0xFDAF0490 & 2) | 1;
      }
    }

    //
    // Map the CMOS result to a dispatch type.
    //   Raw CMOS value >= 1 and <= 254: valid range (n3_1 - 1 <= 0xFD).
    //     Value 1 -> DispatchType = 0x8000000C (NVDIMM path data dispatch)
    //     Value 2..254 -> DispatchType = 0x80000006 (FIX/PCIe path data dispatch)
    //   Raw CMOS value 0 or 255: out of range, no dispatch performed,
    //     returns default value of (RawCmosResult - 1).
    //
    if ((UINT8)(RawCmosResult - 1) <= 0xFD) {
      Result = 4;
      DispatchType = 0x80000006LL;
      if (RawCmosResult == 1) {
        DispatchType = 0x8000000CLL;
      }
    } else {
      Result = RawCmosResult - 1;
    }

    //
    // If the dispatch type matches the input mask, call the protocol function.
    //
    if ((DispatchType & (UINT64)InputValue) != 0) {
      Result = ProtocolFunc ((UINT64)InputValue, (UINT64)Result, (UINT64 *)&Result);
    }
  }

  return Result;
}

/**
  Driver entry point.

  Initializes UEFI global table pointers, locates the HOB list,
  then registers the ACPI path format table, UMPT, PIRQ, and FIX
  protocols via the UEFI boot services configuration table mechanism.

  @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         A required protocol or configuration table was not found.
  @retval EFI_OUT_OF_RESOURCES  Memory allocation failed.
**/
EFI_STATUS
EFIAPI
ModuleEntryPoint (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS  Status;
  UINT64      ProtocolHandle;

  //
  // Save the global UEFI table pointers.
  //
  gImageHandle = ImageHandle;
  if (ImageHandle == NULL) {
    AssertPrint (
      "e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
      51,
      "gImageHandle != ((void *) 0)"
      );
  }

  gSystemTable = SystemTable;
  if (SystemTable == NULL) {
    AssertPrint (
      "e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
      57,
      "gST != ((void *) 0)"
      );
  }

  gBootServices = SystemTable->BootServices;
  if (gBootServices == NULL) {
    AssertPrint (
      "e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
      63,
      "gBS != ((void *) 0)"
      );
  }

  gRuntimeServices = SystemTable->RuntimeServices;
  if (gRuntimeServices == NULL) {
    AssertPrint (
      "e:\\hs\\MdePkg\\Library\\UefiRuntimeServicesTableLib\\UefiRuntimeServicesTableLib.c",
      47,
      "gRT != ((void *) 0)"
      );
  }

  //
  // Initialize the HOB list for later use (DxeHobLib compatibility).
  //
  GetBootModeHobList ();

  //
  // Locate the OEM configuration table entry (UMPT protocol GUID)
  // to obtain the protocol handle for subsequent registrations.
  //
  ProtocolHandle = 0;
  Status = gBootServices->LocateProtocol (
                            &gUbaOemGuid2,
                            NULL,
                            &ProtocolHandle
                            );
  if (!EFI_ERROR (Status)) {
    //
    // Register the ACPI path format table.
    // This installs the mAcpiPathFormatTable (containing "ACPF" signature
    // and pointer to the ACPI path entries) as the protocol interface.
    //
    Status = ((EFI_STATUS (*)(UINT64, VOID *, VOID *, UINTN)) \
              (*((UINT64 **)ProtocolHandle))[2]) (
               ProtocolHandle,
               &gAcpfProtocolGuid,
               &mAcpiPathFormatTable,
               sizeof (mAcpiPathFormatTable)
               );
    if (!EFI_ERROR (Status)) {
      //
      // Register the PIRQ protocol interface.
      //
      Status = ((EFI_STATUS (*)(UINT64, VOID *, VOID *, UINTN)) \
                (*((UINT64 **)ProtocolHandle))[2]) (
                 ProtocolHandle,
                 &gPirqProtocolGuid,
                 &gPirqProtocolHandle,
                 sizeof (gPirqProtocolHandle)
                 );
      if (!EFI_ERROR (Status)) {
        //
        // Register the FIX protocol interface.
        //
        return ((EFI_STATUS (*)(UINT64, VOID *, VOID *, UINTN)) \
                (*((UINT64 **)ProtocolHandle))[2]) (
                 ProtocolHandle,
                 &gFixProtocolGuid,
                 &gFixProtocolHandle,
                 sizeof (gFixProtocolHandle)
                 );
      }
    }
  }

  return Status;
}