diff options
Diffstat (limited to 'Core/UefiCpuPkg/Library/MpInitLib')
-rw-r--r-- | Core/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf | 75 | ||||
-rw-r--r-- | Core/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.uni | 22 | ||||
-rw-r--r-- | Core/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c | 743 | ||||
-rw-r--r-- | Core/UefiCpuPkg/Library/MpInitLib/Ia32/MpEqu.inc | 43 | ||||
-rw-r--r-- | Core/UefiCpuPkg/Library/MpInitLib/Ia32/MpFuncs.nasm | 339 | ||||
-rw-r--r-- | Core/UefiCpuPkg/Library/MpInitLib/Microcode.c | 218 | ||||
-rw-r--r-- | Core/UefiCpuPkg/Library/MpInitLib/MpLib.c | 2126 | ||||
-rw-r--r-- | Core/UefiCpuPkg/Library/MpInitLib/MpLib.h | 594 | ||||
-rw-r--r-- | Core/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf | 70 | ||||
-rw-r--r-- | Core/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.uni | 22 | ||||
-rw-r--r-- | Core/UefiCpuPkg/Library/MpInitLib/PeiMpLib.c | 624 | ||||
-rw-r--r-- | Core/UefiCpuPkg/Library/MpInitLib/X64/MpEqu.inc | 43 | ||||
-rw-r--r-- | Core/UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm | 398 |
13 files changed, 5317 insertions, 0 deletions
diff --git a/Core/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf b/Core/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf new file mode 100644 index 0000000000..9751ba1f0d --- /dev/null +++ b/Core/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf @@ -0,0 +1,75 @@ +## @file
+# MP Initialize Library instance for DXE driver.
+#
+# Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
+# 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 = DxeMpInitLib
+ MODULE_UNI_FILE = DxeMpInitLib.uni
+ FILE_GUID = B88F7146-9834-4c55-BFAC-481CC0C33736
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.1
+ LIBRARY_CLASS = MpInitLib|DXE_DRIVER
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64
+#
+
+[Sources.IA32]
+ Ia32/MpEqu.inc
+ Ia32/MpFuncs.nasm
+
+[Sources.X64]
+ X64/MpEqu.inc
+ X64/MpFuncs.nasm
+
+[Sources.common]
+ DxeMpLib.c
+ MpLib.c
+ MpLib.h
+ Microcode.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ UefiCpuPkg/UefiCpuPkg.dec
+
+[LibraryClasses]
+ BaseLib
+ LocalApicLib
+ MemoryAllocationLib
+ HobLib
+ MtrrLib
+ CpuLib
+ UefiCpuLib
+ UefiBootServicesTableLib
+ DebugAgentLib
+
+[Protocols]
+ gEfiTimerArchProtocolGuid ## SOMETIMES_CONSUMES
+
+[Guids]
+ gEfiEventExitBootServicesGuid ## CONSUMES ## Event
+ gEfiEventLegacyBootGuid ## CONSUMES ## Event
+
+[Pcd]
+ gUefiCpuPkgTokenSpaceGuid.PcdCpuMaxLogicalProcessorNumber ## CONSUMES
+ gUefiCpuPkgTokenSpaceGuid.PcdCpuApInitTimeOutInMicroSeconds ## SOMETIMES_CONSUMES
+ gUefiCpuPkgTokenSpaceGuid.PcdCpuApStackSize ## CONSUMES
+ gUefiCpuPkgTokenSpaceGuid.PcdCpuMicrocodePatchAddress ## CONSUMES
+ gUefiCpuPkgTokenSpaceGuid.PcdCpuMicrocodePatchRegionSize ## CONSUMES
+ gUefiCpuPkgTokenSpaceGuid.PcdCpuApLoopMode ## CONSUMES
+ gUefiCpuPkgTokenSpaceGuid.PcdCpuApTargetCstate ## SOMETIMES_CONSUMES
+
diff --git a/Core/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.uni b/Core/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.uni new file mode 100644 index 0000000000..99d79974be --- /dev/null +++ b/Core/UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.uni @@ -0,0 +1,22 @@ +// /** @file
+// MP Initialize Library instance for DXE driver.
+//
+// MP Initialize Library instance for DXE driver.
+//
+// Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
+//
+// 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 "MP Initialize Library instance for DXE driver."
+
+#string STR_MODULE_DESCRIPTION #language en-US "MP Initialize Library instance for DXE driver."
+
diff --git a/Core/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c b/Core/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c new file mode 100644 index 0000000000..b393244e05 --- /dev/null +++ b/Core/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c @@ -0,0 +1,743 @@ +/** @file
+ MP initialize support functions for DXE phase.
+
+ Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
+ 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 "MpLib.h"
+
+#include <Library/UefiLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/DebugAgentLib.h>
+
+#include <Protocol/Timer.h>
+
+#define AP_CHECK_INTERVAL (EFI_TIMER_PERIOD_MILLISECONDS (100))
+#define AP_SAFE_STACK_SIZE 128
+
+CPU_MP_DATA *mCpuMpData = NULL;
+EFI_EVENT mCheckAllApsEvent = NULL;
+EFI_EVENT mMpInitExitBootServicesEvent = NULL;
+EFI_EVENT mLegacyBootEvent = NULL;
+volatile BOOLEAN mStopCheckAllApsStatus = TRUE;
+VOID *mReservedApLoopFunc = NULL;
+UINTN mReservedTopOfApStack;
+volatile UINT32 mNumberToFinish = 0;
+
+/**
+ Enable Debug Agent to support source debugging on AP function.
+
+**/
+VOID
+EnableDebugAgent (
+ VOID
+ )
+{
+ //
+ // Initialize Debug Agent to support source level debug in DXE phase
+ //
+ InitializeDebugAgent (DEBUG_AGENT_INIT_DXE_AP, NULL, NULL);
+}
+
+/**
+ Get the pointer to CPU MP Data structure.
+
+ @return The pointer to CPU MP Data structure.
+**/
+CPU_MP_DATA *
+GetCpuMpData (
+ VOID
+ )
+{
+ ASSERT (mCpuMpData != NULL);
+ return mCpuMpData;
+}
+
+/**
+ Save the pointer to CPU MP Data structure.
+
+ @param[in] CpuMpData The pointer to CPU MP Data structure will be saved.
+**/
+VOID
+SaveCpuMpData (
+ IN CPU_MP_DATA *CpuMpData
+ )
+{
+ mCpuMpData = CpuMpData;
+}
+
+/**
+ Allocate reset vector buffer.
+
+ @param[in, out] CpuMpData The pointer to CPU MP Data structure.
+**/
+VOID
+AllocateResetVector (
+ IN OUT CPU_MP_DATA *CpuMpData
+ )
+{
+ EFI_STATUS Status;
+ UINTN ApResetVectorSize;
+ EFI_PHYSICAL_ADDRESS StartAddress;
+
+ if (CpuMpData->SaveRestoreFlag) {
+ BackupAndPrepareWakeupBuffer (CpuMpData);
+ } else {
+ ApResetVectorSize = CpuMpData->AddressMap.RendezvousFunnelSize +
+ sizeof (MP_CPU_EXCHANGE_INFO);
+
+ StartAddress = BASE_1MB;
+ Status = gBS->AllocatePages (
+ AllocateMaxAddress,
+ EfiACPIMemoryNVS,
+ EFI_SIZE_TO_PAGES (ApResetVectorSize),
+ &StartAddress
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ CpuMpData->WakeupBuffer = (UINTN) StartAddress;
+ CpuMpData->MpCpuExchangeInfo = (MP_CPU_EXCHANGE_INFO *) (UINTN)
+ (CpuMpData->WakeupBuffer + CpuMpData->AddressMap.RendezvousFunnelSize);
+ //
+ // copy AP reset code in it
+ //
+ CopyMem (
+ (VOID *) CpuMpData->WakeupBuffer,
+ (VOID *) CpuMpData->AddressMap.RendezvousFunnelAddress,
+ CpuMpData->AddressMap.RendezvousFunnelSize
+ );
+ }
+}
+
+/**
+ Free AP reset vector buffer.
+
+ @param[in] CpuMpData The pointer to CPU MP Data structure.
+**/
+VOID
+FreeResetVector (
+ IN CPU_MP_DATA *CpuMpData
+ )
+{
+ EFI_STATUS Status;
+ UINTN ApResetVectorSize;
+
+ if (CpuMpData->SaveRestoreFlag) {
+ RestoreWakeupBuffer (CpuMpData);
+ } else {
+ ApResetVectorSize = CpuMpData->AddressMap.RendezvousFunnelSize +
+ sizeof (MP_CPU_EXCHANGE_INFO);
+ Status = gBS->FreePages(
+ (EFI_PHYSICAL_ADDRESS)CpuMpData->WakeupBuffer,
+ EFI_SIZE_TO_PAGES (ApResetVectorSize)
+ );
+ ASSERT_EFI_ERROR (Status);
+ }
+}
+
+/**
+ Checks APs status and updates APs status if needed.
+
+**/
+VOID
+CheckAndUpdateApsStatus (
+ VOID
+ )
+{
+ UINTN ProcessorNumber;
+ EFI_STATUS Status;
+ CPU_MP_DATA *CpuMpData;
+
+ CpuMpData = GetCpuMpData ();
+
+ //
+ // First, check whether pending StartupAllAPs() exists.
+ //
+ if (CpuMpData->WaitEvent != NULL) {
+
+ Status = CheckAllAPs ();
+ //
+ // If all APs finish for StartupAllAPs(), signal the WaitEvent for it.
+ //
+ if (Status != EFI_NOT_READY) {
+ Status = gBS->SignalEvent (CpuMpData->WaitEvent);
+ CpuMpData->WaitEvent = NULL;
+ }
+ }
+
+ //
+ // Second, check whether pending StartupThisAPs() callings exist.
+ //
+ for (ProcessorNumber = 0; ProcessorNumber < CpuMpData->CpuCount; ProcessorNumber++) {
+
+ if (CpuMpData->CpuData[ProcessorNumber].WaitEvent == NULL) {
+ continue;
+ }
+
+ Status = CheckThisAP (ProcessorNumber);
+
+ if (Status != EFI_NOT_READY) {
+ gBS->SignalEvent (CpuMpData->CpuData[ProcessorNumber].WaitEvent);
+ CpuMpData->CpuData[ProcessorNumber].WaitEvent = NULL;
+ }
+ }
+}
+
+/**
+ Checks APs' status periodically.
+
+ This function is triggered by timer periodically to check the
+ state of APs for StartupAllAPs() and StartupThisAP() executed
+ in non-blocking mode.
+
+ @param[in] Event Event triggered.
+ @param[in] Context Parameter passed with the event.
+
+**/
+VOID
+EFIAPI
+CheckApsStatus (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ //
+ // If CheckApsStatus() is not stopped, otherwise return immediately.
+ //
+ if (!mStopCheckAllApsStatus) {
+ CheckAndUpdateApsStatus ();
+ }
+}
+
+/**
+ Get Protected mode code segment from current GDT table.
+
+ @return Protected mode code segment value.
+**/
+UINT16
+GetProtectedModeCS (
+ VOID
+ )
+{
+ IA32_DESCRIPTOR GdtrDesc;
+ IA32_SEGMENT_DESCRIPTOR *GdtEntry;
+ UINTN GdtEntryCount;
+ UINT16 Index;
+
+ Index = (UINT16) -1;
+ AsmReadGdtr (&GdtrDesc);
+ GdtEntryCount = (GdtrDesc.Limit + 1) / sizeof (IA32_SEGMENT_DESCRIPTOR);
+ GdtEntry = (IA32_SEGMENT_DESCRIPTOR *) GdtrDesc.Base;
+ for (Index = 0; Index < GdtEntryCount; Index++) {
+ if (GdtEntry->Bits.L == 0) {
+ if (GdtEntry->Bits.Type > 8 && GdtEntry->Bits.L == 0) {
+ break;
+ }
+ }
+ GdtEntry++;
+ }
+ ASSERT (Index != -1);
+ return Index * 8;
+}
+
+/**
+ Do sync on APs.
+
+ @param[in, out] Buffer Pointer to private data buffer.
+**/
+VOID
+EFIAPI
+RelocateApLoop (
+ IN OUT VOID *Buffer
+ )
+{
+ CPU_MP_DATA *CpuMpData;
+ BOOLEAN MwaitSupport;
+ ASM_RELOCATE_AP_LOOP AsmRelocateApLoopFunc;
+ UINTN ProcessorNumber;
+
+ MpInitLibWhoAmI (&ProcessorNumber);
+ CpuMpData = GetCpuMpData ();
+ MwaitSupport = IsMwaitSupport ();
+ AsmRelocateApLoopFunc = (ASM_RELOCATE_AP_LOOP) (UINTN) mReservedApLoopFunc;
+ AsmRelocateApLoopFunc (
+ MwaitSupport,
+ CpuMpData->ApTargetCState,
+ CpuMpData->PmCodeSegment,
+ mReservedTopOfApStack - ProcessorNumber * AP_SAFE_STACK_SIZE,
+ (UINTN) &mNumberToFinish
+ );
+ //
+ // It should never reach here
+ //
+ ASSERT (FALSE);
+}
+
+/**
+ Callback function for ExitBootServices.
+
+ @param[in] Event Event whose notification function is being invoked.
+ @param[in] Context The pointer to the notification function's context,
+ which is implementation-dependent.
+
+**/
+VOID
+EFIAPI
+MpInitChangeApLoopCallback (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ CPU_MP_DATA *CpuMpData;
+
+ CpuMpData = GetCpuMpData ();
+ CpuMpData->SaveRestoreFlag = TRUE;
+ CpuMpData->PmCodeSegment = GetProtectedModeCS ();
+ CpuMpData->ApLoopMode = PcdGet8 (PcdCpuApLoopMode);
+ mNumberToFinish = CpuMpData->CpuCount - 1;
+ WakeUpAP (CpuMpData, TRUE, 0, RelocateApLoop, NULL);
+ while (mNumberToFinish > 0) {
+ CpuPause ();
+ }
+ DEBUG ((DEBUG_INFO, "%a() done!\n", __FUNCTION__));
+}
+
+/**
+ Initialize global data for MP support.
+
+ @param[in] CpuMpData The pointer to CPU MP Data structure.
+**/
+VOID
+InitMpGlobalData (
+ IN CPU_MP_DATA *CpuMpData
+ )
+{
+ EFI_STATUS Status;
+ EFI_PHYSICAL_ADDRESS Address;
+ UINTN ApSafeBufferSize;
+
+ SaveCpuMpData (CpuMpData);
+
+ if (CpuMpData->CpuCount == 1) {
+ //
+ // If only BSP exists, return
+ //
+ return;
+ }
+
+ //
+ // Avoid APs access invalid buffer data which allocated by BootServices,
+ // so we will allocate reserved data for AP loop code. We also need to
+ // allocate this buffer below 4GB due to APs may be transferred to 32bit
+ // protected mode on long mode DXE.
+ // Allocating it in advance since memory services are not available in
+ // Exit Boot Services callback function.
+ //
+ ApSafeBufferSize = CpuMpData->AddressMap.RelocateApLoopFuncSize;
+ ApSafeBufferSize += CpuMpData->CpuCount * AP_SAFE_STACK_SIZE;
+
+ Address = BASE_4GB - 1;
+ Status = gBS->AllocatePages (
+ AllocateMaxAddress,
+ EfiReservedMemoryType,
+ EFI_SIZE_TO_PAGES (ApSafeBufferSize),
+ &Address
+ );
+ ASSERT_EFI_ERROR (Status);
+ mReservedApLoopFunc = (VOID *) (UINTN) Address;
+ ASSERT (mReservedApLoopFunc != NULL);
+ mReservedTopOfApStack = (UINTN) Address + EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (ApSafeBufferSize));
+ ASSERT ((mReservedTopOfApStack & (UINTN)(CPU_STACK_ALIGNMENT - 1)) == 0);
+ CopyMem (
+ mReservedApLoopFunc,
+ CpuMpData->AddressMap.RelocateApLoopFuncAddress,
+ CpuMpData->AddressMap.RelocateApLoopFuncSize
+ );
+
+ Status = gBS->CreateEvent (
+ EVT_TIMER | EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ CheckApsStatus,
+ NULL,
+ &mCheckAllApsEvent
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ //
+ // Set timer to check all APs status.
+ //
+ Status = gBS->SetTimer (
+ mCheckAllApsEvent,
+ TimerPeriodic,
+ AP_CHECK_INTERVAL
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ Status = gBS->CreateEvent (
+ EVT_SIGNAL_EXIT_BOOT_SERVICES,
+ TPL_CALLBACK,
+ MpInitChangeApLoopCallback,
+ NULL,
+ &mMpInitExitBootServicesEvent
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ Status = gBS->CreateEventEx (
+ EVT_NOTIFY_SIGNAL,
+ TPL_CALLBACK,
+ MpInitChangeApLoopCallback,
+ NULL,
+ &gEfiEventLegacyBootGuid,
+ &mLegacyBootEvent
+ );
+ ASSERT_EFI_ERROR (Status);
+}
+
+/**
+ This service executes a caller provided function on all enabled APs.
+
+ @param[in] Procedure A pointer to the function to be run on
+ enabled APs of the system. See type
+ EFI_AP_PROCEDURE.
+ @param[in] SingleThread If TRUE, then all the enabled APs execute
+ the function specified by Procedure one by
+ one, in ascending order of processor handle
+ number. If FALSE, then all the enabled APs
+ execute the function specified by Procedure
+ simultaneously.
+ @param[in] WaitEvent The event created by the caller with CreateEvent()
+ service. If it is NULL, then execute in
+ blocking mode. BSP waits until all APs finish
+ or TimeoutInMicroSeconds expires. If it's
+ not NULL, then execute in non-blocking mode.
+ BSP requests the function specified by
+ Procedure to be started on all the enabled
+ APs, and go on executing immediately. If
+ all return from Procedure, or TimeoutInMicroSeconds
+ expires, this event is signaled. The BSP
+ can use the CheckEvent() or WaitForEvent()
+ services to check the state of event. Type
+ EFI_EVENT is defined in CreateEvent() in
+ the Unified Extensible Firmware Interface
+ Specification.
+ @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for
+ APs to return from Procedure, either for
+ blocking or non-blocking mode. 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 MpInitLibStartupAllAPs() or
+ MPInitLibStartupThisAP().
+ If the timeout expires in blocking mode,
+ BSP returns EFI_TIMEOUT. If the timeout
+ expires in non-blocking mode, WaitEvent
+ is signaled with SignalEvent().
+ @param[in] ProcedureArgument The parameter passed into Procedure for
+ all APs.
+ @param[out] FailedCpuList If NULL, this parameter is ignored. Otherwise,
+ if all APs finish successfully, then its
+ content is set to NULL. If not all APs
+ finish before timeout expires, then its
+ content is set to address of the buffer
+ holding handle numbers of the failed APs.
+ The buffer is allocated by MP Initialization
+ library, and it's the caller's responsibility to
+ free the buffer with FreePool() service.
+ In blocking mode, it is ready for consumption
+ when the call returns. In non-blocking mode,
+ it is ready when WaitEvent is signaled. The
+ list of failed CPU is terminated by
+ END_OF_CPU_LIST.
+
+ @retval EFI_SUCCESS In blocking mode, all APs have finished before
+ the timeout expired.
+ @retval EFI_SUCCESS In non-blocking mode, function has been dispatched
+ to all enabled APs.
+ @retval EFI_UNSUPPORTED A non-blocking mode request was made after the
+ UEFI event EFI_EVENT_GROUP_READY_TO_BOOT was
+ signaled.
+ @retval EFI_UNSUPPORTED WaitEvent is not NULL if non-blocking mode is not
+ supported.
+ @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_NOT_READY MP Initialize Library is not initialized.
+ @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
+MpInitLibStartupAllAPs (
+ IN EFI_AP_PROCEDURE Procedure,
+ IN BOOLEAN SingleThread,
+ IN EFI_EVENT WaitEvent OPTIONAL,
+ IN UINTN TimeoutInMicroseconds,
+ IN VOID *ProcedureArgument OPTIONAL,
+ OUT UINTN **FailedCpuList OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // Temporarily stop checkAllApsStatus for avoid resource dead-lock.
+ //
+ mStopCheckAllApsStatus = TRUE;
+
+ Status = StartupAllAPsWorker (
+ Procedure,
+ SingleThread,
+ WaitEvent,
+ TimeoutInMicroseconds,
+ ProcedureArgument,
+ FailedCpuList
+ );
+
+ //
+ // Start checkAllApsStatus
+ //
+ mStopCheckAllApsStatus = FALSE;
+
+ return Status;
+}
+
+/**
+ This service lets the caller get one enabled AP to execute a caller-provided
+ function.
+
+ @param[in] Procedure A pointer to the function to be run on the
+ designated AP of the system. See type
+ EFI_AP_PROCEDURE.
+ @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
+ MpInitLibGetNumberOfProcessors().
+ @param[in] WaitEvent The event created by the caller with CreateEvent()
+ service. If it is NULL, then execute in
+ blocking mode. BSP waits until this AP finish
+ or TimeoutInMicroSeconds expires. If it's
+ not NULL, then execute in non-blocking mode.
+ BSP requests the function specified by
+ Procedure to be started on this AP,
+ and go on executing immediately. If this AP
+ return from Procedure or TimeoutInMicroSeconds
+ expires, this event is signaled. The BSP
+ can use the CheckEvent() or WaitForEvent()
+ services to check the state of event. Type
+ EFI_EVENT is defined in CreateEvent() in
+ the Unified Extensible Firmware Interface
+ Specification.
+ @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for
+ this AP to finish this Procedure, either for
+ blocking or non-blocking mode. Zero means
+ infinity. If the timeout expires before
+ this AP returns from Procedure, then Procedure
+ on the AP is terminated. The
+ AP is available for next function assigned
+ by MpInitLibStartupAllAPs() or
+ MpInitLibStartupThisAP().
+ If the timeout expires in blocking mode,
+ BSP returns EFI_TIMEOUT. If the timeout
+ expires in non-blocking mode, WaitEvent
+ is signaled with SignalEvent().
+ @param[in] ProcedureArgument The parameter passed into Procedure on the
+ specified AP.
+ @param[out] Finished If NULL, this parameter is ignored. In
+ blocking mode, this parameter is ignored.
+ In non-blocking mode, if AP returns from
+ Procedure before the timeout expires, its
+ content is set to TRUE. Otherwise, the
+ value is set to FALSE. The caller can
+ determine if the AP returned from Procedure
+ by evaluating this value.
+
+ @retval EFI_SUCCESS In blocking mode, specified AP finished before
+ the timeout expires.
+ @retval EFI_SUCCESS In non-blocking mode, the function has been
+ dispatched to specified AP.
+ @retval EFI_UNSUPPORTED A non-blocking mode request was made after the
+ UEFI event EFI_EVENT_GROUP_READY_TO_BOOT was
+ signaled.
+ @retval EFI_UNSUPPORTED WaitEvent is not NULL if non-blocking mode is not
+ supported.
+ @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_READY The specified AP is busy.
+ @retval EFI_NOT_READY MP Initialize Library is not initialized.
+ @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
+MpInitLibStartupThisAP (
+ IN EFI_AP_PROCEDURE Procedure,
+ IN UINTN ProcessorNumber,
+ IN EFI_EVENT WaitEvent OPTIONAL,
+ IN UINTN TimeoutInMicroseconds,
+ IN VOID *ProcedureArgument OPTIONAL,
+ OUT BOOLEAN *Finished OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // temporarily stop checkAllApsStatus for avoid resource dead-lock.
+ //
+ mStopCheckAllApsStatus = TRUE;
+
+ Status = StartupThisAPWorker (
+ Procedure,
+ ProcessorNumber,
+ WaitEvent,
+ TimeoutInMicroseconds,
+ ProcedureArgument,
+ Finished
+ );
+
+ mStopCheckAllApsStatus = FALSE;
+
+ 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.
+
+ @param[in] ProcessorNumber The handle number of AP that is to become the new
+ BSP. The range is from 0 to the total number of
+ logical processors minus 1. The total number of
+ logical processors can be retrieved by
+ MpInitLibGetNumberOfProcessors().
+ @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_DEVICE_ERROR 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.
+ @retval EFI_NOT_READY MP Initialize Library is not initialized.
+
+**/
+EFI_STATUS
+EFIAPI
+MpInitLibSwitchBSP (
+ IN UINTN ProcessorNumber,
+ IN BOOLEAN EnableOldBSP
+ )
+{
+ EFI_STATUS Status;
+ EFI_TIMER_ARCH_PROTOCOL *Timer;
+ UINT64 TimerPeriod;
+
+ TimerPeriod = 0;
+ //
+ // Locate Timer Arch Protocol
+ //
+ Status = gBS->LocateProtocol (&gEfiTimerArchProtocolGuid, NULL, (VOID **) &Timer);
+ if (EFI_ERROR (Status)) {
+ Timer = NULL;
+ }
+
+ if (Timer != NULL) {
+ //
+ // Save current rate of DXE Timer
+ //
+ Timer->GetTimerPeriod (Timer, &TimerPeriod);
+ //
+ // Disable DXE Timer and drain pending interrupts
+ //
+ Timer->SetTimerPeriod (Timer, 0);
+ }
+
+ Status = SwitchBSPWorker (ProcessorNumber, EnableOldBSP);
+
+ if (Timer != NULL) {
+ //
+ // Enable and restore rate of DXE Timer
+ //
+ Timer->SetTimerPeriod (Timer, TimerPeriod);
+ }
+
+ return Status;
+}
+
+/**
+ This service lets the caller enable or disable an AP from this point onward.
+ This service may only be called from the BSP.
+
+ @param[in] ProcessorNumber The handle number of AP.
+ The range is from 0 to the total number of
+ logical processors minus 1. The total number of
+ logical processors can be retrieved by
+ MpInitLibGetNumberOfProcessors().
+ @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_MP_SERVICES_PROTOCOL.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.
+ @retval EFI_NOT_READY MP Initialize Library is not initialized.
+
+**/
+EFI_STATUS
+EFIAPI
+MpInitLibEnableDisableAP (
+ IN UINTN ProcessorNumber,
+ IN BOOLEAN EnableAP,
+ IN UINT32 *HealthFlag OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ BOOLEAN TempStopCheckState;
+
+ TempStopCheckState = FALSE;
+ //
+ // temporarily stop checkAllAPsStatus for initialize parameters.
+ //
+ if (!mStopCheckAllApsStatus) {
+ mStopCheckAllApsStatus = TRUE;
+ TempStopCheckState = TRUE;
+ }
+
+ Status = EnableDisableApWorker (ProcessorNumber, EnableAP, HealthFlag);
+
+ if (TempStopCheckState) {
+ mStopCheckAllApsStatus = FALSE;
+ }
+
+ return Status;
+}
diff --git a/Core/UefiCpuPkg/Library/MpInitLib/Ia32/MpEqu.inc b/Core/UefiCpuPkg/Library/MpInitLib/Ia32/MpEqu.inc new file mode 100644 index 0000000000..62762308e2 --- /dev/null +++ b/Core/UefiCpuPkg/Library/MpInitLib/Ia32/MpEqu.inc @@ -0,0 +1,43 @@ +;------------------------------------------------------------------------------ ;
+; Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.<BR>
+; 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
+NumApsExecutingLocation equ LockLocation + 24h
+CodeSegmentLocation equ LockLocation + 28h
+DataSegmentLocation equ LockLocation + 2Ch
+EnableExecuteDisableLocation equ LockLocation + 30h
+Cr3Location equ LockLocation + 34h
+InitFlagLocation equ LockLocation + 38h
+CpuInfoLocation equ LockLocation + 3Ch
+
diff --git a/Core/UefiCpuPkg/Library/MpInitLib/Ia32/MpFuncs.nasm b/Core/UefiCpuPkg/Library/MpInitLib/Ia32/MpFuncs.nasm new file mode 100644 index 0000000000..52363e6e08 --- /dev/null +++ b/Core/UefiCpuPkg/Library/MpInitLib/Ia32/MpFuncs.nasm @@ -0,0 +1,339 @@ +;------------------------------------------------------------------------------ ;
+; Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.<BR>
+; 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, EnableExecuteDisableLocation
+ cmp byte [edi], 0
+ jz SkipEnableExecuteDisable
+
+ ;
+ ; Enable IA32 PAE execute disable
+ ;
+
+ mov ecx, 0xc0000080
+ rdmsr
+ bts eax, 11
+ wrmsr
+
+ mov edi, esi
+ add edi, Cr3Location
+ mov eax, dword [edi]
+ mov cr3, eax
+
+ mov eax, cr4
+ bts eax, 5
+ mov cr4, eax
+
+ mov eax, cr0
+ bts eax, 31
+ mov cr0, eax
+
+SkipEnableExecuteDisable:
+ mov edi, esi
+ add edi, InitFlagLocation
+ cmp dword [edi], 1 ; 1 == ApInitConfig
+ jnz GetApicId
+
+ ; AP init
+ mov edi, esi
+ add edi, LockLocation
+ mov eax, NotVacantFlag
+
+TestLock:
+ xchg [edi], eax
+ cmp eax, NotVacantFlag
+ jz TestLock
+
+ mov ecx, esi
+ add ecx, NumApsExecutingLocation
+ inc dword [ecx]
+ mov ebx, [ecx]
+
+Releaselock:
+ mov eax, VacantFlag
+ xchg [edi], eax
+
+ mov edi, esi
+ add edi, StackSizeLocation
+ mov eax, [edi]
+ mov ecx, ebx
+ inc ecx
+ mul ecx ; EAX = StackSize * (CpuNumber + 1)
+ mov edi, esi
+ add edi, StackStartAddressLocation
+ add eax, [edi]
+ mov esp, eax
+ jmp CProcedureInvoke
+
+GetApicId:
+ mov eax, 0
+ cpuid
+ cmp eax, 0bh
+ jb NoX2Apic ; CPUID level below CPUID_EXTENDED_TOPOLOGY
+
+ mov eax, 0bh
+ xor ecx, ecx
+ cpuid
+ test ebx, 0ffffh
+ jz NoX2Apic ; CPUID.0BH:EBX[15:0] is zero
+
+ ; Processor is x2APIC capable; 32-bit x2APIC ID is already in EDX
+ jmp GetProcessorNumber
+
+NoX2Apic:
+ ; Processor is not x2APIC capable, so get 8-bit APIC ID
+ mov eax, 1
+ cpuid
+ shr ebx, 24
+ mov edx, ebx
+
+GetProcessorNumber:
+ ;
+ ; Get processor number for this AP
+ ; Note that BSP may become an AP due to SwitchBsp()
+ ;
+ xor ebx, ebx
+ lea eax, [esi + CpuInfoLocation]
+ mov edi, [eax]
+
+GetNextProcNumber:
+ cmp [edi], edx ; APIC ID match?
+ jz ProgramStack
+ add edi, 20
+ inc ebx
+ jmp GetNextProcNumber
+
+ProgramStack:
+ mov esp, [edi + 12]
+
+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:
+
+;-------------------------------------------------------------------------------------
+; AsmRelocateApLoop (MwaitSupport, ApTargetCState, PmCodeSegment, TopOfApStack, CountTofinish);
+;-------------------------------------------------------------------------------------
+global ASM_PFX(AsmRelocateApLoop)
+ASM_PFX(AsmRelocateApLoop):
+AsmRelocateApLoopStart:
+ mov eax, esp
+ mov esp, [eax + 16] ; TopOfApStack
+ push dword [eax] ; push return address for stack trace
+ push ebp
+ mov ebp, esp
+ mov ebx, [eax + 8] ; ApTargetCState
+ mov ecx, [eax + 4] ; MwaitSupport
+ mov eax, [eax + 20] ; CountTofinish
+ lock dec dword [eax] ; (*CountTofinish)--
+ cmp cl, 1 ; Check mwait-monitor support
+ jnz HltLoop
+MwaitLoop:
+ mov eax, esp
+ xor ecx, ecx
+ xor edx, edx
+ monitor
+ mov eax, ebx ; Mwait Cx, Target C-State per eax[7:4]
+ shl eax, 4
+ mwait
+ jmp MwaitLoop
+HltLoop:
+ cli
+ hlt
+ jmp HltLoop
+AsmRelocateApLoopEnd:
+
+;-------------------------------------------------------------------------------------
+; 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
+ mov dword [ebx + 0Ch], AsmRelocateApLoopStart
+ mov dword [ebx + 10h], AsmRelocateApLoopEnd - AsmRelocateApLoopStart
+
+ 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/Library/MpInitLib/Microcode.c b/Core/UefiCpuPkg/Library/MpInitLib/Microcode.c new file mode 100644 index 0000000000..982995be7d --- /dev/null +++ b/Core/UefiCpuPkg/Library/MpInitLib/Microcode.c @@ -0,0 +1,218 @@ +/** @file
+ Implementation of loading microcode on processors.
+
+ Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.<BR>
+ 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 "MpLib.h"
+
+/**
+ Get microcode update signature of currently loaded microcode update.
+
+ @return Microcode signature.
+**/
+UINT32
+GetCurrentMicrocodeSignature (
+ VOID
+ )
+{
+ MSR_IA32_BIOS_SIGN_ID_REGISTER BiosSignIdMsr;
+
+ AsmWriteMsr64 (MSR_IA32_BIOS_SIGN_ID, 0);
+ AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, NULL, NULL);
+ BiosSignIdMsr.Uint64 = AsmReadMsr64 (MSR_IA32_BIOS_SIGN_ID);
+ return BiosSignIdMsr.Bits.MicrocodeUpdateSignature;
+}
+
+/**
+ Detect whether specified processor can find matching microcode patch and load it.
+
+ @param[in] CpuMpData The pointer to CPU MP Data structure.
+**/
+VOID
+MicrocodeDetect (
+ IN CPU_MP_DATA *CpuMpData
+ )
+{
+ UINT64 MicrocodePatchAddress;
+ UINT64 MicrocodePatchRegionSize;
+ UINT32 ExtendedTableLength;
+ UINT32 ExtendedTableCount;
+ CPU_MICROCODE_EXTENDED_TABLE *ExtendedTable;
+ CPU_MICROCODE_EXTENDED_TABLE_HEADER *ExtendedTableHeader;
+ CPU_MICROCODE_HEADER *MicrocodeEntryPoint;
+ UINTN MicrocodeEnd;
+ UINTN Index;
+ UINT8 PlatformId;
+ CPUID_VERSION_INFO_EAX Eax;
+ UINT32 CurrentRevision;
+ UINT32 LatestRevision;
+ UINTN TotalSize;
+ UINT32 CheckSum32;
+ BOOLEAN CorrectMicrocode;
+ VOID *MicrocodeData;
+ MSR_IA32_PLATFORM_ID_REGISTER PlatformIdMsr;
+
+ 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 sCPUID data.
+ //
+ AsmCpuid (CPUID_VERSION_INFO, &Eax.Uint32, NULL, NULL, NULL);
+
+ //
+ // The index of platform information resides in bits 50:52 of MSR IA32_PLATFORM_ID
+ //
+ PlatformIdMsr.Uint64 = AsmReadMsr64 (MSR_IA32_PLATFORM_ID);
+ PlatformId = (UINT8) PlatformIdMsr.Bits.PlatformId;
+
+ LatestRevision = 0;
+ MicrocodeData = NULL;
+ MicrocodeEnd = (UINTN) (MicrocodePatchAddress + MicrocodePatchRegionSize);
+ MicrocodeEntryPoint = (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
+ // because the padding data should not include 0x00000001 and it should be the repeated
+ // byte format (like 0xXYXYXYXY....).
+ //
+ if (MicrocodeEntryPoint->ProcessorSignature.Uint32 == Eax.Uint32 &&
+ MicrocodeEntryPoint->UpdateRevision > LatestRevision &&
+ (MicrocodeEntryPoint->ProcessorFlags & (1 << PlatformId))
+ ) {
+ if (MicrocodeEntryPoint->DataSize == 0) {
+ CheckSum32 = CalculateSum32 ((UINT32 *) MicrocodeEntryPoint, 2048);
+ } else {
+ CheckSum32 = CalculateSum32 (
+ (UINT32 *) MicrocodeEntryPoint,
+ MicrocodeEntryPoint->DataSize + sizeof (CPU_MICROCODE_HEADER)
+ );
+ }
+ if (CheckSum32 == 0) {
+ CorrectMicrocode = TRUE;
+ }
+ } else if ((MicrocodeEntryPoint->DataSize != 0) &&
+ (MicrocodeEntryPoint->UpdateRevision > LatestRevision)) {
+ ExtendedTableLength = MicrocodeEntryPoint->TotalSize - (MicrocodeEntryPoint->DataSize +
+ sizeof (CPU_MICROCODE_HEADER));
+ if (ExtendedTableLength != 0) {
+ //
+ // Extended Table exist, check if the CPU in support list
+ //
+ ExtendedTableHeader = (CPU_MICROCODE_EXTENDED_TABLE_HEADER *) ((UINT8 *) (MicrocodeEntryPoint)
+ + MicrocodeEntryPoint->DataSize + sizeof (CPU_MICROCODE_HEADER));
+ //
+ // Calculate Extended Checksum
+ //
+ if ((ExtendedTableLength % 4) == 0) {
+ CheckSum32 = CalculateSum32 ((UINT32 *) ExtendedTableHeader, ExtendedTableLength);
+ if (CheckSum32 == 0) {
+ //
+ // Checksum correct
+ //
+ ExtendedTableCount = ExtendedTableHeader->ExtendedSignatureCount;
+ ExtendedTable = (CPU_MICROCODE_EXTENDED_TABLE *) (ExtendedTableHeader + 1);
+ for (Index = 0; Index < ExtendedTableCount; Index ++) {
+ CheckSum32 = CalculateSum32 ((UINT32 *) ExtendedTable, sizeof(CPU_MICROCODE_EXTENDED_TABLE));
+ if (CheckSum32 == 0) {
+ //
+ // Verify Header
+ //
+ if ((ExtendedTable->ProcessorSignature.Uint32 == Eax.Uint32) &&
+ (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 = (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;
+ MicrocodeData = (VOID *) ((UINTN) MicrocodeEntryPoint + sizeof (CPU_MICROCODE_HEADER));
+ }
+
+ MicrocodeEntryPoint = (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.
+ //
+ ASSERT (MicrocodeData != NULL);
+ AsmWriteMsr64 (
+ MSR_IA32_BIOS_UPDT_TRIG,
+ (UINT64) (UINTN) MicrocodeData
+ );
+ //
+ // Get and check new microcode signature
+ //
+ CurrentRevision = GetCurrentMicrocodeSignature ();
+ if (CurrentRevision != LatestRevision) {
+ AcquireSpinLock(&CpuMpData->MpLock);
+ DEBUG ((EFI_D_ERROR, "Updated microcode signature [0x%08x] does not match \
+ loaded microcode signature [0x%08x]\n", CurrentRevision, LatestRevision));
+ ReleaseSpinLock(&CpuMpData->MpLock);
+ }
+ }
+}
diff --git a/Core/UefiCpuPkg/Library/MpInitLib/MpLib.c b/Core/UefiCpuPkg/Library/MpInitLib/MpLib.c new file mode 100644 index 0000000000..03d6c2d89e --- /dev/null +++ b/Core/UefiCpuPkg/Library/MpInitLib/MpLib.c @@ -0,0 +1,2126 @@ +/** @file
+ CPU MP Initialize Library common functions.
+
+ Copyright (c) 2016 - 2017, Intel Corporation. All rights reserved.<BR>
+ 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 "MpLib.h"
+
+EFI_GUID mCpuInitMpLibHobGuid = CPU_INIT_MP_LIB_HOB_GUID;
+
+/**
+ The function will check if BSP Execute Disable is enabled.
+
+ DxeIpl may have enabled Execute Disable for BSP, APs need to
+ get the status and sync up the settings.
+ If BSP's CR0.Paging is not set, BSP execute Disble feature is
+ not working actually.
+
+ @retval TRUE BSP Execute Disable is enabled.
+ @retval FALSE BSP Execute Disable is not enabled.
+**/
+BOOLEAN
+IsBspExecuteDisableEnabled (
+ VOID
+ )
+{
+ UINT32 Eax;
+ CPUID_EXTENDED_CPU_SIG_EDX Edx;
+ MSR_IA32_EFER_REGISTER EferMsr;
+ BOOLEAN Enabled;
+ IA32_CR0 Cr0;
+
+ Enabled = FALSE;
+ Cr0.UintN = AsmReadCr0 ();
+ if (Cr0.Bits.PG != 0) {
+ //
+ // If CR0 Paging bit is set
+ //
+ AsmCpuid (CPUID_EXTENDED_FUNCTION, &Eax, NULL, NULL, NULL);
+ if (Eax >= CPUID_EXTENDED_CPU_SIG) {
+ AsmCpuid (CPUID_EXTENDED_CPU_SIG, NULL, NULL, NULL, &Edx.Uint32);
+ //
+ // CPUID 0x80000001
+ // Bit 20: Execute Disable Bit available.
+ //
+ if (Edx.Bits.NX != 0) {
+ EferMsr.Uint64 = AsmReadMsr64 (MSR_IA32_EFER);
+ //
+ // MSR 0xC0000080
+ // Bit 11: Execute Disable Bit enable.
+ //
+ if (EferMsr.Bits.NXE != 0) {
+ Enabled = TRUE;
+ }
+ }
+ }
+ }
+
+ return Enabled;
+}
+
+/**
+ Worker function for SwitchBSP().
+
+ Worker function for SwitchBSP(), assigned to the AP which is intended
+ to become BSP.
+
+ @param[in] Buffer Pointer to CPU MP Data
+**/
+VOID
+EFIAPI
+FutureBSPProc (
+ IN VOID *Buffer
+ )
+{
+ CPU_MP_DATA *DataInHob;
+
+ DataInHob = (CPU_MP_DATA *) Buffer;
+ AsmExchangeRole (&DataInHob->APInfo, &DataInHob->BSPInfo);
+}
+
+/**
+ Get the Application Processors state.
+
+ @param[in] CpuData The pointer to CPU_AP_DATA of specified AP
+
+ @return The AP status
+**/
+CPU_STATE
+GetApState (
+ IN CPU_AP_DATA *CpuData
+ )
+{
+ return CpuData->State;
+}
+
+/**
+ Set the Application Processors state.
+
+ @param[in] CpuData The pointer to CPU_AP_DATA of specified AP
+ @param[in] State The AP status
+**/
+VOID
+SetApState (
+ IN CPU_AP_DATA *CpuData,
+ IN CPU_STATE State
+ )
+{
+ AcquireSpinLock (&CpuData->ApLock);
+ CpuData->State = State;
+ ReleaseSpinLock (&CpuData->ApLock);
+}
+
+/**
+ Save BSP's local APIC timer setting.
+
+ @param[in] CpuMpData Pointer to CPU MP Data
+**/
+VOID
+SaveLocalApicTimerSetting (
+ IN CPU_MP_DATA *CpuMpData
+ )
+{
+ //
+ // Record the current local APIC timer setting of BSP
+ //
+ GetApicTimerState (
+ &CpuMpData->DivideValue,
+ &CpuMpData->PeriodicMode,
+ &CpuMpData->Vector
+ );
+ CpuMpData->CurrentTimerCount = GetApicTimerCurrentCount ();
+ CpuMpData->TimerInterruptState = GetApicTimerInterruptState ();
+}
+
+/**
+ Sync local APIC timer setting from BSP to AP.
+
+ @param[in] CpuMpData Pointer to CPU MP Data
+**/
+VOID
+SyncLocalApicTimerSetting (
+ IN CPU_MP_DATA *CpuMpData
+ )
+{
+ //
+ // Sync local APIC timer setting from BSP to AP
+ //
+ InitializeApicTimer (
+ CpuMpData->DivideValue,
+ CpuMpData->CurrentTimerCount,
+ CpuMpData->PeriodicMode,
+ CpuMpData->Vector
+ );
+ //
+ // Disable AP's local APIC timer interrupt
+ //
+ DisableApicTimerInterrupt ();
+}
+
+/**
+ Save the volatile registers required to be restored following INIT IPI.
+
+ @param[out] VolatileRegisters Returns buffer saved the volatile resisters
+**/
+VOID
+SaveVolatileRegisters (
+ OUT CPU_VOLATILE_REGISTERS *VolatileRegisters
+ )
+{
+ CPUID_VERSION_INFO_EDX VersionInfoEdx;
+
+ VolatileRegisters->Cr0 = AsmReadCr0 ();
+ VolatileRegisters->Cr3 = AsmReadCr3 ();
+ VolatileRegisters->Cr4 = AsmReadCr4 ();
+
+ AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, NULL, &VersionInfoEdx.Uint32);
+ if (VersionInfoEdx.Bits.DE != 0) {
+ //
+ // If processor supports Debugging Extensions feature
+ // by CPUID.[EAX=01H]:EDX.BIT2
+ //
+ VolatileRegisters->Dr0 = AsmReadDr0 ();
+ VolatileRegisters->Dr1 = AsmReadDr1 ();
+ VolatileRegisters->Dr2 = AsmReadDr2 ();
+ VolatileRegisters->Dr3 = AsmReadDr3 ();
+ VolatileRegisters->Dr6 = AsmReadDr6 ();
+ VolatileRegisters->Dr7 = AsmReadDr7 ();
+ }
+}
+
+/**
+ Restore the volatile registers following INIT IPI.
+
+ @param[in] VolatileRegisters Pointer to volatile resisters
+ @param[in] IsRestoreDr TRUE: Restore DRx if supported
+ FALSE: Do not restore DRx
+**/
+VOID
+RestoreVolatileRegisters (
+ IN CPU_VOLATILE_REGISTERS *VolatileRegisters,
+ IN BOOLEAN IsRestoreDr
+ )
+{
+ CPUID_VERSION_INFO_EDX VersionInfoEdx;
+
+ AsmWriteCr0 (VolatileRegisters->Cr0);
+ AsmWriteCr3 (VolatileRegisters->Cr3);
+ AsmWriteCr4 (VolatileRegisters->Cr4);
+
+ if (IsRestoreDr) {
+ AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, NULL, &VersionInfoEdx.Uint32);
+ if (VersionInfoEdx.Bits.DE != 0) {
+ //
+ // If processor supports Debugging Extensions feature
+ // by CPUID.[EAX=01H]:EDX.BIT2
+ //
+ AsmWriteDr0 (VolatileRegisters->Dr0);
+ AsmWriteDr1 (VolatileRegisters->Dr1);
+ AsmWriteDr2 (VolatileRegisters->Dr2);
+ AsmWriteDr3 (VolatileRegisters->Dr3);
+ AsmWriteDr6 (VolatileRegisters->Dr6);
+ AsmWriteDr7 (VolatileRegisters->Dr7);
+ }
+ }
+}
+
+/**
+ Detect whether Mwait-monitor feature is supported.
+
+ @retval TRUE Mwait-monitor feature is supported.
+ @retval FALSE Mwait-monitor feature is not supported.
+**/
+BOOLEAN
+IsMwaitSupport (
+ VOID
+ )
+{
+ CPUID_VERSION_INFO_ECX VersionInfoEcx;
+
+ AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, &VersionInfoEcx.Uint32, NULL);
+ return (VersionInfoEcx.Bits.MONITOR == 1) ? TRUE : FALSE;
+}
+
+/**
+ Get AP loop mode.
+
+ @param[out] MonitorFilterSize Returns the largest monitor-line size in bytes.
+
+ @return The AP loop mode.
+**/
+UINT8
+GetApLoopMode (
+ OUT UINT32 *MonitorFilterSize
+ )
+{
+ UINT8 ApLoopMode;
+ CPUID_MONITOR_MWAIT_EBX MonitorMwaitEbx;
+
+ ASSERT (MonitorFilterSize != NULL);
+
+ ApLoopMode = PcdGet8 (PcdCpuApLoopMode);
+ ASSERT (ApLoopMode >= ApInHltLoop && ApLoopMode <= ApInRunLoop);
+ if (ApLoopMode == ApInMwaitLoop) {
+ if (!IsMwaitSupport ()) {
+ //
+ // If processor does not support MONITOR/MWAIT feature,
+ // force AP in Hlt-loop mode
+ //
+ ApLoopMode = ApInHltLoop;
+ }
+ }
+
+ if (ApLoopMode != ApInMwaitLoop) {
+ *MonitorFilterSize = sizeof (UINT32);
+ } else {
+ //
+ // 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, &MonitorMwaitEbx.Uint32, NULL, NULL);
+ *MonitorFilterSize = MonitorMwaitEbx.Bits.LargestMonitorLineSize;
+ }
+
+ return ApLoopMode;
+}
+
+/**
+ 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[in] CpuMpData Pointer to PEI CPU MP Data
+**/
+VOID
+SortApicId (
+ IN CPU_MP_DATA *CpuMpData
+ )
+{
+ UINTN Index1;
+ UINTN Index2;
+ UINTN Index3;
+ UINT32 ApicId;
+ CPU_INFO_IN_HOB CpuInfo;
+ UINT32 ApCount;
+ CPU_INFO_IN_HOB *CpuInfoInHob;
+
+ ApCount = CpuMpData->CpuCount - 1;
+ CpuInfoInHob = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob;
+ if (ApCount != 0) {
+ for (Index1 = 0; Index1 < ApCount; Index1++) {
+ Index3 = Index1;
+ //
+ // Sort key is the hardware default APIC ID
+ //
+ ApicId = CpuInfoInHob[Index1].ApicId;
+ for (Index2 = Index1 + 1; Index2 <= ApCount; Index2++) {
+ if (ApicId > CpuInfoInHob[Index2].ApicId) {
+ Index3 = Index2;
+ ApicId = CpuInfoInHob[Index2].ApicId;
+ }
+ }
+ if (Index3 != Index1) {
+ CopyMem (&CpuInfo, &CpuInfoInHob[Index3], sizeof (CPU_INFO_IN_HOB));
+ CopyMem (
+ &CpuInfoInHob[Index3],
+ &CpuInfoInHob[Index1],
+ sizeof (CPU_INFO_IN_HOB)
+ );
+ CopyMem (&CpuInfoInHob[Index1], &CpuInfo, sizeof (CPU_INFO_IN_HOB));
+ }
+ }
+
+ //
+ // Get the processor number for the BSP
+ //
+ ApicId = GetInitialApicId ();
+ for (Index1 = 0; Index1 < CpuMpData->CpuCount; Index1++) {
+ if (CpuInfoInHob[Index1].ApicId == ApicId) {
+ CpuMpData->BspNumber = (UINT32) Index1;
+ break;
+ }
+ }
+ }
+}
+
+/**
+ Enable x2APIC mode on APs.
+
+ @param[in, out] Buffer Pointer to private data buffer.
+**/
+VOID
+EFIAPI
+ApFuncEnableX2Apic (
+ IN OUT VOID *Buffer
+ )
+{
+ SetApicMode (LOCAL_APIC_MODE_X2APIC);
+}
+
+/**
+ Do sync on APs.
+
+ @param[in, out] Buffer Pointer to private data buffer.
+**/
+VOID
+EFIAPI
+ApInitializeSync (
+ IN OUT VOID *Buffer
+ )
+{
+ CPU_MP_DATA *CpuMpData;
+
+ CpuMpData = (CPU_MP_DATA *) Buffer;
+ //
+ // Load microcode on AP
+ //
+ MicrocodeDetect (CpuMpData);
+ //
+ // Sync BSP's MTRR table to AP
+ //
+ MtrrSetAllMtrrs (&CpuMpData->MtrrTable);
+}
+
+/**
+ Find the current Processor number by APIC ID.
+
+ @param[in] CpuMpData Pointer to PEI CPU MP Data
+ @param[out] 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 CPU_MP_DATA *CpuMpData,
+ OUT UINTN *ProcessorNumber
+ )
+{
+ UINTN TotalProcessorNumber;
+ UINTN Index;
+ CPU_INFO_IN_HOB *CpuInfoInHob;
+
+ CpuInfoInHob = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob;
+
+ TotalProcessorNumber = CpuMpData->CpuCount;
+ for (Index = 0; Index < TotalProcessorNumber; Index ++) {
+ if (CpuInfoInHob[Index].ApicId == GetApicId ()) {
+ *ProcessorNumber = Index;
+ return EFI_SUCCESS;
+ }
+ }
+ return EFI_NOT_FOUND;
+}
+
+/**
+ This function will get CPU count in the system.
+
+ @param[in] CpuMpData Pointer to PEI CPU MP Data
+
+ @return CPU count detected
+**/
+UINTN
+CollectProcessorCount (
+ IN CPU_MP_DATA *CpuMpData
+ )
+{
+ //
+ // Send 1st broadcast IPI to APs to wakeup APs
+ //
+ CpuMpData->InitFlag = ApInitConfig;
+ CpuMpData->X2ApicEnable = FALSE;
+ WakeUpAP (CpuMpData, TRUE, 0, NULL, NULL);
+ CpuMpData->InitFlag = ApInitDone;
+ ASSERT (CpuMpData->CpuCount <= PcdGet32 (PcdCpuMaxLogicalProcessorNumber));
+ //
+ // Wait for all APs finished the initialization
+ //
+ while (CpuMpData->FinishedCount < (CpuMpData->CpuCount - 1)) {
+ CpuPause ();
+ }
+
+ if (CpuMpData->X2ApicEnable) {
+ DEBUG ((DEBUG_INFO, "Force x2APIC mode!\n"));
+ //
+ // Wakeup all APs to enable x2APIC mode
+ //
+ WakeUpAP (CpuMpData, TRUE, 0, ApFuncEnableX2Apic, NULL);
+ //
+ // Wait for all known APs finished
+ //
+ while (CpuMpData->FinishedCount < (CpuMpData->CpuCount - 1)) {
+ CpuPause ();
+ }
+ //
+ // Enable x2APIC on BSP
+ //
+ SetApicMode (LOCAL_APIC_MODE_X2APIC);
+ }
+ DEBUG ((DEBUG_INFO, "APIC MODE is %d\n", GetApicMode ()));
+ //
+ // Sort BSP/Aps by CPU APIC ID in ascending order
+ //
+ SortApicId (CpuMpData);
+
+ DEBUG ((DEBUG_INFO, "MpInitLib: Find %d processors in system.\n", CpuMpData->CpuCount));
+
+ return CpuMpData->CpuCount;
+}
+
+/**
+ Initialize CPU AP Data when AP is wakeup at the first time.
+
+ @param[in, out] CpuMpData Pointer to PEI CPU MP Data
+ @param[in] ProcessorNumber The handle number of processor
+ @param[in] BistData Processor BIST data
+ @param[in] ApTopOfStack Top of AP stack
+
+**/
+VOID
+InitializeApData (
+ IN OUT CPU_MP_DATA *CpuMpData,
+ IN UINTN ProcessorNumber,
+ IN UINT32 BistData,
+ IN UINT64 ApTopOfStack
+ )
+{
+ CPU_INFO_IN_HOB *CpuInfoInHob;
+
+ CpuInfoInHob = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob;
+ CpuInfoInHob[ProcessorNumber].InitialApicId = GetInitialApicId ();
+ CpuInfoInHob[ProcessorNumber].ApicId = GetApicId ();
+ CpuInfoInHob[ProcessorNumber].Health = BistData;
+ CpuInfoInHob[ProcessorNumber].ApTopOfStack = ApTopOfStack;
+
+ CpuMpData->CpuData[ProcessorNumber].Waiting = FALSE;
+ CpuMpData->CpuData[ProcessorNumber].CpuHealthy = (BistData == 0) ? TRUE : FALSE;
+ if (CpuInfoInHob[ProcessorNumber].InitialApicId >= 0xFF) {
+ //
+ // Set x2APIC mode if there are any logical processor reporting
+ // an Initial APIC ID of 255 or greater.
+ //
+ AcquireSpinLock(&CpuMpData->MpLock);
+ CpuMpData->X2ApicEnable = TRUE;
+ ReleaseSpinLock(&CpuMpData->MpLock);
+ }
+
+ InitializeSpinLock(&CpuMpData->CpuData[ProcessorNumber].ApLock);
+ SetApState (&CpuMpData->CpuData[ProcessorNumber], CpuStateIdle);
+}
+
+/**
+ This function will be called from AP reset code if BSP uses WakeUpAP.
+
+ @param[in] ExchangeInfo Pointer to the MP exchange info buffer
+ @param[in] NumApsExecuting Number of current executing AP
+**/
+VOID
+EFIAPI
+ApWakeupFunction (
+ IN MP_CPU_EXCHANGE_INFO *ExchangeInfo,
+ IN UINTN NumApsExecuting
+ )
+{
+ CPU_MP_DATA *CpuMpData;
+ UINTN ProcessorNumber;
+ EFI_AP_PROCEDURE Procedure;
+ VOID *Parameter;
+ UINT32 BistData;
+ volatile UINT32 *ApStartupSignalBuffer;
+ CPU_INFO_IN_HOB *CpuInfoInHob;
+ UINT64 ApTopOfStack;
+
+ //
+ // AP finished assembly code and begin to execute C code
+ //
+ CpuMpData = ExchangeInfo->CpuMpData;
+
+ //
+ // AP's local APIC settings will be lost after received INIT IPI
+ // We need to re-initialize them at here
+ //
+ ProgramVirtualWireMode ();
+ SyncLocalApicTimerSetting (CpuMpData);
+
+ while (TRUE) {
+ if (CpuMpData->InitFlag == ApInitConfig) {
+ //
+ // Add CPU number
+ //
+ InterlockedIncrement ((UINT32 *) &CpuMpData->CpuCount);
+ ProcessorNumber = NumApsExecuting;
+ //
+ // This is first time AP wakeup, get BIST information from AP stack
+ //
+ ApTopOfStack = CpuMpData->Buffer + (ProcessorNumber + 1) * CpuMpData->CpuApStackSize;
+ BistData = *(UINT32 *) ((UINTN) ApTopOfStack - sizeof (UINTN));
+ //
+ // Do some AP initialize sync
+ //
+ ApInitializeSync (CpuMpData);
+ //
+ // Sync BSP's Control registers to APs
+ //
+ RestoreVolatileRegisters (&CpuMpData->CpuData[0].VolatileRegisters, FALSE);
+ InitializeApData (CpuMpData, ProcessorNumber, BistData, ApTopOfStack);
+ ApStartupSignalBuffer = CpuMpData->CpuData[ProcessorNumber].StartupApSignal;
+ } else {
+ //
+ // Execute AP function if AP is ready
+ //
+ GetProcessorNumber (CpuMpData, &ProcessorNumber);
+ //
+ // Clear AP start-up signal when AP waken up
+ //
+ ApStartupSignalBuffer = CpuMpData->CpuData[ProcessorNumber].StartupApSignal;
+ InterlockedCompareExchange32 (
+ (UINT32 *) ApStartupSignalBuffer,
+ WAKEUP_AP_SIGNAL,
+ 0
+ );
+ if (CpuMpData->ApLoopMode == ApInHltLoop) {
+ //
+ // Restore AP's volatile registers saved
+ //
+ RestoreVolatileRegisters (&CpuMpData->CpuData[ProcessorNumber].VolatileRegisters, TRUE);
+ }
+
+ if (GetApState (&CpuMpData->CpuData[ProcessorNumber]) == CpuStateReady) {
+ Procedure = (EFI_AP_PROCEDURE)CpuMpData->CpuData[ProcessorNumber].ApFunction;
+ Parameter = (VOID *) CpuMpData->CpuData[ProcessorNumber].ApFunctionArgument;
+ if (Procedure != NULL) {
+ SetApState (&CpuMpData->CpuData[ProcessorNumber], CpuStateBusy);
+ //
+ // Enable source debugging on AP function
+ //
+ EnableDebugAgent ();
+ //
+ // Invoke AP function here
+ //
+ Procedure (Parameter);
+ CpuInfoInHob = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob;
+ if (CpuMpData->SwitchBspFlag) {
+ //
+ // Re-get the processor number due to BSP/AP maybe exchange in AP function
+ //
+ GetProcessorNumber (CpuMpData, &ProcessorNumber);
+ CpuMpData->CpuData[ProcessorNumber].ApFunction = 0;
+ CpuMpData->CpuData[ProcessorNumber].ApFunctionArgument = 0;
+ ApStartupSignalBuffer = CpuMpData->CpuData[ProcessorNumber].StartupApSignal;
+ CpuInfoInHob[ProcessorNumber].ApTopOfStack = CpuInfoInHob[CpuMpData->NewBspNumber].ApTopOfStack;
+ } else {
+ //
+ // Re-get the CPU APICID and Initial APICID
+ //
+ CpuInfoInHob[ProcessorNumber].ApicId = GetApicId ();
+ CpuInfoInHob[ProcessorNumber].InitialApicId = GetInitialApicId ();
+ }
+ }
+ SetApState (&CpuMpData->CpuData[ProcessorNumber], CpuStateFinished);
+ }
+ }
+
+ //
+ // AP finished executing C code
+ //
+ InterlockedIncrement ((UINT32 *) &CpuMpData->FinishedCount);
+
+ //
+ // Place AP is specified loop mode
+ //
+ if (CpuMpData->ApLoopMode == ApInHltLoop) {
+ //
+ // Save AP volatile registers
+ //
+ SaveVolatileRegisters (&CpuMpData->CpuData[ProcessorNumber].VolatileRegisters);
+ //
+ // Place AP in HLT-loop
+ //
+ while (TRUE) {
+ DisableInterrupts ();
+ CpuSleep ();
+ CpuPause ();
+ }
+ }
+ while (TRUE) {
+ DisableInterrupts ();
+ if (CpuMpData->ApLoopMode == ApInMwaitLoop) {
+ //
+ // Place AP in MWAIT-loop
+ //
+ AsmMonitor ((UINTN) ApStartupSignalBuffer, 0, 0);
+ if (*ApStartupSignalBuffer != WAKEUP_AP_SIGNAL) {
+ //
+ // Check AP start-up signal again.
+ // If AP start-up signal is not set, place AP into
+ // the specified C-state
+ //
+ AsmMwait (CpuMpData->ApTargetCState << 4, 0);
+ }
+ } else if (CpuMpData->ApLoopMode == ApInRunLoop) {
+ //
+ // Place AP in Run-loop
+ //
+ CpuPause ();
+ } else {
+ ASSERT (FALSE);
+ }
+
+ //
+ // If AP start-up signal is written, AP is waken up
+ // otherwise place AP in loop again
+ //
+ if (*ApStartupSignalBuffer == WAKEUP_AP_SIGNAL) {
+ break;
+ }
+ }
+ }
+}
+
+/**
+ Wait for AP wakeup and write AP start-up signal till AP is waken up.
+
+ @param[in] ApStartupSignalBuffer Pointer to AP wakeup signal
+**/
+VOID
+WaitApWakeup (
+ IN volatile UINT32 *ApStartupSignalBuffer
+ )
+{
+ //
+ // If AP is waken up, StartupApSignal should be cleared.
+ // Otherwise, write StartupApSignal again till AP waken up.
+ //
+ while (InterlockedCompareExchange32 (
+ (UINT32 *) ApStartupSignalBuffer,
+ WAKEUP_AP_SIGNAL,
+ WAKEUP_AP_SIGNAL
+ ) != 0) {
+ CpuPause ();
+ }
+}
+
+/**
+ This function will fill the exchange info structure.
+
+ @param[in] CpuMpData Pointer to CPU MP Data
+
+**/
+VOID
+FillExchangeInfoData (
+ IN CPU_MP_DATA *CpuMpData
+ )
+{
+ volatile MP_CPU_EXCHANGE_INFO *ExchangeInfo;
+
+ ExchangeInfo = CpuMpData->MpCpuExchangeInfo;
+ ExchangeInfo->Lock = 0;
+ ExchangeInfo->StackStart = CpuMpData->Buffer;
+ ExchangeInfo->StackSize = CpuMpData->CpuApStackSize;
+ ExchangeInfo->BufferStart = CpuMpData->WakeupBuffer;
+ ExchangeInfo->ModeOffset = CpuMpData->AddressMap.ModeEntryOffset;
+
+ ExchangeInfo->CodeSegment = AsmReadCs ();
+ ExchangeInfo->DataSegment = AsmReadDs ();
+
+ ExchangeInfo->Cr3 = AsmReadCr3 ();
+
+ ExchangeInfo->CFunction = (UINTN) ApWakeupFunction;
+ ExchangeInfo->NumApsExecuting = 0;
+ ExchangeInfo->InitFlag = (UINTN) CpuMpData->InitFlag;
+ ExchangeInfo->CpuInfo = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob;
+ ExchangeInfo->CpuMpData = CpuMpData;
+
+ ExchangeInfo->EnableExecuteDisable = IsBspExecuteDisableEnabled ();
+
+ //
+ // Get the BSP's data of GDT and IDT
+ //
+ AsmReadGdtr ((IA32_DESCRIPTOR *) &ExchangeInfo->GdtrProfile);
+ AsmReadIdtr ((IA32_DESCRIPTOR *) &ExchangeInfo->IdtrProfile);
+}
+
+/**
+ Helper function that waits until the finished AP count reaches the specified
+ limit, or the specified timeout elapses (whichever comes first).
+
+ @param[in] CpuMpData Pointer to CPU MP Data.
+ @param[in] FinishedApLimit The number of finished APs to wait for.
+ @param[in] TimeLimit The number of microseconds to wait for.
+**/
+VOID
+TimedWaitForApFinish (
+ IN CPU_MP_DATA *CpuMpData,
+ IN UINT32 FinishedApLimit,
+ IN UINT32 TimeLimit
+ );
+
+/**
+ This function will be called by BSP to wakeup AP.
+
+ @param[in] CpuMpData Pointer to CPU MP Data
+ @param[in] Broadcast TRUE: Send broadcast IPI to all APs
+ FALSE: Send IPI to AP by ApicId
+ @param[in] ProcessorNumber The handle number of specified processor
+ @param[in] Procedure The function to be invoked by AP
+ @param[in] ProcedureArgument The argument to be passed into AP function
+**/
+VOID
+WakeUpAP (
+ IN CPU_MP_DATA *CpuMpData,
+ IN BOOLEAN Broadcast,
+ IN UINTN ProcessorNumber,
+ IN EFI_AP_PROCEDURE Procedure, OPTIONAL
+ IN VOID *ProcedureArgument OPTIONAL
+ )
+{
+ volatile MP_CPU_EXCHANGE_INFO *ExchangeInfo;
+ UINTN Index;
+ CPU_AP_DATA *CpuData;
+ BOOLEAN ResetVectorRequired;
+ CPU_INFO_IN_HOB *CpuInfoInHob;
+
+ CpuMpData->FinishedCount = 0;
+ ResetVectorRequired = FALSE;
+
+ if (CpuMpData->ApLoopMode == ApInHltLoop ||
+ CpuMpData->InitFlag != ApInitDone) {
+ ResetVectorRequired = TRUE;
+ AllocateResetVector (CpuMpData);
+ FillExchangeInfoData (CpuMpData);
+ SaveLocalApicTimerSetting (CpuMpData);
+ } else if (CpuMpData->ApLoopMode == ApInMwaitLoop) {
+ //
+ // Get AP target C-state each time when waking up AP,
+ // for it maybe updated by platform again
+ //
+ CpuMpData->ApTargetCState = PcdGet8 (PcdCpuApTargetCstate);
+ }
+
+ ExchangeInfo = CpuMpData->MpCpuExchangeInfo;
+
+ if (Broadcast) {
+ for (Index = 0; Index < CpuMpData->CpuCount; Index++) {
+ if (Index != CpuMpData->BspNumber) {
+ CpuData = &CpuMpData->CpuData[Index];
+ CpuData->ApFunction = (UINTN) Procedure;
+ CpuData->ApFunctionArgument = (UINTN) ProcedureArgument;
+ SetApState (CpuData, CpuStateReady);
+ if (CpuMpData->InitFlag != ApInitConfig) {
+ *(UINT32 *) CpuData->StartupApSignal = WAKEUP_AP_SIGNAL;
+ }
+ }
+ }
+ if (ResetVectorRequired) {
+ //
+ // Wakeup all APs
+ //
+ SendInitSipiSipiAllExcludingSelf ((UINT32) ExchangeInfo->BufferStart);
+ }
+ if (CpuMpData->InitFlag == ApInitConfig) {
+ //
+ // Wait for all potential APs waken up in one specified period
+ //
+ TimedWaitForApFinish (
+ CpuMpData,
+ PcdGet32 (PcdCpuMaxLogicalProcessorNumber) - 1,
+ PcdGet32 (PcdCpuApInitTimeOutInMicroSeconds)
+ );
+ } else {
+ //
+ // Wait all APs waken up if this is not the 1st broadcast of SIPI
+ //
+ for (Index = 0; Index < CpuMpData->CpuCount; Index++) {
+ CpuData = &CpuMpData->CpuData[Index];
+ if (Index != CpuMpData->BspNumber) {
+ WaitApWakeup (CpuData->StartupApSignal);
+ }
+ }
+ }
+ } else {
+ CpuData = &CpuMpData->CpuData[ProcessorNumber];
+ CpuData->ApFunction = (UINTN) Procedure;
+ CpuData->ApFunctionArgument = (UINTN) ProcedureArgument;
+ SetApState (CpuData, CpuStateReady);
+ //
+ // Wakeup specified AP
+ //
+ ASSERT (CpuMpData->InitFlag != ApInitConfig);
+ *(UINT32 *) CpuData->StartupApSignal = WAKEUP_AP_SIGNAL;
+ if (ResetVectorRequired) {
+ CpuInfoInHob = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob;
+ SendInitSipiSipi (
+ CpuInfoInHob[ProcessorNumber].ApicId,
+ (UINT32) ExchangeInfo->BufferStart
+ );
+ }
+ //
+ // Wait specified AP waken up
+ //
+ WaitApWakeup (CpuData->StartupApSignal);
+ }
+
+ if (ResetVectorRequired) {
+ FreeResetVector (CpuMpData);
+ }
+}
+
+/**
+ Calculate timeout value and return the current performance counter value.
+
+ Calculate the number of performance counter ticks required for a timeout.
+ If TimeoutInMicroseconds is 0, return value is also 0, which is recognized
+ as infinity.
+
+ @param[in] TimeoutInMicroseconds Timeout value in microseconds.
+ @param[out] CurrentTime Returns the current value of the performance counter.
+
+ @return Expected time stamp counter for timeout.
+ If TimeoutInMicroseconds is 0, return value is also 0, which is recognized
+ as infinity.
+
+**/
+UINT64
+CalculateTimeout (
+ IN UINTN TimeoutInMicroseconds,
+ OUT UINT64 *CurrentTime
+ )
+{
+ //
+ // Read the current value of the performance counter
+ //
+ *CurrentTime = GetPerformanceCounter ();
+
+ //
+ // If TimeoutInMicroseconds is 0, return value is also 0, which is recognized
+ // as infinity.
+ //
+ if (TimeoutInMicroseconds == 0) {
+ return 0;
+ }
+
+ //
+ // GetPerformanceCounterProperties () returns the timestamp counter's frequency
+ // in Hz. So multiply the return value with TimeoutInMicroseconds and then divide
+ // it by 1,000,000, to get the number of ticks for the timeout value.
+ //
+ return DivU64x32 (
+ MultU64x64 (
+ GetPerformanceCounterProperties (NULL, NULL),
+ TimeoutInMicroseconds
+ ),
+ 1000000
+ );
+}
+
+/**
+ Checks whether timeout expires.
+
+ Check whether the number of elapsed performance counter ticks required for
+ a timeout condition has been reached.
+ If Timeout is zero, which means infinity, return value is always FALSE.
+
+ @param[in, out] PreviousTime On input, the value of the performance counter
+ when it was last read.
+ On output, the current value of the performance
+ counter
+ @param[in] TotalTime The total amount of elapsed time in performance
+ counter ticks.
+ @param[in] Timeout The number of performance counter ticks required
+ to reach a timeout condition.
+
+ @retval TRUE A timeout condition has been reached.
+ @retval FALSE A timeout condition has not been reached.
+
+**/
+BOOLEAN
+CheckTimeout (
+ IN OUT UINT64 *PreviousTime,
+ IN UINT64 *TotalTime,
+ IN UINT64 Timeout
+ )
+{
+ UINT64 Start;
+ UINT64 End;
+ UINT64 CurrentTime;
+ INT64 Delta;
+ INT64 Cycle;
+
+ if (Timeout == 0) {
+ return FALSE;
+ }
+ GetPerformanceCounterProperties (&Start, &End);
+ Cycle = End - Start;
+ if (Cycle < 0) {
+ Cycle = -Cycle;
+ }
+ Cycle++;
+ CurrentTime = GetPerformanceCounter();
+ Delta = (INT64) (CurrentTime - *PreviousTime);
+ if (Start > End) {
+ Delta = -Delta;
+ }
+ if (Delta < 0) {
+ Delta += Cycle;
+ }
+ *TotalTime += Delta;
+ *PreviousTime = CurrentTime;
+ if (*TotalTime > Timeout) {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/**
+ Helper function that waits until the finished AP count reaches the specified
+ limit, or the specified timeout elapses (whichever comes first).
+
+ @param[in] CpuMpData Pointer to CPU MP Data.
+ @param[in] FinishedApLimit The number of finished APs to wait for.
+ @param[in] TimeLimit The number of microseconds to wait for.
+**/
+VOID
+TimedWaitForApFinish (
+ IN CPU_MP_DATA *CpuMpData,
+ IN UINT32 FinishedApLimit,
+ IN UINT32 TimeLimit
+ )
+{
+ //
+ // CalculateTimeout() and CheckTimeout() consider a TimeLimit of 0
+ // "infinity", so check for (TimeLimit == 0) explicitly.
+ //
+ if (TimeLimit == 0) {
+ return;
+ }
+
+ CpuMpData->TotalTime = 0;
+ CpuMpData->ExpectedTime = CalculateTimeout (
+ TimeLimit,
+ &CpuMpData->CurrentTime
+ );
+ while (CpuMpData->FinishedCount < FinishedApLimit &&
+ !CheckTimeout (
+ &CpuMpData->CurrentTime,
+ &CpuMpData->TotalTime,
+ CpuMpData->ExpectedTime
+ )) {
+ CpuPause ();
+ }
+
+ if (CpuMpData->FinishedCount >= FinishedApLimit) {
+ DEBUG ((
+ DEBUG_VERBOSE,
+ "%a: reached FinishedApLimit=%u in %Lu microseconds\n",
+ __FUNCTION__,
+ FinishedApLimit,
+ DivU64x64Remainder (
+ MultU64x32 (CpuMpData->TotalTime, 1000000),
+ GetPerformanceCounterProperties (NULL, NULL),
+ NULL
+ )
+ ));
+ }
+}
+
+/**
+ Reset an AP to Idle state.
+
+ Any task being executed by the AP will be aborted and the AP
+ will be waiting for a new task in Wait-For-SIPI state.
+
+ @param[in] ProcessorNumber The handle number of processor.
+**/
+VOID
+ResetProcessorToIdleState (
+ IN UINTN ProcessorNumber
+ )
+{
+ CPU_MP_DATA *CpuMpData;
+
+ CpuMpData = GetCpuMpData ();
+
+ CpuMpData->InitFlag = ApInitReconfig;
+ WakeUpAP (CpuMpData, FALSE, ProcessorNumber, NULL, NULL);
+ while (CpuMpData->FinishedCount < 1) {
+ CpuPause ();
+ }
+ CpuMpData->InitFlag = ApInitDone;
+
+ SetApState (&CpuMpData->CpuData[ProcessorNumber], CpuStateIdle);
+}
+
+/**
+ Searches for the next waiting AP.
+
+ Search for the next AP that is put in waiting state by single-threaded StartupAllAPs().
+
+ @param[out] NextProcessorNumber Pointer to the processor number of the next waiting AP.
+
+ @retval EFI_SUCCESS The next waiting AP has been found.
+ @retval EFI_NOT_FOUND No waiting AP exists.
+
+**/
+EFI_STATUS
+GetNextWaitingProcessorNumber (
+ OUT UINTN *NextProcessorNumber
+ )
+{
+ UINTN ProcessorNumber;
+ CPU_MP_DATA *CpuMpData;
+
+ CpuMpData = GetCpuMpData ();
+
+ for (ProcessorNumber = 0; ProcessorNumber < CpuMpData->CpuCount; ProcessorNumber++) {
+ if (CpuMpData->CpuData[ProcessorNumber].Waiting) {
+ *NextProcessorNumber = ProcessorNumber;
+ return EFI_SUCCESS;
+ }
+ }
+
+ return EFI_NOT_FOUND;
+}
+
+/** Checks status of specified AP.
+
+ This function checks whether the specified AP has finished the task assigned
+ by StartupThisAP(), and whether timeout expires.
+
+ @param[in] ProcessorNumber The handle number of processor.
+
+ @retval EFI_SUCCESS Specified AP has finished task assigned by StartupThisAPs().
+ @retval EFI_TIMEOUT The timeout expires.
+ @retval EFI_NOT_READY Specified AP has not finished task and timeout has not expired.
+**/
+EFI_STATUS
+CheckThisAP (
+ IN UINTN ProcessorNumber
+ )
+{
+ CPU_MP_DATA *CpuMpData;
+ CPU_AP_DATA *CpuData;
+
+ CpuMpData = GetCpuMpData ();
+ CpuData = &CpuMpData->CpuData[ProcessorNumber];
+
+ //
+ // Check the CPU state of AP. If it is CpuStateFinished, then the AP has finished its task.
+ // Only BSP and corresponding AP access this unit of CPU Data. This means the AP will not modify the
+ // value of state after setting the it to CpuStateFinished, so BSP can safely make use of its value.
+ //
+ //
+ // If the AP finishes for StartupThisAP(), return EFI_SUCCESS.
+ //
+ if (GetApState(CpuData) == CpuStateFinished) {
+ if (CpuData->Finished != NULL) {
+ *(CpuData->Finished) = TRUE;
+ }
+ SetApState (CpuData, CpuStateIdle);
+ return EFI_SUCCESS;
+ } else {
+ //
+ // If timeout expires for StartupThisAP(), report timeout.
+ //
+ if (CheckTimeout (&CpuData->CurrentTime, &CpuData->TotalTime, CpuData->ExpectedTime)) {
+ if (CpuData->Finished != NULL) {
+ *(CpuData->Finished) = FALSE;
+ }
+ //
+ // Reset failed AP to idle state
+ //
+ ResetProcessorToIdleState (ProcessorNumber);
+
+ return EFI_TIMEOUT;
+ }
+ }
+ return EFI_NOT_READY;
+}
+
+/**
+ Checks status of all APs.
+
+ This function checks whether all APs have finished task assigned by StartupAllAPs(),
+ and whether timeout expires.
+
+ @retval EFI_SUCCESS All APs have finished task assigned by StartupAllAPs().
+ @retval EFI_TIMEOUT The timeout expires.
+ @retval EFI_NOT_READY APs have not finished task and timeout has not expired.
+**/
+EFI_STATUS
+CheckAllAPs (
+ VOID
+ )
+{
+ UINTN ProcessorNumber;
+ UINTN NextProcessorNumber;
+ UINTN ListIndex;
+ EFI_STATUS Status;
+ CPU_MP_DATA *CpuMpData;
+ CPU_AP_DATA *CpuData;
+
+ CpuMpData = GetCpuMpData ();
+
+ NextProcessorNumber = 0;
+
+ //
+ // Go through all APs that are responsible for the StartupAllAPs().
+ //
+ for (ProcessorNumber = 0; ProcessorNumber < CpuMpData->CpuCount; ProcessorNumber++) {
+ if (!CpuMpData->CpuData[ProcessorNumber].Waiting) {
+ continue;
+ }
+
+ CpuData = &CpuMpData->CpuData[ProcessorNumber];
+ //
+ // Check the CPU state of AP. If it is CpuStateFinished, then the AP has finished its task.
+ // Only BSP and corresponding AP access this unit of CPU Data. This means the AP will not modify the
+ // value of state after setting the it to CpuStateFinished, so BSP can safely make use of its value.
+ //
+ if (GetApState(CpuData) == CpuStateFinished) {
+ CpuMpData->RunningCount ++;
+ CpuMpData->CpuData[ProcessorNumber].Waiting = FALSE;
+ SetApState(CpuData, CpuStateIdle);
+
+ //
+ // If in Single Thread mode, then search for the next waiting AP for execution.
+ //
+ if (CpuMpData->SingleThread) {
+ Status = GetNextWaitingProcessorNumber (&NextProcessorNumber);
+
+ if (!EFI_ERROR (Status)) {
+ WakeUpAP (
+ CpuMpData,
+ FALSE,
+ (UINT32) NextProcessorNumber,
+ CpuMpData->Procedure,
+ CpuMpData->ProcArguments
+ );
+ }
+ }
+ }
+ }
+
+ //
+ // If all APs finish, return EFI_SUCCESS.
+ //
+ if (CpuMpData->RunningCount == CpuMpData->StartCount) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // If timeout expires, report timeout.
+ //
+ if (CheckTimeout (
+ &CpuMpData->CurrentTime,
+ &CpuMpData->TotalTime,
+ CpuMpData->ExpectedTime)
+ ) {
+ //
+ // If FailedCpuList is not NULL, record all failed APs in it.
+ //
+ if (CpuMpData->FailedCpuList != NULL) {
+ *CpuMpData->FailedCpuList =
+ AllocatePool ((CpuMpData->StartCount - CpuMpData->FinishedCount + 1) * sizeof (UINTN));
+ ASSERT (*CpuMpData->FailedCpuList != NULL);
+ }
+ ListIndex = 0;
+
+ for (ProcessorNumber = 0; ProcessorNumber < CpuMpData->CpuCount; ProcessorNumber++) {
+ //
+ // Check whether this processor is responsible for StartupAllAPs().
+ //
+ if (CpuMpData->CpuData[ProcessorNumber].Waiting) {
+ //
+ // Reset failed APs to idle state
+ //
+ ResetProcessorToIdleState (ProcessorNumber);
+ CpuMpData->CpuData[ProcessorNumber].Waiting = FALSE;
+ if (CpuMpData->FailedCpuList != NULL) {
+ (*CpuMpData->FailedCpuList)[ListIndex++] = ProcessorNumber;
+ }
+ }
+ }
+ if (CpuMpData->FailedCpuList != NULL) {
+ (*CpuMpData->FailedCpuList)[ListIndex] = END_OF_CPU_LIST;
+ }
+ return EFI_TIMEOUT;
+ }
+ return EFI_NOT_READY;
+}
+
+/**
+ MP Initialize Library initialization.
+
+ This service will allocate AP reset vector and wakeup all APs to do APs
+ initialization.
+
+ This service must be invoked before all other MP Initialize Library
+ service are invoked.
+
+ @retval EFI_SUCCESS MP initialization succeeds.
+ @retval Others MP initialization fails.
+
+**/
+EFI_STATUS
+EFIAPI
+MpInitLibInitialize (
+ VOID
+ )
+{
+ CPU_MP_DATA *OldCpuMpData;
+ CPU_INFO_IN_HOB *CpuInfoInHob;
+ UINT32 MaxLogicalProcessorNumber;
+ UINT32 ApStackSize;
+ MP_ASSEMBLY_ADDRESS_MAP AddressMap;
+ UINTN BufferSize;
+ UINT32 MonitorFilterSize;
+ VOID *MpBuffer;
+ UINTN Buffer;
+ CPU_MP_DATA *CpuMpData;
+ UINT8 ApLoopMode;
+ UINT8 *MonitorBuffer;
+ UINTN Index;
+ UINTN ApResetVectorSize;
+ UINTN BackupBufferAddr;
+
+ OldCpuMpData = GetCpuMpDataFromGuidedHob ();
+ if (OldCpuMpData == NULL) {
+ MaxLogicalProcessorNumber = PcdGet32(PcdCpuMaxLogicalProcessorNumber);
+ } else {
+ MaxLogicalProcessorNumber = OldCpuMpData->CpuCount;
+ }
+ ASSERT (MaxLogicalProcessorNumber != 0);
+
+ AsmGetAddressMap (&AddressMap);
+ ApResetVectorSize = AddressMap.RendezvousFunnelSize + sizeof (MP_CPU_EXCHANGE_INFO);
+ ApStackSize = PcdGet32(PcdCpuApStackSize);
+ ApLoopMode = GetApLoopMode (&MonitorFilterSize);
+
+ BufferSize = ApStackSize * MaxLogicalProcessorNumber;
+ BufferSize += MonitorFilterSize * MaxLogicalProcessorNumber;
+ BufferSize += sizeof (CPU_MP_DATA);
+ BufferSize += ApResetVectorSize;
+ BufferSize += (sizeof (CPU_AP_DATA) + sizeof (CPU_INFO_IN_HOB))* MaxLogicalProcessorNumber;
+ MpBuffer = AllocatePages (EFI_SIZE_TO_PAGES (BufferSize));
+ ASSERT (MpBuffer != NULL);
+ ZeroMem (MpBuffer, BufferSize);
+ Buffer = (UINTN) MpBuffer;
+
+ MonitorBuffer = (UINT8 *) (Buffer + ApStackSize * MaxLogicalProcessorNumber);
+ BackupBufferAddr = (UINTN) MonitorBuffer + MonitorFilterSize * MaxLogicalProcessorNumber;
+ CpuMpData = (CPU_MP_DATA *) (BackupBufferAddr + ApResetVectorSize);
+ CpuMpData->Buffer = Buffer;
+ CpuMpData->CpuApStackSize = ApStackSize;
+ CpuMpData->BackupBuffer = BackupBufferAddr;
+ CpuMpData->BackupBufferSize = ApResetVectorSize;
+ CpuMpData->SaveRestoreFlag = FALSE;
+ CpuMpData->WakeupBuffer = (UINTN) -1;
+ CpuMpData->CpuCount = 1;
+ CpuMpData->BspNumber = 0;
+ CpuMpData->WaitEvent = NULL;
+ CpuMpData->SwitchBspFlag = FALSE;
+ CpuMpData->CpuData = (CPU_AP_DATA *) (CpuMpData + 1);
+ CpuMpData->CpuInfoInHob = (UINT64) (UINTN) (CpuMpData->CpuData + MaxLogicalProcessorNumber);
+ InitializeSpinLock(&CpuMpData->MpLock);
+ //
+ // Save BSP's Control registers to APs
+ //
+ SaveVolatileRegisters (&CpuMpData->CpuData[0].VolatileRegisters);
+ //
+ // Set BSP basic information
+ //
+ InitializeApData (CpuMpData, 0, 0, CpuMpData->Buffer);
+ //
+ // Save assembly code information
+ //
+ CopyMem (&CpuMpData->AddressMap, &AddressMap, sizeof (MP_ASSEMBLY_ADDRESS_MAP));
+ //
+ // Finally set AP loop mode
+ //
+ CpuMpData->ApLoopMode = ApLoopMode;
+ DEBUG ((DEBUG_INFO, "AP Loop Mode is %d\n", CpuMpData->ApLoopMode));
+ //
+ // Set up APs wakeup signal buffer
+ //
+ for (Index = 0; Index < MaxLogicalProcessorNumber; Index++) {
+ CpuMpData->CpuData[Index].StartupApSignal =
+ (UINT32 *)(MonitorBuffer + MonitorFilterSize * Index);
+ }
+ //
+ // Load Microcode on BSP
+ //
+ MicrocodeDetect (CpuMpData);
+ //
+ // Store BSP's MTRR setting
+ //
+ MtrrGetAllMtrrs (&CpuMpData->MtrrTable);
+
+ if (OldCpuMpData == NULL) {
+ if (MaxLogicalProcessorNumber > 1) {
+ //
+ // Wakeup all APs and calculate the processor count in system
+ //
+ CollectProcessorCount (CpuMpData);
+ }
+ } else {
+ //
+ // APs have been wakeup before, just get the CPU Information
+ // from HOB
+ //
+ CpuMpData->CpuCount = OldCpuMpData->CpuCount;
+ CpuMpData->BspNumber = OldCpuMpData->BspNumber;
+ CpuMpData->InitFlag = ApInitReconfig;
+ CpuMpData->CpuInfoInHob = OldCpuMpData->CpuInfoInHob;
+ CpuInfoInHob = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob;
+ for (Index = 0; Index < CpuMpData->CpuCount; Index++) {
+ InitializeSpinLock(&CpuMpData->CpuData[Index].ApLock);
+ if (CpuInfoInHob[Index].InitialApicId >= 255) {
+ CpuMpData->X2ApicEnable = TRUE;
+ }
+ CpuMpData->CpuData[Index].CpuHealthy = (CpuInfoInHob[Index].Health == 0)? TRUE:FALSE;
+ CpuMpData->CpuData[Index].ApFunction = 0;
+ CopyMem (
+ &CpuMpData->CpuData[Index].VolatileRegisters,
+ &CpuMpData->CpuData[0].VolatileRegisters,
+ sizeof (CPU_VOLATILE_REGISTERS)
+ );
+ }
+ if (MaxLogicalProcessorNumber > 1) {
+ //
+ // Wakeup APs to do some AP initialize sync
+ //
+ WakeUpAP (CpuMpData, TRUE, 0, ApInitializeSync, CpuMpData);
+ //
+ // Wait for all APs finished initialization
+ //
+ while (CpuMpData->FinishedCount < (CpuMpData->CpuCount - 1)) {
+ CpuPause ();
+ }
+ CpuMpData->InitFlag = ApInitDone;
+ for (Index = 0; Index < CpuMpData->CpuCount; Index++) {
+ SetApState (&CpuMpData->CpuData[Index], CpuStateIdle);
+ }
+ }
+ }
+
+ //
+ // Initialize global data for MP support
+ //
+ InitMpGlobalData (CpuMpData);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Gets detailed MP-related information on the requested processor at the
+ instant this call is made. This service may only be called from the BSP.
+
+ @param[in] ProcessorNumber The handle number of processor.
+ @param[out] ProcessorInfoBuffer A pointer to the buffer where information for
+ the requested processor is deposited.
+ @param[out] HealthData Return processor health data.
+
+ @retval EFI_SUCCESS Processor information was returned.
+ @retval EFI_DEVICE_ERROR The calling processor is an AP.
+ @retval EFI_INVALID_PARAMETER ProcessorInfoBuffer is NULL.
+ @retval EFI_NOT_FOUND The processor with the handle specified by
+ ProcessorNumber does not exist in the platform.
+ @retval EFI_NOT_READY MP Initialize Library is not initialized.
+
+**/
+EFI_STATUS
+EFIAPI
+MpInitLibGetProcessorInfo (
+ IN UINTN ProcessorNumber,
+ OUT EFI_PROCESSOR_INFORMATION *ProcessorInfoBuffer,
+ OUT EFI_HEALTH_FLAGS *HealthData OPTIONAL
+ )
+{
+ CPU_MP_DATA *CpuMpData;
+ UINTN CallerNumber;
+ CPU_INFO_IN_HOB *CpuInfoInHob;
+
+ CpuMpData = GetCpuMpData ();
+ CpuInfoInHob = (CPU_INFO_IN_HOB *) (UINTN) CpuMpData->CpuInfoInHob;
+
+ //
+ // Check whether caller processor is BSP
+ //
+ MpInitLibWhoAmI (&CallerNumber);
+ if (CallerNumber != CpuMpData->BspNumber) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ if (ProcessorInfoBuffer == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (ProcessorNumber >= CpuMpData->CpuCount) {
+ return EFI_NOT_FOUND;
+ }
+
+ ProcessorInfoBuffer->ProcessorId = (UINT64) CpuInfoInHob[ProcessorNumber].ApicId;
+ ProcessorInfoBuffer->StatusFlag = 0;
+ if (ProcessorNumber == CpuMpData->BspNumber) {
+ ProcessorInfoBuffer->StatusFlag |= PROCESSOR_AS_BSP_BIT;
+ }
+ if (CpuMpData->CpuData[ProcessorNumber].CpuHealthy) {
+ ProcessorInfoBuffer->StatusFlag |= PROCESSOR_HEALTH_STATUS_BIT;
+ }
+ if (GetApState (&CpuMpData->CpuData[ProcessorNumber]) == CpuStateDisabled) {
+ ProcessorInfoBuffer->StatusFlag &= ~PROCESSOR_ENABLED_BIT;
+ } else {
+ ProcessorInfoBuffer->StatusFlag |= PROCESSOR_ENABLED_BIT;
+ }
+
+ //
+ // Get processor location information
+ //
+ GetProcessorLocationByApicId (
+ CpuInfoInHob[ProcessorNumber].ApicId,
+ &ProcessorInfoBuffer->Location.Package,
+ &ProcessorInfoBuffer->Location.Core,
+ &ProcessorInfoBuffer->Location.Thread
+ );
+
+ if (HealthData != NULL) {
+ HealthData->Uint32 = CpuInfoInHob[ProcessorNumber].Health;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Worker function to switch the requested AP to be the BSP from that point onward.
+
+ @param[in] ProcessorNumber The handle number of AP that is to become the new BSP.
+ @param[in] EnableOldBSP If TRUE, then the old BSP will be listed as an
+ enabled AP. Otherwise, it will be disabled.
+
+ @retval EFI_SUCCESS BSP successfully switched.
+ @retval others Failed to switch BSP.
+
+**/
+EFI_STATUS
+SwitchBSPWorker (
+ IN UINTN ProcessorNumber,
+ IN BOOLEAN EnableOldBSP
+ )
+{
+ CPU_MP_DATA *CpuMpData;
+ UINTN CallerNumber;
+ CPU_STATE State;
+ MSR_IA32_APIC_BASE_REGISTER ApicBaseMsr;
+ BOOLEAN OldInterruptState;
+ BOOLEAN OldTimerInterruptState;
+
+ //
+ // Save and Disable Local APIC timer interrupt
+ //
+ OldTimerInterruptState = GetApicTimerInterruptState ();
+ DisableApicTimerInterrupt ();
+ //
+ // Before send both BSP and AP to a procedure to exchange their roles,
+ // interrupt must be disabled. This is because during the exchange role
+ // process, 2 CPU may use 1 stack. If interrupt happens, the stack will
+ // be corrupted, since interrupt return address will be pushed to stack
+ // by hardware.
+ //
+ OldInterruptState = SaveAndDisableInterrupts ();
+
+ //
+ // Mask LINT0 & LINT1 for the old BSP
+ //
+ DisableLvtInterrupts ();
+
+ CpuMpData = GetCpuMpData ();
+
+ //
+ // Check whether caller processor is BSP
+ //
+ MpInitLibWhoAmI (&CallerNumber);
+ if (CallerNumber != CpuMpData->BspNumber) {
+ return EFI_SUCCESS;
+ }
+
+ if (ProcessorNumber >= CpuMpData->CpuCount) {
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // Check whether specified AP is disabled
+ //
+ State = GetApState (&CpuMpData->CpuData[ProcessorNumber]);
+ if (State == CpuStateDisabled) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Check whether ProcessorNumber specifies the current BSP
+ //
+ if (ProcessorNumber == CpuMpData->BspNumber) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Check whether specified AP is busy
+ //
+ if (State == CpuStateBusy) {
+ return EFI_NOT_READY;
+ }
+
+ CpuMpData->BSPInfo.State = CPU_SWITCH_STATE_IDLE;
+ CpuMpData->APInfo.State = CPU_SWITCH_STATE_IDLE;
+ CpuMpData->SwitchBspFlag = TRUE;
+ CpuMpData->NewBspNumber = ProcessorNumber;
+
+ //
+ // Clear the BSP bit of MSR_IA32_APIC_BASE
+ //
+ ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE);
+ ApicBaseMsr.Bits.BSP = 0;
+ AsmWriteMsr64 (MSR_IA32_APIC_BASE, ApicBaseMsr.Uint64);
+
+ //
+ // Need to wakeUp AP (future BSP).
+ //
+ WakeUpAP (CpuMpData, FALSE, ProcessorNumber, FutureBSPProc, CpuMpData);
+
+ AsmExchangeRole (&CpuMpData->BSPInfo, &CpuMpData->APInfo);
+
+ //
+ // Set the BSP bit of MSR_IA32_APIC_BASE on new BSP
+ //
+ ApicBaseMsr.Uint64 = AsmReadMsr64 (MSR_IA32_APIC_BASE);
+ ApicBaseMsr.Bits.BSP = 1;
+ AsmWriteMsr64 (MSR_IA32_APIC_BASE, ApicBaseMsr.Uint64);
+
+ //
+ // Wait for old BSP finished AP task
+ //
+ while (GetApState (&CpuMpData->CpuData[CallerNumber]) != CpuStateFinished) {
+ CpuPause ();
+ }
+
+ CpuMpData->SwitchBspFlag = FALSE;
+ //
+ // Set old BSP enable state
+ //
+ if (!EnableOldBSP) {
+ SetApState (&CpuMpData->CpuData[CallerNumber], CpuStateDisabled);
+ } else {
+ SetApState (&CpuMpData->CpuData[CallerNumber], CpuStateIdle);
+ }
+ //
+ // Save new BSP number
+ //
+ CpuMpData->BspNumber = (UINT32) ProcessorNumber;
+
+ //
+ // Restore interrupt state.
+ //
+ SetInterruptState (OldInterruptState);
+
+ if (OldTimerInterruptState) {
+ EnableApicTimerInterrupt ();
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Worker function to let the caller enable or disable an AP from this point onward.
+ This service may only be called from the BSP.
+
+ @param[in] ProcessorNumber The handle number of AP.
+ @param[in] EnableAP Specifies the new state for the processor for
+ enabled, FALSE for disabled.
+ @param[in] HealthFlag If not NULL, a pointer to a value that specifies
+ the new health status of the AP.
+
+ @retval EFI_SUCCESS The specified AP was enabled or disabled successfully.
+ @retval others Failed to Enable/Disable AP.
+
+**/
+EFI_STATUS
+EnableDisableApWorker (
+ IN UINTN ProcessorNumber,
+ IN BOOLEAN EnableAP,
+ IN UINT32 *HealthFlag OPTIONAL
+ )
+{
+ CPU_MP_DATA *CpuMpData;
+ UINTN CallerNumber;
+
+ CpuMpData = GetCpuMpData ();
+
+ //
+ // Check whether caller processor is BSP
+ //
+ MpInitLibWhoAmI (&CallerNumber);
+ if (CallerNumber != CpuMpData->BspNumber) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ if (ProcessorNumber == CpuMpData->BspNumber) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (ProcessorNumber >= CpuMpData->CpuCount) {
+ return EFI_NOT_FOUND;
+ }
+
+ if (!EnableAP) {
+ SetApState (&CpuMpData->CpuData[ProcessorNumber], CpuStateDisabled);
+ } else {
+ SetApState (&CpuMpData->CpuData[ProcessorNumber], CpuStateIdle);
+ }
+
+ if (HealthFlag != NULL) {
+ CpuMpData->CpuData[ProcessorNumber].CpuHealthy =
+ (BOOLEAN) ((*HealthFlag & PROCESSOR_HEALTH_STATUS_BIT) != 0);
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ This return the handle number for the calling processor. This service may be
+ called from the BSP and APs.
+
+ @param[out] ProcessorNumber Pointer to the handle number of AP.
+ The range is from 0 to the total number of
+ logical processors minus 1. The total number of
+ logical processors can be retrieved by
+ MpInitLibGetNumberOfProcessors().
+
+ @retval EFI_SUCCESS The current processor handle number was returned
+ in ProcessorNumber.
+ @retval EFI_INVALID_PARAMETER ProcessorNumber is NULL.
+ @retval EFI_NOT_READY MP Initialize Library is not initialized.
+
+**/
+EFI_STATUS
+EFIAPI
+MpInitLibWhoAmI (
+ OUT UINTN *ProcessorNumber
+ )
+{
+ CPU_MP_DATA *CpuMpData;
+
+ if (ProcessorNumber == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ CpuMpData = GetCpuMpData ();
+
+ return GetProcessorNumber (CpuMpData, ProcessorNumber);
+}
+
+/**
+ Retrieves the number of logical processor in the platform and the number of
+ those logical processors that are enabled on this boot. This service may only
+ be called from the BSP.
+
+ @param[out] NumberOfProcessors Pointer to the total number of logical
+ processors in the system, including the BSP
+ and disabled APs.
+ @param[out] NumberOfEnabledProcessors Pointer to the number of enabled logical
+ processors that exist in system, including
+ the BSP.
+
+ @retval EFI_SUCCESS The number of logical processors and enabled
+ logical processors was retrieved.
+ @retval EFI_DEVICE_ERROR The calling processor is an AP.
+ @retval EFI_INVALID_PARAMETER NumberOfProcessors is NULL and NumberOfEnabledProcessors
+ is NULL.
+ @retval EFI_NOT_READY MP Initialize Library is not initialized.
+
+**/
+EFI_STATUS
+EFIAPI
+MpInitLibGetNumberOfProcessors (
+ OUT UINTN *NumberOfProcessors, OPTIONAL
+ OUT UINTN *NumberOfEnabledProcessors OPTIONAL
+ )
+{
+ CPU_MP_DATA *CpuMpData;
+ UINTN CallerNumber;
+ UINTN ProcessorNumber;
+ UINTN EnabledProcessorNumber;
+ UINTN Index;
+
+ CpuMpData = GetCpuMpData ();
+
+ if ((NumberOfProcessors == NULL) && (NumberOfEnabledProcessors == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Check whether caller processor is BSP
+ //
+ MpInitLibWhoAmI (&CallerNumber);
+ if (CallerNumber != CpuMpData->BspNumber) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ ProcessorNumber = CpuMpData->CpuCount;
+ EnabledProcessorNumber = 0;
+ for (Index = 0; Index < ProcessorNumber; Index++) {
+ if (GetApState (&CpuMpData->CpuData[Index]) != CpuStateDisabled) {
+ EnabledProcessorNumber ++;
+ }
+ }
+
+ if (NumberOfProcessors != NULL) {
+ *NumberOfProcessors = ProcessorNumber;
+ }
+ if (NumberOfEnabledProcessors != NULL) {
+ *NumberOfEnabledProcessors = EnabledProcessorNumber;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Worker function to execute a caller provided function on all enabled APs.
+
+ @param[in] Procedure A pointer to the function to be run on
+ enabled APs of the system.
+ @param[in] SingleThread If TRUE, then all the enabled APs execute
+ the function specified by Procedure one by
+ one, in ascending order of processor handle
+ number. If FALSE, then all the enabled APs
+ execute the function specified by Procedure
+ simultaneously.
+ @param[in] WaitEvent The event created by the caller with CreateEvent()
+ service.
+ @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for
+ APs to return from Procedure, either for
+ blocking or non-blocking mode.
+ @param[in] ProcedureArgument The parameter passed into Procedure for
+ all APs.
+ @param[out] FailedCpuList If all APs finish successfully, then its
+ content is set to NULL. If not all APs
+ finish before timeout expires, then its
+ content is set to address of the buffer
+ holding handle numbers of the failed APs.
+
+ @retval EFI_SUCCESS In blocking mode, all APs have finished before
+ the timeout expired.
+ @retval EFI_SUCCESS In non-blocking mode, function has been dispatched
+ to all enabled APs.
+ @retval others Failed to Startup all APs.
+
+**/
+EFI_STATUS
+StartupAllAPsWorker (
+ IN EFI_AP_PROCEDURE Procedure,
+ IN BOOLEAN SingleThread,
+ IN EFI_EVENT WaitEvent OPTIONAL,
+ IN UINTN TimeoutInMicroseconds,
+ IN VOID *ProcedureArgument OPTIONAL,
+ OUT UINTN **FailedCpuList OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ CPU_MP_DATA *CpuMpData;
+ UINTN ProcessorCount;
+ UINTN ProcessorNumber;
+ UINTN CallerNumber;
+ CPU_AP_DATA *CpuData;
+ BOOLEAN HasEnabledAp;
+ CPU_STATE ApState;
+
+ CpuMpData = GetCpuMpData ();
+
+ if (FailedCpuList != NULL) {
+ *FailedCpuList = NULL;
+ }
+
+ if (CpuMpData->CpuCount == 1) {
+ return EFI_NOT_STARTED;
+ }
+
+ if (Procedure == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Check whether caller processor is BSP
+ //
+ MpInitLibWhoAmI (&CallerNumber);
+ if (CallerNumber != CpuMpData->BspNumber) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ //
+ // Update AP state
+ //
+ CheckAndUpdateApsStatus ();
+
+ ProcessorCount = CpuMpData->CpuCount;
+ HasEnabledAp = FALSE;
+ //
+ // Check whether all enabled APs are idle.
+ // If any enabled AP is not idle, return EFI_NOT_READY.
+ //
+ for (ProcessorNumber = 0; ProcessorNumber < ProcessorCount; ProcessorNumber++) {
+ CpuData = &CpuMpData->CpuData[ProcessorNumber];
+ if (ProcessorNumber != CpuMpData->BspNumber) {
+ ApState = GetApState (CpuData);
+ if (ApState != CpuStateDisabled) {
+ HasEnabledAp = TRUE;
+ if (ApState != CpuStateIdle) {
+ //
+ // If any enabled APs are busy, return EFI_NOT_READY.
+ //
+ return EFI_NOT_READY;
+ }
+ }
+ }
+ }
+
+ if (!HasEnabledAp) {
+ //
+ // If no enabled AP exists, return EFI_NOT_STARTED.
+ //
+ return EFI_NOT_STARTED;
+ }
+
+ CpuMpData->StartCount = 0;
+ for (ProcessorNumber = 0; ProcessorNumber < ProcessorCount; ProcessorNumber++) {
+ CpuData = &CpuMpData->CpuData[ProcessorNumber];
+ CpuData->Waiting = FALSE;
+ if (ProcessorNumber != CpuMpData->BspNumber) {
+ if (CpuData->State == CpuStateIdle) {
+ //
+ // Mark this processor as responsible for current calling.
+ //
+ CpuData->Waiting = TRUE;
+ CpuMpData->StartCount++;
+ }
+ }
+ }
+
+ CpuMpData->Procedure = Procedure;
+ CpuMpData->ProcArguments = ProcedureArgument;
+ CpuMpData->SingleThread = SingleThread;
+ CpuMpData->FinishedCount = 0;
+ CpuMpData->RunningCount = 0;
+ CpuMpData->FailedCpuList = FailedCpuList;
+ CpuMpData->ExpectedTime = CalculateTimeout (
+ TimeoutInMicroseconds,
+ &CpuMpData->CurrentTime
+ );
+ CpuMpData->TotalTime = 0;
+ CpuMpData->WaitEvent = WaitEvent;
+
+ if (!SingleThread) {
+ WakeUpAP (CpuMpData, TRUE, 0, Procedure, ProcedureArgument);
+ } else {
+ for (ProcessorNumber = 0; ProcessorNumber < ProcessorCount; ProcessorNumber++) {
+ if (ProcessorNumber == CallerNumber) {
+ continue;
+ }
+ if (CpuMpData->CpuData[ProcessorNumber].Waiting) {
+ WakeUpAP (CpuMpData, FALSE, ProcessorNumber, Procedure, ProcedureArgument);
+ break;
+ }
+ }
+ }
+
+ Status = EFI_SUCCESS;
+ if (WaitEvent == NULL) {
+ do {
+ Status = CheckAllAPs ();
+ } while (Status == EFI_NOT_READY);
+ }
+
+ return Status;
+}
+
+/**
+ Worker function to let the caller get one enabled AP to execute a caller-provided
+ function.
+
+ @param[in] Procedure A pointer to the function to be run on
+ enabled APs of the system.
+ @param[in] ProcessorNumber The handle number of the AP.
+ @param[in] WaitEvent The event created by the caller with CreateEvent()
+ service.
+ @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for
+ APs to return from Procedure, either for
+ blocking or non-blocking mode.
+ @param[in] ProcedureArgument The parameter passed into Procedure for
+ all APs.
+ @param[out] Finished If AP returns from Procedure before the
+ timeout expires, its content is set to TRUE.
+ Otherwise, the value is set to FALSE.
+
+ @retval EFI_SUCCESS In blocking mode, specified AP finished before
+ the timeout expires.
+ @retval others Failed to Startup AP.
+
+**/
+EFI_STATUS
+StartupThisAPWorker (
+ IN EFI_AP_PROCEDURE Procedure,
+ IN UINTN ProcessorNumber,
+ IN EFI_EVENT WaitEvent OPTIONAL,
+ IN UINTN TimeoutInMicroseconds,
+ IN VOID *ProcedureArgument OPTIONAL,
+ OUT BOOLEAN *Finished OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ CPU_MP_DATA *CpuMpData;
+ CPU_AP_DATA *CpuData;
+ UINTN CallerNumber;
+
+ CpuMpData = GetCpuMpData ();
+
+ if (Finished != NULL) {
+ *Finished = FALSE;
+ }
+
+ //
+ // Check whether caller processor is BSP
+ //
+ MpInitLibWhoAmI (&CallerNumber);
+ if (CallerNumber != CpuMpData->BspNumber) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ //
+ // Check whether processor with the handle specified by ProcessorNumber exists
+ //
+ if (ProcessorNumber >= CpuMpData->CpuCount) {
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // Check whether specified processor is BSP
+ //
+ if (ProcessorNumber == CpuMpData->BspNumber) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Check parameter Procedure
+ //
+ if (Procedure == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Update AP state
+ //
+ CheckAndUpdateApsStatus ();
+
+ //
+ // Check whether specified AP is disabled
+ //
+ if (GetApState (&CpuMpData->CpuData[ProcessorNumber]) == CpuStateDisabled) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // If WaitEvent is not NULL, execute in non-blocking mode.
+ // BSP saves data for CheckAPsStatus(), and returns EFI_SUCCESS.
+ // CheckAPsStatus() will check completion and timeout periodically.
+ //
+ CpuData = &CpuMpData->CpuData[ProcessorNumber];
+ CpuData->WaitEvent = WaitEvent;
+ CpuData->Finished = Finished;
+ CpuData->ExpectedTime = CalculateTimeout (TimeoutInMicroseconds, &CpuData->CurrentTime);
+ CpuData->TotalTime = 0;
+
+ WakeUpAP (CpuMpData, FALSE, ProcessorNumber, Procedure, ProcedureArgument);
+
+ //
+ // If WaitEvent is NULL, execute in blocking mode.
+ // BSP checks AP's state until it finishes or TimeoutInMicrosecsond expires.
+ //
+ Status = EFI_SUCCESS;
+ if (WaitEvent == NULL) {
+ do {
+ Status = CheckThisAP (ProcessorNumber);
+ } while (Status == EFI_NOT_READY);
+ }
+
+ return Status;
+}
+
+/**
+ Get pointer to CPU MP Data structure from GUIDed HOB.
+
+ @return The pointer to CPU MP Data structure.
+**/
+CPU_MP_DATA *
+GetCpuMpDataFromGuidedHob (
+ VOID
+ )
+{
+ EFI_HOB_GUID_TYPE *GuidHob;
+ VOID *DataInHob;
+ CPU_MP_DATA *CpuMpData;
+
+ CpuMpData = NULL;
+ GuidHob = GetFirstGuidHob (&mCpuInitMpLibHobGuid);
+ if (GuidHob != NULL) {
+ DataInHob = GET_GUID_HOB_DATA (GuidHob);
+ CpuMpData = (CPU_MP_DATA *) (*(UINTN *) DataInHob);
+ }
+ return CpuMpData;
+}
+
+/**
+ Get available system memory below 1MB by specified size.
+
+ @param[in] CpuMpData The pointer to CPU MP Data structure.
+**/
+VOID
+BackupAndPrepareWakeupBuffer(
+ IN CPU_MP_DATA *CpuMpData
+ )
+{
+ CopyMem (
+ (VOID *) CpuMpData->BackupBuffer,
+ (VOID *) CpuMpData->WakeupBuffer,
+ CpuMpData->BackupBufferSize
+ );
+ CopyMem (
+ (VOID *) CpuMpData->WakeupBuffer,
+ (VOID *) CpuMpData->AddressMap.RendezvousFunnelAddress,
+ CpuMpData->AddressMap.RendezvousFunnelSize
+ );
+}
+
+/**
+ Restore wakeup buffer data.
+
+ @param[in] CpuMpData The pointer to CPU MP Data structure.
+**/
+VOID
+RestoreWakeupBuffer(
+ IN CPU_MP_DATA *CpuMpData
+ )
+{
+ CopyMem (
+ (VOID *) CpuMpData->WakeupBuffer,
+ (VOID *) CpuMpData->BackupBuffer,
+ CpuMpData->BackupBufferSize
+ );
+}
diff --git a/Core/UefiCpuPkg/Library/MpInitLib/MpLib.h b/Core/UefiCpuPkg/Library/MpInitLib/MpLib.h new file mode 100644 index 0000000000..7a272d78ec --- /dev/null +++ b/Core/UefiCpuPkg/Library/MpInitLib/MpLib.h @@ -0,0 +1,594 @@ +/** @file
+ Common header file for MP Initialize Library.
+
+ Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
+ 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 _MP_LIB_H_
+#define _MP_LIB_H_
+
+#include <PiPei.h>
+
+#include <Register/Cpuid.h>
+#include <Register/Msr.h>
+#include <Register/LocalApic.h>
+#include <Register/Microcode.h>
+
+#include <Library/MpInitLib.h>
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/DebugLib.h>
+#include <Library/LocalApicLib.h>
+#include <Library/CpuLib.h>
+#include <Library/UefiCpuLib.h>
+#include <Library/TimerLib.h>
+#include <Library/SynchronizationLib.h>
+#include <Library/MtrrLib.h>
+#include <Library/HobLib.h>
+
+#define WAKEUP_AP_SIGNAL SIGNATURE_32 ('S', 'T', 'A', 'P')
+
+#define CPU_INIT_MP_LIB_HOB_GUID \
+ { \
+ 0x58eb6a19, 0x3699, 0x4c68, { 0xa8, 0x36, 0xda, 0xcd, 0x8e, 0xdc, 0xad, 0x4a } \
+ }
+
+//
+// The MP data for switch BSP
+//
+#define CPU_SWITCH_STATE_IDLE 0
+#define CPU_SWITCH_STATE_STORED 1
+#define CPU_SWITCH_STATE_LOADED 2
+
+//
+// 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;
+
+//
+// AP loop state when APs are in idle state
+// It's value is the same with PcdCpuApLoopMode
+//
+typedef enum {
+ ApInHltLoop = 1,
+ ApInMwaitLoop = 2,
+ ApInRunLoop = 3
+} AP_LOOP_MODE;
+
+//
+// AP initialization state during APs wakeup
+//
+typedef enum {
+ ApInitConfig = 1,
+ ApInitReconfig = 2,
+ ApInitDone = 3
+} AP_INIT_STATE;
+
+//
+// AP state
+//
+typedef enum {
+ CpuStateIdle,
+ CpuStateReady,
+ CpuStateBusy,
+ CpuStateFinished,
+ CpuStateDisabled
+} CPU_STATE;
+
+//
+// CPU volatile registers around INIT-SIPI-SIPI
+//
+typedef struct {
+ UINTN Cr0;
+ UINTN Cr3;
+ UINTN Cr4;
+ UINTN Dr0;
+ UINTN Dr1;
+ UINTN Dr2;
+ UINTN Dr3;
+ UINTN Dr6;
+ UINTN Dr7;
+} CPU_VOLATILE_REGISTERS;
+
+//
+// AP related data
+//
+typedef struct {
+ SPIN_LOCK ApLock;
+ volatile UINT32 *StartupApSignal;
+ volatile UINTN ApFunction;
+ volatile UINTN ApFunctionArgument;
+ BOOLEAN CpuHealthy;
+ volatile CPU_STATE State;
+ CPU_VOLATILE_REGISTERS VolatileRegisters;
+ BOOLEAN Waiting;
+ BOOLEAN *Finished;
+ UINT64 ExpectedTime;
+ UINT64 CurrentTime;
+ UINT64 TotalTime;
+ EFI_EVENT WaitEvent;
+} CPU_AP_DATA;
+
+//
+// Basic CPU information saved in Guided HOB.
+// Because the contents will be shard between PEI and DXE,
+// we need to make sure the each fields offset same in different
+// architecture.
+//
+#pragma pack (1)
+typedef struct {
+ UINT32 InitialApicId;
+ UINT32 ApicId;
+ UINT32 Health;
+ UINT64 ApTopOfStack;
+} CPU_INFO_IN_HOB;
+#pragma pack ()
+
+//
+// AP reset code information including code address and size,
+// this structure will be shared be C code and assembly code.
+// It is natural aligned by design.
+//
+typedef struct {
+ UINT8 *RendezvousFunnelAddress;
+ UINTN ModeEntryOffset;
+ UINTN RendezvousFunnelSize;
+ UINT8 *RelocateApLoopFuncAddress;
+ UINTN RelocateApLoopFuncSize;
+} MP_ASSEMBLY_ADDRESS_MAP;
+
+typedef struct _CPU_MP_DATA 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 EnableExecuteDisable;
+ UINTN Cr3;
+ UINTN InitFlag;
+ CPU_INFO_IN_HOB *CpuInfo;
+ CPU_MP_DATA *CpuMpData;
+} MP_CPU_EXCHANGE_INFO;
+
+#pragma pack()
+
+//
+// CPU MP Data save in memory
+//
+struct _CPU_MP_DATA {
+ UINT64 CpuInfoInHob;
+ UINT32 CpuCount;
+ UINT32 BspNumber;
+ //
+ // The above fields data will be passed from PEI to DXE
+ // Please make sure the fields offset same in the different
+ // architecture.
+ //
+ SPIN_LOCK MpLock;
+ UINTN Buffer;
+ UINTN CpuApStackSize;
+ MP_ASSEMBLY_ADDRESS_MAP AddressMap;
+ UINTN WakeupBuffer;
+ UINTN BackupBuffer;
+ UINTN BackupBufferSize;
+ BOOLEAN SaveRestoreFlag;
+
+ volatile UINT32 StartCount;
+ volatile UINT32 FinishedCount;
+ volatile UINT32 RunningCount;
+ BOOLEAN SingleThread;
+ EFI_AP_PROCEDURE Procedure;
+ VOID *ProcArguments;
+ BOOLEAN *Finished;
+ UINT64 ExpectedTime;
+ UINT64 CurrentTime;
+ UINT64 TotalTime;
+ EFI_EVENT WaitEvent;
+ UINTN **FailedCpuList;
+
+ AP_INIT_STATE InitFlag;
+ BOOLEAN X2ApicEnable;
+ BOOLEAN SwitchBspFlag;
+ UINTN NewBspNumber;
+ CPU_EXCHANGE_ROLE_INFO BSPInfo;
+ CPU_EXCHANGE_ROLE_INFO APInfo;
+ MTRR_SETTINGS MtrrTable;
+ UINT8 ApLoopMode;
+ UINT8 ApTargetCState;
+ UINT16 PmCodeSegment;
+ CPU_AP_DATA *CpuData;
+ volatile MP_CPU_EXCHANGE_INFO *MpCpuExchangeInfo;
+
+ UINT32 CurrentTimerCount;
+ UINTN DivideValue;
+ UINT8 Vector;
+ BOOLEAN PeriodicMode;
+ BOOLEAN TimerInterruptState;
+};
+
+extern EFI_GUID mCpuInitMpLibHobGuid;
+
+/**
+ Assembly code to place AP into safe loop mode.
+
+ Place AP into targeted C-State if MONITOR is supported, otherwise
+ place AP into hlt state.
+ Place AP in protected mode if the current is long mode. Due to AP maybe
+ wakeup by some hardware event. It could avoid accessing page table that
+ may not available during booting to OS.
+
+ @param[in] MwaitSupport TRUE indicates MONITOR is supported.
+ FALSE indicates MONITOR is not supported.
+ @param[in] ApTargetCState Target C-State value.
+ @param[in] PmCodeSegment Protected mode code segment value.
+**/
+typedef
+VOID
+(EFIAPI * ASM_RELOCATE_AP_LOOP) (
+ IN BOOLEAN MwaitSupport,
+ IN UINTN ApTargetCState,
+ IN UINTN PmCodeSegment,
+ IN UINTN TopOfApStack,
+ IN UINTN NumberToFinish
+ );
+
+/**
+ 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[out] AddressMap Output buffer for address map information.
+**/
+VOID
+EFIAPI
+AsmGetAddressMap (
+ OUT MP_ASSEMBLY_ADDRESS_MAP *AddressMap
+ );
+
+/**
+ 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[in] MyInfo Pointer to buffer holding the exchanging information for the executing processor.
+ @param[in] 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
+ );
+
+/**
+ Get the pointer to CPU MP Data structure.
+
+ @return The pointer to CPU MP Data structure.
+**/
+CPU_MP_DATA *
+GetCpuMpData (
+ VOID
+ );
+
+/**
+ Save the pointer to CPU MP Data structure.
+
+ @param[in] CpuMpData The pointer to CPU MP Data structure will be saved.
+**/
+VOID
+SaveCpuMpData (
+ IN CPU_MP_DATA *CpuMpData
+ );
+
+/**
+ Allocate reset vector buffer.
+
+ @param[in, out] CpuMpData The pointer to CPU MP Data structure.
+**/
+VOID
+AllocateResetVector (
+ IN OUT CPU_MP_DATA *CpuMpData
+ );
+
+/**
+ Free AP reset vector buffer.
+
+ @param[in] CpuMpData The pointer to CPU MP Data structure.
+**/
+VOID
+FreeResetVector (
+ IN CPU_MP_DATA *CpuMpData
+ );
+
+/**
+ This function will be called by BSP to wakeup AP.
+
+ @param[in] CpuMpData Pointer to CPU MP Data
+ @param[in] Broadcast TRUE: Send broadcast IPI to all APs
+ FALSE: Send IPI to AP by ApicId
+ @param[in] ProcessorNumber The handle number of specified processor
+ @param[in] Procedure The function to be invoked by AP
+ @param[in] ProcedureArgument The argument to be passed into AP function
+**/
+VOID
+WakeUpAP (
+ IN CPU_MP_DATA *CpuMpData,
+ IN BOOLEAN Broadcast,
+ IN UINTN ProcessorNumber,
+ IN EFI_AP_PROCEDURE Procedure, OPTIONAL
+ IN VOID *ProcedureArgument OPTIONAL
+ );
+
+/**
+ Initialize global data for MP support.
+
+ @param[in] CpuMpData The pointer to CPU MP Data structure.
+**/
+VOID
+InitMpGlobalData (
+ IN CPU_MP_DATA *CpuMpData
+ );
+
+/**
+ Worker function to execute a caller provided function on all enabled APs.
+
+ @param[in] Procedure A pointer to the function to be run on
+ enabled APs of the system.
+ @param[in] SingleThread If TRUE, then all the enabled APs execute
+ the function specified by Procedure one by
+ one, in ascending order of processor handle
+ number. If FALSE, then all the enabled APs
+ execute the function specified by Procedure
+ simultaneously.
+ @param[in] WaitEvent The event created by the caller with CreateEvent()
+ service.
+ @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for
+ APs to return from Procedure, either for
+ blocking or non-blocking mode.
+ @param[in] ProcedureArgument The parameter passed into Procedure for
+ all APs.
+ @param[out] FailedCpuList If all APs finish successfully, then its
+ content is set to NULL. If not all APs
+ finish before timeout expires, then its
+ content is set to address of the buffer
+ holding handle numbers of the failed APs.
+
+ @retval EFI_SUCCESS In blocking mode, all APs have finished before
+ the timeout expired.
+ @retval EFI_SUCCESS In non-blocking mode, function has been dispatched
+ to all enabled APs.
+ @retval others Failed to Startup all APs.
+
+**/
+EFI_STATUS
+StartupAllAPsWorker (
+ IN EFI_AP_PROCEDURE Procedure,
+ IN BOOLEAN SingleThread,
+ IN EFI_EVENT WaitEvent OPTIONAL,
+ IN UINTN TimeoutInMicroseconds,
+ IN VOID *ProcedureArgument OPTIONAL,
+ OUT UINTN **FailedCpuList OPTIONAL
+ );
+
+/**
+ Worker function to let the caller get one enabled AP to execute a caller-provided
+ function.
+
+ @param[in] Procedure A pointer to the function to be run on
+ enabled APs of the system.
+ @param[in] ProcessorNumber The handle number of the AP.
+ @param[in] WaitEvent The event created by the caller with CreateEvent()
+ service.
+ @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for
+ APs to return from Procedure, either for
+ blocking or non-blocking mode.
+ @param[in] ProcedureArgument The parameter passed into Procedure for
+ all APs.
+ @param[out] Finished If AP returns from Procedure before the
+ timeout expires, its content is set to TRUE.
+ Otherwise, the value is set to FALSE.
+
+ @retval EFI_SUCCESS In blocking mode, specified AP finished before
+ the timeout expires.
+ @retval others Failed to Startup AP.
+
+**/
+EFI_STATUS
+StartupThisAPWorker (
+ IN EFI_AP_PROCEDURE Procedure,
+ IN UINTN ProcessorNumber,
+ IN EFI_EVENT WaitEvent OPTIONAL,
+ IN UINTN TimeoutInMicroseconds,
+ IN VOID *ProcedureArgument OPTIONAL,
+ OUT BOOLEAN *Finished OPTIONAL
+ );
+
+/**
+ Worker function to switch the requested AP to be the BSP from that point onward.
+
+ @param[in] ProcessorNumber The handle number of AP that is to become the new BSP.
+ @param[in] EnableOldBSP If TRUE, then the old BSP will be listed as an
+ enabled AP. Otherwise, it will be disabled.
+
+ @retval EFI_SUCCESS BSP successfully switched.
+ @retval others Failed to switch BSP.
+
+**/
+EFI_STATUS
+SwitchBSPWorker (
+ IN UINTN ProcessorNumber,
+ IN BOOLEAN EnableOldBSP
+ );
+
+/**
+ Worker function to let the caller enable or disable an AP from this point onward.
+ This service may only be called from the BSP.
+
+ @param[in] ProcessorNumber The handle number of AP.
+ @param[in] EnableAP Specifies the new state for the processor for
+ enabled, FALSE for disabled.
+ @param[in] HealthFlag If not NULL, a pointer to a value that specifies
+ the new health status of the AP.
+
+ @retval EFI_SUCCESS The specified AP was enabled or disabled successfully.
+ @retval others Failed to Enable/Disable AP.
+
+**/
+EFI_STATUS
+EnableDisableApWorker (
+ IN UINTN ProcessorNumber,
+ IN BOOLEAN EnableAP,
+ IN UINT32 *HealthFlag OPTIONAL
+ );
+
+/**
+ Get pointer to CPU MP Data structure from GUIDed HOB.
+
+ @return The pointer to CPU MP Data structure.
+**/
+CPU_MP_DATA *
+GetCpuMpDataFromGuidedHob (
+ VOID
+ );
+
+/** Checks status of specified AP.
+
+ This function checks whether the specified AP has finished the task assigned
+ by StartupThisAP(), and whether timeout expires.
+
+ @param[in] ProcessorNumber The handle number of processor.
+
+ @retval EFI_SUCCESS Specified AP has finished task assigned by StartupThisAPs().
+ @retval EFI_TIMEOUT The timeout expires.
+ @retval EFI_NOT_READY Specified AP has not finished task and timeout has not expired.
+**/
+EFI_STATUS
+CheckThisAP (
+ IN UINTN ProcessorNumber
+ );
+
+/**
+ Checks status of all APs.
+
+ This function checks whether all APs have finished task assigned by StartupAllAPs(),
+ and whether timeout expires.
+
+ @retval EFI_SUCCESS All APs have finished task assigned by StartupAllAPs().
+ @retval EFI_TIMEOUT The timeout expires.
+ @retval EFI_NOT_READY APs have not finished task and timeout has not expired.
+**/
+EFI_STATUS
+CheckAllAPs (
+ VOID
+ );
+
+/**
+ Checks APs status and updates APs status if needed.
+
+**/
+VOID
+CheckAndUpdateApsStatus (
+ VOID
+ );
+
+/**
+ Detect whether specified processor can find matching microcode patch and load it.
+
+ @param[in] CpuMpData The pointer to CPU MP Data structure.
+**/
+VOID
+MicrocodeDetect (
+ IN CPU_MP_DATA *CpuMpData
+ );
+
+/**
+ Detect whether Mwait-monitor feature is supported.
+
+ @retval TRUE Mwait-monitor feature is supported.
+ @retval FALSE Mwait-monitor feature is not supported.
+**/
+BOOLEAN
+IsMwaitSupport (
+ VOID
+ );
+
+/**
+ 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[in] PeiServices The pointer to the PEI Services Table.
+ @param[in] NotifyDescriptor Address of the notification descriptor data structure.
+ @param[in] 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
+ );
+
+/**
+ Get available system memory below 1MB by specified size.
+
+ @param[in] CpuMpData The pointer to CPU MP Data structure.
+**/
+VOID
+BackupAndPrepareWakeupBuffer(
+ IN CPU_MP_DATA *CpuMpData
+ );
+
+/**
+ Restore wakeup buffer data.
+
+ @param[in] CpuMpData The pointer to CPU MP Data structure.
+**/
+VOID
+RestoreWakeupBuffer(
+ IN CPU_MP_DATA *CpuMpData
+ );
+
+/**
+ Enable Debug Agent to support source debugging on AP function.
+
+**/
+VOID
+EnableDebugAgent (
+ VOID
+ );
+
+#endif
+
diff --git a/Core/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf b/Core/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf new file mode 100644 index 0000000000..0c6873da79 --- /dev/null +++ b/Core/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.inf @@ -0,0 +1,70 @@ +## @file
+# MP Initialize Library instance for PEI driver.
+#
+# Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
+# 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 = PeiMpInitLib
+ MODULE_UNI_FILE = PeiMpInitLib.uni
+ FILE_GUID = B00F6090-7739-4830-B906-E0032D388987
+ MODULE_TYPE = PEIM
+ VERSION_STRING = 1.1
+ LIBRARY_CLASS = MpInitLib|PEIM
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64
+#
+
+[Sources.IA32]
+ Ia32/MpEqu.inc
+ Ia32/MpFuncs.nasm
+
+[Sources.X64]
+ X64/MpEqu.inc
+ X64/MpFuncs.nasm
+
+[Sources.common]
+ PeiMpLib.c
+ MpLib.c
+ MpLib.h
+ Microcode.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ UefiCpuPkg/UefiCpuPkg.dec
+
+[LibraryClasses]
+ BaseLib
+ LocalApicLib
+ MemoryAllocationLib
+ HobLib
+ PeiServicesLib
+ MtrrLib
+ CpuLib
+ UefiCpuLib
+ SynchronizationLib
+
+[Ppis]
+ gEfiEndOfPeiSignalPpiGuid ## NOTIFY
+
+[Pcd]
+ gUefiCpuPkgTokenSpaceGuid.PcdCpuMaxLogicalProcessorNumber ## CONSUMES
+ gUefiCpuPkgTokenSpaceGuid.PcdCpuApInitTimeOutInMicroSeconds ## CONSUMES
+ gUefiCpuPkgTokenSpaceGuid.PcdCpuApStackSize ## CONSUMES
+ gUefiCpuPkgTokenSpaceGuid.PcdCpuMicrocodePatchAddress ## CONSUMES
+ gUefiCpuPkgTokenSpaceGuid.PcdCpuMicrocodePatchRegionSize ## CONSUMES
+ gUefiCpuPkgTokenSpaceGuid.PcdCpuApLoopMode ## CONSUMES
+ gUefiCpuPkgTokenSpaceGuid.PcdCpuApTargetCstate ## SOMETIMES_CONSUMES
+
diff --git a/Core/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.uni b/Core/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.uni new file mode 100644 index 0000000000..d16f306685 --- /dev/null +++ b/Core/UefiCpuPkg/Library/MpInitLib/PeiMpInitLib.uni @@ -0,0 +1,22 @@ +// /** @file
+// MP Initialize Library instance for PEI driver.
+//
+// MP Initialize Library instance for PEI driver.
+//
+// Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
+//
+// 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 "MP Initialize Library instance for PEI driver."
+
+#string STR_MODULE_DESCRIPTION #language en-US "MP Initialize Library instance for PEI driver."
+
diff --git a/Core/UefiCpuPkg/Library/MpInitLib/PeiMpLib.c b/Core/UefiCpuPkg/Library/MpInitLib/PeiMpLib.c new file mode 100644 index 0000000000..fb1d48fad8 --- /dev/null +++ b/Core/UefiCpuPkg/Library/MpInitLib/PeiMpLib.c @@ -0,0 +1,624 @@ +/** @file
+ MP initialize support functions for PEI phase.
+
+ Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
+ 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 "MpLib.h"
+#include <Ppi/EndOfPeiPhase.h>
+#include <Library/PeiServicesLib.h>
+
+//
+// Global PEI notify function descriptor on EndofPei event
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_PEI_NOTIFY_DESCRIPTOR mMpInitLibNotifyList = {
+ (EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
+ &gEfiEndOfPeiSignalPpiGuid,
+ CpuMpEndOfPeiCallback
+};
+
+
+/**
+ Enable Debug Agent to support source debugging on AP function.
+
+**/
+VOID
+EnableDebugAgent (
+ VOID
+ )
+{
+}
+
+/**
+ Get pointer to CPU MP Data structure.
+
+ @return The pointer to CPU MP Data structure.
+**/
+CPU_MP_DATA *
+GetCpuMpData (
+ VOID
+ )
+{
+ CPU_MP_DATA *CpuMpData;
+
+ CpuMpData = GetCpuMpDataFromGuidedHob ();
+ ASSERT (CpuMpData != NULL);
+ return CpuMpData;
+}
+
+/**
+ Save the pointer to CPU MP Data structure.
+
+ @param[in] CpuMpData The pointer to CPU MP Data structure will be saved.
+**/
+VOID
+SaveCpuMpData (
+ IN CPU_MP_DATA *CpuMpData
+ )
+{
+ UINT64 Data64;
+ //
+ // Build location of CPU MP DATA buffer in HOB
+ //
+ Data64 = (UINT64) (UINTN) CpuMpData;
+ BuildGuidDataHob (
+ &mCpuInitMpLibHobGuid,
+ (VOID *) &Data64,
+ sizeof (UINT64)
+ );
+}
+
+/**
+ 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[in] PeiServices The pointer to the PEI Services Table.
+ @param[in] NotifyDescriptor Address of the notification descriptor data structure.
+ @param[in] 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;
+ CPU_MP_DATA *CpuMpData;
+ EFI_PEI_HOB_POINTERS Hob;
+ EFI_HOB_MEMORY_ALLOCATION *MemoryHob;
+
+ DEBUG ((DEBUG_INFO, "PeiMpInitLib: CpuMpEndOfPeiCallback () invoked\n"));
+
+ Status = PeiServicesGetBootMode (&BootMode);
+ ASSERT_EFI_ERROR (Status);
+
+ CpuMpData = GetCpuMpData ();
+ 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 == CpuMpData->WakeupBuffer) {
+ //
+ // Flag this HOB type to un-used
+ //
+ GET_HOB_TYPE (Hob) = EFI_HOB_TYPE_UNUSED;
+ break;
+ }
+ }
+ Hob.Raw = GET_NEXT_HOB (Hob);
+ }
+ } else {
+ CpuMpData->SaveRestoreFlag = TRUE;
+ RestoreWakeupBuffer (CpuMpData);
+ }
+ return EFI_SUCCESS;
+}
+
+/**
+ Check if AP wakeup buffer is overlapped with existing allocated buffer.
+
+ @param[in] WakeupBufferStart AP wakeup buffer start address.
+ @param[in] WakeupBufferEnd AP wakeup buffer end address.
+
+ @retval TRUE There is overlap.
+ @retval FALSE There is no overlap.
+**/
+BOOLEAN
+CheckOverlapWithAllocatedBuffer (
+ IN UINTN WakeupBufferStart,
+ IN UINTN WakeupBufferEnd
+ )
+{
+ EFI_PEI_HOB_POINTERS Hob;
+ EFI_HOB_MEMORY_ALLOCATION *MemoryHob;
+ BOOLEAN Overlapped;
+ UINTN MemoryStart;
+ UINTN MemoryEnd;
+
+ Overlapped = FALSE;
+ //
+ // 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;
+ MemoryStart = (UINTN) MemoryHob->AllocDescriptor.MemoryBaseAddress;
+ MemoryEnd = (UINTN) (MemoryHob->AllocDescriptor.MemoryBaseAddress +
+ MemoryHob->AllocDescriptor.MemoryLength);
+ if (!((WakeupBufferStart >= MemoryEnd) || (WakeupBufferEnd <= MemoryStart))) {
+ Overlapped = TRUE;
+ break;
+ }
+ }
+ Hob.Raw = GET_NEXT_HOB (Hob);
+ }
+ return Overlapped;
+}
+
+/**
+ Get available system memory below 1MB by specified size.
+
+ @param[in] 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;
+
+ WakeupBufferSize = (WakeupBufferSize + SIZE_4KB - 1) & ~(SIZE_4KB - 1);
+
+ //
+ // 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;
+ }
+ while (WakeupBufferEnd > WakeupBufferSize) {
+ //
+ // Wakeup buffer should be aligned on 4KB
+ //
+ WakeupBufferStart = (WakeupBufferEnd - WakeupBufferSize) & ~(SIZE_4KB - 1);
+ if (WakeupBufferStart < Hob.ResourceDescriptor->PhysicalStart) {
+ break;
+ }
+ if (CheckOverlapWithAllocatedBuffer (WakeupBufferStart, WakeupBufferEnd)) {
+ //
+ // If this range is overlapped with existing allocated buffer, skip it
+ // and find the next range
+ //
+ WakeupBufferEnd -= WakeupBufferSize;
+ continue;
+ }
+ DEBUG ((DEBUG_INFO, "WakeupBufferStart = %x, WakeupBufferSize = %x\n",
+ WakeupBufferStart, WakeupBufferSize));
+ //
+ // Create a memory allocation HOB.
+ //
+ BuildMemoryAllocationHob (
+ WakeupBufferStart,
+ WakeupBufferSize,
+ EfiBootServicesData
+ );
+ return WakeupBufferStart;
+ }
+ }
+ }
+ //
+ // Find the next HOB
+ //
+ Hob.Raw = GET_NEXT_HOB (Hob);
+ }
+
+ return (UINTN) -1;
+}
+
+/**
+ Allocate reset vector buffer.
+
+ @param[in, out] CpuMpData The pointer to CPU MP Data structure.
+**/
+VOID
+AllocateResetVector (
+ IN OUT CPU_MP_DATA *CpuMpData
+ )
+{
+ UINTN ApResetVectorSize;
+
+ if (CpuMpData->WakeupBuffer == (UINTN) -1) {
+ ApResetVectorSize = CpuMpData->AddressMap.RendezvousFunnelSize +
+ sizeof (MP_CPU_EXCHANGE_INFO);
+
+ CpuMpData->WakeupBuffer = GetWakeupBuffer (ApResetVectorSize);
+ CpuMpData->MpCpuExchangeInfo = (MP_CPU_EXCHANGE_INFO *) (UINTN)
+ (CpuMpData->WakeupBuffer + CpuMpData->AddressMap.RendezvousFunnelSize);
+ BackupAndPrepareWakeupBuffer (CpuMpData);
+ }
+
+ if (CpuMpData->SaveRestoreFlag) {
+ BackupAndPrepareWakeupBuffer (CpuMpData);
+ }
+}
+
+/**
+ Free AP reset vector buffer.
+
+ @param[in] CpuMpData The pointer to CPU MP Data structure.
+**/
+VOID
+FreeResetVector (
+ IN CPU_MP_DATA *CpuMpData
+ )
+{
+ if (CpuMpData->SaveRestoreFlag) {
+ RestoreWakeupBuffer (CpuMpData);
+ }
+}
+
+/**
+ Checks APs status and updates APs status if needed.
+
+**/
+VOID
+CheckAndUpdateApsStatus (
+ VOID
+ )
+{
+}
+
+/**
+ Initialize global data for MP support.
+
+ @param[in] CpuMpData The pointer to CPU MP Data structure.
+**/
+VOID
+InitMpGlobalData (
+ IN CPU_MP_DATA *CpuMpData
+ )
+{
+ EFI_STATUS Status;
+
+ SaveCpuMpData (CpuMpData);
+
+ if (CpuMpData->CpuCount == 1) {
+ //
+ // If only BSP exists, return
+ //
+ return;
+ }
+
+ //
+ // Register an event for EndOfPei
+ //
+ Status = PeiServicesNotifyPpi (&mMpInitLibNotifyList);
+ ASSERT_EFI_ERROR (Status);
+}
+
+/**
+ This service executes a caller provided function on all enabled APs.
+
+ @param[in] Procedure A pointer to the function to be run on
+ enabled APs of the system. See type
+ EFI_AP_PROCEDURE.
+ @param[in] SingleThread If TRUE, then all the enabled APs execute
+ the function specified by Procedure one by
+ one, in ascending order of processor handle
+ number. If FALSE, then all the enabled APs
+ execute the function specified by Procedure
+ simultaneously.
+ @param[in] WaitEvent The event created by the caller with CreateEvent()
+ service. If it is NULL, then execute in
+ blocking mode. BSP waits until all APs finish
+ or TimeoutInMicroSeconds expires. If it's
+ not NULL, then execute in non-blocking mode.
+ BSP requests the function specified by
+ Procedure to be started on all the enabled
+ APs, and go on executing immediately. If
+ all return from Procedure, or TimeoutInMicroSeconds
+ expires, this event is signaled. The BSP
+ can use the CheckEvent() or WaitForEvent()
+ services to check the state of event. Type
+ EFI_EVENT is defined in CreateEvent() in
+ the Unified Extensible Firmware Interface
+ Specification.
+ @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for
+ APs to return from Procedure, either for
+ blocking or non-blocking mode. 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 MpInitLibStartupAllAPs() or
+ MPInitLibStartupThisAP().
+ If the timeout expires in blocking mode,
+ BSP returns EFI_TIMEOUT. If the timeout
+ expires in non-blocking mode, WaitEvent
+ is signaled with SignalEvent().
+ @param[in] ProcedureArgument The parameter passed into Procedure for
+ all APs.
+ @param[out] FailedCpuList If NULL, this parameter is ignored. Otherwise,
+ if all APs finish successfully, then its
+ content is set to NULL. If not all APs
+ finish before timeout expires, then its
+ content is set to address of the buffer
+ holding handle numbers of the failed APs.
+ The buffer is allocated by MP Initialization
+ library, and it's the caller's responsibility to
+ free the buffer with FreePool() service.
+ In blocking mode, it is ready for consumption
+ when the call returns. In non-blocking mode,
+ it is ready when WaitEvent is signaled. The
+ list of failed CPU is terminated by
+ END_OF_CPU_LIST.
+
+ @retval EFI_SUCCESS In blocking mode, all APs have finished before
+ the timeout expired.
+ @retval EFI_SUCCESS In non-blocking mode, function has been dispatched
+ to all enabled APs.
+ @retval EFI_UNSUPPORTED A non-blocking mode request was made after the
+ UEFI event EFI_EVENT_GROUP_READY_TO_BOOT was
+ signaled.
+ @retval EFI_UNSUPPORTED WaitEvent is not NULL if non-blocking mode is not
+ supported.
+ @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_NOT_READY MP Initialize Library is not initialized.
+ @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
+MpInitLibStartupAllAPs (
+ IN EFI_AP_PROCEDURE Procedure,
+ IN BOOLEAN SingleThread,
+ IN EFI_EVENT WaitEvent OPTIONAL,
+ IN UINTN TimeoutInMicroseconds,
+ IN VOID *ProcedureArgument OPTIONAL,
+ OUT UINTN **FailedCpuList OPTIONAL
+ )
+{
+ if (WaitEvent != NULL) {
+ return EFI_UNSUPPORTED;
+ }
+
+ return StartupAllAPsWorker (
+ Procedure,
+ SingleThread,
+ NULL,
+ TimeoutInMicroseconds,
+ ProcedureArgument,
+ FailedCpuList
+ );
+}
+
+/**
+ This service lets the caller get one enabled AP to execute a caller-provided
+ function.
+
+ @param[in] Procedure A pointer to the function to be run on the
+ designated AP of the system. See type
+ EFI_AP_PROCEDURE.
+ @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
+ MpInitLibGetNumberOfProcessors().
+ @param[in] WaitEvent The event created by the caller with CreateEvent()
+ service. If it is NULL, then execute in
+ blocking mode. BSP waits until this AP finish
+ or TimeoutInMicroSeconds expires. If it's
+ not NULL, then execute in non-blocking mode.
+ BSP requests the function specified by
+ Procedure to be started on this AP,
+ and go on executing immediately. If this AP
+ return from Procedure or TimeoutInMicroSeconds
+ expires, this event is signaled. The BSP
+ can use the CheckEvent() or WaitForEvent()
+ services to check the state of event. Type
+ EFI_EVENT is defined in CreateEvent() in
+ the Unified Extensible Firmware Interface
+ Specification.
+ @param[in] TimeoutInMicroseconds Indicates the time limit in microseconds for
+ this AP to finish this Procedure, either for
+ blocking or non-blocking mode. Zero means
+ infinity. If the timeout expires before
+ this AP returns from Procedure, then Procedure
+ on the AP is terminated. The
+ AP is available for next function assigned
+ by MpInitLibStartupAllAPs() or
+ MpInitLibStartupThisAP().
+ If the timeout expires in blocking mode,
+ BSP returns EFI_TIMEOUT. If the timeout
+ expires in non-blocking mode, WaitEvent
+ is signaled with SignalEvent().
+ @param[in] ProcedureArgument The parameter passed into Procedure on the
+ specified AP.
+ @param[out] Finished If NULL, this parameter is ignored. In
+ blocking mode, this parameter is ignored.
+ In non-blocking mode, if AP returns from
+ Procedure before the timeout expires, its
+ content is set to TRUE. Otherwise, the
+ value is set to FALSE. The caller can
+ determine if the AP returned from Procedure
+ by evaluating this value.
+
+ @retval EFI_SUCCESS In blocking mode, specified AP finished before
+ the timeout expires.
+ @retval EFI_SUCCESS In non-blocking mode, the function has been
+ dispatched to specified AP.
+ @retval EFI_UNSUPPORTED A non-blocking mode request was made after the
+ UEFI event EFI_EVENT_GROUP_READY_TO_BOOT was
+ signaled.
+ @retval EFI_UNSUPPORTED WaitEvent is not NULL if non-blocking mode is not
+ supported.
+ @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_READY The specified AP is busy.
+ @retval EFI_NOT_READY MP Initialize Library is not initialized.
+ @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
+MpInitLibStartupThisAP (
+ IN EFI_AP_PROCEDURE Procedure,
+ IN UINTN ProcessorNumber,
+ IN EFI_EVENT WaitEvent OPTIONAL,
+ IN UINTN TimeoutInMicroseconds,
+ IN VOID *ProcedureArgument OPTIONAL,
+ OUT BOOLEAN *Finished OPTIONAL
+ )
+{
+ if (WaitEvent != NULL) {
+ return EFI_UNSUPPORTED;
+ }
+
+ return StartupThisAPWorker (
+ Procedure,
+ ProcessorNumber,
+ NULL,
+ TimeoutInMicroseconds,
+ ProcedureArgument,
+ Finished
+ );
+}
+
+/**
+ 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.
+
+ @param[in] ProcessorNumber The handle number of AP that is to become the new
+ BSP. The range is from 0 to the total number of
+ logical processors minus 1. The total number of
+ logical processors can be retrieved by
+ MpInitLibGetNumberOfProcessors().
+ @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_DEVICE_ERROR 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.
+ @retval EFI_NOT_READY MP Initialize Library is not initialized.
+
+**/
+EFI_STATUS
+EFIAPI
+MpInitLibSwitchBSP (
+ IN UINTN ProcessorNumber,
+ IN BOOLEAN EnableOldBSP
+ )
+{
+ return SwitchBSPWorker (ProcessorNumber, EnableOldBSP);
+}
+
+/**
+ This service lets the caller enable or disable an AP from this point onward.
+ This service may only be called from the BSP.
+
+ @param[in] ProcessorNumber The handle number of AP.
+ The range is from 0 to the total number of
+ logical processors minus 1. The total number of
+ logical processors can be retrieved by
+ MpInitLibGetNumberOfProcessors().
+ @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_MP_SERVICES_PROTOCOL.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.
+ @retval EFI_NOT_READY MP Initialize Library is not initialized.
+
+**/
+EFI_STATUS
+EFIAPI
+MpInitLibEnableDisableAP (
+ IN UINTN ProcessorNumber,
+ IN BOOLEAN EnableAP,
+ IN UINT32 *HealthFlag OPTIONAL
+ )
+{
+ return EnableDisableApWorker (ProcessorNumber, EnableAP, HealthFlag);
+}
+
+
diff --git a/Core/UefiCpuPkg/Library/MpInitLib/X64/MpEqu.inc b/Core/UefiCpuPkg/Library/MpInitLib/X64/MpEqu.inc new file mode 100644 index 0000000000..a63cd23a40 --- /dev/null +++ b/Core/UefiCpuPkg/Library/MpInitLib/X64/MpEqu.inc @@ -0,0 +1,43 @@ +;------------------------------------------------------------------------------ ;
+; Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.<BR>
+; 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
+NumApsExecutingLocation equ LockLocation + 44h
+CodeSegmentLocation equ LockLocation + 4Ch
+DataSegmentLocation equ LockLocation + 54h
+EnableExecuteDisableLocation equ LockLocation + 5Ch
+Cr3Location equ LockLocation + 64h
+InitFlagLocation equ LockLocation + 6Ch
+CpuInfoLocation equ LockLocation + 74h
+
diff --git a/Core/UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm b/Core/UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm new file mode 100644 index 0000000000..fa54d01542 --- /dev/null +++ b/Core/UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm @@ -0,0 +1,398 @@ +;------------------------------------------------------------------------------ ;
+; Copyright (c) 2015 - 2016, Intel Corporation. All rights reserved.<BR>
+; 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 si, EnableExecuteDisableLocation
+ cmp byte [si], 0
+ jz SkipEnableExecuteDisableBit
+
+ ;
+ ; Enable execute disable bit
+ ;
+ mov ecx, 0c0000080h ; EFER MSR number
+ rdmsr ; Read EFER
+ bts eax, 11 ; Enable Execute Disable Bit
+ wrmsr ; Write EFER
+
+SkipEnableExecuteDisableBit:
+
+ 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
+ lea edi, [esi + InitFlagLocation]
+ cmp qword [edi], 1 ; ApInitConfig
+ jnz GetApicId
+
+ ; AP init
+ mov edi, esi
+ add edi, LockLocation
+ mov rax, NotVacantFlag
+
+TestLock:
+ xchg qword [edi], rax
+ cmp rax, NotVacantFlag
+ jz TestLock
+
+ lea ecx, [esi + NumApsExecutingLocation]
+ inc dword [ecx]
+ mov ebx, [ecx]
+
+Releaselock:
+ mov rax, VacantFlag
+ xchg qword [edi], rax
+ ; program stack
+ mov edi, esi
+ add edi, StackSizeLocation
+ mov eax, dword [edi]
+ mov ecx, ebx
+ inc ecx
+ mul ecx ; EAX = StackSize * (CpuNumber + 1)
+ mov edi, esi
+ add edi, StackStartAddressLocation
+ add rax, qword [edi]
+ mov rsp, rax
+ jmp CProcedureInvoke
+
+GetApicId:
+ mov eax, 0
+ cpuid
+ cmp eax, 0bh
+ jb NoX2Apic ; CPUID level below CPUID_EXTENDED_TOPOLOGY
+
+ mov eax, 0bh
+ xor ecx, ecx
+ cpuid
+ test ebx, 0ffffh
+ jz NoX2Apic ; CPUID.0BH:EBX[15:0] is zero
+
+ ; Processor is x2APIC capable; 32-bit x2APIC ID is already in EDX
+ jmp GetProcessorNumber
+
+NoX2Apic:
+ ; Processor is not x2APIC capable, so get 8-bit APIC ID
+ mov eax, 1
+ cpuid
+ shr ebx, 24
+ mov edx, ebx
+
+GetProcessorNumber:
+ ;
+ ; Get processor number for this AP
+ ; Note that BSP may become an AP due to SwitchBsp()
+ ;
+ xor ebx, ebx
+ lea eax, [esi + CpuInfoLocation]
+ mov edi, [eax]
+
+GetNextProcNumber:
+ cmp dword [edi], edx ; APIC ID match?
+ jz ProgramStack
+ add edi, 20
+ inc ebx
+ jmp GetNextProcNumber
+
+ProgramStack:
+ mov rsp, qword [edi + 12]
+
+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 $ ; Should never reach here
+
+RendezvousFunnelProcEnd:
+
+;-------------------------------------------------------------------------------------
+; AsmRelocateApLoop (MwaitSupport, ApTargetCState, PmCodeSegment, TopOfApStack, CountTofinish);
+;-------------------------------------------------------------------------------------
+global ASM_PFX(AsmRelocateApLoop)
+ASM_PFX(AsmRelocateApLoop):
+AsmRelocateApLoopStart:
+ mov rax, [rsp + 40] ; CountTofinish
+ lock dec dword [rax] ; (*CountTofinish)--
+ mov rsp, r9
+ push rcx
+ push rdx
+
+ lea rsi, [PmEntry] ; rsi <- The start address of transition code
+
+ push r8
+ push rsi
+ DB 0x48
+ retf
+BITS 32
+PmEntry:
+ mov eax, cr0
+ btr eax, 31 ; Clear CR0.PG
+ mov cr0, eax ; Disable paging and caches
+
+ mov ebx, edx ; Save EntryPoint to rbx, for rdmsr will overwrite rdx
+ mov ecx, 0xc0000080
+ rdmsr
+ and ah, ~ 1 ; Clear LME
+ wrmsr
+ mov eax, cr4
+ and al, ~ (1 << 5) ; Clear PAE
+ mov cr4, eax
+
+ pop edx
+ add esp, 4
+ pop ecx,
+ add esp, 4
+ cmp cl, 1 ; Check mwait-monitor support
+ jnz HltLoop
+ mov ebx, edx ; Save C-State to ebx
+MwaitLoop:
+ mov eax, esp ; Set Monitor Address
+ xor ecx, ecx ; ecx = 0
+ xor edx, edx ; edx = 0
+ monitor
+ mov eax, ebx ; Mwait Cx, Target C-State per eax[7:4]
+ shl eax, 4
+ mwait
+ jmp MwaitLoop
+HltLoop:
+ cli
+ hlt
+ jmp HltLoop
+BITS 64
+AsmRelocateApLoopEnd:
+
+;-------------------------------------------------------------------------------------
+; 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
+ mov rax, ASM_PFX(AsmRelocateApLoop)
+ mov qword [rcx + 18h], rax
+ mov qword [rcx + 20h], AsmRelocateApLoopEnd - AsmRelocateApLoopStart
+ ret
+
+;-------------------------------------------------------------------------------------
+;AsmExchangeRole procedure follows. This procedure executed by current BSP, that is
+;about to become an AP. It switches its 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
|