/* $NoKeywords:$ */
/**
 * @file
 *
 * AMD CPU Register Table Related Functions
 *
 * Set registers according to a set of register tables
 *
 * @xrefitem bom "File Content Label" "Release Content"
 * @e project:      AGESA
 * @e sub-project:  CPU
 * @e \$Revision: 85962 $   @e \$Date: 2013-01-14 20:12:29 -0600 (Mon, 14 Jan 2013) $
 *
 */
/*
 ******************************************************************************
 *
 * Copyright (c) 2008 - 2013, Advanced Micro Devices, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of Advanced Micro Devices, Inc. nor the names of
 *       its contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL ADVANCED MICRO DEVICES, INC. BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ******************************************************************************
 */

/*----------------------------------------------------------------------------------------
 *                            M O D U L E S    U S E D
 *----------------------------------------------------------------------------------------
 */
#include "AGESA.h"
#include "amdlib.h"
#include "Ids.h"
#include "Topology.h"
#include "OptionMultiSocket.h"
#include "cpuRegisters.h"
#include "cpuFamilyTranslation.h"
#include "Table.h"
#include "GeneralServices.h"
#include "cpuServices.h"
#include "cpuFeatures.h"
#include "CommonReturns.h"
#include "cpuL3Features.h"
#include "Filecode.h"
CODE_GROUP (G1_PEICC)
RDATA_GROUP (G1_PEICC)

#define FILECODE PROC_CPU_TABLE_FILECODE

/*----------------------------------------------------------------------------------------
 *                   D E F I N I T I O N S    A N D    M A C R O S
 *----------------------------------------------------------------------------------------
 */

/*----------------------------------------------------------------------------------------
 *                  T Y P E D E F S     A N D     S T R U C T U R E S
 *----------------------------------------------------------------------------------------
 */

/*----------------------------------------------------------------------------------------
 *           P R O T O T Y P E S     O F     L O C A L     F U N C T I O N S
 *----------------------------------------------------------------------------------------
 */
VOID
SetRegistersFromTablesBeforeApLaunch (
  IN       CPU_SPECIFIC_SERVICES  *FamilyServices,
  IN       AMD_CPU_EARLY_PARAMS   *EarlyParams,
  IN       AMD_CONFIG_PARAMS      *StdHeader
  );

VOID
SetRegistersFromTablesAfterApLaunch (
  IN       CPU_SPECIFIC_SERVICES  *FamilyServices,
  IN       AMD_CPU_EARLY_PARAMS   *EarlyParams,
  IN       AMD_CONFIG_PARAMS      *StdHeader
  );

/*----------------------------------------------------------------------------------------
 *                          E X P O R T E D    F U N C T I O N S
 *----------------------------------------------------------------------------------------
 */
extern BUILD_OPT_CFG UserOptions;
extern CPU_FAMILY_SUPPORT_TABLE L3FeatureFamilyServiceTable;

/*---------------------------------------------------------------------------------------*/
/**
 * An iterator for all the Family and Model Register Tables.
 *
 * RegisterTableHandle should be set to NULL to begin iteration, the first time the method is
 * invoked.  Register tables can be processed, until this method returns NULL.  RegisterTableHandle
 * should simply be passed back to the method without modification or use by the caller.
 * The table selector allows the relevant tables for different cores to be iterated, if the family separates
 * tables.  For example, MSRs can be in a table processed by all cores and PCI registers in a table processed by
 * primary cores.
 *
 * @param[in]     FamilySpecificServices  The current Family Specific Services.
 * @param[in]     Selector                Select whether to iterate over tables for either all cores, primary cores, bsp, ....
 * @param[in]     TimePoint               Time point designator
 * @param[in,out] RegisterTableHandle     IN: The handle of the current register table, or NULL if Begin.
 *                                        OUT: The handle of the next register table, if not End.
 * @param[out]    NumberOfEntries         The number of entries in the table returned, if not End.
 * @param[in]     StdHeader               Handle of Header for calling lib functions and services.
 *
 * @return        The pointer to the next Register Table, or NULL if End.
 */
TABLE_ENTRY_FIELDS
STATIC
*GetNextRegisterTable (
  IN       CPU_SPECIFIC_SERVICES  *FamilySpecificServices,
  IN       TABLE_CORE_SELECTOR     Selector,
  IN       UINT32                  TimePoint,
  IN OUT   REGISTER_TABLE       ***RegisterTableHandle,
     OUT   UINTN                  *NumberOfEntries,
  IN       AMD_CONFIG_PARAMS      *StdHeader
  )
{
  REGISTER_TABLE **NextTable;
  TABLE_ENTRY_FIELDS *Entries;

  ASSERT ((FamilySpecificServices != NULL) && (StdHeader != NULL));
  ASSERT (Selector < TableCoreSelectorMax);

  NextTable = *RegisterTableHandle;
  if (NextTable == NULL) {
    // Begin
    NextTable = FamilySpecificServices->RegisterTableList;
    IDS_OPTION_HOOK (IDS_TRAP_TABLE, &NextTable, StdHeader);
  } else {
    NextTable++;
  }
  // skip if not selected
  while ((*NextTable != NULL) && (((*NextTable)->Selector != Selector) || (((*NextTable)->TimePoint & TimePoint) == 0))) {
    NextTable++;
  }
  if (*NextTable == NULL) {
    // End
    *RegisterTableHandle = NULL;
    Entries = NULL;
  } else {
    // Iterate next table
    *RegisterTableHandle = NextTable;
    *NumberOfEntries = (*NextTable)->NumberOfEntries;
    Entries = (TABLE_ENTRY_FIELDS *) (*NextTable)->Table;
  }
  return Entries;
}

/*---------------------------------------------------------------------------------------*/
/**
 * Compare counts to a pair of ranges.
 *
 * @param[in]       FirstCount       The actual count to be compared to the first range.
 * @param[in]       SecondCount      The actual count to be compared to the second range.
 * @param[in]       Ranges           The ranges which the counts are compared to.
 *
 * @retval          TRUE             Either one, or both, of the counts is in the range given.
 * @retval          FALSE            Neither count is in the range given.
 */
BOOLEAN
IsEitherCountInRange (
  IN       UINTN                FirstCount,
  IN       UINTN                SecondCount,
  IN       COUNT_RANGE_FEATURE  Ranges
  )
{
  // Errors: Entire Range value is zero, Min and Max reversed or not <=, ranges overlap (OK if first range is all),
  // the real counts are too big.
  ASSERT ((Ranges.Range0Min <= Ranges.Range0Max) &&
          (Ranges.Range1Min <= Ranges.Range1Max) &&
          (Ranges.Range0Max != 0) &&
          (Ranges.Range1Max != 0) &&
          ((Ranges.Range0Max == COUNT_RANGE_HIGH) || (Ranges.Range0Max < Ranges.Range1Min)) &&
          ((FirstCount < COUNT_RANGE_HIGH) && (SecondCount < COUNT_RANGE_HIGH)));

  return (BOOLEAN) (((FirstCount <= Ranges.Range0Max) && (FirstCount >= Ranges.Range0Min)) ||
                   ((SecondCount <= Ranges.Range1Max) && (SecondCount >= Ranges.Range1Min)));
}

/*-------------------------------------------------------------------------------------*/
/**
 * Returns the performance profile features list of the currently running processor core.
 *
 * @param[out]      Features          The performance profile features supported by this platform
 * @param[in]       PlatformConfig    Config handle for platform specific information
 * @param[in]       StdHeader         Header for library and services
 *
 */
VOID
GetPerformanceFeatures (
     OUT   PERFORMANCE_PROFILE_FEATS    *Features,
  IN       PLATFORM_CONFIGURATION       *PlatformConfig,
  IN       AMD_CONFIG_PARAMS            *StdHeader
  )
{
  CPUID_DATA  CpuidDataStruct;
  CPU_SPECIFIC_SERVICES  *FamilySpecificServices;
  L3_FEATURE_FAMILY_SERVICES *FeatureFamilyServices;

  Features->PerformanceProfileValue = 0;
  // Reflect Probe Filter Configuration.
  Features->PerformanceProfileFeatures.ProbeFilter = 0;
  if (IsFeatureEnabled (L3Features, PlatformConfig, StdHeader)) {
    GetFeatureServicesOfCurrentCore (&L3FeatureFamilyServiceTable, (CONST VOID **)&FeatureFamilyServices, StdHeader);
    if ((FeatureFamilyServices != NULL) &&
        (FeatureFamilyServices->IsHtAssistSupported (FeatureFamilyServices, PlatformConfig, StdHeader))) {
      Features->PerformanceProfileFeatures.ProbeFilter = 1;
    }
  }

  // Reflect Display Refresh Requests use 32 bytes Configuration.
  Features->PerformanceProfileFeatures.RefreshRequest32Byte = 0;
  if (PlatformConfig->PlatformProfile.Use32ByteRefresh) {
    Features->PerformanceProfileFeatures.RefreshRequest32Byte = 1;
  }
  // Reflect Mct Isoc Read Priority set to variable Configuration.
  Features->PerformanceProfileFeatures.MctIsocVariable = 0;
  if (PlatformConfig->PlatformProfile.UseVariableMctIsocPriority) {
    Features->PerformanceProfileFeatures.MctIsocVariable = 1;
  }
  // Indicate if this boot is a warm reset.
  Features->PerformanceProfileFeatures.IsWarmReset = 0;
  if (IsWarmReset (StdHeader)) {
    Features->PerformanceProfileFeatures.IsWarmReset = 1;
  }

  // Get L3 Cache present as indicated by CPUID
  Features->PerformanceProfileFeatures.L3Cache = 0;
  Features->PerformanceProfileFeatures.NoL3Cache = 1;
  LibAmdCpuidRead (AMD_CPUID_L2L3Cache_L2TLB, &CpuidDataStruct, StdHeader);
  if (((CpuidDataStruct.EDX_Reg & 0xFFFC0000) >> 18) != 0) {
    Features->PerformanceProfileFeatures.L3Cache = 1;
    Features->PerformanceProfileFeatures.NoL3Cache = 0;
  }

  // Get VRM select high speed from build option.
  Features->PerformanceProfileFeatures.VrmHighSpeed = 0;
  if (PlatformConfig->VrmProperties[CoreVrm].HiSpeedEnable) {
    Features->PerformanceProfileFeatures.VrmHighSpeed = 1;
  }

  // Get some family, model specific performance type info.
  GetCpuServicesOfCurrentCore ((CONST CPU_SPECIFIC_SERVICES **)&FamilySpecificServices, StdHeader);
  ASSERT (FamilySpecificServices != NULL);

  // Is the Northbridge P-State feature enabled
  Features->PerformanceProfileFeatures.NbPstates = 0;
  if (FamilySpecificServices->IsNbPstateEnabled (FamilySpecificServices, PlatformConfig, StdHeader)) {
    Features->PerformanceProfileFeatures.NbPstates = 1;
  }
}

/*---------------------------------------------------------------------------------------*/
/**
 * Perform the MSR Register Entry.
 *
 * @TableEntryTypeMethod{::MsrRegister}.
 *
 * Read - Modify - Write the MSR, clearing masked bits, and setting the data bits.
 *
 * @param[in]     Entry             The MSR register entry to perform
 * @param[in]     PlatformConfig    Config handle for platform specific information
 * @param[in]     StdHeader         Config handle for library and services.
 *
 */
VOID
SetRegisterForMsrEntry (
  IN       TABLE_ENTRY_DATA       *Entry,
  IN       PLATFORM_CONFIGURATION *PlatformConfig,
  IN       AMD_CONFIG_PARAMS      *StdHeader
  )
{
  UINT64          MsrData;

  // Even for only single bit fields, use those in the mask.  "Mask nothing" is a bug, even if just by policy.
  ASSERT (Entry->MsrEntry.Mask != 0);

  LibAmdMsrRead (Entry->MsrEntry.Address, &MsrData, StdHeader);
  MsrData = MsrData & (~(Entry->MsrEntry.Mask));
  MsrData = MsrData | Entry->MsrEntry.Data;
  LibAmdMsrWrite (Entry->MsrEntry.Address, &MsrData, StdHeader);
}

/*---------------------------------------------------------------------------------------*/
/**
 * Perform the PCI Register Entry.
 *
 * @TableEntryTypeMethod{::PciRegister}.
 *
 * Make the current core's PCI address with the function and register for the entry.
 * Read - Modify - Write the PCI register, clearing masked bits, and setting the data bits.
 *
 * @param[in]     Entry             The PCI register entry to perform
 * @param[in]     PlatformConfig    Config handle for platform specific information
 * @param[in]     StdHeader         Config handle for library and services.
 *
 */
VOID
SetRegisterForPciEntry (
  IN       TABLE_ENTRY_DATA       *Entry,
  IN       PLATFORM_CONFIGURATION *PlatformConfig,
  IN       AMD_CONFIG_PARAMS      *StdHeader
  )
{
  UINT32          TempVar32_a;
  UINT32          MySocket;
  UINT32          MyModule;
  UINT32          Ignored;
  PCI_ADDR        MyPciAddress;
  AGESA_STATUS    IgnoredSts;
  TABLE_ENTRY_DATA  PciEntry;

  // Errors:  Possible values in unused entry space, extra type features, value range checks.
  // Check that the entry type is correct and the actual supplied entry data is appropriate for that entry.
  // Even for only single bit fields, use those in the mask.  "Mask nothing" is a bug, even if just by policy.
  ASSERT ((Entry->InitialValues[4] == 0) &&
          (Entry->InitialValues[3] == 0) &&
          (Entry->PciEntry.Mask != 0));

  LibAmdMemFill (&PciEntry, 0, sizeof (TABLE_ENTRY_DATA), StdHeader);
  PciEntry.PciEntry = Entry->PciEntry;

  IDS_OPTION_HOOK (IDS_SET_PCI_REGISTER_ENTRY, &PciEntry, StdHeader);

  IdentifyCore (StdHeader, &MySocket, &MyModule, &Ignored, &IgnoredSts);
  GetPciAddress (StdHeader, MySocket, MyModule, &MyPciAddress, &IgnoredSts);
  MyPciAddress.Address.Function = PciEntry.PciEntry.Address.Address.Function;
  MyPciAddress.Address.Register = PciEntry.PciEntry.Address.Address.Register;
  LibAmdPciRead (AccessWidth32, MyPciAddress, &TempVar32_a, StdHeader);
  TempVar32_a = TempVar32_a & (~(PciEntry.PciEntry.Mask));
  TempVar32_a = TempVar32_a | PciEntry.PciEntry.Data;
  LibAmdPciWrite (AccessWidth32, MyPciAddress, &TempVar32_a, StdHeader);
}

/*---------------------------------------------------------------------------------------*/
/**
 * Perform the Family Specific Workaround Register Entry.
 *
 * @TableEntryTypeMethod{::FamSpecificWorkaround}.
 *
 * Call the function, passing the data.
 *
 * See if you can use the other entries or make an entry that covers the fix.
 * After all, the purpose of having a table entry is to @b NOT have code which
 * isn't generic feature code, but is family/model code specific to one case.
 *
 * @param[in]     Entry             The Family Specific Workaround register entry to perform
 * @param[in]     PlatformConfig    Config handle for platform specific information
 * @param[in]     StdHeader         Config handle for library and services.
 *
 */
VOID
SetRegisterForFamSpecificWorkaroundEntry (
  IN       TABLE_ENTRY_DATA       *Entry,
  IN       PLATFORM_CONFIGURATION *PlatformConfig,
  IN       AMD_CONFIG_PARAMS      *StdHeader
  )
{
  ASSERT (Entry->FamSpecificEntry.DoAction != NULL);

  Entry->FamSpecificEntry.DoAction (Entry->FamSpecificEntry.Data, StdHeader);
}

/*---------------------------------------------------------------------------------------*/
/**
 * Perform the Performance Profile PCI Register Entry.
 *
 * @TableEntryTypeMethod{::ProfileFixup}.
 *
 * Check the entry's performance profile features to the platform's and do the
 * PCI register entry if they match.
 *
 * @param[in]     Entry             The Performance Profile register entry to perform
 * @param[in]     PlatformConfig    Config handle for platform specific information
 * @param[in]     StdHeader         Config handle for library and services.
 *
 */
VOID
SetRegisterForPerformanceProfileEntry (
  IN       TABLE_ENTRY_DATA      *Entry,
  IN       PLATFORM_CONFIGURATION *PlatformConfig,
  IN       AMD_CONFIG_PARAMS     *StdHeader
  )
{
  PERFORMANCE_PROFILE_FEATS PlatformProfile;
  TABLE_ENTRY_DATA          PciEntry;

  // Errors:  Possible values in unused entry space, extra type features, value range checks.
  // Check that the entry type is correct and the actual supplied entry data is appropriate for that entry.
  ASSERT (((Entry->TokenPciEntry.TypeFeats.PerformanceProfileValue & ~((PERFORMANCE_PROFILE_ALL) | (PERFORMANCE_AND))) == 0) &&
          (Entry->InitialValues[4] == 0));

  GetPerformanceFeatures (&PlatformProfile, PlatformConfig, StdHeader);
  if (DoesEntryTypeSpecificInfoMatch (PlatformProfile.PerformanceProfileValue,
                                      Entry->FixupEntry.TypeFeats.PerformanceProfileValue)) {
    LibAmdMemFill (&PciEntry, 0, sizeof (TABLE_ENTRY_DATA), StdHeader);
    PciEntry.PciEntry = Entry->FixupEntry.PciEntry;
    SetRegisterForPciEntry (&PciEntry, PlatformConfig, StdHeader);
  }
}


/*---------------------------------------------------------------------------------------*/
/**
 * Perform the Core Counts Performance PCI Register Entry.
 *
 * @TableEntryTypeMethod{::CoreCountsPciRegister}.
 *
 * Check the performance profile.
 * Check the actual core count to the range pair given, and apply if matched.
 *
 * @param[in]     Entry             The PCI register entry to perform
 * @param[in]     PlatformConfig    Config handle for platform specific information
 * @param[in]     StdHeader         Config handle for library and services.
 *
 */
VOID
SetRegisterForCoreCountsPerformanceEntry (
  IN       TABLE_ENTRY_DATA      *Entry,
  IN       PLATFORM_CONFIGURATION *PlatformConfig,
  IN       AMD_CONFIG_PARAMS     *StdHeader
  )
{
  PERFORMANCE_PROFILE_FEATS PlatformProfile;
  UINTN ActualCoreCount;
  TABLE_ENTRY_DATA          PciEntry;

  // Errors:  Possible values in unused entry space, extra type features, value range checks.
  // Check that the entry type is correct and the actual supplied entry data is appropriate for that entry.
  ASSERT (((Entry->CoreCountEntry.TypeFeats.PerformanceProfileValue & ~((PERFORMANCE_PROFILE_ALL) | (PERFORMANCE_AND))) == 0));

  GetPerformanceFeatures (&PlatformProfile, PlatformConfig, StdHeader);
  if (DoesEntryTypeSpecificInfoMatch (PlatformProfile.PerformanceProfileValue, Entry->CoreCountEntry.TypeFeats.PerformanceProfileValue)) {
    ActualCoreCount = GetActiveCoresInCurrentModule (StdHeader);
    // Check if the actual core count is in either range.
    if (IsEitherCountInRange (ActualCoreCount, ActualCoreCount, Entry->CoreCountEntry.CoreCounts.CoreRanges)) {
      LibAmdMemFill (&PciEntry, 0, sizeof (TABLE_ENTRY_DATA), StdHeader);
      PciEntry.PciEntry = Entry->CoreCountEntry.PciEntry;
      SetRegisterForPciEntry (&PciEntry, PlatformConfig, StdHeader);
    }
  }
}

/*---------------------------------------------------------------------------------------*/
/**
 * Perform the Processor Counts PCI Register Entry.
 *
 * @TableEntryTypeMethod{::ProcCountsPciRegister}.
 *
 * Check the performance profile.
 * Check the actual processor count (not node count!) to the range pair given, and apply if matched.
 *
 * @param[in]     Entry             The PCI register entry to perform
 * @param[in]     PlatformConfig    Config handle for platform specific information
 * @param[in]     StdHeader         Config handle for library and services.
 *
 */
VOID
SetRegisterForProcessorCountsEntry (
  IN       TABLE_ENTRY_DATA       *Entry,
  IN       PLATFORM_CONFIGURATION *PlatformConfig,
  IN       AMD_CONFIG_PARAMS      *StdHeader
  )
{
  PERFORMANCE_PROFILE_FEATS PlatformProfile;
  UINTN ProcessorCount;
  TABLE_ENTRY_DATA          PciEntry;

  // Errors:  Possible values in unused entry space, extra type features, value range checks.
  // Check that the entry type is correct and the actual supplied entry data is appropriate for that entry.
  ASSERT (((Entry->ProcCountEntry.TypeFeats.PerformanceProfileValue & ~((PERFORMANCE_PROFILE_ALL) | (PERFORMANCE_AND))) == 0));

  GetPerformanceFeatures (&PlatformProfile, PlatformConfig, StdHeader);
  if (DoesEntryTypeSpecificInfoMatch (PlatformProfile.PerformanceProfileValue, Entry->ProcCountEntry.TypeFeats.PerformanceProfileValue)) {
    ProcessorCount = GetNumberOfProcessors (StdHeader);
    // Check if the actual processor count is in either range.
    if (IsEitherCountInRange (ProcessorCount, ProcessorCount, Entry->ProcCountEntry.ProcessorCounts.ProcessorCountRanges)) {
      LibAmdMemFill (&PciEntry, 0, sizeof (TABLE_ENTRY_DATA), StdHeader);
      PciEntry.PciEntry = Entry->ProcCountEntry.PciEntry;
      SetRegisterForPciEntry (&PciEntry, PlatformConfig, StdHeader);
    }
  }
}

/*---------------------------------------------------------------------------------------*/
/**
 * Perform the Compute Unit Counts PCI Register Entry.
 *
 * @TableEntryTypeMethod{::CompUnitCountsPciRegister}.
 *
 * Check the entry's performance profile features and the compute unit count
 * to the platform's and do the PCI register entry if they match.
 *
 * @param[in]     Entry             The PCI register entry to perform
 * @param[in]     PlatformConfig    Config handle for platform specific information
 * @param[in]     StdHeader         Config handle for library and services.
 *
 */
VOID
SetRegisterForComputeUnitCountsEntry (
  IN       TABLE_ENTRY_DATA       *Entry,
  IN       PLATFORM_CONFIGURATION *PlatformConfig,
  IN       AMD_CONFIG_PARAMS      *StdHeader
  )
{
  PERFORMANCE_PROFILE_FEATS PlatformProfile;
  UINTN ComputeUnitCount;
  TABLE_ENTRY_DATA          PciEntry;

  // Errors:  Possible values in unused entry space, extra type features, value range checks.
  // Check that the entry type is correct and the actual supplied entry data is appropriate for that entry.
  ASSERT (((Entry->CompUnitCountEntry.TypeFeats.PerformanceProfileValue & ~((PERFORMANCE_PROFILE_ALL) | (PERFORMANCE_AND))) == 0));

  GetPerformanceFeatures (&PlatformProfile, PlatformConfig, StdHeader);
  if (DoesEntryTypeSpecificInfoMatch (PlatformProfile.PerformanceProfileValue, Entry->CompUnitCountEntry.TypeFeats.PerformanceProfileValue)) {
    ComputeUnitCount = GetNumberOfCompUnitsInCurrentModule (StdHeader);
    // Check if the actual compute unit count is in either range.
    if (IsEitherCountInRange (ComputeUnitCount, ComputeUnitCount, Entry->CompUnitCountEntry.ComputeUnitCounts.ComputeUnitRanges)) {
      LibAmdMemFill (&PciEntry, 0, sizeof (TABLE_ENTRY_DATA), StdHeader);
      PciEntry.PciEntry = Entry->CompUnitCountEntry.PciEntry;
      SetRegisterForPciEntry (&PciEntry, PlatformConfig, StdHeader);
    }
  }
}

/*---------------------------------------------------------------------------------------*/
/**
 * Perform the Compute Unit Counts MSR Register Entry.
 *
 * @TableEntryTypeMethod{::CompUnitCountsMsr}.
 *
 * Check the entry's compute unit count to the platform's and do the
 * MSR entry if they match.
 *
 * @param[in]     Entry             The PCI register entry to perform
 * @param[in]     PlatformConfig    Config handle for platform specific information
 * @param[in]     StdHeader         Config handle for library and services.
 *
 */
VOID
SetMsrForComputeUnitCountsEntry (
  IN       TABLE_ENTRY_DATA       *Entry,
  IN       PLATFORM_CONFIGURATION *PlatformConfig,
  IN       AMD_CONFIG_PARAMS      *StdHeader
  )
{
  UINTN ComputeUnitCount;
  TABLE_ENTRY_DATA          MsrEntry;

  ComputeUnitCount = GetNumberOfCompUnitsInCurrentModule (StdHeader);
  // Check if the actual compute unit count is in either range.
  if (IsEitherCountInRange (ComputeUnitCount, ComputeUnitCount, Entry->CompUnitCountMsrEntry.ComputeUnitCounts.ComputeUnitRanges)) {
    LibAmdMemFill (&MsrEntry, 0, sizeof (TABLE_ENTRY_DATA), StdHeader);
    MsrEntry.MsrEntry = Entry->CompUnitCountMsrEntry.MsrEntry;
    SetRegisterForMsrEntry (&MsrEntry, PlatformConfig, StdHeader);
  }
}

/* -----------------------------------------------------------------------------*/
/**
 * Returns the platform features list of the currently running processor core.
 *
 * @param[out]      Features          The Features supported by this platform
 * @param[in]       PlatformConfig    Config handle for platform specific information
 * @param[in]       StdHeader         Header for library and services
 *
 */
VOID
GetPlatformFeatures (
     OUT   PLATFORM_FEATS         *Features,
  IN       PLATFORM_CONFIGURATION *PlatformConfig,
  IN       AMD_CONFIG_PARAMS      *StdHeader
  )
{
  PCI_ADDR  PciAddress;
  UINT32    CapabilityReg;
  UINT32     Link;
  CPU_SPECIFIC_SERVICES  *FamilySpecificServices;
  UINT32     CoreCount;

  // Start with none.
  Features->PlatformValue = 0;

  switch (PlatformConfig->PlatformProfile.PlatformControlFlowMode) {
  case Nfcm:
    Features->PlatformFeatures.PlatformNfcm = 1;
    break;
  case UmaDr:
    Features->PlatformFeatures.PlatformUma = 1;
    break;
  case UmaIfcm:
    Features->PlatformFeatures.PlatformUmaIfcm = 1;
    break;
  case Ifcm:
    Features->PlatformFeatures.PlatformIfcm = 1;
    break;
  case Iommu:
    Features->PlatformFeatures.PlatformIommu = 1;
    break;
  default:
    ASSERT (FALSE);
  }
  // Check - Single Link?
  // This is based on the implemented links on the package regardless of their
  // connection status.  All processors must match the BSP, so we only check it and
  // not the current node.  We don't care exactly how many links there are, as soon
  // as we find more than one we are done.
  Link = 0;
  PciAddress.AddressValue = MAKE_SBDFO (0, 0, PCI_DEV_BASE, FUNC_0, 0);
  // Until either all capabilities are done or until the desired link is found,
  // keep looking for HT Host Capabilities.
  while (Link < 2) {
    LibAmdPciFindNextCap (&PciAddress, StdHeader);
    if (PciAddress.AddressValue != ILLEGAL_SBDFO) {
      LibAmdPciRead (AccessWidth32, PciAddress, &CapabilityReg, StdHeader);
      if ((CapabilityReg & 0xE00000FF) == 0x20000008) {
        Link++;
      }
      // A capability other than an HT capability, keep looking.
    } else {
      // end of capabilities
      break;
    }
  }
  if (Link < 2) {
    Features->PlatformFeatures.PlatformSingleLink = 1;
  } else {
    Features->PlatformFeatures.PlatformMultiLink = 1;
  }

  // Set the legacy core count bits.
  GetActiveCoresInCurrentSocket (&CoreCount, StdHeader);
  switch (CoreCount) {
  case 1:
    Features->PlatformFeatures.PlatformSingleCore = 1;
    break;
  case 2:
    Features->PlatformFeatures.PlatformDualCore = 1;
    break;
  default:
    Features->PlatformFeatures.PlatformMultiCore = 1;
  }

  //
  // Get some specific platform type info, VC...etc.
  //
  GetCpuServicesOfCurrentCore ((CONST CPU_SPECIFIC_SERVICES **)&FamilySpecificServices, StdHeader);
  ASSERT (FamilySpecificServices != NULL);
  FamilySpecificServices->GetPlatformTypeSpecificInfo (FamilySpecificServices, Features, StdHeader);

}

/*---------------------------------------------------------------------------------------*/
/**
 * Checks if a register table entry applies to the executing core.
 *
 * This function uses a combination of logical ID and platform features to
 * determine whether or not a register table entry applies to the executing core.
 *
 * @param[in]     CoreCpuRevision         The current core's logical ID
 * @param[in]     EntryCpuRevision        The entry's desired logical IDs
 * @param[in]     PlatformFeatures        The platform features
 * @param[in]     EntryFeatures           The entry's desired platform features
 *
 * @retval        TRUE           This entry should be applied
 * @retval        FALSE          This entry does not apply
 *
 */
BOOLEAN
STATIC
DoesEntryMatchPlatform (
  IN       CPU_LOGICAL_ID   CoreCpuRevision,
  IN       CPU_LOGICAL_ID   EntryCpuRevision,
  IN       PLATFORM_FEATS   PlatformFeatures,
  IN       PLATFORM_FEATS   EntryFeatures
  )
{
  BOOLEAN Result;

  Result = FALSE;

  if (((CoreCpuRevision.Family & EntryCpuRevision.Family) != 0) &&
      ((CoreCpuRevision.Revision & EntryCpuRevision.Revision) != 0)) {
    if (EntryFeatures.PlatformFeatures.AndPlatformFeats == 0) {
      // Match if ANY entry feats match a platform feat (an OR test)
      if ((EntryFeatures.PlatformValue & PlatformFeatures.PlatformValue) != 0) {
        Result = TRUE;
      }
    } else {
      // Match if ALL entry feats match a platform feat (an AND test)
      if ((EntryFeatures.PlatformValue & ~(AMD_PF_AND)) ==
          (EntryFeatures.PlatformValue & PlatformFeatures.PlatformValue)) {
        Result = TRUE;
      }
    }
  }

  return Result;
}

/*---------------------------------------------------------------------------------------*/
/**
 * Checks register table entry type specific criteria to the platform.
 *
 * Entry Data Type implementer methods can use this generically to check their own
 * specific criteria.  The method collects the actual platform characteristics and
 * provides them along with the table entry's criteria to this service.
 *
 * There are a couple considerations for any implementer method using this service.
 * The criteria value has to be representable as a UINT32.  The MSB, Bit 31, has to
 * be used as a AND test request if set in the entry.  (The platform value should never
 * have that bit set.)
 *
 * @param[in]     PlatformTypeSpecificFeatures        The platform features
 * @param[in]     EntryTypeFeatures                   The entry's desired platform features
 *
 * @retval        TRUE                                This entry should be applied
 * @retval        FALSE                               This entry does not apply
 *
 */
BOOLEAN
DoesEntryTypeSpecificInfoMatch (
  IN       UINT32   PlatformTypeSpecificFeatures,
  IN       UINT32   EntryTypeFeatures
  )
{
  BOOLEAN Result;

  Result = FALSE;

  if ((EntryTypeFeatures & BIT31) == 0) {
    // Match if ANY entry feats match a platform feat (an OR test)
    if ((EntryTypeFeatures & PlatformTypeSpecificFeatures) != 0) {
      Result = TRUE;
    }
  } else {
    // Match if ALL entry feats match a platform feat (an AND test)
    if ((EntryTypeFeatures & ~(BIT31)) == (EntryTypeFeatures & PlatformTypeSpecificFeatures)) {
      Result = TRUE;
    }
  }
  return Result;
}

/*---------------------------------------------------------------------------------------*/
/**
 * Determine this core's Selector matches.
 *
 * @param[in]  Selector    Is the current core this selector type?
 * @param[in]  StdHeader   Config handle for library and services.
 *
 * @retval  TRUE           Yes, it is.
 * @retval  FALSE          No, it is not.
 */
BOOLEAN
STATIC
IsCoreSelector (
  IN       TABLE_CORE_SELECTOR       Selector,
  IN       AMD_CONFIG_PARAMS        *StdHeader
  )
{
  BOOLEAN Result;
  AGESA_STATUS  CalledStatus;

  Result = TRUE;
  ASSERT (Selector < TableCoreSelectorMax);

  if ((Selector == PrimaryCores) && !IsCurrentCorePrimary (StdHeader)) {
    Result = FALSE;
  }
  if ((Selector == ComputeUnitPrimary) && !IsCoreComputeUnitPrimary (FirstCoreIsComputeUnitPrimary, StdHeader)) {
    Result = FALSE;
  }
  if ((Selector == BscCore) && (!IsBsp (StdHeader, &CalledStatus))) {
    Result = FALSE;
  }
  return Result;
}

/*---------------------------------------------------------------------------------------*/
/**
 * Set the registers for this core based on entries in a list of Register Tables.
 *
 * Determine the platform features and this core's logical id.  Get the specific table
 * entry type implementations for the logical model, which may be either generic (the ones
 * in this file) or specific.
 *
 * Scan the tables starting the with ones for all cores and progressively narrowing the selection
 * based on this core's role (ex. primary core).  For a selected table, check for each entry
 * matching the current core and platform, and call the implementer method to perform the
 * register set operation if it matches.
 *
 * @param[in]  PlatformConfig    Config handle for platform specific information
 * @param[in]  TimePoint         Time point designator
 * @param[in]  StdHeader         Config handle for library and services.
 *
 */
VOID
SetRegistersFromTables (
  IN       PLATFORM_CONFIGURATION *PlatformConfig,
  IN       UINT32                 TimePoint,
  IN       AMD_CONFIG_PARAMS      *StdHeader
  )
{
  CPU_LOGICAL_ID         CpuLogicalId;
  PLATFORM_FEATS         PlatformFeatures;
  CPU_SPECIFIC_SERVICES *FamilySpecificServices;
  TABLE_ENTRY_FIELDS    *Entries;
  TABLE_CORE_SELECTOR    Selector;
  TABLE_ENTRY_TYPE       EntryType;
  REGISTER_TABLE       **TableHandle;
  UINTN                  NumberOfEntries;
  UINTN                  CurrentEntryCount;
  TABLE_ENTRY_TYPE_DESCRIPTOR *TypeImplementer;
  PF_DO_TABLE_ENTRY       DoTableEntry[TableEntryTypeMax];

  // Did you really mean to increase the size of ALL table entries??!!
  // While it is not necessarily a bug to increase the size of table entries:
  //   - Is this warning a surprise?  Please fix it.
  //   - If expected, is this really a feature which is worth the increase?  Then let other entries also use the space.
  ASSERT (sizeof (TABLE_ENTRY_DATA) == (MAX_ENTRY_TYPE_ITEMS32 * sizeof (UINT32)));

  PlatformFeatures.PlatformValue = 0;
  GetLogicalIdOfCurrentCore (&CpuLogicalId, StdHeader);
  GetPlatformFeatures (&PlatformFeatures, PlatformConfig, StdHeader);
  GetCpuServicesFromLogicalId (&CpuLogicalId, (CONST CPU_SPECIFIC_SERVICES **)&FamilySpecificServices, StdHeader);

  // Build a non-sparse table of implementer methods, so we don't have to keep searching.
  // It is a bug to not include a descriptor for a type that is in the table (but the
  // descriptor can point to a non-assert stub).
  // Also, it is not a bug to have no register table implementations, but it is a bug to have none and call this routine.
  for (EntryType = MsrRegister; EntryType < TableEntryTypeMax; EntryType++) {
    DoTableEntry[EntryType] = (PF_DO_TABLE_ENTRY)CommonAssert;
  }
  TypeImplementer = FamilySpecificServices->TableEntryTypeDescriptors;
  ASSERT (TypeImplementer != NULL);
  while (TypeImplementer->EntryType < TableEntryTypeMax) {
    DoTableEntry[TypeImplementer->EntryType] = TypeImplementer->DoTableEntry;
    TypeImplementer++;
  }

  for (Selector = AllCores; Selector < TableCoreSelectorMax; Selector++) {
    if (IsCoreSelector (Selector, StdHeader)) {
      // If the current core is the selected type of core, work the table list for tables for that type of core.
      TableHandle = NULL;
      Entries = GetNextRegisterTable (FamilySpecificServices, Selector, TimePoint, &TableHandle, &NumberOfEntries, StdHeader);
      while (Entries != NULL) {
        for (CurrentEntryCount = 0; CurrentEntryCount < NumberOfEntries; CurrentEntryCount++, Entries++) {
          if (DoesEntryMatchPlatform (CpuLogicalId, Entries->CpuRevision, PlatformFeatures, Entries->Features)) {
            // The entry matches this config, Do It!
            // Find the implementer for this entry type and pass the entry data to it.
            ASSERT (Entries->EntryType < TableEntryTypeMax);
            DoTableEntry[Entries->EntryType] (&Entries->Entry, PlatformConfig, StdHeader);
          }
        }
        Entries = GetNextRegisterTable (FamilySpecificServices, Selector, TimePoint, &TableHandle, &NumberOfEntries, StdHeader);
      }
    } else {
      // Once a selector does not match the current core, quit looking.
      break;
    }
  }
}

/*---------------------------------------------------------------------------------------*/
/**
 * Set the registers for this core based on entries in a list of Register Tables.
 *
 * This function acts as a wrapper for calling the SetRegistersFromTables
 * routine at AmdInitEarly.
 *
 *  @param[in]   FamilyServices      The current Family Specific Services.
 *  @param[in]   EarlyParams         Service parameters.
 *  @param[in]   StdHeader           Config handle for library and services.
 *
 */
VOID
SetRegistersFromTablesBeforeApLaunch (
  IN       CPU_SPECIFIC_SERVICES  *FamilyServices,
  IN       AMD_CPU_EARLY_PARAMS   *EarlyParams,
  IN       AMD_CONFIG_PARAMS      *StdHeader
  )
{
  AGESA_TESTPOINT (TpProcCpuProcessRegisterTables, StdHeader);
  SetRegistersFromTables (&EarlyParams->PlatformConfig, PERFORM_TP_BEFORE_AP_LAUNCH, StdHeader);
}

/*---------------------------------------------------------------------------------------*/
/**
 * Set the registers for this core based on entries in a list of Register Tables.
 *
 * This function acts as a wrapper for calling the SetRegistersFromTables
 * routine at AmdInitEarly.
 *
 *  @param[in]   FamilyServices      The current Family Specific Services.
 *  @param[in]   EarlyParams         Service parameters.
 *  @param[in]   StdHeader           Config handle for library and services.
 *
 */
VOID
SetRegistersFromTablesAfterApLaunch (
  IN       CPU_SPECIFIC_SERVICES  *FamilyServices,
  IN       AMD_CPU_EARLY_PARAMS   *EarlyParams,
  IN       AMD_CONFIG_PARAMS      *StdHeader
  )
{
  AGESA_TESTPOINT (TpProcCpuProcessRegisterTables, StdHeader);
  SetRegistersFromTables (&EarlyParams->PlatformConfig, PERFORM_TP_AFTER_AP_LAUNCH, StdHeader);
}

