/** AP initialization before SMBASE relocation in the S3 boot path. **/ VOID EarlyMPRendezvousProcedure ( VOID ) { CPU_REGISTER_TABLE *RegisterTableList; UINT32 InitApicId; UINTN Index; LoadMtrrData (mAcpiCpuData.MtrrTable); // // Find processor number for this CPU. // RegisterTableList = (CPU_REGISTER_TABLE *) (UINTN) mAcpiCpuData.PreSmmInitRegisterTable; InitApicId = GetInitialApicId (); for (Index = 0; Index < mAcpiCpuData.NumberOfCpus; Index++) { if (RegisterTableList[Index].InitialApicId == InitApicId) { SetProcessorRegister (&RegisterTableList[Index]); break; } } // // Count down the number with lock mechanism. // InterlockedDecrement (&mNumberToFinish); }
/** The function is invoked after SMBASE relocation in S3 path to restors CPU status. The function is invoked after SMBASE relocation in S3 path. It restores configuration according to data saved by normal boot path for both BSP and APs. **/ VOID InitializeCpu ( VOID ) { CPU_REGISTER_TABLE *RegisterTableList; UINT32 InitApicId; UINTN Index; RegisterTableList = (CPU_REGISTER_TABLE *) (UINTN) mAcpiCpuData.RegisterTable; InitApicId = GetInitialApicId (); for (Index = 0; Index < mAcpiCpuData.NumberOfCpus; Index++) { if (RegisterTableList[Index].InitialApicId == InitApicId) { SetProcessorRegister (&RegisterTableList[Index]); break; } } mNumberToFinish = mAcpiCpuData.NumberOfCpus - 1; mExchangeInfo->ApFunction = (VOID *) (UINTN) MPRendezvousProcedure; // // Send INIT IPI - SIPI to all APs // SendInitSipiSipiAllExcludingSelf ((UINT32)mAcpiCpuData.StartupVector); while (mNumberToFinish > 0) { CpuPause (); } }
/** AP initialization after SMBASE relocation in the S3 boot path. **/ VOID MPRendezvousProcedure ( VOID ) { CPU_REGISTER_TABLE *RegisterTableList; UINT32 InitApicId; UINTN Index; ProgramVirtualWireMode (); DisableLvtInterrupts (); RegisterTableList = (CPU_REGISTER_TABLE *) (UINTN) mAcpiCpuData.RegisterTable; InitApicId = GetInitialApicId (); for (Index = 0; Index < mAcpiCpuData.NumberOfCpus; Index++) { if (RegisterTableList[Index].InitialApicId == InitApicId) { SetProcessorRegister (&RegisterTableList[Index]); break; } } // // Count down the number with lock mechanism. // InterlockedDecrement (&mNumberToFinish); }
/** The function is invoked after SMBASE relocation in S3 path to restores CPU status. The function is invoked after SMBASE relocation in S3 path. It restores configuration according to data saved by normal boot path for both BSP and APs. **/ VOID InitializeCpu ( VOID ) { CPU_REGISTER_TABLE *RegisterTableList; UINT32 InitApicId; UINTN Index; RegisterTableList = (CPU_REGISTER_TABLE *) (UINTN) mAcpiCpuData.RegisterTable; InitApicId = GetInitialApicId (); for (Index = 0; Index < mAcpiCpuData.NumberOfCpus; Index++) { if (RegisterTableList[Index].InitialApicId == InitApicId) { SetProcessorRegister (&RegisterTableList[Index]); break; } } mNumberToFinish = mAcpiCpuData.NumberOfCpus - 1; // // StackStart was updated when APs were waken up in EarlyInitializeCpu. // Re-initialize StackAddress to original beginning address. // mExchangeInfo->StackStart = (VOID *) (UINTN) mAcpiCpuData.StackAddress; mExchangeInfo->ApFunction = (VOID *) (UINTN) MPRendezvousProcedure; // // Send INIT IPI - SIPI to all APs // SendInitSipiSipiAllExcludingSelf ((UINT32)mAcpiCpuData.StartupVector); while (mNumberToFinish > 0) { CpuPause (); } }
/** This function will be called from AP reset code if BSP uses WakeUpAP. @param ExchangeInfo Pointer to the MP exchange info buffer @param NumApsExecuting Number of curret executing AP **/ VOID EFIAPI ApCFunction ( IN MP_CPU_EXCHANGE_INFO *ExchangeInfo, IN UINTN NumApsExecuting ) { PEI_CPU_MP_DATA *PeiCpuMpData; UINTN ProcessorNumber; EFI_AP_PROCEDURE Procedure; UINTN BistData; PeiCpuMpData = ExchangeInfo->PeiCpuMpData; if (PeiCpuMpData->InitFlag) { // // This is first time AP wakeup, get BIST information from AP stack // BistData = *(UINTN *) (PeiCpuMpData->Buffer + NumApsExecuting * PeiCpuMpData->CpuApStackSize - sizeof (UINTN)); PeiCpuMpData->CpuData[NumApsExecuting].Health.Uint32 = (UINT32) BistData; PeiCpuMpData->CpuData[NumApsExecuting].ApicId = GetInitialApicId (); if (PeiCpuMpData->CpuData[NumApsExecuting].ApicId >= 0xFF) { // // Set x2APIC mode if there are any logical processor reporting // an APIC ID of 255 or greater. // AcquireSpinLock(&PeiCpuMpData->MpLock); PeiCpuMpData->X2ApicEnable = TRUE; ReleaseSpinLock(&PeiCpuMpData->MpLock); } // // Sync BSP's Mtrr table to all wakeup APs and load microcode on APs. // MtrrSetAllMtrrs (&PeiCpuMpData->MtrrTable); MicrocodeDetect (); } else { // // Execute AP function if AP is not disabled // GetProcessorNumber (PeiCpuMpData, &ProcessorNumber); if ((PeiCpuMpData->CpuData[ProcessorNumber].State != CpuStateDisabled) && (PeiCpuMpData->ApFunction != 0)) { PeiCpuMpData->CpuData[ProcessorNumber].State = CpuStateBusy; Procedure = (EFI_AP_PROCEDURE)(UINTN)PeiCpuMpData->ApFunction; Procedure ((VOID *)(UINTN)PeiCpuMpData->ApFunctionArgument); PeiCpuMpData->CpuData[ProcessorNumber].State = CpuStateIdle; } } // // AP finished executing C code // InterlockedIncrement ((UINT32 *)&PeiCpuMpData->FinishedCount); AsmCliHltLoop (); }
/** Sort the APIC ID of all processors. This function sorts the APIC ID of all processors so that processor number is assigned in the ascending order of APIC ID which eases MP debugging. @param PeiCpuMpData Pointer to PEI CPU MP Data **/ VOID SortApicId ( IN PEI_CPU_MP_DATA *PeiCpuMpData ) { UINTN Index1; UINTN Index2; UINTN Index3; UINT32 ApicId; PEI_CPU_DATA CpuData; UINT32 ApCount; ApCount = PeiCpuMpData->CpuCount - 1; if (ApCount != 0) { for (Index1 = 0; Index1 < ApCount; Index1++) { Index3 = Index1; // // Sort key is the hardware default APIC ID // ApicId = PeiCpuMpData->CpuData[Index1].ApicId; for (Index2 = Index1 + 1; Index2 <= ApCount; Index2++) { if (ApicId > PeiCpuMpData->CpuData[Index2].ApicId) { Index3 = Index2; ApicId = PeiCpuMpData->CpuData[Index2].ApicId; } } if (Index3 != Index1) { CopyMem (&CpuData, &PeiCpuMpData->CpuData[Index3], sizeof (PEI_CPU_DATA)); CopyMem ( &PeiCpuMpData->CpuData[Index3], &PeiCpuMpData->CpuData[Index1], sizeof (PEI_CPU_DATA) ); CopyMem (&PeiCpuMpData->CpuData[Index1], &CpuData, sizeof (PEI_CPU_DATA)); } } // // Get the processor number for the BSP // ApicId = GetInitialApicId (); for (Index1 = 0; Index1 < PeiCpuMpData->CpuCount; Index1++) { if (PeiCpuMpData->CpuData[Index1].ApicId == ApicId) { PeiCpuMpData->BspNumber = (UINT32) Index1; break; } } } }
/** Sort the APIC ID of all processors. This function sorts the APIC ID of all processors so that processor number is assigned in the ascending order of APIC ID which eases MP debugging. @param PeiCpuMpData Pointer to PEI CPU MP Data **/ VOID SortApicId ( IN PEI_CPU_MP_DATA *PeiCpuMpData ) { UINTN Index1; UINTN Index2; UINTN Index3; UINT32 ApicId; EFI_HEALTH_FLAGS Health; UINT32 ApCount; ApCount = PeiCpuMpData->CpuCount - 1; if (ApCount != 0) { for (Index1 = 0; Index1 < ApCount; Index1++) { Index3 = Index1; // // Sort key is the hardware default APIC ID // ApicId = PeiCpuMpData->CpuData[Index1].ApicId; for (Index2 = Index1 + 1; Index2 <= ApCount; Index2++) { if (ApicId > PeiCpuMpData->CpuData[Index2].ApicId) { Index3 = Index2; ApicId = PeiCpuMpData->CpuData[Index2].ApicId; } } if (Index3 != Index1) { PeiCpuMpData->CpuData[Index3].ApicId = PeiCpuMpData->CpuData[Index1].ApicId; PeiCpuMpData->CpuData[Index1].ApicId = ApicId; Health = PeiCpuMpData->CpuData[Index3].Health; PeiCpuMpData->CpuData[Index3].Health = PeiCpuMpData->CpuData[Index1].Health; PeiCpuMpData->CpuData[Index1].Health = Health; } } // // Get the processor number for the BSP // ApicId = GetInitialApicId (); for (Index1 = 0; Index1 < PeiCpuMpData->CpuCount; Index1++) { if (PeiCpuMpData->CpuData[Index1].ApicId == ApicId) { PeiCpuMpData->BspNumber = (UINT32) Index1; break; } } } }
/** The function is invoked before SMBASE relocation in S3 path to restors CPU status. The function is invoked before SMBASE relocation in S3 path. It does first time microcode load and restores MTRRs for both BSP and APs. **/ VOID EarlyInitializeCpu ( VOID ) { CPU_REGISTER_TABLE *RegisterTableList; UINT32 InitApicId; UINTN Index; LoadMtrrData (mAcpiCpuData.MtrrTable); // // Find processor number for this CPU. // RegisterTableList = (CPU_REGISTER_TABLE *) (UINTN) mAcpiCpuData.PreSmmInitRegisterTable; InitApicId = GetInitialApicId (); for (Index = 0; Index < mAcpiCpuData.NumberOfCpus; Index++) { if (RegisterTableList[Index].InitialApicId == InitApicId) { SetProcessorRegister (&RegisterTableList[Index]); break; } } ProgramVirtualWireMode (); PrepareAPStartupVector (mAcpiCpuData.StartupVector); mNumberToFinish = mAcpiCpuData.NumberOfCpus - 1; mExchangeInfo->ApFunction = (VOID *) (UINTN) EarlyMPRendezvousProcedure; // // Send INIT IPI - SIPI to all APs // SendInitSipiSipiAllExcludingSelf ((UINT32)mAcpiCpuData.StartupVector); while (mNumberToFinish > 0) { CpuPause (); } }
/** This function will be called from AP reset code if BSP uses WakeUpAP. @param ExchangeInfo Pointer to the MP exchange info buffer @param NumApsExecuting Number of current executing AP **/ VOID EFIAPI ApCFunction ( IN MP_CPU_EXCHANGE_INFO *ExchangeInfo, IN UINTN NumApsExecuting ) { PEI_CPU_MP_DATA *PeiCpuMpData; UINTN ProcessorNumber; EFI_AP_PROCEDURE Procedure; UINTN BistData; volatile UINT32 *ApStartupSignalBuffer; PeiCpuMpData = ExchangeInfo->PeiCpuMpData; while (TRUE) { if (PeiCpuMpData->InitFlag) { ProcessorNumber = NumApsExecuting; // // Sync BSP's Control registers to APs // RestoreVolatileRegisters (&PeiCpuMpData->CpuData[0].VolatileRegisters, FALSE); // // This is first time AP wakeup, get BIST information from AP stack // BistData = *(UINTN *) (PeiCpuMpData->Buffer + ProcessorNumber * PeiCpuMpData->CpuApStackSize - sizeof (UINTN)); PeiCpuMpData->CpuData[ProcessorNumber].Health.Uint32 = (UINT32) BistData; PeiCpuMpData->CpuData[ProcessorNumber].ApicId = GetInitialApicId (); if (PeiCpuMpData->CpuData[ProcessorNumber].ApicId >= 0xFF) { // // Set x2APIC mode if there are any logical processor reporting // an APIC ID of 255 or greater. // AcquireSpinLock(&PeiCpuMpData->MpLock); PeiCpuMpData->X2ApicEnable = TRUE; ReleaseSpinLock(&PeiCpuMpData->MpLock); } // // Sync BSP's Mtrr table to all wakeup APs and load microcode on APs. // MtrrSetAllMtrrs (&PeiCpuMpData->MtrrTable); MicrocodeDetect (); PeiCpuMpData->CpuData[ProcessorNumber].State = CpuStateIdle; } else { // // Execute AP function if AP is not disabled // GetProcessorNumber (PeiCpuMpData, &ProcessorNumber); if (PeiCpuMpData->ApLoopMode == ApInHltLoop) { // // Restore AP's volatile registers saved // RestoreVolatileRegisters (&PeiCpuMpData->CpuData[ProcessorNumber].VolatileRegisters, TRUE); } if ((PeiCpuMpData->CpuData[ProcessorNumber].State != CpuStateDisabled) && (PeiCpuMpData->ApFunction != 0)) { PeiCpuMpData->CpuData[ProcessorNumber].State = CpuStateBusy; Procedure = (EFI_AP_PROCEDURE)(UINTN)PeiCpuMpData->ApFunction; // // Invoke AP function here // Procedure ((VOID *)(UINTN)PeiCpuMpData->ApFunctionArgument); // // Re-get the processor number due to BSP/AP maybe exchange in AP function // GetProcessorNumber (PeiCpuMpData, &ProcessorNumber); PeiCpuMpData->CpuData[ProcessorNumber].State = CpuStateIdle; } } // // AP finished executing C code // InterlockedIncrement ((UINT32 *)&PeiCpuMpData->FinishedCount); // // Place AP is specified loop mode // if (PeiCpuMpData->ApLoopMode == ApInHltLoop) { // // Save AP volatile registers // SaveVolatileRegisters (&PeiCpuMpData->CpuData[ProcessorNumber].VolatileRegisters); // // Place AP in Hlt-loop // while (TRUE) { DisableInterrupts (); CpuSleep (); CpuPause (); } } ApStartupSignalBuffer = PeiCpuMpData->CpuData[ProcessorNumber].StartupApSignal; // // Clear AP start-up signal // *ApStartupSignalBuffer = 0; while (TRUE) { DisableInterrupts (); if (PeiCpuMpData->ApLoopMode == ApInMwaitLoop) { // // Place AP in Mwait-loop // AsmMonitor ((UINTN)ApStartupSignalBuffer, 0, 0); if (*ApStartupSignalBuffer != WAKEUP_AP_SIGNAL) { // // If AP start-up signal is not set, place AP into // the maximum C-state // AsmMwait (PeiCpuMpData->ApTargetCState << 4, 0); } } else if (PeiCpuMpData->ApLoopMode == ApInRunLoop) { // // Place AP in Run-loop // CpuPause (); } else { ASSERT (FALSE); } // // If AP start-up signal is written, AP is waken up // otherwise place AP in loop again // if (*ApStartupSignalBuffer == WAKEUP_AP_SIGNAL) { break; } } } }
/** Collects BIST data from PPI. This function collects BIST data from Sec Platform Information2 PPI or SEC Platform Information PPI. @param PeiServices Pointer to PEI Services Table @param PeiCpuMpData Pointer to PEI CPU MP Data **/ VOID CollectBistDataFromPpi ( IN CONST EFI_PEI_SERVICES **PeiServices, IN PEI_CPU_MP_DATA *PeiCpuMpData ) { EFI_STATUS Status; EFI_PEI_PPI_DESCRIPTOR *SecInformationDescriptor; EFI_SEC_PLATFORM_INFORMATION_RECORD2 *SecPlatformInformation2; EFI_SEC_PLATFORM_INFORMATION_RECORD *SecPlatformInformation; UINTN NumberOfData; EFI_SEC_PLATFORM_INFORMATION_CPU *CpuInstance; EFI_SEC_PLATFORM_INFORMATION_CPU BspCpuInstance; UINTN ProcessorNumber; UINTN CpuIndex; PEI_CPU_DATA *CpuData; SecPlatformInformation2 = NULL; SecPlatformInformation = NULL; NumberOfData = 0; CpuInstance = NULL; // // Get BIST information from Sec Platform Information2 Ppi firstly // Status = GetBistInfoFromPpi ( PeiServices, &gEfiSecPlatformInformation2PpiGuid, &SecInformationDescriptor, (VOID *) &SecPlatformInformation2 ); if (Status == EFI_SUCCESS) { // // Sec Platform Information2 PPI includes BSP/APs' BIST information // NumberOfData = SecPlatformInformation2->NumberOfCpus; CpuInstance = SecPlatformInformation2->CpuInstance; } else { // // Otherwise, get BIST information from Sec Platform Information Ppi // Status = GetBistInfoFromPpi ( PeiServices, &gEfiSecPlatformInformationPpiGuid, &SecInformationDescriptor, (VOID *) &SecPlatformInformation ); if (Status == EFI_SUCCESS) { NumberOfData = 1; // // SEC Platform Information only includes BSP's BIST information // and does not have BSP's APIC ID // BspCpuInstance.CpuLocation = GetInitialApicId (); BspCpuInstance.InfoRecord.IA32HealthFlags.Uint32 = SecPlatformInformation->IA32HealthFlags.Uint32; CpuInstance = &BspCpuInstance; } else { DEBUG ((EFI_D_INFO, "Does not find any stored CPU BIST information from PPI!\n")); } } for (ProcessorNumber = 0; ProcessorNumber < PeiCpuMpData->CpuCount; ProcessorNumber ++) { CpuData = &PeiCpuMpData->CpuData[ProcessorNumber]; for (CpuIndex = 0; CpuIndex < NumberOfData; CpuIndex ++) { ASSERT (CpuInstance != NULL); if (CpuData->ApicId == CpuInstance[CpuIndex].CpuLocation) { // // Update processor's BIST data if it is already stored before // CpuData->Health = CpuInstance[CpuIndex].InfoRecord.IA32HealthFlags; } } if (CpuData->Health.Uint32 == 0) { CpuData->CpuHealthy = TRUE; } else { CpuData->CpuHealthy = FALSE; // // Report Status Code that self test is failed // REPORT_STATUS_CODE ( EFI_ERROR_CODE | EFI_ERROR_MAJOR, (EFI_COMPUTING_UNIT_HOST_PROCESSOR | EFI_CU_HP_EC_SELF_TEST) ); } DEBUG ((EFI_D_INFO, " APICID - 0x%08x, BIST - 0x%08x\n", PeiCpuMpData->CpuData[ProcessorNumber].ApicId, PeiCpuMpData->CpuData[ProcessorNumber].Health.Uint32 )); } if (SecPlatformInformation2 != NULL && NumberOfData < PeiCpuMpData->CpuCount) { // // Reinstall SecPlatformInformation2 PPI to include new BIST inforamtion // Status = PeiServicesReInstallPpi ( SecInformationDescriptor, &mPeiSecPlatformInformation2Ppi ); ASSERT_EFI_ERROR (Status); } else { // // Install SecPlatformInformation2 PPI to include new BIST inforamtion // Status = PeiServicesInstallPpi (&mPeiSecPlatformInformation2Ppi); ASSERT_EFI_ERROR(Status); } }
/** Programs registers for the calling processor. This function programs registers for the calling processor. @param RegisterTables Pointer to register table of the running processor. @param RegisterTableCount Register table count. **/ VOID SetProcessorRegister ( IN CPU_REGISTER_TABLE *RegisterTables, IN UINTN RegisterTableCount ) { CPU_REGISTER_TABLE_ENTRY *RegisterTableEntry; UINTN Index; UINTN Value; SPIN_LOCK *MsrSpinLock; UINT32 InitApicId; CPU_REGISTER_TABLE *RegisterTable; InitApicId = GetInitialApicId (); RegisterTable = NULL; for (Index = 0; Index < RegisterTableCount; Index++) { if (RegisterTables[Index].InitialApicId == InitApicId) { RegisterTable = &RegisterTables[Index]; break; } } ASSERT (RegisterTable != NULL); // // Traverse Register Table of this logical processor // RegisterTableEntry = (CPU_REGISTER_TABLE_ENTRY *) (UINTN) RegisterTable->RegisterTableEntry; for (Index = 0; Index < RegisterTable->TableLength; Index++, RegisterTableEntry++) { // // Check the type of specified register // switch (RegisterTableEntry->RegisterType) { // // The specified register is Control Register // case ControlRegister: switch (RegisterTableEntry->Index) { case 0: Value = AsmReadCr0 (); Value = (UINTN) BitFieldWrite64 ( Value, RegisterTableEntry->ValidBitStart, RegisterTableEntry->ValidBitStart + RegisterTableEntry->ValidBitLength - 1, (UINTN) RegisterTableEntry->Value ); AsmWriteCr0 (Value); break; case 2: Value = AsmReadCr2 (); Value = (UINTN) BitFieldWrite64 ( Value, RegisterTableEntry->ValidBitStart, RegisterTableEntry->ValidBitStart + RegisterTableEntry->ValidBitLength - 1, (UINTN) RegisterTableEntry->Value ); AsmWriteCr2 (Value); break; case 3: Value = AsmReadCr3 (); Value = (UINTN) BitFieldWrite64 ( Value, RegisterTableEntry->ValidBitStart, RegisterTableEntry->ValidBitStart + RegisterTableEntry->ValidBitLength - 1, (UINTN) RegisterTableEntry->Value ); AsmWriteCr3 (Value); break; case 4: Value = AsmReadCr4 (); Value = (UINTN) BitFieldWrite64 ( Value, RegisterTableEntry->ValidBitStart, RegisterTableEntry->ValidBitStart + RegisterTableEntry->ValidBitLength - 1, (UINTN) RegisterTableEntry->Value ); AsmWriteCr4 (Value); break; default: break; } break; // // The specified register is Model Specific Register // case Msr: // // If this function is called to restore register setting after INIT signal, // there is no need to restore MSRs in register table. // if (RegisterTableEntry->ValidBitLength >= 64) { // // If length is not less than 64 bits, then directly write without reading // AsmWriteMsr64 ( RegisterTableEntry->Index, RegisterTableEntry->Value ); } else { // // Get lock to avoid Package/Core scope MSRs programming issue in parallel execution mode // to make sure MSR read/write operation is atomic. // MsrSpinLock = GetMsrSpinLockByIndex (RegisterTableEntry->Index); AcquireSpinLock (MsrSpinLock); // // Set the bit section according to bit start and length // AsmMsrBitFieldWrite64 ( RegisterTableEntry->Index, RegisterTableEntry->ValidBitStart, RegisterTableEntry->ValidBitStart + RegisterTableEntry->ValidBitLength - 1, RegisterTableEntry->Value ); ReleaseSpinLock (MsrSpinLock); } break; // // MemoryMapped operations // case MemoryMapped: AcquireSpinLock (mMemoryMappedLock); MmioBitFieldWrite32 ( (UINTN)(RegisterTableEntry->Index | LShiftU64 (RegisterTableEntry->HighIndex, 32)), RegisterTableEntry->ValidBitStart, RegisterTableEntry->ValidBitStart + RegisterTableEntry->ValidBitLength - 1, (UINT32)RegisterTableEntry->Value ); ReleaseSpinLock (mMemoryMappedLock); break; // // Enable or disable cache // case CacheControl: // // If value of the entry is 0, then disable cache. Otherwise, enable cache. // if (RegisterTableEntry->Value == 0) { AsmDisableCache (); } else { AsmEnableCache (); } break; default: break; } } }