From 2cc68ed95f4e6a5b6256a6cc1e9f6a585a7d2ba7 Mon Sep 17 00:00:00 2001 From: Guo Mang Date: Thu, 22 Dec 2016 17:11:19 +0800 Subject: UefiCpuPkg: Move to new location Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Guo Mang --- Core/UefiCpuPkg/CpuMpPei/CpuBist.c | 263 ++++++++ Core/UefiCpuPkg/CpuMpPei/CpuMpPei.c | 884 ++++++++++++++++++++++++++ Core/UefiCpuPkg/CpuMpPei/CpuMpPei.h | 315 ++++++++++ Core/UefiCpuPkg/CpuMpPei/CpuMpPei.inf | 96 +++ Core/UefiCpuPkg/CpuMpPei/CpuMpPei.uni | 22 + Core/UefiCpuPkg/CpuMpPei/CpuMpPeiExtra.uni | 20 + Core/UefiCpuPkg/CpuMpPei/Ia32/MpEqu.inc | 39 ++ Core/UefiCpuPkg/CpuMpPei/Ia32/MpFuncs.asm | 250 ++++++++ Core/UefiCpuPkg/CpuMpPei/Ia32/MpFuncs.nasm | 229 +++++++ Core/UefiCpuPkg/CpuMpPei/Microcode.c | 213 +++++++ Core/UefiCpuPkg/CpuMpPei/Microcode.h | 58 ++ Core/UefiCpuPkg/CpuMpPei/PeiMpServices.c | 956 +++++++++++++++++++++++++++++ Core/UefiCpuPkg/CpuMpPei/PeiMpServices.h | 377 ++++++++++++ Core/UefiCpuPkg/CpuMpPei/X64/MpEqu.inc | 41 ++ Core/UefiCpuPkg/CpuMpPei/X64/MpFuncs.asm | 290 +++++++++ Core/UefiCpuPkg/CpuMpPei/X64/MpFuncs.nasm | 281 +++++++++ 16 files changed, 4334 insertions(+) create mode 100644 Core/UefiCpuPkg/CpuMpPei/CpuBist.c create mode 100644 Core/UefiCpuPkg/CpuMpPei/CpuMpPei.c create mode 100644 Core/UefiCpuPkg/CpuMpPei/CpuMpPei.h create mode 100644 Core/UefiCpuPkg/CpuMpPei/CpuMpPei.inf create mode 100644 Core/UefiCpuPkg/CpuMpPei/CpuMpPei.uni create mode 100644 Core/UefiCpuPkg/CpuMpPei/CpuMpPeiExtra.uni create mode 100644 Core/UefiCpuPkg/CpuMpPei/Ia32/MpEqu.inc create mode 100644 Core/UefiCpuPkg/CpuMpPei/Ia32/MpFuncs.asm create mode 100644 Core/UefiCpuPkg/CpuMpPei/Ia32/MpFuncs.nasm create mode 100644 Core/UefiCpuPkg/CpuMpPei/Microcode.c create mode 100644 Core/UefiCpuPkg/CpuMpPei/Microcode.h create mode 100644 Core/UefiCpuPkg/CpuMpPei/PeiMpServices.c create mode 100644 Core/UefiCpuPkg/CpuMpPei/PeiMpServices.h create mode 100644 Core/UefiCpuPkg/CpuMpPei/X64/MpEqu.inc create mode 100644 Core/UefiCpuPkg/CpuMpPei/X64/MpFuncs.asm create mode 100644 Core/UefiCpuPkg/CpuMpPei/X64/MpFuncs.nasm (limited to 'Core/UefiCpuPkg/CpuMpPei') diff --git a/Core/UefiCpuPkg/CpuMpPei/CpuBist.c b/Core/UefiCpuPkg/CpuMpPei/CpuBist.c new file mode 100644 index 0000000000..56292452a9 --- /dev/null +++ b/Core/UefiCpuPkg/CpuMpPei/CpuBist.c @@ -0,0 +1,263 @@ +/** @file + Update and publish processors' BIST information. + + Copyright (c) 2015, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "CpuMpPei.h" + +EFI_SEC_PLATFORM_INFORMATION2_PPI mSecPlatformInformation2Ppi = { + SecPlatformInformation2 +}; + +EFI_PEI_PPI_DESCRIPTOR mPeiSecPlatformInformation2Ppi = { + (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), + &gEfiSecPlatformInformation2PpiGuid, + &mSecPlatformInformation2Ppi +}; + +/** + Implementation of the PlatformInformation2 service in EFI_SEC_PLATFORM_INFORMATION2_PPI. + + @param PeiServices The pointer to the PEI Services Table. + @param StructureSize The pointer to the variable describing size of the input buffer. + @param PlatformInformationRecord2 The pointer to the EFI_SEC_PLATFORM_INFORMATION_RECORD2. + + @retval EFI_SUCCESS The data was successfully returned. + @retval EFI_BUFFER_TOO_SMALL The buffer was too small. The current buffer size needed to + hold the record is returned in StructureSize. + +**/ +EFI_STATUS +EFIAPI +SecPlatformInformation2 ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN OUT UINT64 *StructureSize, + OUT EFI_SEC_PLATFORM_INFORMATION_RECORD2 *PlatformInformationRecord2 + ) +{ + PEI_CPU_MP_DATA *PeiCpuMpData; + UINTN BistInformationSize; + UINTN CpuIndex; + EFI_SEC_PLATFORM_INFORMATION_CPU *CpuInstance; + + PeiCpuMpData = GetMpHobData (); + + BistInformationSize = sizeof (EFI_SEC_PLATFORM_INFORMATION_RECORD2) + + sizeof (EFI_SEC_PLATFORM_INFORMATION_CPU) * PeiCpuMpData->CpuCount; + // + // return the information size if input buffer size is too small + // + if ((*StructureSize) < (UINT64) BistInformationSize) { + *StructureSize = (UINT64) BistInformationSize; + return EFI_BUFFER_TOO_SMALL; + } + + PlatformInformationRecord2->NumberOfCpus = PeiCpuMpData->CpuCount; + CpuInstance = PlatformInformationRecord2->CpuInstance; + for (CpuIndex = 0; CpuIndex < PeiCpuMpData->CpuCount; CpuIndex ++) { + CpuInstance[CpuIndex].CpuLocation = PeiCpuMpData->CpuData[CpuIndex].ApicId; + CpuInstance[CpuIndex].InfoRecord.IA32HealthFlags = PeiCpuMpData->CpuData[CpuIndex].Health; + } + + return EFI_SUCCESS; +} + +/** + Worker function to get CPUs' BIST by calling SecPlatformInformationPpi + or SecPlatformInformation2Ppi. + + @param PeiServices Pointer to PEI Services Table + @param Guid PPI Guid + @param PpiDescriptor Return a pointer to instance of the + EFI_PEI_PPI_DESCRIPTOR + @param BistInformationData Pointer to BIST information data + + @retval EFI_SUCCESS Retrieve of the BIST data successfully + @retval EFI_NOT_FOUND No sec platform information(2) ppi export + @retval EFI_DEVICE_ERROR Failed to get CPU Information + +**/ +EFI_STATUS +GetBistInfoFromPpi ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN CONST EFI_GUID *Guid, + OUT EFI_PEI_PPI_DESCRIPTOR **PpiDescriptor, + OUT VOID **BistInformationData + ) +{ + EFI_STATUS Status; + EFI_SEC_PLATFORM_INFORMATION2_PPI *SecPlatformInformation2Ppi; + EFI_SEC_PLATFORM_INFORMATION_RECORD2 *SecPlatformInformation2; + UINT64 InformationSize; + + Status = PeiServicesLocatePpi ( + Guid, // GUID + 0, // INSTANCE + PpiDescriptor, // EFI_PEI_PPI_DESCRIPTOR + (VOID **)&SecPlatformInformation2Ppi // PPI + ); + if (Status == EFI_NOT_FOUND) { + return EFI_NOT_FOUND; + } + + if (Status == EFI_SUCCESS) { + // + // Get the size of the sec platform information2(BSP/APs' BIST data) + // + InformationSize = 0; + SecPlatformInformation2 = NULL; + Status = SecPlatformInformation2Ppi->PlatformInformation2 ( + PeiServices, + &InformationSize, + SecPlatformInformation2 + ); + if (Status == EFI_BUFFER_TOO_SMALL) { + Status = PeiServicesAllocatePool ( + (UINTN) InformationSize, + (VOID **) &SecPlatformInformation2 + ); + if (Status == EFI_SUCCESS) { + // + // Retrieve BIST data + // + Status = SecPlatformInformation2Ppi->PlatformInformation2 ( + PeiServices, + &InformationSize, + SecPlatformInformation2 + ); + if (Status == EFI_SUCCESS) { + *BistInformationData = SecPlatformInformation2; + return EFI_SUCCESS; + } + } + } + } + + return EFI_DEVICE_ERROR; +} + +/** + 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); + } +} diff --git a/Core/UefiCpuPkg/CpuMpPei/CpuMpPei.c b/Core/UefiCpuPkg/CpuMpPei/CpuMpPei.c new file mode 100644 index 0000000000..4a453c6570 --- /dev/null +++ b/Core/UefiCpuPkg/CpuMpPei/CpuMpPei.c @@ -0,0 +1,884 @@ +/** @file + CPU PEI Module installs CPU Multiple Processor PPI. + + Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "CpuMpPei.h" + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_PEI_NOTIFY_DESCRIPTOR mNotifyList = { + (EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), + &gEfiEndOfPeiSignalPpiGuid, + CpuMpEndOfPeiCallback +}; + +/** + 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; + } + } + } +} + +/** + Enable x2APIC mode on APs. + + @param Buffer Pointer to private data buffer. +**/ +VOID +EFIAPI +ApFuncEnableX2Apic ( + IN OUT VOID *Buffer + ) +{ + SetApicMode (LOCAL_APIC_MODE_X2APIC); +} + +/** + Get AP loop mode. + + @param MonitorFilterSize Returns the largest monitor-line size in bytes. + + @return The AP loop mode. +**/ +UINT8 +GetApLoopMode ( + OUT UINT16 *MonitorFilterSize + ) +{ + UINT8 ApLoopMode; + UINT32 RegEbx; + UINT32 RegEcx; + UINT32 RegEdx; + + ASSERT (MonitorFilterSize != NULL); + + ApLoopMode = PcdGet8 (PcdCpuApLoopMode); + ASSERT (ApLoopMode >= ApInHltLoop && ApLoopMode <= ApInRunLoop); + if (ApLoopMode == ApInMwaitLoop) { + AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, &RegEcx, NULL); + if ((RegEcx & BIT3) == 0) { + // + // If processor does not support MONITOR/MWAIT feature + // by CPUID.[EAX=01H]:ECX.BIT3, force AP in Hlt-loop mode + // + ApLoopMode = ApInHltLoop; + } + } + + if (ApLoopMode == ApInHltLoop) { + *MonitorFilterSize = 0; + } else if (ApLoopMode == ApInRunLoop) { + *MonitorFilterSize = sizeof (UINT32); + } else if (ApLoopMode == ApInMwaitLoop) { + // + // CPUID.[EAX=05H]:EBX.BIT0-15: Largest monitor-line size in bytes + // CPUID.[EAX=05H].EDX: C-states supported using MWAIT + // + AsmCpuid (CPUID_MONITOR_MWAIT, NULL, &RegEbx, NULL, &RegEdx); + *MonitorFilterSize = RegEbx & 0xFFFF; + } + + return ApLoopMode; +} + +/** + Get CPU MP Data pointer from the Guided HOB. + + @return Pointer to Pointer to PEI CPU MP Data +**/ +PEI_CPU_MP_DATA * +GetMpHobData ( + VOID + ) +{ + EFI_HOB_GUID_TYPE *GuidHob; + VOID *DataInHob; + PEI_CPU_MP_DATA *CpuMpData; + + CpuMpData = NULL; + GuidHob = GetFirstGuidHob (&gEfiCallerIdGuid); + if (GuidHob != NULL) { + DataInHob = GET_GUID_HOB_DATA (GuidHob); + CpuMpData = (PEI_CPU_MP_DATA *)(*(UINTN *)DataInHob); + } + ASSERT (CpuMpData != NULL); + return CpuMpData; +} + +/** + Save the volatile registers required to be restored following INIT IPI. + + @param VolatileRegisters Returns buffer saved the volatile resisters +**/ +VOID +SaveVolatileRegisters ( + OUT CPU_VOLATILE_REGISTERS *VolatileRegisters + ) +{ + UINT32 RegEdx; + + VolatileRegisters->Cr0 = AsmReadCr0 (); + VolatileRegisters->Cr3 = AsmReadCr3 (); + VolatileRegisters->Cr4 = AsmReadCr4 (); + + AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, NULL, &RegEdx); + if ((RegEdx & BIT2) != 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 VolatileRegisters Pointer to volatile resisters + @param IsRestoreDr TRUE: Restore DRx if supported + FALSE: Do not restore DRx +**/ +VOID +RestoreVolatileRegisters ( + IN CPU_VOLATILE_REGISTERS *VolatileRegisters, + IN BOOLEAN IsRestoreDr + ) +{ + UINT32 RegEdx; + + AsmWriteCr0 (VolatileRegisters->Cr0); + AsmWriteCr3 (VolatileRegisters->Cr3); + AsmWriteCr4 (VolatileRegisters->Cr4); + + if (IsRestoreDr) { + AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, NULL, &RegEdx); + if ((RegEdx & BIT2) != 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); + } + } +} + +/** + 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); + 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; + 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) { + // + // Clear AP start-up signal when AP waken up + // + InterlockedCompareExchange32 ( + (UINT32 *)ApStartupSignalBuffer, + WAKEUP_AP_SIGNAL, + 0 + ); + break; + } + } + } +} + +/** + Write AP start-up signal to wakeup AP. + + @param ApStartupSignalBuffer Pointer to AP wakeup signal +**/ +VOID +WriteStartupSignal ( + IN volatile UINT32 *ApStartupSignalBuffer + ) +{ + *ApStartupSignalBuffer = WAKEUP_AP_SIGNAL; + // + // 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 be called by BSP to wakeup AP. + + @param PeiCpuMpData Pointer to PEI CPU MP Data + @param Broadcast TRUE: Send broadcast IPI to all APs + FALSE: Send IPI to AP by ApicId + @param ProcessorNumber The handle number of specified processor + @param Procedure The function to be invoked by AP + @param ProcedureArgument The argument to be passed into AP function +**/ +VOID +WakeUpAP ( + IN PEI_CPU_MP_DATA *PeiCpuMpData, + IN BOOLEAN Broadcast, + IN UINTN ProcessorNumber, + IN EFI_AP_PROCEDURE Procedure, OPTIONAL + IN VOID *ProcedureArgument OPTIONAL + ) +{ + volatile MP_CPU_EXCHANGE_INFO *ExchangeInfo; + UINTN Index; + + PeiCpuMpData->ApFunction = (UINTN) Procedure; + PeiCpuMpData->ApFunctionArgument = (UINTN) ProcedureArgument; + PeiCpuMpData->FinishedCount = 0; + + ExchangeInfo = PeiCpuMpData->MpCpuExchangeInfo; + ExchangeInfo->Lock = 0; + ExchangeInfo->StackStart = PeiCpuMpData->Buffer; + ExchangeInfo->StackSize = PeiCpuMpData->CpuApStackSize; + ExchangeInfo->BufferStart = PeiCpuMpData->WakeupBuffer; + ExchangeInfo->ModeOffset = PeiCpuMpData->AddressMap.ModeEntryOffset; + ExchangeInfo->Cr3 = AsmReadCr3 (); + ExchangeInfo->CodeSegment = AsmReadCs (); + ExchangeInfo->DataSegment = AsmReadDs (); + ExchangeInfo->CFunction = (UINTN) ApCFunction; + ExchangeInfo->NumApsExecuting = 0; + ExchangeInfo->PeiCpuMpData = PeiCpuMpData; + + // + // Get the BSP's data of GDT and IDT + // + AsmReadGdtr ((IA32_DESCRIPTOR *) &ExchangeInfo->GdtrProfile); + AsmReadIdtr ((IA32_DESCRIPTOR *) &ExchangeInfo->IdtrProfile); + + if (PeiCpuMpData->ApLoopMode == ApInMwaitLoop) { + // + // Get AP target C-state each time when waking up AP, + // for it maybe updated by platform again + // + PeiCpuMpData->ApTargetCState = PcdGet8 (PcdCpuApTargetCstate); + } + + // + // Wakeup APs per AP loop state + // + if (PeiCpuMpData->ApLoopMode == ApInHltLoop || PeiCpuMpData->InitFlag) { + if (Broadcast) { + SendInitSipiSipiAllExcludingSelf ((UINT32) ExchangeInfo->BufferStart); + } else { + SendInitSipiSipi ( + PeiCpuMpData->CpuData[ProcessorNumber].ApicId, + (UINT32) ExchangeInfo->BufferStart + ); + } + } else if ((PeiCpuMpData->ApLoopMode == ApInMwaitLoop) || + (PeiCpuMpData->ApLoopMode == ApInRunLoop)) { + if (Broadcast) { + for (Index = 0; Index < PeiCpuMpData->CpuCount; Index++) { + if (Index != PeiCpuMpData->BspNumber) { + WriteStartupSignal (PeiCpuMpData->CpuData[Index].StartupApSignal); + } + } + } else { + WriteStartupSignal (PeiCpuMpData->CpuData[ProcessorNumber].StartupApSignal); + } + } else { + ASSERT (FALSE); + } + return ; +} + +/** + Get available system memory below 1MB by specified size. + + @param WakeupBufferSize Wakeup buffer size required + + @retval other Return wakeup buffer address below 1MB. + @retval -1 Cannot find free memory below 1MB. +**/ +UINTN +GetWakeupBuffer ( + IN UINTN WakeupBufferSize + ) +{ + EFI_PEI_HOB_POINTERS Hob; + UINTN WakeupBufferStart; + UINTN WakeupBufferEnd; + + // + // Get the HOB list for processing + // + Hob.Raw = GetHobList (); + + // + // Collect memory ranges + // + while (!END_OF_HOB_LIST (Hob)) { + if (Hob.Header->HobType == EFI_HOB_TYPE_RESOURCE_DESCRIPTOR) { + if ((Hob.ResourceDescriptor->PhysicalStart < BASE_1MB) && + (Hob.ResourceDescriptor->ResourceType == EFI_RESOURCE_SYSTEM_MEMORY) && + ((Hob.ResourceDescriptor->ResourceAttribute & + (EFI_RESOURCE_ATTRIBUTE_READ_PROTECTED | + EFI_RESOURCE_ATTRIBUTE_WRITE_PROTECTED | + EFI_RESOURCE_ATTRIBUTE_EXECUTION_PROTECTED + )) == 0) + ) { + // + // Need memory under 1MB to be collected here + // + WakeupBufferEnd = (UINTN) (Hob.ResourceDescriptor->PhysicalStart + Hob.ResourceDescriptor->ResourceLength); + if (WakeupBufferEnd > BASE_1MB) { + // + // Wakeup buffer should be under 1MB + // + WakeupBufferEnd = BASE_1MB; + } + // + // Wakeup buffer should be aligned on 4KB + // + WakeupBufferStart = (WakeupBufferEnd - WakeupBufferSize) & ~(SIZE_4KB - 1); + if (WakeupBufferStart < Hob.ResourceDescriptor->PhysicalStart) { + continue; + } + // + // Create a memory allocation HOB. + // + BuildMemoryAllocationHob ( + WakeupBufferStart, + WakeupBufferSize, + EfiBootServicesData + ); + return WakeupBufferStart; + } + } + // + // Find the next HOB + // + Hob.Raw = GET_NEXT_HOB (Hob); + } + + return (UINTN) -1; +} + +/** + Get available system memory below 1MB by specified size. + + @param PeiCpuMpData Pointer to PEI CPU MP Data +**/ +VOID +BackupAndPrepareWakeupBuffer( + IN PEI_CPU_MP_DATA *PeiCpuMpData + ) +{ + CopyMem ( + (VOID *) PeiCpuMpData->BackupBuffer, + (VOID *) PeiCpuMpData->WakeupBuffer, + PeiCpuMpData->BackupBufferSize + ); + CopyMem ( + (VOID *) PeiCpuMpData->WakeupBuffer, + (VOID *) PeiCpuMpData->AddressMap.RendezvousFunnelAddress, + PeiCpuMpData->AddressMap.RendezvousFunnelSize + ); +} + +/** + Restore wakeup buffer data. + + @param PeiCpuMpData Pointer to PEI CPU MP Data +**/ +VOID +RestoreWakeupBuffer( + IN PEI_CPU_MP_DATA *PeiCpuMpData + ) +{ + CopyMem ((VOID *) PeiCpuMpData->WakeupBuffer, (VOID *) PeiCpuMpData->BackupBuffer, PeiCpuMpData->BackupBufferSize); +} + +/** + This function will get CPU count in the system. + + @param PeiCpuMpData Pointer to PEI CPU MP Data + + @return AP processor count +**/ +UINT32 +CountProcessorNumber ( + IN PEI_CPU_MP_DATA *PeiCpuMpData + ) +{ + // + // Load Microcode on BSP + // + MicrocodeDetect (PeiCpuMpData); + // + // Store BSP's MTRR setting + // + MtrrGetAllMtrrs (&PeiCpuMpData->MtrrTable); + + // + // Only perform AP detection if PcdCpuMaxLogicalProcessorNumber is greater than 1 + // + if (PcdGet32 (PcdCpuMaxLogicalProcessorNumber) > 1) { + // + // Send 1st broadcast IPI to APs to wakeup APs + // + PeiCpuMpData->InitFlag = TRUE; + PeiCpuMpData->X2ApicEnable = FALSE; + WakeUpAP (PeiCpuMpData, TRUE, 0, NULL, NULL); + // + // Wait for AP task to complete and then exit. + // + MicroSecondDelay (PcdGet32 (PcdCpuApInitTimeOutInMicroSeconds)); + PeiCpuMpData->InitFlag = FALSE; + PeiCpuMpData->CpuCount += (UINT32)PeiCpuMpData->MpCpuExchangeInfo->NumApsExecuting; + ASSERT (PeiCpuMpData->CpuCount <= PcdGet32 (PcdCpuMaxLogicalProcessorNumber)); + // + // Wait for all APs finished the initialization + // + while (PeiCpuMpData->FinishedCount < (PeiCpuMpData->CpuCount - 1)) { + CpuPause (); + } + + if (PeiCpuMpData->X2ApicEnable) { + DEBUG ((EFI_D_INFO, "Force x2APIC mode!\n")); + // + // Wakeup all APs to enable x2APIC mode + // + WakeUpAP (PeiCpuMpData, TRUE, 0, ApFuncEnableX2Apic, NULL); + // + // Wait for all known APs finished + // + while (PeiCpuMpData->FinishedCount < (PeiCpuMpData->CpuCount - 1)) { + CpuPause (); + } + // + // Enable x2APIC on BSP + // + SetApicMode (LOCAL_APIC_MODE_X2APIC); + } + DEBUG ((EFI_D_INFO, "APIC MODE is %d\n", GetApicMode ())); + // + // Sort BSP/Aps by CPU APIC ID in ascending order + // + SortApicId (PeiCpuMpData); + } + + DEBUG ((EFI_D_INFO, "CpuMpPei: Find %d processors in system.\n", PeiCpuMpData->CpuCount)); + return PeiCpuMpData->CpuCount; +} + +/** + Prepare for AP wakeup buffer and copy AP reset code into it. + + Get wakeup buffer below 1MB. Allocate memory for CPU MP Data and APs Stack. + + @return Pointer to PEI CPU MP Data +**/ +PEI_CPU_MP_DATA * +PrepareAPStartupVector ( + VOID + ) +{ + EFI_STATUS Status; + UINT32 MaxCpuCount; + PEI_CPU_MP_DATA *PeiCpuMpData; + EFI_PHYSICAL_ADDRESS Buffer; + UINTN BufferSize; + UINTN WakeupBuffer; + UINTN WakeupBufferSize; + MP_ASSEMBLY_ADDRESS_MAP AddressMap; + UINT8 ApLoopMode; + UINT16 MonitorFilterSize; + UINT8 *MonitorBuffer; + UINTN Index; + + AsmGetAddressMap (&AddressMap); + WakeupBufferSize = AddressMap.RendezvousFunnelSize + sizeof (MP_CPU_EXCHANGE_INFO); + WakeupBuffer = GetWakeupBuffer ((WakeupBufferSize + SIZE_4KB - 1) & ~(SIZE_4KB - 1)); + ASSERT (WakeupBuffer != (UINTN) -1); + DEBUG ((EFI_D_INFO, "CpuMpPei: WakeupBuffer = 0x%x\n", WakeupBuffer)); + + // + // Allocate Pages for APs stack, CPU MP Data, backup buffer for wakeup buffer, + // and monitor buffer if required. + // + MaxCpuCount = PcdGet32(PcdCpuMaxLogicalProcessorNumber); + BufferSize = PcdGet32 (PcdCpuApStackSize) * MaxCpuCount + sizeof (PEI_CPU_MP_DATA) + + WakeupBufferSize + sizeof (PEI_CPU_DATA) * MaxCpuCount; + ApLoopMode = GetApLoopMode (&MonitorFilterSize); + BufferSize += MonitorFilterSize * MaxCpuCount; + Status = PeiServicesAllocatePages ( + EfiBootServicesData, + EFI_SIZE_TO_PAGES (BufferSize), + &Buffer + ); + ASSERT_EFI_ERROR (Status); + + PeiCpuMpData = (PEI_CPU_MP_DATA *) (UINTN) (Buffer + PcdGet32 (PcdCpuApStackSize) * MaxCpuCount); + PeiCpuMpData->Buffer = (UINTN) Buffer; + PeiCpuMpData->CpuApStackSize = PcdGet32 (PcdCpuApStackSize); + PeiCpuMpData->WakeupBuffer = WakeupBuffer; + PeiCpuMpData->BackupBuffer = (UINTN)PeiCpuMpData + sizeof (PEI_CPU_MP_DATA); + PeiCpuMpData->BackupBufferSize = WakeupBufferSize; + PeiCpuMpData->MpCpuExchangeInfo = (MP_CPU_EXCHANGE_INFO *) (UINTN) (WakeupBuffer + AddressMap.RendezvousFunnelSize); + + PeiCpuMpData->CpuCount = 1; + PeiCpuMpData->BspNumber = 0; + PeiCpuMpData->CpuData = (PEI_CPU_DATA *) (PeiCpuMpData->BackupBuffer + + PeiCpuMpData->BackupBufferSize); + PeiCpuMpData->CpuData[0].ApicId = GetInitialApicId (); + PeiCpuMpData->CpuData[0].Health.Uint32 = 0; + PeiCpuMpData->EndOfPeiFlag = FALSE; + InitializeSpinLock(&PeiCpuMpData->MpLock); + SaveVolatileRegisters (&PeiCpuMpData->CpuData[0].VolatileRegisters); + CopyMem (&PeiCpuMpData->AddressMap, &AddressMap, sizeof (MP_ASSEMBLY_ADDRESS_MAP)); + // + // Initialize AP loop mode + // + PeiCpuMpData->ApLoopMode = ApLoopMode; + DEBUG ((EFI_D_INFO, "AP Loop Mode is %d\n", PeiCpuMpData->ApLoopMode)); + MonitorBuffer = (UINT8 *)(PeiCpuMpData->CpuData + MaxCpuCount); + if (PeiCpuMpData->ApLoopMode != ApInHltLoop) { + // + // Set up APs wakeup signal buffer + // + for (Index = 0; Index < MaxCpuCount; Index++) { + PeiCpuMpData->CpuData[Index].StartupApSignal = + (UINT32 *)(MonitorBuffer + MonitorFilterSize * Index); + } + } + // + // Backup original data and copy AP reset code in it + // + BackupAndPrepareWakeupBuffer(PeiCpuMpData); + + return PeiCpuMpData; +} + +/** + Notify function on End Of Pei PPI. + + On S3 boot, this function will restore wakeup buffer data. + On normal boot, this function will flag wakeup buffer to be un-used type. + + @param PeiServices The pointer to the PEI Services Table. + @param NotifyDescriptor Address of the notification descriptor data structure. + @param Ppi Address of the PPI that was installed. + + @retval EFI_SUCCESS When everything is OK. + +**/ +EFI_STATUS +EFIAPI +CpuMpEndOfPeiCallback ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor, + IN VOID *Ppi + ) +{ + EFI_STATUS Status; + EFI_BOOT_MODE BootMode; + PEI_CPU_MP_DATA *PeiCpuMpData; + EFI_PEI_HOB_POINTERS Hob; + EFI_HOB_MEMORY_ALLOCATION *MemoryHob; + + DEBUG ((EFI_D_INFO, "CpuMpPei: CpuMpEndOfPeiCallback () invoked\n")); + + Status = PeiServicesGetBootMode (&BootMode); + ASSERT_EFI_ERROR (Status); + + PeiCpuMpData = GetMpHobData (); + ASSERT (PeiCpuMpData != NULL); + + if (BootMode != BOOT_ON_S3_RESUME) { + // + // Get the HOB list for processing + // + Hob.Raw = GetHobList (); + // + // Collect memory ranges + // + while (!END_OF_HOB_LIST (Hob)) { + if (Hob.Header->HobType == EFI_HOB_TYPE_MEMORY_ALLOCATION) { + MemoryHob = Hob.MemoryAllocation; + if(MemoryHob->AllocDescriptor.MemoryBaseAddress == PeiCpuMpData->WakeupBuffer) { + // + // Flag this HOB type to un-used + // + GET_HOB_TYPE (Hob) = EFI_HOB_TYPE_UNUSED; + break; + } + } + Hob.Raw = GET_NEXT_HOB (Hob); + } + } else { + RestoreWakeupBuffer (PeiCpuMpData); + PeiCpuMpData->EndOfPeiFlag = TRUE; + } + return EFI_SUCCESS; +} + +/** + The Entry point of the MP CPU PEIM. + + This function will wakeup APs and collect CPU AP count and install the + Mp Service Ppi. + + @param FileHandle Handle of the file being invoked. + @param PeiServices Describes the list of possible PEI Services. + + @retval EFI_SUCCESS MpServicePpi is installed successfully. + +**/ +EFI_STATUS +EFIAPI +CpuMpPeimInit ( + IN EFI_PEI_FILE_HANDLE FileHandle, + IN CONST EFI_PEI_SERVICES **PeiServices + ) +{ + EFI_STATUS Status; + PEI_CPU_MP_DATA *PeiCpuMpData; + EFI_VECTOR_HANDOFF_INFO *VectorInfo; + EFI_PEI_VECTOR_HANDOFF_INFO_PPI *VectorHandoffInfoPpi; + + // + // Get Vector Hand-off Info PPI + // + VectorInfo = NULL; + Status = PeiServicesLocatePpi ( + &gEfiVectorHandoffInfoPpiGuid, + 0, + NULL, + (VOID **)&VectorHandoffInfoPpi + ); + if (Status == EFI_SUCCESS) { + VectorInfo = VectorHandoffInfoPpi->Info; + } + Status = InitializeCpuExceptionHandlers (VectorInfo); + ASSERT_EFI_ERROR (Status); + // + // Get wakeup buffer and copy AP reset code in it + // + PeiCpuMpData = PrepareAPStartupVector (); + // + // Count processor number and collect processor information + // + CountProcessorNumber (PeiCpuMpData); + // + // Build location of PEI CPU MP DATA buffer in HOB + // + BuildGuidDataHob ( + &gEfiCallerIdGuid, + (VOID *)&PeiCpuMpData, + sizeof(UINT64) + ); + // + // Update and publish CPU BIST information + // + CollectBistDataFromPpi (PeiServices, PeiCpuMpData); + // + // register an event for EndOfPei + // + Status = PeiServicesNotifyPpi (&mNotifyList); + ASSERT_EFI_ERROR (Status); + // + // Install CPU MP PPI + // + Status = PeiServicesInstallPpi(&mPeiCpuMpPpiDesc); + ASSERT_EFI_ERROR (Status); + + return Status; +} diff --git a/Core/UefiCpuPkg/CpuMpPei/CpuMpPei.h b/Core/UefiCpuPkg/CpuMpPei/CpuMpPei.h new file mode 100644 index 0000000000..b2e578b96a --- /dev/null +++ b/Core/UefiCpuPkg/CpuMpPei/CpuMpPei.h @@ -0,0 +1,315 @@ +/** @file + Definitions to install Multiple Processor PPI. + + Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _CPU_MP_PEI_H_ +#define _CPU_MP_PEI_H_ + +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Microcode.h" + +// +// AP state +// +typedef enum { + CpuStateIdle, + CpuStateBusy, + CpuStateDisabled +} CPU_STATE; + +#define WAKEUP_AP_SIGNAL SIGNATURE_32 ('S', 'T', 'A', 'P') + +typedef enum { + ApInHltLoop = 1, + ApInMwaitLoop = 2, + ApInRunLoop = 3 +} AP_LOOP_MODE; + +// +// AP reset code information +// +typedef struct { + UINT8 *RendezvousFunnelAddress; + UINTN ModeEntryOffset; + UINTN RendezvousFunnelSize; +} MP_ASSEMBLY_ADDRESS_MAP; + +// +// CPU exchange information for switch BSP +// +typedef struct { + UINT8 State; // offset 0 + UINTN StackPointer; // offset 4 / 8 + IA32_DESCRIPTOR Gdtr; // offset 8 / 16 + IA32_DESCRIPTOR Idtr; // offset 14 / 26 +} CPU_EXCHANGE_ROLE_INFO; + +typedef struct _PEI_CPU_MP_DATA PEI_CPU_MP_DATA; + +#pragma pack(1) + +// +// MP CPU exchange information for AP reset code +// This structure is required to be packed because fixed field offsets +// into this structure are used in assembly code in this module +// +typedef struct { + UINTN Lock; + UINTN StackStart; + UINTN StackSize; + UINTN CFunction; + IA32_DESCRIPTOR GdtrProfile; + IA32_DESCRIPTOR IdtrProfile; + UINTN BufferStart; + UINTN ModeOffset; + UINTN NumApsExecuting; + UINTN CodeSegment; + UINTN DataSegment; + UINTN Cr3; + PEI_CPU_MP_DATA *PeiCpuMpData; +} MP_CPU_EXCHANGE_INFO; + +#pragma pack() + +typedef struct { + UINTN Cr0; + UINTN Cr3; + UINTN Cr4; + UINTN Dr0; + UINTN Dr1; + UINTN Dr2; + UINTN Dr3; + UINTN Dr6; + UINTN Dr7; +} CPU_VOLATILE_REGISTERS; + +typedef struct { + volatile UINT32 *StartupApSignal; + UINT32 ApicId; + EFI_HEALTH_FLAGS Health; + CPU_STATE State; + BOOLEAN CpuHealthy; + CPU_VOLATILE_REGISTERS VolatileRegisters; +} PEI_CPU_DATA; + +// +// PEI CPU MP Data save in memory +// +struct _PEI_CPU_MP_DATA { + SPIN_LOCK MpLock; + UINT32 CpuCount; + UINT32 BspNumber; + UINTN Buffer; + UINTN CpuApStackSize; + MP_ASSEMBLY_ADDRESS_MAP AddressMap; + UINTN WakeupBuffer; + UINTN BackupBuffer; + UINTN BackupBufferSize; + UINTN ApFunction; + UINTN ApFunctionArgument; + volatile UINT32 FinishedCount; + BOOLEAN EndOfPeiFlag; + BOOLEAN InitFlag; + BOOLEAN X2ApicEnable; + CPU_EXCHANGE_ROLE_INFO BSPInfo; + CPU_EXCHANGE_ROLE_INFO APInfo; + MTRR_SETTINGS MtrrTable; + UINT8 ApLoopMode; + UINT8 ApTargetCState; + PEI_CPU_DATA *CpuData; + volatile MP_CPU_EXCHANGE_INFO *MpCpuExchangeInfo; +}; +extern EFI_PEI_PPI_DESCRIPTOR mPeiCpuMpPpiDesc; + + +/** + Assembly code to get starting address and size of the rendezvous entry for APs. + Information for fixing a jump instruction in the code is also returned. + + @param AddressMap Output buffer for address map information. +**/ +VOID +EFIAPI +AsmGetAddressMap ( + OUT MP_ASSEMBLY_ADDRESS_MAP *AddressMap + ); + +/** + Assembly code to load GDT table and update segment accordingly. + + @param Gdtr Pointer to GDT descriptor +**/ +VOID +EFIAPI +AsmInitializeGdt ( + IN IA32_DESCRIPTOR *Gdtr + ); + +/** + Get available system memory below 1MB by specified size. + + @param PeiCpuMpData Pointer to PEI CPU MP Data +**/ +VOID +BackupAndPrepareWakeupBuffer( + IN PEI_CPU_MP_DATA *PeiCpuMpData + ); + +/** + Restore wakeup buffer data. + + @param PeiCpuMpData Pointer to PEI CPU MP Data +**/ +VOID +RestoreWakeupBuffer( + IN PEI_CPU_MP_DATA *PeiCpuMpData + ); + +/** + Notify function on End Of Pei PPI. + + On S3 boot, this function will restore wakeup buffer data. + On normal boot, this function will flag wakeup buffer to be un-used type. + + @param PeiServices The pointer to the PEI Services Table. + @param NotifyDescriptor Address of the notification descriptor data structure. + @param Ppi Address of the PPI that was installed. + + @retval EFI_SUCCESS When everything is OK. + +**/ +EFI_STATUS +EFIAPI +CpuMpEndOfPeiCallback ( + IN EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor, + IN VOID *Ppi + ); + +/** + This function will be called by BSP to wakeup AP. + + @param PeiCpuMpData Pointer to PEI CPU MP Data + @param Broadcast TRUE: Send broadcast IPI to all APs + FALSE: Send IPI to AP by ApicId + @param ProcessorNumber The handle number of specified processor + @param Procedure The function to be invoked by AP + @param ProcedureArgument The argument to be passed into AP function +**/ +VOID +WakeUpAP ( + IN PEI_CPU_MP_DATA *PeiCpuMpData, + IN BOOLEAN Broadcast, + IN UINTN ProcessorNumber, + IN EFI_AP_PROCEDURE Procedure, OPTIONAL + IN VOID *ProcedureArgument OPTIONAL + ); + +/** + Get CPU MP Data pointer from the Guided HOB. + + @return Pointer to Pointer to PEI CPU MP Data +**/ +PEI_CPU_MP_DATA * +GetMpHobData ( + VOID + ); + +/** + Find the current Processor number by APIC ID. + + @param PeiCpuMpData Pointer to PEI CPU MP Data + @param ProcessorNumber Return the pocessor number found + + @retval EFI_SUCCESS ProcessorNumber is found and returned. + @retval EFI_NOT_FOUND ProcessorNumber is not found. +**/ +EFI_STATUS +GetProcessorNumber ( + IN PEI_CPU_MP_DATA *PeiCpuMpData, + OUT UINTN *ProcessorNumber + ); + +/** + 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 + ); + +/** + Implementation of the PlatformInformation2 service in EFI_SEC_PLATFORM_INFORMATION2_PPI. + + @param PeiServices The pointer to the PEI Services Table. + @param StructureSize The pointer to the variable describing size of the input buffer. + @param PlatformInformationRecord2 The pointer to the EFI_SEC_PLATFORM_INFORMATION_RECORD2. + + @retval EFI_SUCCESS The data was successfully returned. + @retval EFI_BUFFER_TOO_SMALL The buffer was too small. The current buffer size needed to + hold the record is returned in StructureSize. + +**/ +EFI_STATUS +EFIAPI +SecPlatformInformation2 ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN OUT UINT64 *StructureSize, + OUT EFI_SEC_PLATFORM_INFORMATION_RECORD2 *PlatformInformationRecord2 + ); + +/** + Detect whether specified processor can find matching microcode patch and load it. + + @param PeiCpuMpData Pointer to PEI CPU MP Data +**/ +VOID +MicrocodeDetect ( + IN PEI_CPU_MP_DATA *PeiCpuMpData + ); + +#endif diff --git a/Core/UefiCpuPkg/CpuMpPei/CpuMpPei.inf b/Core/UefiCpuPkg/CpuMpPei/CpuMpPei.inf new file mode 100644 index 0000000000..445caf98f2 --- /dev/null +++ b/Core/UefiCpuPkg/CpuMpPei/CpuMpPei.inf @@ -0,0 +1,96 @@ +## @file +# CPU driver installs CPU PI Multi-processor PPI. +# +# Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.
+# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = CpuMpPei + MODULE_UNI_FILE = CpuMpPei.uni + FILE_GUID = EDADEB9D-DDBA-48BD-9D22-C1C169C8C5C6 + MODULE_TYPE = PEIM + VERSION_STRING = 1.0 + ENTRY_POINT = CpuMpPeimInit + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Sources] + CpuMpPei.h + CpuMpPei.c + CpuBist.c + Microcode.h + Microcode.c + PeiMpServices.h + PeiMpServices.c + +[Sources.IA32] + Ia32/MpEqu.inc + Ia32/MpFuncs.asm | MSFT + Ia32/MpFuncs.asm | INTEL + Ia32/MpFuncs.nasm | GCC + +[Sources.X64] + X64/MpEqu.inc + X64/MpFuncs.asm | MSFT + X64/MpFuncs.asm | INTEL + X64/MpFuncs.nasm | GCC + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + UefiCpuPkg/UefiCpuPkg.dec + +[LibraryClasses] + BaseLib + BaseMemoryLib + DebugLib + HobLib + LocalApicLib + MtrrLib + PcdLib + PeimEntryPoint + PeiServicesLib + ReportStatusCodeLib + SynchronizationLib + TimerLib + UefiCpuLib + CpuLib + CpuExceptionHandlerLib + +[Ppis] + gEfiPeiMpServicesPpiGuid ## PRODUCES + gEfiEndOfPeiSignalPpiGuid ## NOTIFY + gEfiSecPlatformInformationPpiGuid ## SOMETIMES_CONSUMES + ## SOMETIMES_CONSUMES + ## SOMETIMES_PRODUCES + gEfiSecPlatformInformation2PpiGuid + gEfiVectorHandoffInfoPpiGuid ## SOMETIMES_CONSUMES + +[Pcd] + gUefiCpuPkgTokenSpaceGuid.PcdCpuMaxLogicalProcessorNumber ## CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdCpuApInitTimeOutInMicroSeconds ## CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdCpuApStackSize ## CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdCpuMicrocodePatchAddress ## CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdCpuMicrocodePatchRegionSize ## CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdCpuApLoopMode ## CONSUMES + gUefiCpuPkgTokenSpaceGuid.PcdCpuApTargetCstate ## SOMETIMES_CONSUMES + +[Depex] + gEfiPeiMemoryDiscoveredPpiGuid + +[UserExtensions.TianoCore."ExtraFiles"] + CpuMpPeiExtra.uni + diff --git a/Core/UefiCpuPkg/CpuMpPei/CpuMpPei.uni b/Core/UefiCpuPkg/CpuMpPei/CpuMpPei.uni new file mode 100644 index 0000000000..d458d0d624 --- /dev/null +++ b/Core/UefiCpuPkg/CpuMpPei/CpuMpPei.uni @@ -0,0 +1,22 @@ +// /** @file +// CPU driver installs CPU PI Multi-processor PPI. +// +// CPU driver installs CPU PI Multi-processor PPI. +// +// Copyright (c) 2015, Intel Corporation. All rights reserved.
+// +// This program and the accompanying materials +// are licensed and made available under the terms and conditions of the BSD License +// which accompanies this distribution. The full text of the license may be found at +// http://opensource.org/licenses/bsd-license.php +// +// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "Installs CPU PI Multi-processor PPI" + +#string STR_MODULE_DESCRIPTION #language en-US "CPU driver installs CPU PI Multi-processor PPI." + diff --git a/Core/UefiCpuPkg/CpuMpPei/CpuMpPeiExtra.uni b/Core/UefiCpuPkg/CpuMpPei/CpuMpPeiExtra.uni new file mode 100644 index 0000000000..cf832fb80b --- /dev/null +++ b/Core/UefiCpuPkg/CpuMpPei/CpuMpPeiExtra.uni @@ -0,0 +1,20 @@ +// /** @file +// CpuMpPei Localized Strings and Content +// +// Copyright (c) 2015, Intel Corporation. All rights reserved.
+// +// This program and the accompanying materials +// are licensed and made available under the terms and conditions of the BSD License +// which accompanies this distribution. The full text of the license may be found at +// http://opensource.org/licenses/bsd-license.php +// +// THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +// WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +// +// **/ + +#string STR_PROPERTIES_MODULE_NAME +#language en-US +"CPU Multi-processor PEIM Driver" + + diff --git a/Core/UefiCpuPkg/CpuMpPei/Ia32/MpEqu.inc b/Core/UefiCpuPkg/CpuMpPei/Ia32/MpEqu.inc new file mode 100644 index 0000000000..773eab3a4d --- /dev/null +++ b/Core/UefiCpuPkg/CpuMpPei/Ia32/MpEqu.inc @@ -0,0 +1,39 @@ +;------------------------------------------------------------------------------ ; +; Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.
+; This program and the accompanying materials +; are licensed and made available under the terms and conditions of the BSD License +; which accompanies this distribution. The full text of the license may be found at +; http://opensource.org/licenses/bsd-license.php. +; +; THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +; WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +; +; Module Name: +; +; MpEqu.inc +; +; Abstract: +; +; This is the equates file for Multiple Processor support +; +;------------------------------------------------------------------------------- + +VacantFlag equ 00h +NotVacantFlag equ 0ffh + +CPU_SWITCH_STATE_IDLE equ 0 +CPU_SWITCH_STATE_STORED equ 1 +CPU_SWITCH_STATE_LOADED equ 2 + +LockLocation equ (RendezvousFunnelProcEnd - RendezvousFunnelProcStart) +StackStartAddressLocation equ LockLocation + 04h +StackSizeLocation equ LockLocation + 08h +ApProcedureLocation equ LockLocation + 0Ch +GdtrLocation equ LockLocation + 10h +IdtrLocation equ LockLocation + 16h +BufferStartLocation equ LockLocation + 1Ch +ModeOffsetLocation equ LockLocation + 20h +NumApsExecutingLoction equ LockLocation + 24h +CodeSegmentLocation equ LockLocation + 28h +DataSegmentLocation equ LockLocation + 2Ch + diff --git a/Core/UefiCpuPkg/CpuMpPei/Ia32/MpFuncs.asm b/Core/UefiCpuPkg/CpuMpPei/Ia32/MpFuncs.asm new file mode 100644 index 0000000000..27e16c66f4 --- /dev/null +++ b/Core/UefiCpuPkg/CpuMpPei/Ia32/MpFuncs.asm @@ -0,0 +1,250 @@ +;------------------------------------------------------------------------------ ; +; Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.
+; This program and the accompanying materials +; are licensed and made available under the terms and conditions of the BSD License +; which accompanies this distribution. The full text of the license may be found at +; http://opensource.org/licenses/bsd-license.php. +; +; THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +; WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +; +; Module Name: +; +; MpFuncs32.asm +; +; Abstract: +; +; This is the assembly code for MP support +; +;------------------------------------------------------------------------------- + +.686p +.model flat + +include MpEqu.inc +InitializeFloatingPointUnits PROTO C + +.code + +;------------------------------------------------------------------------------------- +;RendezvousFunnelProc procedure follows. All APs execute their procedure. This +;procedure serializes all the AP processors through an Init sequence. It must be +;noted that APs arrive here very raw...ie: real mode, no stack. +;ALSO THIS PROCEDURE IS EXECUTED BY APs ONLY ON 16 BIT MODE. HENCE THIS PROC +;IS IN MACHINE CODE. +;------------------------------------------------------------------------------------- +RendezvousFunnelProc PROC PUBLIC +RendezvousFunnelProcStart:: +; At this point CS = 0x(vv00) and ip= 0x0. +; Save BIST information to ebp firstly + db 66h, 08bh, 0e8h ; mov ebp, eax ; save BIST information + + db 8ch,0c8h ; mov ax, cs + db 8eh,0d8h ; mov ds, ax + db 8eh,0c0h ; mov es, ax + db 8eh,0d0h ; mov ss, ax + db 33h,0c0h ; xor ax, ax + db 8eh,0e0h ; mov fs, ax + db 8eh,0e8h ; mov gs, ax + + db 0BEh ; opcode of mov si, mem16 + dw BufferStartLocation ; mov si, BufferStartLocation + db 66h, 8Bh, 1Ch ; mov ebx, dword ptr [si] + + db 0BEh ; opcode of mov si, mem16 + dw ModeOffsetLocation ; mov si, ModeOffsetLocation + db 66h, 8Bh, 04h ; mov eax, [si] + db 0BEh ; opcode of mov si, mem16 + dw CodeSegmentLocation ; mov si, CodeSegmentLocation + db 66h, 8Bh, 14h ; mov edx, [si] + db 89h, 0C7h ; mov di, ax + db 83h, 0EFh, 02h ; sub di, 02h + db 89h, 15h ; mov [di], dx + db 83h, 0EFh, 04h ; sub di, 04h + db 66h, 01h, 0D8h ; add eax, ebx + db 66h, 89h, 05h ; mov [di], eax + + db 0BEh ; opcode of mov si, mem16 + dw DataSegmentLocation ; mov si, DataSegmentLocation + db 66h, 8Bh, 14h ; mov edx, [si] + + db 0BEh ; opcode of mov si, mem16 + dw GdtrLocation ; mov si, GdtrLocation + db 66h ; db 66h + db 2Eh, 0Fh, 01h, 14h ; lgdt fword ptr cs:[si] + + db 0BEh + dw IdtrLocation ; mov si, IdtrLocation + db 66h ; db 66h + db 2Eh,0Fh, 01h, 1Ch ; lidt fword ptr cs:[si] + + db 33h, 0C0h ; xor ax, ax + db 8Eh, 0D8h ; mov ds, ax + + db 0Fh, 20h, 0C0h ; mov eax, cr0 ;Get control register 0 + db 66h, 83h, 0C8h, 03h ; or eax, 000000003h ;Set PE bit (bit #0) & MP + db 0Fh, 22h, 0C0h ; mov cr0, eax + + db 66h, 67h, 0EAh ; far jump + dd 0h ; 32-bit offset + dw 0h ; 16-bit selector + +Flat32Start:: ; protected mode entry point + mov ds, dx + mov es, dx + mov fs, dx + mov gs, dx + mov ss, dx + + mov esi, ebx + mov edi, esi + add edi, LockLocation + mov eax, NotVacantFlag + +TestLock: + xchg dword ptr [edi], eax + cmp eax, NotVacantFlag + jz TestLock + + mov edi, esi + add edi, NumApsExecutingLoction + inc dword ptr [edi] + mov ebx, dword ptr [edi] + +ProgramStack: + mov edi, esi + add edi, StackSizeLocation + mov eax, dword ptr [edi] + mov edi, esi + add edi, StackStartAddressLocation + add eax, dword ptr [edi] + mov esp, eax + mov dword ptr [edi], eax + +Releaselock: + mov eax, VacantFlag + mov edi, esi + add edi, LockLocation + xchg dword ptr [edi], eax + +CProcedureInvoke: + push ebp ; push BIST data at top of AP stack + xor ebp, ebp ; clear ebp for call stack trace + push ebp + mov ebp, esp + + mov eax, InitializeFloatingPointUnits + call eax ; Call assembly function to initialize FPU per UEFI spec + + push ebx ; Push NumApsExecuting + mov eax, esi + add eax, LockLocation + push eax ; push address of exchange info data buffer + + mov edi, esi + add edi, ApProcedureLocation + mov eax, dword ptr [edi] + + call eax ; invoke C function + + jmp $ ; never reach here + +RendezvousFunnelProc ENDP +RendezvousFunnelProcEnd:: + +;------------------------------------------------------------------------------------- +; AsmGetAddressMap (&AddressMap); +;------------------------------------------------------------------------------------- +AsmGetAddressMap PROC near C PUBLIC + pushad + mov ebp,esp + + mov ebx, dword ptr [ebp+24h] + mov dword ptr [ebx], RendezvousFunnelProcStart + mov dword ptr [ebx + 4h], Flat32Start - RendezvousFunnelProcStart + mov dword ptr [ebx + 8h], RendezvousFunnelProcEnd - RendezvousFunnelProcStart + + popad + ret +AsmGetAddressMap ENDP + +PAUSE32 MACRO + DB 0F3h + DB 090h + ENDM + +;------------------------------------------------------------------------------------- +;AsmExchangeRole procedure follows. This procedure executed by current BSP, that is +;about to become an AP. It switches it'stack with the current AP. +;AsmExchangeRole (IN CPU_EXCHANGE_INFO *MyInfo, IN CPU_EXCHANGE_INFO *OthersInfo); +;------------------------------------------------------------------------------------- +AsmExchangeRole PROC near C PUBLIC + ; DO NOT call other functions in this function, since 2 CPU may use 1 stack + ; at the same time. If 1 CPU try to call a function, stack will be corrupted. + pushad + mov ebp,esp + + ; esi contains MyInfo pointer + mov esi, dword ptr [ebp+24h] + + ; edi contains OthersInfo pointer + mov edi, dword ptr [ebp+28h] + + ;Store EFLAGS, GDTR and IDTR register to stack + pushfd + mov eax, cr4 + push eax ; push cr4 firstly + mov eax, cr0 + push eax + + sgdt fword ptr [esi+8] + sidt fword ptr [esi+14] + + ; Store the its StackPointer + mov dword ptr [esi+4],esp + + ; update its switch state to STORED + mov byte ptr [esi], CPU_SWITCH_STATE_STORED + +WaitForOtherStored: + ; wait until the other CPU finish storing its state + cmp byte ptr [edi], CPU_SWITCH_STATE_STORED + jz OtherStored + PAUSE32 + jmp WaitForOtherStored + +OtherStored: + ; Since another CPU already stored its state, load them + ; load GDTR value + lgdt fword ptr [edi+8] + + ; load IDTR value + lidt fword ptr [edi+14] + + ; load its future StackPointer + mov esp, dword ptr [edi+4] + + ; update the other CPU's switch state to LOADED + mov byte ptr [edi], CPU_SWITCH_STATE_LOADED + +WaitForOtherLoaded: + ; wait until the other CPU finish loading new state, + ; otherwise the data in stack may corrupt + cmp byte ptr [esi], CPU_SWITCH_STATE_LOADED + jz OtherLoaded + PAUSE32 + jmp WaitForOtherLoaded + +OtherLoaded: + ; since the other CPU already get the data it want, leave this procedure + pop eax + mov cr0, eax + pop eax + mov cr4, eax + popfd + + popad + ret +AsmExchangeRole ENDP + +END diff --git a/Core/UefiCpuPkg/CpuMpPei/Ia32/MpFuncs.nasm b/Core/UefiCpuPkg/CpuMpPei/Ia32/MpFuncs.nasm new file mode 100644 index 0000000000..0852a5bc84 --- /dev/null +++ b/Core/UefiCpuPkg/CpuMpPei/Ia32/MpFuncs.nasm @@ -0,0 +1,229 @@ +;------------------------------------------------------------------------------ ; +; Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.
+; This program and the accompanying materials +; are licensed and made available under the terms and conditions of the BSD License +; which accompanies this distribution. The full text of the license may be found at +; http://opensource.org/licenses/bsd-license.php. +; +; THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +; WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +; +; Module Name: +; +; MpFuncs.nasm +; +; Abstract: +; +; This is the assembly code for MP support +; +;------------------------------------------------------------------------------- + +%include "MpEqu.inc" +extern ASM_PFX(InitializeFloatingPointUnits) + +SECTION .text + +;------------------------------------------------------------------------------------- +;RendezvousFunnelProc procedure follows. All APs execute their procedure. This +;procedure serializes all the AP processors through an Init sequence. It must be +;noted that APs arrive here very raw...ie: real mode, no stack. +;ALSO THIS PROCEDURE IS EXECUTED BY APs ONLY ON 16 BIT MODE. HENCE THIS PROC +;IS IN MACHINE CODE. +;------------------------------------------------------------------------------------- +global ASM_PFX(RendezvousFunnelProc) +ASM_PFX(RendezvousFunnelProc): +RendezvousFunnelProcStart: +; At this point CS = 0x(vv00) and ip= 0x0. +BITS 16 + mov ebp, eax ; save BIST information + + mov ax, cs + mov ds, ax + mov es, ax + mov ss, ax + xor ax, ax + mov fs, ax + mov gs, ax + + mov si, BufferStartLocation + mov ebx, [si] + + mov si, ModeOffsetLocation + mov eax, [si] + mov si, CodeSegmentLocation + mov edx, [si] + mov di, ax + sub di, 02h + mov [di], dx + sub di, 04h + add eax, ebx + mov [di],eax + + mov si, DataSegmentLocation + mov edx, [si] + + mov si, GdtrLocation +o32 lgdt [cs:si] + + mov si, IdtrLocation +o32 lidt [cs:si] + + xor ax, ax + mov ds, ax + + mov eax, cr0 ;Get control register 0 + or eax, 000000003h ;Set PE bit (bit #0) & MP + mov cr0, eax + + jmp 0:strict dword 0 ; far jump to protected mode +BITS 32 +Flat32Start: ; protected mode entry point + mov ds, dx + mov es, dx + mov fs, dx + mov gs, dx + mov ss, dx + + mov esi, ebx + mov edi, esi + add edi, LockLocation + mov eax, NotVacantFlag + +TestLock: + xchg [edi], eax + cmp eax, NotVacantFlag + jz TestLock + + mov edi, esi + add edi, NumApsExecutingLoction + inc dword [edi] + mov ebx, [edi] + +ProgramStack: + mov edi, esi + add edi, StackSizeLocation + mov eax, [edi] + mov edi, esi + add edi, StackStartAddressLocation + add eax, [edi] + mov esp, eax + mov [edi], eax + +Releaselock: + mov eax, VacantFlag + mov edi, esi + add edi, LockLocation + xchg [edi], eax + +CProcedureInvoke: + push ebp ; push BIST data at top of AP stack + xor ebp, ebp ; clear ebp for call stack trace + push ebp + mov ebp, esp + + mov eax, ASM_PFX(InitializeFloatingPointUnits) + call eax ; Call assembly function to initialize FPU per UEFI spec + + push ebx ; Push NumApsExecuting + mov eax, esi + add eax, LockLocation + push eax ; push address of exchange info data buffer + + mov edi, esi + add edi, ApProcedureLocation + mov eax, [edi] + + call eax ; invoke C function + + jmp $ ; never reach here +RendezvousFunnelProcEnd: + +;------------------------------------------------------------------------------------- +; AsmGetAddressMap (&AddressMap); +;------------------------------------------------------------------------------------- +global ASM_PFX(AsmGetAddressMap) +ASM_PFX(AsmGetAddressMap): + pushad + mov ebp,esp + + mov ebx, [ebp + 24h] + mov dword [ebx], RendezvousFunnelProcStart + mov dword [ebx + 4h], Flat32Start - RendezvousFunnelProcStart + mov dword [ebx + 8h], RendezvousFunnelProcEnd - RendezvousFunnelProcStart + + popad + ret + +;------------------------------------------------------------------------------------- +;AsmExchangeRole procedure follows. This procedure executed by current BSP, that is +;about to become an AP. It switches it'stack with the current AP. +;AsmExchangeRole (IN CPU_EXCHANGE_INFO *MyInfo, IN CPU_EXCHANGE_INFO *OthersInfo); +;------------------------------------------------------------------------------------- +global ASM_PFX(AsmExchangeRole) +ASM_PFX(AsmExchangeRole): + ; DO NOT call other functions in this function, since 2 CPU may use 1 stack + ; at the same time. If 1 CPU try to call a function, stack will be corrupted. + pushad + mov ebp,esp + + ; esi contains MyInfo pointer + mov esi, [ebp + 24h] + + ; edi contains OthersInfo pointer + mov edi, [ebp + 28h] + + ;Store EFLAGS, GDTR and IDTR register to stack + pushfd + mov eax, cr4 + push eax ; push cr4 firstly + mov eax, cr0 + push eax + + sgdt [esi + 8] + sidt [esi + 14] + + ; Store the its StackPointer + mov [esi + 4],esp + + ; update its switch state to STORED + mov byte [esi], CPU_SWITCH_STATE_STORED + +WaitForOtherStored: + ; wait until the other CPU finish storing its state + cmp byte [edi], CPU_SWITCH_STATE_STORED + jz OtherStored + pause + jmp WaitForOtherStored + +OtherStored: + ; Since another CPU already stored its state, load them + ; load GDTR value + lgdt [edi + 8] + + ; load IDTR value + lidt [edi + 14] + + ; load its future StackPointer + mov esp, [edi + 4] + + ; update the other CPU's switch state to LOADED + mov byte [edi], CPU_SWITCH_STATE_LOADED + +WaitForOtherLoaded: + ; wait until the other CPU finish loading new state, + ; otherwise the data in stack may corrupt + cmp byte [esi], CPU_SWITCH_STATE_LOADED + jz OtherLoaded + pause + jmp WaitForOtherLoaded + +OtherLoaded: + ; since the other CPU already get the data it want, leave this procedure + pop eax + mov cr0, eax + pop eax + mov cr4, eax + popfd + + popad + ret diff --git a/Core/UefiCpuPkg/CpuMpPei/Microcode.c b/Core/UefiCpuPkg/CpuMpPei/Microcode.c new file mode 100644 index 0000000000..f91f658d0a --- /dev/null +++ b/Core/UefiCpuPkg/CpuMpPei/Microcode.c @@ -0,0 +1,213 @@ +/** @file + Implementation of loading microcode on processors. + + Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "CpuMpPei.h" + +/** + Get microcode update signature of currently loaded microcode update. + + @return Microcode signature. + +**/ +UINT32 +GetCurrentMicrocodeSignature ( + VOID + ) +{ + UINT64 Signature; + + AsmWriteMsr64 (EFI_MSR_IA32_BIOS_SIGN_ID, 0); + AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, NULL, NULL); + Signature = AsmReadMsr64 (EFI_MSR_IA32_BIOS_SIGN_ID); + return (UINT32) RShiftU64 (Signature, 32); +} + +/** + Detect whether specified processor can find matching microcode patch and load it. + + @param PeiCpuMpData Pointer to PEI CPU MP Data +**/ +VOID +MicrocodeDetect ( + IN PEI_CPU_MP_DATA *PeiCpuMpData + ) +{ + UINT64 MicrocodePatchAddress; + UINT64 MicrocodePatchRegionSize; + UINT32 ExtendedTableLength; + UINT32 ExtendedTableCount; + EFI_CPU_MICROCODE_EXTENDED_TABLE *ExtendedTable; + EFI_CPU_MICROCODE_EXTENDED_TABLE_HEADER *ExtendedTableHeader; + EFI_CPU_MICROCODE_HEADER *MicrocodeEntryPoint; + UINTN MicrocodeEnd; + UINTN Index; + UINT8 PlatformId; + UINT32 RegEax; + UINT32 CurrentRevision; + UINT32 LatestRevision; + UINTN TotalSize; + UINT32 CheckSum32; + BOOLEAN CorrectMicrocode; + MICROCODE_INFO MicrocodeInfo; + + ZeroMem (&MicrocodeInfo, sizeof (MICROCODE_INFO)); + MicrocodePatchAddress = PcdGet64 (PcdCpuMicrocodePatchAddress); + MicrocodePatchRegionSize = PcdGet64 (PcdCpuMicrocodePatchRegionSize); + if (MicrocodePatchRegionSize == 0) { + // + // There is no microcode patches + // + return; + } + + CurrentRevision = GetCurrentMicrocodeSignature (); + if (CurrentRevision != 0) { + // + // Skip loading microcode if it has been loaded successfully + // + return; + } + + ExtendedTableLength = 0; + // + // Here data of CPUID leafs have not been collected into context buffer, so + // GetProcessorCpuid() cannot be used here to retrieve CPUID data. + // + AsmCpuid (CPUID_VERSION_INFO, &RegEax, NULL, NULL, NULL); + + // + // The index of platform information resides in bits 50:52 of MSR IA32_PLATFORM_ID + // + PlatformId = (UINT8) AsmMsrBitFieldRead64 (EFI_MSR_IA32_PLATFORM_ID, 50, 52); + + LatestRevision = 0; + MicrocodeEnd = (UINTN) (MicrocodePatchAddress + MicrocodePatchRegionSize); + MicrocodeEntryPoint = (EFI_CPU_MICROCODE_HEADER *) (UINTN) MicrocodePatchAddress; + do { + // + // Check if the microcode is for the Cpu and the version is newer + // and the update can be processed on the platform + // + CorrectMicrocode = FALSE; + if (MicrocodeEntryPoint->HeaderVersion == 0x1) { + // + // It is the microcode header. It is not the padding data between microcode patches + // becasue the padding data should not include 0x00000001 and it should be the repeated + // byte format (like 0xXYXYXYXY....). + // + if (MicrocodeEntryPoint->ProcessorId == RegEax && + MicrocodeEntryPoint->UpdateRevision > LatestRevision && + (MicrocodeEntryPoint->ProcessorFlags & (1 << PlatformId)) + ) { + if (MicrocodeEntryPoint->DataSize == 0) { + CheckSum32 = CalculateSum32 ((UINT32 *)MicrocodeEntryPoint, 2048); + } else { + CheckSum32 = CalculateSum32 ((UINT32 *)MicrocodeEntryPoint, MicrocodeEntryPoint->DataSize + sizeof(EFI_CPU_MICROCODE_HEADER)); + } + if (CheckSum32 == 0) { + CorrectMicrocode = TRUE; + } + } else if ((MicrocodeEntryPoint->DataSize != 0) && + (MicrocodeEntryPoint->UpdateRevision > LatestRevision)) { + ExtendedTableLength = MicrocodeEntryPoint->TotalSize - (MicrocodeEntryPoint->DataSize + sizeof (EFI_CPU_MICROCODE_HEADER)); + if (ExtendedTableLength != 0) { + // + // Extended Table exist, check if the CPU in support list + // + ExtendedTableHeader = (EFI_CPU_MICROCODE_EXTENDED_TABLE_HEADER *)((UINT8 *)(MicrocodeEntryPoint) + MicrocodeEntryPoint->DataSize + sizeof (EFI_CPU_MICROCODE_HEADER)); + // + // Calculate Extended Checksum + // + if ((ExtendedTableLength % 4) == 0) { + CheckSum32 = CalculateSum32 ((UINT32 *)ExtendedTableHeader, ExtendedTableLength); + if (CheckSum32 == 0) { + // + // Checksum correct + // + ExtendedTableCount = ExtendedTableHeader->ExtendedSignatureCount; + ExtendedTable = (EFI_CPU_MICROCODE_EXTENDED_TABLE *)(ExtendedTableHeader + 1); + for (Index = 0; Index < ExtendedTableCount; Index ++) { + CheckSum32 = CalculateSum32 ((UINT32 *)ExtendedTable, sizeof(EFI_CPU_MICROCODE_EXTENDED_TABLE)); + if (CheckSum32 == 0) { + // + // Verify Header + // + if ((ExtendedTable->ProcessorSignature == RegEax) && + (ExtendedTable->ProcessorFlag & (1 << PlatformId)) ) { + // + // Find one + // + CorrectMicrocode = TRUE; + break; + } + } + ExtendedTable ++; + } + } + } + } + } + } else { + // + // It is the padding data between the microcode patches for microcode patches alignment. + // Because the microcode patch is the multiple of 1-KByte, the padding data should not + // exist if the microcode patch alignment value is not larger than 1-KByte. So, the microcode + // alignment value should be larger than 1-KByte. We could skip SIZE_1KB padding data to + // find the next possible microcode patch header. + // + MicrocodeEntryPoint = (EFI_CPU_MICROCODE_HEADER *) (((UINTN) MicrocodeEntryPoint) + SIZE_1KB); + continue; + } + // + // Get the next patch. + // + if (MicrocodeEntryPoint->DataSize == 0) { + TotalSize = 2048; + } else { + TotalSize = MicrocodeEntryPoint->TotalSize; + } + + if (CorrectMicrocode) { + LatestRevision = MicrocodeEntryPoint->UpdateRevision; + MicrocodeInfo.MicrocodeData = (VOID *)((UINTN)MicrocodeEntryPoint + sizeof (EFI_CPU_MICROCODE_HEADER)); + MicrocodeInfo.MicrocodeSize = TotalSize; + MicrocodeInfo.ProcessorId = RegEax; + } + + MicrocodeEntryPoint = (EFI_CPU_MICROCODE_HEADER *) (((UINTN) MicrocodeEntryPoint) + TotalSize); + } while (((UINTN) MicrocodeEntryPoint < MicrocodeEnd)); + + if (LatestRevision > CurrentRevision) { + // + // BIOS only authenticate updates that contain a numerically larger revision + // than the currently loaded revision, where Current Signature < New Update + // Revision. A processor with no loaded update is considered to have a + // revision equal to zero. + // + AsmWriteMsr64 ( + EFI_MSR_IA32_BIOS_UPDT_TRIG, + (UINT64) (UINTN) MicrocodeInfo.MicrocodeData + ); + // + // Get and check new microcode signature + // + CurrentRevision = GetCurrentMicrocodeSignature (); + if (CurrentRevision != LatestRevision) { + AcquireSpinLock(&PeiCpuMpData->MpLock); + DEBUG ((EFI_D_ERROR, "Updated microcode signature [0x%08x] does not match \ + loaded microcode signature [0x%08x]\n", CurrentRevision, LatestRevision)); + ReleaseSpinLock(&PeiCpuMpData->MpLock); + } + } +} diff --git a/Core/UefiCpuPkg/CpuMpPei/Microcode.h b/Core/UefiCpuPkg/CpuMpPei/Microcode.h new file mode 100644 index 0000000000..965604522a --- /dev/null +++ b/Core/UefiCpuPkg/CpuMpPei/Microcode.h @@ -0,0 +1,58 @@ +/** @file + Definitions for loading microcode on processors. + + Copyright (c) 2015, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _CPU_MICROCODE_H_ +#define _CPU_MICROCODE_H_ + +#define EFI_MSR_IA32_PLATFORM_ID 0x17 +#define EFI_MSR_IA32_BIOS_UPDT_TRIG 0x79 +#define EFI_MSR_IA32_BIOS_SIGN_ID 0x8b + +#define MAX_MICROCODE_DESCRIPTOR_LENGTH 100 + +typedef struct { + VOID *MicrocodeData; + UINTN MicrocodeSize; + UINT32 ProcessorId; +} MICROCODE_INFO; + +// +// Definition for IA32 microcode format +// +typedef struct { + UINT32 HeaderVersion; + UINT32 UpdateRevision; + UINT32 Date; + UINT32 ProcessorId; + UINT32 Checksum; + UINT32 LoaderRevision; + UINT32 ProcessorFlags; + UINT32 DataSize; + UINT32 TotalSize; + UINT8 Reserved[12]; +} EFI_CPU_MICROCODE_HEADER; + +typedef struct { + UINT32 ExtendedSignatureCount; + UINT32 ExtendedTableChecksum; + UINT8 Reserved[12]; +} EFI_CPU_MICROCODE_EXTENDED_TABLE_HEADER; + +typedef struct { + UINT32 ProcessorSignature; + UINT32 ProcessorFlag; + UINT32 ProcessorChecksum; +} EFI_CPU_MICROCODE_EXTENDED_TABLE; + +#endif diff --git a/Core/UefiCpuPkg/CpuMpPei/PeiMpServices.c b/Core/UefiCpuPkg/CpuMpPei/PeiMpServices.c new file mode 100644 index 0000000000..e784377d67 --- /dev/null +++ b/Core/UefiCpuPkg/CpuMpPei/PeiMpServices.c @@ -0,0 +1,956 @@ +/** @file + Implementation of Multiple Processor PPI services. + + Copyright (c) 2015, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "PeiMpServices.h" + +// +// CPU MP PPI to be installed +// +EFI_PEI_MP_SERVICES_PPI mMpServicesPpi = { + PeiGetNumberOfProcessors, + PeiGetProcessorInfo, + PeiStartupAllAPs, + PeiStartupThisAP, + PeiSwitchBSP, + PeiEnableDisableAP, + PeiWhoAmI, +}; + +EFI_PEI_PPI_DESCRIPTOR mPeiCpuMpPpiDesc = { + (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), + &gEfiPeiMpServicesPpiGuid, + &mMpServicesPpi +}; + +/** + Get CPU Package/Core/Thread location information. + + @param InitialApicId CPU APIC ID + @param Location Pointer to CPU location information +**/ +VOID +ExtractProcessorLocation ( + IN UINT32 InitialApicId, + OUT EFI_CPU_PHYSICAL_LOCATION *Location + ) +{ + BOOLEAN TopologyLeafSupported; + UINTN ThreadBits; + UINTN CoreBits; + UINT32 RegEax; + UINT32 RegEbx; + UINT32 RegEcx; + UINT32 RegEdx; + UINT32 MaxCpuIdIndex; + UINT32 SubIndex; + UINTN LevelType; + UINT32 MaxLogicProcessorsPerPackage; + UINT32 MaxCoresPerPackage; + + // + // Check if the processor is capable of supporting more than one logical processor. + // + AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, NULL, &RegEdx); + if ((RegEdx & BIT28) == 0) { + Location->Thread = 0; + Location->Core = 0; + Location->Package = 0; + return; + } + + ThreadBits = 0; + CoreBits = 0; + + // + // Assume three-level mapping of APIC ID: Package:Core:SMT. + // + + TopologyLeafSupported = FALSE; + // + // Get the max index of basic CPUID + // + AsmCpuid (CPUID_SIGNATURE, &MaxCpuIdIndex, NULL, NULL, NULL); + + // + // If the extended topology enumeration leaf is available, it + // is the preferred mechanism for enumerating topology. + // + if (MaxCpuIdIndex >= CPUID_EXTENDED_TOPOLOGY) { + AsmCpuidEx (CPUID_EXTENDED_TOPOLOGY, 0, &RegEax, &RegEbx, &RegEcx, NULL); + // + // If CPUID.(EAX=0BH, ECX=0H):EBX returns zero and maximum input value for + // basic CPUID information is greater than 0BH, then CPUID.0BH leaf is not + // supported on that processor. + // + if (RegEbx != 0) { + TopologyLeafSupported = TRUE; + + // + // Sub-leaf index 0 (ECX= 0 as input) provides enumeration parameters to extract + // the SMT sub-field of x2APIC ID. + // + LevelType = (RegEcx >> 8) & 0xff; + ASSERT (LevelType == CPUID_EXTENDED_TOPOLOGY_LEVEL_TYPE_SMT); + ThreadBits = RegEax & 0x1f; + + // + // Software must not assume any "level type" encoding + // value to be related to any sub-leaf index, except sub-leaf 0. + // + SubIndex = 1; + do { + AsmCpuidEx (CPUID_EXTENDED_TOPOLOGY, SubIndex, &RegEax, NULL, &RegEcx, NULL); + LevelType = (RegEcx >> 8) & 0xff; + if (LevelType == CPUID_EXTENDED_TOPOLOGY_LEVEL_TYPE_CORE) { + CoreBits = (RegEax & 0x1f) - ThreadBits; + break; + } + SubIndex++; + } while (LevelType != CPUID_EXTENDED_TOPOLOGY_LEVEL_TYPE_INVALID); + } + } + + if (!TopologyLeafSupported) { + AsmCpuid (CPUID_VERSION_INFO, NULL, &RegEbx, NULL, NULL); + MaxLogicProcessorsPerPackage = (RegEbx >> 16) & 0xff; + if (MaxCpuIdIndex >= CPUID_CACHE_PARAMS) { + AsmCpuidEx (CPUID_CACHE_PARAMS, 0, &RegEax, NULL, NULL, NULL); + MaxCoresPerPackage = (RegEax >> 26) + 1; + } else { + // + // Must be a single-core processor. + // + MaxCoresPerPackage = 1; + } + + ThreadBits = (UINTN) (HighBitSet32 (MaxLogicProcessorsPerPackage / MaxCoresPerPackage - 1) + 1); + CoreBits = (UINTN) (HighBitSet32 (MaxCoresPerPackage - 1) + 1); + } + + Location->Thread = InitialApicId & ~((-1) << ThreadBits); + Location->Core = (InitialApicId >> ThreadBits) & ~((-1) << CoreBits); + Location->Package = (InitialApicId >> (ThreadBits + CoreBits)); +} + +/** + Find the current Processor number by APIC ID. + + @param PeiCpuMpData Pointer to PEI CPU MP Data + @param ProcessorNumber Return the pocessor number found + + @retval EFI_SUCCESS ProcessorNumber is found and returned. + @retval EFI_NOT_FOUND ProcessorNumber is not found. +**/ +EFI_STATUS +GetProcessorNumber ( + IN PEI_CPU_MP_DATA *PeiCpuMpData, + OUT UINTN *ProcessorNumber + ) +{ + UINTN TotalProcessorNumber; + UINTN Index; + + TotalProcessorNumber = PeiCpuMpData->CpuCount; + for (Index = 0; Index < TotalProcessorNumber; Index ++) { + if (PeiCpuMpData->CpuData[Index].ApicId == GetInitialApicId ()) { + *ProcessorNumber = Index; + return EFI_SUCCESS; + } + } + return EFI_NOT_FOUND; +} + +/** + Worker function for SwitchBSP(). + + Worker function for SwitchBSP(), assigned to the AP which is intended to become BSP. + + @param Buffer Pointer to CPU MP Data +**/ +VOID +EFIAPI +FutureBSPProc ( + IN VOID *Buffer + ) +{ + PEI_CPU_MP_DATA *DataInHob; + + DataInHob = (PEI_CPU_MP_DATA *) Buffer; + AsmExchangeRole (&DataInHob->APInfo, &DataInHob->BSPInfo); +} + +/** + This service 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. + + This function is used to retrieve the following information: + - The number of logical processors that are present in the system. + - The number of enabled logical processors in the system at the instant + this call is made. + + Because MP Service Ppi provides services to enable and disable processors + dynamically, the number of enabled logical processors may vary during the + course of a boot session. + + If this service is called from an AP, then EFI_DEVICE_ERROR is returned. + If NumberOfProcessors or NumberOfEnabledProcessors is NULL, then + EFI_INVALID_PARAMETER is returned. Otherwise, the total number of processors + is returned in NumberOfProcessors, the number of currently enabled processor + is returned in NumberOfEnabledProcessors, and EFI_SUCCESS is returned. + + @param[in] PeiServices An indirect pointer to the PEI Services Table + published by the PEI Foundation. + @param[in] This Pointer to this instance of the PPI. + @param[out] NumberOfProcessors Pointer to the total number of logical processors in + the system, including the BSP and disabled APs. + @param[out] NumberOfEnabledProcessors + Number of processors in the system that are enabled. + + @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. + NumberOfEnabledProcessors is NULL. +**/ +EFI_STATUS +EFIAPI +PeiGetNumberOfProcessors ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_MP_SERVICES_PPI *This, + OUT UINTN *NumberOfProcessors, + OUT UINTN *NumberOfEnabledProcessors + ) +{ + PEI_CPU_MP_DATA *PeiCpuMpData; + UINTN CallerNumber; + UINTN ProcessorNumber; + UINTN EnabledProcessorNumber; + UINTN Index; + + PeiCpuMpData = GetMpHobData (); + if (PeiCpuMpData == NULL) { + return EFI_NOT_FOUND; + } + + if ((NumberOfProcessors == NULL) || (NumberOfEnabledProcessors == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // + // Check whether caller processor is BSP + // + PeiWhoAmI (PeiServices, This, &CallerNumber); + if (CallerNumber != PeiCpuMpData->BspNumber) { + return EFI_DEVICE_ERROR; + } + + ProcessorNumber = PeiCpuMpData->CpuCount; + EnabledProcessorNumber = 0; + for (Index = 0; Index < ProcessorNumber; Index++) { + if (PeiCpuMpData->CpuData[Index].State != CpuStateDisabled) { + EnabledProcessorNumber ++; + } + } + + *NumberOfProcessors = ProcessorNumber; + *NumberOfEnabledProcessors = EnabledProcessorNumber; + + 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. + + This service retrieves detailed MP-related information about any processor + on the platform. Note the following: + - The processor information may change during the course of a boot session. + - The information presented here is entirely MP related. + + Information regarding the number of caches and their sizes, frequency of operation, + slot numbers is all considered platform-related information and is not provided + by this service. + + @param[in] PeiServices An indirect pointer to the PEI Services Table + published by the PEI Foundation. + @param[in] This Pointer to this instance of the PPI. + @param[in] ProcessorNumber Pointer to the total number of logical processors in + the system, including the BSP and disabled APs. + @param[out] ProcessorInfoBuffer Number of processors in the system that are enabled. + + @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. +**/ +EFI_STATUS +EFIAPI +PeiGetProcessorInfo ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_MP_SERVICES_PPI *This, + IN UINTN ProcessorNumber, + OUT EFI_PROCESSOR_INFORMATION *ProcessorInfoBuffer + ) +{ + PEI_CPU_MP_DATA *PeiCpuMpData; + UINTN CallerNumber; + + PeiCpuMpData = GetMpHobData (); + if (PeiCpuMpData == NULL) { + return EFI_NOT_FOUND; + } + + // + // Check whether caller processor is BSP + // + PeiWhoAmI (PeiServices, This, &CallerNumber); + if (CallerNumber != PeiCpuMpData->BspNumber) { + return EFI_DEVICE_ERROR; + } + + if (ProcessorInfoBuffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (ProcessorNumber >= PeiCpuMpData->CpuCount) { + return EFI_NOT_FOUND; + } + + ProcessorInfoBuffer->ProcessorId = (UINT64) PeiCpuMpData->CpuData[ProcessorNumber].ApicId; + ProcessorInfoBuffer->StatusFlag = 0; + if (PeiCpuMpData->CpuData[ProcessorNumber].ApicId == GetInitialApicId()) { + ProcessorInfoBuffer->StatusFlag |= PROCESSOR_AS_BSP_BIT; + } + if (PeiCpuMpData->CpuData[ProcessorNumber].CpuHealthy) { + ProcessorInfoBuffer->StatusFlag |= PROCESSOR_HEALTH_STATUS_BIT; + } + if (PeiCpuMpData->CpuData[ProcessorNumber].State == CpuStateDisabled) { + ProcessorInfoBuffer->StatusFlag &= ~PROCESSOR_ENABLED_BIT; + } else { + ProcessorInfoBuffer->StatusFlag |= PROCESSOR_ENABLED_BIT; + } + + // + // Get processor location information + // + ExtractProcessorLocation (PeiCpuMpData->CpuData[ProcessorNumber].ApicId, &ProcessorInfoBuffer->Location); + + return EFI_SUCCESS; +} + +/** + This service executes a caller provided function on all enabled APs. APs can + run either simultaneously or one at a time in sequence. This service supports + both blocking requests only. This service may only + be called from the BSP. + + This function is used to dispatch all the enabled APs to the function specified + by Procedure. If any enabled AP is busy, then EFI_NOT_READY is returned + immediately and Procedure is not started on any AP. + + If SingleThread is TRUE, all the enabled APs execute the function specified by + Procedure one by one, in ascending order of processor handle number. Otherwise, + all the enabled APs execute the function specified by Procedure simultaneously. + + If the timeout specified by TimeoutInMicroSeconds expires before all APs return + from Procedure, then Procedure on the failed APs is terminated. All enabled APs + are always available for further calls to EFI_PEI_MP_SERVICES_PPI.StartupAllAPs() + and EFI_PEI_MP_SERVICES_PPI.StartupThisAP(). If FailedCpuList is not NULL, its + content points to the list of processor handle numbers in which Procedure was + terminated. + + Note: It is the responsibility of the consumer of the EFI_PEI_MP_SERVICES_PPI.StartupAllAPs() + to make sure that the nature of the code that is executed on the BSP and the + dispatched APs is well controlled. The MP Services Ppi does not guarantee + that the Procedure function is MP-safe. Hence, the tasks that can be run in + parallel are limited to certain independent tasks and well-controlled exclusive + code. PEI services and Ppis may not be called by APs unless otherwise + specified. + + In blocking execution mode, BSP waits until all APs finish or + TimeoutInMicroSeconds expires. + + @param[in] PeiServices An indirect pointer to the PEI Services Table + published by the PEI Foundation. + @param[in] This A pointer to the EFI_PEI_MP_SERVICES_PPI instance. + @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] TimeoutInMicroSeconds + Indicates the time limit in microseconds for APs to + return from Procedure, for blocking mode only. Zero + means infinity. If the timeout expires before all APs + return from Procedure, then Procedure on the failed APs + is terminated. All enabled APs are available for next + function assigned by EFI_PEI_MP_SERVICES_PPI.StartupAllAPs() + or EFI_PEI_MP_SERVICES_PPI.StartupThisAP(). If the + timeout expires in blocking mode, BSP returns + EFI_TIMEOUT. + @param[in] ProcedureArgument The parameter passed into Procedure for all APs. + + @retval EFI_SUCCESS In blocking mode, all APs have finished before the + timeout expired. + @retval EFI_DEVICE_ERROR Caller processor is AP. + @retval EFI_NOT_STARTED No enabled APs exist in the system. + @retval EFI_NOT_READY Any enabled APs are busy. + @retval EFI_TIMEOUT In blocking mode, the timeout expired before all + enabled APs have finished. + @retval EFI_INVALID_PARAMETER Procedure is NULL. +**/ +EFI_STATUS +EFIAPI +PeiStartupAllAPs ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_MP_SERVICES_PPI *This, + IN EFI_AP_PROCEDURE Procedure, + IN BOOLEAN SingleThread, + IN UINTN TimeoutInMicroSeconds, + IN VOID *ProcedureArgument OPTIONAL + ) +{ + PEI_CPU_MP_DATA *PeiCpuMpData; + UINTN ProcessorNumber; + UINTN Index; + UINTN CallerNumber; + BOOLEAN HasEnabledAp; + BOOLEAN HasEnabledIdleAp; + volatile UINT32 *FinishedCount; + EFI_STATUS Status; + UINTN WaitCountIndex; + UINTN WaitCountNumber; + + PeiCpuMpData = GetMpHobData (); + if (PeiCpuMpData == NULL) { + return EFI_NOT_FOUND; + } + + if (Procedure == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Check whether caller processor is BSP + // + PeiWhoAmI (PeiServices, This, &CallerNumber); + if (CallerNumber != PeiCpuMpData->BspNumber) { + return EFI_DEVICE_ERROR; + } + + ProcessorNumber = PeiCpuMpData->CpuCount; + + HasEnabledAp = FALSE; + HasEnabledIdleAp = FALSE; + for (Index = 0; Index < ProcessorNumber; Index ++) { + if (Index == CallerNumber) { + // + // Skip BSP + // + continue; + } + if (PeiCpuMpData->CpuData[Index].State != CpuStateDisabled) { + HasEnabledAp = TRUE; + if (PeiCpuMpData->CpuData[Index].State != CpuStateBusy) { + HasEnabledIdleAp = TRUE; + } + } + } + if (!HasEnabledAp) { + // + // If no enabled AP exists, return EFI_NOT_STARTED. + // + return EFI_NOT_STARTED; + } + if (!HasEnabledIdleAp) { + // + // If any enabled APs are busy, return EFI_NOT_READY. + // + return EFI_NOT_READY; + } + + if (PeiCpuMpData->EndOfPeiFlag) { + // + // Backup original data and copy AP reset vector in it + // + BackupAndPrepareWakeupBuffer(PeiCpuMpData); + } + + WaitCountNumber = TimeoutInMicroSeconds / CPU_CHECK_AP_INTERVAL + 1; + WaitCountIndex = 0; + FinishedCount = &PeiCpuMpData->FinishedCount; + if (!SingleThread) { + WakeUpAP (PeiCpuMpData, TRUE, 0, Procedure, ProcedureArgument); + // + // Wait to finish + // + if (TimeoutInMicroSeconds == 0) { + while (*FinishedCount < ProcessorNumber - 1) { + CpuPause (); + } + Status = EFI_SUCCESS; + } else { + Status = EFI_TIMEOUT; + for (WaitCountIndex = 0; WaitCountIndex < WaitCountNumber; WaitCountIndex++) { + MicroSecondDelay (CPU_CHECK_AP_INTERVAL); + if (*FinishedCount >= ProcessorNumber - 1) { + Status = EFI_SUCCESS; + break; + } + } + } + } else { + Status = EFI_SUCCESS; + for (Index = 0; Index < ProcessorNumber; Index++) { + if (Index == CallerNumber) { + continue; + } + WakeUpAP (PeiCpuMpData, FALSE, Index, Procedure, ProcedureArgument); + // + // Wait to finish + // + if (TimeoutInMicroSeconds == 0) { + while (*FinishedCount < 1) { + CpuPause (); + } + } else { + for (WaitCountIndex = 0; WaitCountIndex < WaitCountNumber; WaitCountIndex++) { + MicroSecondDelay (CPU_CHECK_AP_INTERVAL); + if (*FinishedCount >= 1) { + break; + } + } + if (WaitCountIndex == WaitCountNumber) { + Status = EFI_TIMEOUT; + } + } + } + } + + if (PeiCpuMpData->EndOfPeiFlag) { + // + // Restore original data + // + RestoreWakeupBuffer(PeiCpuMpData); + } + + return Status; +} + +/** + This service lets the caller get one enabled AP to execute a caller-provided + function. The caller can request the BSP to wait for the completion + of the AP. This service may only be called from the BSP. + + This function is used to dispatch one enabled AP to the function specified by + Procedure passing in the argument specified by ProcedureArgument. + The execution is in blocking mode. The BSP waits until the AP finishes or + TimeoutInMicroSecondss expires. + + If the timeout specified by TimeoutInMicroseconds expires before the AP returns + from Procedure, then execution of Procedure by the AP is terminated. The AP is + available for subsequent calls to EFI_PEI_MP_SERVICES_PPI.StartupAllAPs() and + EFI_PEI_MP_SERVICES_PPI.StartupThisAP(). + + @param[in] PeiServices An indirect pointer to the PEI Services Table + published by the PEI Foundation. + @param[in] This A pointer to the EFI_PEI_MP_SERVICES_PPI instance. + @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. The range is from 0 to the + total number of logical processors minus 1. The total + number of logical processors can be retrieved by + EFI_PEI_MP_SERVICES_PPI.GetNumberOfProcessors(). + @param[in] TimeoutInMicroseconds + Indicates the time limit in microseconds for APs to + return from Procedure, for blocking mode only. Zero + means infinity. If the timeout expires before all APs + return from Procedure, then Procedure on the failed APs + is terminated. All enabled APs are available for next + function assigned by EFI_PEI_MP_SERVICES_PPI.StartupAllAPs() + or EFI_PEI_MP_SERVICES_PPI.StartupThisAP(). If the + timeout expires in blocking mode, BSP returns + EFI_TIMEOUT. + @param[in] ProcedureArgument The parameter passed into Procedure for all APs. + + @retval EFI_SUCCESS In blocking mode, specified AP finished before the + timeout expires. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_TIMEOUT In blocking mode, the timeout expired before the + specified AP has finished. + @retval EFI_NOT_FOUND The processor with the handle specified by + ProcessorNumber does not exist. + @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP or disabled AP. + @retval EFI_INVALID_PARAMETER Procedure is NULL. +**/ +EFI_STATUS +EFIAPI +PeiStartupThisAP ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_MP_SERVICES_PPI *This, + IN EFI_AP_PROCEDURE Procedure, + IN UINTN ProcessorNumber, + IN UINTN TimeoutInMicroseconds, + IN VOID *ProcedureArgument OPTIONAL + ) +{ + PEI_CPU_MP_DATA *PeiCpuMpData; + UINTN CallerNumber; + volatile UINT32 *FinishedCount; + EFI_STATUS Status; + UINTN WaitCountIndex; + UINTN WaitCountNumber; + + PeiCpuMpData = GetMpHobData (); + if (PeiCpuMpData == NULL) { + return EFI_NOT_FOUND; + } + + // + // Check whether caller processor is BSP + // + PeiWhoAmI (PeiServices, This, &CallerNumber); + if (CallerNumber != PeiCpuMpData->BspNumber) { + return EFI_DEVICE_ERROR; + } + + if (ProcessorNumber >= PeiCpuMpData->CpuCount) { + return EFI_NOT_FOUND; + } + + if (ProcessorNumber == PeiCpuMpData->BspNumber || Procedure == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Check whether specified AP is disabled + // + if (PeiCpuMpData->CpuData[ProcessorNumber].State == CpuStateDisabled) { + return EFI_INVALID_PARAMETER; + } + + if (PeiCpuMpData->EndOfPeiFlag) { + // + // Backup original data and copy AP reset vector in it + // + BackupAndPrepareWakeupBuffer(PeiCpuMpData); + } + + WaitCountNumber = TimeoutInMicroseconds / CPU_CHECK_AP_INTERVAL + 1; + WaitCountIndex = 0; + FinishedCount = &PeiCpuMpData->FinishedCount; + + WakeUpAP (PeiCpuMpData, FALSE, ProcessorNumber, Procedure, ProcedureArgument); + + // + // Wait to finish + // + if (TimeoutInMicroseconds == 0) { + while (*FinishedCount < 1) { + CpuPause() ; + } + Status = EFI_SUCCESS; + } else { + Status = EFI_TIMEOUT; + for (WaitCountIndex = 0; WaitCountIndex < WaitCountNumber; WaitCountIndex++) { + MicroSecondDelay (CPU_CHECK_AP_INTERVAL); + if (*FinishedCount >= 1) { + Status = EFI_SUCCESS; + break; + } + } + } + + if (PeiCpuMpData->EndOfPeiFlag) { + // + // Backup original data and copy AP reset vector in it + // + RestoreWakeupBuffer(PeiCpuMpData); + } + + return Status; +} + +/** + This service switches the requested AP to be the BSP from that point onward. + This service changes the BSP for all purposes. This call can only be performed + by the current BSP. + + This service switches the requested AP to be the BSP from that point onward. + This service changes the BSP for all purposes. The new BSP can take over the + execution of the old BSP and continue seamlessly from where the old one left + off. + + If the BSP cannot be switched prior to the return from this service, then + EFI_UNSUPPORTED must be returned. + + @param[in] PeiServices An indirect pointer to the PEI Services Table + published by the PEI Foundation. + @param[in] This A pointer to the EFI_PEI_MP_SERVICES_PPI instance. + @param[in] ProcessorNumber The handle number of the 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 + EFI_PEI_MP_SERVICES_PPI.GetNumberOfProcessors(). + @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 EFI_UNSUPPORTED Switching the BSP cannot be completed prior to this + service returning. + @retval EFI_UNSUPPORTED Switching the BSP is not supported. + @retval EFI_SUCCESS The calling processor is an AP. + @retval EFI_NOT_FOUND The processor with the handle specified by + ProcessorNumber does not exist. + @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the current BSP or a disabled + AP. + @retval EFI_NOT_READY The specified AP is busy. +**/ +EFI_STATUS +EFIAPI +PeiSwitchBSP ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_MP_SERVICES_PPI *This, + IN UINTN ProcessorNumber, + IN BOOLEAN EnableOldBSP + ) +{ + PEI_CPU_MP_DATA *PeiCpuMpData; + UINTN CallerNumber; + MSR_IA32_APIC_BASE ApicBaseMsr; + + PeiCpuMpData = GetMpHobData (); + if (PeiCpuMpData == NULL) { + return EFI_NOT_FOUND; + } + + // + // Check whether caller processor is BSP + // + PeiWhoAmI (PeiServices, This, &CallerNumber); + if (CallerNumber != PeiCpuMpData->BspNumber) { + return EFI_SUCCESS; + } + + if (ProcessorNumber >= PeiCpuMpData->CpuCount) { + return EFI_NOT_FOUND; + } + + // + // Check whether specified AP is disabled + // + if (PeiCpuMpData->CpuData[ProcessorNumber].State == CpuStateDisabled) { + return EFI_INVALID_PARAMETER; + } + + // + // Check whether ProcessorNumber specifies the current BSP + // + if (ProcessorNumber == PeiCpuMpData->BspNumber) { + return EFI_INVALID_PARAMETER; + } + + // + // Check whether specified AP is busy + // + if (PeiCpuMpData->CpuData[ProcessorNumber].State == CpuStateBusy) { + return EFI_NOT_READY; + } + + // + // Clear the BSP bit of MSR_IA32_APIC_BASE + // + ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE_ADDRESS); + ApicBaseMsr.Bits.Bsp = 0; + AsmWriteMsr64 (MSR_IA32_APIC_BASE_ADDRESS, ApicBaseMsr.Uint64); + + PeiCpuMpData->BSPInfo.State = CPU_SWITCH_STATE_IDLE; + PeiCpuMpData->APInfo.State = CPU_SWITCH_STATE_IDLE; + + if (PeiCpuMpData->EndOfPeiFlag) { + // + // Backup original data and copy AP reset vector in it + // + BackupAndPrepareWakeupBuffer(PeiCpuMpData); + } + + // + // Need to wakeUp AP (future BSP). + // + WakeUpAP (PeiCpuMpData, FALSE, ProcessorNumber, FutureBSPProc, PeiCpuMpData); + + AsmExchangeRole (&PeiCpuMpData->BSPInfo, &PeiCpuMpData->APInfo); + + if (PeiCpuMpData->EndOfPeiFlag) { + // + // Backup original data and copy AP reset vector in it + // + RestoreWakeupBuffer(PeiCpuMpData); + } + + // + // Set the BSP bit of MSR_IA32_APIC_BASE on new BSP + // + ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE_ADDRESS); + ApicBaseMsr.Bits.Bsp = 1; + AsmWriteMsr64 (MSR_IA32_APIC_BASE_ADDRESS, ApicBaseMsr.Uint64); + // + // Set old BSP enable state + // + if (!EnableOldBSP) { + PeiCpuMpData->CpuData[PeiCpuMpData->BspNumber].State = CpuStateDisabled; + } + // + // Save new BSP number + // + PeiCpuMpData->BspNumber = (UINT32) ProcessorNumber; + + return EFI_SUCCESS; +} + +/** + This service lets the caller enable or disable an AP from this point onward. + This service may only be called from the BSP. + + This service allows the caller enable or disable an AP from this point onward. + The caller can optionally specify the health status of the AP by Health. If + an AP is being disabled, then the state of the disabled AP is implementation + dependent. If an AP is enabled, then the implementation must guarantee that a + complete initialization sequence is performed on the AP, so the AP is in a state + that is compatible with an MP operating system. + + If the enable or disable AP operation cannot be completed prior to the return + from this service, then EFI_UNSUPPORTED must be returned. + + @param[in] PeiServices An indirect pointer to the PEI Services Table + published by the PEI Foundation. + @param[in] This A pointer to the EFI_PEI_MP_SERVICES_PPI instance. + @param[in] ProcessorNumber The handle number of the 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 + EFI_PEI_MP_SERVICES_PPI.GetNumberOfProcessors(). + @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. This flag corresponds to + StatusFlag defined in EFI_PEI_MP_SERVICES_PPI.GetProcessorInfo(). + Only the PROCESSOR_HEALTH_STATUS_BIT is used. All other + bits are ignored. If it is NULL, this parameter is + ignored. + + @retval EFI_SUCCESS The specified AP was enabled or disabled successfully. + @retval EFI_UNSUPPORTED Enabling or disabling an AP cannot be completed prior + to this service returning. + @retval EFI_UNSUPPORTED Enabling or disabling an AP is not supported. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_NOT_FOUND Processor with the handle specified by ProcessorNumber + does not exist. + @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP. +**/ +EFI_STATUS +EFIAPI +PeiEnableDisableAP ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_MP_SERVICES_PPI *This, + IN UINTN ProcessorNumber, + IN BOOLEAN EnableAP, + IN UINT32 *HealthFlag OPTIONAL + ) +{ + PEI_CPU_MP_DATA *PeiCpuMpData; + UINTN CallerNumber; + + PeiCpuMpData = GetMpHobData (); + if (PeiCpuMpData == NULL) { + return EFI_NOT_FOUND; + } + + // + // Check whether caller processor is BSP + // + PeiWhoAmI (PeiServices, This, &CallerNumber); + if (CallerNumber != PeiCpuMpData->BspNumber) { + return EFI_DEVICE_ERROR; + } + + if (ProcessorNumber == PeiCpuMpData->BspNumber) { + return EFI_INVALID_PARAMETER; + } + + if (ProcessorNumber >= PeiCpuMpData->CpuCount) { + return EFI_NOT_FOUND; + } + + if (!EnableAP) { + PeiCpuMpData->CpuData[ProcessorNumber].State = CpuStateDisabled; + } else { + PeiCpuMpData->CpuData[ProcessorNumber].State = CpuStateIdle; + } + + if (HealthFlag != NULL) { + PeiCpuMpData->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. + + This service returns the processor handle number for the calling processor. + The returned value is in the range from 0 to the total number of logical + processors minus 1. The total number of logical processors can be retrieved + with EFI_PEI_MP_SERVICES_PPI.GetNumberOfProcessors(). This service may be + called from the BSP and APs. If ProcessorNumber is NULL, then EFI_INVALID_PARAMETER + is returned. Otherwise, the current processors handle number is returned in + ProcessorNumber, and EFI_SUCCESS is returned. + + @param[in] PeiServices An indirect pointer to the PEI Services Table + published by the PEI Foundation. + @param[in] This A pointer to the EFI_PEI_MP_SERVICES_PPI instance. + @param[out] ProcessorNumber The handle number of the 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 + EFI_PEI_MP_SERVICES_PPI.GetNumberOfProcessors(). + + @retval EFI_SUCCESS The current processor handle number was returned in + ProcessorNumber. + @retval EFI_INVALID_PARAMETER ProcessorNumber is NULL. +**/ +EFI_STATUS +EFIAPI +PeiWhoAmI ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_MP_SERVICES_PPI *This, + OUT UINTN *ProcessorNumber + ) +{ + PEI_CPU_MP_DATA *PeiCpuMpData; + + PeiCpuMpData = GetMpHobData (); + if (PeiCpuMpData == NULL) { + return EFI_NOT_FOUND; + } + + if (ProcessorNumber == NULL) { + return EFI_INVALID_PARAMETER; + } + + return GetProcessorNumber (PeiCpuMpData, ProcessorNumber); +} + diff --git a/Core/UefiCpuPkg/CpuMpPei/PeiMpServices.h b/Core/UefiCpuPkg/CpuMpPei/PeiMpServices.h new file mode 100644 index 0000000000..57f7691161 --- /dev/null +++ b/Core/UefiCpuPkg/CpuMpPei/PeiMpServices.h @@ -0,0 +1,377 @@ +/** @file + Functions prototype of Multiple Processor PPI services. + + Copyright (c) 2015, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _PEI_MP_SERVICES_H_ +#define _PEI_MP_SERVICES_H_ + +#include "CpuMpPei.h" + +// +// The MP data for switch BSP +// +#define CPU_SWITCH_STATE_IDLE 0 +#define CPU_SWITCH_STATE_STORED 1 +#define CPU_SWITCH_STATE_LOADED 2 + +#define CPU_CHECK_AP_INTERVAL 0x100 // 100 microseconds + +/** + This function is called by both the BSP and the AP which is to become the BSP to + Exchange execution context including stack between them. After return from this + function, the BSP becomes AP and the AP becomes the BSP. + + @param MyInfo Pointer to buffer holding the exchanging information for the executing processor. + @param OthersInfo Pointer to buffer holding the exchanging information for the peer. +**/ +VOID +EFIAPI +AsmExchangeRole ( + IN CPU_EXCHANGE_ROLE_INFO *MyInfo, + IN CPU_EXCHANGE_ROLE_INFO *OthersInfo + ); + +/** + This service 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. + + This function is used to retrieve the following information: + - The number of logical processors that are present in the system. + - The number of enabled logical processors in the system at the instant + this call is made. + + Because MP Service Ppi provides services to enable and disable processors + dynamically, the number of enabled logical processors may vary during the + course of a boot session. + + If this service is called from an AP, then EFI_DEVICE_ERROR is returned. + If NumberOfProcessors or NumberOfEnabledProcessors is NULL, then + EFI_INVALID_PARAMETER is returned. Otherwise, the total number of processors + is returned in NumberOfProcessors, the number of currently enabled processor + is returned in NumberOfEnabledProcessors, and EFI_SUCCESS is returned. + + @param[in] PeiServices An indirect pointer to the PEI Services Table + published by the PEI Foundation. + @param[in] This Pointer to this instance of the PPI. + @param[out] NumberOfProcessors Pointer to the total number of logical processors in + the system, including the BSP and disabled APs. + @param[out] NumberOfEnabledProcessors + Number of processors in the system that are enabled. + + @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. + NumberOfEnabledProcessors is NULL. +**/ +EFI_STATUS +EFIAPI +PeiGetNumberOfProcessors ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_MP_SERVICES_PPI *This, + OUT UINTN *NumberOfProcessors, + OUT UINTN *NumberOfEnabledProcessors + ); + +/** + 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. + + This service retrieves detailed MP-related information about any processor + on the platform. Note the following: + - The processor information may change during the course of a boot session. + - The information presented here is entirely MP related. + + Information regarding the number of caches and their sizes, frequency of operation, + slot numbers is all considered platform-related information and is not provided + by this service. + + @param[in] PeiServices An indirect pointer to the PEI Services Table + published by the PEI Foundation. + @param[in] This Pointer to this instance of the PPI. + @param[in] ProcessorNumber Pointer to the total number of logical processors in + the system, including the BSP and disabled APs. + @param[out] ProcessorInfoBuffer Number of processors in the system that are enabled. + + @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. +**/ +EFI_STATUS +EFIAPI +PeiGetProcessorInfo ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_MP_SERVICES_PPI *This, + IN UINTN ProcessorNumber, + OUT EFI_PROCESSOR_INFORMATION *ProcessorInfoBuffer + ); + +/** + This service executes a caller provided function on all enabled APs. APs can + run either simultaneously or one at a time in sequence. This service supports + both blocking requests only. This service may only + be called from the BSP. + + This function is used to dispatch all the enabled APs to the function specified + by Procedure. If any enabled AP is busy, then EFI_NOT_READY is returned + immediately and Procedure is not started on any AP. + + If SingleThread is TRUE, all the enabled APs execute the function specified by + Procedure one by one, in ascending order of processor handle number. Otherwise, + all the enabled APs execute the function specified by Procedure simultaneously. + + If the timeout specified by TimeoutInMicroSeconds expires before all APs return + from Procedure, then Procedure on the failed APs is terminated. All enabled APs + are always available for further calls to EFI_PEI_MP_SERVICES_PPI.StartupAllAPs() + and EFI_PEI_MP_SERVICES_PPI.StartupThisAP(). If FailedCpuList is not NULL, its + content points to the list of processor handle numbers in which Procedure was + terminated. + + Note: It is the responsibility of the consumer of the EFI_PEI_MP_SERVICES_PPI.StartupAllAPs() + to make sure that the nature of the code that is executed on the BSP and the + dispatched APs is well controlled. The MP Services Ppi does not guarantee + that the Procedure function is MP-safe. Hence, the tasks that can be run in + parallel are limited to certain independent tasks and well-controlled exclusive + code. PEI services and Ppis may not be called by APs unless otherwise + specified. + + In blocking execution mode, BSP waits until all APs finish or + TimeoutInMicroSeconds expires. + + @param[in] PeiServices An indirect pointer to the PEI Services Table + published by the PEI Foundation. + @param[in] This A pointer to the EFI_PEI_MP_SERVICES_PPI instance. + @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] TimeoutInMicroSeconds + Indicates the time limit in microseconds for APs to + return from Procedure, for blocking mode only. Zero + means infinity. If the timeout expires before all APs + return from Procedure, then Procedure on the failed APs + is terminated. All enabled APs are available for next + function assigned by EFI_PEI_MP_SERVICES_PPI.StartupAllAPs() + or EFI_PEI_MP_SERVICES_PPI.StartupThisAP(). If the + timeout expires in blocking mode, BSP returns + EFI_TIMEOUT. + @param[in] ProcedureArgument The parameter passed into Procedure for all APs. + + @retval EFI_SUCCESS In blocking mode, all APs have finished before the + timeout expired. + @retval EFI_DEVICE_ERROR Caller processor is AP. + @retval EFI_NOT_STARTED No enabled APs exist in the system. + @retval EFI_NOT_READY Any enabled APs are busy. + @retval EFI_TIMEOUT In blocking mode, the timeout expired before all + enabled APs have finished. + @retval EFI_INVALID_PARAMETER Procedure is NULL. +**/ +EFI_STATUS +EFIAPI +PeiStartupAllAPs ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_MP_SERVICES_PPI *This, + IN EFI_AP_PROCEDURE Procedure, + IN BOOLEAN SingleThread, + IN UINTN TimeoutInMicroSeconds, + IN VOID *ProcedureArgument OPTIONAL + ); + +/** + This service lets the caller get one enabled AP to execute a caller-provided + function. The caller can request the BSP to wait for the completion + of the AP. This service may only be called from the BSP. + + This function is used to dispatch one enabled AP to the function specified by + Procedure passing in the argument specified by ProcedureArgument. + The execution is in blocking mode. The BSP waits until the AP finishes or + TimeoutInMicroSecondss expires. + + If the timeout specified by TimeoutInMicroseconds expires before the AP returns + from Procedure, then execution of Procedure by the AP is terminated. The AP is + available for subsequent calls to EFI_PEI_MP_SERVICES_PPI.StartupAllAPs() and + EFI_PEI_MP_SERVICES_PPI.StartupThisAP(). + + @param[in] PeiServices An indirect pointer to the PEI Services Table + published by the PEI Foundation. + @param[in] This A pointer to the EFI_PEI_MP_SERVICES_PPI instance. + @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. The range is from 0 to the + total number of logical processors minus 1. The total + number of logical processors can be retrieved by + EFI_PEI_MP_SERVICES_PPI.GetNumberOfProcessors(). + @param[in] TimeoutInMicroseconds + Indicates the time limit in microseconds for APs to + return from Procedure, for blocking mode only. Zero + means infinity. If the timeout expires before all APs + return from Procedure, then Procedure on the failed APs + is terminated. All enabled APs are available for next + function assigned by EFI_PEI_MP_SERVICES_PPI.StartupAllAPs() + or EFI_PEI_MP_SERVICES_PPI.StartupThisAP(). If the + timeout expires in blocking mode, BSP returns + EFI_TIMEOUT. + @param[in] ProcedureArgument The parameter passed into Procedure for all APs. + + @retval EFI_SUCCESS In blocking mode, specified AP finished before the + timeout expires. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_TIMEOUT In blocking mode, the timeout expired before the + specified AP has finished. + @retval EFI_NOT_FOUND The processor with the handle specified by + ProcessorNumber does not exist. + @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP or disabled AP. + @retval EFI_INVALID_PARAMETER Procedure is NULL. +**/ +EFI_STATUS +EFIAPI +PeiStartupThisAP ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_MP_SERVICES_PPI *This, + IN EFI_AP_PROCEDURE Procedure, + IN UINTN ProcessorNumber, + IN UINTN TimeoutInMicroseconds, + IN VOID *ProcedureArgument OPTIONAL + ); + +/** + This service switches the requested AP to be the BSP from that point onward. + This service changes the BSP for all purposes. This call can only be performed + by the current BSP. + + This service switches the requested AP to be the BSP from that point onward. + This service changes the BSP for all purposes. The new BSP can take over the + execution of the old BSP and continue seamlessly from where the old one left + off. + + If the BSP cannot be switched prior to the return from this service, then + EFI_UNSUPPORTED must be returned. + + @param[in] PeiServices An indirect pointer to the PEI Services Table + published by the PEI Foundation. + @param[in] This A pointer to the EFI_PEI_MP_SERVICES_PPI instance. + @param[in] ProcessorNumber The handle number of the 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 + EFI_PEI_MP_SERVICES_PPI.GetNumberOfProcessors(). + @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 EFI_UNSUPPORTED Switching the BSP cannot be completed prior to this + service returning. + @retval EFI_UNSUPPORTED Switching the BSP is not supported. + @retval EFI_SUCCESS The calling processor is an AP. + @retval EFI_NOT_FOUND The processor with the handle specified by + ProcessorNumber does not exist. + @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the current BSP or a disabled + AP. + @retval EFI_NOT_READY The specified AP is busy. +**/ +EFI_STATUS +EFIAPI +PeiSwitchBSP ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_MP_SERVICES_PPI *This, + IN UINTN ProcessorNumber, + IN BOOLEAN EnableOldBSP + ); + +/** + This service lets the caller enable or disable an AP from this point onward. + This service may only be called from the BSP. + + This service allows the caller enable or disable an AP from this point onward. + The caller can optionally specify the health status of the AP by Health. If + an AP is being disabled, then the state of the disabled AP is implementation + dependent. If an AP is enabled, then the implementation must guarantee that a + complete initialization sequence is performed on the AP, so the AP is in a state + that is compatible with an MP operating system. + + If the enable or disable AP operation cannot be completed prior to the return + from this service, then EFI_UNSUPPORTED must be returned. + + @param[in] PeiServices An indirect pointer to the PEI Services Table + published by the PEI Foundation. + @param[in] This A pointer to the EFI_PEI_MP_SERVICES_PPI instance. + @param[in] ProcessorNumber The handle number of the 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 + EFI_PEI_MP_SERVICES_PPI.GetNumberOfProcessors(). + @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. This flag corresponds to + StatusFlag defined in EFI_PEI_MP_SERVICES_PPI.GetProcessorInfo(). + Only the PROCESSOR_HEALTH_STATUS_BIT is used. All other + bits are ignored. If it is NULL, this parameter is + ignored. + + @retval EFI_SUCCESS The specified AP was enabled or disabled successfully. + @retval EFI_UNSUPPORTED Enabling or disabling an AP cannot be completed prior + to this service returning. + @retval EFI_UNSUPPORTED Enabling or disabling an AP is not supported. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_NOT_FOUND Processor with the handle specified by ProcessorNumber + does not exist. + @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP. +**/ +EFI_STATUS +EFIAPI +PeiEnableDisableAP ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_MP_SERVICES_PPI *This, + IN UINTN ProcessorNumber, + IN BOOLEAN EnableAP, + IN UINT32 *HealthFlag OPTIONAL + ); + +/** + This return the handle number for the calling processor. This service may be + called from the BSP and APs. + + This service returns the processor handle number for the calling processor. + The returned value is in the range from 0 to the total number of logical + processors minus 1. The total number of logical processors can be retrieved + with EFI_PEI_MP_SERVICES_PPI.GetNumberOfProcessors(). This service may be + called from the BSP and APs. If ProcessorNumber is NULL, then EFI_INVALID_PARAMETER + is returned. Otherwise, the current processors handle number is returned in + ProcessorNumber, and EFI_SUCCESS is returned. + + @param[in] PeiServices An indirect pointer to the PEI Services Table + published by the PEI Foundation. + @param[in] This A pointer to the EFI_PEI_MP_SERVICES_PPI instance. + @param[out] ProcessorNumber The handle number of the 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 + EFI_PEI_MP_SERVICES_PPI.GetNumberOfProcessors(). + + @retval EFI_SUCCESS The current processor handle number was returned in + ProcessorNumber. + @retval EFI_INVALID_PARAMETER ProcessorNumber is NULL. +**/ +EFI_STATUS +EFIAPI +PeiWhoAmI ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_MP_SERVICES_PPI *This, + OUT UINTN *ProcessorNumber + ); + +#endif diff --git a/Core/UefiCpuPkg/CpuMpPei/X64/MpEqu.inc b/Core/UefiCpuPkg/CpuMpPei/X64/MpEqu.inc new file mode 100644 index 0000000000..00f57ce549 --- /dev/null +++ b/Core/UefiCpuPkg/CpuMpPei/X64/MpEqu.inc @@ -0,0 +1,41 @@ +;------------------------------------------------------------------------------ ; +; Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.
+; This program and the accompanying materials +; are licensed and made available under the terms and conditions of the BSD License +; which accompanies this distribution. The full text of the license may be found at +; http://opensource.org/licenses/bsd-license.php. +; +; THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +; WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +; +; Module Name: +; +; MpEqu.inc +; +; Abstract: +; +; This is the equates file for Multiple Processor support +; +;------------------------------------------------------------------------------- + +VacantFlag equ 00h +NotVacantFlag equ 0ffh + +CPU_SWITCH_STATE_IDLE equ 0 +CPU_SWITCH_STATE_STORED equ 1 +CPU_SWITCH_STATE_LOADED equ 2 + +LockLocation equ (RendezvousFunnelProcEnd - RendezvousFunnelProcStart) +StackStartAddressLocation equ LockLocation + 08h +StackSizeLocation equ LockLocation + 10h +ApProcedureLocation equ LockLocation + 18h +GdtrLocation equ LockLocation + 20h +IdtrLocation equ LockLocation + 2Ah +BufferStartLocation equ LockLocation + 34h +ModeOffsetLocation equ LockLocation + 3Ch +NumApsExecutingLoction equ LockLocation + 44h +CodeSegmentLocation equ LockLocation + 4Ch +DataSegmentLocation equ LockLocation + 54h +Cr3Location equ LockLocation + 5Ch + +;------------------------------------------------------------------------------- diff --git a/Core/UefiCpuPkg/CpuMpPei/X64/MpFuncs.asm b/Core/UefiCpuPkg/CpuMpPei/X64/MpFuncs.asm new file mode 100644 index 0000000000..4adfff3c1c --- /dev/null +++ b/Core/UefiCpuPkg/CpuMpPei/X64/MpFuncs.asm @@ -0,0 +1,290 @@ +;------------------------------------------------------------------------------ ; +; Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.
+; This program and the accompanying materials +; are licensed and made available under the terms and conditions of the BSD License +; which accompanies this distribution. The full text of the license may be found at +; http://opensource.org/licenses/bsd-license.php. +; +; THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +; WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +; +; Module Name: +; +; MpFuncs32.asm +; +; Abstract: +; +; This is the assembly code for MP support +; +;------------------------------------------------------------------------------- + +include MpEqu.inc +extern InitializeFloatingPointUnits:PROC + +.code +;------------------------------------------------------------------------------------- +;RendezvousFunnelProc procedure follows. All APs execute their procedure. This +;procedure serializes all the AP processors through an Init sequence. It must be +;noted that APs arrive here very raw...ie: real mode, no stack. +;ALSO THIS PROCEDURE IS EXECUTED BY APs ONLY ON 16 BIT MODE. HENCE THIS PROC +;IS IN MACHINE CODE. +;------------------------------------------------------------------------------------- +RendezvousFunnelProc PROC PUBLIC +RendezvousFunnelProcStart:: +; At this point CS = 0x(vv00) and ip= 0x0. +; Save BIST information to ebp firstly + db 66h, 08bh, 0e8h ; mov ebp, eax ; save BIST information + + db 8ch,0c8h ; mov ax, cs + db 8eh,0d8h ; mov ds, ax + db 8eh,0c0h ; mov es, ax + db 8eh,0d0h ; mov ss, ax + db 33h,0c0h ; xor ax, ax + db 8eh,0e0h ; mov fs, ax + db 8eh,0e8h ; mov gs, ax + + db 0BEh ; opcode of mov si, mem16 + dw BufferStartLocation ; mov si, BufferStartLocation + db 66h, 8Bh, 1Ch ; mov ebx, dword ptr [si] + + db 0BFh ; opcode of mov di, mem16 + dw ModeOffsetLocation ; mov di, ModeOffsetLocation + db 66h, 8Bh, 05h ; mov eax, [di] + db 0BFh ; opcode of mov di, mem16 + dw CodeSegmentLocation ; mov di, CodeSegmentLocation + db 66h, 8Bh, 15h ; mov edx, [di] + db 89h, 0C7h ; mov di, ax + db 83h, 0EFh, 02h ; sub di, 02h + db 89h, 15h ; mov [di], dx ; Patch long mode CS + db 83h, 0EFh, 04h ; sub di, 04h + db 66h, 01h, 0D8h ; add eax, ebx + db 66h, 89h, 05h ; mov [di], eax ; Patch address + + db 0BEh ; opcode of mov si, mem16 + dw GdtrLocation ; mov si, GdtrLocation + db 66h ; db 66h + db 2Eh, 0Fh, 01h, 14h ; lgdt fword ptr cs:[si] + + db 0BEh + dw IdtrLocation ; mov si, IdtrLocation + db 66h ; db 66h + db 2Eh,0Fh, 01h, 1Ch ; lidt fword ptr cs:[si] + + db 0BFh ; opcode of mov di, mem16 + dw DataSegmentLocation ; mov di, DataSegmentLocation + db 66h, 8Bh, 3Dh ; mov edi, [di] ; Save long mode DS in edi + + db 0BEh + dw Cr3Location ; mov si, Cr3Location + db 66h, 8Bh, 0Ch ; mov ecx, dword ptr [si] ; ECX is keeping the value of CR3 + + db 31h, 0C0h ; xor ax, ax + db 8Eh, 0D8h ; mov ds, ax ; Clear data segment + + db 0Fh, 20h, 0C0h ; mov eax, cr0 ; Get control register 0 + db 66h, 83h, 0C8h, 03h ; or eax, 000000003h ; Set PE bit (bit #0) & MP + db 0Fh, 22h, 0C0h ; mov cr0, eax + + db 0Fh, 20h, 0E0h ; mov eax, cr4 + db 66h, 0Fh, 0BAh, 0E8h, 05h ; bts eax, 5 + db 0Fh, 22h, 0E0h ; mov cr4, eax + + db 0Fh, 22h, 0D9h ; mov cr3, ecx + + db 66h, 0B9h + dd 0C0000080h ; mov ecx, 0c0000080h ; EFER MSR number. + db 0Fh, 32h ; rdmsr ; Read EFER. + db 66h, 0Fh, 0BAh, 0E8h, 08h; bts eax, 8 ; Set LME=1. + db 0Fh, 30h ; wrmsr ; Write EFER. + + db 0Fh, 20h, 0C0h ; mov eax, cr0 ; Read CR0. + db 66h, 0Fh, 0BAh, 0E8h, 1Fh; bts eax, 31 ; Set PG=1. + db 0Fh, 22h, 0C0h ; mov cr0, eax ; Write CR0. + +LONG_JUMP: + db 66h, 0EAh ; far jump + dd 0h ; 32-bit offset + dw 0h ; 16-bit selector + +LongModeStart:: + mov eax, edi + mov ds, ax + mov es, ax + mov ss, ax + + mov esi, ebx + mov edi, esi + add edi, LockLocation + mov rax, NotVacantFlag + +TestLock: + xchg qword ptr [edi], rax + cmp rax, NotVacantFlag + jz TestLock + + mov edi, esi + add edi, NumApsExecutingLoction + inc dword ptr [edi] + mov ebx, dword ptr [edi] + +ProgramStack: + mov edi, esi + add edi, StackSizeLocation + mov rax, qword ptr [edi] + mov edi, esi + add edi, StackStartAddressLocation + add rax, qword ptr [edi] + mov rsp, rax + mov qword ptr [edi], rax + +Releaselock: + mov rax, VacantFlag + mov edi, esi + add edi, LockLocation + xchg qword ptr [edi], rax + +CProcedureInvoke: + push rbp ; push BIST data + xor rbp, rbp ; clear ebp for call stack trace + push rbp + mov rbp, rsp + + mov rax, InitializeFloatingPointUnits + sub rsp, 20h + call rax ; Call assembly function to initialize FPU per UEFI spec + add rsp, 20h + + mov edx, ebx ; edx is NumApsExecuting + mov ecx, esi + add ecx, LockLocation ; rcx is address of exchange info data buffer + + mov edi, esi + add edi, ApProcedureLocation + mov rax, qword ptr [edi] + + sub rsp, 20h + call rax ; invoke C function + add rsp, 20h + jmp $ + +RendezvousFunnelProc ENDP +RendezvousFunnelProcEnd:: + +;------------------------------------------------------------------------------------- +; AsmGetAddressMap (&AddressMap); +;------------------------------------------------------------------------------------- +AsmGetAddressMap PROC + mov rax, offset RendezvousFunnelProcStart + mov qword ptr [rcx], rax + mov qword ptr [rcx + 8h], LongModeStart - RendezvousFunnelProcStart + mov qword ptr [rcx + 10h], RendezvousFunnelProcEnd - RendezvousFunnelProcStart + ret +AsmGetAddressMap ENDP + +;------------------------------------------------------------------------------------- +;AsmExchangeRole procedure follows. This procedure executed by current BSP, that is +;about to become an AP. It switches it'stack with the current AP. +;AsmExchangeRole (IN CPU_EXCHANGE_INFO *MyInfo, IN CPU_EXCHANGE_INFO *OthersInfo); +;------------------------------------------------------------------------------------- +AsmExchangeRole PROC + ; DO NOT call other functions in this function, since 2 CPU may use 1 stack + ; at the same time. If 1 CPU try to call a function, stack will be corrupted. + + push rax + push rbx + push rcx + push rdx + push rsi + push rdi + push rbp + push r8 + push r9 + push r10 + push r11 + push r12 + push r13 + push r14 + push r15 + + mov rax, cr0 + push rax + + mov rax, cr4 + push rax + + ; rsi contains MyInfo pointer + mov rsi, rcx + + ; rdi contains OthersInfo pointer + mov rdi, rdx + + ;Store EFLAGS, GDTR and IDTR regiter to stack + pushfq + sgdt fword ptr [rsi + 16] + sidt fword ptr [rsi + 26] + + ; Store the its StackPointer + mov qword ptr [rsi + 8], rsp + + ; update its switch state to STORED + mov byte ptr [rsi], CPU_SWITCH_STATE_STORED + +WaitForOtherStored: + ; wait until the other CPU finish storing its state + cmp byte ptr [rdi], CPU_SWITCH_STATE_STORED + jz OtherStored + pause + jmp WaitForOtherStored + +OtherStored: + ; Since another CPU already stored its state, load them + ; load GDTR value + lgdt fword ptr [rdi + 16] + + ; load IDTR value + lidt fword ptr [rdi + 26] + + ; load its future StackPointer + mov rsp, qword ptr [rdi + 8] + + ; update the other CPU's switch state to LOADED + mov byte ptr [rdi], CPU_SWITCH_STATE_LOADED + +WaitForOtherLoaded: + ; wait until the other CPU finish loading new state, + ; otherwise the data in stack may corrupt + cmp byte ptr [rsi], CPU_SWITCH_STATE_LOADED + jz OtherLoaded + pause + jmp WaitForOtherLoaded + +OtherLoaded: + ; since the other CPU already get the data it want, leave this procedure + popfq + + pop rax + mov cr4, rax + + pop rax + mov cr0, rax + + pop r15 + pop r14 + pop r13 + pop r12 + pop r11 + pop r10 + pop r9 + pop r8 + pop rbp + pop rdi + pop rsi + pop rdx + pop rcx + pop rbx + pop rax + + ret +AsmExchangeRole ENDP + +END diff --git a/Core/UefiCpuPkg/CpuMpPei/X64/MpFuncs.nasm b/Core/UefiCpuPkg/CpuMpPei/X64/MpFuncs.nasm new file mode 100644 index 0000000000..f19c75f913 --- /dev/null +++ b/Core/UefiCpuPkg/CpuMpPei/X64/MpFuncs.nasm @@ -0,0 +1,281 @@ +;------------------------------------------------------------------------------ ; +; Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.
+; This program and the accompanying materials +; are licensed and made available under the terms and conditions of the BSD License +; which accompanies this distribution. The full text of the license may be found at +; http://opensource.org/licenses/bsd-license.php. +; +; THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +; WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +; +; Module Name: +; +; MpFuncs.nasm +; +; Abstract: +; +; This is the assembly code for MP support +; +;------------------------------------------------------------------------------- + +%include "MpEqu.inc" +extern ASM_PFX(InitializeFloatingPointUnits) + +DEFAULT REL + +SECTION .text + +;------------------------------------------------------------------------------------- +;RendezvousFunnelProc procedure follows. All APs execute their procedure. This +;procedure serializes all the AP processors through an Init sequence. It must be +;noted that APs arrive here very raw...ie: real mode, no stack. +;ALSO THIS PROCEDURE IS EXECUTED BY APs ONLY ON 16 BIT MODE. HENCE THIS PROC +;IS IN MACHINE CODE. +;------------------------------------------------------------------------------------- +global ASM_PFX(RendezvousFunnelProc) +ASM_PFX(RendezvousFunnelProc): +RendezvousFunnelProcStart: +; At this point CS = 0x(vv00) and ip= 0x0. +; Save BIST information to ebp firstly + +BITS 16 + mov ebp, eax ; Save BIST information + + mov ax, cs + mov ds, ax + mov es, ax + mov ss, ax + xor ax, ax + mov fs, ax + mov gs, ax + + mov si, BufferStartLocation + mov ebx, [si] + + mov di, ModeOffsetLocation + mov eax, [di] + mov di, CodeSegmentLocation + mov edx, [di] + mov di, ax + sub di, 02h + mov [di],dx ; Patch long mode CS + sub di, 04h + add eax, ebx + mov [di],eax ; Patch address + + mov si, GdtrLocation +o32 lgdt [cs:si] + + mov si, IdtrLocation +o32 lidt [cs:si] + + + mov di, DataSegmentLocation + mov edi, [di] ; Save long mode DS in edi + + mov si, Cr3Location ; Save CR3 in ecx + mov ecx, [si] + + xor ax, ax + mov ds, ax ; Clear data segment + + mov eax, cr0 ; Get control register 0 + or eax, 000000003h ; Set PE bit (bit #0) & MP + mov cr0, eax + + mov eax, cr4 + bts eax, 5 + mov cr4, eax + + mov cr3, ecx ; Load CR3 + + mov ecx, 0c0000080h ; EFER MSR number + rdmsr ; Read EFER + bts eax, 8 ; Set LME=1 + wrmsr ; Write EFER + + mov eax, cr0 ; Read CR0 + bts eax, 31 ; Set PG=1 + mov cr0, eax ; Write CR0 + + jmp 0:strict dword 0 ; far jump to long mode +BITS 64 +LongModeStart: + mov eax, edi + mov ds, ax + mov es, ax + mov ss, ax + + mov esi, ebx + mov edi, esi + add edi, LockLocation + mov rax, NotVacantFlag + +TestLock: + xchg qword [edi], rax + cmp rax, NotVacantFlag + jz TestLock + + mov edi, esi + add edi, NumApsExecutingLoction + inc dword [edi] + mov ebx, [edi] + +ProgramStack: + mov edi, esi + add edi, StackSizeLocation + mov rax, qword [edi] + mov edi, esi + add edi, StackStartAddressLocation + add rax, qword [edi] + mov rsp, rax + mov qword [edi], rax + +Releaselock: + mov rax, VacantFlag + mov edi, esi + add edi, LockLocation + xchg qword [edi], rax + +CProcedureInvoke: + push rbp ; push BIST data at top of AP stack + xor rbp, rbp ; clear ebp for call stack trace + push rbp + mov rbp, rsp + + mov rax, ASM_PFX(InitializeFloatingPointUnits) + sub rsp, 20h + call rax ; Call assembly function to initialize FPU per UEFI spec + add rsp, 20h + + mov edx, ebx ; edx is NumApsExecuting + mov ecx, esi + add ecx, LockLocation ; rcx is address of exchange info data buffer + + mov edi, esi + add edi, ApProcedureLocation + mov rax, qword [edi] + + sub rsp, 20h + call rax ; invoke C function + add rsp, 20h + jmp $ + +RendezvousFunnelProcEnd: + +;------------------------------------------------------------------------------------- +; AsmGetAddressMap (&AddressMap); +;------------------------------------------------------------------------------------- +global ASM_PFX(AsmGetAddressMap) +ASM_PFX(AsmGetAddressMap): + mov rax, ASM_PFX(RendezvousFunnelProc) + mov qword [rcx], rax + mov qword [rcx + 8h], LongModeStart - RendezvousFunnelProcStart + mov qword [rcx + 10h], RendezvousFunnelProcEnd - RendezvousFunnelProcStart + ret + +;------------------------------------------------------------------------------------- +;AsmExchangeRole procedure follows. This procedure executed by current BSP, that is +;about to become an AP. It switches it'stack with the current AP. +;AsmExchangeRole (IN CPU_EXCHANGE_INFO *MyInfo, IN CPU_EXCHANGE_INFO *OthersInfo); +;------------------------------------------------------------------------------------- +global ASM_PFX(AsmExchangeRole) +ASM_PFX(AsmExchangeRole): + ; DO NOT call other functions in this function, since 2 CPU may use 1 stack + ; at the same time. If 1 CPU try to call a function, stack will be corrupted. + + push rax + push rbx + push rcx + push rdx + push rsi + push rdi + push rbp + push r8 + push r9 + push r10 + push r11 + push r12 + push r13 + push r14 + push r15 + + mov rax, cr0 + push rax + + mov rax, cr4 + push rax + + ; rsi contains MyInfo pointer + mov rsi, rcx + + ; rdi contains OthersInfo pointer + mov rdi, rdx + + ;Store EFLAGS, GDTR and IDTR regiter to stack + pushfq + sgdt [rsi + 16] + sidt [rsi + 26] + + ; Store the its StackPointer + mov [rsi + 8], rsp + + ; update its switch state to STORED + mov byte [rsi], CPU_SWITCH_STATE_STORED + +WaitForOtherStored: + ; wait until the other CPU finish storing its state + cmp byte [rdi], CPU_SWITCH_STATE_STORED + jz OtherStored + pause + jmp WaitForOtherStored + +OtherStored: + ; Since another CPU already stored its state, load them + ; load GDTR value + lgdt [rdi + 16] + + ; load IDTR value + lidt [rdi + 26] + + ; load its future StackPointer + mov rsp, [rdi + 8] + + ; update the other CPU's switch state to LOADED + mov byte [rdi], CPU_SWITCH_STATE_LOADED + +WaitForOtherLoaded: + ; wait until the other CPU finish loading new state, + ; otherwise the data in stack may corrupt + cmp byte [rsi], CPU_SWITCH_STATE_LOADED + jz OtherLoaded + pause + jmp WaitForOtherLoaded + +OtherLoaded: + ; since the other CPU already get the data it want, leave this procedure + popfq + + pop rax + mov cr4, rax + + pop rax + mov cr0, rax + + pop r15 + pop r14 + pop r13 + pop r12 + pop r11 + pop r10 + pop r9 + pop r8 + pop rbp + pop rdi + pop rsi + pop rdx + pop rcx + pop rbx + pop rax + + ret -- cgit v1.2.3