/** @file | |
Multi-Processor support functions implementation. | |
Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include "DebugAgent.h" | |
GLOBAL_REMOVE_IF_UNREFERENCED DEBUG_MP_CONTEXT volatile mDebugMpContext = { 0, 0, 0, { 0 }, { 0 }, 0, 0, 0, 0, FALSE, FALSE }; | |
GLOBAL_REMOVE_IF_UNREFERENCED DEBUG_CPU_DATA volatile mDebugCpuData = { 0 }; | |
/** | |
Acquire a spin lock when Multi-processor supported. | |
It will block in the function if cannot get the access control. | |
If Multi-processor is not supported, return directly. | |
@param[in, out] MpSpinLock A pointer to the spin lock. | |
**/ | |
VOID | |
AcquireMpSpinLock ( | |
IN OUT SPIN_LOCK *MpSpinLock | |
) | |
{ | |
if (!MultiProcessorDebugSupport ()) { | |
return; | |
} | |
while (TRUE) { | |
if (AcquireSpinLockOrFail (MpSpinLock)) { | |
break; | |
} | |
CpuPause (); | |
continue; | |
} | |
} | |
/** | |
Release a spin lock when Multi-processor supported. | |
@param[in, out] MpSpinLock A pointer to the spin lock. | |
**/ | |
VOID | |
ReleaseMpSpinLock ( | |
IN OUT SPIN_LOCK *MpSpinLock | |
) | |
{ | |
if (!MultiProcessorDebugSupport ()) { | |
return; | |
} | |
ReleaseSpinLock (MpSpinLock); | |
} | |
/** | |
Break the other processor by send IPI. | |
@param[in] CurrentProcessorIndex Current processor index value. | |
**/ | |
VOID | |
HaltOtherProcessors ( | |
IN UINT32 CurrentProcessorIndex | |
) | |
{ | |
DebugAgentMsgPrint (DEBUG_AGENT_INFO, "processor[%x]:Try to halt other processors.\n", CurrentProcessorIndex); | |
if (!DebugAgentIsBsp (CurrentProcessorIndex)) { | |
SetIpiSentByApFlag (TRUE); | |
} | |
mDebugMpContext.BreakAtCpuIndex = CurrentProcessorIndex; | |
// | |
// Set the debug viewpoint to the current breaking CPU. | |
// | |
SetDebugViewPoint (CurrentProcessorIndex); | |
// | |
// Send fixed IPI to other processors. | |
// | |
SendFixedIpiAllExcludingSelf (DEBUG_TIMER_VECTOR); | |
} | |
/** | |
Get the current processor's index. | |
@return Processor index value. | |
**/ | |
UINT32 | |
GetProcessorIndex ( | |
VOID | |
) | |
{ | |
UINT32 Index; | |
UINT16 LocalApicID; | |
LocalApicID = (UINT16)GetApicId (); | |
AcquireMpSpinLock (&mDebugMpContext.MpContextSpinLock); | |
for (Index = 0; Index < mDebugCpuData.CpuCount; Index++) { | |
if (mDebugCpuData.ApicID[Index] == LocalApicID) { | |
break; | |
} | |
} | |
if (Index == mDebugCpuData.CpuCount) { | |
mDebugCpuData.ApicID[Index] = LocalApicID; | |
mDebugCpuData.CpuCount++; | |
} | |
ReleaseMpSpinLock (&mDebugMpContext.MpContextSpinLock); | |
return Index; | |
} | |
/** | |
Check if the specified processor is BSP or not. | |
@param[in] ProcessorIndex Processor index value. | |
@retval TRUE It is BSP. | |
@retval FALSE It isn't BSP. | |
**/ | |
BOOLEAN | |
DebugAgentIsBsp ( | |
IN UINT32 ProcessorIndex | |
) | |
{ | |
MSR_IA32_APIC_BASE_REGISTER MsrApicBase; | |
// | |
// If there are less than 2 CPUs detected, then the currently executing CPU | |
// must be the BSP. This avoids an access to an MSR that may not be supported | |
// on single core CPUs. | |
// | |
if (mDebugCpuData.CpuCount < 2) { | |
return TRUE; | |
} | |
MsrApicBase.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE); | |
if (MsrApicBase.Bits.BSP == 1) { | |
if (mDebugMpContext.BspIndex != ProcessorIndex) { | |
AcquireMpSpinLock (&mDebugMpContext.MpContextSpinLock); | |
mDebugMpContext.BspIndex = ProcessorIndex; | |
ReleaseMpSpinLock (&mDebugMpContext.MpContextSpinLock); | |
} | |
return TRUE; | |
} else { | |
return FALSE; | |
} | |
} | |
/** | |
Set processor stop flag bitmask in MP context. | |
@param[in] ProcessorIndex Processor index value. | |
@param[in] StopFlag TRUE means set stop flag. | |
FALSE means clean break flag. | |
**/ | |
VOID | |
SetCpuStopFlagByIndex ( | |
IN UINT32 ProcessorIndex, | |
IN BOOLEAN StopFlag | |
) | |
{ | |
UINT8 Value; | |
UINTN Index; | |
AcquireMpSpinLock (&mDebugMpContext.MpContextSpinLock); | |
Value = mDebugMpContext.CpuStopStatusMask[ProcessorIndex / 8]; | |
Index = ProcessorIndex % 8; | |
if (StopFlag) { | |
Value = BitFieldWrite8 (Value, Index, Index, 1); | |
} else { | |
Value = BitFieldWrite8 (Value, Index, Index, 0); | |
} | |
mDebugMpContext.CpuStopStatusMask[ProcessorIndex / 8] = Value; | |
ReleaseMpSpinLock (&mDebugMpContext.MpContextSpinLock); | |
} | |
/** | |
Set processor break flag bitmask in MP context. | |
@param[in] ProcessorIndex Processor index value. | |
@param[in] BreakFlag TRUE means set break flag. | |
FALSE means clean break flag. | |
**/ | |
VOID | |
SetCpuBreakFlagByIndex ( | |
IN UINT32 ProcessorIndex, | |
IN BOOLEAN BreakFlag | |
) | |
{ | |
UINT8 Value; | |
UINTN Index; | |
AcquireMpSpinLock (&mDebugMpContext.MpContextSpinLock); | |
Value = mDebugMpContext.CpuBreakMask[ProcessorIndex / 8]; | |
Index = ProcessorIndex % 8; | |
if (BreakFlag) { | |
Value = BitFieldWrite8 (Value, Index, Index, 1); | |
} else { | |
Value = BitFieldWrite8 (Value, Index, Index, 0); | |
} | |
mDebugMpContext.CpuBreakMask[ProcessorIndex / 8] = Value; | |
ReleaseMpSpinLock (&mDebugMpContext.MpContextSpinLock); | |
} | |
/** | |
Check if processor is stopped already. | |
@param[in] ProcessorIndex Processor index value. | |
@retval TRUE Processor is stopped already. | |
@retval TRUE Processor isn't stopped. | |
**/ | |
BOOLEAN | |
IsCpuStopped ( | |
IN UINT32 ProcessorIndex | |
) | |
{ | |
UINT8 CpuMask; | |
CpuMask = (UINT8)(1 << (ProcessorIndex % 8)); | |
if ((mDebugMpContext.CpuStopStatusMask[ProcessorIndex / 8] & CpuMask) != 0) { | |
return TRUE; | |
} else { | |
return FALSE; | |
} | |
} | |
/** | |
Set the run command flag. | |
@param[in] RunningFlag TRUE means run command flag is set. | |
FALSE means run command flag is cleared. | |
**/ | |
VOID | |
SetCpuRunningFlag ( | |
IN BOOLEAN RunningFlag | |
) | |
{ | |
AcquireMpSpinLock (&mDebugMpContext.MpContextSpinLock); | |
mDebugMpContext.RunCommandSet = RunningFlag; | |
ReleaseMpSpinLock (&mDebugMpContext.MpContextSpinLock); | |
} | |
/** | |
Set the current view point to be debugged. | |
@param[in] ProcessorIndex Processor index value. | |
**/ | |
VOID | |
SetDebugViewPoint ( | |
IN UINT32 ProcessorIndex | |
) | |
{ | |
AcquireMpSpinLock (&mDebugMpContext.MpContextSpinLock); | |
mDebugMpContext.ViewPointIndex = ProcessorIndex; | |
ReleaseMpSpinLock (&mDebugMpContext.MpContextSpinLock); | |
} | |
/** | |
Set the IPI send by BPS/AP flag. | |
@param[in] IpiSentByApFlag TRUE means this IPI is sent by AP. | |
FALSE means this IPI is sent by BSP. | |
**/ | |
VOID | |
SetIpiSentByApFlag ( | |
IN BOOLEAN IpiSentByApFlag | |
) | |
{ | |
AcquireMpSpinLock (&mDebugMpContext.MpContextSpinLock); | |
mDebugMpContext.IpiSentByAp = IpiSentByApFlag; | |
ReleaseMpSpinLock (&mDebugMpContext.MpContextSpinLock); | |
} | |
/** | |
Check the next pending breaking CPU. | |
@retval others There is at least one processor broken, the minimum | |
index number of Processor returned. | |
@retval -1 No any processor broken. | |
**/ | |
UINT32 | |
FindNextPendingBreakCpu ( | |
VOID | |
) | |
{ | |
UINT32 Index; | |
for (Index = 0; Index < DEBUG_CPU_MAX_COUNT / 8; Index++) { | |
if (mDebugMpContext.CpuBreakMask[Index] != 0) { | |
return (UINT32)LowBitSet32 (mDebugMpContext.CpuBreakMask[Index]) + Index * 8; | |
} | |
} | |
return (UINT32)-1; | |
} | |
/** | |
Check if all processors are in running status. | |
@retval TRUE All processors run. | |
@retval FALSE At least one processor does not run. | |
**/ | |
BOOLEAN | |
IsAllCpuRunning ( | |
VOID | |
) | |
{ | |
UINTN Index; | |
for (Index = 0; Index < DEBUG_CPU_MAX_COUNT / 8; Index++) { | |
if (mDebugMpContext.CpuStopStatusMask[Index] != 0) { | |
return FALSE; | |
} | |
} | |
return TRUE; | |
} | |
/** | |
Check if the current processor is the first breaking processor. | |
If yes, halt other processors. | |
@param[in] ProcessorIndex Processor index value. | |
@return TRUE This processor is the first breaking processor. | |
@return FALSE This processor is not the first breaking processor. | |
**/ | |
BOOLEAN | |
IsFirstBreakProcessor ( | |
IN UINT32 ProcessorIndex | |
) | |
{ | |
if (MultiProcessorDebugSupport ()) { | |
if (mDebugMpContext.BreakAtCpuIndex != (UINT32)-1) { | |
// | |
// The current processor is not the first breaking one. | |
// | |
SetCpuBreakFlagByIndex (ProcessorIndex, TRUE); | |
return FALSE; | |
} else { | |
// | |
// If no any processor breaks, try to halt other processors | |
// | |
HaltOtherProcessors (ProcessorIndex); | |
return TRUE; | |
} | |
} | |
return TRUE; | |
} |