/* $NoKeywords:$ */
/**
 * @file
 *
 * AMD CPU APIC related utility functions.
 *
 * Contains code that provides mechanism to invoke and control APIC communication.
 *
 * @xrefitem bom "File Content Label" "Release Content"
 * @e project:      AGESA
 * @e sub-project:  CPU
 * @e \$Revision: 44325 $   @e \$Date: 2010-12-22 03:29:53 -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 "amdlib.h"
#include "Ids.h"
#include "cpuCacheInit.h"
#include "cpuRegisters.h"
#include "cpuApicUtilities.h"
#include "cpuFamilyTranslation.h"
#include "GeneralServices.h"
#include "cpuServices.h"
#include "heapManager.h"
#include "Filecode.h"
CODE_GROUP (G1_PEICC)
RDATA_GROUP (G1_PEICC)

#define FILECODE PROC_CPU_CPUAPICUTILITIES_FILECODE

/*----------------------------------------------------------------------------------------
 *                   D E F I N I T I O N S    A N D    M A C R O S
 *----------------------------------------------------------------------------------------
 */
/* ApFlags bits */
#define AP_TASK_HAS_INPUT    0x00000001
#define AP_TASK_HAS_OUTPUT   0x00000002
#define AP_RETURN_PARAMS     0x00000004
#define AP_END_AT_HLT        0x00000008
#define AP_PASS_EARLY_PARAMS 0x00000010

#define SEG_DESC_PRESENT     0x80

#define SEG_DESC_TYPE_LDT    0x02
#define SEG_DESC_TYPE_CALL16 0x04
#define SEG_DESC_TYPE_TASK   0x05
#define SEG_DESC_TYPE_INT16  0x06
#define SEG_DESC_TYPE_TRAP16 0x07
#define SEG_DESC_TYPE_CALL32 0x0C
#define SEG_DESC_TYPE_INT32  0x0E
#define SEG_DESC_TYPE_TRAP32 0x0F

#define XFER_ELEMENT_SIZE    sizeof (UINT32)

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

typedef VOID F_CPU_AMD_NMI_HANDLER (
  IN       AMD_CONFIG_PARAMS *StdHeader
  );
typedef F_CPU_AMD_NMI_HANDLER *PF_CPU_AMD_NMI_HANDLER;

/// Interrupt Descriptor Table entry
typedef struct {
  UINT16 OffsetLo;     ///< Lower 16 bits of the interrupt handler routine's offset
  UINT16 Selector;     ///< Interrupt handler routine's selector
  UINT8  Rsvd;         ///< Reserved
  UINT8  Flags;        ///< Interrupt flags
  UINT16 OffsetHi;     ///< Upper 16 bits of the interrupt handler routine's offset
  UINT32 Offset64;     ///< High order 32 bits of the handler's offset needed when in 64 bit mode
  UINT32 Rsvd64;       ///< Reserved
} IDT_DESCRIPTOR;

/// Structure needed to load the IDTR using the lidt instruction
//typedef struct {
//  UINT16 Limit;        ///< Interrupt Descriptor Table size
//  UINT64 Base;         ///< Interrupt Descriptor Table base address
//} IDT_BASE_LIMIT;

/*----------------------------------------------------------------------------------------
 *           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
 *----------------------------------------------------------------------------------------
 */
VOID
STATIC
ApUtilSetupIdtForHlt (
  IN       IDT_DESCRIPTOR *NmiIdtDescPtr,
  IN       AMD_CONFIG_PARAMS *StdHeader
  );

UINT32
STATIC
ApUtilRemoteRead (
  IN       UINT8 Socket,
  IN       UINT8 Core,
  IN       UINT8 RegAddr,
  IN       AMD_CONFIG_PARAMS *StdHeader
  );

VOID
STATIC
ApUtilLocalWrite (
  IN       UINT32 RegAddr,
  IN       UINT32 Value,
  IN       AMD_CONFIG_PARAMS *StdHeader
  );

UINT32
STATIC
ApUtilLocalRead (
  IN       UINT32 RegAddr,
  IN       AMD_CONFIG_PARAMS *StdHeader
  );

VOID
STATIC
ApUtilGetLocalApicBase (
     OUT   UINT64 *ApicBase,
  IN       AMD_CONFIG_PARAMS *StdHeader
  );

UINT8
STATIC
ApUtilCalculateUniqueId (
  IN      UINT8 Socket,
  IN      UINT8 Core,
  IN      AMD_CONFIG_PARAMS *StdHeader
  );

VOID
STATIC
ApUtilFireDirectedNmi (
  IN      UINT8 Socket,
  IN      UINT8 Core,
  IN      AMD_CONFIG_PARAMS *StdHeader
  );

VOID
STATIC
ApUtilReceivePointer (
  IN       UINT8 Socket,
  IN       UINT8 Core,
     OUT   VOID  **ReturnPointer,
  IN       AMD_CONFIG_PARAMS *StdHeader
  );

VOID
STATIC
ApUtilTransmitPointer (
  IN       UINT8 Socket,
  IN       UINT8 Core,
  IN       VOID  **Pointer,
  IN       AMD_CONFIG_PARAMS *StdHeader
  );

VOID
STATIC
PerformFinalHalt (
  IN       AMD_CONFIG_PARAMS *StdHeader
  );

VOID
LocalApicInitialization (
  IN       AMD_CPU_EARLY_PARAMS *CpuEarlyParamsPtr,
  IN       AMD_CONFIG_PARAMS    *StdHeader
  );

VOID
LocalApicInitializationAtEarly (
  IN       CPU_SPECIFIC_SERVICES  *FamilyServices,
  IN       AMD_CPU_EARLY_PARAMS   *EarlyParams,
  IN       AMD_CONFIG_PARAMS      *StdHeader
  );

/*----------------------------------------------------------------------------------------
 *                          E X P O R T E D    F U N C T I O N S
 *----------------------------------------------------------------------------------------
 */
extern
VOID
ExecuteHltInstruction (
  IN       AMD_CONFIG_PARAMS *StdHeader
  );

extern
VOID
GetCsSelector (
  IN       UINT16 *Selector,
  IN       AMD_CONFIG_PARAMS *StdHeader
  );

extern
VOID
NmiHandler (
  IN       AMD_CONFIG_PARAMS *StdHeader
  );

extern
VOID
SetIdtr (
  IN       IDT_BASE_LIMIT *IdtInfo,
  IN       AMD_CONFIG_PARAMS *StdHeader
  );

extern
VOID
ExecuteFinalHltInstruction (
  IN       UINT32 SharedCore,
  IN       AP_MTRR_SETTINGS  *ApMtrrSettingsList,
  IN       AMD_CONFIG_PARAMS *StdHeader
  );

extern BUILD_OPT_CFG UserOptions;

/*---------------------------------------------------------------------------------------*/
/**
 * Initialize the Local APIC.
 *
 * This function determines and programs the appropriate APIC ID value
 * for the executing core.  This code must be run after HT initialization
 * is complete.
 *
 *  @param[in]     CpuEarlyParamsPtr  Service parameters.
 *  @param[in]     StdHeader          Config handle for library and services.
 *
 */
VOID
LocalApicInitialization (
  IN       AMD_CPU_EARLY_PARAMS *CpuEarlyParamsPtr,
  IN       AMD_CONFIG_PARAMS    *StdHeader
  )
{
  UINT32    CurrentCore;
  UINT32    CurrentNodeNum;
  UINT32    CoreIdBits;
  UINT32    Mnc;
  UINT32    ProcessorCount;
  UINT32    ProcessorApicIndex;
  UINT32    IoApicNum;
  UINT32    StartLocalApicId;
  UINT64    LocalApicBase;
  UINT32    TempVar_a;
  UINT64    MsrData;
  UINT64    Address;
  CPUID_DATA  CpuidData;

  // Local variables default values
  IoApicNum = CpuEarlyParamsPtr->PlatformConfig.NumberOfIoApics;

  GetCurrentCore (&CurrentCore, StdHeader);
  GetCurrentNodeNum (&CurrentNodeNum, StdHeader);

  // Get Mnc
  LibAmdCpuidRead (AMD_CPUID_ASIZE_PCCOUNT, &CpuidData, StdHeader);
  CoreIdBits = (CpuidData.ECX_Reg & 0x0000F000) >> 12;
  Mnc = 1 << (CoreIdBits & 0x000F);

  // Get ProcessorCount in the system
  ProcessorCount = GetNumberOfProcessors (StdHeader);

  // Get the APIC Index of this processor.
  ProcessorApicIndex = GetProcessorApicIndex (CurrentNodeNum, StdHeader);

  TempVar_a = (Mnc * ProcessorCount) + IoApicNum;
  ASSERT (TempVar_a < 255);

  // Apply apic enumeration rules
  // For systems with >= 16 APICs, put the IO-APICs at 0..n and
  // put the local-APICs at m..z
  // For systems with < 16 APICs, put the Local-APICs at 0..n and
  // put the IO-APICs at (n + 1)..z
  // This is needed because many IO-APIC devices only have 4 bits
  // for their APIC id and therefore must reside at 0..15
  StartLocalApicId = 0;
  if (TempVar_a >= 16) {
    if (IoApicNum >= 1) {
      StartLocalApicId = (IoApicNum - 1) / Mnc;
      StartLocalApicId = (StartLocalApicId + 1) * Mnc;
    }
  }

  // Set local apic id
  TempVar_a = (ProcessorApicIndex * Mnc) + CurrentCore + StartLocalApicId;
  IDS_HDT_CONSOLE (CPU_TRACE, "  Node %d core %d APIC ID = 0x%x\n", CurrentNodeNum, CurrentCore, TempVar_a);
  TempVar_a = TempVar_a << APIC20_ApicId;

  // Enable local apic id
  LibAmdMsrRead (MSR_APIC_BAR, &MsrData, StdHeader);
  MsrData |= APIC_ENABLE_BIT;
  LibAmdMsrWrite (MSR_APIC_BAR, &MsrData, StdHeader);

  // Get local apic base Address
  ApUtilGetLocalApicBase (&LocalApicBase, StdHeader);

  Address = LocalApicBase + APIC_ID_REG;
  LibAmdMemWrite (AccessWidth32, Address, &TempVar_a, StdHeader);
}

/*---------------------------------------------------------------------------------------*/
/**
 * Initialize the Local APIC at the AmdInitEarly entry point.
 *
 * This function acts as a wrapper for calling the LocalApicInitialization
 * routine at AmdInitEarly.
 *
 *  @param[in]   FamilyServices      The current Family Specific Services.
 *  @param[in]   EarlyParams         Service parameters.
 *  @param[in]   StdHeader           Config handle for library and services.
 *
 */
VOID
LocalApicInitializationAtEarly (
  IN       CPU_SPECIFIC_SERVICES  *FamilyServices,
  IN       AMD_CPU_EARLY_PARAMS   *EarlyParams,
  IN       AMD_CONFIG_PARAMS      *StdHeader
  )
{
  AGESA_TESTPOINT (TpProcCpuLocalApicInit, StdHeader);
  LocalApicInitialization (EarlyParams, StdHeader);
}


/*---------------------------------------------------------------------------------------*/
/**
 * Main entry point for all APs in the system.
 *
 * This routine puts the AP cores in an infinite loop in which the cores
 * will poll their masters, waiting to be told to perform a task.  At early,
 * all socket-relative core zeros will receive their tasks from the BSC.
 * All others will receive their tasks from the core zero of their local
 * processor.  At the end of AmdInitEarly, all cores will switch to receiving
 * their tasks from the BSC.
 *
 * @param[in]     StdHeader       Handle to config for library and services.
 * @param[in]     CpuEarlyParams  AMD_CPU_EARLY_PARAMS pointer.
 *
 */
VOID
ApEntry (
  IN       AMD_CONFIG_PARAMS *StdHeader,
  IN       AMD_CPU_EARLY_PARAMS *CpuEarlyParams
  )
{
  UINT8   RemoteCmd;
  UINT8   SourceSocket;
  UINT8   CommandStart;
  UINT32  ApFlags;
  UINT32  FuncType;
  UINT32  ReturnCode;
  UINT32  CurrentSocket;
  UINT32  CurrentCore;
  UINT32  *InputDataPtr;
  UINT32  BscSocket;
  UINT32  Ignored;
  AP_FUNCTION_PTR FuncAddress;
  IDT_DESCRIPTOR IdtDesc;
  AP_DATA_TRANSFER DataTransferInfo;
  AGESA_STATUS IgnoredSts;

  ASSERT (!IsBsp (StdHeader, &IgnoredSts));

  // Initialize local variables
  ReturnCode = 0;
  DataTransferInfo.DataTransferFlags = 0;
  InputDataPtr = NULL;

  // Determine the executing core's socket and core numbers
  IdentifyCore (StdHeader, &CurrentSocket, &Ignored, &CurrentCore, &IgnoredSts);

  IDS_HDT_CONSOLE (CPU_TRACE, "  Socket %d core %d begin AP tasking engine\n", CurrentSocket, CurrentCore);

  // Determine the BSC's socket number
  GetSocketModuleOfNode ((UINT32) 0x00000000, &BscSocket, &Ignored, StdHeader);

  // Setup Interrupt Descriptor Table for sleep mode
  ApUtilSetupIdtForHlt (&IdtDesc, StdHeader);

  // Indicate to the BSC that we have reached the tasking engine
  ApUtilWriteControlByte (CORE_IDLE, StdHeader);

  if (CurrentCore == 0) {
    // Core 0s receive their tasks from the BSC
    SourceSocket = (UINT8) BscSocket;
  } else {
    // All non-zero cores receive their tasks from the core 0 of their socket
    SourceSocket = (UINT8) CurrentSocket;
  }

  // Determine the unique value that the master will write when it has a task
  // for this core to perform.
  CommandStart = ApUtilCalculateUniqueId (
                   (UINT8)CurrentSocket,
                   (UINT8)CurrentCore,
                   StdHeader
                   );
  for (;;) {
    RemoteCmd = ApUtilReadRemoteControlByte (SourceSocket, 0, StdHeader);
    if (RemoteCmd == CommandStart) {
      ApFlags = ApUtilReadRemoteDataDword (SourceSocket, 0, StdHeader);

      ApUtilReceivePointer (SourceSocket, 0, (VOID **) &FuncAddress, StdHeader);

      FuncType = ApFlags & (UINT32) (AP_TASK_HAS_INPUT | AP_TASK_HAS_OUTPUT | AP_PASS_EARLY_PARAMS);
      if ((ApFlags & AP_TASK_HAS_INPUT) != 0) {
        DataTransferInfo.DataSizeInDwords = 0;
        DataTransferInfo.DataPtr = NULL;
        DataTransferInfo.DataTransferFlags = 0;
        if (ApUtilReceiveBuffer (SourceSocket, 0, &DataTransferInfo, StdHeader) == AGESA_ERROR) {
          // There is not enough space to put the input data on the heap.  Undefined behavior is about
          // to result.
          IDS_ERROR_TRAP;
        }
        InputDataPtr = (UINT32 *) DataTransferInfo.DataPtr;
      }
      ApUtilWriteControlByte (CORE_ACTIVE, StdHeader);
      switch (FuncType) {
      case 0:
        FuncAddress.PfApTask (StdHeader);
        break;
      case AP_TASK_HAS_INPUT:
        FuncAddress.PfApTaskI (InputDataPtr, StdHeader);
        break;
      case AP_PASS_EARLY_PARAMS:
        FuncAddress.PfApTaskC (StdHeader, CpuEarlyParams);
        break;
      case (AP_TASK_HAS_INPUT | AP_PASS_EARLY_PARAMS):
        FuncAddress.PfApTaskIC (InputDataPtr, StdHeader, CpuEarlyParams);
        break;
      case AP_TASK_HAS_OUTPUT:
        ReturnCode = FuncAddress.PfApTaskO (StdHeader);
        break;
      case (AP_TASK_HAS_INPUT | AP_TASK_HAS_OUTPUT):
        ReturnCode = FuncAddress.PfApTaskIO (InputDataPtr, StdHeader);
        break;
      case (AP_TASK_HAS_OUTPUT | AP_PASS_EARLY_PARAMS):
        ReturnCode = FuncAddress.PfApTaskOC (StdHeader, CpuEarlyParams);
        break;
      case (AP_TASK_HAS_INPUT | AP_TASK_HAS_OUTPUT | AP_PASS_EARLY_PARAMS):
        ReturnCode = FuncAddress.PfApTaskIOC (InputDataPtr, StdHeader, CpuEarlyParams);
        break;
      default:
        ReturnCode = 0;
        break;
      }
      if (((ApFlags & AP_RETURN_PARAMS) != 0)) {
        ApUtilTransmitBuffer (SourceSocket, 0, &DataTransferInfo, StdHeader);
      }
      if ((ApFlags & AP_TASK_HAS_OUTPUT) != 0) {
        ApUtilWriteDataDword (ReturnCode, StdHeader);
      }
      if ((ApFlags & AP_END_AT_HLT) != 0) {
        RemoteCmd = CORE_IDLE_HLT;
      } else {
        ApUtilWriteControlByte (CORE_IDLE, StdHeader);
      }
    }
    if (RemoteCmd == CORE_IDLE_HLT) {
      SourceSocket = (UINT8) BscSocket;
      ApUtilWriteControlByte (CORE_IDLE_HLT, StdHeader);
      ExecuteHltInstruction (StdHeader);
      ApUtilWriteControlByte (CORE_IDLE, StdHeader);
    }
  }
}


/*---------------------------------------------------------------------------------------*/
/**
 * Reads the 'control byte' on the designated remote core.
 *
 * This function will read the current contents of the control byte
 * on the designated core using the APIC remote read inter-
 * processor interrupt sequence.
 *
 * @param[in]      Socket        Socket number of the desired core
 * @param[in]      Core          Core number of the desired core
 * @param[in]      StdHeader     Configuration parameters pointer
 *
 * @return         The current value of the remote cores control byte
 *
 */
UINT8
ApUtilReadRemoteControlByte (
  IN       UINT8 Socket,
  IN       UINT8 Core,
  IN       AMD_CONFIG_PARAMS *StdHeader
  )
{
  UINT8  ControlByte;
  UINT32 ApicRegister;

  ApicRegister = ApUtilRemoteRead (Socket, Core, APIC_CTRL_DWORD, StdHeader);
  ControlByte = (UINT8) ((ApicRegister & APIC_CTRL_MASK) >> APIC_CTRL_SHIFT);
  return (ControlByte);
}


/*---------------------------------------------------------------------------------------*/
/**
 * Writes the 'control byte' on the executing core.
 *
 * This function writes data to a local APIC offset used in inter-
 * processor communication.
 *
 * @param[in]       Value
 * @param[in]       StdHeader
 *
 */
VOID
ApUtilWriteControlByte (
  IN       UINT8 Value,
  IN       AMD_CONFIG_PARAMS *StdHeader
  )
{
  UINT32 ApicRegister;

  ApicRegister = ApUtilLocalRead (APIC_CTRL_REG, StdHeader);
  ApicRegister = ((ApicRegister & ~APIC_CTRL_MASK) | (UINT32) (Value << APIC_CTRL_SHIFT));
  ApUtilLocalWrite (APIC_CTRL_REG, ApicRegister, StdHeader);
}


/*---------------------------------------------------------------------------------------*/
/**
 * Reads the 'data dword' on the designated remote core.
 *
 * This function will read the current contents of the data dword
 * on the designated core using the APIC remote read inter-
 * processor interrupt sequence.

 * @param[in]      Socket        Socket number of the desired core
 * @param[in]      Core          Core number of the desired core
 * @param[in]      StdHeader     Configuration parameters pointer
 *
 * @return         The current value of the remote core's data dword
 *
 */
UINT32
ApUtilReadRemoteDataDword (
  IN       UINT8 Socket,
  IN       UINT8 Core,
  IN       AMD_CONFIG_PARAMS *StdHeader
  )
{
  return (ApUtilRemoteRead (Socket, Core, APIC_DATA_DWORD, StdHeader));
}


/*---------------------------------------------------------------------------------------*/
/**
 * Writes the 'data dword' on the executing core.
 *
 * This function writes data to a local APIC offset used in inter-
 * processor communication.
 *
 * @param[in]      Value        Value to write
 * @param[in]      StdHeader    Configuration parameters pointer
 *
 */
VOID
ApUtilWriteDataDword (
  IN       UINT32 Value,
  IN       AMD_CONFIG_PARAMS *StdHeader
  )
{
  ApUtilLocalWrite (APIC_DATA_REG, Value, StdHeader);
}


/*---------------------------------------------------------------------------------------*/
/**
 * Runs the given task on the specified local core.
 *
 * This function is used to invoke an AP to run a specified AGESA
 * procedure.  It can only be called by cores that have subordinate
 * APs -- the BSC at POST, or any socket-relative core 0s at Early.
 *
 * @param[in]      Socket       Socket number of the target core
 * @param[in]      Core         Core number of the target core
 * @param[in]      TaskPtr      Function descriptor
 * @param[in]      StdHeader    Configuration parameters pointer
 *
 * @return         Return value of the task that the AP core ran,
 *                 or zero if the task was VOID.
 *
 */
UINT32
ApUtilRunCodeOnSocketCore (
  IN       UINT8   Socket,
  IN       UINT8   Core,
  IN       AP_TASK *TaskPtr,
  IN       AMD_CONFIG_PARAMS *StdHeader
  )
{
  UINT8  CoreId;
  UINT8  CurrentStatus;
  UINT8  WaitStatus[3];
  UINT32 ApFlags;
  UINT32 ReturnCode;
  AP_WAIT_FOR_STATUS WaitForStatus;

  ApFlags = 0;
  ReturnCode = 0;

  CoreId = ApUtilCalculateUniqueId (Socket, Core, StdHeader);

  if (TaskPtr->DataTransfer.DataSizeInDwords != 0) {
    ApFlags |= AP_TASK_HAS_INPUT;
    if (((TaskPtr->ExeFlags & RETURN_PARAMS) != 0) &&
        ((TaskPtr->DataTransfer.DataTransferFlags & DATA_IN_MEMORY) == 0)) {
      ApFlags |= AP_RETURN_PARAMS;
    }
  }

  if ((TaskPtr->ExeFlags & TASK_HAS_OUTPUT) != 0) {
    ApFlags |= AP_TASK_HAS_OUTPUT;
  }

  if ((TaskPtr->ExeFlags & END_AT_HLT) != 0) {
    ApFlags |= AP_END_AT_HLT;
  }

  if ((TaskPtr->ExeFlags & PASS_EARLY_PARAMS) != 0) {
    ApFlags |= AP_PASS_EARLY_PARAMS;
  }

  WaitStatus[0] = CORE_IDLE;
  WaitStatus[1] = CORE_IDLE_HLT;
  WaitStatus[2] = CORE_UNAVAILABLE;
  WaitForStatus.Status = WaitStatus;
  WaitForStatus.NumberOfElements = 3;
  WaitForStatus.RetryCount = WAIT_INFINITELY;
  WaitForStatus.WaitForStatusFlags = WAIT_STATUS_EQUALITY;
  CurrentStatus = ApUtilWaitForCoreStatus (Socket, Core, &WaitForStatus, StdHeader);

  if (CurrentStatus != CORE_UNAVAILABLE) {
    ApUtilWriteDataDword (ApFlags, StdHeader);
    ApUtilWriteControlByte (CoreId, StdHeader);

    if (CurrentStatus == CORE_IDLE_HLT) {
      ApUtilFireDirectedNmi (Socket, Core, StdHeader);
    }

    ApUtilTransmitPointer (Socket, Core, (VOID **) &TaskPtr->FuncAddress, StdHeader);

    if ((ApFlags & AP_TASK_HAS_INPUT) != 0) {
      ApUtilTransmitBuffer (Socket, Core, &TaskPtr->DataTransfer, StdHeader);
    }

    if ((TaskPtr->ExeFlags & WAIT_FOR_CORE) != 0) {
      if (((ApFlags & AP_TASK_HAS_INPUT) != 0) &&
          ((ApFlags & AP_RETURN_PARAMS) != 0) &&
          ((TaskPtr->DataTransfer.DataTransferFlags & DATA_IN_MEMORY) == 0)) {
        if (ApUtilReceiveBuffer (Socket, Core, &TaskPtr->DataTransfer, StdHeader) == AGESA_ERROR) {
          // There is not enough space to put the return data.  This should never occur.  If it
          // does, this would point to strange heap corruption.
          IDS_ERROR_TRAP;
        }
      }

      ApUtilWaitForCoreStatus (Socket, Core, &WaitForStatus, StdHeader);
      if ((ApFlags & AP_TASK_HAS_OUTPUT) != 0) {
        ReturnCode = ApUtilReadRemoteDataDword (Socket, Core, StdHeader);
      }
    }
  } else {
    ReturnCode = 0;
  }
  return (ReturnCode);
}


/*---------------------------------------------------------------------------------------*/
/**
 * Waits for a remote core's control byte value to either be equal or
 * not equal to any number of specified values.
 *
 * This function will loop doing remote read IPIs until the remote core's
 * control byte becomes one of the values in the input array if the input
 * flags are set for equality.  Otherwise, the loop will continue until
 * the control byte value is not equal to one of the elements in the
 * array.  The caller can also specify an iteration count for timeout
 * purposes.
 *
 * @param[in]      Socket
 * @param[in]      Core
 * @param[in]      WaitParamsPtr
 * @param[in]      StdHeader
 *
 * @return         The current value of the remote core's control byte
 *
 */
UINT8
ApUtilWaitForCoreStatus (
  IN       UINT8 Socket,
  IN       UINT8 Core,
  IN       AP_WAIT_FOR_STATUS *WaitParamsPtr,
  IN       AMD_CONFIG_PARAMS  *StdHeader
  )
{
  BOOLEAN  IsEqual;
  UINT8 CoreStatus;
  UINT8 i;
  UINT8 j;

  CoreStatus = 0;
  for (i = 0; (WaitParamsPtr->RetryCount == WAIT_INFINITELY) ||
              (i < WaitParamsPtr->RetryCount); ++i) {
    CoreStatus = ApUtilReadRemoteControlByte (Socket, Core, StdHeader);
    // Determine whether or not the current remote status is equal
    // to an element in the array.
    IsEqual = FALSE;
    for (j = 0; !IsEqual && j < WaitParamsPtr->NumberOfElements; ++j) {
      if (CoreStatus == WaitParamsPtr->Status[j]) {
        IsEqual = TRUE;
      }
    }
    if ((((WaitParamsPtr->WaitForStatusFlags & WAIT_STATUS_EQUALITY) != 0) && IsEqual) ||
        (((WaitParamsPtr->WaitForStatusFlags & WAIT_STATUS_EQUALITY) == 0) && !IsEqual)) {
      break;
    }
  }
  return (CoreStatus);
}


/*---------------------------------------------------------------------------------------*/
/**
 * Runs the AP task on the executing core.
 *
 * @param[in]      TaskPtr      Function descriptor
 * @param[in]      StdHeader    Configuration parameters pointer
 * @param[in]      ConfigParams Entry point CPU parameters pointer
 *
 * @return         Return value of the task, or zero if the task
 *                 was VOID.
 *
 */
UINT32
ApUtilTaskOnExecutingCore (
  IN       AP_TASK *TaskPtr,
  IN       AMD_CONFIG_PARAMS *StdHeader,
  IN       VOID *ConfigParams
  )
{
  UINT32   InvocationOptions;
  UINT32   ReturnCode;

  ReturnCode = 0;
  InvocationOptions = 0;

  if (TaskPtr->DataTransfer.DataSizeInDwords != 0) {
    InvocationOptions |= AP_TASK_HAS_INPUT;
  }
  if ((TaskPtr->ExeFlags & TASK_HAS_OUTPUT) != 0) {
    InvocationOptions |= AP_TASK_HAS_OUTPUT;
  }
  if ((TaskPtr->ExeFlags & PASS_EARLY_PARAMS) != 0) {
    InvocationOptions |= AP_PASS_EARLY_PARAMS;
  }

  switch (InvocationOptions) {
  case 0:
    TaskPtr->FuncAddress.PfApTask (StdHeader);
    break;
  case AP_TASK_HAS_INPUT:
    TaskPtr->FuncAddress.PfApTaskI (TaskPtr->DataTransfer.DataPtr, StdHeader);
    break;
  case AP_PASS_EARLY_PARAMS:
    TaskPtr->FuncAddress.PfApTaskC (StdHeader, ConfigParams);
    break;
  case (AP_TASK_HAS_INPUT | AP_PASS_EARLY_PARAMS):
    TaskPtr->FuncAddress.PfApTaskIC (TaskPtr->DataTransfer.DataPtr, StdHeader, ConfigParams);
    break;
  case AP_TASK_HAS_OUTPUT:
    ReturnCode = TaskPtr->FuncAddress.PfApTaskO (StdHeader);
    break;
  case (AP_TASK_HAS_INPUT | AP_TASK_HAS_OUTPUT):
    ReturnCode = TaskPtr->FuncAddress.PfApTaskIO (TaskPtr->DataTransfer.DataPtr, StdHeader);
    break;
  case (AP_TASK_HAS_OUTPUT | AP_PASS_EARLY_PARAMS):
    ReturnCode = TaskPtr->FuncAddress.PfApTaskOC (StdHeader, ConfigParams);
    break;
  case (AP_TASK_HAS_INPUT | AP_TASK_HAS_OUTPUT | AP_PASS_EARLY_PARAMS):
    ReturnCode = TaskPtr->FuncAddress.PfApTaskIOC (TaskPtr->DataTransfer.DataPtr, StdHeader, ConfigParams);
    break;
  default:
    ReturnCode = 0;
    break;
  }
  return (ReturnCode);
}

/*---------------------------------------------------------------------------------------*/
/**
 * Sets up the AP's IDT with NMI (INT2) being the only valid descriptor
 *
 * This function prepares the executing AP core for recovering from a hlt
 * instruction by initializing its IDTR.
 *
 * @param[in]        NmiIdtDescPtr Pointer to a writable IDT entry to
 *                                 be used for NMIs
 * @param[in]        StdHeader     Configuration parameters pointer
 *
 */
VOID
STATIC
ApUtilSetupIdtForHlt (
  IN       IDT_DESCRIPTOR *NmiIdtDescPtr,
  IN       AMD_CONFIG_PARAMS *StdHeader
  )
{
  UINT8   DescSize;
  UINT64  HandlerOffset;
  UINT64  EferRegister;
  IDT_BASE_LIMIT IdtInfo;

  LibAmdMsrRead (MSR_EXTENDED_FEATURE_EN, &EferRegister, StdHeader);
  if ((EferRegister & 0x100) != 0) {
    DescSize = 16;
  } else {
    DescSize = 8;
  }

  HandlerOffset = (UINT64) (intptr_t) &NmiHandler;
  NmiIdtDescPtr->OffsetLo = (UINT16) (HandlerOffset & 0xFFFF);
  NmiIdtDescPtr->OffsetHi = (UINT16) ((HandlerOffset >> 16) & 0xFFFF);
  GetCsSelector (&NmiIdtDescPtr->Selector, StdHeader);
  NmiIdtDescPtr->Flags = SEG_DESC_PRESENT | SEG_DESC_TYPE_INT32;
  NmiIdtDescPtr->Rsvd = 0;
  NmiIdtDescPtr->Offset64 = (UINT32) (HandlerOffset >> 32);
  NmiIdtDescPtr->Rsvd64 = 0;
  IdtInfo.Limit = (UINT16) ((DescSize * 3) - 1);
  IdtInfo.Base = (UINT64) (intptr_t) NmiIdtDescPtr - (DescSize * 2);
  SetIdtr (&IdtInfo , StdHeader);
}


/*---------------------------------------------------------------------------------------*/
/**
 * Calculate the APIC ID for a given core.
 *
 * Get the current node's apic id and deconstruct it to the base id of local apic id space.
 * Then construct the target's apic id using that base.
 * @b Assumes: The target Socket and Core exist!
 * Other Notes:
 *  - Must run after HT initialization is complete.
 *  - Code sync: This calculation MUST match the assignment
 *    calculation done above in LocalApicInitializationAtEarly function.
 *  - Assumes family homogeneous population of all sockets.
 *
 *  @param[in]      TargetSocket   The socket in which the Core's Processor is installed.
 *  @param[in]      TargetCore     The Core on that Processor
 *  @param[out]     LocalApicId    Its APIC Id
 *  @param[in]      StdHeader      Handle to header for library and services.
 *
 */
VOID
GetLocalApicIdForCore (
  IN       UINT32            TargetSocket,
  IN       UINT32            TargetCore,
     OUT   UINT32            *LocalApicId,
  IN       AMD_CONFIG_PARAMS *StdHeader
  )
{
  UINT32  CoreIdBits;
  UINT32  CurrentNode;
  UINT32  CurrentCore;
  UINT32  TargetNode;
  UINT32  MaxCoresInProcessor;
  UINT32  TotalCores;
  UINT32  CurrentLocalApicId;
  UINT64  LocalApicBase;
  UINT32  TempVar_a;
  UINT64  Address;
  UINT32  ProcessorApicIndex;
  BOOLEAN ReturnResult;
  CPUID_DATA  CpuidData;

  TargetNode = 0;

  // Get local apic base Address
  ApUtilGetLocalApicBase (&LocalApicBase, StdHeader);
  Address = LocalApicBase + APIC_ID_REG;

  LibAmdMemRead (AccessWidth32, Address, &TempVar_a, StdHeader);

  // ApicId [7:0]
  CurrentLocalApicId = (TempVar_a >> APIC20_ApicId) & 0x000000FF;

  GetCurrentNodeAndCore (&CurrentNode, &CurrentCore, StdHeader);
  LibAmdCpuidRead (AMD_CPUID_ASIZE_PCCOUNT, &CpuidData, StdHeader);
  CoreIdBits = (CpuidData.ECX_Reg & 0x0000F000) >> 12;
  MaxCoresInProcessor = (1 << CoreIdBits);

  // Get the APIC Index of this processor.
  ProcessorApicIndex = GetProcessorApicIndex (CurrentNode, StdHeader);

  TotalCores = (MaxCoresInProcessor * ProcessorApicIndex) + CurrentCore;
  CurrentLocalApicId -= TotalCores;

  // Use the Node Id of TargetSocket, Module 0.  No socket transitions are missed or added,
  // even if the TargetCore is not on Module 0 in that processor and that's all that matters now.
  ReturnResult = GetNodeId (TargetSocket, 0, (UINT8 *)&TargetNode, StdHeader);
  ASSERT (ReturnResult);

  // Get the APIC Index of this processor.
  ProcessorApicIndex = GetProcessorApicIndex (TargetNode, StdHeader);

  CurrentLocalApicId += ((MaxCoresInProcessor * ProcessorApicIndex) + TargetCore);
  *LocalApicId = CurrentLocalApicId;
}

/*---------------------------------------------------------------------------------------*/
/**
 * Securely passes a buffer to the designated remote core.
 *
 * This function uses a sequence of remote reads to transmit a data
 * buffer, one UINT32 at a time.
 *
 * @param[in]      Socket       Socket number of the remote core
 * @param[in]      Core         Core number of the remote core
 * @param[in]      BufferInfo   Information about the buffer to pass, and
 *                              how to pass it
 * @param[in]      StdHeader    Configuration parameters pointer
 *
 */
VOID
ApUtilTransmitBuffer (
  IN       UINT8   Socket,
  IN       UINT8   Core,
  IN       AP_DATA_TRANSFER  *BufferInfo,
  IN       AMD_CONFIG_PARAMS *StdHeader
  )
{
  UINT8  TargetCore;
  UINT8  MyUniqueId;
  UINT8  CurrentStatus;
  UINT32 *CurrentPtr;
  UINT32 i;
  UINT32 MyCore;
  UINT32 MySocket;
  UINT32 Ignored;
  AP_WAIT_FOR_STATUS WaitForStatus;
  AGESA_STATUS IgnoredSts;

  if ((BufferInfo->DataTransferFlags & DATA_IN_MEMORY) != 0) {
    ApUtilWriteDataDword ((UINT32) 0x00000000, StdHeader);
  } else {
    ApUtilWriteDataDword ((UINT32) BufferInfo->DataSizeInDwords, StdHeader);
  }
  TargetCore = ApUtilCalculateUniqueId (Socket, Core, StdHeader);

  ApUtilWriteControlByte (TargetCore, StdHeader);

  IdentifyCore (StdHeader, &MySocket, &Ignored, &MyCore, &IgnoredSts);

  MyUniqueId = ApUtilCalculateUniqueId ((UINT8)MySocket, (UINT8)MyCore, StdHeader);

  WaitForStatus.Status = &MyUniqueId;
  WaitForStatus.NumberOfElements = 1;
  WaitForStatus.RetryCount = WAIT_INFINITELY;
  WaitForStatus.WaitForStatusFlags = WAIT_STATUS_EQUALITY;

  ApUtilWaitForCoreStatus (Socket, Core, &WaitForStatus, StdHeader);
  ApUtilWriteDataDword (BufferInfo->DataTransferFlags, StdHeader);

  ApUtilWriteControlByte (CORE_DATA_FLAGS_READY, StdHeader);
  WaitForStatus.WaitForStatusFlags = 0;
  ApUtilWaitForCoreStatus (Socket, Core, &WaitForStatus, StdHeader);
  if ((BufferInfo->DataTransferFlags & DATA_IN_MEMORY) != 0) {
    ApUtilTransmitPointer (Socket, Core, (VOID **) &BufferInfo->DataPtr, StdHeader);
  } else {
    ApUtilWriteControlByte (CORE_STS_DATA_READY_1, StdHeader);
    CurrentStatus = CORE_STS_DATA_READY_0;
    WaitForStatus.Status = &CurrentStatus;
    WaitForStatus.WaitForStatusFlags = WAIT_STATUS_EQUALITY;
    ApUtilWaitForCoreStatus (Socket, Core, &WaitForStatus, StdHeader);
    WaitForStatus.WaitForStatusFlags = 0;
    CurrentPtr = (UINT32 *) BufferInfo->DataPtr;
    for (i = 0; i < BufferInfo->DataSizeInDwords; ++i) {
      ApUtilWriteDataDword (*CurrentPtr++, StdHeader);
      ApUtilWriteControlByte (CurrentStatus, StdHeader);
      ApUtilWaitForCoreStatus (Socket, Core, &WaitForStatus, StdHeader);
      CurrentStatus ^= 0x01;
    }
  }
  ApUtilWriteControlByte (CORE_ACTIVE, StdHeader);
}


/*---------------------------------------------------------------------------------------*/
/**
 * Securely receives a buffer from the designated remote core.
 *
 * This function uses a sequence of remote reads to receive a data
 * buffer, one UINT32 at a time.
 *
 * @param[in]      Socket       Socket number of the remote core
 * @param[in]      Core         Core number of the remote core
 * @param[in]      BufferInfo   Information about where to place the buffer
 * @param[in]      StdHeader    Configuration parameters pointer
 *
 * @retval         AGESA_SUCCESS Transaction was successful
 * @retval         AGESA_ALERT   The non-NULL desired location to place
 *                               the buffer was not used as the buffer
 *                               resides in a shared memory space.  The
 *                               input data pointer has changed.
 * @retval         AGESA_ERROR   There is not enough room to receive the
 *                               buffer.
 *
 */
AGESA_STATUS
ApUtilReceiveBuffer (
  IN       UINT8   Socket,
  IN       UINT8   Core,
  IN OUT   AP_DATA_TRANSFER  *BufferInfo,
  IN       AMD_CONFIG_PARAMS *StdHeader
  )
{
  UINT8    MyUniqueId;
  UINT8    SourceUniqueId;
  UINT8    CurrentStatus;
  UINT32   i;
  UINT32   MySocket;
  UINT32   MyCore;
  UINT32   Ignored;
  UINT32   *CurrentPtr;
  UINT32   TransactionSize;
  AGESA_STATUS ReturnStatus;
  ALLOCATE_HEAP_PARAMS HeapMalloc;
  AP_WAIT_FOR_STATUS WaitForStatus;

  ReturnStatus = AGESA_SUCCESS;
  IdentifyCore (StdHeader, &MySocket, &Ignored, &MyCore, &ReturnStatus);

  MyUniqueId = ApUtilCalculateUniqueId ((UINT8)MySocket, (UINT8)MyCore, StdHeader);

  WaitForStatus.Status = &MyUniqueId;
  WaitForStatus.NumberOfElements = 1;
  WaitForStatus.RetryCount = WAIT_INFINITELY;
  WaitForStatus.WaitForStatusFlags = WAIT_STATUS_EQUALITY;

  ApUtilWaitForCoreStatus (Socket, Core, &WaitForStatus, StdHeader);
  TransactionSize = ApUtilReadRemoteDataDword (Socket, Core, StdHeader);

  if (BufferInfo->DataPtr == NULL && TransactionSize != 0) {
    HeapMalloc.BufferHandle = AMD_CPU_AP_TASKING_HANDLE;
    HeapMalloc.Persist = HEAP_LOCAL_CACHE;
    // Deallocate the general purpose heap structure, if it exists.  Ignore
    // the status in case it does not exist.
    HeapDeallocateBuffer (HeapMalloc.BufferHandle, StdHeader);
    HeapMalloc.RequestedBufferSize = (TransactionSize * XFER_ELEMENT_SIZE);
    if (HeapAllocateBuffer (&HeapMalloc, StdHeader) == AGESA_SUCCESS) {
      BufferInfo->DataPtr = (UINT32 *) HeapMalloc.BufferPtr;
      BufferInfo->DataSizeInDwords = (UINT16) (HeapMalloc.RequestedBufferSize / XFER_ELEMENT_SIZE);
    } else {
      BufferInfo->DataSizeInDwords = 0;
    }
  }

  if (TransactionSize <= BufferInfo->DataSizeInDwords) {
    SourceUniqueId = ApUtilCalculateUniqueId (Socket, Core, StdHeader);
    ApUtilWriteControlByte (SourceUniqueId, StdHeader);
    CurrentStatus = CORE_DATA_FLAGS_READY;
    WaitForStatus.Status = &CurrentStatus;
    ApUtilWaitForCoreStatus (Socket, Core, &WaitForStatus, StdHeader);
    BufferInfo->DataTransferFlags =  ApUtilReadRemoteDataDword (Socket, Core, StdHeader);
    ApUtilWriteControlByte (CORE_DATA_FLAGS_ACKNOWLEDGE, StdHeader);
    if ((BufferInfo->DataTransferFlags & DATA_IN_MEMORY) != 0) {
      if (BufferInfo->DataPtr != NULL) {
        ReturnStatus = AGESA_ALERT;
      }
      ApUtilReceivePointer (Socket, Core, (VOID **) &BufferInfo->DataPtr, StdHeader);
    } else {
      CurrentStatus = CORE_STS_DATA_READY_1;
      ApUtilWaitForCoreStatus (Socket, Core, &WaitForStatus, StdHeader);
      CurrentStatus = CORE_STS_DATA_READY_0;
      ApUtilWriteControlByte (CurrentStatus, StdHeader);
      CurrentPtr = BufferInfo->DataPtr;
      for (i = 0; i < TransactionSize; ++i) {
        ApUtilWaitForCoreStatus (Socket, Core, &WaitForStatus, StdHeader);
        *CurrentPtr++ = ApUtilReadRemoteDataDword (Socket, Core, StdHeader);
        CurrentStatus ^= 0x01;
        ApUtilWriteControlByte (CurrentStatus, StdHeader);
      }
    }
    ApUtilWriteControlByte (CORE_ACTIVE, StdHeader);
  } else {
    BufferInfo->DataSizeInDwords = (UINT16) TransactionSize;
    ReturnStatus = AGESA_ERROR;
  }
  return (ReturnStatus);
}


VOID
RelinquishControlOfAllAPs (
  IN       AMD_CONFIG_PARAMS   *StdHeader
  )
{
  UINT32        BscSocket;
  UINT32        Ignored;
  UINT32        BscCoreNum;
  UINT32        Core;
  UINT32        Socket;
  UINT32        NumberOfSockets;
  AP_TASK       TaskPtr;
  AGESA_STATUS  IgnoredSts;

  ASSERT (IsBsp (StdHeader, &IgnoredSts));

  TaskPtr.FuncAddress.PfApTask = PerformFinalHalt;
  TaskPtr.DataTransfer.DataSizeInDwords = 0;
  TaskPtr.ExeFlags = WAIT_FOR_CORE;

  IdentifyCore (StdHeader, &BscSocket, &Ignored, &BscCoreNum, &IgnoredSts);
  NumberOfSockets = GetPlatformNumberOfSockets ();

  for (Socket = 0; Socket < NumberOfSockets; Socket++) {
    if (GetActiveCoresInGivenSocket (Socket, &Core, StdHeader)) {
      while (Core-- > 0) {
        if ((Socket != BscSocket) || (Core != BscCoreNum)) {
          ApUtilRunCodeOnSocketCore ((UINT8) Socket, (UINT8) Core, &TaskPtr, StdHeader);
        }
      }
    }
  }
}

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

/*---------------------------------------------------------------------------------------*/
/**
 * The last AGESA code that an AP performs
 *
 * This function, run only by APs, breaks down their cache subsystem, sets up
 * for memory to be present upon wake (from IBV Init/Startup IPIs), and halts.
 *
 * @param[in]  StdHeader         Config handle for library and services
 *
 */
VOID
STATIC
PerformFinalHalt (
  IN       AMD_CONFIG_PARAMS *StdHeader
  )
{
  UINT32 PrimaryCore;
  UINT32 HaltFlags;
  UINT32 CacheEnDis;
  CPU_SPECIFIC_SERVICES *FamilyServices;

  GetCpuServicesOfCurrentCore ((const CPU_SPECIFIC_SERVICES **)&FamilyServices, StdHeader);
  ASSERT (FamilyServices != NULL);
  // CacheEnDis is a family specific flag, that lets the code to decide whether to
  // keep the cache control bits set or cleared.
  CacheEnDis = FamilyServices->InitCacheDisabled;

  // Determine if the current core has the primary core role.  The first core to execute
  // in each compute unit has the primary role.
  PrimaryCore = (UINT32) IsCorePairPrimary (FirstCoreIsComputeUnitPrimary, StdHeader);

  // Aggregate the flags for the halt service.
  HaltFlags = PrimaryCore | (CacheEnDis << 1);

  ApUtilWriteControlByte (CORE_UNAVAILABLE, StdHeader);
  ExecuteFinalHltInstruction (HaltFlags, UserOptions.CfgApMtrrSettingsList, StdHeader);
}

/*---------------------------------------------------------------------------------------*/
/**
 * Reads the APIC register on the designated remote core.
 *
 * This function uses the remote read inter-processor interrupt protocol
 * to read an APIC register from the remote core
 *
 * @param[in]        Socket       Socket number of remote core
 * @param[in]        Core         Core number of remote core
 * @param[in]        RegAddr      APIC register to read
 * @param[in]        StdHeader    Configuration parameters pointer
 *
 * @return           The current value of the remote core's desired APIC register
 *
 */
UINT32
STATIC
ApUtilRemoteRead (
  IN       UINT8             Socket,
  IN       UINT8             Core,
  IN       UINT8             RegAddr,
  IN       AMD_CONFIG_PARAMS *StdHeader
  )
{
  UINT32 ApicRegister;
  UINT32 TargetApicId;
  UINT64 ApicBase;
  UINT64 ApicAddr;

  ApUtilGetLocalApicBase (&ApicBase, StdHeader);
  GetLocalApicIdForCore ((UINT32) Socket, (UINT32) Core, &TargetApicId, StdHeader);
  TargetApicId <<= LOCAL_APIC_ID;

  do {
    ApicAddr = ApicBase + APIC_CMD_HI_REG;
    LibAmdMemWrite (AccessWidth32, ApicAddr, &TargetApicId, StdHeader);
    ApicAddr = ApicBase + APIC_CMD_LO_REG;
    ApicRegister = CMD_REG_TO_READ | (UINT32) RegAddr;
    LibAmdMemWrite (AccessWidth32, ApicAddr, &ApicRegister, StdHeader);
    do {
      LibAmdMemRead (AccessWidth32, ApicAddr, &ApicRegister, StdHeader);
    } while ((ApicRegister & CMD_REG_DELIVERY_STATUS) != 0);
    while ((ApicRegister & CMD_REG_REMOTE_RD_STS_MSK) == CMD_REG_REMOTE_DELIVERY_PENDING) {
      LibAmdMemRead (AccessWidth32, ApicAddr, &ApicRegister, StdHeader);
    }
  } while ((ApicRegister & CMD_REG_REMOTE_RD_STS_MSK) != CMD_REG_REMOTE_DELIVERY_DONE);
  ApicAddr = ApicBase + APIC_REMOTE_READ_REG;
  LibAmdMemRead (AccessWidth32, ApicAddr, &ApicRegister, StdHeader);
  return (ApicRegister);
}


/*---------------------------------------------------------------------------------------*/
/**
 * Writes an APIC register on the executing core.
 *
 * This function gets the base address of the executing core's local APIC,
 * and writes a UINT32 value to a specified offset.
 *
 * @param[in]      RegAddr      APIC register to write to
 * @param[in]      Value        Data to be written to the desired APIC register
 * @param[in]      StdHeader    Configuration parameters pointer
 *
 */
VOID
STATIC
ApUtilLocalWrite (
  IN       UINT32 RegAddr,
  IN       UINT32 Value,
  IN       AMD_CONFIG_PARAMS *StdHeader
  )
{
  UINT64 ApicAddr;

  ApUtilGetLocalApicBase (&ApicAddr, StdHeader);
  ApicAddr += RegAddr;

  LibAmdMemWrite (AccessWidth32, ApicAddr, &Value, StdHeader);
}


/*---------------------------------------------------------------------------------------*/
/**
 * Reads an APIC register on the executing core.
 *
 * This function gets the base address of the executing core's local APIC,
 * and reads a UINT32 value from a specified offset.
 *
 * @param[in]      RegAddr      APIC register to read from
 * @param[in]      StdHeader    Configuration parameters pointer
 *
 * @return         The current value of the local APIC register
 *
 */
UINT32
STATIC
ApUtilLocalRead (
  IN       UINT32  RegAddr,
  IN       AMD_CONFIG_PARAMS *StdHeader
  )
{
  UINT32 ApicRegister;
  UINT64 ApicAddr;

  ApUtilGetLocalApicBase (&ApicAddr, StdHeader);
  ApicAddr += RegAddr;
  LibAmdMemRead (AccessWidth32, ApicAddr, &ApicRegister, StdHeader);

  return (ApicRegister);
}


/*---------------------------------------------------------------------------------------*/
/**
 * Returns the 64-bit base address of the executing core's local APIC.
 *
 * This function reads the APICBASE MSR and isolates the programmed address.
 *
 * @param[out]     ApicBase     Base address
 * @param[in]      StdHeader    Configuration parameters pointer
 *
 */
VOID
STATIC
ApUtilGetLocalApicBase (
     OUT   UINT64 *ApicBase,
  IN       AMD_CONFIG_PARAMS *StdHeader
  )
{
  LibAmdMsrRead (MSR_APIC_BAR, ApicBase, StdHeader);
  *ApicBase &= LAPIC_BASE_ADDR_MASK;
}


/*---------------------------------------------------------------------------------------*/
/**
 * Determines the unique ID of the input Socket/Core.
 *
 * This routine converts a socket-core combination to to a number
 * that will be used to directly address a particular core.  This
 * unique value must be less than 128 because we only have a byte
 * to use for status.  APIC IDs are not guaranteed to be below
 * 128.
 *
 * @param[in]      Socket       Socket number of the remote core
 * @param[in]      Core         Core number of the remote core
 * @param[in]      StdHeader    Configuration parameters pointer
 *
 * @return         The unique ID of the desired core
 *
 */
UINT8
STATIC
ApUtilCalculateUniqueId (
  IN       UINT8 Socket,
  IN       UINT8 Core,
  IN       AMD_CONFIG_PARAMS *StdHeader
  )
{
  UINT8 UniqueId;

  UniqueId = ((Core << 3) | Socket);
  ASSERT ((UniqueId & 0x80) == 0);
  return (UniqueId);
}


/*---------------------------------------------------------------------------------------*/
/**
 * Wakes up a core from the halted state.
 *
 * This function sends a directed NMI inter-processor interrupt to
 * the input Socket/Core.
 *
 * @param[in]      Socket          Socket number of remote core to wake up
 * @param[in]      Core            Socket-relative core number of the remote core to wake up
 * @param[in]      StdHeader       Configuration parameters pointer
 *
 */
VOID
STATIC
ApUtilFireDirectedNmi (
  IN       UINT8 Socket,
  IN       UINT8 Core,
  IN       AMD_CONFIG_PARAMS *StdHeader
  )
{
  UINT32 TargetApicId;

  GetLocalApicIdForCore ((UINT32) Socket, (UINT32) Core, &TargetApicId, StdHeader);
  TargetApicId <<= LOCAL_APIC_ID;

  ApUtilLocalWrite ((UINT32) APIC_CMD_HI_REG, TargetApicId, StdHeader);
  ApUtilLocalWrite ((UINT32) APIC_CMD_LO_REG, (UINT32) CMD_REG_TO_NMI, StdHeader);
}


/*---------------------------------------------------------------------------------------*/
/**
 * Securely receives a pointer from the designated remote core.
 *
 * This function uses a sequence of remote reads to receive a pointer,
 * one UINT32 at a time.
 *
 * @param[in]      Socket        Socket number of the remote core
 * @param[in]      Core          Core number of the remote core
 * @param[out]     ReturnPointer Pointer passed from remote core
 * @param[in]      StdHeader     Configuration parameters pointer
 *
 */
VOID
STATIC
ApUtilReceivePointer (
  IN       UINT8 Socket,
  IN       UINT8 Core,
     OUT   VOID  **ReturnPointer,
  IN       AMD_CONFIG_PARAMS *StdHeader
  )
{
  UINT8   i;
  UINT8   WaitStatus;
  UINT32  *AddressScratchPtr;
  AP_WAIT_FOR_STATUS WaitForStatus;

  WaitStatus = CORE_STS_DATA_READY_0;
  WaitForStatus.Status = &WaitStatus;
  WaitForStatus.NumberOfElements = 1;
  WaitForStatus.RetryCount = WAIT_INFINITELY;
  AddressScratchPtr = (UINT32 *) ReturnPointer;
  for (i = 0; i < SIZE_IN_DWORDS (AddressScratchPtr); ++i) {
    ApUtilWriteControlByte (CORE_NEEDS_PTR, StdHeader);
    WaitForStatus.WaitForStatusFlags = WAIT_STATUS_EQUALITY;
    ApUtilWaitForCoreStatus (Socket, Core, &WaitForStatus, StdHeader);
    *AddressScratchPtr++ = ApUtilReadRemoteDataDword (Socket, Core, StdHeader);
    ApUtilWriteControlByte (CORE_ACTIVE, StdHeader);
    WaitForStatus.WaitForStatusFlags = 0;
    ApUtilWaitForCoreStatus (Socket, Core, &WaitForStatus, StdHeader);
  }
}


/*---------------------------------------------------------------------------------------*/
/**
 * Securely transmits a pointer to the designated remote core.
 *
 * This function uses a sequence of remote reads to transmit a pointer,
 * one UINT32 at a time.
 *
 * @param[in]      Socket       Socket number of the remote core
 * @param[in]      Core         Core number of the remote core
 * @param[out]     Pointer      Pointer passed from remote core
 * @param[in]      StdHeader    Configuration parameters pointer
 *
 */
VOID
STATIC
ApUtilTransmitPointer (
  IN       UINT8 Socket,
  IN       UINT8 Core,
  IN       VOID  **Pointer,
  IN       AMD_CONFIG_PARAMS *StdHeader
  )
{
  UINT8   i;
  UINT8   WaitStatus;
  UINT32  *AddressScratchPtr;
  AP_WAIT_FOR_STATUS WaitForStatus;

  WaitStatus = CORE_NEEDS_PTR;
  WaitForStatus.Status = &WaitStatus;
  WaitForStatus.NumberOfElements = 1;
  WaitForStatus.RetryCount = WAIT_INFINITELY;

  AddressScratchPtr = (UINT32 *) Pointer;

  for (i = 0; i < SIZE_IN_DWORDS (AddressScratchPtr); i++) {
    WaitForStatus.WaitForStatusFlags = WAIT_STATUS_EQUALITY;
    ApUtilWaitForCoreStatus (Socket, Core, &WaitForStatus, StdHeader);
    ApUtilWriteDataDword (*AddressScratchPtr++, StdHeader);
    ApUtilWriteControlByte (CORE_STS_DATA_READY_0, StdHeader);
    WaitForStatus.WaitForStatusFlags = 0;
    ApUtilWaitForCoreStatus (Socket, Core, &WaitForStatus, StdHeader);
    ApUtilWriteControlByte (CORE_ACTIVE, StdHeader);
  }
}
