/** @file Multi-Processor support functions implementation. Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.
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; }