/** @file\r
SMM MP service implementation\r
\r
-Copyright (c) 2009 - 2019, Intel Corporation. All rights reserved.<BR>\r
+Copyright (c) 2009 - 2021, Intel Corporation. All rights reserved.<BR>\r
Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR>\r
\r
-This program and the accompanying materials\r
-are licensed and made available under the terms and conditions of the BSD License\r
-which accompanies this distribution. The full text of the license may be found at\r
-http://opensource.org/licenses/bsd-license.php\r
-\r
-THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
-WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+SPDX-License-Identifier: BSD-2-Clause-Patent\r
\r
**/\r
\r
SPIN_LOCK *mPFLock = NULL;\r
SMM_CPU_SYNC_MODE mCpuSmmSyncMode;\r
BOOLEAN mMachineCheckSupported = FALSE;\r
+MM_COMPLETION mSmmStartupThisApToken;\r
+\r
+extern UINTN mSmmShadowStackSize;\r
\r
/**\r
Performs an atomic compare exchange operation to get semaphore.\r
{\r
UINT32 Value;\r
\r
- do {\r
+ for (;;) {\r
Value = *Sem;\r
- } while (Value == 0 ||\r
- InterlockedCompareExchange32 (\r
- (UINT32*)Sem,\r
- Value,\r
- Value - 1\r
- ) != Value);\r
+ if (Value != 0 &&\r
+ InterlockedCompareExchange32 (\r
+ (UINT32*)Sem,\r
+ Value,\r
+ Value - 1\r
+ ) == Value) {\r
+ break;\r
+ }\r
+ CpuPause ();\r
+ }\r
return Value - 1;\r
}\r
\r
)\r
{\r
UINTN Index;\r
- UINTN BspIndex;\r
\r
- BspIndex = mSmmMpSyncData->BspIndex;\r
- for (Index = mMaxNumberOfCpus; Index-- > 0;) {\r
- if (Index != BspIndex && *(mSmmMpSyncData->CpuData[Index].Present)) {\r
+ for (Index = 0; Index < mMaxNumberOfCpus; Index++) {\r
+ if (IsPresentAp (Index)) {\r
ReleaseSemaphore (mSmmMpSyncData->CpuData[Index].Run);\r
}\r
}\r
\r
CpuData = mSmmMpSyncData->CpuData;\r
ProcessorInfo = gSmmCpuPrivate->ProcessorInfo;\r
- for (Index = mMaxNumberOfCpus; Index-- > 0;) {\r
+ for (Index = 0; Index < mMaxNumberOfCpus; Index++) {\r
if (!(*(CpuData[Index].Present)) && ProcessorInfo[Index].ProcessorId != INVALID_APIC_ID) {\r
if (((Exceptions & ARRIVAL_EXCEPTION_DELAYED) != 0) && SmmCpuFeaturesGetSmmRegister (Index, SmmRegSmmDelayed) != 0) {\r
continue;\r
//\r
// Send SMI IPIs to bring outside processors in\r
//\r
- for (Index = mMaxNumberOfCpus; Index-- > 0;) {\r
+ for (Index = 0; Index < mMaxNumberOfCpus; Index++) {\r
if (!(*(mSmmMpSyncData->CpuData[Index].Present)) && gSmmCpuPrivate->ProcessorInfo[Index].ProcessorId != INVALID_APIC_ID) {\r
SendSmiIpi ((UINT32)gSmmCpuPrivate->ProcessorInfo[Index].ProcessorId);\r
}\r
MtrrSetAllMtrrs (&gSmiMtrrs);\r
}\r
\r
+/**\r
+ Wheck whether task has been finished by all APs.\r
+\r
+ @param BlockMode Whether did it in block mode or non-block mode.\r
+\r
+ @retval TRUE Task has been finished by all APs.\r
+ @retval FALSE Task not has been finished by all APs.\r
+\r
+**/\r
+BOOLEAN\r
+WaitForAllAPsNotBusy (\r
+ IN BOOLEAN BlockMode\r
+ )\r
+{\r
+ UINTN Index;\r
+\r
+ for (Index = 0; Index < mMaxNumberOfCpus; Index++) {\r
+ //\r
+ // Ignore BSP and APs which not call in SMM.\r
+ //\r
+ if (!IsPresentAp(Index)) {\r
+ continue;\r
+ }\r
+\r
+ if (BlockMode) {\r
+ AcquireSpinLock(mSmmMpSyncData->CpuData[Index].Busy);\r
+ ReleaseSpinLock(mSmmMpSyncData->CpuData[Index].Busy);\r
+ } else {\r
+ if (AcquireSpinLockOrFail (mSmmMpSyncData->CpuData[Index].Busy)) {\r
+ ReleaseSpinLock(mSmmMpSyncData->CpuData[Index].Busy);\r
+ } else {\r
+ return FALSE;\r
+ }\r
+ }\r
+ }\r
+\r
+ return TRUE;\r
+}\r
+\r
+/**\r
+ Check whether it is an present AP.\r
+\r
+ @param CpuIndex The AP index which calls this function.\r
+\r
+ @retval TRUE It's a present AP.\r
+ @retval TRUE This is not an AP or it is not present.\r
+\r
+**/\r
+BOOLEAN\r
+IsPresentAp (\r
+ IN UINTN CpuIndex\r
+ )\r
+{\r
+ return ((CpuIndex != gSmmCpuPrivate->SmmCoreEntryContext.CurrentlyExecutingCpu) &&\r
+ *(mSmmMpSyncData->CpuData[CpuIndex].Present));\r
+}\r
+\r
+/**\r
+ Clean up the status flags used during executing the procedure.\r
+\r
+ @param CpuIndex The AP index which calls this function.\r
+\r
+**/\r
+VOID\r
+ReleaseToken (\r
+ IN UINTN CpuIndex\r
+ )\r
+{\r
+ PROCEDURE_TOKEN *Token;\r
+\r
+ Token = mSmmMpSyncData->CpuData[CpuIndex].Token;\r
+\r
+ if (InterlockedDecrement (&Token->RunningApCount) == 0) {\r
+ ReleaseSpinLock (Token->SpinLock);\r
+ }\r
+\r
+ mSmmMpSyncData->CpuData[CpuIndex].Token = NULL;\r
+}\r
+\r
+/**\r
+ Free the tokens in the maintained list.\r
+\r
+**/\r
+VOID\r
+ResetTokens (\r
+ VOID\r
+ )\r
+{\r
+ //\r
+ // Reset the FirstFreeToken to the beginning of token list upon exiting SMI.\r
+ //\r
+ gSmmCpuPrivate->FirstFreeToken = GetFirstNode (&gSmmCpuPrivate->TokenList);\r
+}\r
+\r
/**\r
SMI handler for BSP.\r
\r
//\r
// Make sure all APs have completed their pending none-block tasks\r
//\r
- for (Index = mMaxNumberOfCpus; Index-- > 0;) {\r
- if (Index != CpuIndex && *(mSmmMpSyncData->CpuData[Index].Present)) {\r
- AcquireSpinLock (mSmmMpSyncData->CpuData[Index].Busy);\r
- ReleaseSpinLock (mSmmMpSyncData->CpuData[Index].Busy);\r
- }\r
- }\r
+ WaitForAllAPsNotBusy (TRUE);\r
\r
//\r
// Perform the remaining tasks\r
//\r
while (TRUE) {\r
PresentCount = 0;\r
- for (Index = mMaxNumberOfCpus; Index-- > 0;) {\r
+ for (Index = 0; Index < mMaxNumberOfCpus; Index++) {\r
if (*(mSmmMpSyncData->CpuData[Index].Present)) {\r
PresentCount ++;\r
}\r
//\r
WaitForAllAPs (ApCount);\r
\r
+ //\r
+ // Reset the tokens buffer.\r
+ //\r
+ ResetTokens ();\r
+\r
//\r
// Reset BspIndex to -1, meaning BSP has not been elected.\r
//\r
UINT64 Timer;\r
UINTN BspIndex;\r
MTRR_SETTINGS Mtrrs;\r
+ EFI_STATUS ProcedureStatus;\r
\r
//\r
// Timeout BSP\r
//\r
// Invoke the scheduled procedure\r
//\r
- (*mSmmMpSyncData->CpuData[CpuIndex].Procedure) (\r
- (VOID*)mSmmMpSyncData->CpuData[CpuIndex].Parameter\r
- );\r
+ ProcedureStatus = (*mSmmMpSyncData->CpuData[CpuIndex].Procedure) (\r
+ (VOID*)mSmmMpSyncData->CpuData[CpuIndex].Parameter\r
+ );\r
+ if (mSmmMpSyncData->CpuData[CpuIndex].Status != NULL) {\r
+ *mSmmMpSyncData->CpuData[CpuIndex].Status = ProcedureStatus;\r
+ }\r
+\r
+ if (mSmmMpSyncData->CpuData[CpuIndex].Token != NULL) {\r
+ ReleaseToken (CpuIndex);\r
+ }\r
\r
//\r
// Release BUSY\r
// Add two more pages for known good stack and stack guard page,\r
// then find the lower 2MB aligned address.\r
//\r
- High2MBoundary = (mSmmStackArrayEnd - mSmmStackSize + EFI_PAGE_SIZE * 2) & ~(SIZE_2MB-1);\r
+ High2MBoundary = (mSmmStackArrayEnd - mSmmStackSize - mSmmShadowStackSize + EFI_PAGE_SIZE * 2) & ~(SIZE_2MB-1);\r
PagesNeeded = ((High2MBoundary - Low2MBoundary) / SIZE_2MB) + 1;\r
}\r
//\r
// Mark the guard page as non-present\r
//\r
Pte[Index] = PageAddress | mAddressEncMask;\r
- GuardPage += mSmmStackSize;\r
+ GuardPage += (mSmmStackSize + mSmmShadowStackSize);\r
if (GuardPage > mSmmStackArrayEnd) {\r
GuardPage = 0;\r
}\r
return (UINT32)(UINTN)PageTable;\r
}\r
\r
+/**\r
+ Checks whether the input token is the current used token.\r
+\r
+ @param[in] Token This parameter describes the token that was passed into DispatchProcedure or\r
+ BroadcastProcedure.\r
+\r
+ @retval TRUE The input token is the current used token.\r
+ @retval FALSE The input token is not the current used token.\r
+**/\r
+BOOLEAN\r
+IsTokenInUse (\r
+ IN SPIN_LOCK *Token\r
+ )\r
+{\r
+ LIST_ENTRY *Link;\r
+ PROCEDURE_TOKEN *ProcToken;\r
+\r
+ if (Token == NULL) {\r
+ return FALSE;\r
+ }\r
+\r
+ Link = GetFirstNode (&gSmmCpuPrivate->TokenList);\r
+ //\r
+ // Only search used tokens.\r
+ //\r
+ while (Link != gSmmCpuPrivate->FirstFreeToken) {\r
+ ProcToken = PROCEDURE_TOKEN_FROM_LINK (Link);\r
+\r
+ if (ProcToken->SpinLock == Token) {\r
+ return TRUE;\r
+ }\r
+\r
+ Link = GetNextNode (&gSmmCpuPrivate->TokenList, Link);\r
+ }\r
+\r
+ return FALSE;\r
+}\r
+\r
+/**\r
+ Allocate buffer for the SPIN_LOCK and PROCEDURE_TOKEN.\r
+\r
+ @return First token of the token buffer.\r
+**/\r
+LIST_ENTRY *\r
+AllocateTokenBuffer (\r
+ VOID\r
+ )\r
+{\r
+ UINTN SpinLockSize;\r
+ UINT32 TokenCountPerChunk;\r
+ UINTN Index;\r
+ SPIN_LOCK *SpinLock;\r
+ UINT8 *SpinLockBuffer;\r
+ PROCEDURE_TOKEN *ProcTokens;\r
+\r
+ SpinLockSize = GetSpinLockProperties ();\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
+ ProcTokens = AllocatePool (sizeof (PROCEDURE_TOKEN) * TokenCountPerChunk);\r
+ ASSERT (ProcTokens != NULL);\r
+\r
+ for (Index = 0; Index < TokenCountPerChunk; Index++) {\r
+ SpinLock = (SPIN_LOCK *)(SpinLockBuffer + SpinLockSize * Index);\r
+ InitializeSpinLock (SpinLock);\r
+\r
+ ProcTokens[Index].Signature = PROCEDURE_TOKEN_SIGNATURE;\r
+ ProcTokens[Index].SpinLock = SpinLock;\r
+ ProcTokens[Index].RunningApCount = 0;\r
+\r
+ InsertTailList (&gSmmCpuPrivate->TokenList, &ProcTokens[Index].Link);\r
+ }\r
+\r
+ return &ProcTokens[0].Link;\r
+}\r
+\r
+/**\r
+ Get the free token.\r
+\r
+ If no free token, allocate new tokens then return the free one.\r
+\r
+ @param RunningApsCount The Running Aps count for this token.\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
+ //\r
+ // If FirstFreeToken meets the end of token list, enlarge the token list.\r
+ // Set FirstFreeToken to the first free token.\r
+ //\r
+ if (gSmmCpuPrivate->FirstFreeToken == &gSmmCpuPrivate->TokenList) {\r
+ gSmmCpuPrivate->FirstFreeToken = AllocateTokenBuffer ();\r
+ }\r
+ NewToken = PROCEDURE_TOKEN_FROM_LINK (gSmmCpuPrivate->FirstFreeToken);\r
+ gSmmCpuPrivate->FirstFreeToken = GetNextNode (&gSmmCpuPrivate->TokenList, gSmmCpuPrivate->FirstFreeToken);\r
+\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] BlockingMode Startup AP in blocking mode or not\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
**/\r
EFI_STATUS\r
InternalSmmStartupThisAp (\r
- IN EFI_AP_PROCEDURE Procedure,\r
- IN UINTN CpuIndex,\r
- IN OUT VOID *ProcArguments OPTIONAL,\r
- IN BOOLEAN BlockingMode\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
return EFI_INVALID_PARAMETER;\r
}\r
-\r
- if (BlockingMode) {\r
- AcquireSpinLock (mSmmMpSyncData->CpuData[CpuIndex].Busy);\r
- } else {\r
- if (!AcquireSpinLockOrFail (mSmmMpSyncData->CpuData[CpuIndex].Busy)) {\r
- DEBUG((DEBUG_ERROR, "mSmmMpSyncData->CpuData[%d].Busy\n", CpuIndex));\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
+ if (Token != &mSmmStartupThisApToken) {\r
+ //\r
+ // When Token points to mSmmStartupThisApToken, this routine is called\r
+ // from SmmStartupThisAp() in non-blocking mode (PcdCpuSmmBlockStartupThisAp == FALSE).\r
+ //\r
+ // In this case, caller wants to startup AP procedure in non-blocking\r
+ // mode and cannot get the completion status from the Token because there\r
+ // is no way to return the Token to caller from SmmStartupThisAp().\r
+ // Caller needs to use its implementation specific way to query the completion status.\r
+ //\r
+ // There is no need to allocate a token for such case so the 3 overheads\r
+ // can be avoided:\r
+ // 1. Call AllocateTokenBuffer() when there is no free token.\r
+ // 2. Get a free token from the token buffer.\r
+ // 3. Call ReleaseToken() in APHandler().\r
+ //\r
+ ProcToken = GetFreeToken (1);\r
+ mSmmMpSyncData->CpuData[CpuIndex].Token = ProcToken;\r
+ *Token = (MM_COMPLETION)ProcToken->SpinLock;\r
+ }\r
+ }\r
+ mSmmMpSyncData->CpuData[CpuIndex].Status = CpuStatus;\r
+ if (mSmmMpSyncData->CpuData[CpuIndex].Status != NULL) {\r
+ *mSmmMpSyncData->CpuData[CpuIndex].Status = EFI_NOT_READY;\r
+ }\r
+\r
ReleaseSemaphore (mSmmMpSyncData->CpuData[CpuIndex].Run);\r
\r
- if (BlockingMode) {\r
+ if (Token == NULL) {\r
AcquireSpinLock (mSmmMpSyncData->CpuData[CpuIndex].Busy);\r
ReleaseSpinLock (mSmmMpSyncData->CpuData[CpuIndex].Busy);\r
}\r
+\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
return EFI_SUCCESS;\r
}\r
\r
IN OUT VOID *ProcArguments OPTIONAL\r
)\r
{\r
- return InternalSmmStartupThisAp(Procedure, CpuIndex, ProcArguments, TRUE);\r
+ PROCEDURE_WRAPPER Wrapper;\r
+\r
+ Wrapper.Procedure = Procedure;\r
+ Wrapper.ProcedureArgument = ProcArguments;\r
+\r
+ //\r
+ // Use wrapper function to convert EFI_AP_PROCEDURE to EFI_AP_PROCEDURE2.\r
+ //\r
+ return InternalSmmStartupThisAp (ProcedureWrapper, CpuIndex, &Wrapper, NULL, 0, NULL);\r
}\r
\r
/**\r
IN OUT VOID *ProcArguments OPTIONAL\r
)\r
{\r
- return InternalSmmStartupThisAp(Procedure, CpuIndex, ProcArguments, FeaturePcdGet (PcdCpuSmmBlockStartupThisAp));\r
+ gSmmCpuPrivate->ApWrapperFunc[CpuIndex].Procedure = Procedure;\r
+ gSmmCpuPrivate->ApWrapperFunc[CpuIndex].ProcedureArgument = ProcArguments;\r
+\r
+ //\r
+ // Use wrapper function to convert EFI_AP_PROCEDURE to EFI_AP_PROCEDURE2.\r
+ //\r
+ return InternalSmmStartupThisAp (\r
+ ProcedureWrapper,\r
+ CpuIndex,\r
+ &gSmmCpuPrivate->ApWrapperFunc[CpuIndex],\r
+ FeaturePcdGet (PcdCpuSmmBlockStartupThisAp) ? NULL : &mSmmStartupThisApToken,\r
+ 0,\r
+ NULL\r
+ );\r
}\r
\r
/**\r
ASSERT(CpuIndex < mMaxNumberOfCpus);\r
\r
//\r
- // Save Cr2 because Page Fault exception in SMM may override its value\r
+ // Save Cr2 because Page Fault exception in SMM may override its value,\r
+ // when using on-demand paging for above 4G memory.\r
+ //\r
+ Cr2 = 0;\r
+ SaveCr2 (&Cr2);\r
+\r
+ //\r
+ // Call the user register Startup function first.\r
//\r
- Cr2 = AsmReadCr2 ();\r
+ if (mSmmMpSyncData->StartupProcedure != NULL) {\r
+ mSmmMpSyncData->StartupProcedure (mSmmMpSyncData->StartupProcArgs);\r
+ }\r
\r
//\r
// Perform CPU specific entry hooks\r
\r
Exit:\r
SmmCpuFeaturesRendezvousExit (CpuIndex);\r
+\r
//\r
// Restore Cr2\r
//\r
- AsmWriteCr2 (Cr2);\r
+ RestoreCr2 (Cr2);\r
+}\r
+\r
+/**\r
+ Allocate buffer for SpinLock and Wrapper function buffer.\r
+\r
+**/\r
+VOID\r
+InitializeDataForMmMp (\r
+ VOID\r
+ )\r
+{\r
+ gSmmCpuPrivate->ApWrapperFunc = AllocatePool (sizeof (PROCEDURE_WRAPPER) * gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus);\r
+ ASSERT (gSmmCpuPrivate->ApWrapperFunc != NULL);\r
+\r
+ InitializeListHead (&gSmmCpuPrivate->TokenList);\r
+\r
+ gSmmCpuPrivate->FirstFreeToken = AllocateTokenBuffer ();\r
}\r
\r
/**\r
GlobalSemaphoresSize = (sizeof (SMM_CPU_SEMAPHORE_GLOBAL) / sizeof (VOID *)) * SemaphoreSize;\r
CpuSemaphoresSize = (sizeof (SMM_CPU_SEMAPHORE_CPU) / sizeof (VOID *)) * ProcessorCount * SemaphoreSize;\r
TotalSize = GlobalSemaphoresSize + CpuSemaphoresSize;\r
- DEBUG((EFI_D_INFO, "One Semaphore Size = 0x%x\n", SemaphoreSize));\r
- DEBUG((EFI_D_INFO, "Total Semaphores Size = 0x%x\n", TotalSize));\r
+ DEBUG((DEBUG_INFO, "One Semaphore Size = 0x%x\n", SemaphoreSize));\r
+ DEBUG((DEBUG_INFO, "Total Semaphores Size = 0x%x\n", TotalSize));\r
Pages = EFI_SIZE_TO_PAGES (TotalSize);\r
SemaphoreBlock = AllocatePages (Pages);\r
ASSERT (SemaphoreBlock != NULL);\r
IN UINTN ShadowStackSize\r
)\r
{\r
- UINT32 Cr3;\r
- UINTN Index;\r
- UINT8 *GdtTssTables;\r
- UINTN GdtTableStepSize;\r
- CPUID_VERSION_INFO_EDX RegEdx;\r
+ UINT32 Cr3;\r
+ UINTN Index;\r
+ UINT8 *GdtTssTables;\r
+ UINTN GdtTableStepSize;\r
+ CPUID_VERSION_INFO_EDX RegEdx;\r
+ UINT32 MaxExtendedFunction;\r
+ CPUID_VIR_PHY_ADDRESS_SIZE_EAX VirPhyAddressSize;\r
\r
//\r
// Determine if this CPU supports machine check\r
// Initialize physical address mask\r
// NOTE: Physical memory above virtual address limit is not supported !!!\r
//\r
- AsmCpuid (0x80000008, (UINT32*)&Index, NULL, NULL, NULL);\r
- gPhyMask = LShiftU64 (1, (UINT8)Index) - 1;\r
- gPhyMask &= (1ull << 48) - EFI_PAGE_SIZE;\r
+ AsmCpuid (CPUID_EXTENDED_FUNCTION, &MaxExtendedFunction, NULL, NULL, NULL);\r
+ if (MaxExtendedFunction >= CPUID_VIR_PHY_ADDRESS_SIZE) {\r
+ AsmCpuid (CPUID_VIR_PHY_ADDRESS_SIZE, &VirPhyAddressSize.Uint32, NULL, NULL, NULL);\r
+ } else {\r
+ VirPhyAddressSize.Bits.PhysicalAddressBits = 36;\r
+ }\r
+ gPhyMask = LShiftU64 (1, VirPhyAddressSize.Bits.PhysicalAddressBits) - 1;\r
+ //\r
+ // Clear the low 12 bits\r
+ //\r
+ gPhyMask &= 0xfffffffffffff000ULL;\r
\r
//\r
// Create page tables\r
gSmmCpuPrivate->SmmCoreEntry = SmmEntryPoint;\r
return EFI_SUCCESS;\r
}\r
+\r
+/**\r
+\r
+ Register the SMM Foundation entry point.\r
+\r
+ @param[in] Procedure A pointer to the code stream to be run on the designated target AP\r
+ of the system. Type EFI_AP_PROCEDURE is defined below in Volume 2\r
+ with the related definitions of\r
+ EFI_MP_SERVICES_PROTOCOL.StartupAllAPs.\r
+ If caller may pass a value of NULL to deregister any existing\r
+ startup procedure.\r
+ @param[in,out] ProcedureArguments Allows the caller to pass a list of parameters to the code that is\r
+ run by the AP. It is an optional common mailbox between APs and\r
+ the caller to share information\r
+\r
+ @retval EFI_SUCCESS The Procedure has been set successfully.\r
+ @retval EFI_INVALID_PARAMETER The Procedure is NULL but ProcedureArguments not NULL.\r
+\r
+**/\r
+EFI_STATUS\r
+RegisterStartupProcedure (\r
+ IN EFI_AP_PROCEDURE Procedure,\r
+ IN OUT VOID *ProcedureArguments OPTIONAL\r
+ )\r
+{\r
+ if (Procedure == NULL && ProcedureArguments != NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ if (mSmmMpSyncData == NULL) {\r
+ return EFI_NOT_READY;\r
+ }\r
+\r
+ mSmmMpSyncData->StartupProcedure = Procedure;\r
+ mSmmMpSyncData->StartupProcArgs = ProcedureArguments;\r
+\r
+ return EFI_SUCCESS;\r
+}\r