X-Git-Url: https://git.proxmox.com/?p=mirror_edk2.git;a=blobdiff_plain;f=UefiCpuPkg%2FLibrary%2FMpInitLib%2FMpLib.c;h=15dbfa1e7d6c7f5e9d85d88e0030c38ea65f4a40;hp=f05db7c2a5dc7420b5e63f4a4db4b9e370fb8b86;hb=9f91cb01c43e394f5368c6e9d16b58cbebf9ad9b;hpb=e59f8f6b3670e37687778f4294d8f59fec0c5dcb diff --git a/UefiCpuPkg/Library/MpInitLib/MpLib.c b/UefiCpuPkg/Library/MpInitLib/MpLib.c index f05db7c2a5..15dbfa1e7d 100644 --- a/UefiCpuPkg/Library/MpInitLib/MpLib.c +++ b/UefiCpuPkg/Library/MpInitLib/MpLib.c @@ -14,6 +14,167 @@ #include "MpLib.h" +EFI_GUID mCpuInitMpLibHobGuid = CPU_INIT_MP_LIB_HOB_GUID; + +/** + The function will check if BSP Execute Disable is enabled. + DxeIpl may have enabled Execute Disable for BSP, + APs need to get the status and sync up the settings. + + @retval TRUE BSP Execute Disable is enabled. + @retval FALSE BSP Execute Disable is not enabled. +**/ +BOOLEAN +IsBspExecuteDisableEnabled ( + VOID + ) +{ + UINT32 Eax; + CPUID_EXTENDED_CPU_SIG_EDX Edx; + MSR_IA32_EFER_REGISTER EferMsr; + BOOLEAN Enabled; + + Enabled = FALSE; + AsmCpuid (CPUID_EXTENDED_FUNCTION, &Eax, NULL, NULL, NULL); + if (Eax >= CPUID_EXTENDED_CPU_SIG) { + AsmCpuid (CPUID_EXTENDED_CPU_SIG, NULL, NULL, NULL, &Edx.Uint32); + // + // CPUID 0x80000001 + // Bit 20: Execute Disable Bit available. + // + if (Edx.Bits.NX != 0) { + EferMsr.Uint64 = AsmReadMsr64 (MSR_IA32_EFER); + // + // MSR 0xC0000080 + // Bit 11: Execute Disable Bit enable. + // + if (EferMsr.Bits.NXE != 0) { + Enabled = TRUE; + } + } + } + + return Enabled; +} + +/** + Worker function for SwitchBSP(). + + Worker function for SwitchBSP(), assigned to the AP which is intended + to become BSP. + + @param[in] Buffer Pointer to CPU MP Data +**/ +VOID +EFIAPI +FutureBSPProc ( + IN VOID *Buffer + ) +{ + CPU_MP_DATA *DataInHob; + + DataInHob = (CPU_MP_DATA *) Buffer; + AsmExchangeRole (&DataInHob->APInfo, &DataInHob->BSPInfo); +} + +/** + Get the Application Processors state. + + @param[in] CpuData The pointer to CPU_AP_DATA of specified AP + + @return The AP status +**/ +CPU_STATE +GetApState ( + IN CPU_AP_DATA *CpuData + ) +{ + return CpuData->State; +} + +/** + Set the Application Processors state. + + @param[in] CpuData The pointer to CPU_AP_DATA of specified AP + @param[in] State The AP status +**/ +VOID +SetApState ( + IN CPU_AP_DATA *CpuData, + IN CPU_STATE State + ) +{ + AcquireSpinLock (&CpuData->ApLock); + CpuData->State = State; + ReleaseSpinLock (&CpuData->ApLock); +} + +/** + Save the volatile registers required to be restored following INIT IPI. + + @param[out] VolatileRegisters Returns buffer saved the volatile resisters +**/ +VOID +SaveVolatileRegisters ( + OUT CPU_VOLATILE_REGISTERS *VolatileRegisters + ) +{ + CPUID_VERSION_INFO_EDX VersionInfoEdx; + + VolatileRegisters->Cr0 = AsmReadCr0 (); + VolatileRegisters->Cr3 = AsmReadCr3 (); + VolatileRegisters->Cr4 = AsmReadCr4 (); + + AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, NULL, &VersionInfoEdx.Uint32); + if (VersionInfoEdx.Bits.DE != 0) { + // + // If processor supports Debugging Extensions feature + // by CPUID.[EAX=01H]:EDX.BIT2 + // + VolatileRegisters->Dr0 = AsmReadDr0 (); + VolatileRegisters->Dr1 = AsmReadDr1 (); + VolatileRegisters->Dr2 = AsmReadDr2 (); + VolatileRegisters->Dr3 = AsmReadDr3 (); + VolatileRegisters->Dr6 = AsmReadDr6 (); + VolatileRegisters->Dr7 = AsmReadDr7 (); + } +} + +/** + Restore the volatile registers following INIT IPI. + + @param[in] VolatileRegisters Pointer to volatile resisters + @param[in] IsRestoreDr TRUE: Restore DRx if supported + FALSE: Do not restore DRx +**/ +VOID +RestoreVolatileRegisters ( + IN CPU_VOLATILE_REGISTERS *VolatileRegisters, + IN BOOLEAN IsRestoreDr + ) +{ + CPUID_VERSION_INFO_EDX VersionInfoEdx; + + AsmWriteCr0 (VolatileRegisters->Cr0); + AsmWriteCr3 (VolatileRegisters->Cr3); + AsmWriteCr4 (VolatileRegisters->Cr4); + + if (IsRestoreDr) { + AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, NULL, &VersionInfoEdx.Uint32); + if (VersionInfoEdx.Bits.DE != 0) { + // + // If processor supports Debugging Extensions feature + // by CPUID.[EAX=01H]:EDX.BIT2 + // + AsmWriteDr0 (VolatileRegisters->Dr0); + AsmWriteDr1 (VolatileRegisters->Dr1); + AsmWriteDr2 (VolatileRegisters->Dr2); + AsmWriteDr3 (VolatileRegisters->Dr3); + AsmWriteDr6 (VolatileRegisters->Dr6); + AsmWriteDr7 (VolatileRegisters->Dr7); + } + } +} /** Detect whether Mwait-monitor feature is supported. @@ -74,163 +235,1721 @@ GetApLoopMode ( return ApLoopMode; } + /** - MP Initialize Library initialization. + Sort the APIC ID of all processors. - This service will allocate AP reset vector and wakeup all APs to do APs - initialization. + 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. - This service must be invoked before all other MP Initialize Library - service are invoked. + @param[in] CpuMpData Pointer to PEI CPU MP Data +**/ +VOID +SortApicId ( + IN CPU_MP_DATA *CpuMpData + ) +{ + UINTN Index1; + UINTN Index2; + UINTN Index3; + UINT32 ApicId; + CPU_INFO_IN_HOB CpuInfo; + UINT32 ApCount; + CPU_INFO_IN_HOB *CpuInfoInHob; - @retval EFI_SUCCESS MP initialization succeeds. - @retval Others MP initialization fails. + ApCount = CpuMpData->CpuCount - 1; + CpuInfoInHob = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob; + if (ApCount != 0) { + for (Index1 = 0; Index1 < ApCount; Index1++) { + Index3 = Index1; + // + // Sort key is the hardware default APIC ID + // + ApicId = CpuInfoInHob[Index1].ApicId; + for (Index2 = Index1 + 1; Index2 <= ApCount; Index2++) { + if (ApicId > CpuInfoInHob[Index2].ApicId) { + Index3 = Index2; + ApicId = CpuInfoInHob[Index2].ApicId; + } + } + if (Index3 != Index1) { + CopyMem (&CpuInfo, &CpuInfoInHob[Index3], sizeof (CPU_INFO_IN_HOB)); + CopyMem ( + &CpuInfoInHob[Index3], + &CpuInfoInHob[Index1], + sizeof (CPU_INFO_IN_HOB) + ); + CopyMem (&CpuInfoInHob[Index1], &CpuInfo, sizeof (CPU_INFO_IN_HOB)); + } + } + + // + // Get the processor number for the BSP + // + ApicId = GetInitialApicId (); + for (Index1 = 0; Index1 < CpuMpData->CpuCount; Index1++) { + if (CpuInfoInHob[Index1].ApicId == ApicId) { + CpuMpData->BspNumber = (UINT32) Index1; + break; + } + } + } +} + +/** + Enable x2APIC mode on APs. + @param[in, out] Buffer Pointer to private data buffer. **/ -EFI_STATUS +VOID EFIAPI -MpInitLibInitialize ( - VOID +ApFuncEnableX2Apic ( + IN OUT VOID *Buffer ) { - UINT32 MaxLogicalProcessorNumber; - UINT32 ApStackSize; - MP_ASSEMBLY_ADDRESS_MAP AddressMap; - UINTN BufferSize; - UINT32 MonitorFilterSize; - VOID *MpBuffer; - UINTN Buffer; - CPU_MP_DATA *CpuMpData; - UINT8 ApLoopMode; - UINT8 *MonitorBuffer; - UINTN ApResetVectorSize; - UINTN BackupBufferAddr; - MaxLogicalProcessorNumber = PcdGet32(PcdCpuMaxLogicalProcessorNumber); + SetApicMode (LOCAL_APIC_MODE_X2APIC); +} - AsmGetAddressMap (&AddressMap); - ApResetVectorSize = AddressMap.RendezvousFunnelSize + sizeof (MP_CPU_EXCHANGE_INFO); - ApStackSize = PcdGet32(PcdCpuApStackSize); - ApLoopMode = GetApLoopMode (&MonitorFilterSize); +/** + Do sync on APs. - BufferSize = ApStackSize * MaxLogicalProcessorNumber; - BufferSize += MonitorFilterSize * MaxLogicalProcessorNumber; - BufferSize += sizeof (CPU_MP_DATA); - BufferSize += ApResetVectorSize; - BufferSize += (sizeof (CPU_AP_DATA) + sizeof (CPU_INFO_IN_HOB))* MaxLogicalProcessorNumber; - MpBuffer = AllocatePages (EFI_SIZE_TO_PAGES (BufferSize)); - ASSERT (MpBuffer != NULL); - ZeroMem (MpBuffer, BufferSize); - Buffer = (UINTN) MpBuffer; + @param[in, out] Buffer Pointer to private data buffer. +**/ +VOID +EFIAPI +ApInitializeSync ( + IN OUT VOID *Buffer + ) +{ + CPU_MP_DATA *CpuMpData; - MonitorBuffer = (UINT8 *) (Buffer + ApStackSize * MaxLogicalProcessorNumber); - BackupBufferAddr = (UINTN) MonitorBuffer + MonitorFilterSize * MaxLogicalProcessorNumber; - CpuMpData = (CPU_MP_DATA *) (BackupBufferAddr + ApResetVectorSize); - CpuMpData->Buffer = Buffer; - CpuMpData->CpuApStackSize = ApStackSize; - CpuMpData->BackupBuffer = BackupBufferAddr; - CpuMpData->BackupBufferSize = ApResetVectorSize; - CpuMpData->EndOfPeiFlag = FALSE; - CpuMpData->WakeupBuffer = (UINTN) -1; - CpuMpData->CpuCount = 1; - CpuMpData->BspNumber = 0; - CpuMpData->WaitEvent = NULL; - CpuMpData->CpuData = (CPU_AP_DATA *) (CpuMpData + 1); - CpuMpData->CpuInfoInHob = (UINT64) (UINTN) (CpuMpData->CpuData + MaxLogicalProcessorNumber); - InitializeSpinLock(&CpuMpData->MpLock); - // - // Save assembly code information - // - CopyMem (&CpuMpData->AddressMap, &AddressMap, sizeof (MP_ASSEMBLY_ADDRESS_MAP)); + CpuMpData = (CPU_MP_DATA *) Buffer; // - // Finally set AP loop mode + // Sync BSP's MTRR table to AP // - CpuMpData->ApLoopMode = ApLoopMode; - DEBUG ((DEBUG_INFO, "AP Loop Mode is %d\n", CpuMpData->ApLoopMode)); + MtrrSetAllMtrrs (&CpuMpData->MtrrTable); // - // Store BSP's MTRR setting + // Load microcode on AP // - MtrrGetAllMtrrs (&CpuMpData->MtrrTable); - - return EFI_SUCCESS; + MicrocodeDetect (CpuMpData); } /** - Gets detailed MP-related information on the requested processor at the - instant this call is made. This service may only be called from the BSP. - - @param[in] ProcessorNumber The handle number of processor. - @param[out] ProcessorInfoBuffer A pointer to the buffer where information for - the requested processor is deposited. - @param[out] HealthData Return processor health data. + Find the current Processor number by APIC ID. - @retval EFI_SUCCESS Processor information was returned. - @retval EFI_DEVICE_ERROR The calling processor is an AP. - @retval EFI_INVALID_PARAMETER ProcessorInfoBuffer is NULL. - @retval EFI_NOT_FOUND The processor with the handle specified by - ProcessorNumber does not exist in the platform. - @retval EFI_NOT_READY MP Initialize Library is not initialized. + @param[in] CpuMpData Pointer to PEI CPU MP Data + @param[in] ProcessorNumber Return the pocessor number found + @retval EFI_SUCCESS ProcessorNumber is found and returned. + @retval EFI_NOT_FOUND ProcessorNumber is not found. **/ EFI_STATUS -EFIAPI -MpInitLibGetProcessorInfo ( - IN UINTN ProcessorNumber, - OUT EFI_PROCESSOR_INFORMATION *ProcessorInfoBuffer, - OUT EFI_HEALTH_FLAGS *HealthData OPTIONAL +GetProcessorNumber ( + IN CPU_MP_DATA *CpuMpData, + OUT UINTN *ProcessorNumber ) { - return EFI_UNSUPPORTED; + UINTN TotalProcessorNumber; + UINTN Index; + CPU_INFO_IN_HOB *CpuInfoInHob; + + CpuInfoInHob = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob; + + TotalProcessorNumber = CpuMpData->CpuCount; + for (Index = 0; Index < TotalProcessorNumber; Index ++) { + if (CpuInfoInHob[Index].ApicId == GetApicId ()) { + *ProcessorNumber = Index; + return EFI_SUCCESS; + } + } + return EFI_NOT_FOUND; } -/** - This return the handle number for the calling processor. This service may be - called from the BSP and APs. - @param[out] ProcessorNumber Pointer to the handle number of AP. - The range is from 0 to the total number of - logical processors minus 1. The total number of - logical processors can be retrieved by - MpInitLibGetNumberOfProcessors(). +/** + This function will get CPU count in the system. - @retval EFI_SUCCESS The current processor handle number was returned - in ProcessorNumber. - @retval EFI_INVALID_PARAMETER ProcessorNumber is NULL. - @retval EFI_NOT_READY MP Initialize Library is not initialized. + @param[in] CpuMpData Pointer to PEI CPU MP Data + @return CPU count detected **/ -EFI_STATUS -EFIAPI -MpInitLibWhoAmI ( - OUT UINTN *ProcessorNumber +UINTN +CollectProcessorCount ( + IN CPU_MP_DATA *CpuMpData ) { - return EFI_UNSUPPORTED; + // + // Send 1st broadcast IPI to APs to wakeup APs + // + CpuMpData->InitFlag = ApInitConfig; + CpuMpData->X2ApicEnable = FALSE; + WakeUpAP (CpuMpData, TRUE, 0, NULL, NULL); + CpuMpData->InitFlag = ApInitDone; + ASSERT (CpuMpData->CpuCount <= PcdGet32 (PcdCpuMaxLogicalProcessorNumber)); + // + // Wait for all APs finished the initialization + // + while (CpuMpData->FinishedCount < (CpuMpData->CpuCount - 1)) { + CpuPause (); + } + + if (CpuMpData->X2ApicEnable) { + DEBUG ((DEBUG_INFO, "Force x2APIC mode!\n")); + // + // Wakeup all APs to enable x2APIC mode + // + WakeUpAP (CpuMpData, TRUE, 0, ApFuncEnableX2Apic, NULL); + // + // Wait for all known APs finished + // + while (CpuMpData->FinishedCount < (CpuMpData->CpuCount - 1)) { + CpuPause (); + } + // + // Enable x2APIC on BSP + // + SetApicMode (LOCAL_APIC_MODE_X2APIC); + } + DEBUG ((DEBUG_INFO, "APIC MODE is %d\n", GetApicMode ())); + // + // Sort BSP/Aps by CPU APIC ID in ascending order + // + SortApicId (CpuMpData); + + DEBUG ((DEBUG_INFO, "MpInitLib: Find %d processors in system.\n", CpuMpData->CpuCount)); + + return CpuMpData->CpuCount; } -/** - Retrieves the number of logical processor in the platform and the number of - those logical processors that are enabled on this boot. This service may only - be called from the BSP. - @param[out] NumberOfProcessors Pointer to the total number of logical - processors in the system, including the BSP - and disabled APs. - @param[out] NumberOfEnabledProcessors Pointer to the number of enabled logical - processors that exist in system, including - the BSP. +/* + Initialize CPU AP Data when AP is wakeup at the first time. - @retval EFI_SUCCESS The number of logical processors and enabled - logical processors was retrieved. - @retval EFI_DEVICE_ERROR The calling processor is an AP. - @retval EFI_INVALID_PARAMETER NumberOfProcessors is NULL and NumberOfEnabledProcessors - is NULL. - @retval EFI_NOT_READY MP Initialize Library is not initialized. + @param[in, out] CpuMpData Pointer to PEI CPU MP Data + @param[in] ProcessorNumber The handle number of processor + @param[in] BistData Processor BIST data **/ -EFI_STATUS +VOID +InitializeApData ( + IN OUT CPU_MP_DATA *CpuMpData, + IN UINTN ProcessorNumber, + IN UINT32 BistData, + IN UINT64 ApTopOfStack + ) +{ + CPU_INFO_IN_HOB *CpuInfoInHob; + + CpuInfoInHob = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob; + CpuInfoInHob[ProcessorNumber].InitialApicId = GetInitialApicId (); + CpuInfoInHob[ProcessorNumber].ApicId = GetApicId (); + CpuInfoInHob[ProcessorNumber].Health = BistData; + CpuInfoInHob[ProcessorNumber].ApTopOfStack = ApTopOfStack; + + CpuMpData->CpuData[ProcessorNumber].Waiting = FALSE; + CpuMpData->CpuData[ProcessorNumber].CpuHealthy = (BistData == 0) ? TRUE : FALSE; + if (CpuInfoInHob[ProcessorNumber].InitialApicId >= 0xFF) { + // + // Set x2APIC mode if there are any logical processor reporting + // an Initial APIC ID of 255 or greater. + // + AcquireSpinLock(&CpuMpData->MpLock); + CpuMpData->X2ApicEnable = TRUE; + ReleaseSpinLock(&CpuMpData->MpLock); + } + + InitializeSpinLock(&CpuMpData->CpuData[ProcessorNumber].ApLock); + SetApState (&CpuMpData->CpuData[ProcessorNumber], CpuStateIdle); +} + +/** + This function will be called from AP reset code if BSP uses WakeUpAP. + + @param[in] ExchangeInfo Pointer to the MP exchange info buffer + @param[in] NumApsExecuting Number of current executing AP +**/ +VOID EFIAPI -MpInitLibGetNumberOfProcessors ( - OUT UINTN *NumberOfProcessors, OPTIONAL - OUT UINTN *NumberOfEnabledProcessors OPTIONAL +ApWakeupFunction ( + IN MP_CPU_EXCHANGE_INFO *ExchangeInfo, + IN UINTN NumApsExecuting + ) +{ + CPU_MP_DATA *CpuMpData; + UINTN ProcessorNumber; + EFI_AP_PROCEDURE Procedure; + VOID *Parameter; + UINT32 BistData; + volatile UINT32 *ApStartupSignalBuffer; + CPU_INFO_IN_HOB *CpuInfoInHob; + UINT64 ApTopOfStack; + + // + // AP finished assembly code and begin to execute C code + // + CpuMpData = ExchangeInfo->CpuMpData; + + ProgramVirtualWireMode (); + + while (TRUE) { + if (CpuMpData->InitFlag == ApInitConfig) { + // + // Add CPU number + // + InterlockedIncrement ((UINT32 *) &CpuMpData->CpuCount); + ProcessorNumber = NumApsExecuting; + // + // This is first time AP wakeup, get BIST information from AP stack + // + ApTopOfStack = CpuMpData->Buffer + (ProcessorNumber + 1) * CpuMpData->CpuApStackSize; + BistData = *(UINT32 *) ((UINTN) ApTopOfStack - sizeof (UINTN)); + // + // Do some AP initialize sync + // + ApInitializeSync (CpuMpData); + // + // Sync BSP's Control registers to APs + // + RestoreVolatileRegisters (&CpuMpData->CpuData[0].VolatileRegisters, FALSE); + InitializeApData (CpuMpData, ProcessorNumber, BistData, ApTopOfStack); + ApStartupSignalBuffer = CpuMpData->CpuData[ProcessorNumber].StartupApSignal; + } else { + // + // Execute AP function if AP is ready + // + GetProcessorNumber (CpuMpData, &ProcessorNumber); + // + // Clear AP start-up signal when AP waken up + // + ApStartupSignalBuffer = CpuMpData->CpuData[ProcessorNumber].StartupApSignal; + InterlockedCompareExchange32 ( + (UINT32 *) ApStartupSignalBuffer, + WAKEUP_AP_SIGNAL, + 0 + ); + if (CpuMpData->ApLoopMode == ApInHltLoop) { + // + // Restore AP's volatile registers saved + // + RestoreVolatileRegisters (&CpuMpData->CpuData[ProcessorNumber].VolatileRegisters, TRUE); + } + + if (GetApState (&CpuMpData->CpuData[ProcessorNumber]) == CpuStateReady) { + Procedure = (EFI_AP_PROCEDURE)CpuMpData->CpuData[ProcessorNumber].ApFunction; + Parameter = (VOID *) CpuMpData->CpuData[ProcessorNumber].ApFunctionArgument; + if (Procedure != NULL) { + SetApState (&CpuMpData->CpuData[ProcessorNumber], CpuStateBusy); + // + // Invoke AP function here + // + Procedure (Parameter); + CpuInfoInHob = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob; + if (CpuMpData->SwitchBspFlag) { + // + // Re-get the processor number due to BSP/AP maybe exchange in AP function + // + GetProcessorNumber (CpuMpData, &ProcessorNumber); + CpuMpData->CpuData[ProcessorNumber].ApFunction = 0; + CpuMpData->CpuData[ProcessorNumber].ApFunctionArgument = 0; + ApStartupSignalBuffer = CpuMpData->CpuData[ProcessorNumber].StartupApSignal; + CpuInfoInHob[ProcessorNumber].ApTopOfStack = CpuInfoInHob[CpuMpData->NewBspNumber].ApTopOfStack; + } else { + // + // Re-get the CPU APICID and Initial APICID + // + CpuInfoInHob[ProcessorNumber].ApicId = GetApicId (); + CpuInfoInHob[ProcessorNumber].InitialApicId = GetInitialApicId (); + } + } + SetApState (&CpuMpData->CpuData[ProcessorNumber], CpuStateFinished); + } + } + + // + // AP finished executing C code + // + InterlockedIncrement ((UINT32 *) &CpuMpData->FinishedCount); + + // + // Place AP is specified loop mode + // + if (CpuMpData->ApLoopMode == ApInHltLoop) { + // + // Save AP volatile registers + // + SaveVolatileRegisters (&CpuMpData->CpuData[ProcessorNumber].VolatileRegisters); + // + // Place AP in HLT-loop + // + while (TRUE) { + DisableInterrupts (); + CpuSleep (); + CpuPause (); + } + } + while (TRUE) { + DisableInterrupts (); + if (CpuMpData->ApLoopMode == ApInMwaitLoop) { + // + // Place AP in MWAIT-loop + // + AsmMonitor ((UINTN) ApStartupSignalBuffer, 0, 0); + if (*ApStartupSignalBuffer != WAKEUP_AP_SIGNAL) { + // + // Check AP start-up signal again. + // If AP start-up signal is not set, place AP into + // the specified C-state + // + AsmMwait (CpuMpData->ApTargetCState << 4, 0); + } + } else if (CpuMpData->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; + } + } + } +} + +/** + Wait for AP wakeup and write AP start-up signal till AP is waken up. + + @param[in] ApStartupSignalBuffer Pointer to AP wakeup signal +**/ +VOID +WaitApWakeup ( + IN volatile UINT32 *ApStartupSignalBuffer + ) +{ + // + // If AP is waken up, StartupApSignal should be cleared. + // Otherwise, write StartupApSignal again till AP waken up. + // + while (InterlockedCompareExchange32 ( + (UINT32 *) ApStartupSignalBuffer, + WAKEUP_AP_SIGNAL, + WAKEUP_AP_SIGNAL + ) != 0) { + CpuPause (); + } +} + +/** + This function will fill the exchange info structure. + + @param[in] CpuMpData Pointer to CPU MP Data + +**/ +VOID +FillExchangeInfoData ( + IN CPU_MP_DATA *CpuMpData + ) +{ + volatile MP_CPU_EXCHANGE_INFO *ExchangeInfo; + + ExchangeInfo = CpuMpData->MpCpuExchangeInfo; + ExchangeInfo->Lock = 0; + ExchangeInfo->StackStart = CpuMpData->Buffer; + ExchangeInfo->StackSize = CpuMpData->CpuApStackSize; + ExchangeInfo->BufferStart = CpuMpData->WakeupBuffer; + ExchangeInfo->ModeOffset = CpuMpData->AddressMap.ModeEntryOffset; + + ExchangeInfo->CodeSegment = AsmReadCs (); + ExchangeInfo->DataSegment = AsmReadDs (); + + ExchangeInfo->Cr3 = AsmReadCr3 (); + + ExchangeInfo->CFunction = (UINTN) ApWakeupFunction; + ExchangeInfo->NumApsExecuting = 0; + ExchangeInfo->InitFlag = (UINTN) CpuMpData->InitFlag; + ExchangeInfo->CpuInfo = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob; + ExchangeInfo->CpuMpData = CpuMpData; + + ExchangeInfo->EnableExecuteDisable = IsBspExecuteDisableEnabled (); + + // + // Get the BSP's data of GDT and IDT + // + AsmReadGdtr ((IA32_DESCRIPTOR *) &ExchangeInfo->GdtrProfile); + AsmReadIdtr ((IA32_DESCRIPTOR *) &ExchangeInfo->IdtrProfile); +} + +/** + This function will be called by BSP to wakeup AP. + + @param[in] CpuMpData Pointer to CPU MP Data + @param[in] Broadcast TRUE: Send broadcast IPI to all APs + FALSE: Send IPI to AP by ApicId + @param[in] ProcessorNumber The handle number of specified processor + @param[in] Procedure The function to be invoked by AP + @param[in] ProcedureArgument The argument to be passed into AP function +**/ +VOID +WakeUpAP ( + IN CPU_MP_DATA *CpuMpData, + IN BOOLEAN Broadcast, + IN UINTN ProcessorNumber, + IN EFI_AP_PROCEDURE Procedure, OPTIONAL + IN VOID *ProcedureArgument OPTIONAL + ) +{ + volatile MP_CPU_EXCHANGE_INFO *ExchangeInfo; + UINTN Index; + CPU_AP_DATA *CpuData; + BOOLEAN ResetVectorRequired; + CPU_INFO_IN_HOB *CpuInfoInHob; + + CpuMpData->FinishedCount = 0; + ResetVectorRequired = FALSE; + + if (CpuMpData->ApLoopMode == ApInHltLoop || + CpuMpData->InitFlag != ApInitDone) { + ResetVectorRequired = TRUE; + AllocateResetVector (CpuMpData); + FillExchangeInfoData (CpuMpData); + } else if (CpuMpData->ApLoopMode == ApInMwaitLoop) { + // + // Get AP target C-state each time when waking up AP, + // for it maybe updated by platform again + // + CpuMpData->ApTargetCState = PcdGet8 (PcdCpuApTargetCstate); + } + + ExchangeInfo = CpuMpData->MpCpuExchangeInfo; + + if (Broadcast) { + for (Index = 0; Index < CpuMpData->CpuCount; Index++) { + if (Index != CpuMpData->BspNumber) { + CpuData = &CpuMpData->CpuData[Index]; + CpuData->ApFunction = (UINTN) Procedure; + CpuData->ApFunctionArgument = (UINTN) ProcedureArgument; + SetApState (CpuData, CpuStateReady); + if (CpuMpData->InitFlag != ApInitConfig) { + *(UINT32 *) CpuData->StartupApSignal = WAKEUP_AP_SIGNAL; + } + } + } + if (ResetVectorRequired) { + // + // Wakeup all APs + // + SendInitSipiSipiAllExcludingSelf ((UINT32) ExchangeInfo->BufferStart); + } + if (CpuMpData->InitFlag == ApInitConfig) { + // + // Wait for all potential APs waken up in one specified period + // + MicroSecondDelay (PcdGet32(PcdCpuApInitTimeOutInMicroSeconds)); + } else { + // + // Wait all APs waken up if this is not the 1st broadcast of SIPI + // + for (Index = 0; Index < CpuMpData->CpuCount; Index++) { + CpuData = &CpuMpData->CpuData[Index]; + if (Index != CpuMpData->BspNumber) { + WaitApWakeup (CpuData->StartupApSignal); + } + } + } + } else { + CpuData = &CpuMpData->CpuData[ProcessorNumber]; + CpuData->ApFunction = (UINTN) Procedure; + CpuData->ApFunctionArgument = (UINTN) ProcedureArgument; + SetApState (CpuData, CpuStateReady); + // + // Wakeup specified AP + // + ASSERT (CpuMpData->InitFlag != ApInitConfig); + *(UINT32 *) CpuData->StartupApSignal = WAKEUP_AP_SIGNAL; + if (ResetVectorRequired) { + CpuInfoInHob = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob; + SendInitSipiSipi ( + CpuInfoInHob[ProcessorNumber].ApicId, + (UINT32) ExchangeInfo->BufferStart + ); + } + // + // Wait specified AP waken up + // + WaitApWakeup (CpuData->StartupApSignal); + } + + if (ResetVectorRequired) { + FreeResetVector (CpuMpData); + } +} + +/** + Calculate timeout value and return the current performance counter value. + + Calculate the number of performance counter ticks required for a timeout. + If TimeoutInMicroseconds is 0, return value is also 0, which is recognized + as infinity. + + @param[in] TimeoutInMicroseconds Timeout value in microseconds. + @param[out] CurrentTime Returns the current value of the performance counter. + + @return Expected time stamp counter for timeout. + If TimeoutInMicroseconds is 0, return value is also 0, which is recognized + as infinity. + +**/ +UINT64 +CalculateTimeout ( + IN UINTN TimeoutInMicroseconds, + OUT UINT64 *CurrentTime + ) +{ + // + // Read the current value of the performance counter + // + *CurrentTime = GetPerformanceCounter (); + + // + // If TimeoutInMicroseconds is 0, return value is also 0, which is recognized + // as infinity. + // + if (TimeoutInMicroseconds == 0) { + return 0; + } + + // + // GetPerformanceCounterProperties () returns the timestamp counter's frequency + // in Hz. So multiply the return value with TimeoutInMicroseconds and then divide + // it by 1,000,000, to get the number of ticks for the timeout value. + // + return DivU64x32 ( + MultU64x64 ( + GetPerformanceCounterProperties (NULL, NULL), + TimeoutInMicroseconds + ), + 1000000 + ); +} + +/** + Checks whether timeout expires. + + Check whether the number of elapsed performance counter ticks required for + a timeout condition has been reached. + If Timeout is zero, which means infinity, return value is always FALSE. + + @param[in, out] PreviousTime On input, the value of the performance counter + when it was last read. + On output, the current value of the performance + counter + @param[in] TotalTime The total amount of elapsed time in performance + counter ticks. + @param[in] Timeout The number of performance counter ticks required + to reach a timeout condition. + + @retval TRUE A timeout condition has been reached. + @retval FALSE A timeout condition has not been reached. + +**/ +BOOLEAN +CheckTimeout ( + IN OUT UINT64 *PreviousTime, + IN UINT64 *TotalTime, + IN UINT64 Timeout + ) +{ + UINT64 Start; + UINT64 End; + UINT64 CurrentTime; + INT64 Delta; + INT64 Cycle; + + if (Timeout == 0) { + return FALSE; + } + GetPerformanceCounterProperties (&Start, &End); + Cycle = End - Start; + if (Cycle < 0) { + Cycle = -Cycle; + } + Cycle++; + CurrentTime = GetPerformanceCounter(); + Delta = (INT64) (CurrentTime - *PreviousTime); + if (Start > End) { + Delta = -Delta; + } + if (Delta < 0) { + Delta += Cycle; + } + *TotalTime += Delta; + *PreviousTime = CurrentTime; + if (*TotalTime > Timeout) { + return TRUE; + } + return FALSE; +} + +/** + Reset an AP to Idle state. + + Any task being executed by the AP will be aborted and the AP + will be waiting for a new task in Wait-For-SIPI state. + + @param[in] ProcessorNumber The handle number of processor. +**/ +VOID +ResetProcessorToIdleState ( + IN UINTN ProcessorNumber + ) +{ + CPU_MP_DATA *CpuMpData; + + CpuMpData = GetCpuMpData (); + + CpuMpData->InitFlag = ApInitReconfig; + WakeUpAP (CpuMpData, FALSE, ProcessorNumber, NULL, NULL); + while (CpuMpData->FinishedCount < 1) { + CpuPause (); + } + CpuMpData->InitFlag = ApInitDone; + + SetApState (&CpuMpData->CpuData[ProcessorNumber], CpuStateIdle); +} + +/** + Searches for the next waiting AP. + + Search for the next AP that is put in waiting state by single-threaded StartupAllAPs(). + + @param[out] NextProcessorNumber Pointer to the processor number of the next waiting AP. + + @retval EFI_SUCCESS The next waiting AP has been found. + @retval EFI_NOT_FOUND No waiting AP exists. + +**/ +EFI_STATUS +GetNextWaitingProcessorNumber ( + OUT UINTN *NextProcessorNumber + ) +{ + UINTN ProcessorNumber; + CPU_MP_DATA *CpuMpData; + + CpuMpData = GetCpuMpData (); + + for (ProcessorNumber = 0; ProcessorNumber < CpuMpData->CpuCount; ProcessorNumber++) { + if (CpuMpData->CpuData[ProcessorNumber].Waiting) { + *NextProcessorNumber = ProcessorNumber; + return EFI_SUCCESS; + } + } + + return EFI_NOT_FOUND; +} + +/** Checks status of specified AP. + + This function checks whether the specified AP has finished the task assigned + by StartupThisAP(), and whether timeout expires. + + @param[in] ProcessorNumber The handle number of processor. + + @retval EFI_SUCCESS Specified AP has finished task assigned by StartupThisAPs(). + @retval EFI_TIMEOUT The timeout expires. + @retval EFI_NOT_READY Specified AP has not finished task and timeout has not expired. +**/ +EFI_STATUS +CheckThisAP ( + IN UINTN ProcessorNumber + ) +{ + CPU_MP_DATA *CpuMpData; + CPU_AP_DATA *CpuData; + + CpuMpData = GetCpuMpData (); + CpuData = &CpuMpData->CpuData[ProcessorNumber]; + + // + // Check the CPU state of AP. If it is CpuStateFinished, then the AP has finished its task. + // Only BSP and corresponding AP access this unit of CPU Data. This means the AP will not modify the + // value of state after setting the it to CpuStateFinished, so BSP can safely make use of its value. + // + // + // If the AP finishes for StartupThisAP(), return EFI_SUCCESS. + // + if (GetApState(CpuData) == CpuStateFinished) { + if (CpuData->Finished != NULL) { + *(CpuData->Finished) = TRUE; + } + SetApState (CpuData, CpuStateIdle); + return EFI_SUCCESS; + } else { + // + // If timeout expires for StartupThisAP(), report timeout. + // + if (CheckTimeout (&CpuData->CurrentTime, &CpuData->TotalTime, CpuData->ExpectedTime)) { + if (CpuData->Finished != NULL) { + *(CpuData->Finished) = FALSE; + } + // + // Reset failed AP to idle state + // + ResetProcessorToIdleState (ProcessorNumber); + + return EFI_TIMEOUT; + } + } + return EFI_NOT_READY; +} + +/** + Checks status of all APs. + + This function checks whether all APs have finished task assigned by StartupAllAPs(), + and whether timeout expires. + + @retval EFI_SUCCESS All APs have finished task assigned by StartupAllAPs(). + @retval EFI_TIMEOUT The timeout expires. + @retval EFI_NOT_READY APs have not finished task and timeout has not expired. +**/ +EFI_STATUS +CheckAllAPs ( + VOID + ) +{ + UINTN ProcessorNumber; + UINTN NextProcessorNumber; + UINTN ListIndex; + EFI_STATUS Status; + CPU_MP_DATA *CpuMpData; + CPU_AP_DATA *CpuData; + + CpuMpData = GetCpuMpData (); + + NextProcessorNumber = 0; + + // + // Go through all APs that are responsible for the StartupAllAPs(). + // + for (ProcessorNumber = 0; ProcessorNumber < CpuMpData->CpuCount; ProcessorNumber++) { + if (!CpuMpData->CpuData[ProcessorNumber].Waiting) { + continue; + } + + CpuData = &CpuMpData->CpuData[ProcessorNumber]; + // + // Check the CPU state of AP. If it is CpuStateFinished, then the AP has finished its task. + // Only BSP and corresponding AP access this unit of CPU Data. This means the AP will not modify the + // value of state after setting the it to CpuStateFinished, so BSP can safely make use of its value. + // + if (GetApState(CpuData) == CpuStateFinished) { + CpuMpData->RunningCount ++; + CpuMpData->CpuData[ProcessorNumber].Waiting = FALSE; + SetApState(CpuData, CpuStateIdle); + + // + // If in Single Thread mode, then search for the next waiting AP for execution. + // + if (CpuMpData->SingleThread) { + Status = GetNextWaitingProcessorNumber (&NextProcessorNumber); + + if (!EFI_ERROR (Status)) { + WakeUpAP ( + CpuMpData, + FALSE, + (UINT32) NextProcessorNumber, + CpuMpData->Procedure, + CpuMpData->ProcArguments + ); + } + } + } + } + + // + // If all APs finish, return EFI_SUCCESS. + // + if (CpuMpData->RunningCount == CpuMpData->StartCount) { + return EFI_SUCCESS; + } + + // + // If timeout expires, report timeout. + // + if (CheckTimeout ( + &CpuMpData->CurrentTime, + &CpuMpData->TotalTime, + CpuMpData->ExpectedTime) + ) { + // + // If FailedCpuList is not NULL, record all failed APs in it. + // + if (CpuMpData->FailedCpuList != NULL) { + *CpuMpData->FailedCpuList = + AllocatePool ((CpuMpData->StartCount - CpuMpData->FinishedCount + 1) * sizeof (UINTN)); + ASSERT (*CpuMpData->FailedCpuList != NULL); + } + ListIndex = 0; + + for (ProcessorNumber = 0; ProcessorNumber < CpuMpData->CpuCount; ProcessorNumber++) { + // + // Check whether this processor is responsible for StartupAllAPs(). + // + if (CpuMpData->CpuData[ProcessorNumber].Waiting) { + // + // Reset failed APs to idle state + // + ResetProcessorToIdleState (ProcessorNumber); + CpuMpData->CpuData[ProcessorNumber].Waiting = FALSE; + if (CpuMpData->FailedCpuList != NULL) { + (*CpuMpData->FailedCpuList)[ListIndex++] = ProcessorNumber; + } + } + } + if (CpuMpData->FailedCpuList != NULL) { + (*CpuMpData->FailedCpuList)[ListIndex] = END_OF_CPU_LIST; + } + return EFI_TIMEOUT; + } + return EFI_NOT_READY; +} + +/** + MP Initialize Library initialization. + + This service will allocate AP reset vector and wakeup all APs to do APs + initialization. + + This service must be invoked before all other MP Initialize Library + service are invoked. + + @retval EFI_SUCCESS MP initialization succeeds. + @retval Others MP initialization fails. + +**/ +EFI_STATUS +EFIAPI +MpInitLibInitialize ( + VOID + ) +{ + CPU_MP_DATA *OldCpuMpData; + CPU_INFO_IN_HOB *CpuInfoInHob; + UINT32 MaxLogicalProcessorNumber; + UINT32 ApStackSize; + MP_ASSEMBLY_ADDRESS_MAP AddressMap; + UINTN BufferSize; + UINT32 MonitorFilterSize; + VOID *MpBuffer; + UINTN Buffer; + CPU_MP_DATA *CpuMpData; + UINT8 ApLoopMode; + UINT8 *MonitorBuffer; + UINTN Index; + UINTN ApResetVectorSize; + UINTN BackupBufferAddr; + + OldCpuMpData = GetCpuMpDataFromGuidedHob (); + if (OldCpuMpData == NULL) { + MaxLogicalProcessorNumber = PcdGet32(PcdCpuMaxLogicalProcessorNumber); + } else { + MaxLogicalProcessorNumber = OldCpuMpData->CpuCount; + } + ASSERT (MaxLogicalProcessorNumber != 0); + + AsmGetAddressMap (&AddressMap); + ApResetVectorSize = AddressMap.RendezvousFunnelSize + sizeof (MP_CPU_EXCHANGE_INFO); + ApStackSize = PcdGet32(PcdCpuApStackSize); + ApLoopMode = GetApLoopMode (&MonitorFilterSize); + + BufferSize = ApStackSize * MaxLogicalProcessorNumber; + BufferSize += MonitorFilterSize * MaxLogicalProcessorNumber; + BufferSize += sizeof (CPU_MP_DATA); + BufferSize += ApResetVectorSize; + BufferSize += (sizeof (CPU_AP_DATA) + sizeof (CPU_INFO_IN_HOB))* MaxLogicalProcessorNumber; + MpBuffer = AllocatePages (EFI_SIZE_TO_PAGES (BufferSize)); + ASSERT (MpBuffer != NULL); + ZeroMem (MpBuffer, BufferSize); + Buffer = (UINTN) MpBuffer; + + MonitorBuffer = (UINT8 *) (Buffer + ApStackSize * MaxLogicalProcessorNumber); + BackupBufferAddr = (UINTN) MonitorBuffer + MonitorFilterSize * MaxLogicalProcessorNumber; + CpuMpData = (CPU_MP_DATA *) (BackupBufferAddr + ApResetVectorSize); + CpuMpData->Buffer = Buffer; + CpuMpData->CpuApStackSize = ApStackSize; + CpuMpData->BackupBuffer = BackupBufferAddr; + CpuMpData->BackupBufferSize = ApResetVectorSize; + CpuMpData->SaveRestoreFlag = FALSE; + CpuMpData->WakeupBuffer = (UINTN) -1; + CpuMpData->CpuCount = 1; + CpuMpData->BspNumber = 0; + CpuMpData->WaitEvent = NULL; + CpuMpData->SwitchBspFlag = FALSE; + CpuMpData->CpuData = (CPU_AP_DATA *) (CpuMpData + 1); + CpuMpData->CpuInfoInHob = (UINT64) (UINTN) (CpuMpData->CpuData + MaxLogicalProcessorNumber); + InitializeSpinLock(&CpuMpData->MpLock); + // + // Save BSP's Control registers to APs + // + SaveVolatileRegisters (&CpuMpData->CpuData[0].VolatileRegisters); + // + // Set BSP basic information + // + InitializeApData (CpuMpData, 0, 0, CpuMpData->Buffer); + // + // Save assembly code information + // + CopyMem (&CpuMpData->AddressMap, &AddressMap, sizeof (MP_ASSEMBLY_ADDRESS_MAP)); + // + // Finally set AP loop mode + // + CpuMpData->ApLoopMode = ApLoopMode; + DEBUG ((DEBUG_INFO, "AP Loop Mode is %d\n", CpuMpData->ApLoopMode)); + // + // Set up APs wakeup signal buffer + // + for (Index = 0; Index < MaxLogicalProcessorNumber; Index++) { + CpuMpData->CpuData[Index].StartupApSignal = + (UINT32 *)(MonitorBuffer + MonitorFilterSize * Index); + } + // + // Load Microcode on BSP + // + MicrocodeDetect (CpuMpData); + // + // Store BSP's MTRR setting + // + MtrrGetAllMtrrs (&CpuMpData->MtrrTable); + + if (OldCpuMpData == NULL) { + if (MaxLogicalProcessorNumber > 1) { + // + // Wakeup all APs and calculate the processor count in system + // + CollectProcessorCount (CpuMpData); + } + } else { + // + // APs have been wakeup before, just get the CPU Information + // from HOB + // + CpuMpData->CpuCount = OldCpuMpData->CpuCount; + CpuMpData->BspNumber = OldCpuMpData->BspNumber; + CpuMpData->InitFlag = ApInitReconfig; + CpuMpData->CpuInfoInHob = OldCpuMpData->CpuInfoInHob; + CpuInfoInHob = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob; + for (Index = 0; Index < CpuMpData->CpuCount; Index++) { + InitializeSpinLock(&CpuMpData->CpuData[Index].ApLock); + if (CpuInfoInHob[Index].InitialApicId >= 255) { + CpuMpData->X2ApicEnable = TRUE; + } + CpuMpData->CpuData[Index].CpuHealthy = (CpuInfoInHob[Index].Health == 0)? TRUE:FALSE; + CpuMpData->CpuData[Index].ApFunction = 0; + CopyMem ( + &CpuMpData->CpuData[Index].VolatileRegisters, + &CpuMpData->CpuData[0].VolatileRegisters, + sizeof (CPU_VOLATILE_REGISTERS) + ); + } + if (MaxLogicalProcessorNumber > 1) { + // + // Wakeup APs to do some AP initialize sync + // + WakeUpAP (CpuMpData, TRUE, 0, ApInitializeSync, CpuMpData); + // + // Wait for all APs finished initialization + // + while (CpuMpData->FinishedCount < (CpuMpData->CpuCount - 1)) { + CpuPause (); + } + CpuMpData->InitFlag = ApInitDone; + for (Index = 0; Index < CpuMpData->CpuCount; Index++) { + SetApState (&CpuMpData->CpuData[Index], CpuStateIdle); + } + } + } + + // + // Initialize global data for MP support + // + InitMpGlobalData (CpuMpData); + + return EFI_SUCCESS; +} + +/** + Gets detailed MP-related information on the requested processor at the + instant this call is made. This service may only be called from the BSP. + + @param[in] ProcessorNumber The handle number of processor. + @param[out] ProcessorInfoBuffer A pointer to the buffer where information for + the requested processor is deposited. + @param[out] HealthData Return processor health data. + + @retval EFI_SUCCESS Processor information was returned. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_INVALID_PARAMETER ProcessorInfoBuffer is NULL. + @retval EFI_NOT_FOUND The processor with the handle specified by + ProcessorNumber does not exist in the platform. + @retval EFI_NOT_READY MP Initialize Library is not initialized. + +**/ +EFI_STATUS +EFIAPI +MpInitLibGetProcessorInfo ( + IN UINTN ProcessorNumber, + OUT EFI_PROCESSOR_INFORMATION *ProcessorInfoBuffer, + OUT EFI_HEALTH_FLAGS *HealthData OPTIONAL + ) +{ + CPU_MP_DATA *CpuMpData; + UINTN CallerNumber; + CPU_INFO_IN_HOB *CpuInfoInHob; + + CpuMpData = GetCpuMpData (); + CpuInfoInHob = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob; + + // + // Check whether caller processor is BSP + // + MpInitLibWhoAmI (&CallerNumber); + if (CallerNumber != CpuMpData->BspNumber) { + return EFI_DEVICE_ERROR; + } + + if (ProcessorInfoBuffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (ProcessorNumber >= CpuMpData->CpuCount) { + return EFI_NOT_FOUND; + } + + ProcessorInfoBuffer->ProcessorId = (UINT64) CpuInfoInHob[ProcessorNumber].ApicId; + ProcessorInfoBuffer->StatusFlag = 0; + if (ProcessorNumber == CpuMpData->BspNumber) { + ProcessorInfoBuffer->StatusFlag |= PROCESSOR_AS_BSP_BIT; + } + if (CpuMpData->CpuData[ProcessorNumber].CpuHealthy) { + ProcessorInfoBuffer->StatusFlag |= PROCESSOR_HEALTH_STATUS_BIT; + } + if (GetApState (&CpuMpData->CpuData[ProcessorNumber]) == CpuStateDisabled) { + ProcessorInfoBuffer->StatusFlag &= ~PROCESSOR_ENABLED_BIT; + } else { + ProcessorInfoBuffer->StatusFlag |= PROCESSOR_ENABLED_BIT; + } + + // + // Get processor location information + // + GetProcessorLocationByApicId ( + CpuInfoInHob[ProcessorNumber].ApicId, + &ProcessorInfoBuffer->Location.Package, + &ProcessorInfoBuffer->Location.Core, + &ProcessorInfoBuffer->Location.Thread + ); + + if (HealthData != NULL) { + HealthData->Uint32 = CpuInfoInHob[ProcessorNumber].Health; + } + + return EFI_SUCCESS; +} + +/** + Worker function to switch the requested AP to be the BSP from that point onward. + + @param[in] ProcessorNumber The handle number of AP that is to become the new BSP. + @param[in] EnableOldBSP If TRUE, then the old BSP will be listed as an + enabled AP. Otherwise, it will be disabled. + + @retval EFI_SUCCESS BSP successfully switched. + @retval others Failed to switch BSP. + +**/ +EFI_STATUS +SwitchBSPWorker ( + IN UINTN ProcessorNumber, + IN BOOLEAN EnableOldBSP + ) +{ + CPU_MP_DATA *CpuMpData; + UINTN CallerNumber; + CPU_STATE State; + MSR_IA32_APIC_BASE_REGISTER ApicBaseMsr; + + CpuMpData = GetCpuMpData (); + + // + // Check whether caller processor is BSP + // + MpInitLibWhoAmI (&CallerNumber); + if (CallerNumber != CpuMpData->BspNumber) { + return EFI_SUCCESS; + } + + if (ProcessorNumber >= CpuMpData->CpuCount) { + return EFI_NOT_FOUND; + } + + // + // Check whether specified AP is disabled + // + State = GetApState (&CpuMpData->CpuData[ProcessorNumber]); + if (State == CpuStateDisabled) { + return EFI_INVALID_PARAMETER; + } + + // + // Check whether ProcessorNumber specifies the current BSP + // + if (ProcessorNumber == CpuMpData->BspNumber) { + return EFI_INVALID_PARAMETER; + } + + // + // Check whether specified AP is busy + // + if (State == CpuStateBusy) { + return EFI_NOT_READY; + } + + CpuMpData->BSPInfo.State = CPU_SWITCH_STATE_IDLE; + CpuMpData->APInfo.State = CPU_SWITCH_STATE_IDLE; + CpuMpData->SwitchBspFlag = TRUE; + CpuMpData->NewBspNumber = ProcessorNumber; + + // + // Clear the BSP bit of MSR_IA32_APIC_BASE + // + ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE); + ApicBaseMsr.Bits.BSP = 0; + AsmWriteMsr64 (MSR_IA32_APIC_BASE, ApicBaseMsr.Uint64); + + // + // Need to wakeUp AP (future BSP). + // + WakeUpAP (CpuMpData, FALSE, ProcessorNumber, FutureBSPProc, CpuMpData); + + AsmExchangeRole (&CpuMpData->BSPInfo, &CpuMpData->APInfo); + + // + // Set the BSP bit of MSR_IA32_APIC_BASE on new BSP + // + ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE); + ApicBaseMsr.Bits.BSP = 1; + AsmWriteMsr64 (MSR_IA32_APIC_BASE, ApicBaseMsr.Uint64); + + // + // Wait for old BSP finished AP task + // + while (GetApState (&CpuMpData->CpuData[CallerNumber]) != CpuStateFinished) { + CpuPause (); + } + + CpuMpData->SwitchBspFlag = FALSE; + // + // Set old BSP enable state + // + if (!EnableOldBSP) { + SetApState (&CpuMpData->CpuData[CallerNumber], CpuStateDisabled); + } + // + // Save new BSP number + // + CpuMpData->BspNumber = (UINT32) ProcessorNumber; + + return EFI_SUCCESS; +} + +/** + Worker function to let the caller enable or disable an AP from this point onward. + This service may only be called from the BSP. + + @param[in] ProcessorNumber The handle number of AP. + @param[in] EnableAP Specifies the new state for the processor for + enabled, FALSE for disabled. + @param[in] HealthFlag If not NULL, a pointer to a value that specifies + the new health status of the AP. + + @retval EFI_SUCCESS The specified AP was enabled or disabled successfully. + @retval others Failed to Enable/Disable AP. + +**/ +EFI_STATUS +EnableDisableApWorker ( + IN UINTN ProcessorNumber, + IN BOOLEAN EnableAP, + IN UINT32 *HealthFlag OPTIONAL + ) +{ + CPU_MP_DATA *CpuMpData; + UINTN CallerNumber; + + CpuMpData = GetCpuMpData (); + + // + // Check whether caller processor is BSP + // + MpInitLibWhoAmI (&CallerNumber); + if (CallerNumber != CpuMpData->BspNumber) { + return EFI_DEVICE_ERROR; + } + + if (ProcessorNumber == CpuMpData->BspNumber) { + return EFI_INVALID_PARAMETER; + } + + if (ProcessorNumber >= CpuMpData->CpuCount) { + return EFI_NOT_FOUND; + } + + if (!EnableAP) { + SetApState (&CpuMpData->CpuData[ProcessorNumber], CpuStateDisabled); + } else { + SetApState (&CpuMpData->CpuData[ProcessorNumber], CpuStateIdle); + } + + if (HealthFlag != NULL) { + CpuMpData->CpuData[ProcessorNumber].CpuHealthy = + (BOOLEAN) ((*HealthFlag & PROCESSOR_HEALTH_STATUS_BIT) != 0); + } + + return EFI_SUCCESS; +} + +/** + This return the handle number for the calling processor. This service may be + called from the BSP and APs. + + @param[out] ProcessorNumber Pointer to the handle number of AP. + The range is from 0 to the total number of + logical processors minus 1. The total number of + logical processors can be retrieved by + MpInitLibGetNumberOfProcessors(). + + @retval EFI_SUCCESS The current processor handle number was returned + in ProcessorNumber. + @retval EFI_INVALID_PARAMETER ProcessorNumber is NULL. + @retval EFI_NOT_READY MP Initialize Library is not initialized. + +**/ +EFI_STATUS +EFIAPI +MpInitLibWhoAmI ( + OUT UINTN *ProcessorNumber + ) +{ + CPU_MP_DATA *CpuMpData; + + if (ProcessorNumber == NULL) { + return EFI_INVALID_PARAMETER; + } + + CpuMpData = GetCpuMpData (); + + return GetProcessorNumber (CpuMpData, ProcessorNumber); +} + +/** + Retrieves the number of logical processor in the platform and the number of + those logical processors that are enabled on this boot. This service may only + be called from the BSP. + + @param[out] NumberOfProcessors Pointer to the total number of logical + processors in the system, including the BSP + and disabled APs. + @param[out] NumberOfEnabledProcessors Pointer to the number of enabled logical + processors that exist in system, including + the BSP. + + @retval EFI_SUCCESS The number of logical processors and enabled + logical processors was retrieved. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_INVALID_PARAMETER NumberOfProcessors is NULL and NumberOfEnabledProcessors + is NULL. + @retval EFI_NOT_READY MP Initialize Library is not initialized. + +**/ +EFI_STATUS +EFIAPI +MpInitLibGetNumberOfProcessors ( + OUT UINTN *NumberOfProcessors, OPTIONAL + OUT UINTN *NumberOfEnabledProcessors OPTIONAL + ) +{ + CPU_MP_DATA *CpuMpData; + UINTN CallerNumber; + UINTN ProcessorNumber; + UINTN EnabledProcessorNumber; + UINTN Index; + + CpuMpData = GetCpuMpData (); + + if ((NumberOfProcessors == NULL) && (NumberOfEnabledProcessors == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // + // Check whether caller processor is BSP + // + MpInitLibWhoAmI (&CallerNumber); + if (CallerNumber != CpuMpData->BspNumber) { + return EFI_DEVICE_ERROR; + } + + ProcessorNumber = CpuMpData->CpuCount; + EnabledProcessorNumber = 0; + for (Index = 0; Index < ProcessorNumber; Index++) { + if (GetApState (&CpuMpData->CpuData[Index]) != CpuStateDisabled) { + EnabledProcessorNumber ++; + } + } + + if (NumberOfProcessors != NULL) { + *NumberOfProcessors = ProcessorNumber; + } + if (NumberOfEnabledProcessors != NULL) { + *NumberOfEnabledProcessors = EnabledProcessorNumber; + } + + return EFI_SUCCESS; +} + + +/** + Worker function to execute a caller provided function on all enabled APs. + + @param[in] Procedure A pointer to the function to be run on + enabled APs of the system. + @param[in] SingleThread If TRUE, then all the enabled APs execute + the function specified by Procedure one by + one, in ascending order of processor handle + number. If FALSE, then all the enabled APs + execute the function specified by Procedure + simultaneously. + @param[in] WaitEvent The event created by the caller with CreateEvent() + service. + @param[in] TimeoutInMicrosecsond Indicates the time limit in microseconds for + APs to return from Procedure, either for + blocking or non-blocking mode. + @param[in] ProcedureArgument The parameter passed into Procedure for + all APs. + @param[out] FailedCpuList If all APs finish successfully, then its + content is set to NULL. If not all APs + finish before timeout expires, then its + content is set to address of the buffer + holding handle numbers of the failed APs. + + @retval EFI_SUCCESS In blocking mode, all APs have finished before + the timeout expired. + @retval EFI_SUCCESS In non-blocking mode, function has been dispatched + to all enabled APs. + @retval others Failed to Startup all APs. + +**/ +EFI_STATUS +StartupAllAPsWorker ( + IN EFI_AP_PROCEDURE Procedure, + IN BOOLEAN SingleThread, + IN EFI_EVENT WaitEvent OPTIONAL, + IN UINTN TimeoutInMicroseconds, + IN VOID *ProcedureArgument OPTIONAL, + OUT UINTN **FailedCpuList OPTIONAL + ) +{ + EFI_STATUS Status; + CPU_MP_DATA *CpuMpData; + UINTN ProcessorCount; + UINTN ProcessorNumber; + UINTN CallerNumber; + CPU_AP_DATA *CpuData; + BOOLEAN HasEnabledAp; + CPU_STATE ApState; + + CpuMpData = GetCpuMpData (); + + if (FailedCpuList != NULL) { + *FailedCpuList = NULL; + } + + if (CpuMpData->CpuCount == 1) { + return EFI_NOT_STARTED; + } + + if (Procedure == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Check whether caller processor is BSP + // + MpInitLibWhoAmI (&CallerNumber); + if (CallerNumber != CpuMpData->BspNumber) { + return EFI_DEVICE_ERROR; + } + + // + // Update AP state + // + CheckAndUpdateApsStatus (); + + ProcessorCount = CpuMpData->CpuCount; + HasEnabledAp = FALSE; + // + // Check whether all enabled APs are idle. + // If any enabled AP is not idle, return EFI_NOT_READY. + // + for (ProcessorNumber = 0; ProcessorNumber < ProcessorCount; ProcessorNumber++) { + CpuData = &CpuMpData->CpuData[ProcessorNumber]; + if (ProcessorNumber != CpuMpData->BspNumber) { + ApState = GetApState (CpuData); + if (ApState != CpuStateDisabled) { + HasEnabledAp = TRUE; + if (ApState != CpuStateIdle) { + // + // If any enabled APs are busy, return EFI_NOT_READY. + // + return EFI_NOT_READY; + } + } + } + } + + if (!HasEnabledAp) { + // + // If no enabled AP exists, return EFI_NOT_STARTED. + // + return EFI_NOT_STARTED; + } + + CpuMpData->StartCount = 0; + for (ProcessorNumber = 0; ProcessorNumber < ProcessorCount; ProcessorNumber++) { + CpuData = &CpuMpData->CpuData[ProcessorNumber]; + CpuData->Waiting = FALSE; + if (ProcessorNumber != CpuMpData->BspNumber) { + if (CpuData->State == CpuStateIdle) { + // + // Mark this processor as responsible for current calling. + // + CpuData->Waiting = TRUE; + CpuMpData->StartCount++; + } + } + } + + CpuMpData->Procedure = Procedure; + CpuMpData->ProcArguments = ProcedureArgument; + CpuMpData->SingleThread = SingleThread; + CpuMpData->FinishedCount = 0; + CpuMpData->RunningCount = 0; + CpuMpData->FailedCpuList = FailedCpuList; + CpuMpData->ExpectedTime = CalculateTimeout ( + TimeoutInMicroseconds, + &CpuMpData->CurrentTime + ); + CpuMpData->TotalTime = 0; + CpuMpData->WaitEvent = WaitEvent; + + if (!SingleThread) { + WakeUpAP (CpuMpData, TRUE, 0, Procedure, ProcedureArgument); + } else { + for (ProcessorNumber = 0; ProcessorNumber < ProcessorCount; ProcessorNumber++) { + if (ProcessorNumber == CallerNumber) { + continue; + } + if (CpuMpData->CpuData[ProcessorNumber].Waiting) { + WakeUpAP (CpuMpData, FALSE, ProcessorNumber, Procedure, ProcedureArgument); + break; + } + } + } + + Status = EFI_SUCCESS; + if (WaitEvent == NULL) { + do { + Status = CheckAllAPs (); + } while (Status == EFI_NOT_READY); + } + + return Status; +} + +/** + Worker function to let the caller get one enabled AP to execute a caller-provided + function. + + @param[in] Procedure A pointer to the function to be run on + enabled APs of the system. + @param[in] ProcessorNumber The handle number of the AP. + @param[in] WaitEvent The event created by the caller with CreateEvent() + service. + @param[in] TimeoutInMicrosecsond Indicates the time limit in microseconds for + APs to return from Procedure, either for + blocking or non-blocking mode. + @param[in] ProcedureArgument The parameter passed into Procedure for + all APs. + @param[out] Finished If AP returns from Procedure before the + timeout expires, its content is set to TRUE. + Otherwise, the value is set to FALSE. + + @retval EFI_SUCCESS In blocking mode, specified AP finished before + the timeout expires. + @retval others Failed to Startup AP. + +**/ +EFI_STATUS +StartupThisAPWorker ( + IN EFI_AP_PROCEDURE Procedure, + IN UINTN ProcessorNumber, + IN EFI_EVENT WaitEvent OPTIONAL, + IN UINTN TimeoutInMicroseconds, + IN VOID *ProcedureArgument OPTIONAL, + OUT BOOLEAN *Finished OPTIONAL + ) +{ + EFI_STATUS Status; + CPU_MP_DATA *CpuMpData; + CPU_AP_DATA *CpuData; + UINTN CallerNumber; + + CpuMpData = GetCpuMpData (); + + if (Finished != NULL) { + *Finished = FALSE; + } + + // + // Check whether caller processor is BSP + // + MpInitLibWhoAmI (&CallerNumber); + if (CallerNumber != CpuMpData->BspNumber) { + return EFI_DEVICE_ERROR; + } + + // + // Check whether processor with the handle specified by ProcessorNumber exists + // + if (ProcessorNumber >= CpuMpData->CpuCount) { + return EFI_NOT_FOUND; + } + + // + // Check whether specified processor is BSP + // + if (ProcessorNumber == CpuMpData->BspNumber) { + return EFI_INVALID_PARAMETER; + } + + // + // Check parameter Procedure + // + if (Procedure == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Update AP state + // + CheckAndUpdateApsStatus (); + + // + // Check whether specified AP is disabled + // + if (GetApState (&CpuMpData->CpuData[ProcessorNumber]) == CpuStateDisabled) { + return EFI_INVALID_PARAMETER; + } + + // + // If WaitEvent is not NULL, execute in non-blocking mode. + // BSP saves data for CheckAPsStatus(), and returns EFI_SUCCESS. + // CheckAPsStatus() will check completion and timeout periodically. + // + CpuData = &CpuMpData->CpuData[ProcessorNumber]; + CpuData->WaitEvent = WaitEvent; + CpuData->Finished = Finished; + CpuData->ExpectedTime = CalculateTimeout (TimeoutInMicroseconds, &CpuData->CurrentTime); + CpuData->TotalTime = 0; + + WakeUpAP (CpuMpData, FALSE, ProcessorNumber, Procedure, ProcedureArgument); + + // + // If WaitEvent is NULL, execute in blocking mode. + // BSP checks AP's state until it finishes or TimeoutInMicrosecsond expires. + // + Status = EFI_SUCCESS; + if (WaitEvent == NULL) { + do { + Status = CheckThisAP (ProcessorNumber); + } while (Status == EFI_NOT_READY); + } + + return Status; +} + +/** + Get pointer to CPU MP Data structure from GUIDed HOB. + + @return The pointer to CPU MP Data structure. +**/ +CPU_MP_DATA * +GetCpuMpDataFromGuidedHob ( + VOID + ) +{ + EFI_HOB_GUID_TYPE *GuidHob; + VOID *DataInHob; + CPU_MP_DATA *CpuMpData; + + CpuMpData = NULL; + GuidHob = GetFirstGuidHob (&mCpuInitMpLibHobGuid); + if (GuidHob != NULL) { + DataInHob = GET_GUID_HOB_DATA (GuidHob); + CpuMpData = (CPU_MP_DATA *) (*(UINTN *) DataInHob); + } + return CpuMpData; +} + +/** + Get available system memory below 1MB by specified size. + + @param[in] CpuMpData The pointer to CPU MP Data structure. +**/ +VOID +BackupAndPrepareWakeupBuffer( + IN CPU_MP_DATA *CpuMpData + ) +{ + CopyMem ( + (VOID *) CpuMpData->BackupBuffer, + (VOID *) CpuMpData->WakeupBuffer, + CpuMpData->BackupBufferSize + ); + CopyMem ( + (VOID *) CpuMpData->WakeupBuffer, + (VOID *) CpuMpData->AddressMap.RendezvousFunnelAddress, + CpuMpData->AddressMap.RendezvousFunnelSize + ); +} + +/** + Restore wakeup buffer data. + + @param[in] CpuMpData The pointer to CPU MP Data structure. +**/ +VOID +RestoreWakeupBuffer( + IN CPU_MP_DATA *CpuMpData ) { - return EFI_UNSUPPORTED; + CopyMem ( + (VOID *) CpuMpData->WakeupBuffer, + (VOID *) CpuMpData->BackupBuffer, + CpuMpData->BackupBufferSize + ); }