/** @file\r
SMM MP service implementation\r
\r
-Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved.<BR>\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
+Copyright (c) 2009 - 2019, Intel Corporation. All rights reserved.<BR>\r
+Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR>\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
//\r
// Slots for all MTRR( FIXED MTRR + VARIABLE MTRR + MTRR_LIB_IA32_MTRR_DEF_TYPE)\r
//\r
-UINT64 gSmiMtrrs[MTRR_NUMBER_OF_FIXED_MTRR + 2 * MTRR_NUMBER_OF_VARIABLE_MTRR + 1];\r
+MTRR_SETTINGS gSmiMtrrs;\r
UINT64 gPhyMask;\r
SMM_DISPATCHER_MP_SYNC_DATA *mSmmMpSyncData = NULL;\r
UINTN mSmmMpSyncDataSize;\r
+SMM_CPU_SEMAPHORES mSmmCpuSemaphores;\r
+UINTN mSemaphoreSize;\r
+SPIN_LOCK *mPFLock = NULL;\r
+SMM_CPU_SYNC_MODE mCpuSmmSyncMode;\r
+BOOLEAN mMachineCheckSupported = FALSE;\r
\r
/**\r
Performs an atomic compare exchange operation to get semaphore.\r
\r
BspIndex = mSmmMpSyncData->BspIndex;\r
while (NumberOfAPs-- > 0) {\r
- WaitForSemaphore (&mSmmMpSyncData->CpuData[BspIndex].Run);\r
+ WaitForSemaphore (mSmmMpSyncData->CpuData[BspIndex].Run);\r
}\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
- ReleaseSemaphore (&mSmmMpSyncData->CpuData[Index].Run);\r
+ if (IsPresentAp (Index)) {\r
+ ReleaseSemaphore (mSmmMpSyncData->CpuData[Index].Run);\r
}\r
}\r
}\r
SMM_CPU_DATA_BLOCK *CpuData;\r
EFI_PROCESSOR_INFORMATION *ProcessorInfo;\r
\r
- ASSERT (mSmmMpSyncData->Counter <= mNumberOfCpus);\r
+ ASSERT (*mSmmMpSyncData->Counter <= mNumberOfCpus);\r
\r
- if (mSmmMpSyncData->Counter == mNumberOfCpus) {\r
+ if (*mSmmMpSyncData->Counter == mNumberOfCpus) {\r
return TRUE;\r
}\r
\r
CpuData = mSmmMpSyncData->CpuData;\r
ProcessorInfo = gSmmCpuPrivate->ProcessorInfo;\r
for (Index = mMaxNumberOfCpus; Index-- > 0;) {\r
- if (!CpuData[Index].Present && ProcessorInfo[Index].ProcessorId != INVALID_APIC_ID) {\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
return TRUE;\r
}\r
\r
+/**\r
+ Has OS enabled Lmce in the MSR_IA32_MCG_EXT_CTL\r
+\r
+ @retval TRUE Os enable lmce.\r
+ @retval FALSE Os not enable lmce.\r
+\r
+**/\r
+BOOLEAN\r
+IsLmceOsEnabled (\r
+ VOID\r
+ )\r
+{\r
+ MSR_IA32_MCG_CAP_REGISTER McgCap;\r
+ MSR_IA32_FEATURE_CONTROL_REGISTER FeatureCtrl;\r
+ MSR_IA32_MCG_EXT_CTL_REGISTER McgExtCtrl;\r
+\r
+ McgCap.Uint64 = AsmReadMsr64 (MSR_IA32_MCG_CAP);\r
+ if (McgCap.Bits.MCG_LMCE_P == 0) {\r
+ return FALSE;\r
+ }\r
+\r
+ FeatureCtrl.Uint64 = AsmReadMsr64 (MSR_IA32_FEATURE_CONTROL);\r
+ if (FeatureCtrl.Bits.LmceOn == 0) {\r
+ return FALSE;\r
+ }\r
+\r
+ McgExtCtrl.Uint64 = AsmReadMsr64 (MSR_IA32_MCG_EXT_CTL);\r
+ return (BOOLEAN) (McgExtCtrl.Bits.LMCE_EN == 1);\r
+}\r
+\r
+/**\r
+ Return if Local machine check exception signaled.\r
+\r
+ Indicates (when set) that a local machine check exception was generated. This indicates that the current machine-check event was\r
+ delivered to only the logical processor.\r
+\r
+ @retval TRUE LMCE was signaled.\r
+ @retval FALSE LMCE was not signaled.\r
+\r
+**/\r
+BOOLEAN\r
+IsLmceSignaled (\r
+ VOID\r
+ )\r
+{\r
+ MSR_IA32_MCG_STATUS_REGISTER McgStatus;\r
+\r
+ McgStatus.Uint64 = AsmReadMsr64 (MSR_IA32_MCG_STATUS);\r
+ return (BOOLEAN) (McgStatus.Bits.LMCE_S == 1);\r
+}\r
\r
/**\r
Given timeout constraint, wait for all APs to arrive, and insure when this function returns, no AP will execute normal mode code before\r
{\r
UINT64 Timer;\r
UINTN Index;\r
+ BOOLEAN LmceEn;\r
+ BOOLEAN LmceSignal;\r
\r
- ASSERT (mSmmMpSyncData->Counter <= mNumberOfCpus);\r
+ ASSERT (*mSmmMpSyncData->Counter <= mNumberOfCpus);\r
+\r
+ LmceEn = FALSE;\r
+ LmceSignal = FALSE;\r
+ if (mMachineCheckSupported) {\r
+ LmceEn = IsLmceOsEnabled ();\r
+ LmceSignal = IsLmceSignaled();\r
+ }\r
\r
//\r
// Platform implementor should choose a timeout value appropriately:\r
// Sync with APs 1st timeout\r
//\r
for (Timer = StartSyncTimer ();\r
- !IsSyncTimerTimeout (Timer) &&\r
+ !IsSyncTimerTimeout (Timer) && !(LmceEn && LmceSignal) &&\r
!AllCpusInSmmWithExceptions (ARRIVAL_EXCEPTION_BLOCKED | ARRIVAL_EXCEPTION_SMI_DISABLED );\r
) {\r
CpuPause ();\r
// - In relaxed flow, CheckApArrival() will check SMI disabling status before calling this function.\r
// In both cases, adding SMI-disabling checking code increases overhead.\r
//\r
- if (mSmmMpSyncData->Counter < mNumberOfCpus) {\r
+ if (*mSmmMpSyncData->Counter < mNumberOfCpus) {\r
//\r
// Send SMI IPIs to bring outside processors in\r
//\r
for (Index = mMaxNumberOfCpus; Index-- > 0;) {\r
- if (!mSmmMpSyncData->CpuData[Index].Present && gSmmCpuPrivate->ProcessorInfo[Index].ProcessorId != INVALID_APIC_ID) {\r
+ if (!(*(mSmmMpSyncData->CpuData[Index].Present)) && gSmmCpuPrivate->ProcessorInfo[Index].ProcessorId != INVALID_APIC_ID) {\r
SendSmiIpi ((UINT32)gSmmCpuPrivate->ProcessorInfo[Index].ProcessorId);\r
}\r
}\r
IN UINTN CpuIndex\r
)\r
{\r
- PROCESSOR_SMM_DESCRIPTOR *Psd;\r
- UINT64 *SmiMtrrs;\r
- MTRR_SETTINGS *BiosMtrr;\r
-\r
- Psd = (PROCESSOR_SMM_DESCRIPTOR*)(mCpuHotPlugData.SmBase[CpuIndex] + SMM_PSD_OFFSET);\r
- SmiMtrrs = (UINT64*)(UINTN)Psd->MtrrBaseMaskPtr;\r
-\r
SmmCpuFeaturesDisableSmrr ();\r
\r
//\r
// Replace all MTRRs registers\r
//\r
- BiosMtrr = (MTRR_SETTINGS*)SmiMtrrs;\r
- MtrrSetAllMtrrs(BiosMtrr);\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 = mMaxNumberOfCpus; Index-- > 0;) {\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
+ Check whether execute in single AP or all APs.\r
+\r
+ Compare two Tokens used by different APs to know whether in StartAllAps call.\r
+\r
+ Whether is an valid AP base on AP's Present flag.\r
+\r
+ @retval TRUE IN StartAllAps call.\r
+ @retval FALSE Not in StartAllAps call.\r
+\r
+**/\r
+BOOLEAN\r
+InStartAllApsCall (\r
+ VOID\r
+ )\r
+{\r
+ UINTN ApIndex;\r
+ UINTN ApIndex2;\r
+\r
+ for (ApIndex = mMaxNumberOfCpus; ApIndex-- > 0;) {\r
+ if (IsPresentAp (ApIndex) && (mSmmMpSyncData->CpuData[ApIndex].Token != NULL)) {\r
+ for (ApIndex2 = ApIndex; ApIndex2-- > 0;) {\r
+ if (IsPresentAp (ApIndex2) && (mSmmMpSyncData->CpuData[ApIndex2].Token != NULL)) {\r
+ return mSmmMpSyncData->CpuData[ApIndex2].Token == mSmmMpSyncData->CpuData[ApIndex].Token;\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ return FALSE;\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
+ UINTN Index;\r
+ BOOLEAN Released;\r
+\r
+ if (InStartAllApsCall ()) {\r
+ //\r
+ // In Start All APs mode, make sure all APs have finished task.\r
+ //\r
+ if (WaitForAllAPsNotBusy (FALSE)) {\r
+ //\r
+ // Clean the flags update in the function call.\r
+ //\r
+ Released = FALSE;\r
+ for (Index = mMaxNumberOfCpus; Index-- > 0;) {\r
+ //\r
+ // Only In SMM APs need to be clean up.\r
+ //\r
+ if (mSmmMpSyncData->CpuData[Index].Present && mSmmMpSyncData->CpuData[Index].Token != NULL) {\r
+ if (!Released) {\r
+ ReleaseSpinLock (mSmmMpSyncData->CpuData[Index].Token);\r
+ Released = TRUE;\r
+ }\r
+ mSmmMpSyncData->CpuData[Index].Token = NULL;\r
+ }\r
+ }\r
+ }\r
+ } else {\r
+ //\r
+ // In single AP mode.\r
+ //\r
+ if (mSmmMpSyncData->CpuData[CpuIndex].Token != NULL) {\r
+ ReleaseSpinLock (mSmmMpSyncData->CpuData[CpuIndex].Token);\r
+ mSmmMpSyncData->CpuData[CpuIndex].Token = NULL;\r
+ }\r
+ }\r
+}\r
+\r
+/**\r
+ Free the tokens in the maintained list.\r
+\r
+**/\r
+VOID\r
+FreeTokens (\r
+ VOID\r
+ )\r
+{\r
+ LIST_ENTRY *Link;\r
+ PROCEDURE_TOKEN *ProcToken;\r
+\r
+ while (!IsListEmpty (&gSmmCpuPrivate->TokenList)) {\r
+ Link = GetFirstNode (&gSmmCpuPrivate->TokenList);\r
+ ProcToken = PROCEDURE_TOKEN_FROM_LINK (Link);\r
+\r
+ RemoveEntryList (&ProcToken->Link);\r
+\r
+ FreePool ((VOID *)ProcToken->ProcedureToken);\r
+ FreePool (ProcToken);\r
+ }\r
}\r
\r
/**\r
//\r
// Flag BSP's presence\r
//\r
- mSmmMpSyncData->InsideSmm = TRUE;\r
+ *mSmmMpSyncData->InsideSmm = TRUE;\r
\r
//\r
// Initialize Debug Agent to start source level debug in BSP handler\r
//\r
// Mark this processor's presence\r
//\r
- mSmmMpSyncData->CpuData[CpuIndex].Present = TRUE;\r
+ *(mSmmMpSyncData->CpuData[CpuIndex].Present) = TRUE;\r
\r
//\r
// Clear platform top level SMI status bit before calling SMI handlers. If\r
//\r
// Lock the counter down and retrieve the number of APs\r
//\r
- mSmmMpSyncData->AllCpusInSync = TRUE;\r
- ApCount = LockdownSemaphore (&mSmmMpSyncData->Counter) - 1;\r
+ *mSmmMpSyncData->AllCpusInSync = TRUE;\r
+ ApCount = LockdownSemaphore (mSmmMpSyncData->Counter) - 1;\r
\r
//\r
// Wait for all APs to get ready for programming MTRRs\r
//\r
// The BUSY lock is initialized to Acquired state\r
//\r
- AcquireSpinLockOrFail (&mSmmMpSyncData->CpuData[CpuIndex].Busy);\r
+ AcquireSpinLock (mSmmMpSyncData->CpuData[CpuIndex].Busy);\r
\r
//\r
// Perform the pre tasks\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
// Lock the counter down and retrieve the number of APs\r
//\r
- mSmmMpSyncData->AllCpusInSync = TRUE;\r
- ApCount = LockdownSemaphore (&mSmmMpSyncData->Counter) - 1;\r
+ *mSmmMpSyncData->AllCpusInSync = TRUE;\r
+ ApCount = LockdownSemaphore (mSmmMpSyncData->Counter) - 1;\r
//\r
// Make sure all APs have their Present flag set\r
//\r
while (TRUE) {\r
PresentCount = 0;\r
for (Index = mMaxNumberOfCpus; Index-- > 0;) {\r
- if (mSmmMpSyncData->CpuData[Index].Present) {\r
+ if (*(mSmmMpSyncData->CpuData[Index].Present)) {\r
PresentCount ++;\r
}\r
}\r
//\r
// Notify all APs to exit\r
//\r
- mSmmMpSyncData->InsideSmm = FALSE;\r
+ *mSmmMpSyncData->InsideSmm = FALSE;\r
ReleaseAllAPs ();\r
\r
//\r
//\r
// Clear the Present flag of BSP\r
//\r
- mSmmMpSyncData->CpuData[CpuIndex].Present = FALSE;\r
+ *(mSmmMpSyncData->CpuData[CpuIndex].Present) = FALSE;\r
\r
//\r
// Gather APs to exit SMM synchronously. Note the Present flag is cleared by now but\r
//\r
WaitForAllAPs (ApCount);\r
\r
+ //\r
+ // Clean the tokens buffer.\r
+ //\r
+ FreeTokens ();\r
+\r
//\r
// Reset BspIndex to -1, meaning BSP has not been elected.\r
//\r
//\r
// Allow APs to check in from this point on\r
//\r
- mSmmMpSyncData->Counter = 0;\r
- mSmmMpSyncData->AllCpusInSync = FALSE;\r
+ *mSmmMpSyncData->Counter = 0;\r
+ *mSmmMpSyncData->AllCpusInSync = FALSE;\r
}\r
\r
/**\r
UINT64 Timer;\r
UINTN BspIndex;\r
MTRR_SETTINGS Mtrrs;\r
+ EFI_STATUS ProcedureStatus;\r
\r
//\r
// Timeout BSP\r
//\r
for (Timer = StartSyncTimer ();\r
!IsSyncTimerTimeout (Timer) &&\r
- !mSmmMpSyncData->InsideSmm;\r
+ !(*mSmmMpSyncData->InsideSmm);\r
) {\r
CpuPause ();\r
}\r
\r
- if (!mSmmMpSyncData->InsideSmm) {\r
+ if (!(*mSmmMpSyncData->InsideSmm)) {\r
//\r
// BSP timeout in the first round\r
//\r
//\r
for (Timer = StartSyncTimer ();\r
!IsSyncTimerTimeout (Timer) &&\r
- !mSmmMpSyncData->InsideSmm;\r
+ !(*mSmmMpSyncData->InsideSmm);\r
) {\r
CpuPause ();\r
}\r
\r
- if (!mSmmMpSyncData->InsideSmm) {\r
+ if (!(*mSmmMpSyncData->InsideSmm)) {\r
//\r
// Give up since BSP is unable to enter SMM\r
// and signal the completion of this AP\r
- WaitForSemaphore (&mSmmMpSyncData->Counter);\r
+ WaitForSemaphore (mSmmMpSyncData->Counter);\r
return;\r
}\r
} else {\r
//\r
// Don't know BSP index. Give up without sending IPI to BSP.\r
//\r
- WaitForSemaphore (&mSmmMpSyncData->Counter);\r
+ WaitForSemaphore (mSmmMpSyncData->Counter);\r
return;\r
}\r
}\r
//\r
// Mark this processor's presence\r
//\r
- mSmmMpSyncData->CpuData[CpuIndex].Present = TRUE;\r
+ *(mSmmMpSyncData->CpuData[CpuIndex].Present) = TRUE;\r
\r
if (SyncMode == SmmCpuSyncModeTradition || SmmCpuFeaturesNeedConfigureMtrrs()) {\r
//\r
// Notify BSP of arrival at this point\r
//\r
- ReleaseSemaphore (&mSmmMpSyncData->CpuData[BspIndex].Run);\r
+ ReleaseSemaphore (mSmmMpSyncData->CpuData[BspIndex].Run);\r
}\r
\r
if (SmmCpuFeaturesNeedConfigureMtrrs()) {\r
//\r
// Wait for the signal from BSP to backup MTRRs\r
//\r
- WaitForSemaphore (&mSmmMpSyncData->CpuData[CpuIndex].Run);\r
+ WaitForSemaphore (mSmmMpSyncData->CpuData[CpuIndex].Run);\r
\r
//\r
// Backup OS MTRRs\r
//\r
// Signal BSP the completion of this AP\r
//\r
- ReleaseSemaphore (&mSmmMpSyncData->CpuData[BspIndex].Run);\r
+ ReleaseSemaphore (mSmmMpSyncData->CpuData[BspIndex].Run);\r
\r
//\r
// Wait for BSP's signal to program MTRRs\r
//\r
- WaitForSemaphore (&mSmmMpSyncData->CpuData[CpuIndex].Run);\r
+ WaitForSemaphore (mSmmMpSyncData->CpuData[CpuIndex].Run);\r
\r
//\r
// Replace OS MTRRs with SMI MTRRs\r
//\r
// Signal BSP the completion of this AP\r
//\r
- ReleaseSemaphore (&mSmmMpSyncData->CpuData[BspIndex].Run);\r
+ ReleaseSemaphore (mSmmMpSyncData->CpuData[BspIndex].Run);\r
}\r
\r
while (TRUE) {\r
//\r
// Wait for something to happen\r
//\r
- WaitForSemaphore (&mSmmMpSyncData->CpuData[CpuIndex].Run);\r
+ WaitForSemaphore (mSmmMpSyncData->CpuData[CpuIndex].Run);\r
\r
//\r
// Check if BSP wants to exit SMM\r
//\r
- if (!mSmmMpSyncData->InsideSmm) {\r
+ if (!(*mSmmMpSyncData->InsideSmm)) {\r
break;\r
}\r
\r
// BUSY should be acquired by SmmStartupThisAp()\r
//\r
ASSERT (\r
- !AcquireSpinLockOrFail (&mSmmMpSyncData->CpuData[CpuIndex].Busy)\r
+ !AcquireSpinLockOrFail (mSmmMpSyncData->CpuData[CpuIndex].Busy)\r
);\r
\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
//\r
// Release BUSY\r
//\r
- ReleaseSpinLock (&mSmmMpSyncData->CpuData[CpuIndex].Busy);\r
+ ReleaseSpinLock (mSmmMpSyncData->CpuData[CpuIndex].Busy);\r
+\r
+ ReleaseToken (CpuIndex);\r
}\r
\r
if (SmmCpuFeaturesNeedConfigureMtrrs()) {\r
//\r
// Notify BSP the readiness of this AP to program MTRRs\r
//\r
- ReleaseSemaphore (&mSmmMpSyncData->CpuData[BspIndex].Run);\r
+ ReleaseSemaphore (mSmmMpSyncData->CpuData[BspIndex].Run);\r
\r
//\r
// Wait for the signal from BSP to program MTRRs\r
//\r
- WaitForSemaphore (&mSmmMpSyncData->CpuData[CpuIndex].Run);\r
+ WaitForSemaphore (mSmmMpSyncData->CpuData[CpuIndex].Run);\r
\r
//\r
// Restore OS MTRRs\r
//\r
// Notify BSP the readiness of this AP to Reset states/semaphore for this processor\r
//\r
- ReleaseSemaphore (&mSmmMpSyncData->CpuData[BspIndex].Run);\r
+ ReleaseSemaphore (mSmmMpSyncData->CpuData[BspIndex].Run);\r
\r
//\r
// Wait for the signal from BSP to Reset states/semaphore for this processor\r
//\r
- WaitForSemaphore (&mSmmMpSyncData->CpuData[CpuIndex].Run);\r
+ WaitForSemaphore (mSmmMpSyncData->CpuData[CpuIndex].Run);\r
\r
//\r
// Reset states/semaphore for this processor\r
//\r
- mSmmMpSyncData->CpuData[CpuIndex].Present = FALSE;\r
+ *(mSmmMpSyncData->CpuData[CpuIndex].Present) = FALSE;\r
\r
//\r
// Notify BSP the readiness of this AP to exit SMM\r
//\r
- ReleaseSemaphore (&mSmmMpSyncData->CpuData[BspIndex].Run);\r
+ ReleaseSemaphore (mSmmMpSyncData->CpuData[BspIndex].Run);\r
\r
}\r
\r
/**\r
Create 4G PageTable in SMRAM.\r
\r
- @param ExtraPages Additional page numbers besides for 4G memory\r
- @param Is32BitPageTable Whether the page table is 32-bit PAE\r
+ @param[in] Is32BitPageTable Whether the page table is 32-bit PAE\r
@return PageTable Address\r
\r
**/\r
UINT32\r
Gen4GPageTable (\r
- IN UINTN ExtraPages,\r
IN BOOLEAN Is32BitPageTable\r
)\r
{\r
//\r
// Allocate the page table\r
//\r
- PageTable = AllocatePageTableMemory (ExtraPages + 5 + PagesNeeded);\r
+ PageTable = AllocatePageTableMemory (5 + PagesNeeded);\r
ASSERT (PageTable != NULL);\r
\r
- PageTable = (VOID *)((UINTN)PageTable + EFI_PAGES_TO_SIZE (ExtraPages));\r
+ PageTable = (VOID *)((UINTN)PageTable);\r
Pte = (UINT64*)PageTable;\r
\r
//\r
// Set Page Directory Pointers\r
//\r
for (Index = 0; Index < 4; Index++) {\r
- Pte[Index] = (UINTN)PageTable + EFI_PAGE_SIZE * (Index + 1) + (Is32BitPageTable ? IA32_PAE_PDPTE_ATTRIBUTE_BITS : PAGE_ATTRIBUTE_BITS);\r
+ Pte[Index] = ((UINTN)PageTable + EFI_PAGE_SIZE * (Index + 1)) | mAddressEncMask |\r
+ (Is32BitPageTable ? IA32_PAE_PDPTE_ATTRIBUTE_BITS : PAGE_ATTRIBUTE_BITS);\r
}\r
Pte += EFI_PAGE_SIZE / sizeof (*Pte);\r
\r
// Fill in Page Directory Entries\r
//\r
for (Index = 0; Index < EFI_PAGE_SIZE * 4 / sizeof (*Pte); Index++) {\r
- Pte[Index] = (Index << 21) | IA32_PG_PS | PAGE_ATTRIBUTE_BITS;\r
+ Pte[Index] = (Index << 21) | mAddressEncMask | IA32_PG_PS | PAGE_ATTRIBUTE_BITS;\r
}\r
\r
+ Pdpte = (UINT64*)PageTable;\r
if (FeaturePcdGet (PcdCpuSmmStackGuard)) {\r
Pages = (UINTN)PageTable + EFI_PAGES_TO_SIZE (5);\r
GuardPage = mSmmStackArrayBase + EFI_PAGE_SIZE;\r
- Pdpte = (UINT64*)PageTable;\r
for (PageIndex = Low2MBoundary; PageIndex <= High2MBoundary; PageIndex += SIZE_2MB) {\r
- Pte = (UINT64*)(UINTN)(Pdpte[BitFieldRead32 ((UINT32)PageIndex, 30, 31)] & ~(EFI_PAGE_SIZE - 1));\r
- Pte[BitFieldRead32 ((UINT32)PageIndex, 21, 29)] = (UINT64)Pages | PAGE_ATTRIBUTE_BITS;\r
+ Pte = (UINT64*)(UINTN)(Pdpte[BitFieldRead32 ((UINT32)PageIndex, 30, 31)] & ~mAddressEncMask & ~(EFI_PAGE_SIZE - 1));\r
+ Pte[BitFieldRead32 ((UINT32)PageIndex, 21, 29)] = (UINT64)Pages | mAddressEncMask | PAGE_ATTRIBUTE_BITS;\r
//\r
// Fill in Page Table Entries\r
//\r
//\r
// Mark the guard page as non-present\r
//\r
- Pte[Index] = PageAddress;\r
+ Pte[Index] = PageAddress | mAddressEncMask;\r
GuardPage += mSmmStackSize;\r
if (GuardPage > mSmmStackArrayEnd) {\r
GuardPage = 0;\r
}\r
} else {\r
- Pte[Index] = PageAddress | PAGE_ATTRIBUTE_BITS;\r
+ Pte[Index] = PageAddress | mAddressEncMask | PAGE_ATTRIBUTE_BITS;\r
}\r
PageAddress+= EFI_PAGE_SIZE;\r
}\r
}\r
}\r
\r
+ if ((PcdGet8 (PcdNullPointerDetectionPropertyMask) & BIT1) != 0) {\r
+ Pte = (UINT64*)(UINTN)(Pdpte[0] & ~mAddressEncMask & ~(EFI_PAGE_SIZE - 1));\r
+ if ((Pte[0] & IA32_PG_PS) == 0) {\r
+ // 4K-page entries are already mapped. Just hide the first one anyway.\r
+ Pte = (UINT64*)(UINTN)(Pte[0] & ~mAddressEncMask & ~(EFI_PAGE_SIZE - 1));\r
+ Pte[0] &= ~(UINT64)IA32_PG_P; // Hide page 0\r
+ } else {\r
+ // Create 4K-page entries\r
+ Pages = (UINTN)AllocatePageTableMemory (1);\r
+ ASSERT (Pages != 0);\r
+\r
+ Pte[0] = (UINT64)(Pages | mAddressEncMask | PAGE_ATTRIBUTE_BITS);\r
+\r
+ Pte = (UINT64*)Pages;\r
+ PageAddress = 0;\r
+ Pte[0] = PageAddress | mAddressEncMask; // Hide page 0 but present left\r
+ for (Index = 1; Index < EFI_PAGE_SIZE / sizeof (*Pte); Index++) {\r
+ PageAddress += EFI_PAGE_SIZE;\r
+ Pte[Index] = PageAddress | mAddressEncMask | PAGE_ATTRIBUTE_BITS;\r
+ }\r
+ }\r
+ }\r
+\r
return (UINT32)(UINTN)PageTable;\r
}\r
\r
/**\r
- Set memory cache ability.\r
+ Checks whether the input token is the current used token.\r
\r
- @param PageTable PageTable Address\r
- @param Address Memory Address to change cache ability\r
- @param Cacheability Cache ability to set\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
-VOID\r
-SetCacheability (\r
- IN UINT64 *PageTable,\r
- IN UINTN Address,\r
- IN UINT8 Cacheability\r
+BOOLEAN\r
+IsTokenInUse (\r
+ IN SPIN_LOCK *Token\r
)\r
{\r
- UINTN PTIndex;\r
- VOID *NewPageTableAddress;\r
- UINT64 *NewPageTable;\r
- UINTN Index;\r
+ LIST_ENTRY *Link;\r
+ PROCEDURE_TOKEN *ProcToken;\r
+\r
+ if (Token == NULL) {\r
+ return FALSE;\r
+ }\r
\r
- ASSERT ((Address & EFI_PAGE_MASK) == 0);\r
+ Link = GetFirstNode (&gSmmCpuPrivate->TokenList);\r
+ while (!IsNull (&gSmmCpuPrivate->TokenList, Link)) {\r
+ ProcToken = PROCEDURE_TOKEN_FROM_LINK (Link);\r
\r
- if (sizeof (UINTN) == sizeof (UINT64)) {\r
- PTIndex = (UINTN)RShiftU64 (Address, 39) & 0x1ff;\r
- ASSERT (PageTable[PTIndex] & IA32_PG_P);\r
- PageTable = (UINT64*)(UINTN)(PageTable[PTIndex] & gPhyMask);\r
+ if (ProcToken->ProcedureToken == Token) {\r
+ return TRUE;\r
+ }\r
+\r
+ Link = GetNextNode (&gSmmCpuPrivate->TokenList, Link);\r
+ }\r
+\r
+ return FALSE;\r
+}\r
+\r
+/**\r
+ create token and save it to the maintain list.\r
+\r
+ @retval return the spin lock used as token.\r
+\r
+**/\r
+SPIN_LOCK *\r
+CreateToken (\r
+ VOID\r
+ )\r
+{\r
+ PROCEDURE_TOKEN *ProcToken;\r
+ SPIN_LOCK *CpuToken;\r
+ UINTN SpinLockSize;\r
+\r
+ SpinLockSize = GetSpinLockProperties ();\r
+ CpuToken = AllocatePool (SpinLockSize);\r
+ ASSERT (CpuToken != NULL);\r
+ InitializeSpinLock (CpuToken);\r
+ AcquireSpinLock (CpuToken);\r
+\r
+ ProcToken = AllocatePool (sizeof (PROCEDURE_TOKEN));\r
+ ASSERT (ProcToken != NULL);\r
+ ProcToken->Signature = PROCEDURE_TOKEN_SIGNATURE;\r
+ ProcToken->ProcedureToken = CpuToken;\r
+\r
+ InsertTailList (&gSmmCpuPrivate->TokenList, &ProcToken->Link);\r
+\r
+ return CpuToken;\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
- PTIndex = (UINTN)RShiftU64 (Address, 30) & 0x1ff;\r
- ASSERT (PageTable[PTIndex] & IA32_PG_P);\r
- PageTable = (UINT64*)(UINTN)(PageTable[PTIndex] & gPhyMask);\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
+ 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
+ if (Token == NULL) {\r
+ AcquireSpinLock (mSmmMpSyncData->CpuData[CpuIndex].Busy);\r
+ } else {\r
+ if (!AcquireSpinLockOrFail (mSmmMpSyncData->CpuData[CpuIndex].Busy)) {\r
+ DEBUG((DEBUG_ERROR, "Can't acquire mSmmMpSyncData->CpuData[%d].Busy\n", CpuIndex));\r
+ return EFI_NOT_READY;\r
+ }\r
+\r
+ *Token = (MM_COMPLETION) CreateToken ();\r
+ }\r
+\r
+ mSmmMpSyncData->CpuData[CpuIndex].Procedure = Procedure;\r
+ mSmmMpSyncData->CpuData[CpuIndex].Parameter = ProcArguments;\r
+ if (Token != NULL) {\r
+ mSmmMpSyncData->CpuData[CpuIndex].Token = (SPIN_LOCK *)(*Token);\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 (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
+\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 = mMaxNumberOfCpus; Index-- > 0;) {\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
+ *Token = (MM_COMPLETION) CreateToken ();\r
+ }\r
\r
//\r
- // A perfect implementation should check the original cacheability with the\r
- // one being set, and break a 2M page entry into pieces only when they\r
- // disagreed.\r
+ // Make sure all BUSY should be acquired.\r
//\r
- PTIndex = (UINTN)RShiftU64 (Address, 21) & 0x1ff;\r
- if ((PageTable[PTIndex] & IA32_PG_PS) != 0) {\r
- //\r
- // Allocate a page from SMRAM\r
- //\r
- NewPageTableAddress = AllocatePageTableMemory (1);\r
- ASSERT (NewPageTableAddress != NULL);\r
-\r
- NewPageTable = (UINT64 *)NewPageTableAddress;\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 = mMaxNumberOfCpus; Index-- > 0;) {\r
+ if (IsPresentAp (Index)) {\r
+ AcquireSpinLock (mSmmMpSyncData->CpuData[Index].Busy);\r
+ }\r
+ }\r
\r
- for (Index = 0; Index < 0x200; Index++) {\r
- NewPageTable[Index] = PageTable[PTIndex];\r
- if ((NewPageTable[Index] & IA32_PG_PAT_2M) != 0) {\r
- NewPageTable[Index] &= ~((UINT64)IA32_PG_PAT_2M);\r
- NewPageTable[Index] |= (UINT64)IA32_PG_PAT_4K;\r
+ for (Index = mMaxNumberOfCpus; Index-- > 0;) {\r
+ if (IsPresentAp (Index)) {\r
+ mSmmMpSyncData->CpuData[Index].Procedure = (EFI_AP_PROCEDURE2) Procedure;\r
+ mSmmMpSyncData->CpuData[Index].Parameter = ProcedureArguments;\r
+ if (Token != NULL) {\r
+ mSmmMpSyncData->CpuData[Index].Token = (SPIN_LOCK *)(*Token);\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
- NewPageTable[Index] |= (UINT64)(Index << EFI_PAGE_SHIFT);\r
}\r
+ }\r
+\r
+ ReleaseAllAPs ();\r
\r
- PageTable[PTIndex] = ((UINTN)NewPageTableAddress & gPhyMask) | PAGE_ATTRIBUTE_BITS;\r
+ if (Token == NULL) {\r
+ //\r
+ // Make sure all APs have completed their tasks.\r
+ //\r
+ WaitForAllAPsNotBusy (TRUE);\r
}\r
\r
- ASSERT (PageTable[PTIndex] & IA32_PG_P);\r
- PageTable = (UINT64*)(UINTN)(PageTable[PTIndex] & gPhyMask);\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
- PTIndex = (UINTN)RShiftU64 (Address, 12) & 0x1ff;\r
- ASSERT (PageTable[PTIndex] & IA32_PG_P);\r
- PageTable[PTIndex] &= ~((UINT64)((IA32_PG_PAT_4K | IA32_PG_CD | IA32_PG_WT)));\r
- PageTable[PTIndex] |= (UINT64)Cacheability;\r
+ return EFI_SUCCESS;\r
}\r
\r
+/**\r
+ Schedule a procedure to run on the specified CPU in blocking mode.\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
+\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
+EFIAPI\r
+SmmBlockingStartupThisAp (\r
+ IN EFI_AP_PROCEDURE Procedure,\r
+ IN UINTN CpuIndex,\r
+ IN OUT VOID *ProcArguments OPTIONAL\r
+ )\r
+{\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
Schedule a procedure to run on the specified CPU.\r
IN OUT VOID *ProcArguments OPTIONAL\r
)\r
{\r
- if (CpuIndex >= gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus ||\r
- CpuIndex == gSmmCpuPrivate->SmmCoreEntryContext.CurrentlyExecutingCpu ||\r
- !mSmmMpSyncData->CpuData[CpuIndex].Present ||\r
- gSmmCpuPrivate->Operation[CpuIndex] == SmmCpuRemove ||\r
- !AcquireSpinLockOrFail (&mSmmMpSyncData->CpuData[CpuIndex].Busy)) {\r
- return EFI_INVALID_PARAMETER;\r
- }\r
+ MM_COMPLETION Token;\r
\r
- mSmmMpSyncData->CpuData[CpuIndex].Procedure = Procedure;\r
- mSmmMpSyncData->CpuData[CpuIndex].Parameter = ProcArguments;\r
- ReleaseSemaphore (&mSmmMpSyncData->CpuData[CpuIndex].Run);\r
+ gSmmCpuPrivate->ApWrapperFunc[CpuIndex].Procedure = Procedure;\r
+ gSmmCpuPrivate->ApWrapperFunc[CpuIndex].ProcedureArgument = ProcArguments;\r
\r
- if (FeaturePcdGet (PcdCpuSmmBlockStartupThisAp)) {\r
- AcquireSpinLock (&mSmmMpSyncData->CpuData[CpuIndex].Busy);\r
- ReleaseSpinLock (&mSmmMpSyncData->CpuData[CpuIndex].Busy);\r
- }\r
- return EFI_SUCCESS;\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 : &Token,\r
+ 0,\r
+ NULL\r
+ );\r
}\r
\r
/**\r
)\r
{\r
SMRAM_SAVE_STATE_MAP *CpuSaveState;\r
- \r
+\r
if (FeaturePcdGet (PcdCpuSmmDebug)) {\r
+ ASSERT(CpuIndex < mMaxNumberOfCpus);\r
CpuSaveState = (SMRAM_SAVE_STATE_MAP *)gSmmCpuPrivate->CpuSaveState[CpuIndex];\r
if (mSmmSaveStateRegisterLma == EFI_SMM_SAVE_STATE_REGISTER_LMA_32BIT) {\r
AsmWriteDr6 (CpuSaveState->x86._DR6);\r
SMRAM_SAVE_STATE_MAP *CpuSaveState;\r
\r
if (FeaturePcdGet (PcdCpuSmmDebug)) {\r
+ ASSERT(CpuIndex < mMaxNumberOfCpus);\r
CpuSaveState = (SMRAM_SAVE_STATE_MAP *)gSmmCpuPrivate->CpuSaveState[CpuIndex];\r
if (mSmmSaveStateRegisterLma == EFI_SMM_SAVE_STATE_REGISTER_LMA_32BIT) {\r
CpuSaveState->x86._DR7 = (UINT32)AsmReadDr7 ();\r
IN UINTN CpuIndex\r
)\r
{\r
- EFI_STATUS Status;\r
- BOOLEAN ValidSmi;\r
- BOOLEAN IsBsp;\r
- BOOLEAN BspInProgress;\r
- UINTN Index;\r
- UINTN Cr2;\r
+ EFI_STATUS Status;\r
+ BOOLEAN ValidSmi;\r
+ BOOLEAN IsBsp;\r
+ BOOLEAN BspInProgress;\r
+ UINTN Index;\r
+ UINTN Cr2;\r
+\r
+ ASSERT(CpuIndex < mMaxNumberOfCpus);\r
+\r
+ //\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
- // Save Cr2 because Page Fault exception in SMM may override its value\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
// Determine if BSP has been already in progress. Note this must be checked after\r
// ValidSmi because BSP may clear a valid SMI source after checking in.\r
//\r
- BspInProgress = mSmmMpSyncData->InsideSmm;\r
+ BspInProgress = *mSmmMpSyncData->InsideSmm;\r
\r
if (!BspInProgress && !ValidSmi) {\r
//\r
//\r
// Signal presence of this processor\r
//\r
- if (ReleaseSemaphore (&mSmmMpSyncData->Counter) == 0) {\r
+ if (ReleaseSemaphore (mSmmMpSyncData->Counter) == 0) {\r
//\r
// BSP has already ended the synchronization, so QUIT!!!\r
//\r
//\r
// Wait for BSP's signal to finish SMI\r
//\r
- while (mSmmMpSyncData->AllCpusInSync) {\r
+ while (*mSmmMpSyncData->AllCpusInSync) {\r
CpuPause ();\r
}\r
goto Exit;\r
// E.g., with Relaxed AP flow, SmmStartupThisAp() may be called immediately\r
// after AP's present flag is detected.\r
//\r
- InitializeSpinLock (&mSmmMpSyncData->CpuData[CpuIndex].Busy);\r
- }\r
-\r
- //\r
- // Try to enable NX\r
- //\r
- if (mXdSupported) {\r
- ActivateXd ();\r
+ InitializeSpinLock (mSmmMpSyncData->CpuData[CpuIndex].Busy);\r
}\r
\r
if (FeaturePcdGet (PcdCpuSmmProfileEnable)) {\r
// BSP Handler is always called with a ValidSmi == TRUE\r
//\r
BSPHandler (CpuIndex, mSmmMpSyncData->EffectiveSyncMode);\r
-\r
} else {\r
APHandler (CpuIndex, ValidSmi, mSmmMpSyncData->EffectiveSyncMode);\r
}\r
}\r
\r
- ASSERT (mSmmMpSyncData->CpuData[CpuIndex].Run == 0);\r
+ ASSERT (*mSmmMpSyncData->CpuData[CpuIndex].Run == 0);\r
\r
//\r
// Wait for BSP's signal to exit SMI\r
//\r
- while (mSmmMpSyncData->AllCpusInSync) {\r
+ while (*mSmmMpSyncData->AllCpusInSync) {\r
CpuPause ();\r
}\r
}\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
\r
+/**\r
+ Allocate buffer for all semaphores and spin locks.\r
+\r
+**/\r
+VOID\r
+InitializeSmmCpuSemaphores (\r
+ VOID\r
+ )\r
+{\r
+ UINTN ProcessorCount;\r
+ UINTN TotalSize;\r
+ UINTN GlobalSemaphoresSize;\r
+ UINTN CpuSemaphoresSize;\r
+ UINTN SemaphoreSize;\r
+ UINTN Pages;\r
+ UINTN *SemaphoreBlock;\r
+ UINTN SemaphoreAddr;\r
+\r
+ SemaphoreSize = GetSpinLockProperties ();\r
+ ProcessorCount = gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus;\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
+ Pages = EFI_SIZE_TO_PAGES (TotalSize);\r
+ SemaphoreBlock = AllocatePages (Pages);\r
+ ASSERT (SemaphoreBlock != NULL);\r
+ ZeroMem (SemaphoreBlock, TotalSize);\r
+\r
+ SemaphoreAddr = (UINTN)SemaphoreBlock;\r
+ mSmmCpuSemaphores.SemaphoreGlobal.Counter = (UINT32 *)SemaphoreAddr;\r
+ SemaphoreAddr += SemaphoreSize;\r
+ mSmmCpuSemaphores.SemaphoreGlobal.InsideSmm = (BOOLEAN *)SemaphoreAddr;\r
+ SemaphoreAddr += SemaphoreSize;\r
+ mSmmCpuSemaphores.SemaphoreGlobal.AllCpusInSync = (BOOLEAN *)SemaphoreAddr;\r
+ SemaphoreAddr += SemaphoreSize;\r
+ mSmmCpuSemaphores.SemaphoreGlobal.PFLock = (SPIN_LOCK *)SemaphoreAddr;\r
+ SemaphoreAddr += SemaphoreSize;\r
+ mSmmCpuSemaphores.SemaphoreGlobal.CodeAccessCheckLock\r
+ = (SPIN_LOCK *)SemaphoreAddr;\r
+ SemaphoreAddr += SemaphoreSize;\r
+\r
+ SemaphoreAddr = (UINTN)SemaphoreBlock + GlobalSemaphoresSize;\r
+ mSmmCpuSemaphores.SemaphoreCpu.Busy = (SPIN_LOCK *)SemaphoreAddr;\r
+ SemaphoreAddr += ProcessorCount * SemaphoreSize;\r
+ mSmmCpuSemaphores.SemaphoreCpu.Run = (UINT32 *)SemaphoreAddr;\r
+ SemaphoreAddr += ProcessorCount * SemaphoreSize;\r
+ mSmmCpuSemaphores.SemaphoreCpu.Present = (BOOLEAN *)SemaphoreAddr;\r
+\r
+ mPFLock = mSmmCpuSemaphores.SemaphoreGlobal.PFLock;\r
+ mConfigSmmCodeAccessCheckLock = mSmmCpuSemaphores.SemaphoreGlobal.CodeAccessCheckLock;\r
+\r
+ mSemaphoreSize = SemaphoreSize;\r
+}\r
\r
/**\r
Initialize un-cacheable data.\r
VOID\r
)\r
{\r
+ UINTN CpuIndex;\r
+\r
if (mSmmMpSyncData != NULL) {\r
+ //\r
+ // mSmmMpSyncDataSize includes one structure of SMM_DISPATCHER_MP_SYNC_DATA, one\r
+ // CpuData array of SMM_CPU_DATA_BLOCK and one CandidateBsp array of BOOLEAN.\r
+ //\r
ZeroMem (mSmmMpSyncData, mSmmMpSyncDataSize);\r
mSmmMpSyncData->CpuData = (SMM_CPU_DATA_BLOCK *)((UINT8 *)mSmmMpSyncData + sizeof (SMM_DISPATCHER_MP_SYNC_DATA));\r
mSmmMpSyncData->CandidateBsp = (BOOLEAN *)(mSmmMpSyncData->CpuData + gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus);\r
//\r
mSmmMpSyncData->BspIndex = (UINT32)-1;\r
}\r
- mSmmMpSyncData->EffectiveSyncMode = (SMM_CPU_SYNC_MODE) PcdGet8 (PcdCpuSmmSyncMode);\r
+ mSmmMpSyncData->EffectiveSyncMode = mCpuSmmSyncMode;\r
+\r
+ mSmmMpSyncData->Counter = mSmmCpuSemaphores.SemaphoreGlobal.Counter;\r
+ mSmmMpSyncData->InsideSmm = mSmmCpuSemaphores.SemaphoreGlobal.InsideSmm;\r
+ mSmmMpSyncData->AllCpusInSync = mSmmCpuSemaphores.SemaphoreGlobal.AllCpusInSync;\r
+ ASSERT (mSmmMpSyncData->Counter != NULL && mSmmMpSyncData->InsideSmm != NULL &&\r
+ mSmmMpSyncData->AllCpusInSync != NULL);\r
+ *mSmmMpSyncData->Counter = 0;\r
+ *mSmmMpSyncData->InsideSmm = FALSE;\r
+ *mSmmMpSyncData->AllCpusInSync = FALSE;\r
+\r
+ for (CpuIndex = 0; CpuIndex < gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus; CpuIndex ++) {\r
+ mSmmMpSyncData->CpuData[CpuIndex].Busy =\r
+ (SPIN_LOCK *)((UINTN)mSmmCpuSemaphores.SemaphoreCpu.Busy + mSemaphoreSize * CpuIndex);\r
+ mSmmMpSyncData->CpuData[CpuIndex].Run =\r
+ (UINT32 *)((UINTN)mSmmCpuSemaphores.SemaphoreCpu.Run + mSemaphoreSize * CpuIndex);\r
+ mSmmMpSyncData->CpuData[CpuIndex].Present =\r
+ (BOOLEAN *)((UINTN)mSmmCpuSemaphores.SemaphoreCpu.Present + mSemaphoreSize * CpuIndex);\r
+ *(mSmmMpSyncData->CpuData[CpuIndex].Busy) = 0;\r
+ *(mSmmMpSyncData->CpuData[CpuIndex].Run) = 0;\r
+ *(mSmmMpSyncData->CpuData[CpuIndex].Present) = FALSE;\r
+ }\r
}\r
}\r
\r
/**\r
Initialize global data for MP synchronization.\r
\r
- @param Stacks Base address of SMI stack buffer for all processors.\r
- @param StackSize Stack size for each processor in SMM.\r
+ @param Stacks Base address of SMI stack buffer for all processors.\r
+ @param StackSize Stack size for each processor in SMM.\r
+ @param ShadowStackSize Shadow Stack size for each processor in SMM.\r
\r
**/\r
UINT32\r
InitializeMpServiceData (\r
IN VOID *Stacks,\r
- IN UINTN StackSize\r
+ IN UINTN StackSize,\r
+ IN UINTN ShadowStackSize\r
)\r
{\r
UINT32 Cr3;\r
UINTN Index;\r
- MTRR_SETTINGS *Mtrr;\r
- PROCESSOR_SMM_DESCRIPTOR *Psd;\r
UINT8 *GdtTssTables;\r
UINTN GdtTableStepSize;\r
+ CPUID_VERSION_INFO_EDX RegEdx;\r
+\r
+ //\r
+ // Determine if this CPU supports machine check\r
+ //\r
+ AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, NULL, &RegEdx.Uint32);\r
+ mMachineCheckSupported = (BOOLEAN)(RegEdx.Bits.MCA == 1);\r
+\r
+ //\r
+ // Allocate memory for all locks and semaphores\r
+ //\r
+ InitializeSmmCpuSemaphores ();\r
+\r
+ //\r
+ // Initialize mSmmMpSyncData\r
+ //\r
+ mSmmMpSyncDataSize = sizeof (SMM_DISPATCHER_MP_SYNC_DATA) +\r
+ (sizeof (SMM_CPU_DATA_BLOCK) + sizeof (BOOLEAN)) * gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus;\r
+ mSmmMpSyncData = (SMM_DISPATCHER_MP_SYNC_DATA*) AllocatePages (EFI_SIZE_TO_PAGES (mSmmMpSyncDataSize));\r
+ ASSERT (mSmmMpSyncData != NULL);\r
+ mCpuSmmSyncMode = (SMM_CPU_SYNC_MODE)PcdGet8 (PcdCpuSmmSyncMode);\r
+ InitializeMpSyncData ();\r
\r
//\r
// Initialize physical address mask\r
GdtTssTables = InitGdt (Cr3, &GdtTableStepSize);\r
\r
//\r
- // Initialize PROCESSOR_SMM_DESCRIPTOR for each CPU\r
+ // Install SMI handler for each CPU\r
//\r
for (Index = 0; Index < mMaxNumberOfCpus; Index++) {\r
- Psd = (PROCESSOR_SMM_DESCRIPTOR *)(VOID *)(UINTN)(mCpuHotPlugData.SmBase[Index] + SMM_PSD_OFFSET);\r
- CopyMem (Psd, &gcPsd, sizeof (gcPsd));\r
- Psd->SmmGdtPtr = (UINT64)(UINTN)(GdtTssTables + GdtTableStepSize * Index);\r
- Psd->SmmGdtSize = gcSmiGdtr.Limit + 1;\r
-\r
- //\r
- // Install SMI handler\r
- //\r
InstallSmiHandler (\r
Index,\r
(UINT32)mCpuHotPlugData.SmBase[Index],\r
- (VOID*)((UINTN)Stacks + (StackSize * Index)),\r
+ (VOID*)((UINTN)Stacks + (StackSize + ShadowStackSize) * Index),\r
StackSize,\r
- (UINTN)Psd->SmmGdtPtr,\r
- Psd->SmmGdtSize,\r
+ (UINTN)(GdtTssTables + GdtTableStepSize * Index),\r
+ gcSmiGdtr.Limit + 1,\r
gcSmiIdtr.Base,\r
gcSmiIdtr.Limit + 1,\r
Cr3\r
);\r
}\r
\r
- //\r
- // Initialize mSmmMpSyncData\r
- //\r
- mSmmMpSyncDataSize = sizeof (SMM_DISPATCHER_MP_SYNC_DATA) +\r
- (sizeof (SMM_CPU_DATA_BLOCK) + sizeof (BOOLEAN)) * gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus;\r
- mSmmMpSyncData = (SMM_DISPATCHER_MP_SYNC_DATA*) AllocatePages (EFI_SIZE_TO_PAGES (mSmmMpSyncDataSize));\r
- ASSERT (mSmmMpSyncData != NULL);\r
- InitializeMpSyncData ();\r
-\r
//\r
// Record current MTRR settings\r
//\r
- ZeroMem(gSmiMtrrs, sizeof (gSmiMtrrs));\r
- Mtrr = (MTRR_SETTINGS*)gSmiMtrrs;\r
- MtrrGetAllMtrrs (Mtrr);\r
+ ZeroMem (&gSmiMtrrs, sizeof (gSmiMtrrs));\r
+ MtrrGetAllMtrrs (&gSmiMtrrs);\r
\r
return Cr3;\r
}\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] 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 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