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


/*
 *----------------------------------------------------------------------------
 *                                MODULES USED
 *
 *----------------------------------------------------------------------------
 */
#include "AGESA.h"
#include "amdlib.h"
#include "Ids.h"
#include "cpuRegisters.h"
#include "Topology.h"
#include "cpuServices.h"
#include "GeneralServices.h"
#include "cpuFamilyTranslation.h"
#include "cpuCacheInit.h"
#include "heapManager.h"
#include "Filecode.h"
CODE_GROUP (G1_PEICC)
RDATA_GROUP (G2_PEI)

#define FILECODE PROC_CPU_FEATURE_CPUCACHEINIT_FILECODE
/*----------------------------------------------------------------------------
 *                          DEFINITIONS AND MACROS
 *
 *----------------------------------------------------------------------------
 */
// 4G - 1, ~max ROM space
#define SIZE_INFINITE_EXE_CACHE 0xFFFFFFFF

/*----------------------------------------------------------------------------
 *                           TYPEDEFS AND STRUCTURES
 *
 *----------------------------------------------------------------------------
 */

/*----------------------------------------------------------------------------
 *             L2 cache Association to Way translation table
 *----------------------------------------------------------------------------
 */
CONST UINT8 ROMDATA L2AssocToL2WayTranslationTable[] =
{
  0,
  1,
  2,
  0xFF,
  4,
  0xFF,
  8,
  0xFF,
  16,
  0xFF,
  32,
  48,
  64,
  96,
  128,
  0xFF,
};


/*----------------------------------------------------------------------------
 *                        PROTOTYPES OF LOCAL FUNCTIONS
 *
 *----------------------------------------------------------------------------
 */

/*----------------------------------------------------------------------------
 *                            EXPORTED FUNCTIONS
 *
 *----------------------------------------------------------------------------
 */
UINT8
STATIC
Ceiling (
  IN UINT32 Divisor,
  IN UINT32 Dividend
  );

UINT32
STATIC
CalculateOccupiedExeCache (
  IN AMD_CONFIG_PARAMS *StdHeader
  );

VOID
STATIC
CompareRegions (
  IN      EXECUTION_CACHE_REGION  ARegion,
  IN      EXECUTION_CACHE_REGION  BRegion,
  IN OUT  MERGED_CACHE_REGION     *CRegion,
  IN      AMD_CONFIG_PARAMS       *StdHeader
  );

BOOLEAN
STATIC
IsPowerOfTwo (
  IN      UINT32                  TestNumber
  );

/*---------------------------------------------------------------------------------------*/
/**
 * This function will setup ROM execution cache.
 *
 * The execution cache regions are passed in, the max number of execution cache regions
 * is three.  Several rules are checked for compliance. If a rule test fails then one of
 * these error suffixes will be added to the general CPU_EVENT_EXECUTION_CACHE_ALLOCATION_ERROR
 * in the SubReason field
 *   -1  available cache size is less than requested, the ROM execution cache
 *       region has been reduced or eliminated.
 *   -2  at least one execution cache region crosses the 1MB line, the ROM execution
 *       cache size has been reduced.
 *   -3  at least one execution cache region crosses the 4GB line, the ROM execution
 *       cache size has been reduced.
 *   -4  the start address of a region is not at the boundary of cache size,
 *        the starting address has been adjusted downward
 *   -5  execution cache start address less than D0000, request is ignored
 *   -6  more than 2 execution cache regions are above 1MB, request is ignored
 * If the start address of all three regions are zero, then no execution cache is allocated.
 *
 * @param[in]   StdHeader          Handle to config for library and services
 * @param[in]   AmdExeAddrMapPtr   Pointer to the start of EXECUTION_CACHE_REGION array
 *
 * @retval      AGESA_SUCCESS      No error
 * @retval      AGESA_WARNING      AGESA_CACHE_SIZE_REDUCED; AGESA_CACHE_REGIONS_ACROSS_1MB;
 *                                 AGESA_CACHE_REGIONS_ACROSS_4GB;
 * @retval      AGESA_ERROR        AGESA_REGION_NOT_ALIGNED_ON_BOUNDARY;
 *                                 AGESA_CACHE_START_ADDRESS_LESS_D0000;
 *                                 AGESA_THREE_CACHE_REGIONS_ABOVE_1MB;
 *
 */
AGESA_STATUS
AllocateExecutionCache (
  IN AMD_CONFIG_PARAMS *StdHeader,
  IN EXECUTION_CACHE_REGION *AmdExeAddrMapPtr
  )
{
  AGESA_STATUS               AgesaStatus;
  AMD_GET_EXE_SIZE_PARAMS    AmdGetExeSize;
  UINT32                     CurrentAllocatedExeCacheSize;
  UINT32                     RemainingExecutionCacheSize;
  UINT64                     MsrData;
  UINT64                     SecondMsrData;
  UINT32                     RequestStartAddr;
  UINT32                     RequestSize;
  UINT32                     StartFixMtrr;
  UINT32                     CurrentMtrr;
  UINT32                     EndFixMtrr;
  UINT8                      i;
  UINT8                      Ignored;
  CACHE_INFO                 *CacheInfoPtr;
  CPU_SPECIFIC_SERVICES      *FamilySpecificServices;
  EXECUTION_CACHE_REGION      MtrrV6;
  EXECUTION_CACHE_REGION      MtrrV7;
  MERGED_CACHE_REGION         Result;

  //
  // If start addresses of all three regions are zero, then return early
  //
  if (AmdExeAddrMapPtr[0].ExeCacheStartAddr == 0) {
    if (AmdExeAddrMapPtr[1].ExeCacheStartAddr == 0) {
      if (AmdExeAddrMapPtr[2].ExeCacheStartAddr == 0) {
        // No regions defined by the caller
        return AGESA_SUCCESS;
      }
    }
  }

  // Get available cache size for ROM execution
  AmdGetExeSize.StdHeader = *StdHeader;
  AgesaStatus = AmdGetAvailableExeCacheSize (&AmdGetExeSize);
  CurrentAllocatedExeCacheSize = CalculateOccupiedExeCache (StdHeader);
  ASSERT (CurrentAllocatedExeCacheSize <= AmdGetExeSize.AvailableExeCacheSize);
  IDS_HDT_CONSOLE (CPU_TRACE, "  Cache size available for execution cache: 0x%x\n", AmdGetExeSize.AvailableExeCacheSize);
  RemainingExecutionCacheSize = AmdGetExeSize.AvailableExeCacheSize - CurrentAllocatedExeCacheSize;

  GetCpuServicesOfCurrentCore ((CONST CPU_SPECIFIC_SERVICES **)&FamilySpecificServices, StdHeader);
  FamilySpecificServices->GetCacheInfo (FamilySpecificServices, (CONST VOID **) &CacheInfoPtr, &Ignored, StdHeader);

  // Process each request entry 0 to 2
  for (i = 0; i < 3; i++) {
    // Exit if no more cache available
    if (RemainingExecutionCacheSize == 0) {
      break;
    }

    // Skip the region if ExeCacheSize = 0
    if (AmdExeAddrMapPtr[i].ExeCacheSize == 0) {
      continue;
    }

    // Align starting addresses on 32K boundary
    AmdExeAddrMapPtr[i].ExeCacheStartAddr =
      AmdExeAddrMapPtr[i].ExeCacheStartAddr & 0xFFFF8000;

    // Adjust size to multiple of 32K (rounding up)
    if ((AmdExeAddrMapPtr[i].ExeCacheSize % 0x8000) != 0) {
      AmdExeAddrMapPtr[i].ExeCacheSize = ((AmdExeAddrMapPtr[i].ExeCacheSize + 0x8000) & 0xFFFF8000);
    }

    // Boundary alignment check and confirm size is an even power of two
    if ( !IsPowerOfTwo (AmdExeAddrMapPtr[i].ExeCacheSize) ||
    ((AmdExeAddrMapPtr[i].ExeCacheStartAddr % AmdExeAddrMapPtr[i].ExeCacheSize) != 0) ) {
      AgesaStatus = AGESA_ERROR;
      PutEventLog (AgesaStatus,
                   (CPU_EVENT_EXECUTION_CACHE_ALLOCATION_ERROR + AGESA_REGION_NOT_ALIGNED_ON_BOUNDARY),
                   i, AmdExeAddrMapPtr[i].ExeCacheStartAddr, AmdExeAddrMapPtr[i].ExeCacheSize, 0, StdHeader);
      break;
    }

    // Check start address boundary
    if (AmdExeAddrMapPtr[i].ExeCacheStartAddr < 0xD0000) {
      AgesaStatus = AGESA_ERROR;
      PutEventLog (AgesaStatus,
                   (CPU_EVENT_EXECUTION_CACHE_ALLOCATION_ERROR + AGESA_CACHE_START_ADDRESS_LESS_D0000),
                   i, AmdExeAddrMapPtr[i].ExeCacheStartAddr, AmdExeAddrMapPtr[i].ExeCacheSize, 0, StdHeader);
      break;
    }

    if (CacheInfoPtr->CarExeType == LimitedByL2Size) {
      // Verify available execution cache size for region 0 to 2 request
      if (RemainingExecutionCacheSize < AmdExeAddrMapPtr[i].ExeCacheSize) {
        // Request is larger than available, reduce the allocation & report the change
        AmdExeAddrMapPtr[i].ExeCacheSize = RemainingExecutionCacheSize;
        RemainingExecutionCacheSize = 0;
        AgesaStatus = AGESA_WARNING;
        PutEventLog (AgesaStatus,
                     (CPU_EVENT_EXECUTION_CACHE_ALLOCATION_ERROR + AGESA_CACHE_SIZE_REDUCED),
                     i, AmdExeAddrMapPtr[i].ExeCacheStartAddr, AmdExeAddrMapPtr[i].ExeCacheSize, 0, StdHeader);
      } else {
        RemainingExecutionCacheSize = RemainingExecutionCacheSize - AmdExeAddrMapPtr[i].ExeCacheSize;
      }
    }
    IDS_HDT_CONSOLE (CPU_TRACE, "  Exe cache allocated: Base 0x%x, Size 0x%x\n", AmdExeAddrMapPtr[i].ExeCacheStartAddr, AmdExeAddrMapPtr[i].ExeCacheSize);

    RequestStartAddr = AmdExeAddrMapPtr[i].ExeCacheStartAddr;
    RequestSize = AmdExeAddrMapPtr[i].ExeCacheSize;

    if (RequestStartAddr < 0x100000) {
      // Region starts below 1MB - Fixed MTTR region,
      // turn on modification bit: MtrrFixDramModEn
      LibAmdMsrRead (MSR_SYS_CFG, &MsrData, StdHeader);
      MsrData |= 0x80000;
      LibAmdMsrWrite (MSR_SYS_CFG, &MsrData, StdHeader);


      // Check for 1M boundary crossing
      if ((RequestStartAddr + RequestSize) > 0x100000) {
        // Request spans the 1M boundary, reduce the size & report the change
        RequestSize = 0x100000 - RequestStartAddr;
        AmdExeAddrMapPtr[i].ExeCacheSize = RequestSize;
        AgesaStatus = AGESA_WARNING;
        PutEventLog (AgesaStatus,
                     (CPU_EVENT_EXECUTION_CACHE_ALLOCATION_ERROR + AGESA_CACHE_REGIONS_ACROSS_1MB),
                     i, RequestStartAddr, RequestSize, 0, StdHeader);
      }

      // Find start MTTR and end MTTR for the requested region
      StartFixMtrr = AMD_MTRR_FIX4K_BASE + ((RequestStartAddr >> 15) & 0x7);
      EndFixMtrr = AMD_MTRR_FIX4K_BASE + ((((RequestStartAddr + RequestSize) - 1) >> 15) & 0x7);

      //
      //Check Mtrr before we use it,
      //  if Mtrr has been used, we need to recover the previously allocated size.
      //    (only work in blocks of 32K size - no splitting of ways)
      for (CurrentMtrr = StartFixMtrr; CurrentMtrr <= EndFixMtrr; CurrentMtrr++) {
        LibAmdMsrRead (CurrentMtrr, &MsrData, StdHeader);
        if ((CacheInfoPtr->CarExeType == LimitedByL2Size) && (MsrData != 0)) {
          // MTRR previously allocated, recover size
          RemainingExecutionCacheSize = RemainingExecutionCacheSize + 0x8000;
        } else {
          // Allocate this MTRR
          MsrData = WP_IO;
          LibAmdMsrWrite (CurrentMtrr, &MsrData, StdHeader);
        }
      }
      // Turn off modification bit: MtrrFixDramModEn
      LibAmdMsrRead (MSR_SYS_CFG, &MsrData, StdHeader);
      MsrData &= 0xFFFFFFFFFFF7FFFFULL;
      LibAmdMsrWrite (MSR_SYS_CFG, &MsrData, StdHeader);


    } else {
      // Region above 1MB -  Variable MTTR region
      //    Need to check both VarMTRRs for each requested region for match or overlap
      //

      // Check for 4G boundary crossing   (using size-1 to keep in 32bit math range)
      if ((0xFFFFFFFFUL - RequestStartAddr) < (RequestSize - 1)) {
        RequestSize = (0xFFFFFFFFUL - RequestStartAddr) + 1;
        AgesaStatus = AGESA_WARNING;
        AmdExeAddrMapPtr[i].ExeCacheSize = RequestSize;
        PutEventLog (AgesaStatus,
                     (CPU_EVENT_EXECUTION_CACHE_ALLOCATION_ERROR + AGESA_CACHE_REGIONS_ACROSS_4GB),
                     i, RequestStartAddr, RequestSize, 0, StdHeader);
      }
      LibAmdMsrRead (AMD_MTRR_VARIABLE_BASE6, &MsrData, StdHeader);
      MtrrV6.ExeCacheStartAddr = ((UINT32) MsrData) & 0xFFFFF000UL;
      LibAmdMsrRead (AMD_MTRR_VARIABLE_BASE6 + 1, &MsrData, StdHeader);
      MtrrV6.ExeCacheSize = (0xFFFFFFFFUL - (((UINT32) MsrData) & 0xFFFFF000UL)) + 1;

      LibAmdMsrRead (AMD_MTRR_VARIABLE_BASE7, &MsrData, StdHeader);
      MtrrV7.ExeCacheStartAddr = ((UINT32) MsrData) & 0xFFFFF000UL;
      LibAmdMsrRead (AMD_MTRR_VARIABLE_BASE7 + 1, &MsrData, StdHeader);
      MtrrV7.ExeCacheSize = (0xFFFFFFFFUL - (((UINT32) MsrData) & 0xFFFFF000UL)) + 1;

      CompareRegions (AmdExeAddrMapPtr[i], MtrrV6, &Result, StdHeader);
      if (Result.OverlapType == EmptySet) {
        // MTRR6 is empty. Allocate request into MTRR6.
        // Note: since all merges are moved down to MTRR6, if MTRR6 is empty so should MTRR7 also be empty
        MtrrV6.ExeCacheStartAddr = AmdExeAddrMapPtr[i].ExeCacheStartAddr;
        MtrrV6.ExeCacheSize = AmdExeAddrMapPtr[i].ExeCacheSize;
      } else if ((Result.OverlapType == Disjoint) ||
                 (Result.OverlapType == NotCombinable)) {
        // MTRR6 is in use, and request does not overlap with MTRR6, check MTRR7
        CompareRegions (AmdExeAddrMapPtr[i], MtrrV7, &Result, StdHeader);
        if (Result.OverlapType == EmptySet) {
          // MTRR7 is empty. Allocate request into MTRR7.
          MtrrV7.ExeCacheStartAddr = AmdExeAddrMapPtr[i].ExeCacheStartAddr;
          MtrrV7.ExeCacheSize = AmdExeAddrMapPtr[i].ExeCacheSize;
        } else if ((Result.OverlapType == Disjoint) ||
                   (Result.OverlapType == NotCombinable)) {
          // MTRR7 is also in use and request does not overlap - error: 3rd region above 1M
          AgesaStatus = AGESA_ERROR;
          PutEventLog (AgesaStatus,
                         (CPU_EVENT_EXECUTION_CACHE_ALLOCATION_ERROR + AGESA_THREE_CACHE_REGIONS_ABOVE_1MB),
                         i, AmdExeAddrMapPtr[i].ExeCacheStartAddr, AmdExeAddrMapPtr[i].ExeCacheSize, 0, StdHeader);
          break;
        } else {
          // Merge request with MTRR7
          MtrrV7.ExeCacheStartAddr = Result.MergedStartAddr;
          MtrrV7.ExeCacheSize = Result.MergedSize;
          if (CacheInfoPtr->CarExeType == LimitedByL2Size) {
            RemainingExecutionCacheSize += Result.OverlapAmount;
          }
        }
      } else {
        // Request overlaps with MTRR6, Merge request with MTRR6
        MtrrV6.ExeCacheStartAddr = Result.MergedStartAddr;
        MtrrV6.ExeCacheSize = Result.MergedSize;
        if (CacheInfoPtr->CarExeType == LimitedByL2Size) {
          RemainingExecutionCacheSize += Result.OverlapAmount;
        }
        CompareRegions (MtrrV6, MtrrV7, &Result, StdHeader);
        if ((Result.OverlapType != Disjoint) &&
            (Result.OverlapType != EmptySet) &&
            (Result.OverlapType != NotCombinable)) {
          // MTRR6 and MTRR7 now overlap, merge them into MTRR6
          MtrrV6.ExeCacheStartAddr = Result.MergedStartAddr;
          MtrrV6.ExeCacheSize = Result.MergedSize;
          MtrrV7.ExeCacheStartAddr = 0;
          MtrrV7.ExeCacheSize = 0;
          if (CacheInfoPtr->CarExeType == LimitedByL2Size) {
            RemainingExecutionCacheSize += Result.OverlapAmount;
          }
        }
      }

      // Set the VarMTRRs.  Base first, then size/mask; this allows for expanding the region safely.
      if (MtrrV6.ExeCacheSize != 0) {
        MsrData = (UINT64) ( 0xFFFFFFFF00000000ULL | ((0xFFFFFFFFUL - (MtrrV6.ExeCacheSize - 1)) | 0x0800UL));
        MsrData &= CacheInfoPtr->VariableMtrrMask;
        SecondMsrData = (UINT64) ( MtrrV6.ExeCacheStartAddr | (WP_IO & 0xFULL));
      } else {
        MsrData = 0;
        SecondMsrData = 0;
      }
      LibAmdMsrWrite (AMD_MTRR_VARIABLE_BASE6, &SecondMsrData, StdHeader);
      LibAmdMsrWrite ((AMD_MTRR_VARIABLE_BASE6 + 1), &MsrData, StdHeader);

      if (MtrrV7.ExeCacheSize != 0) {
        MsrData = (UINT64) ( 0xFFFFFFFF00000000ULL | ((0xFFFFFFFFUL - (MtrrV7.ExeCacheSize - 1)) | 0x0800UL));
        MsrData &= CacheInfoPtr->VariableMtrrMask;
        SecondMsrData = (UINT64) ( MtrrV7.ExeCacheStartAddr | (WP_IO & 0xFULL));
      } else {
        MsrData = 0;
        SecondMsrData = 0;
      }
      LibAmdMsrWrite (AMD_MTRR_VARIABLE_BASE7, &SecondMsrData, StdHeader);
      LibAmdMsrWrite ((AMD_MTRR_VARIABLE_BASE7 + 1), &MsrData, StdHeader);
    } // endif of MTRR region check
  } // end of requests For loop

  return AgesaStatus;
}

/*---------------------------------------------------------------------------------------*/
/**
 * This function calculates available L2 cache space for ROM execution.
 *
 * @param[in]   AmdGetExeSizeParams  Pointer to the start of AmdGetExeSizeParamsPtr structure
 *
 * @retval      AGESA_SUCCESS      No error
 * @retval      AGESA_ALERT        No cache available for execution cache.
 *
 */
AGESA_STATUS
AmdGetAvailableExeCacheSize (
  IN OUT   AMD_GET_EXE_SIZE_PARAMS *AmdGetExeSizeParams
  )
{
  UINT8     WayUsedForCar;
  UINT8     L2Assoc;
  UINT32    L2Size;
  UINT32    L2WaySize;
  UINT32    CurrentCoreNum;
  UINT8     L2Ways;
  UINT8     Ignored;
  UINT32    DieNumber;
  UINT32    TotalCores;
  CPUID_DATA  CpuIdDataStruct;
  CACHE_INFO  *CacheInfoPtr;
  AP_MAIL_INFO ApMailboxInfo;
  AGESA_STATUS IgnoredStatus;
  CPU_SPECIFIC_SERVICES *FamilySpecificServices;

  GetCpuServicesOfCurrentCore ((CONST CPU_SPECIFIC_SERVICES **)&FamilySpecificServices, &AmdGetExeSizeParams->StdHeader);
  FamilySpecificServices->GetCacheInfo (FamilySpecificServices, (CONST VOID **) &CacheInfoPtr, &Ignored, &AmdGetExeSizeParams->StdHeader);
  // CAR_EXE mode is either "Limited by L2 size" or "Infinite Execution space"
  ASSERT (CacheInfoPtr->CarExeType < MaxCarExeMode);
  if (CacheInfoPtr->CarExeType == InfiniteExe) {
    AmdGetExeSizeParams->AvailableExeCacheSize = SIZE_INFINITE_EXE_CACHE;
    return AGESA_SUCCESS;
  }

  // EXE cache size is limited by size of the L2, minus previous allocations for stack, heap, etc.
  // Check for L2 cache size and way size
  LibAmdCpuidRead (AMD_CPUID_L2L3Cache_L2TLB, &CpuIdDataStruct, &AmdGetExeSizeParams->StdHeader);
  L2Assoc = (UINT8) ((CpuIdDataStruct.ECX_Reg >> 12) & 0x0F);

  // get L2Ways from L2 Association to Way translation table
  L2Ways = L2AssocToL2WayTranslationTable[L2Assoc];
  ASSERT (L2Ways != 0xFF);

  // get L2Size
  L2Size = 1024 * ((CpuIdDataStruct.ECX_Reg >> 16) & 0xFFFF);

  // get each L2WaySize
  L2WaySize = L2Size / L2Ways;

  // Determine the size for execution cache
  if (IsBsp (&AmdGetExeSizeParams->StdHeader, &IgnoredStatus)) {
    // BSC (Boot Strap Core)
    WayUsedForCar = Ceiling (CacheInfoPtr->BspStackSize, L2WaySize) +
                    Ceiling (CacheInfoPtr->MemTrainingBufferSize, L2WaySize) +
                    Ceiling (AMD_HEAP_SIZE_PER_CORE , L2WaySize) +
                    Ceiling (CacheInfoPtr->SharedMemSize, L2WaySize);
  } else {
    // AP (Application Processor)
    GetCurrentCore (&CurrentCoreNum, &AmdGetExeSizeParams->StdHeader);

    GetApMailbox (&ApMailboxInfo.Info, &AmdGetExeSizeParams->StdHeader);
    DieNumber = (1 << ApMailboxInfo.Fields.ModuleType);
    GetActiveCoresInCurrentSocket (&TotalCores, &AmdGetExeSizeParams->StdHeader);
    ASSERT ((TotalCores % DieNumber) == 0);
    if ((CurrentCoreNum % (TotalCores / DieNumber)) == 0) {
      WayUsedForCar = Ceiling (CacheInfoPtr->Core0StackSize , L2WaySize) +
                      Ceiling (CacheInfoPtr->MemTrainingBufferSize, L2WaySize) +
                      Ceiling (AMD_HEAP_SIZE_PER_CORE , L2WaySize) +
                      Ceiling (CacheInfoPtr->SharedMemSize, L2WaySize);
    } else {
      WayUsedForCar = Ceiling (CacheInfoPtr->Core1StackSize , L2WaySize) +
                      Ceiling (AMD_HEAP_SIZE_PER_CORE , L2WaySize) +
                      Ceiling (CacheInfoPtr->SharedMemSize, L2WaySize);
    }
  }

  ASSERT (WayUsedForCar < L2Ways);

  if (WayUsedForCar < L2Ways) {
    AmdGetExeSizeParams->AvailableExeCacheSize = L2WaySize * (L2Ways - WayUsedForCar);
    return AGESA_SUCCESS;
  } else {
    AmdGetExeSizeParams->AvailableExeCacheSize = 0;
    return AGESA_ALERT;
  }
}


/*---------------------------------------------------------------------------------------*/
/**
 * This function rounds a quotient up if the remainder is not zero.
 *
 * @param[in]   Divisor            The divisor
 * @param[in]   Dividend           The dividend
 *
 * @retval      Value              Rounded quotient
 *
 */
UINT8
STATIC
Ceiling (
  IN UINT32 Divisor,
  IN UINT32 Dividend
  )
{
  if ((Divisor % Dividend) == 0) {
    return (UINT8) (Divisor / Dividend);
  } else {
    return (UINT8) ((Divisor / Dividend) + 1);
  }
}


/*---------------------------------------------------------------------------------------*/
/**
 * This function calculates the amount of cache that has already been allocated on the
 * executing core.
 *
 * @param[in]   StdHeader       Handle to config for library and services
 *
 * @returns     Allocated size in bytes
 *
 */
UINT32
STATIC
CalculateOccupiedExeCache (
  IN AMD_CONFIG_PARAMS *StdHeader
  )
{
  UINT64                     OccupExeCacheSize;
  UINT64                     MsrData;
  UINT8                      i;

  MsrData = 0;
  OccupExeCacheSize = 0;

  //
  //Calculate Variable MTRR base 6~7
  //
  for (i = 0; i < 2; i++) {
    LibAmdMsrRead ((AMD_MTRR_VARIABLE_BASE6 + (2*i)), &MsrData, StdHeader);
    if (MsrData != 0) {
      LibAmdMsrRead ((AMD_MTRR_VARIABLE_BASE6 + (2*i + 1)), &MsrData, StdHeader);
      OccupExeCacheSize = OccupExeCacheSize + ((~((MsrData & (0xFFFF8000)) - 1))&0xFFFF8000);
    }
  }

  //
  //Calculate Fixed MTRR base D0000~F8000
  //
  for (i = 0; i < 6; i++) {
    LibAmdMsrRead ((AMD_MTRR_FIX4K_BASE + 2 + i), &MsrData, StdHeader);
    if (MsrData!= 0) {
      OccupExeCacheSize = OccupExeCacheSize + 0x8000;
    }
  }

  return (UINT32)OccupExeCacheSize;
}


/*---------------------------------------------------------------------------------------*/
/**
 * This function compares two memory regions for overlap and returns the combined
 *  Base,Size  to describe the new combined region.
 *
 * There are 13 cases for how two regions may overlap:  key: [] region A, ** region B
 * 1- [  ] ***      9-  ***  [  ]   disjoint regions
 * 2- [  ]***       10- ***[  ]     adjacent regions
 * 3- [ ***]        11- **[**]      common ending
 * 4- [  *]**       12- *[**  ]     extending
 * 5- [ ** ]        13- *[*]*       contained
 * 6- [***  ]                       common start, contained
 * 7- [***]                         identity
 * 8- [**]**                        common start, extending
 * 0- one of the regions is empty (has base=0)
 *
 * @param[in]     ARegion       pointer to the base,size pair that describes region A
 * @param[in]     BRegion       pointer to the base,size pair that describes region B
 * @param[in,out] CRegion       pointer to the base,size pair that describes region C This struct also has the
 *                              overlap type and the amount of overlap between the regions.
 * @param[in]     StdHeader     Handle to config for library and services
 *
 * @returns       void, nothing
 */

VOID
STATIC
CompareRegions (
  IN       EXECUTION_CACHE_REGION  ARegion,
  IN       EXECUTION_CACHE_REGION  BRegion,
  IN OUT   MERGED_CACHE_REGION     *CRegion,
  IN       AMD_CONFIG_PARAMS       *StdHeader
  )
{
  // Use Int64 to handle regions ending at or above the 4G boundary.
  UINT64        EndOfA;
  UINT64        EndOfB;


  if ((BRegion.ExeCacheStartAddr == 0) ||
      (ARegion.ExeCacheStartAddr == 0)) {
    CRegion->MergedStartAddr  =
    CRegion->MergedSize       =
    CRegion->OverlapAmount    = 0;
    CRegion->OverlapType = EmptySet;
    return;
  }
  if (BRegion.ExeCacheStartAddr < ARegion.ExeCacheStartAddr) {
    //swap regions A & B. this collapses types 9-13 onto 1-5 and reduces the number of tests
    CRegion->MergedStartAddr  = ARegion.ExeCacheStartAddr;
    CRegion->MergedSize       = ARegion.ExeCacheSize;
    ARegion                   = BRegion;
    BRegion.ExeCacheStartAddr = CRegion->MergedStartAddr;
    BRegion.ExeCacheSize      = CRegion->MergedSize;
  }
  CRegion->MergedStartAddr  =
  CRegion->MergedSize       =
  CRegion->OverlapType      =
  CRegion->OverlapAmount    = 0;

  if (ARegion.ExeCacheStartAddr == BRegion.ExeCacheStartAddr) {
    // Common start, cases 6,7, or 8
    if (ARegion.ExeCacheSize == BRegion.ExeCacheSize) {
      // case 7, identity. Need to recover the overlap size
      CRegion->MergedStartAddr  = ARegion.ExeCacheStartAddr;
      CRegion->MergedSize       = ARegion.ExeCacheSize;
      CRegion->OverlapAmount    = ARegion.ExeCacheSize;
      CRegion->OverlapType      = Identity;
    } else if (ARegion.ExeCacheSize < BRegion.ExeCacheSize) {
      // case 8, common start extending
      CRegion->MergedStartAddr  = ARegion.ExeCacheStartAddr;
      CRegion->MergedSize       = BRegion.ExeCacheSize;
      CRegion->OverlapType      = CommonStartExtending;
      CRegion->OverlapAmount    = ARegion.ExeCacheSize;
    } else {
      // case 6, common start contained
      CRegion->MergedStartAddr  = ARegion.ExeCacheStartAddr;
      CRegion->MergedSize       = ARegion.ExeCacheSize;
      CRegion->OverlapType      = CommonStartContained;
      CRegion->OverlapAmount    = BRegion.ExeCacheSize;
    }
  } else {
    // A_Base is less than B_Base. check for cases 1-5
    EndOfA = ((UINT64) ARegion.ExeCacheStartAddr) + ((UINT64) ARegion.ExeCacheSize);

    if (EndOfA < ((UINT64) BRegion.ExeCacheStartAddr)) {
      // case 1, disjoint
      CRegion->MergedStartAddr  =
      CRegion->MergedSize       =
      CRegion->OverlapAmount    = 0;
      CRegion->OverlapType = Disjoint;

    } else if (EndOfA == ((UINT64) BRegion.ExeCacheStartAddr)) {
      // case 2, adjacent
      CRegion->OverlapType = Adjacent;
      CRegion->MergedStartAddr  = ARegion.ExeCacheStartAddr;
      CRegion->MergedSize       = ARegion.ExeCacheSize + BRegion.ExeCacheSize;
      CRegion->OverlapAmount    = 0;
    } else {
      // EndOfA is > B_Base. check for cases 3,4,5
      EndOfB = ((UINT64) BRegion.ExeCacheStartAddr) + ((UINT64) BRegion.ExeCacheSize);

      if ( EndOfA < EndOfB) {
        // case 4, extending
        CRegion->OverlapType = Extending;
        CRegion->MergedStartAddr  = ARegion.ExeCacheStartAddr;
        CRegion->MergedSize       = (UINT32) (EndOfB - ((UINT64) ARegion.ExeCacheStartAddr));
        CRegion->OverlapAmount    = (UINT32) (EndOfA - ((UINT64) BRegion.ExeCacheStartAddr));
      } else {
        // case 3, same end; or case 5, contained
        CRegion->OverlapType = Contained;
        CRegion->MergedStartAddr  = ARegion.ExeCacheStartAddr;
        CRegion->MergedSize       = ARegion.ExeCacheSize;
        CRegion->OverlapAmount    = BRegion.ExeCacheSize;
      }
    }
  } // endif
  // Once we have combined the regions, they must still obey the MTRR size and boundary rules
  if ( CRegion->OverlapType != Disjoint ) {
    if ((!(IsPowerOfTwo (CRegion->MergedSize))) ||
       ((CRegion->MergedStartAddr % CRegion->MergedSize) != 0) ) {
      CRegion->OverlapType = NotCombinable;
    }
  }

}


/*---------------------------------------------------------------------------------------*/
/**
 * This local function tests the parameter for being an even power of two
 *
 * @param[in]   TestNumber    Number to check
 *
 * @retval      TRUE - TestNumber is a power of two,
 * @retval      FALSE - TestNumber is not a power of two
 *
 */
BOOLEAN
STATIC
IsPowerOfTwo (
  IN      UINT32                  TestNumber
  )
{
  UINT32      PowerTwo;

  ASSERT (TestNumber >= 0x8000UL);
  PowerTwo = 0x8000UL;                    // Start at 32K
  while ( TestNumber > PowerTwo ) {
    PowerTwo = PowerTwo * 2;
  }
  return (((TestNumber % PowerTwo) == 0) ? TRUE: FALSE);
}


