/** SMI handler for AP. @param CpuIndex AP processor Index. @param ValidSmi Indicates that current SMI is a valid SMI or not. @param SyncMode SMM MP sync mode. **/ VOID APHandler ( IN UINTN CpuIndex, IN BOOLEAN ValidSmi, IN SMM_CPU_SYNC_MODE SyncMode ) { UINT64 Timer; UINTN BspIndex; MTRR_SETTINGS Mtrrs; // // Timeout BSP // for (Timer = StartSyncTimer (); !IsSyncTimerTimeout (Timer) && !mSmmMpSyncData->InsideSmm; ) { CpuPause (); } if (!mSmmMpSyncData->InsideSmm) { // // BSP timeout in the first round // if (mSmmMpSyncData->BspIndex != -1) { // // BSP Index is known // BspIndex = mSmmMpSyncData->BspIndex; ASSERT (CpuIndex != BspIndex); // // Send SMI IPI to bring BSP in // SendSmiIpi ((UINT32)gSmmCpuPrivate->ProcessorInfo[BspIndex].ProcessorId); // // Now clock BSP for the 2nd time // for (Timer = StartSyncTimer (); !IsSyncTimerTimeout (Timer) && !mSmmMpSyncData->InsideSmm; ) { CpuPause (); } if (!mSmmMpSyncData->InsideSmm) { // // Give up since BSP is unable to enter SMM // and signal the completion of this AP WaitForSemaphore (&mSmmMpSyncData->Counter); return; } } else { // // Don't know BSP index. Give up without sending IPI to BSP. // WaitForSemaphore (&mSmmMpSyncData->Counter); return; } } // // BSP is available // BspIndex = mSmmMpSyncData->BspIndex; ASSERT (CpuIndex != BspIndex); // // Mark this processor's presence // mSmmMpSyncData->CpuData[CpuIndex].Present = TRUE; if (SyncMode == SmmCpuSyncModeTradition || SmmCpuFeaturesNeedConfigureMtrrs()) { // // Notify BSP of arrival at this point // ReleaseSemaphore (&mSmmMpSyncData->CpuData[BspIndex].Run); } if (SmmCpuFeaturesNeedConfigureMtrrs()) { // // Wait for the signal from BSP to backup MTRRs // WaitForSemaphore (&mSmmMpSyncData->CpuData[CpuIndex].Run); // // Backup OS MTRRs // MtrrGetAllMtrrs(&Mtrrs); // // Signal BSP the completion of this AP // ReleaseSemaphore (&mSmmMpSyncData->CpuData[BspIndex].Run); // // Wait for BSP's signal to program MTRRs // WaitForSemaphore (&mSmmMpSyncData->CpuData[CpuIndex].Run); // // Replace OS MTRRs with SMI MTRRs // ReplaceOSMtrrs (CpuIndex); // // Signal BSP the completion of this AP // ReleaseSemaphore (&mSmmMpSyncData->CpuData[BspIndex].Run); } while (TRUE) { // // Wait for something to happen // WaitForSemaphore (&mSmmMpSyncData->CpuData[CpuIndex].Run); // // Check if BSP wants to exit SMM // if (!mSmmMpSyncData->InsideSmm) { break; } // // BUSY should be acquired by SmmStartupThisAp() // ASSERT ( !AcquireSpinLockOrFail (&mSmmMpSyncData->CpuData[CpuIndex].Busy) ); // // Invoke the scheduled procedure // (*mSmmMpSyncData->CpuData[CpuIndex].Procedure) ( (VOID*)mSmmMpSyncData->CpuData[CpuIndex].Parameter ); // // Release BUSY // ReleaseSpinLock (&mSmmMpSyncData->CpuData[CpuIndex].Busy); } if (SmmCpuFeaturesNeedConfigureMtrrs()) { // // Notify BSP the readiness of this AP to program MTRRs // ReleaseSemaphore (&mSmmMpSyncData->CpuData[BspIndex].Run); // // Wait for the signal from BSP to program MTRRs // WaitForSemaphore (&mSmmMpSyncData->CpuData[CpuIndex].Run); // // Restore OS MTRRs // SmmCpuFeaturesReenableSmrr (); MtrrSetAllMtrrs(&Mtrrs); } // // Notify BSP the readiness of this AP to Reset states/semaphore for this processor // ReleaseSemaphore (&mSmmMpSyncData->CpuData[BspIndex].Run); // // Wait for the signal from BSP to Reset states/semaphore for this processor // WaitForSemaphore (&mSmmMpSyncData->CpuData[CpuIndex].Run); // // Reset states/semaphore for this processor // mSmmMpSyncData->CpuData[CpuIndex].Present = FALSE; // // Notify BSP the readiness of this AP to exit SMM // ReleaseSemaphore (&mSmmMpSyncData->CpuData[BspIndex].Run); }
/** Relocate SmmBases for each processor. Execute on first boot and all S3 resumes **/ VOID EFIAPI SmmRelocateBases ( VOID ) { UINT8 BakBuf[BACK_BUF_SIZE]; SMRAM_SAVE_STATE_MAP BakBuf2; SMRAM_SAVE_STATE_MAP *CpuStatePtr; UINT8 *U8Ptr; UINT32 ApicId; UINTN Index; UINTN BspIndex; // // Make sure the reserved size is large enough for procedure SmmInitTemplate. // ASSERT (sizeof (BakBuf) >= gcSmmInitSize); // // Patch ASM code template with current CR0, CR3, and CR4 values // gSmmCr0 = (UINT32)AsmReadCr0 (); gSmmCr3 = (UINT32)AsmReadCr3 (); gSmmCr4 = (UINT32)AsmReadCr4 (); // // Patch GDTR for SMM base relocation // gcSmiInitGdtr.Base = gcSmiGdtr.Base; gcSmiInitGdtr.Limit = gcSmiGdtr.Limit; U8Ptr = (UINT8*)(UINTN)(SMM_DEFAULT_SMBASE + SMM_HANDLER_OFFSET); CpuStatePtr = (SMRAM_SAVE_STATE_MAP *)(UINTN)(SMM_DEFAULT_SMBASE + SMRAM_SAVE_STATE_MAP_OFFSET); // // Backup original contents at address 0x38000 // CopyMem (BakBuf, U8Ptr, sizeof (BakBuf)); CopyMem (&BakBuf2, CpuStatePtr, sizeof (BakBuf2)); // // Load image for relocation // CopyMem (U8Ptr, gcSmmInitTemplate, gcSmmInitSize); // // Retrieve the local APIC ID of current processor // ApicId = GetApicId (); // // Relocate SM bases for all APs // This is APs' 1st SMI - rebase will be done here, and APs' default SMI handler will be overridden by gcSmmInitTemplate // mIsBsp = FALSE; BspIndex = (UINTN)-1; for (Index = 0; Index < mNumberOfCpus; Index++) { mRebased[Index] = FALSE; if (ApicId != (UINT32)gSmmCpuPrivate->ProcessorInfo[Index].ProcessorId) { SendSmiIpi ((UINT32)gSmmCpuPrivate->ProcessorInfo[Index].ProcessorId); // // Wait for this AP to finish its 1st SMI // while (!mRebased[Index]); } else { // // BSP will be Relocated later // BspIndex = Index; } } // // Relocate BSP's SMM base // ASSERT (BspIndex != (UINTN)-1); mIsBsp = TRUE; SendSmiIpi (ApicId); // // Wait for the BSP to finish its 1st SMI // while (!mRebased[BspIndex]); // // Restore contents at address 0x38000 // CopyMem (CpuStatePtr, &BakBuf2, sizeof (BakBuf2)); CopyMem (U8Ptr, BakBuf, sizeof (BakBuf)); }
/** Given timeout constraint, wait for all APs to arrive, and insure when this function returns, no AP will execute normal mode code before entering SMM, except SMI disabled APs. **/ VOID SmmWaitForApArrival ( VOID ) { UINT64 Timer; UINTN Index; ASSERT (mSmmMpSyncData->Counter <= mNumberOfCpus); // // Platform implementor should choose a timeout value appropriately: // - The timeout value should balance the SMM time constrains and the likelihood that delayed CPUs are excluded in the SMM run. Note // the SMI Handlers must ALWAYS take into account the cases that not all APs are available in an SMI run. // - The timeout value must, in the case of 2nd timeout, be at least long enough to give time for all APs to receive the SMI IPI // and either enter SMM or buffer the SMI, to insure there is no CPU running normal mode code when SMI handling starts. This will // be TRUE even if a blocked CPU is brought out of the blocked state by a normal mode CPU (before the normal mode CPU received the // SMI IPI), because with a buffered SMI, and CPU will enter SMM immediately after it is brought out of the blocked state. // - The timeout value must be longer than longest possible IO operation in the system // // // Sync with APs 1st timeout // for (Timer = StartSyncTimer (); !IsSyncTimerTimeout (Timer) && !AllCpusInSmmWithExceptions (ARRIVAL_EXCEPTION_BLOCKED | ARRIVAL_EXCEPTION_SMI_DISABLED ); ) { CpuPause (); } // // Not all APs have arrived, so we need 2nd round of timeout. IPIs should be sent to ALL none present APs, // because: // a) Delayed AP may have just come out of the delayed state. Blocked AP may have just been brought out of blocked state by some AP running // normal mode code. These APs need to be guaranteed to have an SMI pending to insure that once they are out of delayed / blocked state, they // enter SMI immediately without executing instructions in normal mode. Note traditional flow requires there are no APs doing normal mode // work while SMI handling is on-going. // b) As a consequence of SMI IPI sending, (spurious) SMI may occur after this SMM run. // c) ** NOTE **: Use SMI disabling feature VERY CAREFULLY (if at all) for traditional flow, because a processor in SMI-disabled state // will execute normal mode code, which breaks the traditional SMI handlers' assumption that no APs are doing normal // mode work while SMI handling is on-going. // d) We don't add code to check SMI disabling status to skip sending IPI to SMI disabled APs, because: // - In traditional flow, SMI disabling is discouraged. // - In relaxed flow, CheckApArrival() will check SMI disabling status before calling this function. // In both cases, adding SMI-disabling checking code increases overhead. // if (mSmmMpSyncData->Counter < mNumberOfCpus) { // // Send SMI IPIs to bring outside processors in // for (Index = mMaxNumberOfCpus; Index-- > 0;) { if (!mSmmMpSyncData->CpuData[Index].Present && gSmmCpuPrivate->ProcessorInfo[Index].ProcessorId != INVALID_APIC_ID) { SendSmiIpi ((UINT32)gSmmCpuPrivate->ProcessorInfo[Index].ProcessorId); } } // // Sync with APs 2nd timeout. // for (Timer = StartSyncTimer (); !IsSyncTimerTimeout (Timer) && !AllCpusInSmmWithExceptions (ARRIVAL_EXCEPTION_BLOCKED | ARRIVAL_EXCEPTION_SMI_DISABLED ); ) { CpuPause (); } } return; }