/** @file
  SetCacheMtrr library functions.

  @copyright
  Copyright 2006 - 2021 Intel Corporation. <BR>

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

#include <Uefi.h>
#include <PiPei.h>
#include <Library/HobLib.h>
#include <Library/PcdLib.h>
#include <Library/DebugLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/MtrrLib.h>
#include <Library/PeiServicesLib.h>
#include <Guid/SmramMemoryReserve.h>

#include <CpuAndRevisionDefines.h>
#include <Library/PeiServicesTablePointerLib.h>

#include <Ppi/MpServices.h>

#include <Guid/PlatformInfo.h>
#include <Guid/MemoryMapData.h>
#include <Protocol/IioUds.h>
#include <Register/ArchitecturalMsr.h>
#include <Register/Cpuid.h>
#include <Ppi/DynamicSiLibraryPpi.h>
#include <Cpu/CpuIds.h>

#define EFI_MAX_ADDRESS   0xFFFFFFFF

/**
  Set Cache Mtrr.
**/
VOID
EFIAPI
SetCacheMtrr (
  VOID
  )
{
  EFI_STATUS                  Status;
  EFI_PEI_HOB_POINTERS        Hob;
  MTRR_SETTINGS               MtrrSetting;
  UINT64                      MemoryBase;
  UINT64                      MemoryLength;
  UINT64                      LowMemoryLength;
  UINT64                      HighMemoryLength;
  EFI_BOOT_MODE               BootMode;
  EFI_RESOURCE_ATTRIBUTE_TYPE ResourceAttribute;
  UINT64                      CacheMemoryLength;

  ///
  /// Reset all MTRR setting.
  ///
  ZeroMem(&MtrrSetting, sizeof(MTRR_SETTINGS));

  ///
  /// Cache the Flash area as WP to boost performance
  ///
  Status = MtrrSetMemoryAttributeInMtrrSettings (
                &MtrrSetting,
                (UINTN) PcdGet32 (PcdFlashAreaBaseAddress),
                (UINTN) PcdGet32 (PcdFlashAreaSize),
                CacheWriteProtected
                );
  ASSERT_EFI_ERROR (Status);

  ///
  /// Update MTRR setting from MTRR buffer for Flash Region to be WP to boost performance
  ///
  MtrrSetAllMtrrs (&MtrrSetting);

  ///
  /// Set low to 1 MB. Since 1MB cacheability will always be set
  /// until override by CSM.
  /// Initialize high memory to 0.
  ///
  LowMemoryLength   = 0x100000;
  HighMemoryLength  = 0;
  ResourceAttribute = (
                       EFI_RESOURCE_ATTRIBUTE_PRESENT |
                       EFI_RESOURCE_ATTRIBUTE_INITIALIZED |
                       EFI_RESOURCE_ATTRIBUTE_UNCACHEABLE |
                       EFI_RESOURCE_ATTRIBUTE_WRITE_COMBINEABLE |
                       EFI_RESOURCE_ATTRIBUTE_WRITE_THROUGH_CACHEABLE |
                       EFI_RESOURCE_ATTRIBUTE_WRITE_BACK_CACHEABLE
                       );

  Status = PeiServicesGetBootMode (&BootMode);
  ASSERT_EFI_ERROR (Status);

  if (BootMode != BOOT_ON_S3_RESUME) {
    ResourceAttribute |= EFI_RESOURCE_ATTRIBUTE_TESTED;
  }

  Status = PeiServicesGetHobList ((VOID **) &Hob.Raw);
  while (!END_OF_HOB_LIST (Hob)) {
    if (Hob.Header->HobType == EFI_HOB_TYPE_RESOURCE_DESCRIPTOR) {
      if ((Hob.ResourceDescriptor->ResourceType == EFI_RESOURCE_SYSTEM_MEMORY) ||
          ((Hob.ResourceDescriptor->ResourceType == EFI_RESOURCE_MEMORY_RESERVED) &&
           (Hob.ResourceDescriptor->ResourceAttribute == ResourceAttribute))
         ) {
        if (Hob.ResourceDescriptor->PhysicalStart >= 0x100000000ULL) {
          HighMemoryLength += Hob.ResourceDescriptor->ResourceLength;
        } else if (Hob.ResourceDescriptor->PhysicalStart >= 0x100000) {
          LowMemoryLength += Hob.ResourceDescriptor->ResourceLength;
        }
      }
    }

    Hob.Raw = GET_NEXT_HOB (Hob);
  }

  DEBUG ((DEBUG_INFO, "Memory Length (Below 4GB) = %lx.\n", LowMemoryLength));
  DEBUG ((DEBUG_INFO, "Memory Length (Above 4GB) = %lx.\n", HighMemoryLength));

  ///
  /// Assume size of main memory is multiple of 256MB
  ///
  MemoryLength = (LowMemoryLength + 0xFFFFFFF) & 0xF0000000;
  MemoryBase = 0;

  CacheMemoryLength = MemoryLength;
  ///
  /// Programming MTRRs to avoid override SPI region with UC when MAX TOLUD Length >= 3.5GB
  ///
  if (MemoryLength > 0xDC000000) {
     CacheMemoryLength = 0xC0000000;
     Status = MtrrSetMemoryAttributeInMtrrSettings (
                &MtrrSetting,
                MemoryBase,
                CacheMemoryLength,
                CacheWriteBack
                );
     ASSERT_EFI_ERROR (Status);

     MemoryBase = 0xC0000000;
     CacheMemoryLength = MemoryLength - 0xC0000000;
     if (MemoryLength > 0xE0000000) {
        CacheMemoryLength = 0x20000000;
        Status = MtrrSetMemoryAttributeInMtrrSettings (
                 &MtrrSetting,
                 MemoryBase,
                 CacheMemoryLength,
                 CacheWriteBack
                 );
        ASSERT_EFI_ERROR (Status);

        MemoryBase = 0xE0000000;
        CacheMemoryLength = MemoryLength - 0xE0000000;
     }
  }

  Status = MtrrSetMemoryAttributeInMtrrSettings (
                &MtrrSetting,
                MemoryBase,
                CacheMemoryLength,
                CacheWriteBack
                );
  ASSERT_EFI_ERROR (Status);

  if (LowMemoryLength != MemoryLength) {
     MemoryBase = LowMemoryLength;
     MemoryLength -= LowMemoryLength;
     Status = MtrrSetMemoryAttributeInMtrrSettings (
                   &MtrrSetting,
                   MemoryBase,
                   MemoryLength,
                   CacheUncacheable
                   );
      ASSERT_EFI_ERROR (Status);
  }

  ///
  /// VGA-MMIO - 0xA0000 to 0xC0000 to be UC
  ///
  Status = MtrrSetMemoryAttributeInMtrrSettings (
                &MtrrSetting,
                0xA0000,
                0x20000,
                CacheUncacheable
                );
  ASSERT_EFI_ERROR (Status);

  ///
  /// Update MTRR setting from MTRR buffer
  ///
  MtrrSetAllMtrrs (&MtrrSetting);

  return ;
}

/**

  This function finds the start address and size of control or Block window region in the system.

  @param Host - pointer to sysHost structure on stack
  @param Memtype - Type of the memory range
  @param RangeSize - pointer to the variable to store the StartAddress
  @param RangeSize - pointer to the variable to store RangeSize

  @retval EFI_SUCCESS - success
          EFI_NOT_FOUND - Region not found.

**/
EFI_STATUS
GetMemoryRegionRange(
  struct SystemMemoryMapHob  *systemMemoryMap,
  UINT16                     Memtype,
  EFI_PHYSICAL_ADDRESS       *StartAddress,
  EFI_PHYSICAL_ADDRESS       *RangeSize)
{
  UINT8         Index;
  EFI_STATUS    Status = EFI_NOT_FOUND;
  UINT8         Socket = 0;
  EFI_PHYSICAL_ADDRESS Limit = 0;

  *RangeSize = 0;
  *StartAddress = 0;

  for (Socket = 0; Socket < MAX_SOCKET; Socket++) {
    if (systemMemoryMap->Socket[Socket].SAD[0].Enable == 0) {
      continue;
    }

    for (Index = 0; Index < MAX_SAD_RULES; Index++) {
      if (systemMemoryMap->Socket[Socket].SAD[Index].Enable == 0) {
        continue;
      }
      if (systemMemoryMap->Socket[Socket].SAD[Index].type == Memtype) {
        if ((*StartAddress == 0) && (Index > 0)) {
          //Store the start address for the specified range in bytes
          *StartAddress = (EFI_PHYSICAL_ADDRESS)MultU64x32 ((UINT64)systemMemoryMap->Socket[Socket].SAD[Index-1].Limit, CONVERT_64MB_TO_BYTE);
        }

        if (MultU64x32((UINT64)systemMemoryMap->Socket[Socket].SAD[Index].Limit, CONVERT_64MB_TO_BYTE) > Limit) {
          //Store/Update the end address for the specified range in bytes if greater than previous limit
          Limit = (EFI_PHYSICAL_ADDRESS)MultU64x32 ((UINT64)systemMemoryMap->Socket[Socket].SAD[Index].Limit, CONVERT_64MB_TO_BYTE);
        }
      }
    }
  }

  if (Limit != 0) {
    *RangeSize = Limit - *StartAddress;
    Status = EFI_SUCCESS;
  }

  return Status;
}

/**

  MP programming MSR_MCA_ON_NONNEM_CACHABLEMMIO_EN at socket level.

  @param PeiServices -      Ptr of EFI_PEI_SERVICES ptr
  @param mPeiMpServices -   Ptr of EFI_PEI_MP_SERVICES_PPI

  @retval EFI_SUCCESS     -  Programming done
  @retval EFI_UNSUPPORTED -  Not support this platform

**/
EFI_STATUS
PkgMpEnableMcaOnCacheableMmio(
  IN CONST EFI_PEI_SERVICES    **PeiServices,
  IN EFI_PEI_MP_SERVICES_PPI    *mPeiMpServices
)
{
  UINT16                    PackageDoneBitmap;
  UINTN                     NumberOfProcessors;
  UINTN                     NumberEnabledProcessors;
  UINTN                     Index;
  UINT8                     TotalSockets = 0;
  EFI_PROCESSOR_INFORMATION ProcInfo;
  EFI_STATUS                Status;
  DYNAMIC_SI_LIBARY_PPI     *DynamicSiLibraryPpi = NULL;

  Status = PeiServicesLocatePpi (&gDynamicSiLibraryPpiGuid, 0, NULL, (VOID **) &DynamicSiLibraryPpi);
  if (EFI_ERROR (Status)) {
    ASSERT_EFI_ERROR (Status);
    return Status;
  }

  if (!DynamicSiLibraryPpi->IsCpuAndRevision (CPU_SKX, REV_ALL) && !DynamicSiLibraryPpi->IsCpuAndRevision (CPU_CLX, REV_ALL) && !DynamicSiLibraryPpi->IsCpuAndRevision (CPU_CPX, REV_ALL)) {
    return EFI_UNSUPPORTED;
  }

  PackageDoneBitmap = 0;
  mPeiMpServices->GetNumberOfProcessors(
                    PeiServices,
                    mPeiMpServices,
                    &NumberOfProcessors,
                    &NumberEnabledProcessors
                    );

  //
  // Find total number of sockets present on the board
  //
  for (Index = 0; Index < MAX_SOCKET; Index++) {
    if (DynamicSiLibraryPpi->SocketPresent (Index)) {
      TotalSockets++;
    }
  }

  //
  // Loop through all the enabled processors and find one thread per socket present
  // to write the MSR.  Remote sockets need to use StartupThisAP.
  //
  for (Index = 0; Index < NumberOfProcessors; Index++) {
    mPeiMpServices->GetProcessorInfo(
                      PeiServices,
                      mPeiMpServices,
                      Index,
                      &ProcInfo
                      );

    if (!(PackageDoneBitmap & (1 << ProcInfo.Location.Package)) && (ProcInfo.StatusFlag & PROCESSOR_ENABLED_BIT)) {

      PackageDoneBitmap |= (1 << ProcInfo.Location.Package);
      TotalSockets--;

      if (ProcInfo.StatusFlag & PROCESSOR_AS_BSP_BIT) {
        DynamicSiLibraryPpi->EnableMcaOnCacheableMmio();
      } else {
        Status = mPeiMpServices->StartupThisAP (
                      PeiServices,
                      mPeiMpServices,
                      (EFI_AP_PROCEDURE)DynamicSiLibraryPpi->EnableMcaOnCacheableMmio,
                      Index,
                      0,
                      NULL
                      );
      }

      //
      // All sockets are programmed, skip checking rest of threads
      //
      if (TotalSockets == 0) {
        break;
      }
    }
  }

  return EFI_SUCCESS;
}

/**

  Function to set all MTRRs on the current thread to the MTRR set passed in.

  @param MtrrSettings -  Pointer to MTRR Settings to use

  @retval None

**/
VOID
SetAllMtrrs(
    VOID    *MtrrSettings
)
{
  MtrrSetAllMtrrs((MTRR_SETTINGS*)MtrrSettings);
}

/**

  Function that analyzes memory length passed in to see if there is an
  appropriate address to perform a more efficient top down coverage of
  the memory range.  At this address bit, it is more efficient to overshoot
  memory range with WB cache type and cover the gap with UC cache type.

  @param MemoryLength -  Memory length of entire memory region

  @retval Zero            - No efficient top down approaches found.  More efficient
                            to cover memory range conventional way (bottom up).
          PwrOfTwo Value  - PowerOfTwo bit where the top down approach is most efficient

**/
UINT64
FindTopDownPowerOfTwo (
  IN UINT64               MemoryLength
  )
{
  UINT8   Index;
  BOOLEAN FoundOne = FALSE;
  UINT8   ZeroCount = 0;
  UINT8   OnesCount = 0;
  UINT64  TopDownBit = 0;
  UINT8   MtrrSavings = 0;

  for (Index = 0; Index < 64; Index++){
    if (MemoryLength & LShiftU64(1, Index)) {
      OnesCount++;
      FoundOne = TRUE;
    } else {
      //
      // If there are more 1's than 0's plus 2 between least significant bit set
      // and current bit under test, then top down approach is more efficient.
      // Continue to loop through memory length to look for more efficiencies
      // and compare them against previous efficiencies found to pick best power of two.
      //
      if (((ZeroCount + 2) < OnesCount) && ((OnesCount - (ZeroCount + 2)) > MtrrSavings)) {
        TopDownBit = LShiftU64(1, (Index - 1));
        MtrrSavings = OnesCount - (ZeroCount + 2);
      }
      if (FoundOne) {
        ZeroCount++;
      }
    }
  }

  //
  // MtrrLib can handle this case efficiently
  //
  if (TopDownBit == GetPowerOfTwo64(MemoryLength)) {
    TopDownBit = 0;
  }

  return TopDownBit;
}

/**

  Recalculate the memory length to prevent MTRR out of resource error.

  @param MemoryLength      -  Memory Length that we want to truncate

  @retval UINT64   - New truncated memory length

**/
UINT64
MemLengthRecalculation (
  IN UINT64                     MemoryLength
  )
{
  UINT8  BitIndex;

  for (BitIndex = 0;BitIndex < 64; BitIndex++) {
    if ((RShiftU64(MemoryLength, BitIndex) & 1) == 1) {
      //
      // Clear lowest power of two bit found
      //
      MemoryLength &= ~LShiftU64(1, BitIndex);
      break;
    }
  }
  return MemoryLength;
}

/**

  Sets the uncached part of upper memory as reserved to prevent OS from using.
  The uncached region will always be at the top of high memory.

  @param OriginalMemoryLength   -  Original top of memory value
  @param NewMemoryLength        -  New memory range used for successful programming

  @retval None

**/
VOID
ReserveUncachedMemory (
  IN UINT64           OriginalMemoryLength,
  IN UINT64           NewMemoryLength
  )
{
  EFI_PEI_HOB_POINTERS          Hob;
  EFI_HOB_RESOURCE_DESCRIPTOR   *ResourceHob;
  VOID                          *HobStart;
  UINT64                        TempLength;

  HobStart = GetFirstHob (EFI_HOB_TYPE_RESOURCE_DESCRIPTOR);
  //
  // Scan resource descriptor hobs to set our required range attribute as tested
  //
  for (Hob.Raw = HobStart; !END_OF_HOB_LIST(Hob); Hob.Raw = GET_NEXT_HOB(Hob)) {
    if (GET_HOB_TYPE (Hob) == EFI_HOB_TYPE_RESOURCE_DESCRIPTOR) {
      ResourceHob = Hob.ResourceDescriptor;
      if ((ResourceHob->PhysicalStart >= NewMemoryLength) &&
        ((ResourceHob->PhysicalStart + ResourceHob->ResourceLength) <= OriginalMemoryLength)) {
        //
        // Range is completely included in the uncached area, mark as tested
        //
        ResourceHob->ResourceAttribute |= EFI_RESOURCE_ATTRIBUTE_TESTED;
      } else if ((ResourceHob->PhysicalStart < NewMemoryLength) &&
        (ResourceHob->PhysicalStart + ResourceHob->ResourceLength > NewMemoryLength)) {
        //
        // Shrink previous HOB to base of uncached region, create new hob to cover uncached space
        //
        TempLength = ResourceHob->ResourceLength;
        ResourceHob->ResourceLength = NewMemoryLength - ResourceHob->PhysicalStart;
        BuildResourceDescriptorHob(
          EFI_RESOURCE_SYSTEM_MEMORY,
          (
            EFI_RESOURCE_ATTRIBUTE_PRESENT |
            EFI_RESOURCE_ATTRIBUTE_INITIALIZED |
            EFI_RESOURCE_ATTRIBUTE_UNCACHEABLE |
            EFI_RESOURCE_ATTRIBUTE_TESTED
            ),
          ResourceHob->PhysicalStart + ResourceHob->ResourceLength,
          TempLength - ResourceHob->ResourceLength
          );
        if ((ResourceHob->PhysicalStart + ResourceHob->ResourceLength +
          (TempLength - ResourceHob->ResourceLength)) == OriginalMemoryLength) {
            break;
        }
      }
      if (ResourceHob->PhysicalStart + ResourceHob->ResourceLength == OriginalMemoryLength) {
        break;
      }
    }
  }

  HobStart = GetFirstHob (EFI_HOB_TYPE_MEMORY_ALLOCATION);
  //
  // Scan memory allocation hobs to make sure this range is free
  //
  for (Hob.Raw = HobStart; !END_OF_HOB_LIST(Hob); Hob.Raw = GET_NEXT_HOB(Hob)) {
    if (GET_HOB_TYPE (Hob) == EFI_HOB_TYPE_MEMORY_ALLOCATION) {
      if ((Hob.MemoryAllocation->AllocDescriptor.MemoryBaseAddress > NewMemoryLength) &&
        (OriginalMemoryLength > Hob.MemoryAllocation->AllocDescriptor.MemoryBaseAddress)) {
        //
        // If hob range is above the range we want to reserve, set limit to that base.
        //
        OriginalMemoryLength = Hob.MemoryAllocation->AllocDescriptor.MemoryBaseAddress;
      } else if ((Hob.MemoryAllocation->AllocDescriptor.MemoryBaseAddress +
        Hob.MemoryAllocation->AllocDescriptor.MemoryLength - 1) > NewMemoryLength) {
        //
        // If there is a range allocated that starts below but comes into the reserved range,
        // adjust the base so it starts above previous allocation
        //
        NewMemoryLength = Hob.MemoryAllocation->AllocDescriptor.MemoryBaseAddress +
          Hob.MemoryAllocation->AllocDescriptor.MemoryLength;
      }
    }
  }

  //
  // Create hob to reserve the memory
  //
  if (NewMemoryLength < OriginalMemoryLength) {
    BuildMemoryAllocationHob (
      (EFI_PHYSICAL_ADDRESS)NewMemoryLength,
      OriginalMemoryLength - NewMemoryLength,
      EfiReservedMemoryType
    );
  }
}

/**
  Update MTRR setting in EndOfPei phase.
  This function will clear temporary memory (CAR) phase MTRR settings
  and configure MTRR to cover permanent memory.

  @retval  EFI_SUCCESS  The function completes successfully.
  @retval  Others       Some error occurs.
**/
EFI_STATUS
EFIAPI
SetCacheMtrrAfterEndOfPei (
  VOID
  )
{
  EFI_STATUS            Status;
  UINT64                LowUncableBase;
  UINT64                MemoryBase;
  UINT64                TempQword;
  UINT64                MemoryLength;
  UINT64                TempMemoryLength;
  UINT64                OriginalMemoryLength;
  UINT64                TopDownBit;
  EFI_PEI_HOB_POINTERS  Hob;
  EFI_PLATFORM_INFO     *PlatformInfo;
  EFI_HOB_GUID_TYPE     *GuidHob;
  IIO_UDS               *IioUds;
  MTRR_SETTINGS         MtrrSettings;
  EFI_GUID              UniversalDataGuid = IIO_UNIVERSAL_DATA_GUID;
  EFI_PEI_MP_SERVICES_PPI    *mPeiMpServices = NULL;
  EFI_BOOT_MODE              BootMode;
  CONST EFI_PEI_SERVICES **  PeiServices;


  PeiServices = GetPeiServicesTablePointer();
  BootMode = GetBootModeHob();
  if (BootMode != BOOT_ON_S3_RESUME) {
    //
    // Get required HOBs to be used to generate MTRR programming
    //
    GuidHob = GetFirstGuidHob (&UniversalDataGuid);
    if (GuidHob == NULL) {
      ASSERT(GuidHob != NULL);
      return EFI_NOT_FOUND;
    }
    IioUds = GET_GUID_HOB_DATA (GuidHob);

    GuidHob = GetFirstGuidHob (&gEfiPlatformInfoGuid);
    if (GuidHob == NULL) {
      ASSERT(GuidHob != NULL);
      return EFI_NOT_FOUND;
    }
    PlatformInfo = GET_GUID_HOB_DATA(GuidHob);
    //
    // Calculate the low uncacheable base address
    //
    if (IioUds->PlatformData.PciExpressBase < IioUds->PlatformData.PlatGlobalMmio32Base) {
      LowUncableBase = IioUds->PlatformData.PciExpressBase;
    } else {
      LowUncableBase = IioUds->PlatformData.PlatGlobalMmio32Base;
    }

    LowUncableBase &= (0x0FFF00000);

    //
    // Reset all Mtrrs to 0 including fixed MTRR and variable MTRR
    //
    ZeroMem(&MtrrSettings, sizeof(MTRR_SETTINGS));

    //
    // Set fixed cache for memory range below 1MB
    //
    Status = MtrrSetMemoryAttributeInMtrrSettings(
      &MtrrSettings,
      0,
      0xA0000,
      EFI_CACHE_WRITEBACK
      );
    ASSERT_EFI_ERROR(Status);

    Status = MtrrSetMemoryAttributeInMtrrSettings(
      &MtrrSettings,
      0xA0000,
      0x60000,
      EFI_CACHE_UNCACHEABLE
      );
    ASSERT_EFI_ERROR(Status);

    //
    // Base set to 1mb due to MtrrLib programming method
    //
    MemoryBase = BASE_1MB;
    MemoryLength = LowUncableBase;

    Status = PeiServicesGetHobList ((VOID **) &Hob.Raw);
    while (!END_OF_HOB_LIST (Hob)) {
      if (Hob.Header->HobType == EFI_HOB_TYPE_RESOURCE_DESCRIPTOR) {
        if ((Hob.ResourceDescriptor->ResourceType == EFI_RESOURCE_SYSTEM_MEMORY) ||
            (Hob.ResourceDescriptor->ResourceType == EFI_RESOURCE_MEMORY_MAPPED_IO &&
            (Hob.ResourceDescriptor->ResourceAttribute & EFI_RESOURCE_ATTRIBUTE_WRITE_BACK_CACHEABLE)) ||
            (Hob.ResourceDescriptor->ResourceType == EFI_RESOURCE_MEMORY_RESERVED &&
             (Hob.ResourceDescriptor->ResourceAttribute & EFI_RESOURCE_ATTRIBUTE_WRITE_BACK_CACHEABLE))) {
          if (Hob.ResourceDescriptor->PhysicalStart > EFI_MAX_ADDRESS) {
            TempQword = Hob.ResourceDescriptor->PhysicalStart + Hob.ResourceDescriptor->ResourceLength;
            if (TempQword > MemoryLength) {
              MemoryLength = TempQword;
            }
          }
        }
      }
      Hob.Raw = GET_NEXT_HOB (Hob);
    }

    TempMemoryLength = MemoryLength;
    OriginalMemoryLength = MemoryLength;

    DEBUG((DEBUG_ERROR, "Total Memory size: 0x%lx\n", MemoryLength));

    Status = EFI_SUCCESS;
    //
    // Loop will continue until MTRR programming is successfully done.
    // All regions reserved in this loop are considered essential regions
    // If any of them fail to fit, memory is truncated by lowest power of
    // two until all regions fit into the programming
    //
    do {
      if (Status == RETURN_OUT_OF_RESOURCES){
        //
        // Ran out of MTRRs: lower top of high memory by lowest power of two bit and retry
        //
        MemoryLength = MemLengthRecalculation (TempMemoryLength);
        DEBUG((DEBUG_ERROR, "MTRR: %r, attempting: 0x%lx\n", Status, MemoryLength));
        //
        // Restore the MemoryBase to its original value, save MemoryLength
        //
        MemoryBase = BASE_1MB;
        TempMemoryLength = MemoryLength;
        ZeroMem(&(MtrrSettings.Variables), sizeof(MTRR_VARIABLE_SETTINGS));
      }

      TopDownBit = FindTopDownPowerOfTwo(MemoryLength);

      //
      // If TopDownBit has a value, then we found a more efficient address length
      // to use a top down approach.  We will walk through the full address length to
      // program MTRRs individually.  BASE_1MB fixups are due to MtrrLib program method.
      //
      if(TopDownBit){
        DEBUG((DEBUG_INFO, "Efficient Top Down Power of Two = %lx\n\n", TopDownBit));
        while (MemoryLength != 0) {
          if (GetPowerOfTwo64(MemoryLength) == TopDownBit) {
            //
            // Overshoot address with WB and cover remaining gap with UC
            //
            TempQword = MemoryLength;
            MemoryLength = LShiftU64(GetPowerOfTwo64 (MemoryLength), 1);

            if(MemoryBase == BASE_1MB) {
              MemoryLength -= BASE_1MB;
            }

            Status = MtrrSetMemoryAttributeInMtrrSettings(
                &MtrrSettings,
                MemoryBase,
                MemoryLength,
                EFI_CACHE_WRITEBACK
                );

            if (Status == RETURN_OUT_OF_RESOURCES) {
              break;
            }

            if(MemoryBase == BASE_1MB) {
              MemoryBase = 0;
              MemoryLength += BASE_1MB;
            }

            MemoryBase += TempQword;
            MemoryLength -= TempQword;

            //
            // Program UC region gap between top of memory and WB MTRR
            //
            Status = MtrrSetMemoryAttributeInMtrrSettings(
                &MtrrSettings,
                MemoryBase,
                MemoryLength,
                EFI_CACHE_UNCACHEABLE
                );

            if (Status == RETURN_OUT_OF_RESOURCES) {
              break;
            }

            MemoryLength = 0;
          } else {
            //
            // Grow next power of two upwards and adjust base and length
            //
            TempQword = GetPowerOfTwo64(MemoryLength);
            MemoryLength -= TempQword;

            if(MemoryBase == BASE_1MB) {
              TempQword -= BASE_1MB;
            }

            Status = MtrrSetMemoryAttributeInMtrrSettings(
              &MtrrSettings,
              MemoryBase,
              TempQword,
              EFI_CACHE_WRITEBACK
              );

            if (Status == RETURN_OUT_OF_RESOURCES) {
              break;
            }

            MemoryBase += TempQword;
          }
        }
        if (Status == RETURN_OUT_OF_RESOURCES) {
          continue;
        }
      } else {
        //
        // Create a WB region for the entire memory region
        //
        Status = MtrrSetMemoryAttributeInMtrrSettings(
          &MtrrSettings,
          MemoryBase,
          MemoryLength - BASE_1MB,
          EFI_CACHE_WRITEBACK
          );

        if (Status == RETURN_OUT_OF_RESOURCES) {
          continue;
        }
      }

      //
      // Punch UC hole for lower MMIO region
      //
      Status = MtrrSetMemoryAttributeInMtrrSettings(
        &MtrrSettings,
        LowUncableBase,
        EFI_MAX_ADDRESS - LowUncableBase + 1,
        EFI_CACHE_UNCACHEABLE
        );

      if (Status == RETURN_OUT_OF_RESOURCES) {
        continue;
      }

    } while (Status == RETURN_OUT_OF_RESOURCES);
    //
    // Assert if there was an error other than resource issue
    //
    ASSERT_EFI_ERROR (Status);

    //
    // Set PSMI Trace Region to uncached
    //
    if (PlatformInfo->MemData.PsmiUcTraceRegionSize != 0) {
      Status = MtrrSetMemoryAttributeInMtrrSettings(
          &MtrrSettings,
          PlatformInfo->MemData.PsmiUcTraceRegionBase,
          PlatformInfo->MemData.PsmiUcTraceRegionSize,
          EFI_CACHE_UNCACHEABLE
          );
      if (EFI_ERROR(Status)) {
        DEBUG((DEBUG_ERROR, "PSMI- Could not set Trace Region MemType to UC\n"));
      }
    }

    //
    // Set the calculated MTRR settings for the BSP
    //
    MtrrSetAllMtrrs (&MtrrSettings);

    //
    // Need to mark the uncached memory as reserved
    //
    if (OriginalMemoryLength > TempMemoryLength) {
      DEBUG((DEBUG_ERROR, "New TOHM: 0x%lx, Previous: 0x%lx\n", TempMemoryLength, OriginalMemoryLength));
      ReserveUncachedMemory (OriginalMemoryLength, TempMemoryLength);
    }
  }

  Status = (*PeiServices)->LocatePpi (
                    PeiServices,
                    &gEfiPeiMpServicesPpiGuid,
                    0,
                    NULL,
                    (VOID** ) &mPeiMpServices
                    );

  if (BootMode != BOOT_ON_S3_RESUME) {
    if(!EFI_ERROR(Status)){
      //
      // Sync all AP MTRRs with BSP
      //
      Status = mPeiMpServices->StartupAllAPs (
                    PeiServices,
                    mPeiMpServices,
                    (EFI_AP_PROCEDURE)SetAllMtrrs,
                    FALSE,
                    0,
                    (VOID*) &MtrrSettings
                    );
    }
  }

  PkgMpEnableMcaOnCacheableMmio (PeiServices, mPeiMpServices);


  return EFI_SUCCESS;
}
