/* $NoKeywords:$ */
/**
 * @file
 *
 * AMD AGESA Basic Level Public APIs
 *
 * Contains basic Level Initialization routines.
 *
 * @xrefitem bom "File Content Label" "Release Content"
 * @e project:      AGESA
 * @e sub-project:  Interface
 * @e \$Revision: 35136 $   @e \$Date: 2010-07-16 11:29:48 +0800 (Fri, 16 Jul 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 "mm.h"
#include "mn.h"
#include "S3.h"
#include "mfs3.h"
#include "CommonInits.h"
#include "CreateStruct.h"
#include "Filecode.h"
#include "heapManager.h"
#include "Topology.h"
CODE_GROUP (G3_DXE)
RDATA_GROUP (G3_DXE)

#define FILECODE PROC_COMMON_AMDS3SAVE_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
 *----------------------------------------------------------------------------------------
 */
CONST UINT32 ROMDATA S3LateHeapTable[] =
{
  EVENT_LOG_BUFFER_HANDLE,
  SOCKET_DIE_MAP_HANDLE,
  NODE_ID_MAP_HANDLE,
  LOCAL_AP_MAIL_BOX_CACHE_HANDLE,
  IDS_CONTROL_HANDLE,
  AMD_S3_SCRIPT_SAVE_TABLE_HANDLE,
  AMD_PCIE_COMPLEX_DATA_HANDLE
};

#define S3LATE_TABLE_SIZE (sizeof (S3LateHeapTable) / sizeof (UINT32)) //(sizeof (S3LateHeapTable) / sizeof (S3LATE_HEAP_ELEMENT))


/*----------------------------------------------------------------------------------------
 *           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
 *----------------------------------------------------------------------------------------
 */
AGESA_STATUS
AmdS3SavePlatformConfigInit (
  IN OUT   PLATFORM_CONFIGURATION    *PlatformConfig,
  IN OUT   AMD_CONFIG_PARAMS         *StdHeader
  );

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

/*---------------------------------------------------------------------------------------*/
/**
 * Main entry point for the AMD_S3_SAVE function.
 *
 * This entry point is responsible for saving silicon component registers to the
 * SMM save area in preparation of entering system suspend-to-RAM mode.
 *
 * @param[in,out] AmdS3SaveParams   Required input parameters for the AMD_S3_SAVE
 *                                  entry point.
 *
 * @return        Aggregated status across all internal AMD S3 save calls invoked.
 *
 */
AGESA_STATUS
AmdS3Save (
  IN OUT   AMD_S3SAVE_PARAMS *AmdS3SaveParams
  )
{
  UINTN  i;
  UINT32 EarlyBufferSize;
  UINT32 LateBufferSize;
  UINT32 LateContextSize;
  UINT32 HeapSize;
  UINT8  *BufferPointer;
  UINT8  HeapStatus;
  ALLOCATE_HEAP_PARAMS HeapParams;
  LOCATE_HEAP_PTR LocateHeap;
  BUFFER_NODE *FreeSpaceNode;
  ALLOCATE_HEAP_PARAMS AllocParams;
  DEVICE_BLOCK_HEADER *MemoryRelatedDeviceList;
  DEVICE_BLOCK_HEADER *NonMemoryRelatedDeviceList;
  AGESA_STATUS ReturnStatus;
  VOID   *HeapPtrs[S3LATE_TABLE_SIZE];
  UINT32 HeapSizes[S3LATE_TABLE_SIZE];
  UINT32 HeapBuffersPresent;
  HEAP_MANAGER *HeapPtr;

  AGESA_TESTPOINT (TpIfAmdS3SaveEntry, &AmdS3SaveParams->StdHeader);

  ASSERT (AmdS3SaveParams != NULL);

  HeapBuffersPresent = 0;
  EarlyBufferSize = 0;
  LateBufferSize = 0;
  LateContextSize = 0;
  HeapSize = 0;
  NonMemoryRelatedDeviceList = NULL;
  MemoryRelatedDeviceList = NULL;
  ReturnStatus = AGESA_SUCCESS;

  IDS_SKIP_HOOK (IDS_BEFORE_S3_SAVE, AmdS3SaveParams, &(AmdS3SaveParams->StdHeader)) {

    // Get memory device list
    MemFS3GetDeviceList (&MemoryRelatedDeviceList, &AmdS3SaveParams->StdHeader);
    if (MemoryRelatedDeviceList != NULL) {
      // Determine size needed
      EarlyBufferSize = GetWorstCaseContextSize (MemoryRelatedDeviceList, INIT_RESUME, &AmdS3SaveParams->StdHeader);
    }

    if (UserOptions.CfgS3LateRestore) {
      for (i = 0; i < S3LATE_TABLE_SIZE; i++) {
        LocateHeap.BufferHandle = S3LateHeapTable[i];
        if (HeapLocateBuffer (&LocateHeap, &AmdS3SaveParams->StdHeader) == AGESA_SUCCESS) {
          HeapBuffersPresent++;
          HeapSize += LocateHeap.BufferSize;
          HeapPtrs[i] = LocateHeap.BufferPtr;
          HeapSizes[i] = LocateHeap.BufferSize;
        } else {
          HeapPtrs[i] = NULL;
          HeapSizes[i] = 0;
        }
      }

      // Determine heap data size requirements
      if (HeapBuffersPresent != 0) {
        HeapSize += ((sizeof (HEAP_MANAGER)) + (HeapBuffersPresent * ((sizeof (BUFFER_NODE)) + (NUM_OF_SENTINEL * SIZE_OF_SENTINEL) + 0xF))); // reserve 0xF per buffer node for 16 byte alignment
      }

      // Get non memory device list
      GetNonMemoryRelatedDeviceList (&NonMemoryRelatedDeviceList, &AmdS3SaveParams->StdHeader);

      if (NonMemoryRelatedDeviceList != NULL) {
        // Determine size needed
        LateContextSize = GetWorstCaseContextSize (NonMemoryRelatedDeviceList, S3_LATE_RESTORE, &AmdS3SaveParams->StdHeader);
      }
      LateBufferSize = HeapSize + LateContextSize;
      if (LateBufferSize != 0) {
        LateBufferSize += sizeof (S3_VOLATILE_STORAGE_HEADER);
      }
    }

    if ((EarlyBufferSize != 0) || (LateBufferSize != 0)) {
      //
      // Allocate a buffer
      //
      AllocParams.RequestedBufferSize = EarlyBufferSize + LateBufferSize;
      AllocParams.BufferHandle = AMD_S3_INFO_BUFFER_HANDLE;

      AGESA_TESTPOINT (TpIfBeforeAllocateS3SaveBuffer, &AmdS3SaveParams->StdHeader);
      if (HeapAllocateBuffer (&AllocParams, &AmdS3SaveParams->StdHeader) != AGESA_SUCCESS) {
        if (AGESA_ERROR > ReturnStatus) {
          ReturnStatus = AGESA_ERROR;
        }
      }
      AGESA_TESTPOINT (TpIfAfterAllocateS3SaveBuffer, &AmdS3SaveParams->StdHeader);

      if (EarlyBufferSize != 0) {
        AmdS3SaveParams->S3DataBlock.NvStorage = AllocParams.BufferPtr;
        SaveDeviceListContext (MemoryRelatedDeviceList,
                           AmdS3SaveParams->S3DataBlock.NvStorage,
                           INIT_RESUME,
                           &EarlyBufferSize,
                           &AmdS3SaveParams->StdHeader);

        AmdS3SaveParams->S3DataBlock.NvStorageSize = EarlyBufferSize;
      }

      if (LateBufferSize != 0) {
        BufferPointer = AllocParams.BufferPtr;
        AmdS3SaveParams->S3DataBlock.VolatileStorage = &(BufferPointer[EarlyBufferSize]);

        ((S3_VOLATILE_STORAGE_HEADER *) AmdS3SaveParams->S3DataBlock.VolatileStorage)->HeapOffset = 0;
        ((S3_VOLATILE_STORAGE_HEADER *) AmdS3SaveParams->S3DataBlock.VolatileStorage)->HeapSize = HeapSize;
        ((S3_VOLATILE_STORAGE_HEADER *) AmdS3SaveParams->S3DataBlock.VolatileStorage)->RegisterDataOffset = 0;
        ((S3_VOLATILE_STORAGE_HEADER *) AmdS3SaveParams->S3DataBlock.VolatileStorage)->RegisterDataSize = LateContextSize;

        if (HeapSize != 0) {
          // Transfer heap contents
          ((S3_VOLATILE_STORAGE_HEADER *) AmdS3SaveParams->S3DataBlock.VolatileStorage)->HeapOffset = sizeof (S3_VOLATILE_STORAGE_HEADER);
          HeapPtr = (HEAP_MANAGER *) &BufferPointer[EarlyBufferSize + sizeof (S3_VOLATILE_STORAGE_HEADER)];
          HeapPtr->UsedSize = sizeof (HEAP_MANAGER);
          HeapPtr->Signature = HEAP_SIGNATURE_VALID;
          HeapPtr->FirstActiveBufferOffset = AMD_HEAP_INVALID_HEAP_OFFSET;
          HeapPtr->FirstFreeSpaceOffset = sizeof (HEAP_MANAGER);
          FreeSpaceNode = (BUFFER_NODE *) ((UINT8 *) HeapPtr + sizeof (HEAP_MANAGER));
          FreeSpaceNode->BufferSize = HeapSize - sizeof (HEAP_MANAGER) - sizeof (BUFFER_NODE);
          FreeSpaceNode->OffsetOfNextNode = AMD_HEAP_INVALID_HEAP_OFFSET;

          HeapStatus = AmdS3SaveParams->StdHeader.HeapStatus;
          AmdS3SaveParams->StdHeader.HeapStatus = HEAP_S3_RESUME;
          AmdS3SaveParams->StdHeader.HeapBasePtr = (VOID *) HeapPtr;

          for (i = 0; i < S3LATE_TABLE_SIZE; i++) {
            if (HeapPtrs[i] != NULL) {
              HeapParams.RequestedBufferSize = HeapSizes[i]; // S3LateHeapTable[i].BufferLength;
              HeapParams.BufferHandle = S3LateHeapTable[i];
              HeapParams.Persist = HEAP_S3_RESUME;
              if (HeapAllocateBuffer (&HeapParams, &AmdS3SaveParams->StdHeader) == AGESA_SUCCESS) {
                LibAmdMemCopy ((VOID *) HeapParams.BufferPtr, HeapPtrs[i], HeapSizes[i], &AmdS3SaveParams->StdHeader);
              }
            }
          }

          AmdS3SaveParams->StdHeader.HeapStatus = HeapStatus;
        }


        if (LateContextSize != 0) {

          ((S3_VOLATILE_STORAGE_HEADER *) AmdS3SaveParams->S3DataBlock.VolatileStorage)->RegisterDataOffset = HeapSize + sizeof (S3_VOLATILE_STORAGE_HEADER);

          SaveDeviceListContext (NonMemoryRelatedDeviceList,
                                &(BufferPointer[EarlyBufferSize + HeapSize + sizeof (S3_VOLATILE_STORAGE_HEADER)]),
                                S3_LATE_RESTORE,
                                &LateContextSize,
                                &AmdS3SaveParams->StdHeader);
        }

        AmdS3SaveParams->S3DataBlock.VolatileStorageSize = HeapSize + LateContextSize + sizeof (S3_VOLATILE_STORAGE_HEADER);
      }
    }
  }
  IDS_OPTION_HOOK (IDS_AFTER_S3_SAVE, AmdS3SaveParams, &AmdS3SaveParams->StdHeader);
  AGESA_TESTPOINT (TpIfAmdS3SaveExit, &AmdS3SaveParams->StdHeader);
  return  ReturnStatus;
}

/*---------------------------------------------------------------------------------------*/
/**
 * Constructor for the AMD_S3_SAVE function.
 *
 * This routine is responsible for setting default values for the
 * input parameters needed by the AMD_S3_SAVE entry point.
 *
 * @param[in]    StdHeader       The standard header.
 * @param[in,out] S3SaveParams   Required input parameters for the AMD_S3_SAVE
 *                               entry point.
 *
 * @retval        AGESA_SUCCESS  Always Succeeds.
 *
 */
AGESA_STATUS
AmdS3SaveInitializer (
  IN OUT   AMD_CONFIG_PARAMS *StdHeader,
  IN OUT   AMD_S3SAVE_PARAMS *S3SaveParams
  )
{
  ASSERT (StdHeader != NULL);
  ASSERT (S3SaveParams != NULL);

  S3SaveParams->StdHeader = *StdHeader;

  AmdS3ParamsInitializer (&S3SaveParams->S3DataBlock);

  AmdS3SavePlatformConfigInit (&S3SaveParams->PlatformConfig, &S3SaveParams->StdHeader);

  return AGESA_SUCCESS;
}

/*---------------------------------------------------------------------------------------*/
/**
 * Destructor for the AMD_S3_SAVE function.
 *
 * This routine is responsible for deallocation of heap space allocated during
 * AMD_S3_SAVE entry point.
 *
 * @param[in]     StdHeader       The standard header.
 * @param[in,out] S3SaveParams   Required input parameters for the AMD_INIT_RESUME
 *                               entry point.
 *
 * @retval        AGESA_STATUS
 *
 */
AGESA_STATUS
AmdS3SaveDestructor (
  IN       AMD_CONFIG_PARAMS *StdHeader,
  IN OUT   AMD_S3SAVE_PARAMS *S3SaveParams
  )
{
  AGESA_STATUS ReturnStatus;
  AGESA_STATUS RetVal;

  ASSERT (S3SaveParams != NULL);

  ReturnStatus = AGESA_SUCCESS;

  // Deallocate heap space allocated during memory S3 save
  RetVal = MemS3Deallocate (&S3SaveParams->StdHeader);
  if (RetVal > ReturnStatus) {
    ReturnStatus = RetVal;
  }

  RetVal = HeapDeallocateBuffer (AMD_S3_NB_INFO_BUFFER_HANDLE, StdHeader);
  if (RetVal > ReturnStatus) {
    ReturnStatus = RetVal;
  }

  RetVal = HeapDeallocateBuffer (AMD_S3_INFO_BUFFER_HANDLE, StdHeader);
  if (RetVal > ReturnStatus) {
    ReturnStatus = RetVal;
  }

  return ReturnStatus;
}

/*------------------------------------------------------------------------------------*/
/**
 * Initialize AmdS3Save stage platform profile and user option input.
 *
 * @param[in,out]   PlatformConfig   Platform profile/build option config structure
 * @param[in,out]   StdHeader        AMD standard header config param
 *
 * @retval          AGESA_SUCCESS    Always Succeeds.
 *
 */
AGESA_STATUS
AmdS3SavePlatformConfigInit (
  IN OUT   PLATFORM_CONFIGURATION    *PlatformConfig,
  IN OUT   AMD_CONFIG_PARAMS         *StdHeader
  )
{
  CommonPlatformConfigInit (PlatformConfig, StdHeader);

  return AGESA_SUCCESS;
}
