+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Worker function to execute a caller provided function on all enabled APs.\r
+\r
+ @param[in] Procedure A pointer to the function to be run on\r
+ enabled APs of the system.\r
+ @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for\r
+ APs to return from Procedure, either for\r
+ blocking or non-blocking mode.\r
+ @param[in,out] ProcedureArguments The parameter passed into Procedure for\r
+ all APs.\r
+ @param[in,out] 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,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
+\r
+ @retval EFI_SUCCESS In blocking mode, all APs have finished before\r
+ the timeout expired.\r
+ @retval EFI_SUCCESS In non-blocking mode, function has been dispatched\r
+ to all enabled APs.\r
+ @retval others Failed to Startup all APs.\r
+\r
+**/\r
+EFI_STATUS\r
+InternalSmmStartupAllAPs (\r
+ IN EFI_AP_PROCEDURE2 Procedure,\r
+ IN UINTN TimeoutInMicroseconds,\r
+ IN OUT VOID *ProcedureArguments OPTIONAL,\r
+ IN OUT MM_COMPLETION *Token,\r
+ IN OUT EFI_STATUS *CPUStatus\r
+ )\r
+{\r
+ UINTN Index;\r
+ UINTN CpuCount;\r
+ PROCEDURE_TOKEN *ProcToken;\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
+ CpuCount = 0;\r
+ for (Index = 0; Index < mMaxNumberOfCpus; Index++) {\r
+ if (IsPresentAp (Index)) {\r
+ CpuCount ++;\r
+\r
+ if (gSmmCpuPrivate->Operation[Index] == SmmCpuRemove) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (!AcquireSpinLockOrFail(mSmmMpSyncData->CpuData[Index].Busy)) {\r
+ return EFI_NOT_READY;\r
+ }\r
+ ReleaseSpinLock (mSmmMpSyncData->CpuData[Index].Busy);\r
+ }\r
+ }\r
+ if (CpuCount == 0) {\r
+ return EFI_NOT_STARTED;\r
+ }\r
+\r
+ if (Token != NULL) {\r
+ ProcToken = GetFreeToken ((UINT32)mMaxNumberOfCpus);\r
+ *Token = (MM_COMPLETION)ProcToken->SpinLock;\r
+ } else {\r
+ ProcToken = NULL;\r
+ }\r
+\r
+ //\r
+ // Make sure all BUSY should be acquired.\r
+ //\r
+ // Because former code already check mSmmMpSyncData->CpuData[***].Busy for each AP.\r
+ // Here code always use AcquireSpinLock instead of AcquireSpinLockOrFail for not\r
+ // block mode.\r
+ //\r
+ for (Index = 0; Index < mMaxNumberOfCpus; Index++) {\r
+ if (IsPresentAp (Index)) {\r
+ AcquireSpinLock (mSmmMpSyncData->CpuData[Index].Busy);\r
+ }\r
+ }\r
+\r
+ for (Index = 0; Index < mMaxNumberOfCpus; Index++) {\r
+ if (IsPresentAp (Index)) {\r
+ mSmmMpSyncData->CpuData[Index].Procedure = (EFI_AP_PROCEDURE2) Procedure;\r
+ mSmmMpSyncData->CpuData[Index].Parameter = ProcedureArguments;\r
+ if (ProcToken != NULL) {\r
+ mSmmMpSyncData->CpuData[Index].Token = ProcToken;\r
+ }\r
+ if (CPUStatus != NULL) {\r
+ mSmmMpSyncData->CpuData[Index].Status = &CPUStatus[Index];\r
+ if (mSmmMpSyncData->CpuData[Index].Status != NULL) {\r
+ *mSmmMpSyncData->CpuData[Index].Status = EFI_NOT_READY;\r
+ }\r
+ }\r
+ } else {\r
+ //\r
+ // PI spec requirement:\r
+ // For every excluded processor, the array entry must contain a value of EFI_NOT_STARTED.\r
+ //\r
+ if (CPUStatus != NULL) {\r
+ CPUStatus[Index] = EFI_NOT_STARTED;\r
+ }\r
+\r
+ //\r
+ // Decrease the count to mark this processor(AP or BSP) as finished.\r
+ //\r
+ if (ProcToken != NULL) {\r
+ WaitForSemaphore (&ProcToken->RunningApCount);\r
+ }\r
+ }\r
+ }\r
+\r
+ ReleaseAllAPs ();\r
+\r
+ if (Token == NULL) {\r
+ //\r
+ // Make sure all APs have completed their tasks.\r
+ //\r
+ WaitForAllAPsNotBusy (TRUE);\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ ISO C99 6.5.2.2 "Function calls", paragraph 9:\r
+ If the function is defined with a type that is not compatible with\r
+ the type (of the expression) pointed to by the expression that\r
+ denotes the called function, the behavior is undefined.\r
+\r
+ So add below wrapper function to convert between EFI_AP_PROCEDURE\r
+ and EFI_AP_PROCEDURE2.\r
+\r
+ Wrapper for Procedures.\r
+\r
+ @param[in] Buffer Pointer to PROCEDURE_WRAPPER buffer.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+ProcedureWrapper (\r
+ IN VOID *Buffer\r
+ )\r
+{\r
+ PROCEDURE_WRAPPER *Wrapper;\r
+\r
+ Wrapper = Buffer;\r
+ Wrapper->Procedure (Wrapper->ProcedureArgument);\r
+\r