/**
 * @file
 *
 * AMD Integrated Debug Debug_library Routines
 *
 * Contains AMD AGESA debug macros and library functions
 *
 * @xrefitem bom "File Content Label" "Release Content"
 * @e project:      AGESA
 * @e sub-project:  IDS
 * @e \$Revision: 44323 $   @e \$Date: 2010-12-22 01:24:58 -0700 (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 "Ids.h"
#include "IdsLib.h"
#include "amdlib.h"
#include "AMD.h"
#include "heapManager.h"
#include "cpuRegisters.h"
#include "Filecode.h"
#define FILECODE PROC_IDS_DEBUG_IDSDEBUG_FILECODE

extern BUILD_OPT_CFG UserOptions;
typedef struct _IDS_CONSOLE IDS_CONSOLE;

/*--------------------------------------------------------------------------------------*/
/**
 *  IDS back-end code for AGESA_TESTPOINT
 *
 *  @param[in] TestPoint  Progress indicator value, see @ref AGESA_TP
 *  @param[in,out] StdHeader    The Pointer of AGESA Header
 *
 **/
/*--------------------------------------------------------------------------------------*/
VOID
IdsAgesaTestPoint (
  IN       AGESA_TP      TestPoint,
  IN OUT   AMD_CONFIG_PARAMS *StdHeader
  )
{
  LibAmdIoWrite (AccessWidth8, IDS_DEBUG_PORT, &TestPoint, StdHeader);
  IDS_PERF_TIMESTAMP (StdHeader, TestPoint);
  if (TestPoint == EndAgesaTps) {
    IDS_PERF_ANALYSE (StdHeader);
  }
}

#define DEBUG_PRINT_INIT        0x10BF0000
#define DEBUG_PRINT_EXIT        0xE0BF0000
#define DEBUG_PRINT_COMMAND     0xC0BF0000
#define DEBUG_PRINT_BREAKPOINT  0xB0BF0000
#define DEBUG_PRINT_EVENT       0x1EBF0000

#define IDS_HDTOUT_BPFLAG_FORMAT_STR   0
#define IDS_HDTOUT_BPFLAG_STATUS_STR   1

#define HDTOUT_BP_ACTION_HALT          1
#define HDTOUT_BP_ACTION_PRINTON       2
#define HDTOUT_BP_ACTION_PRINTONE      3
#define HDTOUT_BP_ACTION_PRINTOFF      4

typedef struct _BREAKPOINT_UNIT {
  UINT8 AndFlag : 1;        ///< Next string is ANDed to current string
  UINT8 BpFlag : 1;         ///< Format string or Status string
  UINT8 Action : 4;         ///< Halt, start HDTOUT, or stop HDT,...
  UINT8 BpStrOffset;        ///< Offset from BreakpointList to the breakpoint string
} BREAKPOINT_UNIT;

typedef enum {
  IDS_STATE_OFF = 0xF0,
  IDS_STATE_ON
} IDS_STATE_TYPE;

typedef enum {
  NON_CONSOLE = 0xD0,
  HDT_CONSOLE,
  IDS_CONSOLE_END
} IDS_CONSOLE_TYPE;

// IDS HdtOut Event Level
typedef enum {
  EVENT_OFF = 0x30,              ///< Default,no event triggered.
  EVENT_WARNING,                 ///< Event warning.
  EVENT_ERROR,                   ///< Event error.
  EVENT_FAIL_BUFFER_ALLOCATION,  ///< Reserved, fail buffer allocation
  EVENT_END                   ///< Event end sentinel.
} CONSOLE_EVENT_TYPE;

#define _INT_SIZE_OF(n) ((sizeof (n) + sizeof (UINTN) - 1) &~(sizeof (UINTN) - 1))

//
// Also support coding convention rules for var arg macros
//
typedef CHAR8 *VA_LIST;
#define VA_START(ap, v) (ap = (VA_LIST) & (v) + _INT_SIZE_OF (v))
#define VA_ARG(ap, t)   (*(t *) ((ap += _INT_SIZE_OF (t)) - _INT_SIZE_OF (t)))
#define VA_END(ap)      (ap = (VA_LIST) 0)

#define LEFT_JUSTIFY    0x01
#define PREFIX_SIGN     0x02
#define PREFIX_BLANK    0x04
#define COMMA_TYPE      0x08
#define LONG_TYPE       0x10
#define PREFIX_ZERO     0x20

/**
 *  Create console context
 *
 *  Do hardware settings related with specific console context
 *
 *  @param[in,out] IdsConsole    The Pointer of IDS console
 *
 **/
typedef VOID F_CREATE_CONSOLE_CONTEXT (
  IN OUT   IDS_CONSOLE *IdsConsole
  );
/// Reference to a method.
typedef F_CREATE_CONSOLE_CONTEXT *PF_CREATE_CONSOLE_CONTEXT;

/**
 *  Initialize console context
 *
 *  Initilize preference settings related with specific console context
 *
 *  @param[in,out] IdsConsole    The Pointer of IDS console
 *
 **/
typedef VOID F_INIT_CONSOLE_CONTEXT (
  IN OUT   IDS_CONSOLE *IdsConsole
  );
/// Reference to a method.
typedef F_INIT_CONSOLE_CONTEXT *PF_INIT_CONSOLE_CONTEXT;

/**
 *  Update console context
 *
 *  Update preference settings related with specific console context
 *
 *  @param[in,out] IdsConsole    The Pointer of IDS console
 *
 **/
typedef VOID F_UPDATE_CONSOLE_CONTEXT (
  IN OUT   IDS_CONSOLE *IdsConsole
  );
/// Reference to a method.
typedef F_UPDATE_CONSOLE_CONTEXT *PF_UPDATE_CONSOLE_CONTEXT;

/**
 *  Save console context
 *
 *  Save console context snapshot
 *
 *  @param[in,out] IdsConsole    The Pointer of IDS console
 *
 **/
typedef VOID F_SAVE_CONSOLE_CONTEXT (
  IN OUT   IDS_CONSOLE *IdsConsole
  );
/// Reference to a method.
typedef F_SAVE_CONSOLE_CONTEXT *PF_SAVE_CONSOLE_CONTEXT;

/**
 *  Destroy console context
 *
 *  Destroy console context snapshot
 *
 *  @param[in,out] IdsConsole    The Pointer of IDS console
 *
 **/
typedef VOID F_DESTROY_CONSOLE_CONTEXT (
  IN OUT   IDS_CONSOLE *IdsConsole
  );
/// Reference to a method.
typedef F_DESTROY_CONSOLE_CONTEXT *PF_DESTROY_CONSOLE_CONTEXT;

/**
 *  Print function
 *
 *  Print function related with specific console
 *
 *  @param[in] PrintType             Print Type
 *  @param[in] ConsoleBufferAddress  The address of console buffer
 *  @param[in] ConsoleBufferSize     The size of console buffer
 *  @param[in,out] IdsConsole        The Pointer of IDS console
 *
 **/
typedef VOID F_PRINT (
  IN       UINT32 PrintType,
  IN       UINT32 ConsoleBufferAddress,
  IN       UINT32 ConsoleBufferSize,
  IN OUT   IDS_CONSOLE *IdsConsole
  );
/// Reference to a method.
typedef F_PRINT *PF_PRINT;

/// IDS Console Operations
typedef struct _IDS_CONSOLE_OPERATIONS {
  PF_CREATE_CONSOLE_CONTEXT CreateConsoleContext;
  PF_INIT_CONSOLE_CONTEXT InitConsoleContext;
  PF_UPDATE_CONSOLE_CONTEXT UpdateConsoleContext;
  PF_DESTROY_CONSOLE_CONTEXT DestroyConsoleContext;
  PF_PRINT Print;
} IDS_CONSOLE_OPERATIONS;

/// IDS Console Header
typedef struct _IDS_CONSOLE_HEADER {
  UINT32 Signature;               ///< Signature information.
  UINT32 Version;                 ///< Version.
  UINT8  ConsoleType;             ///< Console type
  UINT8  Event;                   ///< Event type.
  UINT8  PrintState;              ///< On or Off
  UINT8  OutBufferMode;           ///< Off:stack mode, On: heap mode
  UINT16 OutBufferSize;           ///< Buffer size
  UINT16 OutBufferIndex;          ///< Buffer index
  UINT32 NumBreakpointUnit;       ///< default 0 no bp unit others number of bp unit
} IDS_CONSOLE_HEADER;

/// IDS Console
struct _IDS_CONSOLE
{
  IDS_CONSOLE_HEADER Header;      /**< IDS console header - 5 dwords */
  UINT32 FuncListAddr;            /**< 32 bit address to the list of functions that script can execute */
  UINT8  Reserved[56 - 24];       /**< ----------------- New fields must be added here. */
  CHAR8  BreakpointList[300];     /**< Breakpoint list */
  CHAR8  StatusStr[156];          /**< Shows current node, DCT, CS,... */
  CHAR8  OutBuffer[2];            /**< Console Out content. Its size will be determined by BufferSize. */
};

/**
 *  Create hdt console context
 *
 *  Do hardware settings for hdt console context
 *
 *  @param[in,out] IdsConsole    The Pointer of IDS console
 *
 **/
STATIC VOID
CreateHdtConsoleContext (
  IN OUT   IDS_CONSOLE *IdsConsole
  )
{
  UINT64 SMsr;
  UINT32 CR4reg;

  LibAmdMsrRead (0xC001100A, (UINT64*)&SMsr, NULL);
  SMsr |= 1;
  LibAmdMsrWrite (0xC001100A, (UINT64*)&SMsr, NULL);

  LibAmdWriteCpuReg (DR2_REG, 0x99CC);
  LibAmdWriteCpuReg (DR7_REG, 0x02000420);

  LibAmdReadCpuReg (CR4_REG, &CR4reg);
  LibAmdWriteCpuReg (CR4_REG, CR4reg | ((UINT32)1 << 3));
}

/**
 *  Initialize hdt console context
 *
 *  Initilize preference settings for hdt console context
 *
 *  @param[in,out] IdsConsole    The Pointer of IDS console
 *
 **/
STATIC VOID
InitHdtConsoleContext (
  IN OUT   IDS_CONSOLE *IdsConsole
  )
{
  IDS_FUNCLIST_EXTERN ();

  IdsConsole->FuncListAddr = (UINT32) IDS_FUNCLIST_ADDR;
  IdsConsole->StatusStr[0] = 0;

}

/**
 *  Update hdt console context
 *
 *  Update preference settings for hdt console context
 *
 *  @param[in,out] IdsConsole    The Pointer of IDS console
 *
 **/
STATIC VOID
UpdateHdtConsoleContext (
  IN OUT   IDS_CONSOLE *IdsConsole
  )
{
  if (IdsConsole->Header.OutBufferMode == IDS_STATE_OFF) {
    IdsConsole->Header.OutBufferSize = 0;
  }

}

/**
 *  Destroy hdt console context
 *
 *  Save hdt console context snapshot
 *
 *  @param[in,out] IdsConsole    The Pointer of IDS console
 *
 **/
STATIC VOID
DestroyHdtConsoleContext (
  IN OUT   IDS_CONSOLE *IdsConsole
  )
{
  UINT64 SMsr;

  LibAmdMsrRead (0xC001100A, (UINT64 *)&SMsr, NULL);
  SMsr &= ~BIT0;
  LibAmdMsrWrite (0xC001100A, (UINT64 *)&SMsr, NULL);

  LibAmdWriteCpuReg (DR2_REG, 0);
  LibAmdWriteCpuReg (DR3_REG, 0);
  LibAmdWriteCpuReg (DR7_REG, 0);
}

/**
 *  Hdt console print function
 *
 *  Print function related with hdt console
 *
 *  @param[in] PrintType             Print Type
 *  @param[in] ConsoleBufferAddress  The address of console buffer
 *  @param[in] ConsoleBufferSize     The size of console buffer
 *  @param[in,out] IdsConsole        The Pointer of IDS console
 *
 **/
STATIC VOID
HdtConsolePrint (
  IN       UINT32 PrintType,
  IN       UINT32 ConsoleBufferAddress,
  IN       UINT32 ConsoleBufferSize,
  IN OUT   IDS_CONSOLE *IdsConsole
  )
{
  IdsOutPort (PrintType | 0x99CC, ConsoleBufferAddress, ConsoleBufferSize);
}

/// Initial construction data for HDT console header.
CONST IDS_CONSOLE_HEADER ROMDATA HdtConsoleHeader =
{
 0xDB1099CC,
 0x0100,
 IDS_STATE_ON,
 EVENT_OFF,
 IDS_STATE_ON,
 IDS_STATE_ON,
 0x1000,
 0x0,
 0,
};

#define OPTION_HDT_CONSOLE_HEADER &HdtConsoleHeader

/// Initial construction data for HDT console operations.
CONST IDS_CONSOLE_OPERATIONS ROMDATA HdtConsoleOperations =
{
 CreateHdtConsoleContext,
 InitHdtConsoleContext,
 UpdateHdtConsoleContext,
 DestroyHdtConsoleContext,
 HdtConsolePrint
};

#define OPTION_HDT_CONSOLE_OPERATIONS &HdtConsoleOperations

/**
 *  Parses flag and width information from theFormat string and returns the next index
 *  into the Format string that needs to be parsed. See file headed for details of Flag and Width.
 *
 *  @param[in]  Format  Current location in the AvSPrint format string.
 *  @param[out] Flags  Returns flags
 *  @param[out] Width  Returns width of element
 *  @param[out] Marker Vararg list that may be partially consumed and returned.
 *
 *  @retval Pointer indexed into the Format string for all the information parsed by this routine.
 *
 **/
STATIC CHAR8 *
GetFlagsAndWidth (
  IN       CHAR8 *Format,
     OUT   UINTN *Flags,
     OUT   UINTN *Width,
  IN OUT   VA_LIST *Marker
  )
{
  UINTN   Count;
  BOOLEAN Done;

  *Flags = 0;
  *Width = 0;
  for (Done = FALSE; !Done; ) {
    Format++;

    switch (*Format) {

    case '-':
      *Flags |= LEFT_JUSTIFY;
      break;
    case '+':
      *Flags |= PREFIX_SIGN;
      break;
    case ' ':
      *Flags |= PREFIX_BLANK;
      break;
    case ',':
      *Flags |= COMMA_TYPE;
      break;
    case 'L':
    case 'l':
      *Flags |= LONG_TYPE;
      break;

    case '*':
      *Width = VA_ARG (*Marker, UINTN);
      break;

    case '0':
      *Flags |= PREFIX_ZERO;
      break;

    case '1':
    case '2':
    case '3':
    case '4':
    case '5':
    case '6':
    case '7':
    case '8':
    case '9':
      Count = 0;
      do {
        Count = (Count * 10) + *Format - '0';
        Format++;
      } while ((*Format >= '0')  &&  (*Format <= '9'));
      Format--;
      *Width = Count;
      break;

    default:
      Done = TRUE;
    }
  }
  return Format;
}

CHAR8 STATIC HexStr[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
/**
 *
 *  @param[in] Buffer Location to place ascii hex string of Value.
 *  @param[in] Value  - Hex value to convert to a string in Buffer.
 *  @param[in] Flags  - Flags to use in printing Hex string, see file header for details.
 *  @param[in] Width  - Width of hex value.
 *
 *  @retval Number of characters printed.
 **/

STATIC UINTN
ValueTomHexStr (
  IN OUT   CHAR8   *Buffer,
  IN       UINT64      Value,
  IN       UINTN       Flags,
  IN       UINTN       Width
  )
{
  CHAR8   TempBuffer[30];
  CHAR8   *TempStr;
  CHAR8   Prefix;
  CHAR8   *BufferPtr;
  UINTN   Count;
  UINTN   Index;

  TempStr = TempBuffer;
  BufferPtr = Buffer;
  //
  // Count starts at one since we will null terminate. Each iteration of the
  // loop picks off one nibble. Oh yea TempStr ends up backwards
  //
  Count = 0;
  do {
    *(TempStr++) = HexStr[Value & 0x0f];
    Value >>= 4;
    Count++;
  } while (Value != 0);

  if (Flags & PREFIX_ZERO) {
    Prefix = '0';
  } else if (!(Flags & LEFT_JUSTIFY)) {
    Prefix = ' ';
  } else {
    Prefix = 0x00;
  }
  for (Index = Count; Index < Width; Index++) {
    *(TempStr++) = Prefix;
  }

  //
  // Reverse temp string into Buffer.
  //
  while (TempStr != TempBuffer) {
    *(BufferPtr++) = *(--TempStr);
  }

  *BufferPtr = 0;
  return Index;
}

/**
 *  Prints a Value as a decimal number in Buffer
 *
 *  @param[in] Buffer  Location to place ascii decimal number string of Value.
 *  @param[in] Value  Decimal value to convert to a string in Buffer.
 *  @param[in] Flags  Flags to use in printing decimal string, see file header for details.
 *
 *  @retval Number of characters printed.
 *
**/

STATIC UINTN
ValueToString (
  IN OUT   CHAR8   *Buffer,
  IN       INT32       Value,
  IN       UINTN       Flags
  )
{
  CHAR8   TempBuffer[30];
  CHAR8   *TempStr;
  CHAR8   *BufferPtr;
  UINTN   Count;
  UINTN   Remainder;

  TempStr = TempBuffer;
  BufferPtr = Buffer;
  Count = 0;

  if (Value < 0) {
    *(BufferPtr++) = '-';
    Value = - Value;
    Count++;
  }

  do {
    Remainder = Value % 10;
    Value /= 10;
    *(TempStr++) = (CHAR8)(Remainder + '0');
    Count++;
    if ((Flags & COMMA_TYPE) == COMMA_TYPE) {
      if (Count % 3 == 0) {
        *(TempStr++) = ',';
      }
    }
  } while (Value != 0);

  //
  // Reverse temp string into Buffer.
  //
  while (TempStr != TempBuffer) {
    *(BufferPtr++) = *(--TempStr);
  }

  *BufferPtr = 0;
  return Count;
}

/**
 *  Check if String contain the substring
 *
 *  @param[in] String  Pointer of string.
 *  @param[in] Substr  Pointer of sub string.
 *
 *  @retval TRUE S2 is substring of S1
 *  @retval FALSE S2 isn't substring of S1
 *
**/
STATIC BOOLEAN
AmdIdsSubStr (
  IN      CHAR8 *String,
  IN      CHAR8 *Substr
  )
{
  UINT16 i;
  UINT16 j;

  for (i = 0; String[i] != 0 ; i++) {
    for (j = 0; (Substr[j] != 0) && (Substr[j] == String[i + j]); j++) {
    }
    if (Substr[j] == 0) {
      return TRUE;
    }
  }

  return FALSE;
}

/**
 *  IDS Backend Function for Memory timeout control
 *
 *  This function is used to override Memory timeout control.
 *
 *  @param[in,out]   DataPtr      The Pointer of UINT8.
 *
 **/
VOID
IdsMemTimeOut (
  IN OUT   VOID *DataPtr
  )
{
  UINT32 DR2reg;

  LibAmdReadCpuReg (DR2_REG, &DR2reg);
  if (DR2reg == 0x99CC) {
    // Turn timeout off if HDTout is on
    *((UINT8 *)DataPtr) = (UINT8)0;
  }
}

/**
 *
 *  IDS Debug Function to check the sentinels are intact
 *
 * This function complete heap walk and check to be performed at any time.
 *
 * @param[in]      StdHeader         Config handle for library and services.
 *
 * @retval         TRUE     No error
 *
 **/
BOOLEAN
AmdHeapIntactCheck (
  IN       AMD_CONFIG_PARAMS *StdHeader
  )
{
  UINT8       *HeapBufferPtr;
  BUFFER_NODE       *StartOfBufferPtr;
  BUFFER_NODE       *EndOfBufferPtr;
  HEAP_MANAGER *HeapManagerPtr;
  BUFFER_NODE *HeadNodePtr;
  BUFFER_NODE *CurrentNodePtr;
  UINT32  AmdHeapRamAddress;
  UINT32 SentinelBefore;
  UINT32 SentinelAfter;

  ASSERT (StdHeader != NULL);

  AmdHeapRamAddress = (UINT32)UserOptions.CfgHeapDramAddress;

  if (StdHeader->HeapStatus == HEAP_LOCAL_CACHE) {
    HeapBufferPtr = (UINT8 *) HeapGetCurrentBase (StdHeader);
  } else if (StdHeader->HeapStatus == HEAP_TEMP_MEM) {
    HeapBufferPtr = (UINT8 *) AmdHeapRamAddress;
  } else {
    return TRUE;
  }

  HeapManagerPtr = (HEAP_MANAGER *) HeapBufferPtr;
  HeadNodePtr = (BUFFER_NODE *) (HeapBufferPtr + sizeof (HEAP_MANAGER));
  CurrentNodePtr = HeadNodePtr;

  if (HeapManagerPtr->AvailableSize != AMD_HEAP_SIZE_PER_CORE - sizeof (HEAP_MANAGER)) {
    while (CurrentNodePtr != NULL) {
      StartOfBufferPtr = (BUFFER_NODE *) ((UINT8 *) CurrentNodePtr + sizeof (BUFFER_NODE) + CurrentNodePtr->BufferSize + 2 * SIZE_OF_SENTINEL);
      EndOfBufferPtr = (BUFFER_NODE *) ((UINT8 *) HeadNodePtr + AMD_HEAP_SIZE_PER_CORE);
      if (CurrentNodePtr->NextNodePtr != NULL) {
        ASSERT ((CurrentNodePtr->NextNodePtr >= StartOfBufferPtr) && (CurrentNodePtr->NextNodePtr < EndOfBufferPtr));
        SentinelBefore = *(UINT32 *) ((UINT8 *) CurrentNodePtr + sizeof (BUFFER_NODE));
        SentinelAfter = *(UINT32 *) ((UINT8 *) CurrentNodePtr + sizeof (BUFFER_NODE) + SIZE_OF_SENTINEL + CurrentNodePtr->BufferSize);

        ASSERT ((SentinelBefore == SENTINEL_BEFORE_VALUE) && (SentinelAfter == SENTINEL_AFTER_VALUE));
      } else {
        ASSERT ((UINT8 *) StartOfBufferPtr == HeapBufferPtr + AMD_HEAP_SIZE_PER_CORE - HeapManagerPtr->AvailableSize);
        SentinelBefore = *(UINT32 *) ((UINT8 *) CurrentNodePtr + sizeof (BUFFER_NODE));
        SentinelAfter = *(UINT32 *) ((UINT8 *) CurrentNodePtr + sizeof (BUFFER_NODE) + SIZE_OF_SENTINEL + CurrentNodePtr->BufferSize);
        ASSERT ((SentinelBefore == SENTINEL_BEFORE_VALUE) && (SentinelAfter == SENTINEL_AFTER_VALUE));
      }
      CurrentNodePtr = CurrentNodePtr->NextNodePtr;
    }
  }
  return TRUE;
}

/**
 *  Determine whether IDS console is enabled.
 *
 *  @param[in,out] IdsConsole    The Pointer of Ids console data
 *
 *  @retval       TRUE    Ids console is enabled.
 *  @retval       FALSE   Ids console is disabled.
 *
 **/
BOOLEAN
STATIC
IsIdsConsoleEnabled (
  IN OUT   UINTN *IdsConsole
  )
{
  BOOLEAN Result;
  UINT32  DR2reg;

  Result = FALSE;

  LibAmdReadCpuReg (DR2_REG, &DR2reg);
  if (DR2reg == 0x99CC) {
    Result = TRUE;
  }

  return Result;
}

/**
 *  Get IDS console.
 *
 *  @param[in,out] IdsConsolePtr  The Pointer of Ids console data
 *
 **/
STATIC VOID
GetIdsConsole (
  IN OUT   UINTN *IdsConsolePtr
  )
{
  UINT32 DR3Reg;

  LibAmdReadCpuReg (DR3_REG, &DR3Reg);
  *IdsConsolePtr = (UINTN) DR3Reg;
}

/**
 *  Get IDS console operations.
 *
 *  @param[in,out] IdsConsoleOperations  The Pointer of Ids console operations
 *
 **/
STATIC VOID
GetIdsConsoleHeader (
  IN OUT   UINTN *IdsConsoleHeader
  )
{
  IDS_CONSOLE_TYPE IdsConsoleType;

  IdsConsoleType = (IDS_CONSOLE_TYPE) OPTION_IDS_CONSOLE;

  if (IdsConsoleType == HDT_CONSOLE) {
    *IdsConsoleHeader = (UINTN) OPTION_HDT_CONSOLE_HEADER;
  } else {
    ASSERT (FALSE);
  }
}

/**
 *  Get IDS console operations.
 *
 *  @param[in,out] IdsConsoleOperations  The Pointer of Ids console operations
 *
 **/
STATIC VOID
GetIdsConsoleOperations (
  IN OUT   UINTN *IdsConsoleOperations
  )
{
  IDS_CONSOLE_TYPE IdsConsoleType;

  IdsConsoleType = (IDS_CONSOLE_TYPE) OPTION_IDS_CONSOLE;

  if (IdsConsoleType == HDT_CONSOLE) {
    *IdsConsoleOperations = (UINTN) OPTION_HDT_CONSOLE_OPERATIONS;
  } else {
    ASSERT (FALSE);
  }
}

/**
 *  Create IDS console.
 *
 *  @param[in,out] IdsConsole    The Pointer of Ids console data
 *  @param[in,out] StdHeader     The Pointer of AGESA Header
 **/
STATIC VOID
NewIdsConsole (
  IN OUT   IDS_CONSOLE *IdsConsole,
  IN OUT   AMD_CONFIG_PARAMS *StdHeader
  )
{
  IDS_CONSOLE_HEADER *IdsConsoleHeader;
  UINTN IdsConsoleHeaderPtr;

  GetIdsConsoleHeader (&IdsConsoleHeaderPtr);
  IdsConsoleHeader = &(IdsConsole->Header);

  LibAmdMemCopy ((VOID *)IdsConsoleHeader, (VOID *) (IdsConsoleHeaderPtr), (UINT32) sizeof (IDS_CONSOLE_HEADER), StdHeader);
}

/**
 *  Destroy IDS console.
 *
 *  @param[in,out] IdsConsole    The Pointer of Ids console data
 *  @param[in,out] StdHeader     The Pointer of AGESA Header
 **/
STATIC VOID
DestroyIdsConsole (
  IN OUT   IDS_CONSOLE *IdsConsole,
  IN OUT   AMD_CONFIG_PARAMS *StdHeader
  )
{
  HeapDeallocateBuffer (IDS_HDT_OUT_BUFFER_HANDLE, StdHeader);
}

/**
 *  Save IDS console Snapshot.
 *
 *  @param[in,out] IdsConsole    The Pointer of Ids console data
 *  @param[in,out] StdHeader     The Pointer of AGESA Header
 **/
STATIC VOID
SaveIdsConsole (
  IN OUT   IDS_CONSOLE *IdsConsole,
  IN OUT   IDS_CONSOLE_OPERATIONS *IdsConsoleOps,
  IN OUT   AMD_CONFIG_PARAMS *StdHeader
  )
{
  ALLOCATE_HEAP_PARAMS AllocHeapParams;

  do {
    AllocHeapParams.RequestedBufferSize = IdsConsole->Header.OutBufferSize + sizeof (IDS_CONSOLE) - 2;
    AllocHeapParams.BufferHandle = IDS_HDT_OUT_BUFFER_HANDLE;
    AllocHeapParams.Persist = HEAP_SYSTEM_MEM;
    if (HeapAllocateBuffer (&AllocHeapParams, StdHeader) == AGESA_SUCCESS) {
      break;
    } else {
      IdsConsole->Header.OutBufferSize -= 256;
      IdsConsole->Header.Event = EVENT_FAIL_BUFFER_ALLOCATION;
      IdsConsoleOps->Print (DEBUG_PRINT_EVENT, (UINT32)IdsConsole, 0, IdsConsole);
    }
  } while ((IdsConsole->Header.OutBufferSize & 0x8000) == 0);

  if ((IdsConsole->Header.OutBufferSize & 0x8000) == 0) {
    LibAmdWriteCpuReg (DR3_REG, (UINT32)AllocHeapParams.BufferPtr);
    LibAmdMemCopy (AllocHeapParams.BufferPtr, (VOID *) IdsConsole, (UINT32) sizeof (IDS_CONSOLE) - 2, StdHeader);
  }
}

/**
 *
 *  Initial function for IDS console.
 *
 *  Create IDS console context, let Ids console function to be ready.
 *
 *  @param[in,out] StdHeader    The Pointer of AGESA Header
 *
 **/
VOID
AmdIdsConsoleInit (
  IN OUT   AMD_CONFIG_PARAMS *StdHeader
  )
{
  IDS_CONSOLE IdsConsole;
  IDS_CONSOLE_OPERATIONS *IdsConsoleOps;
  UINTN IdsConsolePtr;
  UINTN IdsConsoleOpsPtr;

  if (IsIdsConsoleEnabled (&IdsConsolePtr)) {
    GetIdsConsoleOperations (&IdsConsoleOpsPtr);
    IdsConsoleOps = (IDS_CONSOLE_OPERATIONS *) IdsConsoleOpsPtr;
    NewIdsConsole (&IdsConsole, StdHeader);

    IdsConsoleOps->CreateConsoleContext (&IdsConsole);
    IdsConsoleOps->InitConsoleContext (&IdsConsole);
    IdsConsoleOps->Print (DEBUG_PRINT_INIT, (UINT32)&IdsConsole, 0, &IdsConsole);
    IdsConsoleOps->UpdateConsoleContext (&IdsConsole);

    SaveIdsConsole (&IdsConsole, IdsConsoleOps, StdHeader);
  }
}

/**
 *  Prints string to debug host like printf in C
 *
 *  @param[in] Format of string
 *  @param[in] ... Variable parameter
 *
**/
VOID
AmdIdsConsolePrint (
  IN      CHAR8 *Format,
  IN      ...
  )
{
  IDS_CONSOLE *IdsConsole;
  UINTN IdsConsolePtr;
  IDS_CONSOLE_OPERATIONS *IdsConsoleOps;
  UINTN  IdsConsoleOpsPtr;
  VA_LIST Marker;
  CHAR8 LocalBuffer[256];
  CHAR8 *ConsoleBuffer;
  CHAR8 Null_Str[] = " < null string > ";
  CHAR8 *AsciiStr;
  UINTN Index;
  UINTN Flags;
  UINTN Width;
  UINTN ConsoleBufferSize;
  UINT64 Value;
  UINT16 *Array;
  UINT32 ArrayLength;
  UINT32 ArrayIndex;
  BOOLEAN SaveStatus;
  UINT32 LastIndex;
  UINT32 i;
  UINT32 j;
  UINT32 NumBpUnit;
  BREAKPOINT_UNIT *Pbpunit;
  CHAR8 *Pbpstr;
  CHAR8 *PCmpStr;
  CHAR8 *EventLevelStr;
  BOOLEAN LastBpmatched;
  BOOLEAN Bpmatched;
  IDS_STATE_TYPE PrintCtrl;

  //Init the default Value
  IdsConsole = NULL;
  IdsConsolePtr = 0;
  ConsoleBuffer = LocalBuffer;
  Index = 0;
  LastIndex = 0;
  ConsoleBufferSize = 256;
  NumBpUnit = 0;
  PrintCtrl = IDS_STATE_ON;

  if (IsIdsConsoleEnabled (&IdsConsolePtr)) {
    GetIdsConsole (&IdsConsolePtr);
    IdsConsole = (IDS_CONSOLE *) IdsConsolePtr;
    GetIdsConsoleOperations (&IdsConsoleOpsPtr);
    IdsConsoleOps = (IDS_CONSOLE_OPERATIONS *) IdsConsoleOpsPtr;

    if (IdsConsole->Header.OutBufferMode == IDS_STATE_ON) {
      ConsoleBuffer = IdsConsole->OutBuffer;
      Index = IdsConsole->Header.OutBufferIndex;
      ConsoleBufferSize = (UINTN) (IdsConsole->Header.OutBufferSize);
      NumBpUnit = IdsConsole->Header.NumBreakpointUnit;
      PrintCtrl = IdsConsole->Header.PrintState;
    }

    if ((PrintCtrl != 0) || (NumBpUnit > 0)) {
      VA_START(Marker,Format); //init marker to 1st dynamic parameters.

      LastIndex = (UINT32) Index;

      if (*Format != '@') {
        if (*Format == '!') {
          SaveStatus = TRUE;
          Format++;
        } else {
          SaveStatus = FALSE;
        }

        for (; *Format != '\0'; Format++) {
          if (*Format != '%') {
            ConsoleBuffer[Index++] = *Format;
          } else {
            Format = GetFlagsAndWidth (Format, &Flags, &Width, &Marker);
            switch (*Format) {
            // Using %[EventType] style to catch predefined event.
            case '[':
              EventLevelStr = Format;
              EventLevelStr++;
              if ((*EventLevelStr > EVENT_OFF) && (*EventLevelStr < EVENT_END)) {
                EventLevelStr++;
                if (*EventLevelStr == ']') {
                  EventLevelStr--;
                  IdsConsole->Header.Event = (UINT8) (CONSOLE_EVENT_TYPE) (*EventLevelStr);
                }
              }
              break;
            case 'X':
              Flags |= PREFIX_ZERO;
              Width = sizeof (UINT64) * 2;
              //
              // break skipped on purpose
              //
            case 'x':
              if ((Flags & LONG_TYPE) == LONG_TYPE) {
                Value = VA_ARG (Marker, UINT64);
              } else {
                Value = VA_ARG (Marker, UINTN);
              }
              Index += ValueTomHexStr (&ConsoleBuffer[Index], Value, Flags, Width);
              break;

            case 'd':
              Value = (UINTN)VA_ARG (Marker, UINT32);
              Index += ValueToString (&ConsoleBuffer[Index], (UINT32)Value, Flags);
              break;

            case 's':
            case 'S':
              AsciiStr = (CHAR8 *)VA_ARG (Marker, CHAR8 *);
              if (AsciiStr == NULL) {
                AsciiStr = Null_Str; //" < null string > ";
              }
              while (*AsciiStr != '\0') {
                ConsoleBuffer[Index++] = *AsciiStr++;
              }
              break;

            case 'c':
              ConsoleBuffer[Index++] = (CHAR8)VA_ARG (Marker, UINTN);
              break;

            case 'a':
              Array = (UINT16 *) VA_ARG (Marker, VOID *);
              ArrayLength = (UINT32) VA_ARG (Marker, UINT32);
              for (ArrayIndex = 0; ArrayIndex < ArrayLength; ArrayIndex++) {
                // Only support hex format of an array of UINT16 for now.
                Width = 2;
                Flags = PREFIX_ZERO;
                ConsoleBuffer[Index++] = ' ';
                Index += ValueTomHexStr (&ConsoleBuffer[Index], Array[ArrayIndex] & 0xFF, Flags, Width);

                // If buffer is full
                if (Index > (ConsoleBufferSize - 8)) {
                  if (IdsConsole != NULL) {
                    if (LastIndex != 0) {
                      // Stream all out except current string
                      if (PrintCtrl != 0) {
                        IdsConsoleOps->Print (DEBUG_PRINT_COMMAND, (UINT32)ConsoleBuffer, LastIndex, IdsConsole);
                      }
                      Index = Index - LastIndex;
                      // Move current string too top
                      for (i = 0; i < Index; i++) {
                        ConsoleBuffer[i] = ConsoleBuffer[LastIndex + i];
                      }
                      LastIndex = 0;
                    } else {
                      // Buffer size is too small
                      ASSERT (FALSE);
                    }
                  } else {
                    break;
                  }
                }
              }
              break;

            case 'v':
              ConsoleBuffer[Index++] = '%';
              ConsoleBuffer[Index++] = 'v';
              Format++;
              ConsoleBuffer[Index++] = *Format;
              if (*Format == 'h') {
                Format++;
                ConsoleBuffer[Index++] = *Format;
              }
              break;

            case '%':
              ConsoleBuffer[Index++] = *Format;
              break;

            default:
              //
              // if the type is unknown print it to the screen
              //
              ConsoleBuffer[Index++] = '%';
              ConsoleBuffer[Index++] = *Format;
            }
          }

          // If buffer is full
          if (Index > (ConsoleBufferSize - 32)) {
            if (IdsConsole != NULL) {
              if (LastIndex != 0) {
                // Stream all out except current string
                if (PrintCtrl != IDS_STATE_OFF) {
                  IdsConsoleOps->Print (DEBUG_PRINT_COMMAND, (UINT32)ConsoleBuffer, LastIndex, IdsConsole);
                }
                Index = Index - LastIndex;
                // Move current string too top
                for (i = 0; i < Index; i++) {
                  ConsoleBuffer[i] = ConsoleBuffer[LastIndex + i];
                }
                LastIndex = 0;
              } else {
                // Buffer size is too small
                ASSERT (FALSE);
              }
            } else {
              break;
            }
          }
        }

        if (IdsConsole->Header.OutBufferMode == IDS_STATE_OFF) {
          IdsConsoleOps->Print (DEBUG_PRINT_COMMAND, (UINT32)ConsoleBuffer, (UINT32) Index, IdsConsole);
          Index = 0;
        }

        if ((IdsConsole->Header.Event > EVENT_OFF) && (IdsConsole->Header.Event < EVENT_END) ) {
          // if HdtOut Buffer is on, flush hdtout buffer contents out.
          if (IdsConsole->Header.OutBufferMode == IDS_STATE_ON) {
            IdsConsoleOps->Print (DEBUG_PRINT_COMMAND, (UINT32)ConsoleBuffer, (UINT32) Index, IdsConsole);
            Index = 0;
          }

          IdsConsoleOps->Print (DEBUG_PRINT_EVENT, (UINT32)IdsConsole, 0, IdsConsole);
          IdsConsole->Header.Event = EVENT_OFF;
        }

        //
        //  Check breakpoint
        //
        if (NumBpUnit) {
          Pbpunit = (BREAKPOINT_UNIT *) IdsConsole->BreakpointList;
          LastBpmatched = TRUE;
          Bpmatched = TRUE;
          for (i = 0; i < NumBpUnit; i++) {
            Pbpstr = IdsConsole->BreakpointList + Pbpunit[i].BpStrOffset;
            if (Pbpunit[i].BpFlag == IDS_HDTOUT_BPFLAG_FORMAT_STR) {
              PCmpStr = &ConsoleBuffer[LastIndex];
              ConsoleBuffer[Index] = 0;
            } else {
              PCmpStr = IdsConsole->StatusStr;
            }

            if (LastBpmatched) {
              Bpmatched = AmdIdsSubStr (PCmpStr, Pbpstr);
              if (Bpmatched) {
                if (Pbpunit[i].AndFlag == 0) {
                  // This is the last of matching string of an AND block, apply action here
                  switch (Pbpunit[i].Action) {
                  case HDTOUT_BP_ACTION_HALT:
                    IdsConsoleOps->Print (DEBUG_PRINT_BREAKPOINT, (UINT32)(ConsoleBuffer + LastIndex), ( i << 16) | (UINT32) Index, IdsConsole);
                    break;
                  case HDTOUT_BP_ACTION_PRINTON:
                    PrintCtrl = IDS_STATE_ON;
                    IdsConsole->Header.PrintState = IDS_STATE_ON;
                    break;
                  case HDTOUT_BP_ACTION_PRINTOFF:
                    if (IdsConsole->Header.PrintState != IDS_STATE_OFF) {
                      IdsConsole->Header.PrintState = IDS_STATE_OFF;
                      IdsConsoleOps->Print (DEBUG_PRINT_COMMAND, (UINT32)ConsoleBuffer, (UINT32)Index, IdsConsole);
                    }
                    break;
                  default:
                    ASSERT (FALSE);
                  }
                  break;
                }
              }
            }
            if (Pbpunit[i].AndFlag == 1) {
              LastBpmatched = Bpmatched;
            } else {
              LastBpmatched = TRUE;
            }
          }
        }

        //
        //  Store status fields
        //
        if (SaveStatus) {
          // Look for the start of the first word
          for (; LastIndex < Index; LastIndex++) {
            if ((ConsoleBuffer[LastIndex] > 32) && (ConsoleBuffer[LastIndex] < 127)) {
              break;
            }
          }
          if (LastIndex < Index) {
            // Match the first word in StatusStr
            SaveStatus = FALSE;
            j = LastIndex;
            for (i = 0; !SaveStatus && (IdsConsole->StatusStr[i] != 0); i++) {
              ArrayLength = 1;
              for (j = LastIndex; ConsoleBuffer[j] == IdsConsole->StatusStr[i]; j++) {
                i++;
                ArrayLength++;
                if ((j == (Index - 1)) || (ConsoleBuffer[j] == ' ')) {
                  // Find the length of this entry
                  ArrayIndex = i;
                  for (; IdsConsole->StatusStr[i] != '\n'; i++) {
                    ArrayLength++;
                  }

                  // Remove old entry if it does not fit
                  if (ArrayLength != ((UINT32) Index - LastIndex)) {
                    for (i++; IdsConsole->StatusStr[i] != 0; i++) {
                      IdsConsole->StatusStr[i - ArrayLength] = IdsConsole->StatusStr[i];
                    }
                    j = LastIndex;
                    i = i - ArrayLength - 1;
                    // Mark the end of string
                    IdsConsole->StatusStr[i + ((UINT32) Index - LastIndex) + 1] = 0;
                  } else {
                    i = ArrayIndex - 2;
                  }

                  // Word match, exit for saving
                  SaveStatus = TRUE;
                  break;
                }
              }
            }

            // Copy string to StatusStr
            for (; j < Index; j++, i++) {
              IdsConsole->StatusStr[i] = ConsoleBuffer[j];
            }
            if (!SaveStatus) {
              // Mark the end of string if not done so
              IdsConsole->StatusStr[i] = 0;
            }
          }
        }

      }
    }

    if (IdsConsole != NULL) {
      if (IdsConsole->Header.PrintState == IDS_STATE_OFF) {
        Index = 0;    // Clear buffer if the data will not be printed out.
      }
      IdsConsole->Header.OutBufferIndex = (UINT16) Index;
    } else {
      IdsConsoleOps->Print (DEBUG_PRINT_COMMAND, (UINT32)ConsoleBuffer, (UINT32)Index, IdsConsole);
    }
  }
}

/**
 *
 *  Exit function for IDS console Function.
 *
 *  Restore debug register and Deallocate heap.
 *
 *  @param[in,out] StdHeader    The Pointer of AGESA Header
 *
 **/
VOID
AmdIdsConsoleExit (
  IN OUT   AMD_CONFIG_PARAMS *StdHeader
  )
{
  IDS_CONSOLE *IdsConsole;
  UINTN IdsConsolePtr;
  IDS_CONSOLE_OPERATIONS *IdsConsoleOps;
  UINTN  IdsConsoleOpsPtr;

  if (IsIdsConsoleEnabled (&IdsConsolePtr)) {
    GetIdsConsole (&IdsConsolePtr);
    IdsConsole = (IDS_CONSOLE *) IdsConsolePtr;
    GetIdsConsoleOperations (&IdsConsoleOpsPtr);
    IdsConsoleOps = (IDS_CONSOLE_OPERATIONS *) IdsConsoleOpsPtr;

    if (IdsConsole != NULL) {
      if (IdsConsole->Header.PrintState != IDS_STATE_OFF) {
        IdsConsoleOps->Print (DEBUG_PRINT_COMMAND, (UINT32)(IdsConsole->OutBuffer), IdsConsole->Header.OutBufferIndex, IdsConsole);
      }

      IdsConsoleOps->Print (DEBUG_PRINT_EXIT, (UINT32)IdsConsole, 0, IdsConsole);

      IdsConsoleOps->DestroyConsoleContext (IdsConsole);
      DestroyIdsConsole (IdsConsole, StdHeader);

    }
  }
}
/**
 * Check for CAR Corruption, the performance monitor number three programed to log the CAR Corruption.
 * Check to see if control register is enabled and then check the preformance counter and stop the system by executing
 * IDS_ERROR_TRAP if counter has any value other than zero.
 *
 *  @param[in,out] StdHeader    The Pointer of Standard Header.
 *
 *
 **/
VOID
IdsCarCorruptionCheck (
  IN OUT   AMD_CONFIG_PARAMS *StdHeader
  )
{

  UINT64 Perf_Msr;

  LibAmdMsrRead (MSR_PERF_CONTROL3, (UINT64*)&Perf_Msr, StdHeader);
  if ((Perf_Msr & PERF_RESERVE_BIT_MASK) == PERF_CAR_CORRUPTION_EVENT) {
    LibAmdMsrRead (MSR_PERF_COUNTER3, (UINT64*)&Perf_Msr, StdHeader);
    if ((Perf_Msr != 0)) {
      IDS_ERROR_TRAP;
    }
  }
}
