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

/*----------------------------------------------------------------------------------------
 *                             M O D U L E S    U S E D
 *----------------------------------------------------------------------------------------
 */

#include "AGESA.h"
#include "amdlib.h"
#include "Ids.h"
#include "cpuRegisters.h"
#include "GeneralServices.h"
#include "cpuServices.h"
#include "cpuCacheInit.h"
#include "cpuFamilyTranslation.h"
#include "heapManager.h"
#include "cpuLateInit.h"
#include "Filecode.h"
CODE_GROUP (G1_PEICC)
RDATA_GROUP (G1_PEICC)

#define FILECODE LEGACY_PROC_HOBTRANSFER_FILECODE
/*----------------------------------------------------------------------------------------
 *                   D E F I N I T I O N S    A N D    M A C R O S
 *----------------------------------------------------------------------------------------
 */

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


/*----------------------------------------------------------------------------------------
 *           P R O T O T Y P E S     O F     L O C A L     F U  N C T I O N S
 *----------------------------------------------------------------------------------------
 */

/*----------------------------------------------------------------------------------------
 *                          P U B L I C     F U N C T I O N S
 *----------------------------------------------------------------------------------------
 */

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

/* -----------------------------------------------------------------------------*/
/**
 *
 *  CopyHeapToTempRamAtPost
 *
 *     This function copies BSP heap content to RAM
 *
 *    @param[in,out]   StdHeader   - Pointer to AMD_CONFIG_PARAMS struct.
 *
 *    @retval          AGESA_STATUS
 *
 */
AGESA_STATUS
CopyHeapToTempRamAtPost (
  IN OUT   AMD_CONFIG_PARAMS *StdHeader
  )
{
  UINT8 *BaseAddressInCache;
  UINT8 *BaseAddressInTempMem;
  UINT8 *Source;
  UINT8 *Destination;
  UINT8  AlignTo16ByteInCache;
  UINT8  AlignTo16ByteInTempMem;
  UINT8  Ignored;
  UINT32 SizeOfNodeData;
  UINT32 TotalSize;
  UINT32 HeapRamFixMtrr;
  UINT32 HeapRamVariableMtrr;
  UINT32 HeapInCacheOffset;
  UINT64 MsrData;
  UINT64 VariableMtrrBase;
  UINT64 VariableMtrrMask;
  UINTN  AmdHeapRamAddress;
  AGESA_STATUS IgnoredStatus;
  BUFFER_NODE *HeapInCache;
  BUFFER_NODE *HeapInTempMem;
  HEAP_MANAGER *HeapManagerInCache;
  HEAP_MANAGER *HeapManagerInTempMem;
  CACHE_INFO *CacheInfoPtr;
  CPU_SPECIFIC_SERVICES *FamilySpecificServices;

  AmdHeapRamAddress = (UINTN) UserOptions.CfgHeapDramAddress;
  //
  //If the user define address above 1M, Mem Init has already set
  //whole available memory as WB cacheable.
  //
  if (AmdHeapRamAddress < 0x100000) {
    // Region below 1MB
    // Fixed MTRR region
    // turn on modification bit
    LibAmdMsrRead (MSR_SYS_CFG, &MsrData, StdHeader);
    MsrData |= 0x80000;
    LibAmdMsrWrite (MSR_SYS_CFG, &MsrData, StdHeader);

    if (AmdHeapRamAddress >= 0xC0000) {
      //
      // 0xC0000 ~ 0xFFFFF
      //
      HeapRamFixMtrr = (UINT32) (AMD_MTRR_FIX4k_C0000 + (((AmdHeapRamAddress >> 16) & 0x3) * 2));
      MsrData = (UINT64)AMD_MTRR_FIX4K_UC_DRAM;
      LibAmdMsrWrite (HeapRamFixMtrr, &MsrData, StdHeader);
      LibAmdMsrWrite ((HeapRamFixMtrr + 1), &MsrData, StdHeader);
    } else if (AmdHeapRamAddress >= 0x80000) {
      //
      // 0x80000~0xBFFFF
      //
      HeapRamFixMtrr = (UINT32) (AMD_MTRR_FIX16k_80000 + ((AmdHeapRamAddress >> 17) & 0x1));
      MsrData = (UINT64)AMD_MTRR_FIX16K_UC_DRAM;
      LibAmdMsrWrite (HeapRamFixMtrr, &MsrData, StdHeader);
    } else {
      //
      // 0x0 ~ 0x7FFFF
      //
      LibAmdMsrRead (AMD_MTRR_FIX64k_00000, &MsrData, StdHeader);
      MsrData = MsrData & (~(0xFF << (8 * ((AmdHeapRamAddress >> 16) & 0x7))));
      MsrData = MsrData | (AMD_MTRR_FIX64K_UC_DRAM << (8 * ((AmdHeapRamAddress >> 16) & 0x7)));
      LibAmdMsrWrite (AMD_MTRR_FIX64k_00000, &MsrData, StdHeader);
    }

    // Turn on MTRR enable bit and turn off modification bit
    LibAmdMsrRead (MSR_SYS_CFG, &MsrData, StdHeader);
    MsrData |= 0x40000;
    MsrData &= 0xFFFFFFFFFFF7FFFFull;
    LibAmdMsrWrite (MSR_SYS_CFG, &MsrData, StdHeader);
  } else {
    // Region above 1MB
    // Variable MTRR region
    // Get family specific cache Info
    GetCpuServicesOfCurrentCore ((const CPU_SPECIFIC_SERVICES **)&FamilySpecificServices, StdHeader);
    FamilySpecificServices->GetCacheInfo (FamilySpecificServices, (const VOID **) &CacheInfoPtr, &Ignored, StdHeader);

    // Find an empty MTRRphysBase/MTRRphysMask
    for (HeapRamVariableMtrr = AMD_MTRR_VARIABLE_HEAP_BASE;
         HeapRamVariableMtrr >= AMD_MTRR_VARIABLE_BASE0;
         HeapRamVariableMtrr--) {
      LibAmdMsrRead (HeapRamVariableMtrr, &VariableMtrrBase, StdHeader);
      LibAmdMsrRead ((HeapRamVariableMtrr + 1), &VariableMtrrMask, StdHeader);
      if ((VariableMtrrBase == 0) && (VariableMtrrMask == 0)) {
        break;
      }
    }
    if (HeapRamVariableMtrr < AMD_MTRR_VARIABLE_BASE0) {
      // All variable MTRR is used.
      ASSERT (FALSE);
    }

    // Set variable MTRR base and mask
    // If the address ranges of two or more MTRRs overlap
    // and if at least one of the memory types is UC, the UC memory type is used.
    VariableMtrrBase = (UINT64) (AmdHeapRamAddress & CacheInfoPtr->HeapBaseMask);
    VariableMtrrMask = CacheInfoPtr->VariableMtrrHeapMask & (UINT64)AMD_HEAP_MTRR_MASK;
    LibAmdMsrWrite (HeapRamVariableMtrr, &VariableMtrrBase, StdHeader);
    LibAmdMsrWrite ((HeapRamVariableMtrr + 1), &VariableMtrrMask, StdHeader);
  }
  // Copying Heap content
  if (IsBsp (StdHeader, &IgnoredStatus)) {
    TotalSize = sizeof (HEAP_MANAGER);
    SizeOfNodeData = 0;
    AlignTo16ByteInTempMem = 0;
    BaseAddressInCache = (UINT8 *) (intptr_t) StdHeader->HeapBasePtr;
    HeapManagerInCache = (HEAP_MANAGER *) BaseAddressInCache;
    HeapInCacheOffset = HeapManagerInCache->FirstActiveBufferOffset;
    HeapInCache = (BUFFER_NODE *) (BaseAddressInCache + HeapInCacheOffset);

    BaseAddressInTempMem = (UINT8 *) (intptr_t) UserOptions.CfgHeapDramAddress;
    HeapManagerInTempMem = (HEAP_MANAGER *) BaseAddressInTempMem;
    HeapInTempMem = (BUFFER_NODE *) (BaseAddressInTempMem + TotalSize);

    // copy heap from cache to temp memory.
    // only heap with persist great than HEAP_LOCAL_CACHE will be copied.
    // Note: Only copy heap with persist greater than HEAP_LOCAL_CACHE.
    while (HeapInCacheOffset != AMD_HEAP_INVALID_HEAP_OFFSET) {
      if (HeapInCache->Persist > HEAP_LOCAL_CACHE) {
        AlignTo16ByteInCache = HeapInCache->PadSize;
        AlignTo16ByteInTempMem = (UINT8) ((0x10 - (((UINTN) (VOID *) HeapInTempMem + sizeof (BUFFER_NODE) + SIZE_OF_SENTINEL) & 0xF)) & 0xF);
        SizeOfNodeData = HeapInCache->BufferSize - AlignTo16ByteInCache;
        TotalSize = (UINT32) (TotalSize  + sizeof (BUFFER_NODE) + SizeOfNodeData + AlignTo16ByteInTempMem);
        Source = (UINT8 *) HeapInCache + sizeof (BUFFER_NODE) + AlignTo16ByteInCache;
        Destination = (UINT8 *) HeapInTempMem + sizeof (BUFFER_NODE) + AlignTo16ByteInTempMem;
        LibAmdMemCopy  (HeapInTempMem, HeapInCache, sizeof (BUFFER_NODE), StdHeader);
        LibAmdMemCopy  (Destination, Source, SizeOfNodeData, StdHeader);
        HeapInTempMem->OffsetOfNextNode = TotalSize;
        HeapInTempMem->BufferSize = SizeOfNodeData + AlignTo16ByteInTempMem;
        HeapInTempMem->PadSize = AlignTo16ByteInTempMem;
        HeapInTempMem = (BUFFER_NODE *) (BaseAddressInTempMem + TotalSize);
      }
      HeapInCacheOffset = HeapInCache->OffsetOfNextNode;
      HeapInCache = (BUFFER_NODE *) (BaseAddressInCache + HeapInCacheOffset);
    }
    // initialize heap manager
    if (TotalSize == sizeof (HEAP_MANAGER)) {
      // heap is empty
      HeapManagerInTempMem->UsedSize = sizeof (HEAP_MANAGER);
      HeapManagerInTempMem->FirstActiveBufferOffset = AMD_HEAP_INVALID_HEAP_OFFSET;
      HeapManagerInTempMem->FirstFreeSpaceOffset = sizeof (HEAP_MANAGER);
    } else {
      // heap is NOT empty
      HeapManagerInTempMem->UsedSize = TotalSize;
      HeapManagerInTempMem->FirstActiveBufferOffset = sizeof (HEAP_MANAGER);
      HeapManagerInTempMem->FirstFreeSpaceOffset = TotalSize;
      HeapInTempMem = (BUFFER_NODE *) (BaseAddressInTempMem + TotalSize - SizeOfNodeData - AlignTo16ByteInTempMem - sizeof (BUFFER_NODE));
      HeapInTempMem->OffsetOfNextNode = AMD_HEAP_INVALID_HEAP_OFFSET;
      HeapInTempMem = (BUFFER_NODE *) (BaseAddressInTempMem + TotalSize);
    }
    // heap signature
    HeapManagerInCache->Signature = 0x00000000;
    HeapManagerInTempMem->Signature = HEAP_SIGNATURE_VALID;
    // Free space node
    HeapInTempMem->BufferSize = (UINT32) (AMD_HEAP_SIZE_PER_CORE - TotalSize);
    HeapInTempMem->OffsetOfNextNode = AMD_HEAP_INVALID_HEAP_OFFSET;
  }
  return AGESA_SUCCESS;
}


/* -----------------------------------------------------------------------------*/
/**
 *
 *  CopyHeapToMainRamAtPost
 *
 *    This function copies Temp Ram heap content to Main Ram
 *
 *    @param[in,out]   StdHeader   - Pointer to AMD_CONFIG_PARAMS struct.
 *
 *    @retval          AGESA_STATUS
 *
 */
AGESA_STATUS
CopyHeapToMainRamAtPost (
  IN OUT   AMD_CONFIG_PARAMS *StdHeader
  )
{
  UINT8 *BaseAddressInTempMem;
  UINT8 *BaseAddressInMainMem;
  UINT8 *Source;
  UINT8 *Destination;
  UINT8  AlignTo16ByteInTempMem;
  UINT8  AlignTo16ByteInMainMem;
  UINT8  Ignored;
  UINT32 SizeOfNodeData;
  UINT32 TotalSize;
  UINT32 HeapInTempMemOffset;
  UINT32 HeapRamVariableMtrr;
  UINT64 VariableMtrrBase;
  UINT64 VariableMtrrMask;
  AGESA_STATUS IgnoredStatus;
  BUFFER_NODE *HeapInTempMem;
  BUFFER_NODE *HeapInMainMem;
  HEAP_MANAGER *HeapManagerInTempMem;
  HEAP_MANAGER *HeapManagerInMainMem;
  AGESA_BUFFER_PARAMS AgesaBuffer;
  CACHE_INFO *CacheInfoPtr;
  CPU_SPECIFIC_SERVICES *FamilySpecificServices;

  if (IsBsp (StdHeader, &IgnoredStatus)) {
    TotalSize = sizeof (HEAP_MANAGER);
    SizeOfNodeData = 0;
    AlignTo16ByteInMainMem = 0;
    BaseAddressInTempMem = (UINT8 *) (intptr_t) StdHeader->HeapBasePtr;
    HeapManagerInTempMem = (HEAP_MANAGER *) (intptr_t) StdHeader->HeapBasePtr;
    HeapInTempMemOffset = HeapManagerInTempMem->FirstActiveBufferOffset;
    HeapInTempMem = (BUFFER_NODE *) (BaseAddressInTempMem + HeapInTempMemOffset);

    AgesaBuffer.StdHeader = *StdHeader;
    AgesaBuffer.BufferHandle = AMD_HEAP_IN_MAIN_MEMORY_HANDLE;
    AgesaBuffer.BufferLength = AMD_HEAP_SIZE_PER_CORE;
    if (AgesaAllocateBuffer (0, &AgesaBuffer) != AGESA_SUCCESS) {
      return AGESA_ERROR;
    }
    BaseAddressInMainMem = (UINT8 *) AgesaBuffer.BufferPointer;
    HeapManagerInMainMem = (HEAP_MANAGER *) BaseAddressInMainMem;
    HeapInMainMem = (BUFFER_NODE *) (BaseAddressInMainMem + TotalSize);
    LibAmdMemFill (BaseAddressInMainMem, 0x00, AMD_HEAP_SIZE_PER_CORE, StdHeader);
    // copy heap from temp memory to main memory.
    // only heap with persist great than HEAP_TEMP_MEM will be copied.
    // Note: Only copy heap buffers with persist greater than HEAP_TEMP_MEM.
    while (HeapInTempMemOffset != AMD_HEAP_INVALID_HEAP_OFFSET) {
      if (HeapInTempMem->Persist > HEAP_TEMP_MEM) {
        AlignTo16ByteInTempMem = HeapInTempMem->PadSize;
        AlignTo16ByteInMainMem = (UINT8) ((0x10 - (((UINTN) (VOID *) HeapInMainMem  + sizeof (BUFFER_NODE) + SIZE_OF_SENTINEL) & 0xF)) & 0xF);
        SizeOfNodeData = HeapInTempMem->BufferSize - AlignTo16ByteInTempMem;
        TotalSize = (UINT32) (TotalSize  + sizeof (BUFFER_NODE) + SizeOfNodeData + AlignTo16ByteInMainMem);
        Source = (UINT8 *) HeapInTempMem + sizeof (BUFFER_NODE) + AlignTo16ByteInTempMem;
        Destination = (UINT8 *) HeapInMainMem + sizeof (BUFFER_NODE) + AlignTo16ByteInMainMem;
        LibAmdMemCopy  (HeapInMainMem, HeapInTempMem, sizeof (BUFFER_NODE), StdHeader);
        LibAmdMemCopy  (Destination, Source, SizeOfNodeData, StdHeader);
        HeapInMainMem->OffsetOfNextNode = TotalSize;
        HeapInMainMem->BufferSize = SizeOfNodeData + AlignTo16ByteInMainMem;
        HeapInMainMem->PadSize = AlignTo16ByteInMainMem;
        HeapInMainMem = (BUFFER_NODE *) (BaseAddressInMainMem + TotalSize);
      }
      HeapInTempMemOffset = HeapInTempMem->OffsetOfNextNode;
      HeapInTempMem = (BUFFER_NODE *) (BaseAddressInTempMem + HeapInTempMemOffset);
    }
    // initialize heap manager
    if (TotalSize == sizeof (HEAP_MANAGER)) {
      // heap is empty
      HeapManagerInMainMem->UsedSize = sizeof (HEAP_MANAGER);
      HeapManagerInMainMem->FirstActiveBufferOffset = AMD_HEAP_INVALID_HEAP_OFFSET;
      HeapManagerInMainMem->FirstFreeSpaceOffset = sizeof (HEAP_MANAGER);
    } else {
      // heap is NOT empty
      HeapManagerInMainMem->UsedSize = TotalSize;
      HeapManagerInMainMem->FirstActiveBufferOffset = sizeof (HEAP_MANAGER);
      HeapManagerInMainMem->FirstFreeSpaceOffset = TotalSize;
      HeapInMainMem = (BUFFER_NODE *) (BaseAddressInMainMem + TotalSize - SizeOfNodeData - AlignTo16ByteInMainMem - sizeof (BUFFER_NODE));
      HeapInMainMem->OffsetOfNextNode = AMD_HEAP_INVALID_HEAP_OFFSET;
      HeapInMainMem = (BUFFER_NODE *) (BaseAddressInMainMem + TotalSize);
    }
    // heap signature
    HeapManagerInTempMem->Signature = 0x00000000;
    HeapManagerInMainMem->Signature = HEAP_SIGNATURE_VALID;
    // Free space node
    HeapInMainMem->BufferSize = AMD_HEAP_SIZE_PER_CORE - TotalSize;
    HeapInMainMem->OffsetOfNextNode = AMD_HEAP_INVALID_HEAP_OFFSET;
  }
  // if address of heap in temp memory is above 1M, then we must used one variable MTRR.
  if (StdHeader->HeapBasePtr >= 0x100000) {
    // Find out which variable MTRR was used in CopyHeapToTempRamAtPost.
    GetCpuServicesOfCurrentCore ((const CPU_SPECIFIC_SERVICES **) &FamilySpecificServices, StdHeader);
    FamilySpecificServices->GetCacheInfo (FamilySpecificServices, (const VOID **) &CacheInfoPtr, &Ignored, StdHeader);
    for (HeapRamVariableMtrr = AMD_MTRR_VARIABLE_HEAP_BASE;
         HeapRamVariableMtrr >= AMD_MTRR_VARIABLE_BASE0;
         HeapRamVariableMtrr--) {
      LibAmdMsrRead (HeapRamVariableMtrr, &VariableMtrrBase, StdHeader);
      LibAmdMsrRead ((HeapRamVariableMtrr + 1), &VariableMtrrMask, StdHeader);
      if ((VariableMtrrBase == (UINT64) (StdHeader->HeapBasePtr & CacheInfoPtr->HeapBaseMask)) &&
          (VariableMtrrMask == (UINT64) (CacheInfoPtr->VariableMtrrHeapMask & (UINT64) AMD_HEAP_MTRR_MASK))) {
        break;
      }
    }
    if (HeapRamVariableMtrr >= AMD_MTRR_VARIABLE_BASE0) {
      // Clear variable MTRR which set in CopyHeapToTempRamAtPost.
      VariableMtrrBase = 0;
      VariableMtrrMask = 0;
      LibAmdMsrWrite (HeapRamVariableMtrr, &VariableMtrrBase, StdHeader);
      LibAmdMsrWrite ((HeapRamVariableMtrr + 1), &VariableMtrrMask, StdHeader);
    }
  }
  return AGESA_SUCCESS;
}
