/** @file
  CPU Register Table Library functions.

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

**/

#include "RegisterCpuFeatures.h"

/**
  Function that uses DEBUG() macros to display the contents of a a CPU feature bit mask.

  @param[in]  FeatureMask  A pointer to the CPU feature bit mask.
  @param[in]  BitMaskSize  CPU feature bits mask buffer size.

**/
VOID
DumpCpuFeatureMask (
  IN UINT8   *FeatureMask,
  IN UINT32  BitMaskSize
  )
{
  UINTN  Index;
  UINT8  *Data8;

  Data8 = (UINT8 *)FeatureMask;
  for (Index = 0; Index < BitMaskSize; Index++) {
    DEBUG ((DEBUG_INFO, " %02x ", *Data8++));
  }

  DEBUG ((DEBUG_INFO, "\n"));
}

/**
  Dump CPU feature name or CPU feature bit mask.

  @param[in]  CpuFeature   Pointer to CPU_FEATURES_ENTRY
  @param[in]  BitMaskSize  CPU feature bits mask buffer size.

**/
VOID
DumpCpuFeature (
  IN CPU_FEATURES_ENTRY  *CpuFeature,
  IN UINT32              BitMaskSize
  )
{
  if (CpuFeature->FeatureName != NULL) {
    DEBUG ((DEBUG_INFO, "FeatureName: %a\n", CpuFeature->FeatureName));
  } else {
    DEBUG ((DEBUG_INFO, "FeatureMask = "));
    DumpCpuFeatureMask (CpuFeature->FeatureMask, BitMaskSize);
  }
}

/**
  Determines if the feature bit mask is in dependent CPU feature bit mask buffer.

  @param[in]  FeatureMask        Pointer to CPU feature bit mask
  @param[in]  DependentBitMask   Pointer to dependent CPU feature bit mask buffer

  @retval TRUE  The feature bit mask is in dependent CPU feature bit mask buffer.
  @retval FALSE The feature bit mask is not in dependent CPU feature bit mask buffer.
**/
BOOLEAN
IsBitMaskMatchCheck (
  IN UINT8  *FeatureMask,
  IN UINT8  *DependentBitMask
  )
{
  UINTN              Index;
  UINT8              *Data1;
  UINT8              *Data2;
  CPU_FEATURES_DATA  *CpuFeaturesData;

  CpuFeaturesData = GetCpuFeaturesData ();

  Data1 = FeatureMask;
  Data2 = DependentBitMask;
  for (Index = 0; Index < CpuFeaturesData->BitMaskSize; Index++) {
    if (((*(Data1++)) & (*(Data2++))) != 0) {
      return TRUE;
    }
  }

  return FALSE;
}

/**
  Try to find the specify cpu featuren in former/after feature list.

  @param[in]  FeatureList        Pointer to dependent CPU feature list
  @param[in]  CurrentEntry       Pointer to current CPU feature entry.
  @param[in]  SearchFormer       Find in former feature or after features.
  @param[in]  FeatureMask        Pointer to CPU feature bit mask

  @retval TRUE  The feature bit mask is in dependent CPU feature bit mask buffer.
  @retval FALSE The feature bit mask is not in dependent CPU feature bit mask buffer.
**/
BOOLEAN
FindSpecifyFeature (
  IN LIST_ENTRY  *FeatureList,
  IN LIST_ENTRY  *CurrentEntry,
  IN BOOLEAN     SearchFormer,
  IN UINT8       *FeatureMask
  )
{
  CPU_FEATURES_ENTRY  *CpuFeature;
  LIST_ENTRY          *NextEntry;

  //
  // Check whether exist the not neighborhood entry first.
  // If not exist, return FALSE means not found status.
  //
  if (SearchFormer) {
    NextEntry = CurrentEntry->BackLink;
    if (IsNull (FeatureList, NextEntry)) {
      return FALSE;
    }

    NextEntry = NextEntry->BackLink;
    if (IsNull (FeatureList, NextEntry)) {
      return FALSE;
    }

    NextEntry = CurrentEntry->BackLink->BackLink;
  } else {
    NextEntry = CurrentEntry->ForwardLink;
    if (IsNull (FeatureList, NextEntry)) {
      return FALSE;
    }

    NextEntry = NextEntry->ForwardLink;
    if (IsNull (FeatureList, NextEntry)) {
      return FALSE;
    }

    NextEntry = CurrentEntry->ForwardLink->ForwardLink;
  }

  while (!IsNull (FeatureList, NextEntry)) {
    CpuFeature = CPU_FEATURE_ENTRY_FROM_LINK (NextEntry);

    if (IsBitMaskMatchCheck (FeatureMask, CpuFeature->FeatureMask)) {
      return TRUE;
    }

    if (SearchFormer) {
      NextEntry = NextEntry->BackLink;
    } else {
      NextEntry = NextEntry->ForwardLink;
    }
  }

  return FALSE;
}

/**
  Return feature dependence result.

  @param[in]  CpuFeature            Pointer to CPU feature.
  @param[in]  Before                Check before dependence or after.
  @param[in]  NextCpuFeatureMask    Pointer to next CPU feature Mask.

  @retval     return the dependence result.
**/
CPU_FEATURE_DEPENDENCE_TYPE
DetectFeatureScope (
  IN CPU_FEATURES_ENTRY  *CpuFeature,
  IN BOOLEAN             Before,
  IN UINT8               *NextCpuFeatureMask
  )
{
  //
  // if need to check before type dependence but the feature after current feature is not
  // exist, means this before type dependence not valid, just return NoneDepType.
  // Just like Feature A has a dependence of feature B, but Feature B not installed, so
  // Feature A maybe insert to the last entry of the list. In this case, for below code,
  // Featrure A has depend of feature B, but it is the last entry of the list, so the
  // NextCpuFeatureMask is NULL, so the dependence for feature A here is useless and code
  // just return NoneDepType.
  //
  if (NextCpuFeatureMask == NULL) {
    return NoneDepType;
  }

  if (Before) {
    if ((CpuFeature->PackageBeforeFeatureBitMask != NULL) &&
        IsBitMaskMatchCheck (NextCpuFeatureMask, CpuFeature->PackageBeforeFeatureBitMask))
    {
      return PackageDepType;
    }

    if ((CpuFeature->CoreBeforeFeatureBitMask != NULL) &&
        IsBitMaskMatchCheck (NextCpuFeatureMask, CpuFeature->CoreBeforeFeatureBitMask))
    {
      return CoreDepType;
    }

    if ((CpuFeature->ThreadBeforeFeatureBitMask != NULL) &&
        IsBitMaskMatchCheck (NextCpuFeatureMask, CpuFeature->ThreadBeforeFeatureBitMask))
    {
      return ThreadDepType;
    }

    return NoneDepType;
  }

  if ((CpuFeature->PackageAfterFeatureBitMask != NULL) &&
      IsBitMaskMatchCheck (NextCpuFeatureMask, CpuFeature->PackageAfterFeatureBitMask))
  {
    return PackageDepType;
  }

  if ((CpuFeature->CoreAfterFeatureBitMask != NULL) &&
      IsBitMaskMatchCheck (NextCpuFeatureMask, CpuFeature->CoreAfterFeatureBitMask))
  {
    return CoreDepType;
  }

  if ((CpuFeature->ThreadAfterFeatureBitMask != NULL) &&
      IsBitMaskMatchCheck (NextCpuFeatureMask, CpuFeature->ThreadAfterFeatureBitMask))
  {
    return ThreadDepType;
  }

  return NoneDepType;
}

/**
  Return feature dependence result.

  @param[in]  CpuFeature            Pointer to CPU feature.
  @param[in]  Before                Check before dependence or after.
  @param[in]  FeatureList           Pointer to CPU feature list.

  @retval     return the dependence result.
**/
CPU_FEATURE_DEPENDENCE_TYPE
DetectNoneNeighborhoodFeatureScope (
  IN CPU_FEATURES_ENTRY  *CpuFeature,
  IN BOOLEAN             Before,
  IN LIST_ENTRY          *FeatureList
  )
{
  if (Before) {
    if ((CpuFeature->PackageBeforeFeatureBitMask != NULL) &&
        FindSpecifyFeature (FeatureList, &CpuFeature->Link, FALSE, CpuFeature->PackageBeforeFeatureBitMask))
    {
      return PackageDepType;
    }

    if ((CpuFeature->CoreBeforeFeatureBitMask != NULL) &&
        FindSpecifyFeature (FeatureList, &CpuFeature->Link, FALSE, CpuFeature->CoreBeforeFeatureBitMask))
    {
      return CoreDepType;
    }

    if ((CpuFeature->ThreadBeforeFeatureBitMask != NULL) &&
        FindSpecifyFeature (FeatureList, &CpuFeature->Link, FALSE, CpuFeature->ThreadBeforeFeatureBitMask))
    {
      return ThreadDepType;
    }

    return NoneDepType;
  }

  if ((CpuFeature->PackageAfterFeatureBitMask != NULL) &&
      FindSpecifyFeature (FeatureList, &CpuFeature->Link, TRUE, CpuFeature->PackageAfterFeatureBitMask))
  {
    return PackageDepType;
  }

  if ((CpuFeature->CoreAfterFeatureBitMask != NULL) &&
      FindSpecifyFeature (FeatureList, &CpuFeature->Link, TRUE, CpuFeature->CoreAfterFeatureBitMask))
  {
    return CoreDepType;
  }

  if ((CpuFeature->ThreadAfterFeatureBitMask != NULL) &&
      FindSpecifyFeature (FeatureList, &CpuFeature->Link, TRUE, CpuFeature->ThreadAfterFeatureBitMask))
  {
    return ThreadDepType;
  }

  return NoneDepType;
}

/**
  Base on dependence relationship to asjust feature dependence.

  ONLY when the feature before(or after) the find feature also has
  dependence with the find feature. In this case, driver need to base
  on dependce relationship to decide how to insert current feature and
  adjust the feature dependence.

  @param[in, out]  PreviousFeature    CPU feature current before the find one.
  @param[in, out]  CurrentFeature     Cpu feature need to adjust.
  @param[in]       FindFeature        Cpu feature which current feature depends.
  @param[in]       Before             Before or after dependence relationship.

  @retval   TRUE   means the current feature dependence has been adjusted.

  @retval   FALSE  means the previous feature dependence has been adjusted.
                   or previous feature has no dependence with the find one.

**/
BOOLEAN
AdjustFeaturesDependence (
  IN OUT CPU_FEATURES_ENTRY  *PreviousFeature,
  IN OUT CPU_FEATURES_ENTRY  *CurrentFeature,
  IN     CPU_FEATURES_ENTRY  *FindFeature,
  IN     BOOLEAN             Before
  )
{
  CPU_FEATURE_DEPENDENCE_TYPE  PreDependType;
  CPU_FEATURE_DEPENDENCE_TYPE  CurrentDependType;

  PreDependType     = DetectFeatureScope (PreviousFeature, Before, FindFeature->FeatureMask);
  CurrentDependType = DetectFeatureScope (CurrentFeature, Before, FindFeature->FeatureMask);

  //
  // If previous feature has no dependence with the find featue.
  // return FALSE.
  //
  if (PreDependType == NoneDepType) {
    return FALSE;
  }

  //
  // If both feature have dependence, keep the one which needs use more
  // processors and clear the dependence for the other one.
  //
  if (PreDependType >= CurrentDependType) {
    return TRUE;
  } else {
    return FALSE;
  }
}

/**
  Base on dependence relationship to asjust feature order.

  @param[in]       FeatureList        Pointer to CPU feature list
  @param[in, out]  FindEntry          The entry this feature depend on.
  @param[in, out]  CurrentEntry       The entry for this feature.
  @param[in]       Before             Before or after dependence relationship.

**/
VOID
AdjustEntry (
  IN      LIST_ENTRY  *FeatureList,
  IN OUT  LIST_ENTRY  *FindEntry,
  IN OUT  LIST_ENTRY  *CurrentEntry,
  IN      BOOLEAN     Before
  )
{
  LIST_ENTRY          *PreviousEntry;
  CPU_FEATURES_ENTRY  *PreviousFeature;
  CPU_FEATURES_ENTRY  *CurrentFeature;
  CPU_FEATURES_ENTRY  *FindFeature;

  //
  // For CPU feature which has core or package type dependence, later code need to insert
  // AcquireSpinLock/ReleaseSpinLock logic to sequency the execute order.
  // So if driver finds both feature A and B need to execute before feature C, driver will
  // base on dependence type of feature A and B to update the logic here.
  // For example, feature A has package type dependence and feature B has core type dependence,
  // because package type dependence need to wait for more processors which has strong dependence
  // than core type dependence. So driver will adjust the feature order to B -> A -> C. and driver
  // will remove the feature dependence in feature B.
  // Driver just needs to make sure before feature C been executed, feature A has finished its task
  // in all all thread. Feature A finished in all threads also means feature B have finshed in all
  // threads.
  //
  if (Before) {
    PreviousEntry = GetPreviousNode (FeatureList, FindEntry);
  } else {
    PreviousEntry = GetNextNode (FeatureList, FindEntry);
  }

  CurrentFeature = CPU_FEATURE_ENTRY_FROM_LINK (CurrentEntry);
  RemoveEntryList (CurrentEntry);

  if (IsNull (FeatureList, PreviousEntry)) {
    //
    // If not exist the previous or next entry, just insert the current entry.
    //
    if (Before) {
      InsertTailList (FindEntry, CurrentEntry);
    } else {
      InsertHeadList (FindEntry, CurrentEntry);
    }
  } else {
    //
    // If exist the previous or next entry, need to check it before insert curent entry.
    //
    PreviousFeature = CPU_FEATURE_ENTRY_FROM_LINK (PreviousEntry);
    FindFeature     = CPU_FEATURE_ENTRY_FROM_LINK (FindEntry);

    if (AdjustFeaturesDependence (PreviousFeature, CurrentFeature, FindFeature, Before)) {
      //
      // Return TRUE means current feature dependence has been cleared and the previous
      // feature dependence has been kept and used. So insert current feature before (or after)
      // the previous feature.
      //
      if (Before) {
        InsertTailList (PreviousEntry, CurrentEntry);
      } else {
        InsertHeadList (PreviousEntry, CurrentEntry);
      }
    } else {
      if (Before) {
        InsertTailList (FindEntry, CurrentEntry);
      } else {
        InsertHeadList (FindEntry, CurrentEntry);
      }
    }
  }
}

/**
  Checks and adjusts current CPU features per dependency relationship.

  @param[in]  FeatureList        Pointer to CPU feature list
  @param[in]  CurrentEntry       Pointer to current checked CPU feature
  @param[in]  FeatureMask        The feature bit mask.

  @retval     return Swapped info.
**/
BOOLEAN
InsertToBeforeEntry (
  IN LIST_ENTRY  *FeatureList,
  IN LIST_ENTRY  *CurrentEntry,
  IN UINT8       *FeatureMask
  )
{
  LIST_ENTRY          *CheckEntry;
  CPU_FEATURES_ENTRY  *CheckFeature;
  BOOLEAN             Swapped;

  Swapped = FALSE;

  //
  // Check all features dispatched before this entry
  //
  CheckEntry = GetFirstNode (FeatureList);
  while (CheckEntry != CurrentEntry) {
    CheckFeature = CPU_FEATURE_ENTRY_FROM_LINK (CheckEntry);
    if (IsBitMaskMatchCheck (CheckFeature->FeatureMask, FeatureMask)) {
      AdjustEntry (FeatureList, CheckEntry, CurrentEntry, TRUE);
      Swapped = TRUE;
      break;
    }

    CheckEntry = CheckEntry->ForwardLink;
  }

  return Swapped;
}

/**
  Checks and adjusts current CPU features per dependency relationship.

  @param[in]  FeatureList        Pointer to CPU feature list
  @param[in]  CurrentEntry       Pointer to current checked CPU feature
  @param[in]  FeatureMask        The feature bit mask.

  @retval     return Swapped info.
**/
BOOLEAN
InsertToAfterEntry (
  IN LIST_ENTRY  *FeatureList,
  IN LIST_ENTRY  *CurrentEntry,
  IN UINT8       *FeatureMask
  )
{
  LIST_ENTRY          *CheckEntry;
  CPU_FEATURES_ENTRY  *CheckFeature;
  BOOLEAN             Swapped;

  Swapped = FALSE;

  //
  // Check all features dispatched after this entry
  //
  CheckEntry = GetNextNode (FeatureList, CurrentEntry);
  while (!IsNull (FeatureList, CheckEntry)) {
    CheckFeature = CPU_FEATURE_ENTRY_FROM_LINK (CheckEntry);
    if (IsBitMaskMatchCheck (CheckFeature->FeatureMask, FeatureMask)) {
      AdjustEntry (FeatureList, CheckEntry, CurrentEntry, FALSE);
      Swapped = TRUE;
      break;
    }

    CheckEntry = CheckEntry->ForwardLink;
  }

  return Swapped;
}

/**
  Checks and adjusts CPU features order per dependency relationship.

  @param[in]  FeatureList        Pointer to CPU feature list
**/
VOID
CheckCpuFeaturesDependency (
  IN LIST_ENTRY  *FeatureList
  )
{
  LIST_ENTRY          *CurrentEntry;
  CPU_FEATURES_ENTRY  *CpuFeature;
  LIST_ENTRY          *CheckEntry;
  CPU_FEATURES_ENTRY  *CheckFeature;
  BOOLEAN             Swapped;
  LIST_ENTRY          *TempEntry;
  LIST_ENTRY          *NextEntry;

  CurrentEntry = GetFirstNode (FeatureList);
  while (!IsNull (FeatureList, CurrentEntry)) {
    Swapped    = FALSE;
    CpuFeature = CPU_FEATURE_ENTRY_FROM_LINK (CurrentEntry);
    NextEntry  = CurrentEntry->ForwardLink;
    if (CpuFeature->BeforeAll) {
      //
      // Check all features dispatched before this entry
      //
      CheckEntry = GetFirstNode (FeatureList);
      while (CheckEntry != CurrentEntry) {
        CheckFeature = CPU_FEATURE_ENTRY_FROM_LINK (CheckEntry);
        if (!CheckFeature->BeforeAll) {
          //
          // If this feature has no BeforeAll flag and is dispatched before CpuFeature,
          // insert currentEntry before Checked feature
          //
          RemoveEntryList (CurrentEntry);
          InsertTailList (CheckEntry, CurrentEntry);
          Swapped = TRUE;
          break;
        }

        CheckEntry = CheckEntry->ForwardLink;
      }

      if (Swapped) {
        CurrentEntry = NextEntry;
        continue;
      }
    }

    if (CpuFeature->AfterAll) {
      //
      // Check all features dispatched after this entry
      //
      CheckEntry = GetNextNode (FeatureList, CurrentEntry);
      while (!IsNull (FeatureList, CheckEntry)) {
        CheckFeature = CPU_FEATURE_ENTRY_FROM_LINK (CheckEntry);
        if (!CheckFeature->AfterAll) {
          //
          // If this feature has no AfterAll flag and is dispatched after CpuFeature,
          // insert currentEntry after Checked feature
          //
          TempEntry = GetNextNode (FeatureList, CurrentEntry);
          RemoveEntryList (CurrentEntry);
          InsertHeadList (CheckEntry, CurrentEntry);
          CurrentEntry = TempEntry;
          Swapped      = TRUE;
          break;
        }

        CheckEntry = CheckEntry->ForwardLink;
      }

      if (Swapped) {
        CurrentEntry = NextEntry;
        continue;
      }
    }

    if (CpuFeature->ThreadBeforeFeatureBitMask != NULL) {
      Swapped = InsertToBeforeEntry (FeatureList, CurrentEntry, CpuFeature->ThreadBeforeFeatureBitMask);
      if (Swapped) {
        continue;
      }
    }

    if (CpuFeature->ThreadAfterFeatureBitMask != NULL) {
      Swapped = InsertToAfterEntry (FeatureList, CurrentEntry, CpuFeature->ThreadAfterFeatureBitMask);
      if (Swapped) {
        continue;
      }
    }

    if (CpuFeature->CoreBeforeFeatureBitMask != NULL) {
      Swapped = InsertToBeforeEntry (FeatureList, CurrentEntry, CpuFeature->CoreBeforeFeatureBitMask);
      if (Swapped) {
        continue;
      }
    }

    if (CpuFeature->CoreAfterFeatureBitMask != NULL) {
      Swapped = InsertToAfterEntry (FeatureList, CurrentEntry, CpuFeature->CoreAfterFeatureBitMask);
      if (Swapped) {
        continue;
      }
    }

    if (CpuFeature->PackageBeforeFeatureBitMask != NULL) {
      Swapped = InsertToBeforeEntry (FeatureList, CurrentEntry, CpuFeature->PackageBeforeFeatureBitMask);
      if (Swapped) {
        continue;
      }
    }

    if (CpuFeature->PackageAfterFeatureBitMask != NULL) {
      Swapped = InsertToAfterEntry (FeatureList, CurrentEntry, CpuFeature->PackageAfterFeatureBitMask);
      if (Swapped) {
        continue;
      }
    }

    CurrentEntry = CurrentEntry->ForwardLink;
  }
}

/**
  Worker function to register CPU Feature.

  @param[in]  CpuFeaturesData       Pointer to CPU feature data structure.
  @param[in]  CpuFeature            Pointer to CPU feature entry

  @retval  RETURN_SUCCESS           The CPU feature was successfully registered.
  @retval  RETURN_OUT_OF_RESOURCES  There are not enough resources to register
                                    the CPU feature.
  @retval  RETURN_UNSUPPORTED       Registration of the CPU feature is not
                                    supported due to a circular dependency between
                                    BEFORE and AFTER features.
**/
RETURN_STATUS
RegisterCpuFeatureWorker (
  IN CPU_FEATURES_DATA   *CpuFeaturesData,
  IN CPU_FEATURES_ENTRY  *CpuFeature
  )
{
  EFI_STATUS          Status;
  CPU_FEATURES_ENTRY  *CpuFeatureEntry;
  LIST_ENTRY          *Entry;
  BOOLEAN             FeatureExist;

  FeatureExist    = FALSE;
  CpuFeatureEntry = NULL;
  Entry           = GetFirstNode (&CpuFeaturesData->FeatureList);
  while (!IsNull (&CpuFeaturesData->FeatureList, Entry)) {
    CpuFeatureEntry = CPU_FEATURE_ENTRY_FROM_LINK (Entry);
    if (CompareMem (CpuFeature->FeatureMask, CpuFeatureEntry->FeatureMask, CpuFeaturesData->BitMaskSize) == 0) {
      //
      // If this feature already registered
      //
      FeatureExist = TRUE;
      break;
    }

    Entry = Entry->ForwardLink;
  }

  if (!FeatureExist) {
    DEBUG ((DEBUG_INFO, "[NEW] "));
    DumpCpuFeature (CpuFeature, CpuFeaturesData->BitMaskSize);
    InsertTailList (&CpuFeaturesData->FeatureList, &CpuFeature->Link);
    CpuFeaturesData->FeaturesCount++;
  } else {
    DEBUG ((DEBUG_INFO, "[OVERRIDE] "));
    DumpCpuFeature (CpuFeature, CpuFeaturesData->BitMaskSize);
    ASSERT (CpuFeatureEntry != NULL);
    //
    // Overwrite original parameters of CPU feature
    //
    if (CpuFeature->GetConfigDataFunc != NULL) {
      CpuFeatureEntry->GetConfigDataFunc = CpuFeature->GetConfigDataFunc;
    }

    if (CpuFeature->SupportFunc != NULL) {
      CpuFeatureEntry->SupportFunc = CpuFeature->SupportFunc;
    }

    if (CpuFeature->InitializeFunc != NULL) {
      CpuFeatureEntry->InitializeFunc = CpuFeature->InitializeFunc;
    }

    if (CpuFeature->FeatureName != NULL) {
      if (CpuFeatureEntry->FeatureName == NULL) {
        CpuFeatureEntry->FeatureName = AllocatePool (CPU_FEATURE_NAME_SIZE);
        ASSERT (CpuFeatureEntry->FeatureName != NULL);
      }

      Status = AsciiStrCpyS (CpuFeatureEntry->FeatureName, CPU_FEATURE_NAME_SIZE, CpuFeature->FeatureName);
      ASSERT_EFI_ERROR (Status);
      FreePool (CpuFeature->FeatureName);
    }

    if (CpuFeature->ThreadBeforeFeatureBitMask != NULL) {
      if (CpuFeatureEntry->ThreadBeforeFeatureBitMask != NULL) {
        FreePool (CpuFeatureEntry->ThreadBeforeFeatureBitMask);
      }

      CpuFeatureEntry->ThreadBeforeFeatureBitMask = CpuFeature->ThreadBeforeFeatureBitMask;
    }

    if (CpuFeature->ThreadAfterFeatureBitMask != NULL) {
      if (CpuFeatureEntry->ThreadAfterFeatureBitMask != NULL) {
        FreePool (CpuFeatureEntry->ThreadAfterFeatureBitMask);
      }

      CpuFeatureEntry->ThreadAfterFeatureBitMask = CpuFeature->ThreadAfterFeatureBitMask;
    }

    if (CpuFeature->CoreBeforeFeatureBitMask != NULL) {
      if (CpuFeatureEntry->CoreBeforeFeatureBitMask != NULL) {
        FreePool (CpuFeatureEntry->CoreBeforeFeatureBitMask);
      }

      CpuFeatureEntry->CoreBeforeFeatureBitMask = CpuFeature->CoreBeforeFeatureBitMask;
    }

    if (CpuFeature->CoreAfterFeatureBitMask != NULL) {
      if (CpuFeatureEntry->CoreAfterFeatureBitMask != NULL) {
        FreePool (CpuFeatureEntry->CoreAfterFeatureBitMask);
      }

      CpuFeatureEntry->CoreAfterFeatureBitMask = CpuFeature->CoreAfterFeatureBitMask;
    }

    if (CpuFeature->PackageBeforeFeatureBitMask != NULL) {
      if (CpuFeatureEntry->PackageBeforeFeatureBitMask != NULL) {
        FreePool (CpuFeatureEntry->PackageBeforeFeatureBitMask);
      }

      CpuFeatureEntry->PackageBeforeFeatureBitMask = CpuFeature->PackageBeforeFeatureBitMask;
    }

    if (CpuFeature->PackageAfterFeatureBitMask != NULL) {
      if (CpuFeatureEntry->PackageAfterFeatureBitMask != NULL) {
        FreePool (CpuFeatureEntry->PackageAfterFeatureBitMask);
      }

      CpuFeatureEntry->PackageAfterFeatureBitMask = CpuFeature->PackageAfterFeatureBitMask;
    }

    CpuFeatureEntry->BeforeAll = CpuFeature->BeforeAll;
    CpuFeatureEntry->AfterAll  = CpuFeature->AfterAll;

    FreePool (CpuFeature->FeatureMask);
    FreePool (CpuFeature);
  }

  //
  // Verify CPU features dependency can change CPU feature order
  //
  CheckCpuFeaturesDependency (&CpuFeaturesData->FeatureList);
  return RETURN_SUCCESS;
}

/**
  Sets CPU feature bit mask in CPU feature bit mask buffer.

  @param[in]  FeaturesBitMask       Pointer to CPU feature bit mask buffer
  @param[in]  Feature               The bit number of the CPU feature
  @param[in]  BitMaskSize           CPU feature bit mask buffer size
**/
VOID
SetCpuFeaturesBitMask (
  IN UINT8   **FeaturesBitMask,
  IN UINT32  Feature,
  IN UINTN   BitMaskSize
  )
{
  UINT8  *CpuFeaturesBitMask;

  ASSERT (FeaturesBitMask != NULL);
  CpuFeaturesBitMask = *FeaturesBitMask;
  if (CpuFeaturesBitMask == NULL) {
    CpuFeaturesBitMask = AllocateZeroPool (BitMaskSize);
    ASSERT (CpuFeaturesBitMask != NULL);
    *FeaturesBitMask = CpuFeaturesBitMask;
  }

  CpuFeaturesBitMask  += (Feature / 8);
  *CpuFeaturesBitMask |= (UINT8)(1 << (Feature % 8));
}

/**
  Registers a CPU Feature.

  @param[in]  FeatureName        A Null-terminated Ascii string indicates CPU feature
                                 name.
  @param[in]  GetConfigDataFunc  CPU feature get configuration data function.  This
                                 is an optional parameter that may be NULL.  If NULL,
                                 then the most recently registered function for the
                                 CPU feature is used.  If no functions are registered
                                 for a CPU feature, then the CPU configuration data
                                 for the registered feature is NULL.
  @param[in]  SupportFunc        CPU feature support function.  This is an optional
                                 parameter that may be NULL.  If NULL, then the most
                                 recently registered function for the CPU feature is
                                 used. If no functions are registered for a CPU
                                 feature, then the CPU feature is assumed to be
                                 supported by all CPUs.
  @param[in]  InitializeFunc     CPU feature initialize function.  This is an optional
                                 parameter that may be NULL.  If NULL, then the most
                                 recently registered function for the CPU feature is
                                 used. If no functions are registered for a CPU
                                 feature, then the CPU feature initialization is
                                 skipped.
  @param[in]  ...                Variable argument list of UINT32 CPU feature value.
                                 Values with no modifiers are the features provided
                                 by the registered functions.
                                 Values with CPU_FEATURE_BEFORE modifier are features
                                 that must be initialized after the features provided
                                 by the registered functions are used.
                                 Values with CPU_FEATURE_AFTER modifier are features
                                 that must be initialized before the features provided
                                 by the registered functions are used.
                                 The last argument in this variable argument list must
                                 always be CPU_FEATURE_END.

  @retval  RETURN_SUCCESS           The CPU feature was successfully registered.
  @retval  RETURN_OUT_OF_RESOURCES  There are not enough resources to register
                                    the CPU feature.
  @retval  RETURN_UNSUPPORTED       Registration of the CPU feature is not
                                    supported due to a circular dependency between
                                    BEFORE and AFTER features.
  @retval  RETURN_NOT_READY         CPU feature PCD PcdCpuFeaturesUserConfiguration
                                    not updated by Platform driver yet.

  @note This service could be called by BSP only.
**/
RETURN_STATUS
EFIAPI
RegisterCpuFeature (
  IN CHAR8                        *FeatureName        OPTIONAL,
  IN CPU_FEATURE_GET_CONFIG_DATA  GetConfigDataFunc   OPTIONAL,
  IN CPU_FEATURE_SUPPORT          SupportFunc         OPTIONAL,
  IN CPU_FEATURE_INITIALIZE       InitializeFunc      OPTIONAL,
  ...
  )
{
  EFI_STATUS          Status;
  VA_LIST             Marker;
  UINT32              Feature;
  CPU_FEATURES_ENTRY  *CpuFeature;
  UINT8               *FeatureMask;
  UINT8               *ThreadBeforeFeatureBitMask;
  UINT8               *ThreadAfterFeatureBitMask;
  UINT8               *CoreBeforeFeatureBitMask;
  UINT8               *CoreAfterFeatureBitMask;
  UINT8               *PackageBeforeFeatureBitMask;
  UINT8               *PackageAfterFeatureBitMask;
  BOOLEAN             BeforeAll;
  BOOLEAN             AfterAll;
  CPU_FEATURES_DATA   *CpuFeaturesData;

  FeatureMask                 = NULL;
  ThreadBeforeFeatureBitMask  = NULL;
  ThreadAfterFeatureBitMask   = NULL;
  CoreBeforeFeatureBitMask    = NULL;
  CoreAfterFeatureBitMask     = NULL;
  PackageBeforeFeatureBitMask = NULL;
  PackageAfterFeatureBitMask  = NULL;
  BeforeAll                   = FALSE;
  AfterAll                    = FALSE;

  CpuFeaturesData = GetCpuFeaturesData ();
  if (CpuFeaturesData->FeaturesCount == 0) {
    InitializeListHead (&CpuFeaturesData->FeatureList);
    InitializeSpinLock (&CpuFeaturesData->CpuFlags.MemoryMappedLock);
    //
    // Code assumes below three PCDs have PCD same buffer size.
    //
    ASSERT (PcdGetSize (PcdCpuFeaturesSetting) == PcdGetSize (PcdCpuFeaturesCapability));
    ASSERT (PcdGetSize (PcdCpuFeaturesSetting) == PcdGetSize (PcdCpuFeaturesSupport));
    CpuFeaturesData->BitMaskSize = (UINT32)PcdGetSize (PcdCpuFeaturesSetting);
  }

  VA_START (Marker, InitializeFunc);
  Feature = VA_ARG (Marker, UINT32);
  while (Feature != CPU_FEATURE_END) {
    //
    // It's invalid to require a feature is before AND after all other features.
    //
    ASSERT (
      (Feature & (CPU_FEATURE_BEFORE_ALL | CPU_FEATURE_AFTER_ALL))
      != (CPU_FEATURE_BEFORE_ALL | CPU_FEATURE_AFTER_ALL)
      );

    //
    // It's invalid to require feature A is before AND after before feature B,
    // either in thread level, core level or package level.
    //
    ASSERT (
      (Feature & (CPU_FEATURE_THREAD_BEFORE | CPU_FEATURE_THREAD_AFTER))
      != (CPU_FEATURE_THREAD_BEFORE | CPU_FEATURE_THREAD_AFTER)
      );
    ASSERT (
      (Feature & (CPU_FEATURE_CORE_BEFORE | CPU_FEATURE_CORE_AFTER))
      != (CPU_FEATURE_CORE_BEFORE | CPU_FEATURE_CORE_AFTER)
      );
    ASSERT (
      (Feature & (CPU_FEATURE_PACKAGE_BEFORE | CPU_FEATURE_PACKAGE_AFTER))
      != (CPU_FEATURE_PACKAGE_BEFORE | CPU_FEATURE_PACKAGE_AFTER)
      );
    if (Feature < CPU_FEATURE_THREAD_BEFORE) {
      BeforeAll = ((Feature & CPU_FEATURE_BEFORE_ALL) != 0) ? TRUE : FALSE;
      AfterAll  = ((Feature & CPU_FEATURE_AFTER_ALL) != 0) ? TRUE : FALSE;
      Feature  &= ~(CPU_FEATURE_BEFORE_ALL | CPU_FEATURE_AFTER_ALL);
      ASSERT (FeatureMask == NULL);
      SetCpuFeaturesBitMask (&FeatureMask, Feature, CpuFeaturesData->BitMaskSize);
    } else if ((Feature & CPU_FEATURE_THREAD_BEFORE) != 0) {
      SetCpuFeaturesBitMask (&ThreadBeforeFeatureBitMask, Feature & ~CPU_FEATURE_THREAD_BEFORE, CpuFeaturesData->BitMaskSize);
    } else if ((Feature & CPU_FEATURE_THREAD_AFTER) != 0) {
      SetCpuFeaturesBitMask (&ThreadAfterFeatureBitMask, Feature & ~CPU_FEATURE_THREAD_AFTER, CpuFeaturesData->BitMaskSize);
    } else if ((Feature & CPU_FEATURE_CORE_BEFORE) != 0) {
      SetCpuFeaturesBitMask (&CoreBeforeFeatureBitMask, Feature & ~CPU_FEATURE_CORE_BEFORE, CpuFeaturesData->BitMaskSize);
    } else if ((Feature & CPU_FEATURE_CORE_AFTER) != 0) {
      SetCpuFeaturesBitMask (&CoreAfterFeatureBitMask, Feature & ~CPU_FEATURE_CORE_AFTER, CpuFeaturesData->BitMaskSize);
    } else if ((Feature & CPU_FEATURE_PACKAGE_BEFORE) != 0) {
      SetCpuFeaturesBitMask (&PackageBeforeFeatureBitMask, Feature & ~CPU_FEATURE_PACKAGE_BEFORE, CpuFeaturesData->BitMaskSize);
    } else if ((Feature & CPU_FEATURE_PACKAGE_AFTER) != 0) {
      SetCpuFeaturesBitMask (&PackageAfterFeatureBitMask, Feature & ~CPU_FEATURE_PACKAGE_AFTER, CpuFeaturesData->BitMaskSize);
    }

    Feature = VA_ARG (Marker, UINT32);
  }

  VA_END (Marker);

  CpuFeature = AllocateZeroPool (sizeof (CPU_FEATURES_ENTRY));
  ASSERT (CpuFeature != NULL);
  CpuFeature->Signature                   = CPU_FEATURE_ENTRY_SIGNATURE;
  CpuFeature->FeatureMask                 = FeatureMask;
  CpuFeature->ThreadBeforeFeatureBitMask  = ThreadBeforeFeatureBitMask;
  CpuFeature->ThreadAfterFeatureBitMask   = ThreadAfterFeatureBitMask;
  CpuFeature->CoreBeforeFeatureBitMask    = CoreBeforeFeatureBitMask;
  CpuFeature->CoreAfterFeatureBitMask     = CoreAfterFeatureBitMask;
  CpuFeature->PackageBeforeFeatureBitMask = PackageBeforeFeatureBitMask;
  CpuFeature->PackageAfterFeatureBitMask  = PackageAfterFeatureBitMask;
  CpuFeature->BeforeAll                   = BeforeAll;
  CpuFeature->AfterAll                    = AfterAll;
  CpuFeature->GetConfigDataFunc           = GetConfigDataFunc;
  CpuFeature->SupportFunc                 = SupportFunc;
  CpuFeature->InitializeFunc              = InitializeFunc;
  if (FeatureName != NULL) {
    CpuFeature->FeatureName = AllocatePool (CPU_FEATURE_NAME_SIZE);
    ASSERT (CpuFeature->FeatureName != NULL);
    Status = AsciiStrCpyS (CpuFeature->FeatureName, CPU_FEATURE_NAME_SIZE, FeatureName);
    ASSERT_EFI_ERROR (Status);
  }

  Status = RegisterCpuFeatureWorker (CpuFeaturesData, CpuFeature);
  ASSERT_EFI_ERROR (Status);

  return RETURN_SUCCESS;
}

/**
  Return ACPI_CPU_DATA data.

  @return  Pointer to ACPI_CPU_DATA data.
           NULL if the ACPI CPU data structure cannot be allocated.

**/
ACPI_CPU_DATA *
GetAcpiCpuData (
  VOID
  )
{
  EFI_STATUS                 Status;
  UINTN                      NumberOfCpus;
  UINTN                      NumberOfEnabledProcessors;
  ACPI_CPU_DATA              *AcpiCpuData;
  UINTN                      TableSize;
  CPU_REGISTER_TABLE         *RegisterTable;
  UINTN                      Index;
  EFI_PROCESSOR_INFORMATION  ProcessorInfoBuffer;

  AcpiCpuData = (ACPI_CPU_DATA *)(UINTN)PcdGet64 (PcdCpuS3DataAddress);
  if (AcpiCpuData == NULL) {
    AcpiCpuData = AllocatePages (EFI_SIZE_TO_PAGES (sizeof (ACPI_CPU_DATA)));
    if (AcpiCpuData == NULL) {
      ASSERT (AcpiCpuData != NULL);
      return NULL;
    }

    ZeroMem (AcpiCpuData, sizeof (ACPI_CPU_DATA));

    //
    // Set PcdCpuS3DataAddress to the base address of the ACPI_CPU_DATA structure
    //
    Status = PcdSet64S (PcdCpuS3DataAddress, (UINT64)(UINTN)AcpiCpuData);
    ASSERT_EFI_ERROR (Status);

    GetNumberOfProcessor (&NumberOfCpus, &NumberOfEnabledProcessors);
    AcpiCpuData->NumberOfCpus = (UINT32)NumberOfCpus;
  }

  if ((AcpiCpuData->CpuFeatureInitData.RegisterTable == 0) ||
      (AcpiCpuData->CpuFeatureInitData.PreSmmInitRegisterTable == 0))
  {
    //
    // Allocate buffer for empty RegisterTable and PreSmmInitRegisterTable for all CPUs
    //
    NumberOfCpus  = AcpiCpuData->NumberOfCpus;
    TableSize     = 2 * NumberOfCpus * sizeof (CPU_REGISTER_TABLE);
    RegisterTable = AllocatePages (EFI_SIZE_TO_PAGES (TableSize));
    if (RegisterTable == NULL) {
      // Leave the AcpiCpuData data buffer allocated since it was assigned to a dynamic PCD
      // which could have invoked PCD set callbacks that may have cached the buffer.
      ASSERT (RegisterTable != NULL);
      return NULL;
    }

    for (Index = 0; Index < NumberOfCpus; Index++) {
      Status = GetProcessorInformation (Index, &ProcessorInfoBuffer);
      ASSERT_EFI_ERROR (Status);

      RegisterTable[Index].InitialApicId      = (UINT32)ProcessorInfoBuffer.ProcessorId;
      RegisterTable[Index].TableLength        = 0;
      RegisterTable[Index].AllocatedSize      = 0;
      RegisterTable[Index].RegisterTableEntry = 0;

      RegisterTable[NumberOfCpus + Index].InitialApicId      = (UINT32)ProcessorInfoBuffer.ProcessorId;
      RegisterTable[NumberOfCpus + Index].TableLength        = 0;
      RegisterTable[NumberOfCpus + Index].AllocatedSize      = 0;
      RegisterTable[NumberOfCpus + Index].RegisterTableEntry = 0;
    }

    if (AcpiCpuData->CpuFeatureInitData.RegisterTable == 0) {
      AcpiCpuData->CpuFeatureInitData.RegisterTable = (EFI_PHYSICAL_ADDRESS)(UINTN)RegisterTable;
    }

    if (AcpiCpuData->CpuFeatureInitData.PreSmmInitRegisterTable == 0) {
      AcpiCpuData->CpuFeatureInitData.PreSmmInitRegisterTable = (EFI_PHYSICAL_ADDRESS)(UINTN)(RegisterTable + NumberOfCpus);
    }
  }

  return AcpiCpuData;
}

/**
  Enlarges CPU register table for each processor.

  @param[in, out]  RegisterTable   Pointer processor's CPU register table
**/
STATIC
VOID
EnlargeRegisterTable (
  IN OUT CPU_REGISTER_TABLE  *RegisterTable
  )
{
  EFI_PHYSICAL_ADDRESS  Address;
  UINTN                 UsedPages;

  UsedPages = RegisterTable->AllocatedSize / EFI_PAGE_SIZE;
  Address   = (UINTN)AllocatePages (UsedPages + 1);
  ASSERT (Address != 0);

  //
  // If there are records existing in the register table, then copy its contents
  // to new region and free the old one.
  //
  if (RegisterTable->AllocatedSize > 0) {
    CopyMem (
      (VOID *)(UINTN)Address,
      (VOID *)(UINTN)RegisterTable->RegisterTableEntry,
      RegisterTable->AllocatedSize
      );

    FreePages ((VOID *)(UINTN)RegisterTable->RegisterTableEntry, UsedPages);
  }

  //
  // Adjust the allocated size and register table base address.
  //
  RegisterTable->AllocatedSize     += EFI_PAGE_SIZE;
  RegisterTable->RegisterTableEntry = Address;
}

/**
  Add an entry in specified register table.

  This function adds an entry in specified register table, with given register type,
  register index, bit section and value.

  @param[in]  PreSmmFlag       If TRUE, entry will be added into PreSmm register table
                               If FALSE, entry will be added into register table
  @param[in]  ProcessorNumber  The index of the CPU to add a register table entry
  @param[in]  RegisterType     Type of the register to program
  @param[in]  Index            Index of the register to program
  @param[in]  ValidBitStart    Start of the bit section
  @param[in]  ValidBitLength   Length of the bit section
  @param[in]  Value            Value to write
  @param[in]  TestThenWrite    Whether need to test current Value before writing.

**/
VOID
CpuRegisterTableWriteWorker (
  IN BOOLEAN        PreSmmFlag,
  IN UINTN          ProcessorNumber,
  IN REGISTER_TYPE  RegisterType,
  IN UINT64         Index,
  IN UINT8          ValidBitStart,
  IN UINT8          ValidBitLength,
  IN UINT64         Value,
  IN BOOLEAN        TestThenWrite
  )
{
  CPU_FEATURES_DATA         *CpuFeaturesData;
  ACPI_CPU_DATA             *AcpiCpuData;
  CPU_REGISTER_TABLE        *RegisterTable;
  CPU_REGISTER_TABLE_ENTRY  *RegisterTableEntry;

  CpuFeaturesData = GetCpuFeaturesData ();
  if (CpuFeaturesData->RegisterTable == NULL) {
    AcpiCpuData = GetAcpiCpuData ();
    if (AcpiCpuData == NULL) {
      ASSERT (AcpiCpuData != NULL);
      return;
    }

    ASSERT (AcpiCpuData->CpuFeatureInitData.RegisterTable != 0);
    CpuFeaturesData->RegisterTable       = (CPU_REGISTER_TABLE *)(UINTN)AcpiCpuData->CpuFeatureInitData.RegisterTable;
    CpuFeaturesData->PreSmmRegisterTable = (CPU_REGISTER_TABLE *)(UINTN)AcpiCpuData->CpuFeatureInitData.PreSmmInitRegisterTable;
  }

  if (PreSmmFlag) {
    RegisterTable = &CpuFeaturesData->PreSmmRegisterTable[ProcessorNumber];
  } else {
    RegisterTable = &CpuFeaturesData->RegisterTable[ProcessorNumber];
  }

  if (RegisterTable->TableLength == RegisterTable->AllocatedSize / sizeof (CPU_REGISTER_TABLE_ENTRY)) {
    EnlargeRegisterTable (RegisterTable);
  }

  //
  // Append entry in the register table.
  //
  RegisterTableEntry                                            = (CPU_REGISTER_TABLE_ENTRY *)(UINTN)RegisterTable->RegisterTableEntry;
  RegisterTableEntry[RegisterTable->TableLength].RegisterType   = RegisterType;
  RegisterTableEntry[RegisterTable->TableLength].Index          = (UINT32)Index;
  RegisterTableEntry[RegisterTable->TableLength].HighIndex      = (UINT32)RShiftU64 (Index, 32);
  RegisterTableEntry[RegisterTable->TableLength].ValidBitStart  = ValidBitStart;
  RegisterTableEntry[RegisterTable->TableLength].ValidBitLength = ValidBitLength;
  RegisterTableEntry[RegisterTable->TableLength].Value          = Value;
  RegisterTableEntry[RegisterTable->TableLength].TestThenWrite  = TestThenWrite;

  RegisterTable->TableLength++;
}

/**
  Adds an entry in specified register table.

  This function adds an entry in specified register table, with given register type,
  register index, bit section and value.

  @param[in]  ProcessorNumber  The index of the CPU to add a register table entry
  @param[in]  RegisterType     Type of the register to program
  @param[in]  Index            Index of the register to program
  @param[in]  ValueMask        Mask of bits in register to write
  @param[in]  Value            Value to write

  @note This service could be called by BSP only.
**/
VOID
EFIAPI
CpuRegisterTableWrite (
  IN UINTN          ProcessorNumber,
  IN REGISTER_TYPE  RegisterType,
  IN UINT64         Index,
  IN UINT64         ValueMask,
  IN UINT64         Value
  )
{
  UINT8  Start;
  UINT8  End;
  UINT8  Length;

  Start  = (UINT8)LowBitSet64 (ValueMask);
  End    = (UINT8)HighBitSet64 (ValueMask);
  Length = End - Start + 1;
  CpuRegisterTableWriteWorker (FALSE, ProcessorNumber, RegisterType, Index, Start, Length, Value, FALSE);
}

/**
  Adds an entry in specified register table.

  This function adds an entry in specified register table, with given register type,
  register index, bit section and value.

  @param[in]  ProcessorNumber  The index of the CPU to add a register table entry
  @param[in]  RegisterType     Type of the register to program
  @param[in]  Index            Index of the register to program
  @param[in]  ValueMask        Mask of bits in register to write
  @param[in]  Value            Value to write

  @note This service could be called by BSP only.
**/
VOID
EFIAPI
CpuRegisterTableTestThenWrite (
  IN UINTN          ProcessorNumber,
  IN REGISTER_TYPE  RegisterType,
  IN UINT64         Index,
  IN UINT64         ValueMask,
  IN UINT64         Value
  )
{
  UINT8  Start;
  UINT8  End;
  UINT8  Length;

  Start  = (UINT8)LowBitSet64 (ValueMask);
  End    = (UINT8)HighBitSet64 (ValueMask);
  Length = End - Start + 1;
  CpuRegisterTableWriteWorker (FALSE, ProcessorNumber, RegisterType, Index, Start, Length, Value, TRUE);
}

/**
  Adds an entry in specified Pre-SMM register table.

  This function adds an entry in specified register table, with given register type,
  register index, bit section and value.

  @param[in]  ProcessorNumber  The index of the CPU to add a register table entry.
  @param[in]  RegisterType     Type of the register to program
  @param[in]  Index            Index of the register to program
  @param[in]  ValueMask        Mask of bits in register to write
  @param[in]  Value            Value to write

  @note This service could be called by BSP only.
**/
VOID
EFIAPI
PreSmmCpuRegisterTableWrite (
  IN UINTN          ProcessorNumber,
  IN REGISTER_TYPE  RegisterType,
  IN UINT64         Index,
  IN UINT64         ValueMask,
  IN UINT64         Value
  )
{
  UINT8  Start;
  UINT8  End;
  UINT8  Length;

  Start  = (UINT8)LowBitSet64 (ValueMask);
  End    = (UINT8)HighBitSet64 (ValueMask);
  Length = End - Start + 1;
  CpuRegisterTableWriteWorker (TRUE, ProcessorNumber, RegisterType, Index, Start, Length, Value, FALSE);
}

/**
  Worker function to determine if a CPU feature is set in input CPU feature bit mask buffer.

  @param[in]  CpuBitMask      CPU feature bit mask buffer
  @param[in]  CpuBitMaskSize  The size of CPU feature bit mask buffer
  @param[in]  Feature         The bit number of the CPU feature

  @retval  TRUE   The CPU feature is set in CpuBitMask.
  @retval  FALSE  The CPU feature is not set in CpuBitMask.

**/
BOOLEAN
IsCpuFeatureSetInCpuPcd (
  IN UINT8   *CpuBitMask,
  IN UINTN   CpuBitMaskSize,
  IN UINT32  Feature
  )
{
  if ((Feature >> 3) >= CpuBitMaskSize) {
    return FALSE;
  }

  return ((*(CpuBitMask + (Feature >> 3)) & (1 << (Feature & 0x07))) != 0);
}

/**
  Determines if a CPU feature is enabled in PcdCpuFeaturesSupport bit mask.
  If a CPU feature is disabled in PcdCpuFeaturesSupport then all the code/data
  associated with that feature should be optimized away if compiler
  optimizations are enabled.

  @param[in]  Feature  The bit number of the CPU feature to check in the PCD
                       PcdCpuFeaturesSupport

  @retval  TRUE   The CPU feature is set in PcdCpuFeaturesSupport.
  @retval  FALSE  The CPU feature is not set in PcdCpuFeaturesSupport.

  @note This service could be called by BSP only.
**/
BOOLEAN
EFIAPI
IsCpuFeatureSupported (
  IN UINT32  Feature
  )
{
  return IsCpuFeatureSetInCpuPcd (
           (UINT8 *)PcdGetPtr (PcdCpuFeaturesSupport),
           PcdGetSize (PcdCpuFeaturesSupport),
           Feature
           );
}

/**
  Determines if a CPU feature is set in PcdCpuFeaturesSetting bit mask.

  @param[in]  Feature  The bit number of the CPU feature to check in the PCD
                       PcdCpuFeaturesSetting

  @retval  TRUE   The CPU feature is set in PcdCpuFeaturesSetting.
  @retval  FALSE  The CPU feature is not set in PcdCpuFeaturesSetting.

  @note This service could be called by BSP only.
**/
BOOLEAN
EFIAPI
IsCpuFeatureInSetting (
  IN UINT32  Feature
  )
{
  return IsCpuFeatureSetInCpuPcd (
           (UINT8 *)PcdGetPtr (PcdCpuFeaturesSetting),
           PcdGetSize (PcdCpuFeaturesSetting),
           Feature
           );
}

/**
  Switches to assigned BSP after CPU features initialization.

  @param[in]  ProcessorNumber  The index of the CPU executing this function.

  @note This service could be called by BSP only.
**/
VOID
EFIAPI
SwitchBspAfterFeaturesInitialize (
  IN UINTN  ProcessorNumber
  )
{
  CPU_FEATURES_DATA  *CpuFeaturesData;

  CpuFeaturesData            = GetCpuFeaturesData ();
  CpuFeaturesData->BspNumber = ProcessorNumber;
}
