+ UINTN SpinLockSize;\r
+ UINT32 TokenCountPerChunk;\r
+ UINTN ProcTokenSize;\r
+ UINTN Index;\r
+ PROCEDURE_TOKEN *ProcToken;\r
+ SPIN_LOCK *SpinLock;\r
+ UINT8 *SpinLockBuffer;\r
+ UINT8 *ProcTokenBuffer;\r
+\r
+ SpinLockSize = GetSpinLockProperties ();\r
+ ProcTokenSize = sizeof (PROCEDURE_TOKEN);\r
+\r
+ TokenCountPerChunk = FixedPcdGet32 (PcdCpuSmmMpTokenCountPerChunk);\r
+ ASSERT (TokenCountPerChunk != 0);\r
+ if (TokenCountPerChunk == 0) {\r
+ DEBUG ((DEBUG_ERROR, "PcdCpuSmmMpTokenCountPerChunk should not be Zero!\n"));\r
+ CpuDeadLoop ();\r
+ }\r
+ DEBUG ((DEBUG_INFO, "CpuSmm: SpinLock Size = 0x%x, PcdCpuSmmMpTokenCountPerChunk = 0x%x\n", SpinLockSize, TokenCountPerChunk));\r
+\r
+ //\r
+ // Separate the Spin_lock and Proc_token because the alignment requires by Spin_Lock.\r
+ //\r
+ SpinLockBuffer = AllocatePool (SpinLockSize * TokenCountPerChunk);\r
+ ASSERT (SpinLockBuffer != NULL);\r
+\r
+ ProcTokenBuffer = AllocatePool (ProcTokenSize * TokenCountPerChunk);\r
+ ASSERT (ProcTokenBuffer != NULL);\r
+\r
+ for (Index = 0; Index < TokenCountPerChunk; Index++) {\r
+ SpinLock = (SPIN_LOCK *)(SpinLockBuffer + SpinLockSize * Index);\r
+ InitializeSpinLock (SpinLock);\r
+\r
+ ProcToken = (PROCEDURE_TOKEN *)(ProcTokenBuffer + ProcTokenSize * Index);\r
+ ProcToken->Signature = PROCEDURE_TOKEN_SIGNATURE;\r
+ ProcToken->SpinLock = SpinLock;\r
+ ProcToken->Used = FALSE;\r
+ ProcToken->RunningApCount = 0;\r
+\r
+ InsertTailList (&gSmmCpuPrivate->TokenList, &ProcToken->Link);\r
+ }\r
+}\r
+\r
+/**\r
+ Find first free token in the allocated token list.\r
+\r
+ @retval return the first free PROCEDURE_TOKEN.\r
+\r
+**/\r
+PROCEDURE_TOKEN *\r
+FindFirstFreeToken (\r
+ VOID\r
+ )\r
+{\r
+ LIST_ENTRY *Link;\r
+ PROCEDURE_TOKEN *ProcToken;\r
+\r
+ Link = GetFirstNode (&gSmmCpuPrivate->TokenList);\r
+ while (!IsNull (&gSmmCpuPrivate->TokenList, Link)) {\r
+ ProcToken = PROCEDURE_TOKEN_FROM_LINK (Link);\r
+\r
+ if (!ProcToken->Used) {\r
+ return ProcToken;\r
+ }\r
+\r
+ Link = GetNextNode (&gSmmCpuPrivate->TokenList, Link);\r
+ }\r
+\r
+ return NULL;\r
+}\r
+\r
+/**\r
+ Get the free token.\r
+\r
+ If no free token, allocate new tokens then return the free one.\r
+\r
+ @retval return the first free PROCEDURE_TOKEN.\r
+\r
+**/\r
+PROCEDURE_TOKEN *\r
+GetFreeToken (\r
+ IN UINT32 RunningApsCount\r
+ )\r
+{\r
+ PROCEDURE_TOKEN *NewToken;\r
+\r
+ NewToken = FindFirstFreeToken ();\r
+ if (NewToken == NULL) {\r
+ AllocateTokenBuffer ();\r
+ NewToken = FindFirstFreeToken ();\r
+ }\r
+ ASSERT (NewToken != NULL);\r
+\r
+ NewToken->Used = TRUE;\r
+ NewToken->RunningApCount = RunningApsCount;\r
+ AcquireSpinLock (NewToken->SpinLock);\r
+\r
+ return NewToken;\r
+}\r
+\r
+/**\r
+ Checks status of specified AP.\r
+\r
+ This function checks whether the specified AP has finished the task assigned\r
+ by StartupThisAP(), and whether timeout expires.\r
+\r
+ @param[in] Token This parameter describes the token that was passed into DispatchProcedure or\r
+ BroadcastProcedure.\r
+\r
+ @retval EFI_SUCCESS Specified AP has finished task assigned by StartupThisAPs().\r
+ @retval EFI_NOT_READY Specified AP has not finished task and timeout has not expired.\r
+**/\r
+EFI_STATUS\r
+IsApReady (\r
+ IN SPIN_LOCK *Token\r
+ )\r
+{\r
+ if (AcquireSpinLockOrFail (Token)) {\r
+ ReleaseSpinLock (Token);\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ return EFI_NOT_READY;\r
+}\r
+\r
+/**\r
+ Schedule a procedure to run on the specified CPU.\r
+\r
+ @param[in] Procedure The address of the procedure to run\r
+ @param[in] CpuIndex Target CPU Index\r
+ @param[in,out] ProcArguments The parameter to pass to the procedure\r
+ @param[in] Token This is an optional parameter that allows the caller to execute the\r
+ procedure in a blocking or non-blocking fashion. If it is NULL the\r
+ call is blocking, and the call will not return until the AP has\r
+ completed the procedure. If the token is not NULL, the call will\r
+ return immediately. The caller can check whether the procedure has\r
+ completed with CheckOnProcedure or WaitForProcedure.\r
+ @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for the APs to finish\r
+ execution of Procedure, either for blocking or non-blocking mode.\r
+ Zero means infinity. If the timeout expires before all APs return\r
+ from Procedure, then Procedure on the failed APs is terminated. If\r
+ the timeout expires in blocking mode, the call returns EFI_TIMEOUT.\r
+ If the timeout expires in non-blocking mode, the timeout determined\r
+ can be through CheckOnProcedure or WaitForProcedure.\r
+ Note that timeout support is optional. Whether an implementation\r
+ supports this feature can be determined via the Attributes data\r
+ member.\r
+ @param[in,out] CpuStatus This optional pointer may be used to get the status code returned\r
+ by Procedure when it completes execution on the target AP, or with\r
+ EFI_TIMEOUT if the Procedure fails to complete within the optional\r
+ timeout. The implementation will update this variable with\r
+ EFI_NOT_READY prior to starting Procedure on the target AP.\r
+\r
+ @retval EFI_INVALID_PARAMETER CpuNumber not valid\r
+ @retval EFI_INVALID_PARAMETER CpuNumber specifying BSP\r
+ @retval EFI_INVALID_PARAMETER The AP specified by CpuNumber did not enter SMM\r
+ @retval EFI_INVALID_PARAMETER The AP specified by CpuNumber is busy\r
+ @retval EFI_SUCCESS The procedure has been successfully scheduled\r
+\r
+**/\r
+EFI_STATUS\r
+InternalSmmStartupThisAp (\r
+ IN EFI_AP_PROCEDURE2 Procedure,\r
+ IN UINTN CpuIndex,\r
+ IN OUT VOID *ProcArguments OPTIONAL,\r
+ IN MM_COMPLETION *Token,\r
+ IN UINTN TimeoutInMicroseconds,\r
+ IN OUT EFI_STATUS *CpuStatus\r
+ )\r
+{\r
+ PROCEDURE_TOKEN *ProcToken;\r
+\r
+ if (CpuIndex >= gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus) {\r
+ DEBUG((DEBUG_ERROR, "CpuIndex(%d) >= gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus(%d)\n", CpuIndex, gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus));\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ if (CpuIndex == gSmmCpuPrivate->SmmCoreEntryContext.CurrentlyExecutingCpu) {\r
+ DEBUG((DEBUG_ERROR, "CpuIndex(%d) == gSmmCpuPrivate->SmmCoreEntryContext.CurrentlyExecutingCpu\n", CpuIndex));\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ if (gSmmCpuPrivate->ProcessorInfo[CpuIndex].ProcessorId == INVALID_APIC_ID) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ if (!(*(mSmmMpSyncData->CpuData[CpuIndex].Present))) {\r
+ if (mSmmMpSyncData->EffectiveSyncMode == SmmCpuSyncModeTradition) {\r
+ DEBUG((DEBUG_ERROR, "!mSmmMpSyncData->CpuData[%d].Present\n", CpuIndex));\r
+ }\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ if (gSmmCpuPrivate->Operation[CpuIndex] == SmmCpuRemove) {\r
+ if (!FeaturePcdGet (PcdCpuHotPlugSupport)) {\r
+ DEBUG((DEBUG_ERROR, "gSmmCpuPrivate->Operation[%d] == SmmCpuRemove\n", CpuIndex));\r
+ }\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ if ((TimeoutInMicroseconds != 0) && ((mSmmMp.Attributes & EFI_MM_MP_TIMEOUT_SUPPORTED) == 0)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ if (Procedure == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ AcquireSpinLock (mSmmMpSyncData->CpuData[CpuIndex].Busy);\r
+\r
+ mSmmMpSyncData->CpuData[CpuIndex].Procedure = Procedure;\r
+ mSmmMpSyncData->CpuData[CpuIndex].Parameter = ProcArguments;\r
+ if (Token != NULL) {\r
+ ProcToken= GetFreeToken (1);\r
+ mSmmMpSyncData->CpuData[CpuIndex].Token = ProcToken;\r
+ *Token = (MM_COMPLETION)ProcToken->SpinLock;\r
+ }\r
+ mSmmMpSyncData->CpuData[CpuIndex].Status = CpuStatus;\r
+ if (mSmmMpSyncData->CpuData[CpuIndex].Status != NULL) {\r
+ *mSmmMpSyncData->CpuData[CpuIndex].Status = EFI_NOT_READY;\r
+ }\r