/** @file
  MTRR setting library

  @par Note:
    Most of services in this library instance are suggested to be invoked by BSP only,
    except for MtrrSetAllMtrrs() which is used to sync BSP's MTRR setting to APs.

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

**/

#include <Uefi.h>
#include <Register/Intel/Cpuid.h>
#include <Register/Intel/Msr.h>

#include <Library/MtrrLib.h>
#include <Library/BaseLib.h>
#include <Library/CpuLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/DebugLib.h>

#define OR_SEED              0x0101010101010101ull
#define CLEAR_SEED           0xFFFFFFFFFFFFFFFFull
#define MAX_WEIGHT           MAX_UINT8
#define SCRATCH_BUFFER_SIZE  (4 * SIZE_4KB)
#define MTRR_LIB_ASSERT_ALIGNED(B, L)  ASSERT ((B & ~(L - 1)) == B);

#define M(x, y)  ((x) * VertexCount + (y))
#define O(x, y)  ((y) * VertexCount + (x))

//
// Context to save and restore when MTRRs are programmed
//
typedef struct {
  UINTN      Cr4;
  BOOLEAN    InterruptState;
} MTRR_CONTEXT;

typedef struct {
  UINT64                    Address;
  UINT64                    Alignment;
  UINT64                    Length;
  MTRR_MEMORY_CACHE_TYPE    Type    : 7;

  //
  // Temprary use for calculating the best MTRR settings.
  //
  BOOLEAN                   Visited : 1;
  UINT8                     Weight;
  UINT16                    Previous;
} MTRR_LIB_ADDRESS;

//
// This table defines the offset, base and length of the fixed MTRRs
//
CONST FIXED_MTRR  mMtrrLibFixedMtrrTable[] = {
  {
    MSR_IA32_MTRR_FIX64K_00000,
    0,
    SIZE_64KB
  },
  {
    MSR_IA32_MTRR_FIX16K_80000,
    0x80000,
    SIZE_16KB
  },
  {
    MSR_IA32_MTRR_FIX16K_A0000,
    0xA0000,
    SIZE_16KB
  },
  {
    MSR_IA32_MTRR_FIX4K_C0000,
    0xC0000,
    SIZE_4KB
  },
  {
    MSR_IA32_MTRR_FIX4K_C8000,
    0xC8000,
    SIZE_4KB
  },
  {
    MSR_IA32_MTRR_FIX4K_D0000,
    0xD0000,
    SIZE_4KB
  },
  {
    MSR_IA32_MTRR_FIX4K_D8000,
    0xD8000,
    SIZE_4KB
  },
  {
    MSR_IA32_MTRR_FIX4K_E0000,
    0xE0000,
    SIZE_4KB
  },
  {
    MSR_IA32_MTRR_FIX4K_E8000,
    0xE8000,
    SIZE_4KB
  },
  {
    MSR_IA32_MTRR_FIX4K_F0000,
    0xF0000,
    SIZE_4KB
  },
  {
    MSR_IA32_MTRR_FIX4K_F8000,
    0xF8000,
    SIZE_4KB
  }
};

//
// Lookup table used to print MTRRs
//
GLOBAL_REMOVE_IF_UNREFERENCED CONST CHAR8  *mMtrrMemoryCacheTypeShortName[] = {
  "UC",  // CacheUncacheable
  "WC",  // CacheWriteCombining
  "R*",  // Invalid
  "R*",  // Invalid
  "WT",  // CacheWriteThrough
  "WP",  // CacheWriteProtected
  "WB",  // CacheWriteBack
  "R*"   // Invalid
};

/**
  Worker function prints all MTRRs for debugging.

  If MtrrSetting is not NULL, print MTRR settings from input MTRR
  settings buffer.
  If MtrrSetting is NULL, print MTRR settings from MTRRs.

  @param  MtrrSetting    A buffer holding all MTRRs content.
**/
VOID
MtrrDebugPrintAllMtrrsWorker (
  IN MTRR_SETTINGS  *MtrrSetting
  );

/**
  Worker function returns the variable MTRR count for the CPU.

  @return Variable MTRR count

**/
UINT32
GetVariableMtrrCountWorker (
  VOID
  )
{
  MSR_IA32_MTRRCAP_REGISTER  MtrrCap;

  MtrrCap.Uint64 = AsmReadMsr64 (MSR_IA32_MTRRCAP);
  ASSERT (MtrrCap.Bits.VCNT <= ARRAY_SIZE (((MTRR_VARIABLE_SETTINGS *)0)->Mtrr));
  return MtrrCap.Bits.VCNT;
}

/**
  Returns the variable MTRR count for the CPU.

  @return Variable MTRR count

**/
UINT32
EFIAPI
GetVariableMtrrCount (
  VOID
  )
{
  if (!IsMtrrSupported ()) {
    return 0;
  }

  return GetVariableMtrrCountWorker ();
}

/**
  Worker function returns the firmware usable variable MTRR count for the CPU.

  @return Firmware usable variable MTRR count

**/
UINT32
GetFirmwareVariableMtrrCountWorker (
  VOID
  )
{
  UINT32  VariableMtrrCount;
  UINT32  ReservedMtrrNumber;

  VariableMtrrCount  = GetVariableMtrrCountWorker ();
  ReservedMtrrNumber = PcdGet32 (PcdCpuNumberOfReservedVariableMtrrs);
  if (VariableMtrrCount < ReservedMtrrNumber) {
    return 0;
  }

  return VariableMtrrCount - ReservedMtrrNumber;
}

/**
  Returns the firmware usable variable MTRR count for the CPU.

  @return Firmware usable variable MTRR count

**/
UINT32
EFIAPI
GetFirmwareVariableMtrrCount (
  VOID
  )
{
  if (!IsMtrrSupported ()) {
    return 0;
  }

  return GetFirmwareVariableMtrrCountWorker ();
}

/**
  Worker function returns the default MTRR cache type for the system.

  If MtrrSetting is not NULL, returns the default MTRR cache type from input
  MTRR settings buffer.
  If MtrrSetting is NULL, returns the default MTRR cache type from MSR.

  @param[in]  MtrrSetting    A buffer holding all MTRRs content.

  @return  The default MTRR cache type.

**/
MTRR_MEMORY_CACHE_TYPE
MtrrGetDefaultMemoryTypeWorker (
  IN MTRR_SETTINGS  *MtrrSetting
  )
{
  MSR_IA32_MTRR_DEF_TYPE_REGISTER  DefType;

  if (MtrrSetting == NULL) {
    DefType.Uint64 = AsmReadMsr64 (MSR_IA32_MTRR_DEF_TYPE);
  } else {
    DefType.Uint64 = MtrrSetting->MtrrDefType;
  }

  return (MTRR_MEMORY_CACHE_TYPE)DefType.Bits.Type;
}

/**
  Returns the default MTRR cache type for the system.

  @return  The default MTRR cache type.

**/
MTRR_MEMORY_CACHE_TYPE
EFIAPI
MtrrGetDefaultMemoryType (
  VOID
  )
{
  if (!IsMtrrSupported ()) {
    return CacheUncacheable;
  }

  return MtrrGetDefaultMemoryTypeWorker (NULL);
}

/**
  Preparation before programming MTRR.

  This function will do some preparation for programming MTRRs:
  disable cache, invalid cache and disable MTRR caching functionality

  @param[out] MtrrContext  Pointer to context to save

**/
VOID
MtrrLibPreMtrrChange (
  OUT MTRR_CONTEXT  *MtrrContext
  )
{
  MSR_IA32_MTRR_DEF_TYPE_REGISTER  DefType;

  //
  // Disable interrupts and save current interrupt state
  //
  MtrrContext->InterruptState = SaveAndDisableInterrupts ();

  //
  // Enter no fill cache mode, CD=1(Bit30), NW=0 (Bit29)
  //
  AsmDisableCache ();

  //
  // Save original CR4 value and clear PGE flag (Bit 7)
  //
  MtrrContext->Cr4 = AsmReadCr4 ();
  AsmWriteCr4 (MtrrContext->Cr4 & (~BIT7));

  //
  // Flush all TLBs
  //
  CpuFlushTlb ();

  //
  // Disable MTRRs
  //
  DefType.Uint64 = AsmReadMsr64 (MSR_IA32_MTRR_DEF_TYPE);
  DefType.Bits.E = 0;
  AsmWriteMsr64 (MSR_IA32_MTRR_DEF_TYPE, DefType.Uint64);
}

/**
  Cleaning up after programming MTRRs.

  This function will do some clean up after programming MTRRs:
  Flush all TLBs,  re-enable caching, restore CR4.

  @param[in] MtrrContext  Pointer to context to restore

**/
VOID
MtrrLibPostMtrrChangeEnableCache (
  IN MTRR_CONTEXT  *MtrrContext
  )
{
  //
  // Flush all TLBs
  //
  CpuFlushTlb ();

  //
  // Enable Normal Mode caching CD=NW=0, CD(Bit30), NW(Bit29)
  //
  AsmEnableCache ();

  //
  // Restore original CR4 value
  //
  AsmWriteCr4 (MtrrContext->Cr4);

  //
  // Restore original interrupt state
  //
  SetInterruptState (MtrrContext->InterruptState);
}

/**
  Cleaning up after programming MTRRs.

  This function will do some clean up after programming MTRRs:
  enable MTRR caching functionality, and enable cache

  @param[in] MtrrContext  Pointer to context to restore

**/
VOID
MtrrLibPostMtrrChange (
  IN MTRR_CONTEXT  *MtrrContext
  )
{
  MSR_IA32_MTRR_DEF_TYPE_REGISTER  DefType;

  //
  // Enable Cache MTRR
  //
  DefType.Uint64  = AsmReadMsr64 (MSR_IA32_MTRR_DEF_TYPE);
  DefType.Bits.E  = 1;
  DefType.Bits.FE = 1;
  AsmWriteMsr64 (MSR_IA32_MTRR_DEF_TYPE, DefType.Uint64);

  MtrrLibPostMtrrChangeEnableCache (MtrrContext);
}

/**
  Worker function gets the content in fixed MTRRs

  @param[out]  FixedSettings  A buffer to hold fixed MTRRs content.

  @retval The pointer of FixedSettings

**/
MTRR_FIXED_SETTINGS *
MtrrGetFixedMtrrWorker (
  OUT MTRR_FIXED_SETTINGS  *FixedSettings
  )
{
  UINT32  Index;

  for (Index = 0; Index < MTRR_NUMBER_OF_FIXED_MTRR; Index++) {
    FixedSettings->Mtrr[Index] =
      AsmReadMsr64 (mMtrrLibFixedMtrrTable[Index].Msr);
  }

  return FixedSettings;
}

/**
  This function gets the content in fixed MTRRs

  @param[out]  FixedSettings  A buffer to hold fixed MTRRs content.

  @retval The pointer of FixedSettings

**/
MTRR_FIXED_SETTINGS *
EFIAPI
MtrrGetFixedMtrr (
  OUT MTRR_FIXED_SETTINGS  *FixedSettings
  )
{
  if (!IsMtrrSupported ()) {
    return FixedSettings;
  }

  return MtrrGetFixedMtrrWorker (FixedSettings);
}

/**
  Worker function will get the raw value in variable MTRRs

  If MtrrSetting is not NULL, gets the variable MTRRs raw value from input
  MTRR settings buffer.
  If MtrrSetting is NULL, gets the variable MTRRs raw value from MTRRs.

  @param[in]  MtrrSetting        A buffer holding all MTRRs content.
  @param[in]  VariableMtrrCount  Number of variable MTRRs.
  @param[out] VariableSettings   A buffer to hold variable MTRRs content.

  @return The VariableSettings input pointer

**/
MTRR_VARIABLE_SETTINGS *
MtrrGetVariableMtrrWorker (
  IN  MTRR_SETTINGS           *MtrrSetting,
  IN  UINT32                  VariableMtrrCount,
  OUT MTRR_VARIABLE_SETTINGS  *VariableSettings
  )
{
  UINT32  Index;

  ASSERT (VariableMtrrCount <= ARRAY_SIZE (VariableSettings->Mtrr));

  for (Index = 0; Index < VariableMtrrCount; Index++) {
    if (MtrrSetting == NULL) {
      VariableSettings->Mtrr[Index].Base =
        AsmReadMsr64 (MSR_IA32_MTRR_PHYSBASE0 + (Index << 1));
      VariableSettings->Mtrr[Index].Mask =
        AsmReadMsr64 (MSR_IA32_MTRR_PHYSMASK0 + (Index << 1));
    } else {
      VariableSettings->Mtrr[Index].Base = MtrrSetting->Variables.Mtrr[Index].Base;
      VariableSettings->Mtrr[Index].Mask = MtrrSetting->Variables.Mtrr[Index].Mask;
    }
  }

  return VariableSettings;
}

/**
  Programs fixed MTRRs registers.

  @param[in]      Type             The memory type to set.
  @param[in, out] Base             The base address of memory range.
  @param[in, out] Length           The length of memory range.
  @param[in, out] LastMsrIndex     On input, the last index of the fixed MTRR MSR to program.
                                   On return, the current index of the fixed MTRR MSR to program.
  @param[out]     ClearMask        The bits to clear in the fixed MTRR MSR.
  @param[out]     OrMask           The bits to set in the fixed MTRR MSR.

  @retval RETURN_SUCCESS      The cache type was updated successfully
  @retval RETURN_UNSUPPORTED  The requested range or cache type was invalid
                              for the fixed MTRRs.

**/
RETURN_STATUS
MtrrLibProgramFixedMtrr (
  IN     MTRR_MEMORY_CACHE_TYPE  Type,
  IN OUT UINT64                  *Base,
  IN OUT UINT64                  *Length,
  IN OUT UINT32                  *LastMsrIndex,
  OUT    UINT64                  *ClearMask,
  OUT    UINT64                  *OrMask
  )
{
  UINT32  MsrIndex;
  UINT32  LeftByteShift;
  UINT32  RightByteShift;
  UINT64  SubLength;

  //
  // Find the fixed MTRR index to be programmed
  //
  for (MsrIndex = *LastMsrIndex + 1; MsrIndex < ARRAY_SIZE (mMtrrLibFixedMtrrTable); MsrIndex++) {
    if ((*Base >= mMtrrLibFixedMtrrTable[MsrIndex].BaseAddress) &&
        (*Base <
         (
          mMtrrLibFixedMtrrTable[MsrIndex].BaseAddress +
          (8 * mMtrrLibFixedMtrrTable[MsrIndex].Length)
         )
        )
        )
    {
      break;
    }
  }

  ASSERT (MsrIndex != ARRAY_SIZE (mMtrrLibFixedMtrrTable));

  //
  // Find the begin offset in fixed MTRR and calculate byte offset of left shift
  //
  if ((((UINT32)*Base - mMtrrLibFixedMtrrTable[MsrIndex].BaseAddress) % mMtrrLibFixedMtrrTable[MsrIndex].Length) != 0) {
    //
    // Base address should be aligned to the begin of a certain Fixed MTRR range.
    //
    return RETURN_UNSUPPORTED;
  }

  LeftByteShift = ((UINT32)*Base - mMtrrLibFixedMtrrTable[MsrIndex].BaseAddress) / mMtrrLibFixedMtrrTable[MsrIndex].Length;
  ASSERT (LeftByteShift < 8);

  //
  // Find the end offset in fixed MTRR and calculate byte offset of right shift
  //
  SubLength = mMtrrLibFixedMtrrTable[MsrIndex].Length * (8 - LeftByteShift);
  if (*Length >= SubLength) {
    RightByteShift = 0;
  } else {
    if (((UINT32)(*Length) % mMtrrLibFixedMtrrTable[MsrIndex].Length) != 0) {
      //
      // Length should be aligned to the end of a certain Fixed MTRR range.
      //
      return RETURN_UNSUPPORTED;
    }

    RightByteShift = 8 - LeftByteShift - (UINT32)(*Length) / mMtrrLibFixedMtrrTable[MsrIndex].Length;
    //
    // Update SubLength by actual length
    //
    SubLength = *Length;
  }

  *ClearMask = CLEAR_SEED;
  *OrMask    = MultU64x32 (OR_SEED, (UINT32)Type);

  if (LeftByteShift != 0) {
    //
    // Clear the low bits by LeftByteShift
    //
    *ClearMask &= LShiftU64 (*ClearMask, LeftByteShift * 8);
    *OrMask    &= LShiftU64 (*OrMask, LeftByteShift * 8);
  }

  if (RightByteShift != 0) {
    //
    // Clear the high bits by RightByteShift
    //
    *ClearMask &= RShiftU64 (*ClearMask, RightByteShift * 8);
    *OrMask    &= RShiftU64 (*OrMask, RightByteShift * 8);
  }

  *Length -= SubLength;
  *Base   += SubLength;

  *LastMsrIndex = MsrIndex;

  return RETURN_SUCCESS;
}

/**
  Worker function gets the attribute of variable MTRRs.

  This function shadows the content of variable MTRRs into an
  internal array: VariableMtrr.

  @param[in]   VariableSettings      The variable MTRR values to shadow
  @param[in]   VariableMtrrCount     The number of variable MTRRs
  @param[in]   MtrrValidBitsMask     The mask for the valid bit of the MTRR
  @param[in]   MtrrValidAddressMask  The valid address mask for MTRR
  @param[out]  VariableMtrr          The array to shadow variable MTRRs content

  @return      Number of MTRRs which has been used.

**/
UINT32
MtrrGetMemoryAttributeInVariableMtrrWorker (
  IN  MTRR_VARIABLE_SETTINGS  *VariableSettings,
  IN  UINTN                   VariableMtrrCount,
  IN  UINT64                  MtrrValidBitsMask,
  IN  UINT64                  MtrrValidAddressMask,
  OUT VARIABLE_MTRR           *VariableMtrr
  )
{
  UINTN   Index;
  UINT32  UsedMtrr;

  ZeroMem (VariableMtrr, sizeof (VARIABLE_MTRR) * ARRAY_SIZE (VariableSettings->Mtrr));
  for (Index = 0, UsedMtrr = 0; Index < VariableMtrrCount; Index++) {
    if (((MSR_IA32_MTRR_PHYSMASK_REGISTER *)&VariableSettings->Mtrr[Index].Mask)->Bits.V != 0) {
      VariableMtrr[Index].Msr         = (UINT32)Index;
      VariableMtrr[Index].BaseAddress = (VariableSettings->Mtrr[Index].Base & MtrrValidAddressMask);
      VariableMtrr[Index].Length      =
        ((~(VariableSettings->Mtrr[Index].Mask & MtrrValidAddressMask)) & MtrrValidBitsMask) + 1;
      VariableMtrr[Index].Type  = (VariableSettings->Mtrr[Index].Base & 0x0ff);
      VariableMtrr[Index].Valid = TRUE;
      VariableMtrr[Index].Used  = TRUE;
      UsedMtrr++;
    }
  }

  return UsedMtrr;
}

/**
  Convert variable MTRRs to a RAW MTRR_MEMORY_RANGE array.
  One MTRR_MEMORY_RANGE element is created for each MTRR setting.
  The routine doesn't remove the overlap or combine the near-by region.

  @param[in]   VariableSettings      The variable MTRR values to shadow
  @param[in]   VariableMtrrCount     The number of variable MTRRs
  @param[in]   MtrrValidBitsMask     The mask for the valid bit of the MTRR
  @param[in]   MtrrValidAddressMask  The valid address mask for MTRR
  @param[out]  VariableMtrr          The array to shadow variable MTRRs content

  @return      Number of MTRRs which has been used.

**/
UINT32
MtrrLibGetRawVariableRanges (
  IN  MTRR_VARIABLE_SETTINGS  *VariableSettings,
  IN  UINTN                   VariableMtrrCount,
  IN  UINT64                  MtrrValidBitsMask,
  IN  UINT64                  MtrrValidAddressMask,
  OUT MTRR_MEMORY_RANGE       *VariableMtrr
  )
{
  UINTN   Index;
  UINT32  UsedMtrr;

  ZeroMem (VariableMtrr, sizeof (MTRR_MEMORY_RANGE) * ARRAY_SIZE (VariableSettings->Mtrr));
  for (Index = 0, UsedMtrr = 0; Index < VariableMtrrCount; Index++) {
    if (((MSR_IA32_MTRR_PHYSMASK_REGISTER *)&VariableSettings->Mtrr[Index].Mask)->Bits.V != 0) {
      VariableMtrr[Index].BaseAddress = (VariableSettings->Mtrr[Index].Base & MtrrValidAddressMask);
      VariableMtrr[Index].Length      =
        ((~(VariableSettings->Mtrr[Index].Mask & MtrrValidAddressMask)) & MtrrValidBitsMask) + 1;
      VariableMtrr[Index].Type = (MTRR_MEMORY_CACHE_TYPE)(VariableSettings->Mtrr[Index].Base & 0x0ff);
      UsedMtrr++;
    }
  }

  return UsedMtrr;
}

/**
  Gets the attribute of variable MTRRs.

  This function shadows the content of variable MTRRs into an
  internal array: VariableMtrr.

  @param[in]   MtrrValidBitsMask     The mask for the valid bit of the MTRR
  @param[in]   MtrrValidAddressMask  The valid address mask for MTRR
  @param[out]  VariableMtrr          The array to shadow variable MTRRs content

  @return                       The return value of this parameter indicates the
                                number of MTRRs which has been used.

**/
UINT32
EFIAPI
MtrrGetMemoryAttributeInVariableMtrr (
  IN  UINT64         MtrrValidBitsMask,
  IN  UINT64         MtrrValidAddressMask,
  OUT VARIABLE_MTRR  *VariableMtrr
  )
{
  MTRR_VARIABLE_SETTINGS  VariableSettings;

  if (!IsMtrrSupported ()) {
    return 0;
  }

  MtrrGetVariableMtrrWorker (
    NULL,
    GetVariableMtrrCountWorker (),
    &VariableSettings
    );

  return MtrrGetMemoryAttributeInVariableMtrrWorker (
           &VariableSettings,
           GetFirmwareVariableMtrrCountWorker (),
           MtrrValidBitsMask,
           MtrrValidAddressMask,
           VariableMtrr
           );
}

/**
  Return the biggest alignment (lowest set bit) of address.
  The function is equivalent to: 1 << LowBitSet64 (Address).

  @param Address    The address to return the alignment.
  @param Alignment0 The alignment to return when Address is 0.

  @return The least alignment of the Address.
**/
UINT64
MtrrLibBiggestAlignment (
  UINT64  Address,
  UINT64  Alignment0
  )
{
  if (Address == 0) {
    return Alignment0;
  }

  return Address & ((~Address) + 1);
}

/**
  Return whether the left MTRR type precedes the right MTRR type.

  The MTRR type precedence rules are:
    1. UC precedes any other type
    2. WT precedes WB
  For further details, please refer the IA32 Software Developer's Manual,
  Volume 3, Section "MTRR Precedences".

  @param Left  The left MTRR type.
  @param Right The right MTRR type.

  @retval TRUE  Left precedes Right.
  @retval FALSE Left doesn't precede Right.
**/
BOOLEAN
MtrrLibTypeLeftPrecedeRight (
  IN MTRR_MEMORY_CACHE_TYPE  Left,
  IN MTRR_MEMORY_CACHE_TYPE  Right
  )
{
  return (BOOLEAN)(Left == CacheUncacheable || (Left == CacheWriteThrough && Right == CacheWriteBack));
}

/**
  Initializes the valid bits mask and valid address mask for MTRRs.

  This function initializes the valid bits mask and valid address mask for MTRRs.

  @param[out]  MtrrValidBitsMask     The mask for the valid bit of the MTRR
  @param[out]  MtrrValidAddressMask  The valid address mask for the MTRR

**/
VOID
MtrrLibInitializeMtrrMask (
  OUT UINT64  *MtrrValidBitsMask,
  OUT UINT64  *MtrrValidAddressMask
  )
{
  UINT32                                       MaxExtendedFunction;
  CPUID_VIR_PHY_ADDRESS_SIZE_EAX               VirPhyAddressSize;
  UINT32                                       MaxFunction;
  CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS_ECX  ExtendedFeatureFlagsEcx;
  MSR_IA32_TME_ACTIVATE_REGISTER               TmeActivate;

  AsmCpuid (CPUID_EXTENDED_FUNCTION, &MaxExtendedFunction, NULL, NULL, NULL);

  if (MaxExtendedFunction >= CPUID_VIR_PHY_ADDRESS_SIZE) {
    AsmCpuid (CPUID_VIR_PHY_ADDRESS_SIZE, &VirPhyAddressSize.Uint32, NULL, NULL, NULL);
  } else {
    VirPhyAddressSize.Bits.PhysicalAddressBits = 36;
  }

  //
  // CPUID enumeration of MAX_PA is unaffected by TME-MK activation and will continue
  // to report the maximum physical address bits available for software to use,
  // irrespective of the number of KeyID bits.
  // So, we need to check if TME is enabled and adjust the PA size accordingly.
  //
  AsmCpuid (CPUID_SIGNATURE, &MaxFunction, NULL, NULL, NULL);
  if (MaxFunction >= CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS) {
    AsmCpuidEx (CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS, 0, NULL, NULL, &ExtendedFeatureFlagsEcx.Uint32, NULL);
    if (ExtendedFeatureFlagsEcx.Bits.TME_EN == 1) {
      TmeActivate.Uint64 = AsmReadMsr64 (MSR_IA32_TME_ACTIVATE);
      if (TmeActivate.Bits.TmeEnable == 1) {
        VirPhyAddressSize.Bits.PhysicalAddressBits -= TmeActivate.Bits.MkTmeKeyidBits;
      }
    }
  }

  *MtrrValidBitsMask    = LShiftU64 (1, VirPhyAddressSize.Bits.PhysicalAddressBits) - 1;
  *MtrrValidAddressMask = *MtrrValidBitsMask & 0xfffffffffffff000ULL;
}

/**
  Determines the real attribute of a memory range.

  This function is to arbitrate the real attribute of the memory when
  there are 2 MTRRs covers the same memory range. For further details,
  please refer the IA32 Software Developer's Manual, Volume 3,
  Section "MTRR Precedences".

  @param[in]  MtrrType1    The first kind of Memory type
  @param[in]  MtrrType2    The second kind of memory type

**/
MTRR_MEMORY_CACHE_TYPE
MtrrLibPrecedence (
  IN MTRR_MEMORY_CACHE_TYPE  MtrrType1,
  IN MTRR_MEMORY_CACHE_TYPE  MtrrType2
  )
{
  if (MtrrType1 == MtrrType2) {
    return MtrrType1;
  }

  ASSERT (
    MtrrLibTypeLeftPrecedeRight (MtrrType1, MtrrType2) ||
    MtrrLibTypeLeftPrecedeRight (MtrrType2, MtrrType1)
    );

  if (MtrrLibTypeLeftPrecedeRight (MtrrType1, MtrrType2)) {
    return MtrrType1;
  } else {
    return MtrrType2;
  }
}

/**
  Worker function will get the memory cache type of the specific address.

  If MtrrSetting is not NULL, gets the memory cache type from input
  MTRR settings buffer.
  If MtrrSetting is NULL, gets the memory cache type from MTRRs.

  @param[in]  MtrrSetting        A buffer holding all MTRRs content.
  @param[in]  Address            The specific address

  @return Memory cache type of the specific address

**/
MTRR_MEMORY_CACHE_TYPE
MtrrGetMemoryAttributeByAddressWorker (
  IN MTRR_SETTINGS     *MtrrSetting,
  IN PHYSICAL_ADDRESS  Address
  )
{
  MSR_IA32_MTRR_DEF_TYPE_REGISTER  DefType;
  UINT64                           FixedMtrr;
  UINTN                            Index;
  UINTN                            SubIndex;
  MTRR_MEMORY_CACHE_TYPE           MtrrType;
  MTRR_MEMORY_RANGE                VariableMtrr[ARRAY_SIZE (MtrrSetting->Variables.Mtrr)];
  UINT64                           MtrrValidBitsMask;
  UINT64                           MtrrValidAddressMask;
  UINT32                           VariableMtrrCount;
  MTRR_VARIABLE_SETTINGS           VariableSettings;

  //
  // Check if MTRR is enabled, if not, return UC as attribute
  //
  if (MtrrSetting == NULL) {
    DefType.Uint64 = AsmReadMsr64 (MSR_IA32_MTRR_DEF_TYPE);
  } else {
    DefType.Uint64 = MtrrSetting->MtrrDefType;
  }

  if (DefType.Bits.E == 0) {
    return CacheUncacheable;
  }

  //
  // If address is less than 1M, then try to go through the fixed MTRR
  //
  if (Address < BASE_1MB) {
    if (DefType.Bits.FE != 0) {
      //
      // Go through the fixed MTRR
      //
      for (Index = 0; Index < MTRR_NUMBER_OF_FIXED_MTRR; Index++) {
        if ((Address >= mMtrrLibFixedMtrrTable[Index].BaseAddress) &&
            (Address < mMtrrLibFixedMtrrTable[Index].BaseAddress +
             (mMtrrLibFixedMtrrTable[Index].Length * 8)))
        {
          SubIndex =
            ((UINTN)Address - mMtrrLibFixedMtrrTable[Index].BaseAddress) /
            mMtrrLibFixedMtrrTable[Index].Length;
          if (MtrrSetting == NULL) {
            FixedMtrr = AsmReadMsr64 (mMtrrLibFixedMtrrTable[Index].Msr);
          } else {
            FixedMtrr = MtrrSetting->Fixed.Mtrr[Index];
          }

          return (MTRR_MEMORY_CACHE_TYPE)(RShiftU64 (FixedMtrr, SubIndex * 8) & 0xFF);
        }
      }
    }
  }

  VariableMtrrCount = GetVariableMtrrCountWorker ();
  ASSERT (VariableMtrrCount <= ARRAY_SIZE (MtrrSetting->Variables.Mtrr));
  MtrrGetVariableMtrrWorker (MtrrSetting, VariableMtrrCount, &VariableSettings);

  MtrrLibInitializeMtrrMask (&MtrrValidBitsMask, &MtrrValidAddressMask);
  MtrrLibGetRawVariableRanges (
    &VariableSettings,
    VariableMtrrCount,
    MtrrValidBitsMask,
    MtrrValidAddressMask,
    VariableMtrr
    );

  //
  // Go through the variable MTRR
  //
  MtrrType = CacheInvalid;
  for (Index = 0; Index < VariableMtrrCount; Index++) {
    if (VariableMtrr[Index].Length != 0) {
      if ((Address >= VariableMtrr[Index].BaseAddress) &&
          (Address < VariableMtrr[Index].BaseAddress + VariableMtrr[Index].Length))
      {
        if (MtrrType == CacheInvalid) {
          MtrrType = (MTRR_MEMORY_CACHE_TYPE)VariableMtrr[Index].Type;
        } else {
          MtrrType = MtrrLibPrecedence (MtrrType, (MTRR_MEMORY_CACHE_TYPE)VariableMtrr[Index].Type);
        }
      }
    }
  }

  //
  // If there is no MTRR which covers the Address, use the default MTRR type.
  //
  if (MtrrType == CacheInvalid) {
    MtrrType = (MTRR_MEMORY_CACHE_TYPE)DefType.Bits.Type;
  }

  return MtrrType;
}

/**
  This function will get the memory cache type of the specific address.

  This function is mainly for debug purpose.

  @param[in]  Address   The specific address

  @return Memory cache type of the specific address

**/
MTRR_MEMORY_CACHE_TYPE
EFIAPI
MtrrGetMemoryAttribute (
  IN PHYSICAL_ADDRESS  Address
  )
{
  if (!IsMtrrSupported ()) {
    return CacheUncacheable;
  }

  return MtrrGetMemoryAttributeByAddressWorker (NULL, Address);
}

/**
  Update the Ranges array to change the specified range identified by
  BaseAddress and Length to Type.

  @param Ranges      Array holding memory type settings for all memory regions.
  @param Capacity    The maximum count of memory ranges the array can hold.
  @param Count       Return the new memory range count in the array.
  @param BaseAddress The base address of the memory range to change type.
  @param Length      The length of the memory range to change type.
  @param Type        The new type of the specified memory range.

  @retval RETURN_SUCCESS          The type of the specified memory range is
                                  changed successfully.
  @retval RETURN_ALREADY_STARTED  The type of the specified memory range equals
                                  to the desired type.
  @retval RETURN_OUT_OF_RESOURCES The new type set causes the count of memory
                                  range exceeds capacity.
**/
RETURN_STATUS
MtrrLibSetMemoryType (
  IN MTRR_MEMORY_RANGE       *Ranges,
  IN UINTN                   Capacity,
  IN OUT UINTN               *Count,
  IN UINT64                  BaseAddress,
  IN UINT64                  Length,
  IN MTRR_MEMORY_CACHE_TYPE  Type
  )
{
  UINTN   Index;
  UINT64  Limit;
  UINT64  LengthLeft;
  UINT64  LengthRight;
  UINTN   StartIndex;
  UINTN   EndIndex;
  UINTN   DeltaCount;

  LengthRight = 0;
  LengthLeft  = 0;
  Limit       = BaseAddress + Length;
  StartIndex  = *Count;
  EndIndex    = *Count;
  for (Index = 0; Index < *Count; Index++) {
    if ((StartIndex == *Count) &&
        (Ranges[Index].BaseAddress <= BaseAddress) &&
        (BaseAddress < Ranges[Index].BaseAddress + Ranges[Index].Length))
    {
      StartIndex = Index;
      LengthLeft = BaseAddress - Ranges[Index].BaseAddress;
    }

    if ((EndIndex == *Count) &&
        (Ranges[Index].BaseAddress < Limit) &&
        (Limit <= Ranges[Index].BaseAddress + Ranges[Index].Length))
    {
      EndIndex    = Index;
      LengthRight = Ranges[Index].BaseAddress + Ranges[Index].Length - Limit;
      break;
    }
  }

  ASSERT (StartIndex != *Count && EndIndex != *Count);
  if ((StartIndex == EndIndex) && (Ranges[StartIndex].Type == Type)) {
    return RETURN_ALREADY_STARTED;
  }

  //
  // The type change may cause merging with previous range or next range.
  // Update the StartIndex, EndIndex, BaseAddress, Length so that following
  // logic doesn't need to consider merging.
  //
  if (StartIndex != 0) {
    if ((LengthLeft == 0) && (Ranges[StartIndex - 1].Type == Type)) {
      StartIndex--;
      Length      += Ranges[StartIndex].Length;
      BaseAddress -= Ranges[StartIndex].Length;
    }
  }

  if (EndIndex != (*Count) - 1) {
    if ((LengthRight == 0) && (Ranges[EndIndex + 1].Type == Type)) {
      EndIndex++;
      Length += Ranges[EndIndex].Length;
    }
  }

  //
  // |- 0 -|- 1 -|- 2 -|- 3 -| StartIndex EndIndex DeltaCount  Count (Count = 4)
  //   |++++++++++++++++++|    0          3         1=3-0-2    3
  //   |+++++++|               0          1        -1=1-0-2    5
  //   |+|                     0          0        -2=0-0-2    6
  // |+++|                     0          0        -1=0-0-2+1  5
  //
  //
  DeltaCount = EndIndex - StartIndex - 2;
  if (LengthLeft == 0) {
    DeltaCount++;
  }

  if (LengthRight == 0) {
    DeltaCount++;
  }

  if (*Count - DeltaCount > Capacity) {
    return RETURN_OUT_OF_RESOURCES;
  }

  //
  // Reserve (-DeltaCount) space
  //
  CopyMem (&Ranges[EndIndex + 1 - DeltaCount], &Ranges[EndIndex + 1], (*Count - EndIndex - 1) * sizeof (Ranges[0]));
  *Count -= DeltaCount;

  if (LengthLeft != 0) {
    Ranges[StartIndex].Length = LengthLeft;
    StartIndex++;
  }

  if (LengthRight != 0) {
    Ranges[EndIndex - DeltaCount].BaseAddress = BaseAddress + Length;
    Ranges[EndIndex - DeltaCount].Length      = LengthRight;
    Ranges[EndIndex - DeltaCount].Type        = Ranges[EndIndex].Type;
  }

  Ranges[StartIndex].BaseAddress = BaseAddress;
  Ranges[StartIndex].Length      = Length;
  Ranges[StartIndex].Type        = Type;
  return RETURN_SUCCESS;
}

/**
  Return the number of memory types in range [BaseAddress, BaseAddress + Length).

  @param Ranges      Array holding memory type settings for all memory regions.
  @param RangeCount  The count of memory ranges the array holds.
  @param BaseAddress Base address.
  @param Length      Length.
  @param Types       Return bit mask to indicate all memory types in the specified range.

  @retval  Number of memory types.
**/
UINT8
MtrrLibGetNumberOfTypes (
  IN CONST MTRR_MEMORY_RANGE  *Ranges,
  IN UINTN                    RangeCount,
  IN UINT64                   BaseAddress,
  IN UINT64                   Length,
  IN OUT UINT8                *Types  OPTIONAL
  )
{
  UINTN  Index;
  UINT8  TypeCount;
  UINT8  LocalTypes;

  TypeCount  = 0;
  LocalTypes = 0;
  for (Index = 0; Index < RangeCount; Index++) {
    if ((Ranges[Index].BaseAddress <= BaseAddress) &&
        (BaseAddress < Ranges[Index].BaseAddress + Ranges[Index].Length)
        )
    {
      if ((LocalTypes & (1 << Ranges[Index].Type)) == 0) {
        LocalTypes |= (UINT8)(1 << Ranges[Index].Type);
        TypeCount++;
      }

      if (BaseAddress + Length > Ranges[Index].BaseAddress + Ranges[Index].Length) {
        Length     -= Ranges[Index].BaseAddress + Ranges[Index].Length - BaseAddress;
        BaseAddress = Ranges[Index].BaseAddress + Ranges[Index].Length;
      } else {
        break;
      }
    }
  }

  if (Types != NULL) {
    *Types = LocalTypes;
  }

  return TypeCount;
}

/**
  Calculate the least MTRR number from vertex Start to Stop and update
  the Previous of all vertices from Start to Stop is updated to reflect
  how the memory range is covered by MTRR.

  @param VertexCount     The count of vertices in the graph.
  @param Vertices        Array holding all vertices.
  @param Weight          2-dimention array holding weights between vertices.
  @param Start           Start vertex.
  @param Stop            Stop vertex.
  @param IncludeOptional TRUE to count the optional weight.
**/
VOID
MtrrLibCalculateLeastMtrrs (
  IN UINT16            VertexCount,
  IN MTRR_LIB_ADDRESS  *Vertices,
  IN OUT CONST UINT8   *Weight,
  IN UINT16            Start,
  IN UINT16            Stop,
  IN BOOLEAN           IncludeOptional
  )
{
  UINT16  Index;
  UINT8   MinWeight;
  UINT16  MinI;
  UINT8   Mandatory;
  UINT8   Optional;

  for (Index = Start; Index <= Stop; Index++) {
    Vertices[Index].Visited = FALSE;
    Mandatory               = Weight[M (Start, Index)];
    Vertices[Index].Weight  = Mandatory;
    if (Mandatory != MAX_WEIGHT) {
      Optional                = IncludeOptional ? Weight[O (Start, Index)] : 0;
      Vertices[Index].Weight += Optional;
      ASSERT (Vertices[Index].Weight >= Optional);
    }
  }

  MinI      = Start;
  MinWeight = 0;
  while (!Vertices[Stop].Visited) {
    //
    // Update the weight from the shortest vertex to other unvisited vertices
    //
    for (Index = Start + 1; Index <= Stop; Index++) {
      if (!Vertices[Index].Visited) {
        Mandatory = Weight[M (MinI, Index)];
        if (Mandatory != MAX_WEIGHT) {
          Optional = IncludeOptional ? Weight[O (MinI, Index)] : 0;
          if (MinWeight + Mandatory + Optional <= Vertices[Index].Weight) {
            Vertices[Index].Weight   = MinWeight + Mandatory + Optional;
            Vertices[Index].Previous = MinI; // Previous is Start based.
          }
        }
      }
    }

    //
    // Find the shortest vertex from Start
    //
    MinI      = VertexCount;
    MinWeight = MAX_WEIGHT;
    for (Index = Start + 1; Index <= Stop; Index++) {
      if (!Vertices[Index].Visited && (MinWeight > Vertices[Index].Weight)) {
        MinI      = Index;
        MinWeight = Vertices[Index].Weight;
      }
    }

    //
    // Mark the shortest vertex from Start as visited
    //
    Vertices[MinI].Visited = TRUE;
  }
}

/**
  Append the MTRR setting to MTRR setting array.

  @param Mtrrs        Array holding all MTRR settings.
  @param MtrrCapacity Capacity of the MTRR array.
  @param MtrrCount    The count of MTRR settings in array.
  @param BaseAddress  Base address.
  @param Length       Length.
  @param Type         Memory type.

  @retval RETURN_SUCCESS          MTRR setting is appended to array.
  @retval RETURN_OUT_OF_RESOURCES Array is full.
**/
RETURN_STATUS
MtrrLibAppendVariableMtrr (
  IN OUT MTRR_MEMORY_RANGE       *Mtrrs,
  IN     UINT32                  MtrrCapacity,
  IN OUT UINT32                  *MtrrCount,
  IN     UINT64                  BaseAddress,
  IN     UINT64                  Length,
  IN     MTRR_MEMORY_CACHE_TYPE  Type
  )
{
  if (*MtrrCount == MtrrCapacity) {
    return RETURN_OUT_OF_RESOURCES;
  }

  Mtrrs[*MtrrCount].BaseAddress = BaseAddress;
  Mtrrs[*MtrrCount].Length      = Length;
  Mtrrs[*MtrrCount].Type        = Type;
  (*MtrrCount)++;
  return RETURN_SUCCESS;
}

/**
  Return the memory type that has the least precedence.

  @param TypeBits  Bit mask of memory type.

  @retval  Memory type that has the least precedence.
**/
MTRR_MEMORY_CACHE_TYPE
MtrrLibLowestType (
  IN      UINT8  TypeBits
  )
{
  INT8  Type;

  ASSERT (TypeBits != 0);
  for (Type = 7; (INT8)TypeBits > 0; Type--, TypeBits <<= 1) {
  }

  return (MTRR_MEMORY_CACHE_TYPE)Type;
}

/**
  Calculate the subtractive path from vertex Start to Stop.

  @param DefaultType  Default memory type.
  @param A0           Alignment to use when base address is 0.
  @param Ranges       Array holding memory type settings for all memory regions.
  @param RangeCount   The count of memory ranges the array holds.
  @param VertexCount  The count of vertices in the graph.
  @param Vertices     Array holding all vertices.
  @param Weight       2-dimention array holding weights between vertices.
  @param Start        Start vertex.
  @param Stop         Stop vertex.
  @param Types        Type bit mask of memory range from Start to Stop.
  @param TypeCount    Number of different memory types from Start to Stop.
  @param Mtrrs        Array holding all MTRR settings.
  @param MtrrCapacity Capacity of the MTRR array.
  @param MtrrCount    The count of MTRR settings in array.

  @retval RETURN_SUCCESS          The subtractive path is calculated successfully.
  @retval RETURN_OUT_OF_RESOURCES The MTRR setting array is full.

**/
RETURN_STATUS
MtrrLibCalculateSubtractivePath (
  IN MTRR_MEMORY_CACHE_TYPE   DefaultType,
  IN UINT64                   A0,
  IN CONST MTRR_MEMORY_RANGE  *Ranges,
  IN UINTN                    RangeCount,
  IN UINT16                   VertexCount,
  IN MTRR_LIB_ADDRESS         *Vertices,
  IN OUT UINT8                *Weight,
  IN UINT16                   Start,
  IN UINT16                   Stop,
  IN UINT8                    Types,
  IN UINT8                    TypeCount,
  IN OUT MTRR_MEMORY_RANGE    *Mtrrs        OPTIONAL,
  IN UINT32                   MtrrCapacity  OPTIONAL,
  IN OUT UINT32               *MtrrCount    OPTIONAL
  )
{
  RETURN_STATUS           Status;
  UINT64                  Base;
  UINT64                  Length;
  UINT8                   PrecedentTypes;
  UINTN                   Index;
  UINT64                  HBase;
  UINT64                  HLength;
  UINT64                  SubLength;
  UINT16                  SubStart;
  UINT16                  SubStop;
  UINT16                  Cur;
  UINT16                  Pre;
  MTRR_MEMORY_CACHE_TYPE  LowestType;
  MTRR_MEMORY_CACHE_TYPE  LowestPrecedentType;

  Base   = Vertices[Start].Address;
  Length = Vertices[Stop].Address - Base;

  LowestType = MtrrLibLowestType (Types);

  //
  // Clear the lowest type (highest bit) to get the precedent types
  //
  PrecedentTypes      = ~(1 << LowestType) & Types;
  LowestPrecedentType = MtrrLibLowestType (PrecedentTypes);

  if (Mtrrs == NULL) {
    Weight[M (Start, Stop)] = ((LowestType == DefaultType) ? 0 : 1);
    Weight[O (Start, Stop)] = ((LowestType == DefaultType) ? 1 : 0);
  }

  // Add all high level ranges
  HBase   = MAX_UINT64;
  HLength = 0;
  for (Index = 0; Index < RangeCount; Index++) {
    if (Length == 0) {
      break;
    }

    if ((Base < Ranges[Index].BaseAddress) || (Ranges[Index].BaseAddress + Ranges[Index].Length <= Base)) {
      continue;
    }

    //
    // Base is in the Range[Index]
    //
    if (Base + Length > Ranges[Index].BaseAddress + Ranges[Index].Length) {
      SubLength = Ranges[Index].BaseAddress + Ranges[Index].Length - Base;
    } else {
      SubLength = Length;
    }

    if (((1 << Ranges[Index].Type) & PrecedentTypes) != 0) {
      //
      // Meet a range whose types take precedence.
      // Update the [HBase, HBase + HLength) to include the range,
      // [HBase, HBase + HLength) may contain sub ranges with 2 different types, and both take precedence.
      //
      if (HBase == MAX_UINT64) {
        HBase = Base;
      }

      HLength += SubLength;
    }

    Base   += SubLength;
    Length -= SubLength;

    if (HLength == 0) {
      continue;
    }

    if ((Ranges[Index].Type == LowestType) || (Length == 0)) {
      // meet low type or end

      //
      // Add the MTRRs for each high priority type range
      // the range[HBase, HBase + HLength) contains only two types.
      // We might use positive or subtractive, depending on which way uses less MTRR
      //
      for (SubStart = Start; SubStart <= Stop; SubStart++) {
        if (Vertices[SubStart].Address == HBase) {
          break;
        }
      }

      for (SubStop = SubStart; SubStop <= Stop; SubStop++) {
        if (Vertices[SubStop].Address == HBase + HLength) {
          break;
        }
      }

      ASSERT (Vertices[SubStart].Address == HBase);
      ASSERT (Vertices[SubStop].Address == HBase + HLength);

      if ((TypeCount == 2) || (SubStart == SubStop - 1)) {
        //
        // add subtractive MTRRs for [HBase, HBase + HLength)
        // [HBase, HBase + HLength) contains only one type.
        // while - loop is to split the range to MTRR - compliant aligned range.
        //
        if (Mtrrs == NULL) {
          Weight[M (Start, Stop)] += (UINT8)(SubStop - SubStart);
        } else {
          while (SubStart != SubStop) {
            Status = MtrrLibAppendVariableMtrr (
                       Mtrrs,
                       MtrrCapacity,
                       MtrrCount,
                       Vertices[SubStart].Address,
                       Vertices[SubStart].Length,
                       Vertices[SubStart].Type
                       );
            if (RETURN_ERROR (Status)) {
              return Status;
            }

            SubStart++;
          }
        }
      } else {
        ASSERT (TypeCount == 3);
        MtrrLibCalculateLeastMtrrs (VertexCount, Vertices, Weight, SubStart, SubStop, TRUE);

        if (Mtrrs == NULL) {
          Weight[M (Start, Stop)] += Vertices[SubStop].Weight;
        } else {
          // When we need to collect the optimal path from SubStart to SubStop
          while (SubStop != SubStart) {
            Cur     = SubStop;
            Pre     = Vertices[Cur].Previous;
            SubStop = Pre;

            if (Weight[M (Pre, Cur)] + Weight[O (Pre, Cur)] != 0) {
              Status = MtrrLibAppendVariableMtrr (
                         Mtrrs,
                         MtrrCapacity,
                         MtrrCount,
                         Vertices[Pre].Address,
                         Vertices[Cur].Address - Vertices[Pre].Address,
                         (Pre != Cur - 1) ? LowestPrecedentType : Vertices[Pre].Type
                         );
              if (RETURN_ERROR (Status)) {
                return Status;
              }
            }

            if (Pre != Cur - 1) {
              Status = MtrrLibCalculateSubtractivePath (
                         DefaultType,
                         A0,
                         Ranges,
                         RangeCount,
                         VertexCount,
                         Vertices,
                         Weight,
                         Pre,
                         Cur,
                         PrecedentTypes,
                         2,
                         Mtrrs,
                         MtrrCapacity,
                         MtrrCount
                         );
              if (RETURN_ERROR (Status)) {
                return Status;
              }
            }
          }
        }
      }

      //
      // Reset HBase, HLength
      //
      HBase   = MAX_UINT64;
      HLength = 0;
    }
  }

  return RETURN_SUCCESS;
}

/**
  Calculate MTRR settings to cover the specified memory ranges.

  @param DefaultType  Default memory type.
  @param A0           Alignment to use when base address is 0.
  @param Ranges       Memory range array holding the memory type
                      settings for all memory address.
  @param RangeCount   Count of memory ranges.
  @param Scratch      A temporary scratch buffer that is used to perform the calculation.
                      This is an optional parameter that may be NULL.
  @param ScratchSize  Pointer to the size in bytes of the scratch buffer.
                      It may be updated to the actual required size when the calculation
                      needs more scratch buffer.
  @param Mtrrs        Array holding all MTRR settings.
  @param MtrrCapacity Capacity of the MTRR array.
  @param MtrrCount    The count of MTRR settings in array.

  @retval RETURN_SUCCESS          Variable MTRRs are allocated successfully.
  @retval RETURN_OUT_OF_RESOURCES Count of variable MTRRs exceeds capacity.
  @retval RETURN_BUFFER_TOO_SMALL The scratch buffer is too small for MTRR calculation.
**/
RETURN_STATUS
MtrrLibCalculateMtrrs (
  IN MTRR_MEMORY_CACHE_TYPE   DefaultType,
  IN UINT64                   A0,
  IN CONST MTRR_MEMORY_RANGE  *Ranges,
  IN UINTN                    RangeCount,
  IN VOID                     *Scratch,
  IN OUT UINTN                *ScratchSize,
  IN OUT MTRR_MEMORY_RANGE    *Mtrrs,
  IN UINT32                   MtrrCapacity,
  IN OUT UINT32               *MtrrCount
  )
{
  UINT64            Base0;
  UINT64            Base1;
  UINTN             Index;
  UINT64            Base;
  UINT64            Length;
  UINT64            Alignment;
  UINT64            SubLength;
  MTRR_LIB_ADDRESS  *Vertices;
  UINT8             *Weight;
  UINT32            VertexIndex;
  UINT32            VertexCount;
  UINTN             RequiredScratchSize;
  UINT8             TypeCount;
  UINT16            Start;
  UINT16            Stop;
  UINT8             Type;
  RETURN_STATUS     Status;

  Base0 = Ranges[0].BaseAddress;
  Base1 = Ranges[RangeCount - 1].BaseAddress + Ranges[RangeCount - 1].Length;
  MTRR_LIB_ASSERT_ALIGNED (Base0, Base1 - Base0);

  //
  // Count the number of vertices.
  //
  Vertices = (MTRR_LIB_ADDRESS *)Scratch;
  for (VertexIndex = 0, Index = 0; Index < RangeCount; Index++) {
    Base   = Ranges[Index].BaseAddress;
    Length = Ranges[Index].Length;
    while (Length != 0) {
      Alignment = MtrrLibBiggestAlignment (Base, A0);
      SubLength = Alignment;
      if (SubLength > Length) {
        SubLength = GetPowerOfTwo64 (Length);
      }

      if (VertexIndex < *ScratchSize / sizeof (*Vertices)) {
        Vertices[VertexIndex].Address   = Base;
        Vertices[VertexIndex].Alignment = Alignment;
        Vertices[VertexIndex].Type      = Ranges[Index].Type;
        Vertices[VertexIndex].Length    = SubLength;
      }

      Base   += SubLength;
      Length -= SubLength;
      VertexIndex++;
    }
  }

  //
  // Vertices[VertexIndex] = Base1, so whole vertex count is (VertexIndex + 1).
  //
  VertexCount = VertexIndex + 1;
  DEBUG ((
    DEBUG_CACHE,
    "  Count of vertices (%016llx - %016llx) = %d\n",
    Ranges[0].BaseAddress,
    Ranges[RangeCount - 1].BaseAddress + Ranges[RangeCount - 1].Length,
    VertexCount
    ));
  ASSERT (VertexCount < MAX_UINT16);

  RequiredScratchSize = VertexCount * sizeof (*Vertices) + VertexCount * VertexCount * sizeof (*Weight);
  if (*ScratchSize < RequiredScratchSize) {
    *ScratchSize = RequiredScratchSize;
    return RETURN_BUFFER_TOO_SMALL;
  }

  Vertices[VertexCount - 1].Address = Base1;

  Weight = (UINT8 *)&Vertices[VertexCount];
  for (VertexIndex = 0; VertexIndex < VertexCount; VertexIndex++) {
    //
    // Set optional weight between vertices and self->self to 0
    //
    SetMem (&Weight[M (VertexIndex, 0)], VertexIndex + 1, 0);
    //
    // Set mandatory weight between vertices to MAX_WEIGHT
    //
    SetMem (&Weight[M (VertexIndex, VertexIndex + 1)], VertexCount - VertexIndex - 1, MAX_WEIGHT);

    // Final result looks like:
    //   00 FF FF FF
    //   00 00 FF FF
    //   00 00 00 FF
    //   00 00 00 00
  }

  //
  // Set mandatory weight and optional weight for adjacent vertices
  //
  for (VertexIndex = 0; VertexIndex < VertexCount - 1; VertexIndex++) {
    if (Vertices[VertexIndex].Type != DefaultType) {
      Weight[M (VertexIndex, VertexIndex + 1)] = 1;
      Weight[O (VertexIndex, VertexIndex + 1)] = 0;
    } else {
      Weight[M (VertexIndex, VertexIndex + 1)] = 0;
      Weight[O (VertexIndex, VertexIndex + 1)] = 1;
    }
  }

  for (TypeCount = 2; TypeCount <= 3; TypeCount++) {
    for (Start = 0; Start < VertexCount; Start++) {
      for (Stop = Start + 2; Stop < VertexCount; Stop++) {
        ASSERT (Vertices[Stop].Address > Vertices[Start].Address);
        Length = Vertices[Stop].Address - Vertices[Start].Address;
        if (Length > Vertices[Start].Alignment) {
          //
          // Pickup a new Start when [Start, Stop) cannot be described by one MTRR.
          //
          break;
        }

        if ((Weight[M (Start, Stop)] == MAX_WEIGHT) && IS_POW2 (Length)) {
          if (MtrrLibGetNumberOfTypes (
                Ranges,
                RangeCount,
                Vertices[Start].Address,
                Vertices[Stop].Address - Vertices[Start].Address,
                &Type
                ) == TypeCount)
          {
            //
            // Update the Weight[Start, Stop] using subtractive path.
            //
            MtrrLibCalculateSubtractivePath (
              DefaultType,
              A0,
              Ranges,
              RangeCount,
              (UINT16)VertexCount,
              Vertices,
              Weight,
              Start,
              Stop,
              Type,
              TypeCount,
              NULL,
              0,
              NULL
              );
          } else if (TypeCount == 2) {
            //
            // Pick up a new Start when we expect 2-type range, but 3-type range is met.
            // Because no matter how Stop is increased, we always meet 3-type range.
            //
            break;
          }
        }
      }
    }
  }

  Status = RETURN_SUCCESS;
  MtrrLibCalculateLeastMtrrs ((UINT16)VertexCount, Vertices, Weight, 0, (UINT16)VertexCount - 1, FALSE);
  Stop = (UINT16)VertexCount - 1;
  while (Stop != 0) {
    Start     = Vertices[Stop].Previous;
    TypeCount = MAX_UINT8;
    Type      = 0;
    if (Weight[M (Start, Stop)] != 0) {
      TypeCount = MtrrLibGetNumberOfTypes (Ranges, RangeCount, Vertices[Start].Address, Vertices[Stop].Address - Vertices[Start].Address, &Type);
      Status    = MtrrLibAppendVariableMtrr (
                    Mtrrs,
                    MtrrCapacity,
                    MtrrCount,
                    Vertices[Start].Address,
                    Vertices[Stop].Address - Vertices[Start].Address,
                    MtrrLibLowestType (Type)
                    );
      if (RETURN_ERROR (Status)) {
        break;
      }
    }

    if (Start != Stop - 1) {
      //
      // substractive path
      //
      if (TypeCount == MAX_UINT8) {
        TypeCount = MtrrLibGetNumberOfTypes (
                      Ranges,
                      RangeCount,
                      Vertices[Start].Address,
                      Vertices[Stop].Address - Vertices[Start].Address,
                      &Type
                      );
      }

      Status = MtrrLibCalculateSubtractivePath (
                 DefaultType,
                 A0,
                 Ranges,
                 RangeCount,
                 (UINT16)VertexCount,
                 Vertices,
                 Weight,
                 Start,
                 Stop,
                 Type,
                 TypeCount,
                 Mtrrs,
                 MtrrCapacity,
                 MtrrCount
                 );
      if (RETURN_ERROR (Status)) {
        break;
      }
    }

    Stop = Start;
  }

  return Status;
}

/**
  Apply the fixed MTRR settings to memory range array.

  @param Fixed             The fixed MTRR settings.
  @param Ranges            Return the memory range array holding memory type
                           settings for all memory address.
  @param RangeCapacity     The capacity of memory range array.
  @param RangeCount        Return the count of memory range.

  @retval RETURN_SUCCESS          The memory range array is returned successfully.
  @retval RETURN_OUT_OF_RESOURCES The count of memory ranges exceeds capacity.
**/
RETURN_STATUS
MtrrLibApplyFixedMtrrs (
  IN     MTRR_FIXED_SETTINGS  *Fixed,
  IN OUT MTRR_MEMORY_RANGE    *Ranges,
  IN     UINTN                RangeCapacity,
  IN OUT UINTN                *RangeCount
  )
{
  RETURN_STATUS           Status;
  UINTN                   MsrIndex;
  UINTN                   Index;
  MTRR_MEMORY_CACHE_TYPE  MemoryType;
  UINT64                  Base;

  Base = 0;
  for (MsrIndex = 0; MsrIndex < ARRAY_SIZE (mMtrrLibFixedMtrrTable); MsrIndex++) {
    ASSERT (Base == mMtrrLibFixedMtrrTable[MsrIndex].BaseAddress);
    for (Index = 0; Index < sizeof (UINT64); Index++) {
      MemoryType = (MTRR_MEMORY_CACHE_TYPE)((UINT8 *)(&Fixed->Mtrr[MsrIndex]))[Index];
      Status     = MtrrLibSetMemoryType (
                     Ranges,
                     RangeCapacity,
                     RangeCount,
                     Base,
                     mMtrrLibFixedMtrrTable[MsrIndex].Length,
                     MemoryType
                     );
      if (Status == RETURN_OUT_OF_RESOURCES) {
        return Status;
      }

      Base += mMtrrLibFixedMtrrTable[MsrIndex].Length;
    }
  }

  ASSERT (Base == BASE_1MB);
  return RETURN_SUCCESS;
}

/**
  Apply the variable MTRR settings to memory range array.

  @param VariableMtrr      The variable MTRR array.
  @param VariableMtrrCount The count of variable MTRRs.
  @param Ranges            Return the memory range array with new MTRR settings applied.
  @param RangeCapacity     The capacity of memory range array.
  @param RangeCount        Return the count of memory range.

  @retval RETURN_SUCCESS          The memory range array is returned successfully.
  @retval RETURN_OUT_OF_RESOURCES The count of memory ranges exceeds capacity.
**/
RETURN_STATUS
MtrrLibApplyVariableMtrrs (
  IN     CONST MTRR_MEMORY_RANGE  *VariableMtrr,
  IN     UINT32                   VariableMtrrCount,
  IN OUT MTRR_MEMORY_RANGE        *Ranges,
  IN     UINTN                    RangeCapacity,
  IN OUT UINTN                    *RangeCount
  )
{
  RETURN_STATUS  Status;
  UINTN          Index;

  //
  // WT > WB
  // UC > *
  // UC > * (except WB, UC) > WB
  //

  //
  // 1. Set WB
  //
  for (Index = 0; Index < VariableMtrrCount; Index++) {
    if ((VariableMtrr[Index].Length != 0) && (VariableMtrr[Index].Type == CacheWriteBack)) {
      Status = MtrrLibSetMemoryType (
                 Ranges,
                 RangeCapacity,
                 RangeCount,
                 VariableMtrr[Index].BaseAddress,
                 VariableMtrr[Index].Length,
                 VariableMtrr[Index].Type
                 );
      if (Status == RETURN_OUT_OF_RESOURCES) {
        return Status;
      }
    }
  }

  //
  // 2. Set other types than WB or UC
  //
  for (Index = 0; Index < VariableMtrrCount; Index++) {
    if ((VariableMtrr[Index].Length != 0) &&
        (VariableMtrr[Index].Type != CacheWriteBack) && (VariableMtrr[Index].Type != CacheUncacheable))
    {
      Status = MtrrLibSetMemoryType (
                 Ranges,
                 RangeCapacity,
                 RangeCount,
                 VariableMtrr[Index].BaseAddress,
                 VariableMtrr[Index].Length,
                 VariableMtrr[Index].Type
                 );
      if (Status == RETURN_OUT_OF_RESOURCES) {
        return Status;
      }
    }
  }

  //
  // 3. Set UC
  //
  for (Index = 0; Index < VariableMtrrCount; Index++) {
    if ((VariableMtrr[Index].Length != 0) && (VariableMtrr[Index].Type == CacheUncacheable)) {
      Status = MtrrLibSetMemoryType (
                 Ranges,
                 RangeCapacity,
                 RangeCount,
                 VariableMtrr[Index].BaseAddress,
                 VariableMtrr[Index].Length,
                 VariableMtrr[Index].Type
                 );
      if (Status == RETURN_OUT_OF_RESOURCES) {
        return Status;
      }
    }
  }

  return RETURN_SUCCESS;
}

/**
  Return the memory type bit mask that's compatible to first type in the Ranges.

  @param Ranges     Memory range array holding the memory type
                    settings for all memory address.
  @param RangeCount Count of memory ranges.

  @return Compatible memory type bit mask.
**/
UINT8
MtrrLibGetCompatibleTypes (
  IN CONST MTRR_MEMORY_RANGE  *Ranges,
  IN UINTN                    RangeCount
  )
{
  ASSERT (RangeCount != 0);

  switch (Ranges[0].Type) {
    case CacheWriteBack:
    case CacheWriteThrough:
      return (1 << CacheWriteBack) | (1 << CacheWriteThrough) | (1 << CacheUncacheable);
      break;

    case CacheWriteCombining:
    case CacheWriteProtected:
      return (1 << Ranges[0].Type) | (1 << CacheUncacheable);
      break;

    case CacheUncacheable:
      if (RangeCount == 1) {
        return (1 << CacheUncacheable);
      }

      return MtrrLibGetCompatibleTypes (&Ranges[1], RangeCount - 1);
      break;

    case CacheInvalid:
    default:
      ASSERT (FALSE);
      break;
  }

  return 0;
}

/**
  Overwrite the destination MTRR settings with the source MTRR settings.
  This routine is to make sure the modification to destination MTRR settings
  is as small as possible.

  @param DstMtrrs     Destination MTRR settings.
  @param DstMtrrCount Count of destination MTRR settings.
  @param SrcMtrrs     Source MTRR settings.
  @param SrcMtrrCount Count of source MTRR settings.
  @param Modified     Flag array to indicate which destination MTRR setting is modified.
**/
VOID
MtrrLibMergeVariableMtrr (
  MTRR_MEMORY_RANGE  *DstMtrrs,
  UINT32             DstMtrrCount,
  MTRR_MEMORY_RANGE  *SrcMtrrs,
  UINT32             SrcMtrrCount,
  BOOLEAN            *Modified
  )
{
  UINT32  DstIndex;
  UINT32  SrcIndex;

  ASSERT (SrcMtrrCount <= DstMtrrCount);

  for (DstIndex = 0; DstIndex < DstMtrrCount; DstIndex++) {
    Modified[DstIndex] = FALSE;

    if (DstMtrrs[DstIndex].Length == 0) {
      continue;
    }

    for (SrcIndex = 0; SrcIndex < SrcMtrrCount; SrcIndex++) {
      if ((DstMtrrs[DstIndex].BaseAddress == SrcMtrrs[SrcIndex].BaseAddress) &&
          (DstMtrrs[DstIndex].Length == SrcMtrrs[SrcIndex].Length) &&
          (DstMtrrs[DstIndex].Type == SrcMtrrs[SrcIndex].Type))
      {
        break;
      }
    }

    if (SrcIndex == SrcMtrrCount) {
      //
      // Remove the one from DstMtrrs which is not in SrcMtrrs
      //
      DstMtrrs[DstIndex].Length = 0;
      Modified[DstIndex]        = TRUE;
    } else {
      //
      // Remove the one from SrcMtrrs which is also in DstMtrrs
      //
      SrcMtrrs[SrcIndex].Length = 0;
    }
  }

  //
  // Now valid MTRR only exists in either DstMtrrs or SrcMtrrs.
  // Merge MTRRs from SrcMtrrs to DstMtrrs
  //
  DstIndex = 0;
  for (SrcIndex = 0; SrcIndex < SrcMtrrCount; SrcIndex++) {
    if (SrcMtrrs[SrcIndex].Length != 0) {
      //
      // Find the empty slot in DstMtrrs
      //
      while (DstIndex < DstMtrrCount) {
        if (DstMtrrs[DstIndex].Length == 0) {
          break;
        }

        DstIndex++;
      }

      ASSERT (DstIndex < DstMtrrCount);
      CopyMem (&DstMtrrs[DstIndex], &SrcMtrrs[SrcIndex], sizeof (SrcMtrrs[0]));
      Modified[DstIndex] = TRUE;
    }
  }
}

/**
  Calculate the variable MTRR settings for all memory ranges.

  @param DefaultType          Default memory type.
  @param A0                   Alignment to use when base address is 0.
  @param Ranges               Memory range array holding the memory type
                              settings for all memory address.
  @param RangeCount           Count of memory ranges.
  @param Scratch              Scratch buffer to be used in MTRR calculation.
  @param ScratchSize          Pointer to the size of scratch buffer.
  @param VariableMtrr         Array holding all MTRR settings.
  @param VariableMtrrCapacity Capacity of the MTRR array.
  @param VariableMtrrCount    The count of MTRR settings in array.

  @retval RETURN_SUCCESS          Variable MTRRs are allocated successfully.
  @retval RETURN_OUT_OF_RESOURCES Count of variable MTRRs exceeds capacity.
  @retval RETURN_BUFFER_TOO_SMALL The scratch buffer is too small for MTRR calculation.
                                  The required scratch buffer size is returned through ScratchSize.
**/
RETURN_STATUS
MtrrLibSetMemoryRanges (
  IN MTRR_MEMORY_CACHE_TYPE  DefaultType,
  IN UINT64                  A0,
  IN MTRR_MEMORY_RANGE       *Ranges,
  IN UINTN                   RangeCount,
  IN VOID                    *Scratch,
  IN OUT UINTN               *ScratchSize,
  OUT MTRR_MEMORY_RANGE      *VariableMtrr,
  IN UINT32                  VariableMtrrCapacity,
  OUT UINT32                 *VariableMtrrCount
  )
{
  RETURN_STATUS  Status;
  UINT32         Index;
  UINT64         Base0;
  UINT64         Base1;
  UINT64         Alignment;
  UINT8          CompatibleTypes;
  UINT64         Length;
  UINT32         End;
  UINTN          ActualScratchSize;
  UINTN          BiggestScratchSize;

  *VariableMtrrCount = 0;

  //
  // Since the whole ranges need multiple calls of MtrrLibCalculateMtrrs().
  // Each call needs different scratch buffer size.
  // When the provided scratch buffer size is not sufficient in any call,
  // set the GetActualScratchSize to TRUE, and following calls will only
  // calculate the actual scratch size for the caller.
  //
  BiggestScratchSize = 0;

  for (Index = 0; Index < RangeCount;) {
    Base0 = Ranges[Index].BaseAddress;

    //
    // Full step is optimal
    //
    while (Index < RangeCount) {
      ASSERT (Ranges[Index].BaseAddress == Base0);
      Alignment = MtrrLibBiggestAlignment (Base0, A0);
      while (Base0 + Alignment <= Ranges[Index].BaseAddress + Ranges[Index].Length) {
        if ((BiggestScratchSize <= *ScratchSize) && (Ranges[Index].Type != DefaultType)) {
          Status = MtrrLibAppendVariableMtrr (
                     VariableMtrr,
                     VariableMtrrCapacity,
                     VariableMtrrCount,
                     Base0,
                     Alignment,
                     Ranges[Index].Type
                     );
          if (RETURN_ERROR (Status)) {
            return Status;
          }
        }

        Base0    += Alignment;
        Alignment = MtrrLibBiggestAlignment (Base0, A0);
      }

      //
      // Remove the above range from Ranges[Index]
      //
      Ranges[Index].Length     -= Base0 - Ranges[Index].BaseAddress;
      Ranges[Index].BaseAddress = Base0;
      if (Ranges[Index].Length != 0) {
        break;
      } else {
        Index++;
      }
    }

    if (Index == RangeCount) {
      break;
    }

    //
    // Find continous ranges [Base0, Base1) which could be combined by MTRR.
    // Per SDM, the compatible types between[B0, B1) are:
    //   UC, *
    //   WB, WT
    //   UC, WB, WT
    //
    CompatibleTypes = MtrrLibGetCompatibleTypes (&Ranges[Index], RangeCount - Index);

    End = Index; // End points to last one that matches the CompatibleTypes.
    while (End + 1 < RangeCount) {
      if (((1 << Ranges[End + 1].Type) & CompatibleTypes) == 0) {
        break;
      }

      End++;
    }

    Alignment = MtrrLibBiggestAlignment (Base0, A0);
    Length    = GetPowerOfTwo64 (Ranges[End].BaseAddress + Ranges[End].Length - Base0);
    Base1     = Base0 + MIN (Alignment, Length);

    //
    // Base1 may not in Ranges[End]. Update End to the range Base1 belongs to.
    //
    End = Index;
    while (End + 1 < RangeCount) {
      if (Base1 <= Ranges[End + 1].BaseAddress) {
        break;
      }

      End++;
    }

    Length             = Ranges[End].Length;
    Ranges[End].Length = Base1 - Ranges[End].BaseAddress;
    ActualScratchSize  = *ScratchSize;
    Status             = MtrrLibCalculateMtrrs (
                           DefaultType,
                           A0,
                           &Ranges[Index],
                           End + 1 - Index,
                           Scratch,
                           &ActualScratchSize,
                           VariableMtrr,
                           VariableMtrrCapacity,
                           VariableMtrrCount
                           );
    if (Status == RETURN_BUFFER_TOO_SMALL) {
      BiggestScratchSize = MAX (BiggestScratchSize, ActualScratchSize);
      //
      // Ignore this error, because we need to calculate the biggest
      // scratch buffer size.
      //
      Status = RETURN_SUCCESS;
    }

    if (RETURN_ERROR (Status)) {
      return Status;
    }

    if (Length != Ranges[End].Length) {
      Ranges[End].BaseAddress = Base1;
      Ranges[End].Length      = Length - Ranges[End].Length;
      Index                   = End;
    } else {
      Index = End + 1;
    }
  }

  if (*ScratchSize < BiggestScratchSize) {
    *ScratchSize = BiggestScratchSize;
    return RETURN_BUFFER_TOO_SMALL;
  }

  return RETURN_SUCCESS;
}

/**
  Set the below-1MB memory attribute to fixed MTRR buffer.
  Modified flag array indicates which fixed MTRR is modified.

  @param [in, out] ClearMasks    The bits (when set) to clear in the fixed MTRR MSR.
  @param [in, out] OrMasks       The bits to set in the fixed MTRR MSR.
  @param [in]      BaseAddress   Base address.
  @param [in]      Length        Length.
  @param [in]      Type          Memory type.

  @retval RETURN_SUCCESS      The memory attribute is set successfully.
  @retval RETURN_UNSUPPORTED  The requested range or cache type was invalid
                              for the fixed MTRRs.
**/
RETURN_STATUS
MtrrLibSetBelow1MBMemoryAttribute (
  IN OUT UINT64              *ClearMasks,
  IN OUT UINT64              *OrMasks,
  IN PHYSICAL_ADDRESS        BaseAddress,
  IN UINT64                  Length,
  IN MTRR_MEMORY_CACHE_TYPE  Type
  )
{
  RETURN_STATUS  Status;
  UINT32         MsrIndex;
  UINT64         ClearMask;
  UINT64         OrMask;

  ASSERT (BaseAddress < BASE_1MB);

  MsrIndex = (UINT32)-1;
  while ((BaseAddress < BASE_1MB) && (Length != 0)) {
    Status = MtrrLibProgramFixedMtrr (Type, &BaseAddress, &Length, &MsrIndex, &ClearMask, &OrMask);
    if (RETURN_ERROR (Status)) {
      return Status;
    }

    ClearMasks[MsrIndex] = ClearMasks[MsrIndex] | ClearMask;
    OrMasks[MsrIndex]    = (OrMasks[MsrIndex] & ~ClearMask) | OrMask;
  }

  return RETURN_SUCCESS;
}

/**
  This function attempts to set the attributes into MTRR setting buffer for multiple memory ranges.

  @param[in, out]  MtrrSetting  MTRR setting buffer to be set.
  @param[in]       Scratch      A temporary scratch buffer that is used to perform the calculation.
  @param[in, out]  ScratchSize  Pointer to the size in bytes of the scratch buffer.
                                It may be updated to the actual required size when the calculation
                                needs more scratch buffer.
  @param[in]       Ranges       Pointer to an array of MTRR_MEMORY_RANGE.
                                When range overlap happens, the last one takes higher priority.
                                When the function returns, either all the attributes are set successfully,
                                or none of them is set.
  @param[in]       RangeCount   Count of MTRR_MEMORY_RANGE.

  @retval RETURN_SUCCESS            The attributes were set for all the memory ranges.
  @retval RETURN_INVALID_PARAMETER  Length in any range is zero.
  @retval RETURN_UNSUPPORTED        The processor does not support one or more bytes of the
                                    memory resource range specified by BaseAddress and Length in any range.
  @retval RETURN_UNSUPPORTED        The bit mask of attributes is not support for the memory resource
                                    range specified by BaseAddress and Length in any range.
  @retval RETURN_OUT_OF_RESOURCES   There are not enough system resources to modify the attributes of
                                    the memory resource ranges.
  @retval RETURN_ACCESS_DENIED      The attributes for the memory resource range specified by
                                    BaseAddress and Length cannot be modified.
  @retval RETURN_BUFFER_TOO_SMALL   The scratch buffer is too small for MTRR calculation.
**/
RETURN_STATUS
EFIAPI
MtrrSetMemoryAttributesInMtrrSettings (
  IN OUT MTRR_SETTINGS            *MtrrSetting,
  IN     VOID                     *Scratch,
  IN OUT UINTN                    *ScratchSize,
  IN     CONST MTRR_MEMORY_RANGE  *Ranges,
  IN     UINTN                    RangeCount
  )
{
  RETURN_STATUS  Status;
  UINT32         Index;
  UINT64         BaseAddress;
  UINT64         Length;
  BOOLEAN        Above1MbExist;

  UINT64                  MtrrValidBitsMask;
  UINT64                  MtrrValidAddressMask;
  MTRR_MEMORY_CACHE_TYPE  DefaultType;
  MTRR_VARIABLE_SETTINGS  VariableSettings;
  MTRR_MEMORY_RANGE       WorkingRanges[2 * ARRAY_SIZE (MtrrSetting->Variables.Mtrr) + 2];
  UINTN                   WorkingRangeCount;
  BOOLEAN                 Modified;
  MTRR_VARIABLE_SETTING   VariableSetting;
  UINT32                  OriginalVariableMtrrCount;
  UINT32                  FirmwareVariableMtrrCount;
  UINT32                  WorkingVariableMtrrCount;
  MTRR_MEMORY_RANGE       OriginalVariableMtrr[ARRAY_SIZE (MtrrSetting->Variables.Mtrr)];
  MTRR_MEMORY_RANGE       WorkingVariableMtrr[ARRAY_SIZE (MtrrSetting->Variables.Mtrr)];
  BOOLEAN                 VariableSettingModified[ARRAY_SIZE (MtrrSetting->Variables.Mtrr)];

  UINT64  ClearMasks[ARRAY_SIZE (mMtrrLibFixedMtrrTable)];
  UINT64  OrMasks[ARRAY_SIZE (mMtrrLibFixedMtrrTable)];

  MTRR_CONTEXT  MtrrContext;
  BOOLEAN       MtrrContextValid;

  Status = RETURN_SUCCESS;
  MtrrLibInitializeMtrrMask (&MtrrValidBitsMask, &MtrrValidAddressMask);

  //
  // TRUE indicating the accordingly Variable setting needs modificaiton in OriginalVariableMtrr.
  //
  SetMem (VariableSettingModified, ARRAY_SIZE (VariableSettingModified), FALSE);

  //
  // TRUE indicating the caller requests to set variable MTRRs.
  //
  Above1MbExist             = FALSE;
  OriginalVariableMtrrCount = 0;

  //
  // 0. Dump the requests.
  //
  DEBUG_CODE_BEGIN ();
  DEBUG ((
    DEBUG_CACHE,
    "Mtrr: Set Mem Attribute to %a, ScratchSize = %x%a",
    (MtrrSetting == NULL) ? "Hardware" : "Buffer",
    *ScratchSize,
    (RangeCount <= 1) ? "," : "\n"
    ));
  for (Index = 0; Index < RangeCount; Index++) {
    DEBUG ((
      DEBUG_CACHE,
      " %a: [%016lx, %016lx)\n",
      mMtrrMemoryCacheTypeShortName[MIN (Ranges[Index].Type, CacheInvalid)],
      Ranges[Index].BaseAddress,
      Ranges[Index].BaseAddress + Ranges[Index].Length
      ));
  }

  DEBUG_CODE_END ();

  //
  // 1. Validate the parameters.
  //
  if (!IsMtrrSupported ()) {
    Status = RETURN_UNSUPPORTED;
    goto Exit;
  }

  for (Index = 0; Index < RangeCount; Index++) {
    if (Ranges[Index].Length == 0) {
      Status = RETURN_INVALID_PARAMETER;
      goto Exit;
    }

    if (((Ranges[Index].BaseAddress & ~MtrrValidAddressMask) != 0) ||
        ((((Ranges[Index].BaseAddress + Ranges[Index].Length) & ~MtrrValidAddressMask) != 0) &&
         ((Ranges[Index].BaseAddress + Ranges[Index].Length) != MtrrValidBitsMask + 1))
        )
    {
      //
      // Either the BaseAddress or the Limit doesn't follow the alignment requirement.
      // Note: It's still valid if Limit doesn't follow the alignment requirement but equals to MAX Address.
      //
      Status = RETURN_UNSUPPORTED;
      goto Exit;
    }

    if ((Ranges[Index].Type != CacheUncacheable) &&
        (Ranges[Index].Type != CacheWriteCombining) &&
        (Ranges[Index].Type != CacheWriteThrough) &&
        (Ranges[Index].Type != CacheWriteProtected) &&
        (Ranges[Index].Type != CacheWriteBack))
    {
      Status = RETURN_INVALID_PARAMETER;
      goto Exit;
    }

    if (Ranges[Index].BaseAddress + Ranges[Index].Length > BASE_1MB) {
      Above1MbExist = TRUE;
    }
  }

  //
  // 2. Apply the above-1MB memory attribute settings.
  //
  if (Above1MbExist) {
    //
    // 2.1. Read all variable MTRRs and convert to Ranges.
    //
    OriginalVariableMtrrCount = GetVariableMtrrCountWorker ();
    MtrrGetVariableMtrrWorker (MtrrSetting, OriginalVariableMtrrCount, &VariableSettings);
    MtrrLibGetRawVariableRanges (
      &VariableSettings,
      OriginalVariableMtrrCount,
      MtrrValidBitsMask,
      MtrrValidAddressMask,
      OriginalVariableMtrr
      );

    DefaultType                  = MtrrGetDefaultMemoryTypeWorker (MtrrSetting);
    WorkingRangeCount            = 1;
    WorkingRanges[0].BaseAddress = 0;
    WorkingRanges[0].Length      = MtrrValidBitsMask + 1;
    WorkingRanges[0].Type        = DefaultType;

    Status = MtrrLibApplyVariableMtrrs (
               OriginalVariableMtrr,
               OriginalVariableMtrrCount,
               WorkingRanges,
               ARRAY_SIZE (WorkingRanges),
               &WorkingRangeCount
               );
    ASSERT_RETURN_ERROR (Status);

    ASSERT (OriginalVariableMtrrCount >= PcdGet32 (PcdCpuNumberOfReservedVariableMtrrs));
    FirmwareVariableMtrrCount = OriginalVariableMtrrCount - PcdGet32 (PcdCpuNumberOfReservedVariableMtrrs);
    ASSERT (WorkingRangeCount <= 2 * FirmwareVariableMtrrCount + 1);

    //
    // 2.2. Force [0, 1M) to UC, so that it doesn't impact subtraction algorithm.
    //
    Status = MtrrLibSetMemoryType (
               WorkingRanges,
               ARRAY_SIZE (WorkingRanges),
               &WorkingRangeCount,
               0,
               SIZE_1MB,
               CacheUncacheable
               );
    ASSERT (Status != RETURN_OUT_OF_RESOURCES);

    //
    // 2.3. Apply the new memory attribute settings to Ranges.
    //
    Modified = FALSE;
    for (Index = 0; Index < RangeCount; Index++) {
      BaseAddress = Ranges[Index].BaseAddress;
      Length      = Ranges[Index].Length;
      if (BaseAddress < BASE_1MB) {
        if (Length <= BASE_1MB - BaseAddress) {
          continue;
        }

        Length     -= BASE_1MB - BaseAddress;
        BaseAddress = BASE_1MB;
      }

      Status = MtrrLibSetMemoryType (
                 WorkingRanges,
                 ARRAY_SIZE (WorkingRanges),
                 &WorkingRangeCount,
                 BaseAddress,
                 Length,
                 Ranges[Index].Type
                 );
      if (Status == RETURN_ALREADY_STARTED) {
        Status = RETURN_SUCCESS;
      } else if (Status == RETURN_OUT_OF_RESOURCES) {
        goto Exit;
      } else {
        ASSERT_RETURN_ERROR (Status);
        Modified = TRUE;
      }
    }

    if (Modified) {
      //
      // 2.4. Calculate the Variable MTRR settings based on the Ranges.
      //      Buffer Too Small may be returned if the scratch buffer size is insufficient.
      //
      Status = MtrrLibSetMemoryRanges (
                 DefaultType,
                 LShiftU64 (1, (UINTN)HighBitSet64 (MtrrValidBitsMask)),
                 WorkingRanges,
                 WorkingRangeCount,
                 Scratch,
                 ScratchSize,
                 WorkingVariableMtrr,
                 FirmwareVariableMtrrCount + 1,
                 &WorkingVariableMtrrCount
                 );
      if (RETURN_ERROR (Status)) {
        goto Exit;
      }

      //
      // 2.5. Remove the [0, 1MB) MTRR if it still exists (not merged with other range)
      //
      for (Index = 0; Index < WorkingVariableMtrrCount; Index++) {
        if ((WorkingVariableMtrr[Index].BaseAddress == 0) && (WorkingVariableMtrr[Index].Length == SIZE_1MB)) {
          ASSERT (WorkingVariableMtrr[Index].Type == CacheUncacheable);
          WorkingVariableMtrrCount--;
          CopyMem (
            &WorkingVariableMtrr[Index],
            &WorkingVariableMtrr[Index + 1],
            (WorkingVariableMtrrCount - Index) * sizeof (WorkingVariableMtrr[0])
            );
          break;
        }
      }

      if (WorkingVariableMtrrCount > FirmwareVariableMtrrCount) {
        Status = RETURN_OUT_OF_RESOURCES;
        goto Exit;
      }

      //
      // 2.6. Merge the WorkingVariableMtrr to OriginalVariableMtrr
      //      Make sure least modification is made to OriginalVariableMtrr.
      //
      MtrrLibMergeVariableMtrr (
        OriginalVariableMtrr,
        OriginalVariableMtrrCount,
        WorkingVariableMtrr,
        WorkingVariableMtrrCount,
        VariableSettingModified
        );
    }
  }

  //
  // 3. Apply the below-1MB memory attribute settings.
  //
  // (Value & ~0 | 0) still equals to (Value)
  //
  ZeroMem (ClearMasks, sizeof (ClearMasks));
  ZeroMem (OrMasks, sizeof (OrMasks));
  for (Index = 0; Index < RangeCount; Index++) {
    if (Ranges[Index].BaseAddress >= BASE_1MB) {
      continue;
    }

    Status = MtrrLibSetBelow1MBMemoryAttribute (
               ClearMasks,
               OrMasks,
               Ranges[Index].BaseAddress,
               Ranges[Index].Length,
               Ranges[Index].Type
               );
    if (RETURN_ERROR (Status)) {
      goto Exit;
    }
  }

  MtrrContextValid = FALSE;
  //
  // 4. Write fixed MTRRs that have been modified
  //
  for (Index = 0; Index < ARRAY_SIZE (ClearMasks); Index++) {
    if (ClearMasks[Index] != 0) {
      if (MtrrSetting != NULL) {
        MtrrSetting->Fixed.Mtrr[Index] = (MtrrSetting->Fixed.Mtrr[Index] & ~ClearMasks[Index]) | OrMasks[Index];
      } else {
        if (!MtrrContextValid) {
          MtrrLibPreMtrrChange (&MtrrContext);
          MtrrContextValid = TRUE;
        }

        AsmMsrAndThenOr64 (mMtrrLibFixedMtrrTable[Index].Msr, ~ClearMasks[Index], OrMasks[Index]);
      }
    }
  }

  //
  // 5. Write variable MTRRs that have been modified
  //
  for (Index = 0; Index < OriginalVariableMtrrCount; Index++) {
    if (VariableSettingModified[Index]) {
      if (OriginalVariableMtrr[Index].Length != 0) {
        VariableSetting.Base = (OriginalVariableMtrr[Index].BaseAddress & MtrrValidAddressMask)
                               | (UINT8)OriginalVariableMtrr[Index].Type;
        VariableSetting.Mask = ((~(OriginalVariableMtrr[Index].Length - 1)) & MtrrValidAddressMask) | BIT11;
      } else {
        VariableSetting.Base = 0;
        VariableSetting.Mask = 0;
      }

      if (MtrrSetting != NULL) {
        CopyMem (&MtrrSetting->Variables.Mtrr[Index], &VariableSetting, sizeof (VariableSetting));
      } else {
        if (!MtrrContextValid) {
          MtrrLibPreMtrrChange (&MtrrContext);
          MtrrContextValid = TRUE;
        }

        AsmWriteMsr64 (
          MSR_IA32_MTRR_PHYSBASE0 + (Index << 1),
          VariableSetting.Base
          );
        AsmWriteMsr64 (
          MSR_IA32_MTRR_PHYSMASK0 + (Index << 1),
          VariableSetting.Mask
          );
      }
    }
  }

  if (MtrrSetting != NULL) {
    ((MSR_IA32_MTRR_DEF_TYPE_REGISTER *)&MtrrSetting->MtrrDefType)->Bits.E  = 1;
    ((MSR_IA32_MTRR_DEF_TYPE_REGISTER *)&MtrrSetting->MtrrDefType)->Bits.FE = 1;
  } else {
    if (MtrrContextValid) {
      MtrrLibPostMtrrChange (&MtrrContext);
    }
  }

Exit:
  DEBUG ((DEBUG_CACHE, "  Result = %r\n", Status));
  if (!RETURN_ERROR (Status)) {
    MtrrDebugPrintAllMtrrsWorker (MtrrSetting);
  }

  return Status;
}

/**
  This function attempts to set the attributes into MTRR setting buffer for a memory range.

  @param[in, out]  MtrrSetting  MTRR setting buffer to be set.
  @param[in]       BaseAddress  The physical address that is the start address
                                of a memory range.
  @param[in]       Length       The size in bytes of the memory range.
  @param[in]       Attribute    The bit mask of attributes to set for the
                                memory range.

  @retval RETURN_SUCCESS            The attributes were set for the memory range.
  @retval RETURN_INVALID_PARAMETER  Length is zero.
  @retval RETURN_UNSUPPORTED        The processor does not support one or more bytes of the
                                    memory resource range specified by BaseAddress and Length.
  @retval RETURN_UNSUPPORTED        The bit mask of attributes is not support for the memory resource
                                    range specified by BaseAddress and Length.
  @retval RETURN_ACCESS_DENIED      The attributes for the memory resource range specified by
                                    BaseAddress and Length cannot be modified.
  @retval RETURN_OUT_OF_RESOURCES   There are not enough system resources to modify the attributes of
                                    the memory resource range.
                                    Multiple memory range attributes setting by calling this API multiple
                                    times may fail with status RETURN_OUT_OF_RESOURCES. It may not mean
                                    the number of CPU MTRRs are too small to set such memory attributes.
                                    Pass the multiple memory range attributes to one call of
                                    MtrrSetMemoryAttributesInMtrrSettings() may succeed.
  @retval RETURN_BUFFER_TOO_SMALL   The fixed internal scratch buffer is too small for MTRR calculation.
                                    Caller should use MtrrSetMemoryAttributesInMtrrSettings() to specify
                                    external scratch buffer.
**/
RETURN_STATUS
EFIAPI
MtrrSetMemoryAttributeInMtrrSettings (
  IN OUT MTRR_SETTINGS       *MtrrSetting,
  IN PHYSICAL_ADDRESS        BaseAddress,
  IN UINT64                  Length,
  IN MTRR_MEMORY_CACHE_TYPE  Attribute
  )
{
  UINT8              Scratch[SCRATCH_BUFFER_SIZE];
  UINTN              ScratchSize;
  MTRR_MEMORY_RANGE  Range;

  Range.BaseAddress = BaseAddress;
  Range.Length      = Length;
  Range.Type        = Attribute;
  ScratchSize       = sizeof (Scratch);
  return MtrrSetMemoryAttributesInMtrrSettings (MtrrSetting, Scratch, &ScratchSize, &Range, 1);
}

/**
  This function attempts to set the attributes for a memory range.

  @param[in]  BaseAddress        The physical address that is the start
                                 address of a memory range.
  @param[in]  Length             The size in bytes of the memory range.
  @param[in]  Attributes         The bit mask of attributes to set for the
                                 memory range.

  @retval RETURN_SUCCESS            The attributes were set for the memory
                                    range.
  @retval RETURN_INVALID_PARAMETER  Length is zero.
  @retval RETURN_UNSUPPORTED        The processor does not support one or
                                    more bytes of the memory resource range
                                    specified by BaseAddress and Length.
  @retval RETURN_UNSUPPORTED        The bit mask of attributes is not support
                                    for the memory resource range specified
                                    by BaseAddress and Length.
  @retval RETURN_ACCESS_DENIED      The attributes for the memory resource
                                    range specified by BaseAddress and Length
                                    cannot be modified.
  @retval RETURN_OUT_OF_RESOURCES   There are not enough system resources to
                                    modify the attributes of the memory
                                    resource range.
                                    Multiple memory range attributes setting by calling this API multiple
                                    times may fail with status RETURN_OUT_OF_RESOURCES. It may not mean
                                    the number of CPU MTRRs are too small to set such memory attributes.
                                    Pass the multiple memory range attributes to one call of
                                    MtrrSetMemoryAttributesInMtrrSettings() may succeed.
  @retval RETURN_BUFFER_TOO_SMALL   The fixed internal scratch buffer is too small for MTRR calculation.
                                    Caller should use MtrrSetMemoryAttributesInMtrrSettings() to specify
                                    external scratch buffer.
**/
RETURN_STATUS
EFIAPI
MtrrSetMemoryAttribute (
  IN PHYSICAL_ADDRESS        BaseAddress,
  IN UINT64                  Length,
  IN MTRR_MEMORY_CACHE_TYPE  Attribute
  )
{
  return MtrrSetMemoryAttributeInMtrrSettings (NULL, BaseAddress, Length, Attribute);
}

/**
  Worker function setting variable MTRRs

  @param[in]  VariableSettings   A buffer to hold variable MTRRs content.

**/
VOID
MtrrSetVariableMtrrWorker (
  IN MTRR_VARIABLE_SETTINGS  *VariableSettings
  )
{
  UINT32  Index;
  UINT32  VariableMtrrCount;

  VariableMtrrCount = GetVariableMtrrCountWorker ();
  ASSERT (VariableMtrrCount <= ARRAY_SIZE (VariableSettings->Mtrr));

  for (Index = 0; Index < VariableMtrrCount; Index++) {
    AsmWriteMsr64 (
      MSR_IA32_MTRR_PHYSBASE0 + (Index << 1),
      VariableSettings->Mtrr[Index].Base
      );
    AsmWriteMsr64 (
      MSR_IA32_MTRR_PHYSMASK0 + (Index << 1),
      VariableSettings->Mtrr[Index].Mask
      );
  }
}

/**
  Worker function setting fixed MTRRs

  @param[in]  FixedSettings  A buffer to hold fixed MTRRs content.

**/
VOID
MtrrSetFixedMtrrWorker (
  IN MTRR_FIXED_SETTINGS  *FixedSettings
  )
{
  UINT32  Index;

  for (Index = 0; Index < MTRR_NUMBER_OF_FIXED_MTRR; Index++) {
    AsmWriteMsr64 (
      mMtrrLibFixedMtrrTable[Index].Msr,
      FixedSettings->Mtrr[Index]
      );
  }
}

/**
  This function gets the content in all MTRRs (variable and fixed)

  @param[out]  MtrrSetting  A buffer to hold all MTRRs content.

  @retval the pointer of MtrrSetting

**/
MTRR_SETTINGS *
EFIAPI
MtrrGetAllMtrrs (
  OUT MTRR_SETTINGS  *MtrrSetting
  )
{
  if (!IsMtrrSupported ()) {
    return MtrrSetting;
  }

  //
  // Get fixed MTRRs
  //
  MtrrGetFixedMtrrWorker (&MtrrSetting->Fixed);

  //
  // Get variable MTRRs
  //
  MtrrGetVariableMtrrWorker (
    NULL,
    GetVariableMtrrCountWorker (),
    &MtrrSetting->Variables
    );

  //
  // Get MTRR_DEF_TYPE value
  //
  MtrrSetting->MtrrDefType = AsmReadMsr64 (MSR_IA32_MTRR_DEF_TYPE);

  return MtrrSetting;
}

/**
  This function sets all MTRRs (variable and fixed)

  @param[in]  MtrrSetting  A buffer holding all MTRRs content.

  @retval The pointer of MtrrSetting

**/
MTRR_SETTINGS *
EFIAPI
MtrrSetAllMtrrs (
  IN MTRR_SETTINGS  *MtrrSetting
  )
{
  MTRR_CONTEXT  MtrrContext;

  if (!IsMtrrSupported ()) {
    return MtrrSetting;
  }

  MtrrLibPreMtrrChange (&MtrrContext);

  //
  // Set fixed MTRRs
  //
  MtrrSetFixedMtrrWorker (&MtrrSetting->Fixed);

  //
  // Set variable MTRRs
  //
  MtrrSetVariableMtrrWorker (&MtrrSetting->Variables);

  //
  // Set MTRR_DEF_TYPE value
  //
  AsmWriteMsr64 (MSR_IA32_MTRR_DEF_TYPE, MtrrSetting->MtrrDefType);

  MtrrLibPostMtrrChangeEnableCache (&MtrrContext);

  return MtrrSetting;
}

/**
  Checks if MTRR is supported.

  @retval TRUE  MTRR is supported.
  @retval FALSE MTRR is not supported.

**/
BOOLEAN
EFIAPI
IsMtrrSupported (
  VOID
  )
{
  CPUID_VERSION_INFO_EDX     Edx;
  MSR_IA32_MTRRCAP_REGISTER  MtrrCap;

  //
  // Check CPUID(1).EDX[12] for MTRR capability
  //
  AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, NULL, &Edx.Uint32);
  if (Edx.Bits.MTRR == 0) {
    return FALSE;
  }

  //
  // Check number of variable MTRRs and fixed MTRRs existence.
  // If number of variable MTRRs is zero, or fixed MTRRs do not
  // exist, return false.
  //
  MtrrCap.Uint64 = AsmReadMsr64 (MSR_IA32_MTRRCAP);
  if ((MtrrCap.Bits.VCNT == 0) || (MtrrCap.Bits.FIX == 0)) {
    return FALSE;
  }

  return TRUE;
}

/**
  Worker function prints all MTRRs for debugging.

  If MtrrSetting is not NULL, print MTRR settings from input MTRR
  settings buffer.
  If MtrrSetting is NULL, print MTRR settings from MTRRs.

  @param  MtrrSetting    A buffer holding all MTRRs content.
**/
VOID
MtrrDebugPrintAllMtrrsWorker (
  IN MTRR_SETTINGS  *MtrrSetting
  )
{
  DEBUG_CODE_BEGIN ();
  MTRR_SETTINGS      LocalMtrrs;
  MTRR_SETTINGS      *Mtrrs;
  UINTN              Index;
  UINTN              RangeCount;
  UINT64             MtrrValidBitsMask;
  UINT64             MtrrValidAddressMask;
  UINT32             VariableMtrrCount;
  BOOLEAN            ContainVariableMtrr;
  MTRR_MEMORY_RANGE  Ranges[
                            ARRAY_SIZE (mMtrrLibFixedMtrrTable) * sizeof (UINT64) + 2 * ARRAY_SIZE (Mtrrs->Variables.Mtrr) + 1
  ];
  MTRR_MEMORY_RANGE  RawVariableRanges[ARRAY_SIZE (Mtrrs->Variables.Mtrr)];

  if (!IsMtrrSupported ()) {
    return;
  }

  VariableMtrrCount = GetVariableMtrrCountWorker ();

  if (MtrrSetting != NULL) {
    Mtrrs = MtrrSetting;
  } else {
    MtrrGetAllMtrrs (&LocalMtrrs);
    Mtrrs = &LocalMtrrs;
  }

  //
  // Dump RAW MTRR contents
  //
  DEBUG ((DEBUG_CACHE, "MTRR Settings:\n"));
  DEBUG ((DEBUG_CACHE, "=============\n"));
  DEBUG ((DEBUG_CACHE, "MTRR Default Type: %016lx\n", Mtrrs->MtrrDefType));
  for (Index = 0; Index < ARRAY_SIZE (mMtrrLibFixedMtrrTable); Index++) {
    DEBUG ((DEBUG_CACHE, "Fixed MTRR[%02d]   : %016lx\n", Index, Mtrrs->Fixed.Mtrr[Index]));
  }

  ContainVariableMtrr = FALSE;
  for (Index = 0; Index < VariableMtrrCount; Index++) {
    if ((Mtrrs->Variables.Mtrr[Index].Mask & BIT11) == 0) {
      //
      // If mask is not valid, then do not display range
      //
      continue;
    }

    ContainVariableMtrr = TRUE;
    DEBUG ((
      DEBUG_CACHE,
      "Variable MTRR[%02d]: Base=%016lx Mask=%016lx\n",
      Index,
      Mtrrs->Variables.Mtrr[Index].Base,
      Mtrrs->Variables.Mtrr[Index].Mask
      ));
  }

  if (!ContainVariableMtrr) {
    DEBUG ((DEBUG_CACHE, "Variable MTRR    : None.\n"));
  }

  DEBUG ((DEBUG_CACHE, "\n"));

  //
  // Dump MTRR setting in ranges
  //
  DEBUG ((DEBUG_CACHE, "Memory Ranges:\n"));
  DEBUG ((DEBUG_CACHE, "====================================\n"));
  MtrrLibInitializeMtrrMask (&MtrrValidBitsMask, &MtrrValidAddressMask);
  Ranges[0].BaseAddress = 0;
  Ranges[0].Length      = MtrrValidBitsMask + 1;
  Ranges[0].Type        = MtrrGetDefaultMemoryTypeWorker (Mtrrs);
  RangeCount            = 1;

  MtrrLibGetRawVariableRanges (
    &Mtrrs->Variables,
    VariableMtrrCount,
    MtrrValidBitsMask,
    MtrrValidAddressMask,
    RawVariableRanges
    );
  MtrrLibApplyVariableMtrrs (
    RawVariableRanges,
    VariableMtrrCount,
    Ranges,
    ARRAY_SIZE (Ranges),
    &RangeCount
    );

  MtrrLibApplyFixedMtrrs (&Mtrrs->Fixed, Ranges, ARRAY_SIZE (Ranges), &RangeCount);

  for (Index = 0; Index < RangeCount; Index++) {
    DEBUG ((
      DEBUG_CACHE,
      "%a:%016lx-%016lx\n",
      mMtrrMemoryCacheTypeShortName[Ranges[Index].Type],
      Ranges[Index].BaseAddress,
      Ranges[Index].BaseAddress + Ranges[Index].Length - 1
      ));
  }

  DEBUG_CODE_END ();
}

/**
  This function prints all MTRRs for debugging.
**/
VOID
EFIAPI
MtrrDebugPrintAllMtrrs (
  VOID
  )
{
  MtrrDebugPrintAllMtrrsWorker (NULL);
}
